summaryrefslogtreecommitdiff
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
-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
372 files changed, 85337 insertions, 0 deletions
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_ */