summaryrefslogtreecommitdiffstats
path: root/kscd
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch)
tree9047cf9e6b5c43878d5bf82660adae77ceee097a /kscd
downloadtdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz
tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kscd')
-rw-r--r--kscd/CDQuery.txt21
-rw-r--r--kscd/ChangeLog266
-rw-r--r--kscd/Makefile.am83
-rw-r--r--kscd/README59
-rw-r--r--kscd/TODO9
-rw-r--r--kscd/audiocd_play.desktop55
-rw-r--r--kscd/bitmaps/AUTHORS37
-rw-r--r--kscd/bitmaps/CompactDisc.xpm68
-rw-r--r--kscd/bitmaps/Image3.gifbin0 -> 1043 bytes
-rw-r--r--kscd/bitmaps/Image3.tifbin0 -> 2442 bytes
-rw-r--r--kscd/bitmaps/Image3.xpmbin0 -> 6878 bytes
-rw-r--r--kscd/bitmaps/Image4.gifbin0 -> 156 bytes
-rw-r--r--kscd/bitmaps/Image4.xpm31
-rw-r--r--kscd/bitmaps/Makefile.am2
-rw-r--r--kscd/bitmaps/cd3d.xpm62
-rw-r--r--kscd/bitmaps/cdsmallpause.xpm34
-rw-r--r--kscd/bitmaps/cdsmallplay.xpm33
-rw-r--r--kscd/bitmaps/cdsmallstop.xpm34
-rw-r--r--kscd/bitmaps/db.xbm8
-rw-r--r--kscd/bitmaps/db.xpmbin0 -> 7342 bytes
-rw-r--r--kscd/bitmaps/eject.xbm7
-rw-r--r--kscd/bitmaps/ff.xbm7
-rw-r--r--kscd/bitmaps/info.xbm6
-rw-r--r--kscd/bitmaps/kscdlogo.xpm137
-rw-r--r--kscd/bitmaps/lock.xbm7
-rw-r--r--kscd/bitmaps/logo.xbm28
-rw-r--r--kscd/bitmaps/magic.xbm7
-rw-r--r--kscd/bitmaps/nexttrk.xbm7
-rw-r--r--kscd/bitmaps/options.xbm7
-rw-r--r--kscd/bitmaps/playpaus.xbm10
-rw-r--r--kscd/bitmaps/poweroff.xbm7
-rw-r--r--kscd/bitmaps/prevtrk.xbm7
-rw-r--r--kscd/bitmaps/repeat.xbm7
-rw-r--r--kscd/bitmaps/rew.xbm7
-rw-r--r--kscd/bitmaps/shuffle.xbm7
-rw-r--r--kscd/bitmaps/stop.xbm7
-rw-r--r--kscd/bwlednum.cpp591
-rw-r--r--kscd/bwlednum.h93
-rw-r--r--kscd/cddaslave.c532
-rw-r--r--kscd/cddbdlg.cpp198
-rw-r--r--kscd/cddbdlg.h46
-rw-r--r--kscd/configWidget.cpp143
-rw-r--r--kscd/configWidget.h50
-rw-r--r--kscd/configWidgetUI.ui462
-rw-r--r--kscd/configure.in.in68
-rw-r--r--kscd/cr22-action-cdsmall.pngbin0 -> 1417 bytes
-rw-r--r--kscd/docking.cpp158
-rw-r--r--kscd/docking.h75
-rw-r--r--kscd/hi128-app-kscd.pngbin0 -> 18304 bytes
-rw-r--r--kscd/hi16-app-kscd.pngbin0 -> 820 bytes
-rw-r--r--kscd/hi32-app-kscd.pngbin0 -> 2297 bytes
-rw-r--r--kscd/hi48-app-kscd.pngbin0 -> 4682 bytes
-rw-r--r--kscd/hi64-app-kscd.pngbin0 -> 6912 bytes
-rw-r--r--kscd/kcompactdisc.cpp486
-rw-r--r--kscd/kcompactdisc.h303
-rwxr-xr-xkscd/kscd-script782
-rw-r--r--kscd/kscd.cpp1677
-rw-r--r--kscd/kscd.desktop85
-rw-r--r--kscd/kscd.h258
-rw-r--r--kscd/kscd.kcfg107
-rw-r--r--kscd/kscd.lsm15
-rw-r--r--kscd/kscd.profile.xml59
-rw-r--r--kscd/kscdmagic/Makefile.am40
-rw-r--r--kscd/kscdmagic/README146
-rw-r--r--kscd/kscdmagic/core.cpp410
-rw-r--r--kscd/kscdmagic/logo.h50
-rw-r--r--kscd/kscdmagic/magicconf.h23
-rw-r--r--kscd/kscdmagic/main.cpp300
-rw-r--r--kscd/kscdmagic/polygon.h106
-rw-r--r--kscd/kscdmagic/sound.cpp252
-rw-r--r--kscd/kscdmagic/symbol.h1028
-rw-r--r--kscd/kscdmagic/syna.h180
-rw-r--r--kscd/kscdmagic/version.h1
-rw-r--r--kscd/kscdmagic/xlib.c781
-rw-r--r--kscd/kscdmagic/xlib.h139
-rw-r--r--kscd/kscdmagic/xlibwrap.cpp209
-rw-r--r--kscd/ledlamp.cpp103
-rw-r--r--kscd/ledlamp.h63
-rw-r--r--kscd/libwm/Makefile.am23
-rw-r--r--kscd/libwm/PLAT_IMPL_STATUS48
-rw-r--r--kscd/libwm/README20
-rw-r--r--kscd/libwm/audio/Makefile.am5
-rw-r--r--kscd/libwm/audio/audio.c29
-rw-r--r--kscd/libwm/audio/audio.h21
-rw-r--r--kscd/libwm/audio/audio_alsa.c334
-rw-r--r--kscd/libwm/audio/audio_arts.c141
-rw-r--r--kscd/libwm/audio/audio_sun.c525
-rw-r--r--kscd/libwm/buildindex.c221
-rw-r--r--kscd/libwm/cdda.c458
-rw-r--r--kscd/libwm/cddaslave.c572
-rw-r--r--kscd/libwm/cddb.c588
-rw-r--r--kscd/libwm/cdinfo.c890
-rw-r--r--kscd/libwm/cdrom.c899
-rw-r--r--kscd/libwm/cdtext.c428
-rw-r--r--kscd/libwm/configure.in.in72
-rw-r--r--kscd/libwm/database.c1543
-rw-r--r--kscd/libwm/drv_sony.c166
-rw-r--r--kscd/libwm/drv_toshiba.c149
-rw-r--r--kscd/libwm/include/wm_cdda.h203
-rw-r--r--kscd/libwm/include/wm_cddb.h36
-rw-r--r--kscd/libwm/include/wm_cdinfo.h76
-rw-r--r--kscd/libwm/include/wm_cdrom.h114
-rw-r--r--kscd/libwm/include/wm_cdtext.h105
-rw-r--r--kscd/libwm/include/wm_config.h377
-rw-r--r--kscd/libwm/include/wm_database.h42
-rw-r--r--kscd/libwm/include/wm_helpers.h109
-rw-r--r--kscd/libwm/include/wm_index.h36
-rw-r--r--kscd/libwm/include/wm_platform.h51
-rw-r--r--kscd/libwm/include/wm_scsi.h50
-rw-r--r--kscd/libwm/include/wm_struct.h198
-rw-r--r--kscd/libwm/include/wm_version.h35
-rw-r--r--kscd/libwm/include/workman.h52
-rw-r--r--kscd/libwm/include/workman_defs.h30
-rw-r--r--kscd/libwm/index.c383
-rw-r--r--kscd/libwm/plat_aix.c489
-rw-r--r--kscd/libwm/plat_bsd386.c510
-rw-r--r--kscd/libwm/plat_freebsd.c560
-rw-r--r--kscd/libwm/plat_hpux.c358
-rw-r--r--kscd/libwm/plat_irix.c474
-rw-r--r--kscd/libwm/plat_linux.c803
-rw-r--r--kscd/libwm/plat_linux_audio.c489
-rw-r--r--kscd/libwm/plat_linux_cdda.c253
-rw-r--r--kscd/libwm/plat_news.c442
-rw-r--r--kscd/libwm/plat_openbsd.c545
-rw-r--r--kscd/libwm/plat_osf1.c674
-rw-r--r--kscd/libwm/plat_scor5.c426
-rw-r--r--kscd/libwm/plat_sun.c972
-rw-r--r--kscd/libwm/plat_sun_audio.c493
-rw-r--r--kscd/libwm/plat_sun_cdda.c380
-rw-r--r--kscd/libwm/plat_svr4.c482
-rw-r--r--kscd/libwm/plat_template.c295
-rw-r--r--kscd/libwm/plat_ultrix.c663
-rw-r--r--kscd/libwm/scsi.c667
-rw-r--r--kscd/libwm/wm_helpers.c238
-rw-r--r--kscd/panel.ui468
-rw-r--r--kscd/prefs.kcfgc5
-rw-r--r--kscd/version.h1
-rwxr-xr-xkscd/workman2cddb.pl86
-rw-r--r--kscd/xmcd.desktop75
139 files changed, 30963 insertions, 0 deletions
diff --git a/kscd/CDQuery.txt b/kscd/CDQuery.txt
new file mode 100644
index 00000000..51ae2ecc
--- /dev/null
+++ b/kscd/CDQuery.txt
@@ -0,0 +1,21 @@
+
+Purchases:
+http://cdnow.com/switch/from=sr-288025/target=buyweb_products/artfs=Seal
+http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=Seal/frm=lk_DiscPlay/
+
+Information:
+http://musiccentral.msn.com/MCGen/ReEntry?theurl=http%3A//musiccentral.msn.com/Search/DoFullSearch%3FArtists%3Don%26Albums%3D%26Songs%3D%26ArticleTitles%3D%26FullText%3D%26strInput%3DSeal
+http://www.ubl.com/find/form?SEARCH=Seal
+http://www.excite.com/search.gw?c=web&search=Seal&trace=a
+http://www.search.hotbot.com/hResult.html?SW=web&SM=MC&MT=Seal&DC=10&DE=2&RG=NA&_v=2
+http://www.infoseek.com/Titles?qt=Seal&col=WW&sv=IS&lk=ip-noframes&nh=10
+http://www.lycos.com/cgi-bin/pursuit?cat=lycos&query=Seal
+http://www.mckinley.com/search.gw?search=Seal&c=web&look=magellan
+http://search.yahoo.com/bin/search?p=Seal
+
+Performances:
+http://www.tourdates.com/cgi-bin/search.cgi?type=Artist&search=Seal
+
+
+
+
diff --git a/kscd/ChangeLog b/kscd/ChangeLog
new file mode 100644
index 00000000..24eeb3ee
--- /dev/null
+++ b/kscd/ChangeLog
@@ -0,0 +1,266 @@
+Wed May 31 06:18:21 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Switched from stand alone WorkMan code to libworkman.
+ * Fixed Bugs 296, 298, 299, 301, 371, 815, 1186, 2278, 2354,
+ * 2429, 2559 and 2586 or verified that they were already fixed.
+ * updated ChangeLog.
+
+Sat May 27 09:43:58 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Updated Kscd Magic from Synaesthesia 1.3 to Synaesthesia 2.0
+ * Fixed missing slot in CDDB configuration
+ * Added (and fixed) loop mode display.
+ * updated and sorted libWorkMan API
+ * addressed the 100dpi font problem (Thanks to John R
+ MacMillan <john@weirdways.com> for sending an idea for a fix).
+
+Sun Mar 5 10:54:40 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Completed shuffle mode (some patch data was missing) and
+ added corresponding "shuffle" Display string.
+ * Slightly changed "loop" semantics: "loop" is no longer a single
+ playing mode but a modifier. You can now loop "playing" mode and
+ "shuffle" mode.
+
+Mon Feb 28 07:27:04 2000 Dirk Frsterling <milliByte@gmx.de>
+
+ * Added "random is shuffle" patch from
+ Bernard Kozioziemski <koz@tdl.com>
+ * Started some source formatting my better understanding. Sorry,
+ but I'm a Qt/KDE-programming-newbie and need some visual hints yet
+ to understand the code. (And I _love_ source documentation
+ (comments) ...)
+
+Tue Jan 18 17:27:26 2000 Dirk Frsterling <milliByte@DeathsDoor.com>
+
+ * Added the libWorkMan directory and inserted it into the make
+ process. Please note, that libWorkMan will be built and installed,
+ but is not currently used.
+
+1999-03-17 Harri Porten <porten@kde.org>
+
+ * kscd.cpp: applied patch from Petter Reinholdtsen <pere@hungry.com>
+ (bug#302) that prevents a segfault if cddb directory is invalid.
+ * CDDialog.cpp: fixed length of last title (bug#304).
+
+1999-03-09 Harri Porten <porten@kde.org>
+
+ * ported to Qt 2.0 strings. Haven't tested if everything still works.
+
+1999-02-18 Harri Porten <porten@kde.org>
+
+ * cddb.cpp: work around snprintf() for non-GNUish systems
+
+1998-12-22 Sam Maloney <thufir@illogic.ml.org>
+
+ * Added to last commit, as so the 'autodock' checkbox would be
+ disabled when 'docking' was unchecked, and vice versa.
+
+1998-12-20 Sam Maloney <thufir@illogic.ml.org>
+
+ * Just simple bugfixes to make more stable like...:
+ * Fixed so that when one turns on or off 'enable docking' it
+ adds/removes the dockwidget in the panel (instead of before one
+ would have to restart kscd).
+ * Fixed so that if one turned off 'enable docking' but still had
+ 'autodock enabled' on, then it would NOT dock (and remove the
+ widget).
+
+1998-12-12 Christian Esken <esken@kde.org>
+
+ * Tweaked docking a bit more. Under certain circumstances,
+ dock_widget->dock() correctly does not get called at the start
+ of the program. So if you docked, the window just vanished.
+ I added a call to dock_widget->dock() at "appropiate" places.
+
+1998-11-28 Bernd Wuebben <wuebben@petit.cornell.edu>
+
+ * kscd.cpp: resinstated old look and behavior
+ Sam, we should talk ...
+
+1998-11-20 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed bug/"missing thing" were it would just crash when loading
+ up if it couldn't find the cddb base dir. Now it will use
+ InexactDialog to prompt the user for the directory. (cancel button
+ will force it to get from remote).
+ * NOTE: Im just going fix the inexact dialog box so it can be
+ single line input, (for the cddb dialog), then ill probably
+ release 1.2.4, unless anyone has any objections. (to lazy to fix
+ tonite)
+
+1998-11-11 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed bug with debug flag being messed up when docking
+ (incorrect extern statement)
+ * Worked around a wierd bug thing with docking. (consult
+ docking.cpp in toggleState() )
+
+1998-11-08 Robert Bossecker <Robert.Bossecker@fresenius.de>
+
+ * Fixed problem with editing EXT info, where if you edited track
+ x, it would (stupidly) modify x-1. If editing track 1, it would
+ modify the extinfo for the Title.
+
+1998-11-06 Sam Maloney <thufir@illogic.ml.org>
+
+ * Made smtpMailer a global widget, so it could mail in background.
+ * Fixed problem were after submitting a record via SMTP, and
+ having an error, if one tried to exit Kscd, it would close all
+ widgets, but be stuck in while loop.
+ * Added error/success reporting of SMTP submissions.
+ * Fixed problem with Local Cache, where it would ignore leading
+ 0's in record file names, thus not finding them when looking for
+ that record.
+ * Added a timeout when waiting for server interaction.
+
+1998-11-04 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed problem with 'dock' button, when AutoDock is turned on.
+ * Implimented SMTP code, so Kscd no longer requires sendmail/unix
+ mail program(s) to submit CDDB records.
+ * Added VERY requested feature, of having it retract the tray if
+ its ejected, when one hits the eject button.
+
+1998-10-14 Alexei Dets <dets@services.ru>
+
+ * Fixed CD Database Editor close handling.
+
+1998-10-13 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed compilation problems some compilers will notice related to
+ last CDDB setup modifs
+
+1998-10-09 Sam Maloney <thufir@illogic.ml.org>
+
+ * Added "play on tray close" feature
+ * Many little fixes
+
+1998-09-23 Sam Maloney <thufir@illogic.ml.org>
+
+ * Fixed submission of CDDB records, where it would not include a
+ 'PLAYORDER=' at the bottom of the email.
+ * Switched it over from using 'mail -s <subject> < record', to
+ using sendmail. (currently adding SMTP suppport, so it uses
+ no external program for this)
+
+1998-09-21 Sam Maloney <thufir@illogic.ml.org>
+
+ * Added automatic storing of CDDB records downloaded from CDDB
+ server to local harddrive.
+ * Fixed bug with routine connecting signals, over and over again,
+ causing routines to be called too many time, annoying CDDB
+ server admins. Happens when a new CDDB (remote) request was
+ issued.
+ * Added snazyness of having the pulldown track selection box
+ display ("%02d: %s", tracknumber, trackname) for each item,
+ instead of just the number.
+
+Fri May 15 18:28:40 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * docking.cpp: fixed up the docking behaviour
+
+ * Some Layout improvements. -- still not 100% satisfied with the
+ way the config dialogs look now.
+
+1998-05-12 Vadim Zaliva <lord@crocodile.org>
+
+ * Options dialog made wider
+ * In CDDB setup dialog added list of email serves.
+ * Getting SMTP servers from list of servers.
+
+1998-04-29 Vadim Zaliva <lord@crocodile.org>
+
+ * Corrected problem with not saving "UnixMailCommand" option
+ which was disabling CDDB submissions.
+
+1998-04-28 Vadim Zaliva <lord@crocodile.org>
+
+ * Corrected bug in cddb with current CD information disappearing
+ after some time of playing.
+
+1998-04-26 Vadim Zaliva <lord@crocodile.org>
+
+ * Removed potetial concurency problem in CDDB connection procedure.
+ * Added support for protocol level 3 in CDDB connections.
+
+1998-04-17 Vadim Zaliva <lord@crocodile.org>
+
+ * User and domain name now calculated once, not per each request.
+
+1998-04-16 Vadim Zaliva <lord@crocodile.org>
+
+ * Tested with Netscape Proxy
+
+ * Tested with Squid-1.1.18 HTTP proxy
+ * Tested with HTTP proxy, w/o HTTP proxy, using CDDBP and HTTP.
+ * Small bug in parsing corrected.
+
+ * State automata and parsing reimplemented
+ * Works over HTTP with and without HTTP proxy.
+ * Tested with M>Wall HTTP proxy
+
+ * Implemented more sophisticated HTTP encoding for parameters.
+ * kscd.cpp reformated.
+
+ * CDDB over HTTP works, w/o proxy now. Proxy support to be implemeted to-morrow.
+
+1998-04-15 Vadim Zaliva <lord@crocodile.org>
+
+ * cddb.h, cddb.cpp reformated
+ * tested to work with and without HTTP proxy
+ * tested with squid 1.1.18
+
+ * Added one more default server, with HTTP protocol
+ * Code for getting servers list via HTTP done and works.
+ * Changed 'cddb' object allocation.
+ * (this is intermediate commit, HTTP only partialy works)
+
+1998-04-14 Vadim Zaliva <lord@crocodile.org>
+
+ * GUI added for setting HTTP proxy to access CDDB.
+ * Config options added for HTTP proxy stuff
+ * most includes from cddb.h moved to cddb.cpp,kscd.cpp
+ * new version #1.1.1
+
+Tue Apr 14 05:17:36 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * layout corrections for kscd magic dialog
+
+Sat Jan 17 21:36:27 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * Enabled CDDB support. A couple of clean ups and fixes.
+
+Tue Jan 13 03:24:49 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * applied Sebestyen Zoltan <szoli@digo.inf.elte.hu> FreeBSD patch.
+
+Thu Jan 1 19:08:22 1998 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * Monster commit: Commit cddb and web integration additions
+ Let the games begin!
+
+Fri Dec 26 13:48:56 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * headerfile changes in plat_bsd.c
+
+Sat Oct 25 11:13:40 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * fixed the segfault problem when you didn't have acess permissions
+ to the cdrom device
+
+Thu Aug 14 19:49:18 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * added irix support
+ * added KSCD configuration script
+
+Wed Jun 25 01:24:39 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * added Marc van Kempen's FreeBSD patches
+ He reports also strange behaviour of kscd when reloading a CD
+ Is this with the code or with his CD drive ?
+
+ * Set proper defines that should allow for a smooth compile
+ on a SUN/ SUN SOLARIS
+
+
diff --git a/kscd/Makefile.am b/kscd/Makefile.am
new file mode 100644
index 00000000..f0b8d50b
--- /dev/null
+++ b/kscd/Makefile.am
@@ -0,0 +1,83 @@
+# $Id$
+
+mimedir = $(kde_mimedir)/text
+picsdir = $(kde_datadir)/kscd/pics
+
+INCLUDES= -I$(top_srcdir) -I$(top_builddir)/libkcddb $(all_includes)
+METASOURCES = AUTO
+
+####### Files
+
+noinst_HEADERS = kscd.h bwlednum.h ledlamp.h \
+ version.h docking.h configWidget.h
+
+kscd_SOURCES = panel.ui kscd.cpp kscd.skel ledlamp.cpp \
+ docking.cpp bwlednum.cpp \
+ configWidget.cpp configWidgetUI.ui prefs.kcfgc cddbdlg.cpp
+
+noinst_LTLIBRARIES = libkcompactdisc.la
+libkcompactdisc_la_SOURCES = kcompactdisc.cpp
+
+libkcompactdisc_la_LIBADD = $(top_builddir)/kscd/libwm/libworkman.la
+
+kde_kcfg_DATA = kscd.kcfg
+
+kscd_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kscd_LDADD = $(LIBCDROM) $(LIBCDAUDIO) \
+ $(LIBFPE) $(LIB_KIO) -lkutils $(top_builddir)/libkcddb/libkcddb.la \
+ libkcompactdisc.la
+
+profiledata_DATA = kscd.profile.xml
+profiledatadir = $(kde_datadir)/profiles
+
+servicedata_DATA = audiocd_play.desktop
+servicedatadir = $(kde_datadir)/konqueror/servicemenus
+
+DISTCLEANFILES = config.h
+
+bin_PROGRAMS = kscd
+# Don't Build cddaslave. It doesn't work.
+# cddaslave
+
+#config.h: kscd-script
+#$(SHELL) $(srcdir)/kscd-script
+
+SUBDIRS = bitmaps libwm
+
+bin_SCRIPTS = workman2cddb.pl
+
+kscdicondir = $(kde_datadir)/kscd/icons
+kscdicon_ICON = cdsmall
+KDE_ICON = kscd
+
+xdg_apps_DATA = kscd.desktop
+
+mime_DATA = xmcd.desktop
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kscd.pot
+
+cdrom.c:config.h
+plat_bsd386.c:config.h
+plat_freebsd.c:config.h
+plat_hpux.c:config.h
+plat_irix.c:config.h
+plat_linux.c:config.h
+plat_news.c:config.h
+plat_osf1.c:config.h
+plat_scor5.c:config.h
+plat_sun.c:config.h
+plat_sun_audio.c:config.h
+plat_sun_cdda.c:config.h
+plat_svr4.c:config.h
+plat_ultrix.c:config.h
+
+KDE_OPTIONS = nofinal
+
+
+cddbdlg.o: ../libkcddb/configbase.h ../libkcddb/cdinfodialogbase.h
+configWidget.o: ../libkcddb/configbase.h
+docking.o: ../libkcddb/configbase.h
+kscd.o: ../libkcddb/configbase.h
+kscd_skel.o: ../libkcddb/configbase.h panel.h prefs.h configWidgetUI.h
+
diff --git a/kscd/README b/kscd/README
new file mode 100644
index 00000000..e1366867
--- /dev/null
+++ b/kscd/README
@@ -0,0 +1,59 @@
+KSCD 1.2.3
+==========
+
+ * Fixed DOCKING!
+
+Basically, this version finishes a whole lot more that has been
+missing/broken since the last version that bernd did, (1.2.0).
+
+ Sam Maloney, <thufir@illogic.ml.org>
+
+KSCD 1.2.0t1
+============
+
+ * Fixed CDDB code big time.
+ * Submissions now fixed.
+ * Server requests fixed.
+ * Local storing of CDDB records. (automatic)
+
+KSCD have been broken and NOT updated, since the release of KDE 1.0, and
+that is a long time ago, so i decided to fix MAJOR problems with the CDDB
+code, now it is permitted to use and upload to the CDDB database.
+
+Sam Maloney, <thufir@illogic.ml.org>
+
+KSCD 0.8
+========
+
+Sat Jan 17 17:50:37 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * KSCD 0.8
+ * CDDB submissions enabled.
+
+
+Remember if kscd seems to have trouble with your cd drive, do a 'make clean'
+then 'make' and answer the questions posed by the kscd-script to configure
+kscd suitable for your cdrom drive.
+
+Bernd
+
+KSCD 0.6
+========
+
+ CDDB support, web integration
+
+Bernd
+
+KSCD 0.4
+========
+
+This is version 0.4 of kscd. Kscd is now easier to build than
+ever with its new configuration script. Supprt was added
+for Irix and Sun/Solaris cdda.
+
+Please see the html documentation in the docs directory for
+information about kscd.
+
+Thank you,
+Bernd Wuebben
+wuebben@kde.org
diff --git a/kscd/TODO b/kscd/TODO
new file mode 100644
index 00000000..e10c01c8
--- /dev/null
+++ b/kscd/TODO
@@ -0,0 +1,9 @@
+* fix html encoding
+* Session Management
+* change the GUI
+
+AJS:
+o fix eject button
+o don't show "no disc" error on startup
+o direct digital playback
+o better GUI layout for LCD panel
diff --git a/kscd/audiocd_play.desktop b/kscd/audiocd_play.desktop
new file mode 100644
index 00000000..4200b488
--- /dev/null
+++ b/kscd/audiocd_play.desktop
@@ -0,0 +1,55 @@
+[Desktop Entry]
+ServiceTypes=media/audiocd
+Actions=Play;
+X-KDE-Priority=TopLevel
+
+[Desktop Action Play]
+Name=Play
+Name[bg]=Старт
+Name[bn]=চালাও
+Name[br]=Seniñ
+Name[bs]=Sviraj
+Name[ca]=Reproducció
+Name[cs]=Přehrát
+Name[cy]=Chwarae
+Name[da]=Spil
+Name[de]=Abspielen
+Name[el]=Αναπαραγωγή
+Name[eo]=Ludi
+Name[et]=Esitus
+Name[fa]=پخش
+Name[fi]=Soita
+Name[fr]=Lecture
+Name[ga]=Seinn
+Name[gl]=Reproducir
+Name[he]=נגן
+Name[hu]=Lejátszás
+Name[is]=Spila
+Name[it]=Riproduci
+Name[kk]=Ойнату
+Name[km]=ចាក់
+Name[ko]=재생
+Name[lt]=Groti
+Name[mk]=Пушти
+Name[nb]=Spill
+Name[nds]=Afspelen
+Name[ne]=प्ले
+Name[nl]=Afspelen
+Name[nn]=Spel
+Name[pa]=ਵਜਾਓ
+Name[pl]=Odtwarzaj
+Name[pt]=Tocar
+Name[pt_BR]=DjPlay
+Name[ru]=Воспроизведение
+Name[sl]=Predvajaj
+Name[sv]=Spela
+Name[ta]=தொடங்கு
+Name[th]=เล่น
+Name[tr]=Çal
+Name[uk]=Пуск
+Name[wa]=Djouwer
+Name[zh_CN]=播放
+Name[zh_HK]=播放
+Name[zh_TW]=播放
+Exec=kscd -s -caption "%c" %i %m %u
+Icon=kscd
diff --git a/kscd/bitmaps/AUTHORS b/kscd/bitmaps/AUTHORS
new file mode 100644
index 00000000..3db9dacb
--- /dev/null
+++ b/kscd/bitmaps/AUTHORS
@@ -0,0 +1,37 @@
+The following copyright notice applies to the following files found in
+this directory:
+ eject.xbm
+ ff.xbm
+ lock.xbm
+ logo.xbm
+ nexttrk.xbm
+ options.xbm
+ playpaus.xbm
+ poweroff.xbm
+ prevtrk.xbm
+ repeat.xbm
+ rew.xbm
+ shuffle.xbm
+ stop.xbm
+
+/*
+ * xmcd - Motif(tm) CD Audio Player
+ *
+ * Copyright (C) 1993-1996 Ti Kan
+ * E-mail: ti@amb.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
diff --git a/kscd/bitmaps/CompactDisc.xpm b/kscd/bitmaps/CompactDisc.xpm
new file mode 100644
index 00000000..cf7e4c00
--- /dev/null
+++ b/kscd/bitmaps/CompactDisc.xpm
@@ -0,0 +1,68 @@
+/* XPM */
+static char * image_name[] = {
+"56 46 19 1",
+" c #F7F7F3F3F7F7",
+". c #B6B6B2B2B6B6",
+"X c #000000000000",
+"o c #A6A6A2A2A6A6",
+"O c #969692929696",
+"+ c #61618282B6B6",
+"@ c #EFEFAEAEA6A6",
+"# c #B6B6B6B6B6B6",
+"$ c #F7F7F7F7F7F7",
+"% c #9E9E96969E9E",
+"& c #FFFFFBFBFFFF",
+"* c #696971718E8E",
+"= c #B6B6AEAEB6B6",
+"- c #AEAEAAAA8E8E",
+"; c #000000001010",
+": c #080804040808",
+"> c #A6A69E9EA6A6",
+", c #B6B6BABAC7C7",
+"< c #96969A9AAEAE",
+" .",
+" ......................................................X",
+" ......................................................X",
+" ......................................................X",
+" o...o...o...o...o...o...o...o...o...o...o...o...o...o.X",
+" ......................................................X",
+" o.o.o.o.o.o.o.o...........................o.o.o.o.o.o.X",
+" .....o.......o...OXO.o+X+OXOXOX++X+OXO+o.....o.......oX",
+" o.o.o.o.o.o.o.o..X X.oOOOOOOOOOOOOOOOOOo..o.o.o.o.o.o.X",
+" ...o...o...o.....X X.......................o...o...o..X",
+" o.o.o.o.o.o..OXXXX Xo+X+oOXXXXXOoOXXXXXO..o.o.o.o.o.o.X",
+" .o...o.o.o...X. X.X X.X. X.X. .X.o...o.o.o...oX",
+" o.o.o.o.o.o..X +X+ XoX XoX XXXXOoX XXX X..o.o.o.o.o.o.X",
+" .o.o.o.o.o.o.X X.X X.X X.X X+++..X X.XXX.o.o.o.o.o.o.oX",
+" o.o.o.o.o.o..X X.X XoX XoX. .XoX X......o.o.o.o.o.o.X",
+" oo.ooo.ooo.o.X X.X XoX Xo.++XX XoX X.XXX.o.ooo.ooo.oooX",
+" o.o.o.o.o.o..X +X+ XoX XoOXXXX XoX XXX X..o.o.o.o.o.o.X",
+" oooooooooooo.X. XoX XoX .XoX. .X.oooooooooooooX",
+" o.o.o.o.o.o..OXXXXXOo+X+oOXXXXXOoOXXXXXO..o.o.o.o.o.o.X",
+" ooooooooooooo...........................ooooooooooooooX",
+" ooo.ooo.ooooooooooooooooooooooooooooooooooo.ooo.ooo.ooX",
+" ooooooooooooooooooooooooooooooooooooooooooooooooooooooX",
+" ooooooooooooooooo...@.@#@.@@$@$@$@..oo.ooo.ooo.oooooooX",
+" ooooooooooooooo#@@. O$O + %@@$@ #$@$@.o@oo.oo@.oooooooX",
+" OoooOoooOoooO.@$$$ + + + + @&@$# + # . .@. .ooOoooOoX",
+" oooooooooooo@ . + + %@@$# + O # .$@ .oooooooooX",
+" ooOoOoOoooO@$@$@$ . + @$.$*$O . . @$ @#..ooOoOoX",
+" oooooooooo@$@$@$@$@ @ .$O@# +$# .$@$@$@ #oooooooooX",
+" OoOoOoOoO.$@$@$@$@$@$@$ $$$@$# @$@$$$$$$ oooOoOoOoX",
+" ooooooooo@$#$#$#$#$#$# .XXX#$@$@ $ #@ooooooooX",
+" OoOoOoOoO O$O$O$O$=$.$ .+ooo+# @$@$@$@$@$@$@#o#oOoOoOoX",
+" oOoooOooO@ +$*$=$. .++*# #@#@#$#$.$.$#$+OoooOoooOX",
+" OoOoOoOoO@$ +$# . - @$ $$$@$#@##=....@#$=+OOoOoOoOoX",
+" oOoOoOoOOO . +$*$*$+#@$o$@$#@#.....=$.++OoOoOoOoOX",
+" OoOoOoOoOo+ . + + +$+$+..$o o$@$#@#...@$.*;oOOoOoOoOoX",
+" OOoOOOoOOOo+ . + + +$+.. O o o$@$#@#@$$O+:oOOoOOOoOOOX",
+" OoOoOoOoOoOo+. . + +## O o o o$@$$$$=+X+oOOoOoOoOoOoX",
+" OOOOOOOOOOOOoO+.. @$@ @ . . o o$>$@,*XX+OoOOOOOOOOOOOOX",
+" OoOoOoOoOoOoOooO+++..o.o.o.o.o#>#+XX+<>ooOOoOoOoOoOoOoX",
+" OOOOOOOOOOOOOOOoooO++X+XXXXXXXX+X+OoooOOOOOOOOOOOOOOOOX",
+" OOOoOOOoOOOoOOOoOOoooooooooooooooooOOOOoOOOoOOOoOOOoOOX",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX",
+" OoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOX",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX",
+".XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/kscd/bitmaps/Image3.gif b/kscd/bitmaps/Image3.gif
new file mode 100644
index 00000000..a2a72c4c
--- /dev/null
+++ b/kscd/bitmaps/Image3.gif
Binary files differ
diff --git a/kscd/bitmaps/Image3.tif b/kscd/bitmaps/Image3.tif
new file mode 100644
index 00000000..4fd8fcb9
--- /dev/null
+++ b/kscd/bitmaps/Image3.tif
Binary files differ
diff --git a/kscd/bitmaps/Image3.xpm b/kscd/bitmaps/Image3.xpm
new file mode 100644
index 00000000..488cb3b0
--- /dev/null
+++ b/kscd/bitmaps/Image3.xpm
Binary files differ
diff --git a/kscd/bitmaps/Image4.gif b/kscd/bitmaps/Image4.gif
new file mode 100644
index 00000000..7fcf837f
--- /dev/null
+++ b/kscd/bitmaps/Image4.gif
Binary files differ
diff --git a/kscd/bitmaps/Image4.xpm b/kscd/bitmaps/Image4.xpm
new file mode 100644
index 00000000..611290ec
--- /dev/null
+++ b/kscd/bitmaps/Image4.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * Image4_xpm[] = {
+"20 20 8 1",
+" c #C71BC30BC71B",
+". c #000000000000",
+"X c #79E779E779E7",
+"o c #FFFFFFFF0000",
+"O c #BEFBBEFBBEFB",
+"+ c #0000FFFFFFFF",
+"@ c #0000FFFF0000",
+"# c #FFFFFFFFFFFF",
+" .. ",
+" ... ",
+" XXXXXX .... ",
+" XXooOOOOXX.O.. ",
+" X+oooOOOOOO.OO. ",
+" X@++ooOOOOO..XO. ",
+" X@@+ooOOOO...XO. ",
+" XOO@@+oOOO#...O.. ",
+" XOOO@+XXXXOOOO... ",
+" XOOOO@X##XOOOO... ",
+" XOOOO@X##XOOOO X ",
+" XOOOO#XXXX+OOOOX ",
+" XOOO#OO#@@o+OOOX ",
+" XO#OOOO@@o++OX ",
+" X#OOOOO@@oo++X ",
+" XOOOOO@@@ooX ",
+" XXOOO@@@XX ",
+" XXXXXX ",
+" ",
+" "};
diff --git a/kscd/bitmaps/Makefile.am b/kscd/bitmaps/Makefile.am
new file mode 100644
index 00000000..4785fec0
--- /dev/null
+++ b/kscd/bitmaps/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = *.xpm *.gif *.xbm
+
diff --git a/kscd/bitmaps/cd3d.xpm b/kscd/bitmaps/cd3d.xpm
new file mode 100644
index 00000000..627e76a0
--- /dev/null
+++ b/kscd/bitmaps/cd3d.xpm
@@ -0,0 +1,62 @@
+/* XPM */
+static char * image_name[] = {
+"56 46 13 1",
+" c #F7F7F7F7F7F7",
+". c #B6B6B2B2B6B6",
+"X c #B6B6B6B6B6B6",
+"o c #000000001010",
+"O c #A6A6A2A2A6A6",
+"+ c #969692929696",
+"@ c #3838FBFB3030",
+"# c #717182829696",
+"$ c #FFFFFFFF0000",
+"% c #B6B620202020",
+"& c #51515151FFFF",
+"* c #FFFF0000FFFF",
+"= c #FFFF14144141",
+" .",
+" .X..X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o",
+" ......................................................o",
+" .X..X.................................................o",
+" O...O.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.o",
+" X..X..................................................o",
+" O.+.O.+XO.+.OX+.O.+.OX+.O.+.OX+.O.+.OX+.O.+.OX+.O.O.OXo",
+" .X...O....X..O.X..X..O.X..X..O.X..X..O.X..X..O.X..X..Oo",
+" O.O.OXO.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.o",
+" ...O...OX..O...O...OX..O...OX..O...OX..O...OX..O...O..o",
+" OXO.O.O.O.OXO.OXO.O.O.O.OXO.O.O.OXO.O.O.OXO.O.O.OXO.OXo",
+" .O..XO.O.O...O.O.OX..O.O@#@####O.O...OXO.O...OXO.O...Oo",
+" O.O.O.O.O.O.OXO.O.O.O$#@#@########O.O.O.O.O.O.O.O.OXO.o",
+" .OXO.OXO.OXO.O.O.O.%&#$@@###########.O.O.OXO.O.O.O.O.Oo",
+" O.O.O.O.O.O.O.O.O.#*%&@$@@##O.#######.O.O.O.O.OXO.O.O.o",
+" OO.OOO.OOO.OOO.OO@@#*%&$@##OO##OO#####XOOO.OOO.OOO.OOOo",
+" OXO.O.OXO.OXO.OX&$$@#*%$@@#.O##.#.#####.O.OXO.O.O.OXO.o",
+" OOOOOOOOOOOOOOOO%%&$$#**@@##OOOO#OO####OOOOOOOOOOOOOOOo",
+" O.O.O.O.O.O.O.O*=*%%&$@$=@#####.#X######O.O.O.O.O.O.O.o",
+" OOOOOOOOOOOOOOO#*#=*%&$$&@OOO##OO#######OOOOOOOOOOOOOOo",
+" OOOXOOO.OOO.OO######=*%&$O O###########OO.OOOXOOO.OOo",
+" OOOOOOOOOOOOOO@@@@@@@@@@O O##########OOOOOOOOOOOOOo",
+" OOOOOOOOOOOOOO#########O #XX O#########OOOOOOOOOOOOOo",
+" OOOOOOOOOOOOOO#########O #. +X O#########OOOOOOOOOOOOOo",
+" +OOO+OOO+OOO+O#########O #X. O#########OOO+OOO+OOO+Oo",
+" OOOOOOOOOOOOOO##########O O**########OOOOOOOOOOOOOo",
+" OO+O+O+OOO+O+O####+######O O@&%***#####O+O+O+OOO+O+Oo",
+" OOOOOOOOOOOOOOO##OO#######OOO#$@@&%%**##OOOOOOOOOOOOOOo",
+" +O+O+O+O+O+O+O+####O##########=$@@$&%%**+O+O+O+O+O+O+Oo",
+" OOOOOOOOOOOOOOOO##O#O##########$$@@$&&&OOOOOOOOOOOOOOOo",
+" +O+O+O+O+O+O+O+O####+##########$=$@@@$&O+O+O+O+O+O+O+Oo",
+" O+OOO+OOO+OOO+OOO####+O#########$#$@$@OOO+OOO+OOO+OOO+o",
+" +O+O+O+O+O+O+O+O+O####+#+#######$=#$$O+O+O.O+O+O+O+O+Oo",
+" O+O+O+O+O+O+O+O+O+O####+#########$=#O+O+..O+O+O+O+O+O+o",
+" +O+O+O+O+O+O+O+O+O+O+############$+O+#...++O+O+O+O+O+Oo",
+" ++O+++O+++O+++O+++O+++O+#######+++##...X#+O+++O+++O+++o",
+" +O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+#........+O+O+O+O+O+O+Oo",
+" ++++++++++++++++++++++++++++++++++OOOO++++++++++++++++o",
+" +O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+Oo",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+" +++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O++o",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+" +O+++++++O+++++++O+++++++O+++++++O+++++++O+++++++O++++o",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o",
+"Xooooooooooooooooooooooooooooooooooooooooooooooooooooooo"};
diff --git a/kscd/bitmaps/cdsmallpause.xpm b/kscd/bitmaps/cdsmallpause.xpm
new file mode 100644
index 00000000..7ade7d0b
--- /dev/null
+++ b/kscd/bitmaps/cdsmallpause.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * cdsmallpause_xpm[] = {
+"15 24 7 1",
+" c none",
+". c #FFFFFFFF0000",
+"X c #C71BC30BC71B",
+"o c #0000FFFFFFFF",
+"O c #0000FFFF0000",
+"+ c #FFFFFFFFFFFF",
+"@ c #000000000000",
+" ",
+" ..XXX ",
+" o...XXXXX ",
+" Ooo..XXXX+X ",
+" OOo..XXX+XX ",
+" XXOOo.XX+XXXX ",
+" XXXOo @ XXXXX ",
+" XXXXO@ @XXXXX ",
+" XXXX+ @ oXXXX ",
+" XXX+XX+O.oXXX ",
+" X+XXXXO.ooX ",
+" +XXXXXO..oo ",
+" XXXXXOO.. ",
+" XXXOO ",
+" ",
+" ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" @@ @@ ",
+" "};
diff --git a/kscd/bitmaps/cdsmallplay.xpm b/kscd/bitmaps/cdsmallplay.xpm
new file mode 100644
index 00000000..1622ebce
--- /dev/null
+++ b/kscd/bitmaps/cdsmallplay.xpm
@@ -0,0 +1,33 @@
+/* XPM */
+static char * cdsmallplay_xpm[] = {
+"15 24 6 1",
+" c #FFFFFFFF0000",
+". c #C71BC30BC71B",
+"X c #0000FFFFFFFF",
+"o c #0000FFFF0000",
+"O c #FFFFFFFFFFFF",
+"+ c #000000000000",
+" ",
+" ... ",
+" X ..... ",
+" oXX ....O. ",
+" ooX ...O.. ",
+" ..ooX ..O.... ",
+" ...oX + ..... ",
+" ....o+ +..... ",
+" ....O + X.... ",
+" ...O..Oo X... ",
+" .O....o XX. ",
+" O.....o XX ",
+" .....oo ",
+" ...oo ",
+" ",
+" ",
+" ++ ",
+" ++++ ",
+" ++++++ ",
+" ++++++++ ",
+" ++++++ ",
+" ++++ ",
+" ++ ",
+" "};
diff --git a/kscd/bitmaps/cdsmallstop.xpm b/kscd/bitmaps/cdsmallstop.xpm
new file mode 100644
index 00000000..9bbc477a
--- /dev/null
+++ b/kscd/bitmaps/cdsmallstop.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * cdsmallstop_xpm[] = {
+"15 24 7 1",
+" c none",
+". c #FFFFFFFF0000",
+"X c #C71BC30BC71B",
+"o c #0000FFFFFFFF",
+"O c #0000FFFF0000",
+"+ c #FFFFFFFFFFFF",
+"@ c #000000000000",
+" ",
+" ..XXX ",
+" o...XXXXX ",
+" Ooo..XXXX+X ",
+" OOo..XXX+XX ",
+" XXOOo.XX+XXXX ",
+" XXXOo @ XXXXX ",
+" XXXXO@ @XXXXX ",
+" XXXX+ @ oXXXX ",
+" XXX+XX+O.oXXX ",
+" X+XXXXO.ooX ",
+" +XXXXXO..oo ",
+" XXXXXOO.. ",
+" XXXOO ",
+" ",
+" ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" @@@@@@@ ",
+" "};
diff --git a/kscd/bitmaps/db.xbm b/kscd/bitmaps/db.xbm
new file mode 100644
index 00000000..57f8514a
--- /dev/null
+++ b/kscd/bitmaps/db.xbm
@@ -0,0 +1,8 @@
+#define db_width 20
+#define db_height 16
+static unsigned char db_bits[] = {
+ 0xff, 0xff, 0x0f, 0x01, 0x00, 0x08, 0xfd, 0xff, 0x0b, 0x01, 0x00, 0x08,
+ 0x01, 0x0f, 0x08, 0x01, 0x09, 0x08, 0x01, 0x0f, 0x08, 0x01, 0x00, 0x08,
+ 0x01, 0x00, 0x08, 0x81, 0x1f, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08,
+ 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0xff, 0xff, 0x0f,
+ };
diff --git a/kscd/bitmaps/db.xpm b/kscd/bitmaps/db.xpm
new file mode 100644
index 00000000..56f82078
--- /dev/null
+++ b/kscd/bitmaps/db.xpm
Binary files differ
diff --git a/kscd/bitmaps/eject.xbm b/kscd/bitmaps/eject.xbm
new file mode 100644
index 00000000..709fc709
--- /dev/null
+++ b/kscd/bitmaps/eject.xbm
@@ -0,0 +1,7 @@
+#define eject_width 16
+#define eject_height 16
+static unsigned char eject_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07,
+ 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f,
+ 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/ff.xbm b/kscd/bitmaps/ff.xbm
new file mode 100644
index 00000000..76fdcaa4
--- /dev/null
+++ b/kscd/bitmaps/ff.xbm
@@ -0,0 +1,7 @@
+#define ff_width 16
+#define ff_height 16
+static unsigned char ff_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x18, 0x06, 0x38, 0x0e,
+ 0x78, 0x1e, 0xf8, 0x3e, 0xf8, 0x3e, 0x78, 0x1e, 0x38, 0x0e, 0x18, 0x06,
+ 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/info.xbm b/kscd/bitmaps/info.xbm
new file mode 100644
index 00000000..7359938e
--- /dev/null
+++ b/kscd/bitmaps/info.xbm
@@ -0,0 +1,6 @@
+#define info_width 16
+#define info_height 16
+static unsigned char info_bits[] = {
+ 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0xe0, 0x01, 0xe0, 0x01, 0x80, 0x01,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+ 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, };
diff --git a/kscd/bitmaps/kscdlogo.xpm b/kscd/bitmaps/kscdlogo.xpm
new file mode 100644
index 00000000..559994e5
--- /dev/null
+++ b/kscd/bitmaps/kscdlogo.xpm
@@ -0,0 +1,137 @@
+/* XPM */
+static char *kscdlogo[] = {
+/* width height num_colors chars_per_pixel */
+" 90 120 10 1",
+/* colors */
+". c #000000",
+"# c #1d1d1d",
+"a c #393939",
+"b c #555555",
+"c c #727272",
+"d c #8e8e8e",
+"e c #aaaaaa",
+"f c #c7c7c7",
+"g c #e3e3e3",
+"h c #ffffff",
+/* pixels */
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhbbbbbdhhhhcbbbbbhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhdbbbbfhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhhf.....dhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhha....#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhd.....ehhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhh#....ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhd.....fhhhhhhhhheeeghhhhhebbbehhhhhhhhhhgdbbdb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bg#....bhhhhhhhhc....ehhhe#.....#ehhhhhhhc....bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bc.....ghhhhhhhb.....ehhe.........bhhhhg#.....bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....a.....chhhhhhhf......ehe...........ehhhb......bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....#.....ehhhhhhhb......eh#...........#hhf.......bb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....ba....ahhhhhhh#......ee.....beb.....ehb.....#bbb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bd.....fhhhhhh.....#dgb....bhhheeeeghh.....ahhgb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bh.....ahhhhhh.....ehhb....ehhhhhhhhhh.....ehhhb....ehhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhc.....fhhhhd.....fhhb....bhhha....ch.....chhh#....hhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhf.....ahhda......hhhe.....#b#.....ehb.....cea....#hhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhha.....fhb......ahhhg............#hhf............chhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhe.....ahb......fhhhhd...........dhhhb..........#ghhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhh#.....fb.....bhhhhhhb.........bhhhhg#.........ehhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhh.....bhhhc.....cb....chhhhhhhhd#.....#dhhhhhhhc......aghhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhheeeeefhhhgeeeeeedbbdghhhhhhhhhhhebbcehhhhhhhhhhgebbcfhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
+"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+};
diff --git a/kscd/bitmaps/lock.xbm b/kscd/bitmaps/lock.xbm
new file mode 100644
index 00000000..0f2e6e80
--- /dev/null
+++ b/kscd/bitmaps/lock.xbm
@@ -0,0 +1,7 @@
+#define lock_width 16
+#define lock_height 16
+static unsigned char lock_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x20, 0x04, 0x20, 0x04,
+ 0x20, 0x04, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/logo.xbm b/kscd/bitmaps/logo.xbm
new file mode 100644
index 00000000..16c293bb
--- /dev/null
+++ b/kscd/bitmaps/logo.xbm
@@ -0,0 +1,28 @@
+#define logo_width 48
+#define logo_height 48
+static unsigned char logo_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x63, 0xa2, 0x23, 0xe6, 0x00, 0x9f, 0x94, 0xb6, 0x54, 0x49,
+ 0x00, 0x91, 0x90, 0xaa, 0x54, 0x41, 0x00, 0x91, 0x94, 0xaa, 0x73, 0x49,
+ 0x00, 0x11, 0x63, 0xa2, 0x50, 0x46, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
+ 0xfc, 0x91, 0x0f, 0xff, 0xc3, 0x3f, 0x02, 0x90, 0x88, 0x00, 0x22, 0x40,
+ 0x01, 0x90, 0x48, 0x00, 0x12, 0x80, 0xe1, 0x90, 0x48, 0x00, 0x12, 0x86,
+ 0x11, 0x91, 0x48, 0xfc, 0x13, 0x89, 0x11, 0x91, 0x48, 0x02, 0x10, 0x89,
+ 0x11, 0x91, 0x48, 0xfc, 0x10, 0x89, 0x11, 0x91, 0x48, 0x00, 0x11, 0xf9,
+ 0x11, 0x91, 0x48, 0x00, 0x12, 0x01, 0x11, 0x91, 0x88, 0x00, 0x12, 0xf9,
+ 0x11, 0x91, 0x08, 0x3f, 0x12, 0x89, 0x11, 0x91, 0x08, 0x40, 0x12, 0x89,
+ 0x11, 0x91, 0xc8, 0x3f, 0x12, 0x89, 0xe1, 0x90, 0x48, 0x00, 0x12, 0x86,
+ 0x01, 0x90, 0x48, 0x00, 0x12, 0x80, 0x02, 0x90, 0x48, 0x00, 0x21, 0x40,
+ 0xfc, 0x9f, 0xcf, 0xff, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x27, 0xd7, 0x49, 0x20, 0xe9, 0x64, 0xa9, 0x90, 0x54, 0x50, 0x29, 0x95,
+ 0xa9, 0x96, 0x54, 0x50, 0x29, 0x95, 0xa9, 0x94, 0x5c, 0x70, 0x29, 0x95,
+ 0x27, 0x97, 0xd4, 0x51, 0xe6, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/magic.xbm b/kscd/bitmaps/magic.xbm
new file mode 100644
index 00000000..9b19811e
--- /dev/null
+++ b/kscd/bitmaps/magic.xbm
@@ -0,0 +1,7 @@
+/* Created with The GIMP */
+#define magicxbm_width 16
+#define magicxbm_height 16
+static unsigned char magicxbm_bits[] = {
+ 0x00, 0x10, 0x00, 0x38, 0x80, 0xfe, 0x80, 0x38, 0x80, 0x10, 0xc0, 0x11,
+ 0xc0, 0x01, 0xf0, 0x07, 0xfe, 0x3f, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01,
+ 0x84, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x00, 0x00 };
diff --git a/kscd/bitmaps/nexttrk.xbm b/kscd/bitmaps/nexttrk.xbm
new file mode 100644
index 00000000..6e36d666
--- /dev/null
+++ b/kscd/bitmaps/nexttrk.xbm
@@ -0,0 +1,7 @@
+#define nexttrk_width 16
+#define nexttrk_height 16
+static unsigned char nexttrk_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x86, 0x61, 0x8e, 0x63,
+ 0x9e, 0x67, 0xbe, 0x6f, 0xbe, 0x6f, 0x9e, 0x67, 0x8e, 0x63, 0x86, 0x61,
+ 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/options.xbm b/kscd/bitmaps/options.xbm
new file mode 100644
index 00000000..760ac205
--- /dev/null
+++ b/kscd/bitmaps/options.xbm
@@ -0,0 +1,7 @@
+#define options_width 16
+#define options_height 16
+static unsigned char options_bits[] = {
+ 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x8e, 0x7f, 0xfe, 0x7f,
+ 0x8e, 0x7f, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xfc, 0x7f,
+ 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/playpaus.xbm b/kscd/bitmaps/playpaus.xbm
new file mode 100644
index 00000000..a2494705
--- /dev/null
+++ b/kscd/bitmaps/playpaus.xbm
@@ -0,0 +1,10 @@
+#define playpause_width 32
+#define playpause_height 16
+static unsigned char playpause_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0xc0, 0x71, 0x1e, 0x00, 0xc0, 0x71, 0x7e, 0x00, 0xc0, 0x71,
+ 0xfe, 0x01, 0xc0, 0x71, 0xfe, 0x07, 0xc0, 0x71, 0xfe, 0x07, 0xc0, 0x71,
+ 0xfe, 0x01, 0xc0, 0x71, 0x7e, 0x00, 0xc0, 0x71, 0x1e, 0x00, 0xc0, 0x71,
+ 0x06, 0x00, 0xc0, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/poweroff.xbm b/kscd/bitmaps/poweroff.xbm
new file mode 100644
index 00000000..f332ef22
--- /dev/null
+++ b/kscd/bitmaps/poweroff.xbm
@@ -0,0 +1,7 @@
+#define poweroff_width 16
+#define poweroff_height 16
+static unsigned char poweroff_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x18, 0xfe, 0x10, 0xfe, 0x10, 0xfe, 0x10,
+ 0xfe, 0x10, 0x82, 0x00, 0x82, 0x38, 0x82, 0x44, 0x82, 0x44, 0x82, 0x44,
+ 0xfe, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/prevtrk.xbm b/kscd/bitmaps/prevtrk.xbm
new file mode 100644
index 00000000..1cf3b803
--- /dev/null
+++ b/kscd/bitmaps/prevtrk.xbm
@@ -0,0 +1,7 @@
+#define prevtrk_width 16
+#define prevtrk_height 16
+static unsigned char prevtrk_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x86, 0x61, 0xc6, 0x71,
+ 0xe6, 0x79, 0xf6, 0x7d, 0xf6, 0x7d, 0xe6, 0x79, 0xc6, 0x71, 0x86, 0x61,
+ 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/repeat.xbm b/kscd/bitmaps/repeat.xbm
new file mode 100644
index 00000000..3e6272cf
--- /dev/null
+++ b/kscd/bitmaps/repeat.xbm
@@ -0,0 +1,7 @@
+#define repeat_width 16
+#define repeat_height 16
+static unsigned char repeat_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x13,
+ 0x0c, 0x31, 0x04, 0x20, 0x04, 0x20, 0x8c, 0x30, 0xc8, 0x1f, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/rew.xbm b/kscd/bitmaps/rew.xbm
new file mode 100644
index 00000000..79aa0db3
--- /dev/null
+++ b/kscd/bitmaps/rew.xbm
@@ -0,0 +1,7 @@
+#define rew_width 16
+#define rew_height 16
+static unsigned char rew_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x60, 0x18, 0x70, 0x1c,
+ 0x78, 0x1e, 0x7c, 0x1f, 0x7c, 0x1f, 0x78, 0x1e, 0x70, 0x1c, 0x60, 0x18,
+ 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/shuffle.xbm b/kscd/bitmaps/shuffle.xbm
new file mode 100644
index 00000000..7214cd0a
--- /dev/null
+++ b/kscd/bitmaps/shuffle.xbm
@@ -0,0 +1,7 @@
+#define shuffle_width 16
+#define shuffle_height 16
+static unsigned char shuffle_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xcc, 0x00,
+ 0x84, 0x0c, 0xc0, 0x3c, 0x60, 0x3c, 0x30, 0x0c, 0x00, 0x00, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bitmaps/stop.xbm b/kscd/bitmaps/stop.xbm
new file mode 100644
index 00000000..1f176d13
--- /dev/null
+++ b/kscd/bitmaps/stop.xbm
@@ -0,0 +1,7 @@
+#define stop_width 16
+#define stop_height 16
+static unsigned char stop_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/kscd/bwlednum.cpp b/kscd/bwlednum.cpp
new file mode 100644
index 00000000..a8f828cc
--- /dev/null
+++ b/kscd/bwlednum.cpp
@@ -0,0 +1,591 @@
+/*
+ * BW_LED_Number a very very primitive LED
+ *
+ * Copyright: Bernd Johannes Wuebben, wuebben@math.cornell.edu
+ *
+ * $Id$
+ *
+ */
+
+
+#include "bwlednum.h"
+#include "qbitarray.h"
+#include "qpainter.h"
+#include <stdio.h>
+
+#include "bwlednum.moc"
+
+#define NUM_OF_SEGMENTS 8
+#define STOP_CHAR 25
+
+
+static char segs[14][8] =
+{ { 0, 1, 2, 4, 5, 6,25, 0}, // 0
+ { 2, 5,25, 0, 0, 0, 0, 0}, // 1
+ { 1, 2, 3, 4, 6,25 ,0, 0}, // 2
+ { 1, 2, 3, 5, 6,25, 0, 0}, // 3
+ { 0, 3, 2, 5 ,25, 0, 0, 0}, // 4
+ { 0, 1, 3, 5, 6,25, 0, 0}, // 5
+ { 0, 1, 3, 4, 5, 6,25, 0}, // 6
+ { 1,2 , 5,25, 0, 0, 0, 0}, // 7
+ { 0, 1, 2, 3, 4, 5, 6,25}, // 8
+ { 0, 1, 2, 3, 5, 6,25, 0}, // 9
+ { 3,25, 0, 0, 0, 0, 0, 0}, // -
+ { 7,25, 0, 0, 0, 0, 0, 0}, // .
+ { 8, 9,25, 0, 0, 0, 0, 0}, // :
+ {25, 0, 0, 0, 0, 0, 0, 0} }; // blank
+
+
+BW_LED_Number::BW_LED_Number( QWidget *parent, const char *name )
+ : QFrame( parent, name ){
+
+
+ offcolor = QColor(100,0,0);
+ showOffColon(FALSE);
+ smallLED = false;
+ current_symbol = ' ';
+ old_segments = &segs[13][0]; // nothing
+ current_segments = &segs[13][0]; // nothing
+ setLEDColor(yellow,black);
+
+}
+
+void dump_segments(char * segs){
+
+ printf("dumping segments:");
+ for (int i = 0; i <=7; i++){
+ printf("%d:",segs[i]);
+ }
+ printf("\n");
+}
+
+
+BW_LED_Number::~BW_LED_Number(){
+
+}
+
+void BW_LED_Number::resizeEvent( QResizeEvent * ){
+
+}
+
+void BW_LED_Number::mouseReleaseEvent( QMouseEvent * /* e */ )
+{
+ emit(clicked());
+}
+
+void BW_LED_Number::showOffColon(bool off){
+
+ show_off_colon = off;
+
+}
+
+void BW_LED_Number::setLEDColor( const QColor& fgColor, const QColor& bgColor ){
+
+ fgcolor = fgColor;
+ bgcolor = bgColor;
+
+ QColorGroup old_cg = this->colorGroup();
+
+
+ QColorGroup new_cg( fgColor, bgColor,
+ fgColor, fgColor, fgColor,
+ fgColor, fgColor );
+
+
+ this->setPalette(QPalette(new_cg, new_cg, new_cg));
+
+}
+
+
+
+
+static char *getSegments( char s)
+
+{
+ if (s >= '0' && s <= '9'){
+ return segs[s - '0'];
+ }
+
+ int j;
+
+ switch ( s ) {
+ case '-':
+ j = 10;
+ break;
+ case 'O':
+ j = 0;
+ break;
+ case '.':
+ j = 11;
+ break;
+ case ':':
+ j = 12;
+ break;
+ default:
+ j = 13;
+ break;
+ }
+
+ return segs[j];
+}
+
+void BW_LED_Number::drawContents( QPainter *p ){
+
+ drawSymbol( p, current_symbol,TRUE );
+
+}
+
+
+void BW_LED_Number::display(int i ){
+
+ if( (i<0) || (i> 9))
+ return;
+ display( (char)('0'+ i));
+
+}
+
+
+void BW_LED_Number::display(char s){
+
+ QPainter p;
+
+ p.begin( this );
+
+ old_segments = current_segments;
+ current_symbol = s;
+ current_segments = getSegments(s);
+
+ drawSymbol(&p,s,FALSE);
+
+ p.end();
+
+}
+
+void BW_LED_Number::setSmallLED(bool a_boolean){
+
+ smallLED = a_boolean;
+
+}
+
+
+void BW_LED_Number::drawSymbol( QPainter *p,char ,bool repaint ){
+
+ // printf("drawSymbol repaint = %d\n",repaint);
+
+ QPoint pos;
+
+ int xSegment_Length, ySegment_Length, Segment_Length, xAdvance;
+ int Xoffset, Yoffset, space;
+
+ space = 1;
+
+ xSegment_Length = width()*5/((5 + space) + space) ;
+
+ ySegment_Length = height()*5/12;
+
+ Segment_Length = ySegment_Length > xSegment_Length ? xSegment_Length : ySegment_Length;
+
+ xAdvance = Segment_Length*( 5 + space )/5 +1 ;
+
+ // Xoffset = ( width() - xAdvance + Segment_Length/5 )/2; // origininal
+ Xoffset = ( width() - xAdvance + Segment_Length/4 )/2;
+ Yoffset = ( height() - Segment_Length*2 )/2;
+
+ pos = QPoint( Xoffset , Yoffset );
+
+
+ if(repaint){
+
+ // this draw the non-illumintated segments
+
+ if(show_off_colon){// we want to show the colon which is actually ugly and
+ // by default not shown.
+
+ for(int l = 0; l <= NUM_OF_SEGMENTS +1; l++){
+ drawSegment(pos,(char) l,*p,Segment_Length,TRUE); //erase segment
+ }
+ }
+ else{
+ for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){
+ drawSegment(pos,(char) l,*p,Segment_Length,TRUE); //erase segment
+ }
+ }
+
+ // now draw the illuminated segments
+
+ for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){
+ if(current_segments[l] != STOP_CHAR){
+ drawSegment(pos,current_segments[l],*p,Segment_Length,FALSE); // draw segment
+ }
+ else{
+ break;
+ }
+ }
+ }
+ else{ // we are not repainting ourselves due to a repaint event but rather
+ // genuinely changing the symbol that is to be displayed
+
+ for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){
+
+ if(current_segments[l] != STOP_CHAR){
+ if(!seg_contained_in(current_segments[l],old_segments))
+ drawSegment(pos,current_segments[l],*p,Segment_Length,FALSE); // draw segment
+ }
+ else{
+ break;
+ }
+ }
+
+
+ for(int k = 0; k <= NUM_OF_SEGMENTS -1; k++){
+
+ if(old_segments[k] != STOP_CHAR){
+ if(!seg_contained_in(old_segments[k],current_segments))
+ drawSegment(pos,old_segments[k],*p,Segment_Length,TRUE); //erase segment
+ }
+ else{
+ break;
+ }
+ }
+ }
+}
+
+
+
+bool BW_LED_Number::seg_contained_in( char c, char* seg){
+
+ bool result = FALSE;
+
+ while ( *seg != STOP_CHAR){
+ // printf("Comparing %d with %d\n",c,*seg);
+ if ( c == *seg )
+ result = TRUE;
+ seg++;
+ }
+
+ return result;
+}
+
+void BW_LED_Number::setLEDoffColor(QColor color){
+
+ offcolor = color;
+}
+
+
+void BW_LED_Number::drawSegment( const QPoint &pos, char seg_number, QPainter &p,
+ int Segment_Length, bool erase){
+
+
+ QPoint pt = pos;
+ QColorGroup g = colorGroup();
+ QColor lightColor,darkColor;
+ if ( erase ){
+
+ lightColor = offcolor;
+ darkColor = offcolor;
+
+ } else {
+ lightColor = g.light();
+ darkColor = g.dark();
+ }
+
+ // int Width = (int) Segment_Length/5 ; // original
+ int Width = (int) Segment_Length/4;
+
+
+ QBrush brush(g.light());
+ QPointArray pts;
+
+
+ pt.ry() += (QCOORD)Width/2;
+
+
+ if (erase){
+
+ p.setBrush(offcolor);
+ brush.setColor(offcolor);
+
+ }
+ else
+ p.setBrush(g.light());
+
+ if(!smallLED){
+
+ switch ( seg_number ) {
+ case 0 :
+
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x(), pt.y() ,
+ pt.x(), pt.y()-Width +1,
+ pt.x() + Width-1, pt.y());
+ p.drawPolygon(pts);
+ pts.setPoints(3,pt.x(), pt.y() + Segment_Length -Width - Width/2 -1 ,
+ pt.x() + Width -1 , pt.y() -Width +Segment_Length - Width/2 -1,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 -1);
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ p.fillRect(pt.x(),pt.y()+ Width/2 -1, Width ,
+ Segment_Length - Width -Width +1 ,brush);
+
+ break;
+ case 1 :
+
+ p.fillRect(pt.x()+Width,pt.y()- Width , Segment_Length -2* Width, Width ,brush);
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x()+1, pt.y()-Width ,
+ pt.x()+Width, pt.y()-Width ,
+ pt.x() + Width, pt.y() -1 );
+ p.drawPolygon(pts);
+
+ pts.setPoints(3,pt.x()+ Segment_Length - Width , pt.y() - Width,
+ pt.x()+ Segment_Length -1, pt.y() - Width,
+ pt.x() + Segment_Length - Width , pt.y() -1 );
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+ break;
+ case 2 :
+ pt.rx() += (QCOORD)(Segment_Length);
+
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x() , pt.y() ,
+ pt.x() , pt.y() - Width + 1, // changes from 1 to 2
+ pt.x() - Width +1, pt.y() );
+
+ p.drawPolygon(pts);
+
+ pts.setPoints(3,pt.x() , pt.y() + Segment_Length - Width - Width/2 -1,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 - 1,
+ pt.x() - Width +1, pt.y() + Segment_Length - Width - Width/2 -1);
+
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ p.fillRect(pt.x() - Width+1 ,pt.y() + Width/2- 1, Width ,
+ Segment_Length - Width - Width + 1 ,brush);
+
+ break;
+ case 3 :
+
+ pt.ry() += (QCOORD)Segment_Length;
+
+ p.setPen(g.background());
+
+ pts.setPoints(3,pt.x()-1 , pt.y() - Width/2 -1,
+ pt.x() + Width+2, pt.y()-Width -1 ,//
+ pt.x() + Width+2, pt.y() );
+ p.drawPolygon(pts);
+ pts.setPoints(3,pt.x() + Segment_Length + 1, pt.y() - Width/2 -1 ,
+ pt.x() + Segment_Length - Width - 2 ,
+ pt.y() - Width -1,
+ pt.x() + Segment_Length - Width - 2, pt.y() );
+ p.drawPolygon(pts);
+
+ p.setPen(g.light());
+ p.fillRect(pt.x() + Width -1 ,pt.y() - Width, Segment_Length- 2* Width + 3,
+ Width ,brush);
+
+ break;
+ case 4 :
+ pt.ry() += (QCOORD)(Segment_Length +1);
+ p.fillRect(pt.x(), pt.y(), Width , Segment_Length - Width - Width/2 ,brush);
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x(), pt.y(),
+ pt.x(), pt.y()-Width+1,
+ pt.x() + Width-1, pt.y());
+ p.drawPolygon(pts);
+ pts.setPoints(3,pt.x(), pt.y() + Segment_Length -Width - Width/2 -1 ,
+ pt.x() + Width -1 , pt.y() -Width +Segment_Length - Width/2 -1 ,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 -1);
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ break;
+ case 5 :
+ pt.rx() += (QCOORD)(Segment_Length );
+ pt.ry() += (QCOORD)(Segment_Length +1);
+ p.fillRect(pt.x() - Width +1 ,pt.y(), Width ,
+ Segment_Length - Width - Width/2 ,brush);
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x() , pt.y(),
+ pt.x() , pt.y() - Width +1,
+ pt.x() - Width +1, pt.y());
+
+ p.drawPolygon(pts);
+
+ pts.setPoints(3,pt.x() , pt.y() + Segment_Length - Width - Width/2 -1,
+ pt.x() , pt.y() + Segment_Length - 3*Width/4 -1,
+ pt.x() - Width +1, pt.y() + Segment_Length - Width - Width/2 -1);
+
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+ break;
+ case 6 :
+ pt.ry() += (QCOORD)(Segment_Length*2 );
+ p.fillRect(pt.x() + Width ,pt.y() -Width , Segment_Length -2* Width ,
+ Width ,brush);
+
+ if (erase)
+ p.setPen(offcolor);
+
+ pts.setPoints(3,pt.x()+1, pt.y()-1,
+ pt.x() + Width, pt.y() - Width,
+ pt.x() + Width, pt.y() - 1 );
+ p.drawPolygon(pts);
+
+ pts.setPoints(3, pt.x() + Segment_Length - 1, pt.y()-1,
+ pt.x() + Segment_Length - Width , pt.y() - Width,
+ pt.x() + Segment_Length - Width , pt.y() - 1 );
+
+ p.drawPolygon(pts);
+
+ if (erase)
+ p.setPen(g.light());
+
+
+
+ break;
+ case 7 :
+ pt.rx() += (QCOORD)(Segment_Length/2);
+ pt.ry() += (QCOORD)(Segment_Length*2);
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ break;
+ case 8 :
+ pt.ry() += (QCOORD)(Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ p.moveTo(pt);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+
+ break;
+ case 9 :
+ pt.ry() += (QCOORD)(3*Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+ break;
+ }
+
+ } /* if (!smallLED) */
+
+ else{
+
+ pt.ry() += (QCOORD)Width/2;
+
+ switch ( seg_number ) {
+ case 0 :
+ p.fillRect(pt.x(),pt.y()+ Width /2, Width , Segment_Length - Width -Width/2 ,brush);
+ break;
+ case 1 :
+ p.fillRect(pt.x()+Width,pt.y()- Width , Segment_Length -2* Width, Width ,brush);
+ break;
+ case 2 :
+ pt.rx() += (QCOORD)(Segment_Length);
+ p.fillRect(pt.x()-Width,pt.y()+ Width/2, Width ,
+ Segment_Length - Width -Width/2 ,brush);
+ break;
+ case 3 :
+ pt.ry() += (QCOORD)Segment_Length;
+ p.fillRect(pt.x() + Width ,pt.y() - Width, Segment_Length- 2* Width, Width ,brush);
+ break;
+ case 4 :
+ pt.ry() += (QCOORD)(Segment_Length );
+ p.fillRect(pt.x(), pt.y(), Width , Segment_Length - Width - Width/2 ,brush);
+ break;
+ case 5 :
+ pt.rx() += (QCOORD)(Segment_Length );
+ pt.ry() += (QCOORD)(Segment_Length );
+ p.fillRect(pt.x() - Width ,pt.y(), Width ,
+ Segment_Length - Width - Width/2 ,brush);
+ break;
+ case 6 :
+ pt.ry() += (QCOORD)(Segment_Length*2);
+ p.fillRect(pt.x() + Width ,pt.y() -Width , Segment_Length -2* Width ,
+ Width ,brush);
+ break;
+ case 7 :
+ pt.rx() += (QCOORD)(Segment_Length/2);
+ pt.ry() += (QCOORD)(Segment_Length*2);
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ break;
+ case 8 :
+ pt.ry() += (QCOORD)(Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ p.moveTo(pt);
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+
+ break;
+ case 9 :
+ pt.ry() += (QCOORD)(3*Segment_Length/2 + Width/2);
+ pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1);
+
+ if (!show_off_colon && erase) {
+ p.setBrush(bgcolor);
+ brush.setColor(bgcolor);
+ }
+ p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush);
+ if (!show_off_colon && erase) {
+ p.setBrush(fgcolor);
+ brush.setColor(fgcolor);
+ }
+
+ break;
+ }
+
+ } /* end smallLED */
+
+
+}
diff --git a/kscd/bwlednum.h b/kscd/bwlednum.h
new file mode 100644
index 00000000..f3d66f8e
--- /dev/null
+++ b/kscd/bwlednum.h
@@ -0,0 +1,93 @@
+/*
+ *
+ * BW_LED_Number a very very primitive LED
+ *
+ * Copyright: Bernd Johannes Wuebben, wuebben@math.cornell.edu
+ *
+ *
+ * $Id$
+ *
+ */
+
+
+#ifndef BW_LED_NUM_H
+#define BW_LED_NUM_H
+
+#include "qframe.h"
+#include "qbitarray.h"
+
+
+class BW_LED_Number : public QFrame
+{
+ Q_OBJECT
+
+public:
+
+ BW_LED_Number( QWidget *parent=0, const char *name=0 );
+ ~BW_LED_Number();
+
+ void setSmallLED(bool ); // if you LED is small it might look better
+ // if you call setSmallLED(TRUE)
+
+ // this sets the fore and background color of the LED
+ // the forground defaults to yellow, the background defaults
+ // to black
+
+ void setLEDColor( const QColor& foregroundColor, const QColor& backgroundColor );
+
+
+ // this sets the color of the segments that are not iluminated
+ // the default is a rather dark red.
+
+ void setLEDoffColor(QColor color);
+
+ // calling showOffColon(TRUE) will show the colon if not illuminated
+ // this is rather ugly -- the default is that they are not shown.
+
+ void showOffColon(bool off);
+
+signals:
+ void clicked();
+
+public slots:
+
+ // display one of the characters " 0 1 2 3 4 5 6 7 8 9 . : - "
+ void display( char c );
+
+ // display on e of the numbers " 0 1 2 3 4 5 6 7 8 9"
+ void display( int i );
+
+protected:
+
+ void resizeEvent( QResizeEvent * );
+ void mouseReleaseEvent ( QMouseEvent * e );
+ void drawContents( QPainter * );
+
+private:
+
+ bool seg_contained_in( char c, char* seg);
+ void drawSegment( const QPoint &, char, QPainter &, int, bool = FALSE );
+ void drawSymbol( QPainter *p,char s ,bool repaint);
+
+ char* old_segments;
+ char* current_segments;
+
+ char current_symbol;
+ char old_symbol;
+ QColor offcolor;
+ QColor fgcolor;
+ QColor bgcolor;
+
+
+ bool smallLED;
+ bool show_off_colon;
+
+private: // Disabled copy constructor and operator=
+
+ BW_LED_Number( const BW_LED_Number & );
+ BW_LED_Number &operator=( const BW_LED_Number & );
+
+};
+
+
+#endif // BW_LED_NUM_H
diff --git a/kscd/cddaslave.c b/kscd/cddaslave.c
new file mode 100644
index 00000000..6b65c640
--- /dev/null
+++ b/kscd/cddaslave.c
@@ -0,0 +1,532 @@
+/*
+ * @(#)cddaslave.c 1.11 13 Sep 1995
+ *
+ * Digital audio manipulator for WorkMan.
+ *
+ * The CDDA architecture looks like this:
+ *
+ * WorkMan (or another UI!)
+ * ^^^
+ * ||| (separate processes connected by pipe)
+ * vvv
+ * +------------- cddaslave -------------+
+ * | | |
+ * command module CDDA reader audio output
+ * (portable) (per platform) (per platform)
+ *
+ * This source file has the command module and some of the scaffolding
+ * to hold cddaslave together, plus some non-system-dependent audio
+ * processing code. Look in plat_*_cdda.c for system-specific stuff.
+ */
+
+
+#include "libwm/include/wm_config.h"
+#include "libwm/include/wm_cdda.h"
+
+#ifdef BUILD_CDDA /* { */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifndef timerclear
+#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#endif
+
+int playing = 0; /* Should the CD be playing now? */
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+int loudness = 0;
+unsigned int volume = 32768;
+unsigned int level;
+
+/*
+ * Playback speed (0 = slow)
+ */
+int speed = 128;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+FILE *output = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+#ifdef BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(x);
+#endif
+
+void *malloc();
+long cdda_transform();
+
+/*
+ * Send status information upstream.
+ */
+void
+send_status(struct cdda_block *blk)
+{
+ write(1, blk, sizeof(*blk));
+}
+
+/*
+ * Accept a command from our master.
+ *
+ * The protocol is byte-oriented:
+ * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
+ * xyz is the msf of the start of this chunk, i.e., the
+ * ending boundary for reverse play.
+ * S Stop.
+ * Q Quit.
+ * Vn Set volume level (0-255).
+ * Bn Set balance level (0-255).
+ * EnL Set an equalizer level (n = 0 for bass, 255 for treble)
+ * G Get current status.
+ * sn Set speed multiplier to n.
+ * dn Set direction to forward (n = 0) or reverse.
+ * Fllllx... Start saving to a file (length = l, followed by name)
+ * F0000 Stop saving.
+ * Ln Set loudness level (0-255).
+ */
+void
+command(int cd_fd, struct cdda_block *blk)
+{
+ unsigned char inbuf[10];
+ char *filename;
+ int namelen;
+ struct auheader hdr;
+
+ if (read(0, inbuf, 1) <= 0) /* Parent died. */
+ {
+ wmcdda_close();
+ wmaudio_close();
+ exit(0);
+ }
+
+ switch (inbuf[0]) {
+ case 'P':
+ read(0, inbuf, 9);
+ playing = 1;
+
+ wmaudio_stop();
+
+ wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
+ inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
+ inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
+
+ wmaudio_ready();
+
+ level = 2500;
+ volume = 1 << 15;
+
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'S':
+ playing = 0;
+ wmaudio_stop();
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ blk->status = WMCDDA_STOPPED;
+ send_status(blk);
+ break;
+
+ case 'B':
+ read(0, inbuf, 1);
+ wmaudio_balance(inbuf[0]);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'V':
+ read(0, inbuf, 1);
+ wmaudio_volume(inbuf[0]);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'G':
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+
+ if (playing)
+ blk->status = WMCDDA_PLAYED;
+ else
+ blk->status = WMCDDA_STOPPED;
+ wmaudio_state(blk);
+ send_status(blk);
+ break;
+
+ case 'Q':
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ wmcdda_close();
+ wmaudio_close();
+ exit(0);
+
+ case 's':
+ read(0, inbuf, 1);
+ speed = inbuf[0];
+ wmcdda_speed(speed);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'd':
+ read(0, inbuf, 1);
+ wmcdda_direction(inbuf[0]);
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'L':
+ read(0, inbuf, 1);
+ loudness = inbuf[0];
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ break;
+
+ case 'F':
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL)
+ {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen)
+ {
+ filename = malloc(namelen + 1);
+ if (filename == NULL)
+ {
+ perror("cddaslave");
+ wmcdda_close();
+ wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL)
+ perror(filename);
+ else
+ {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1,
+ output);
+ }
+
+ free(filename);
+ }
+
+ blk->status = WMCDDA_ACK;
+ send_status(blk);
+ }
+}
+
+/*
+ * Transform some CDDA data.
+ */
+long
+wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+ long i;
+ long *buf32 = (long *)rawbuf;
+ register short *buf16 = (short *)rawbuf;
+ register int aval;
+
+ /*
+ * Loudness transformation. Basically this is a self-adjusting
+ * volume control; our goal is to keep the average output level
+ * around a certain value (2500 seems to be pleasing.) We do this
+ * by maintaining a decaying average of the recent output levels
+ * (where "recent" is some fraction of a second.) All output levels
+ * are multiplied by the inverse of the decaying average; this has
+ * the volume-leveling effect we desire, and isn't too CPU-intensive.
+ *
+ * This is done by modifying the digital data, rather than adjusting
+ * the system volume control, because (at least on some systems)
+ * tweaking the system volume can generate little pops and clicks.
+ *
+ * There's probably a more elegant way to achieve this effect, but
+ * what the heck, I never took a DSP class and am making this up as
+ * I go along, with a little help from some friends.
+ *
+ * This is all done with fixed-point math, oriented around powers
+ * of two, which with luck will keep the CPU usage to a minimum.
+ * More could probably be done, for example using lookup tables to
+ * replace multiplies and divides; whether the memory hit (128K
+ * for each table) is worthwhile is unclear.
+ */
+ if (loudness)
+ {
+ /* We aren't really going backwards, but i > 0 is fast */
+ for (i = buflen / 2; i > 0; i--)
+ {
+ /*
+ * Adjust this sample to the current level.
+ */
+ aval = (*buf16 = (((long)*buf16) * volume) >> 15);
+ buf16++;
+
+ /*
+ * Don't adjust the decaying average for each sample;
+ * that just spends CPU time for very little benefit.
+ */
+ if (i & 127)
+ continue;
+
+ /*
+ * We want to use absolute values to compute the
+ * decaying average; otherwise it'd sit around 0.
+ */
+ if (aval < 0)
+ aval = -aval;
+
+ /*
+ * Adjust more quickly when we start hitting peaks,
+ * or we'll get clipping when there's a sudden loud
+ * section after lots of quiet.
+ */
+ if (aval & ~8191)
+ aval <<= 3;
+
+ /*
+ * Adjust the decaying average.
+ */
+ level = ((level << 11) - level + aval) >> 11;
+
+ /*
+ * Let *really* quiet sounds play softly, or we'll
+ * amplify background hiss to full volume and blast
+ * the user's speakers when real sound starts up.
+ */
+ if (! (level & ~511))
+ level = 512;
+
+ /*
+ * And adjust the volume setting using the inverse
+ * of the decaying average.
+ */
+ volume = (2500 << 15) / level;
+ }
+ }
+
+ if (speed == 128)
+ return (buflen);
+
+ /*
+ * Half-speed play. Stretch things out.
+ */
+ if (speed == 0)
+ {
+ buflen /= 2; /* Since we're moving 32 bits at a time */
+
+ for (i = buflen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[i / 2];
+ }
+
+ buflen *= 4; /* 2 for doubling the buffer, 2 from above */
+ }
+
+ /*
+ * Slow play; can't optimize it as well as half-speed.
+ */
+ if (speed && speed < 128)
+ {
+ int multiplier = ((speed + 128) * 128) / 256;
+ int newlen;
+ int tally = 0, pos;
+
+ buflen /= 4; /* Get the number of 32-bit values */
+
+ /*
+ * Buffer length doubles when speed is 0, stays the same
+ * when speed is 128.
+ */
+ newlen = (buflen * 128) / multiplier;
+
+ pos = buflen - 1;
+ for (i = newlen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[pos];
+ tally += multiplier;
+ if (tally & 128)
+ {
+ pos--;
+ tally ^= 128;
+ }
+ }
+
+ buflen = newlen * 4;
+ }
+
+ return (buflen);
+}
+
+
+main(argc, argv)
+ char **argv;
+{
+ int cd_fd = 3;
+ fd_set readfd, dummyfd;
+ struct timeval timeout;
+ char *cddabuf;
+ long cddabuflen;
+ struct cdda_block blockinfo;
+ long result;
+ int nfds;
+ char *devname;
+
+ /*
+ * Device name should be the command-line argument.
+ */
+ if (argc < 2)
+ devname = "";
+ else
+ devname = argv[1];
+
+ /*
+ * If we're running setuid root, bump up our priority then lose
+ * superuser access.
+ */
+ nice(-14);
+ setgid(getgid());
+ setuid(getuid());
+ if (getuid() != geteuid())
+ return 255;
+
+ FD_ZERO(&dummyfd);
+ FD_ZERO(&readfd);
+
+ timerclear(&timeout);
+
+ cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
+ if (cd_fd < 0)
+ exit(1);
+ wmaudio_init();
+
+ blockinfo.status = WMCDDA_ACK;
+ send_status(&blockinfo);
+ blockinfo.status = WMCDDA_STOPPED;
+
+fprintf(stderr,"cddaslave: done init.");
+ /*
+ * Accept commands as they come in, and play some sound if we're
+ * supposed to be doing that.
+ */
+ while (1)
+ {
+ FD_SET(0, &readfd);
+
+ /*
+ * If we're playing, we don't want select to block. Otherwise,
+ * wait a little while for the next command.
+ */
+ if (playing)
+ timeout.tv_usec = 0;
+ else
+ timeout.tv_usec = 500000;
+
+ nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
+
+ if (nfds < 0) /* Broken pipe; our GUI exited. */
+ {
+ wmcdda_close(cd_fd);
+ wmaudio_close();
+ fprintf(stderr,"cddaslave: Borken pipe; GUI must have exited.");
+ exit(0);
+ }
+
+ if (FD_ISSET(0, &readfd))
+ {
+ command(cd_fd, &blockinfo);
+ /*
+ * Process all commands in rapid succession, rather
+ * than possibly waiting for a CDDA read.
+ */
+ continue;
+ }
+
+ if (playing)
+ {
+ result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
+ &blockinfo);
+ if (result <= 0)
+ {
+ /* Let the output queue drain. */
+ if (blockinfo.status == WMCDDA_DONE)
+ {
+ wmaudio_mark_last();
+ if (wmaudio_send_status())
+ {
+ /* queue drained, stop polling*/
+ playing = 0;
+ }
+ }
+ else
+ {
+ playing = 0;
+ send_status(&blockinfo);
+ }
+ }
+ else
+ {
+ result = wmcdda_normalize(cddabuf, result,
+ &blockinfo);
+ result = wmcdda_transform(cddabuf, result,
+ &blockinfo);
+ if (output)
+ fwrite(cddabuf, result, 1, output);
+ result = wmaudio_convert(cddabuf, result,
+ &blockinfo);
+ if (wmaudio_play(cddabuf, result, &blockinfo))
+ {
+ playing = 0;
+ wmaudio_stop();
+ send_status(&blockinfo);
+ }
+ }
+ }
+ else
+ send_status(&blockinfo);
+ }
+}
+
+#else /* BUILD_CDDA } { */
+
+#include <stdio.h>
+int main()
+{
+ printf("cddaslave: will work only on Solaris 2.4 or newer.");
+ exit(0);
+}
+
+#endif /* } */
diff --git a/kscd/cddbdlg.cpp b/kscd/cddbdlg.cpp
new file mode 100644
index 00000000..cc0e9669
--- /dev/null
+++ b/kscd/cddbdlg.cpp
@@ -0,0 +1,198 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <qkeycode.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <klistview.h>
+#include <klineedit.h>
+#include <knuminput.h>
+
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kmessagebox.h>
+
+#include <stdio.h>
+#include <math.h>
+
+#include "version.h"
+#include "kscd.h"
+#include "cddbdlg.h"
+#include "libkcddb/cdinfodialogbase.h"
+
+struct mytoc
+{
+ unsigned absframe;
+};
+
+CDDBDlg::CDDBDlg( QWidget* parent, const char* name )
+ : KDialogBase( parent, name, false, i18n( "CD Editor" ),
+ Ok|Cancel|User1|User2, Ok, true )
+{
+ KGlobal::locale()->insertCatalogue("libkcddb");
+
+ m_dlgBase = new CDInfoDialogBase( this, "m_dlgBase" );
+
+ setMainWidget( m_dlgBase );
+
+ setButtonText( User1, i18n( "Upload" ) );
+ setButtonText( User2, i18n( "Fetch Info" ) );
+
+ connect( this, SIGNAL( okClicked() ), SLOT( save() ) );
+ connect( this, SIGNAL( user1Clicked() ), SLOT( upload() ) );
+ connect( this, SIGNAL( user2Clicked() ), SIGNAL( cddbQuery() ) );
+ connect( m_dlgBase, SIGNAL( play( int ) ), SIGNAL( play( int ) ) );
+
+ cddbClient = new KCDDB::Client();
+ cddbClient->setBlockingMode(false);
+ connect (cddbClient, SIGNAL(finished(CDDB::Result)),
+ SLOT(submitFinished(CDDB::Result)));
+}
+
+
+CDDBDlg::~CDDBDlg()
+{
+ delete cddbClient;
+}
+
+void CDDBDlg::setData(
+ const KCDDB::CDInfo &_cddbInfo,
+ const KCDDB::TrackOffsetList &_trackStartFrames,
+ const QStringList &_playlist)
+{
+ // Let's make a deep copy of the cd struct info so that the data won't
+ // change the cd changes while we are playing with the dialog.
+ cddbInfo = _cddbInfo;
+ trackStartFrames = _trackStartFrames;
+ playlist = _playlist;
+
+ // Write the complete record to the dialog.
+ m_dlgBase->setInfo(cddbInfo, trackStartFrames);
+ // FIXME: KDE4, move this logic into m_dlgBase->setInfo() once KCDDB:CDInfo is updated.
+ m_dlgBase->m_playOrder->setText( playlist.join( "," ) );
+} // setData
+
+void CDDBDlg::submitFinished(KCDDB::CDDB::Result r)
+{
+ if (r == KCDDB::CDDB::Success)
+ {
+ KMessageBox::information(this, i18n("Record submitted successfully."),
+ i18n("Record Submission"));
+ }
+ else
+ {
+ QString str = i18n("Error sending record.\n\n%1")
+ .arg(KCDDB::CDDB::resultToString(r));
+ KMessageBox::error(this, str, i18n("Record Submission"));
+ }
+} // submitFinished()
+
+void CDDBDlg::upload()
+{
+ if (!validInfo())
+ return;
+
+ updateFromDialog();
+
+ // Create a copy with a bumped revision number.
+ KCDDB::CDInfo copyInfo = cddbInfo;
+ copyInfo.revision++;
+ cddbClient->submit(copyInfo, trackStartFrames);
+} // upload
+
+void CDDBDlg::save()
+{
+ updateFromDialog();
+
+ KCDDB::Cache::store(cddbInfo);
+
+ emit newCDInfoStored(cddbInfo);
+} // save
+
+bool CDDBDlg::validInfo()
+{
+ KCDDB::CDInfo copy = m_dlgBase->info();
+
+ if (copy.artist.isEmpty())
+ {
+ KMessageBox::sorry(this,
+ i18n("The artist name of the disc has to be entered.\n"
+ "Please correct the entry and try again."),
+ i18n("Invalid Database Entry"));
+ return false;
+ }
+
+ if (copy.title.isEmpty())
+ {
+ KMessageBox::sorry(this,
+ i18n("The title of the disc has to be entered.\n"
+ "Please correct the entry and try again."),
+ i18n("Invalid Database Entry"));
+ return false;
+ }
+
+ bool have_nonempty_title = false;
+ for (unsigned i = 0; i < copy.trackInfoList.count(); i++)
+ {
+ if (!copy.trackInfoList[i].title.isEmpty())
+ {
+ have_nonempty_title = true;
+ break;
+ }
+ }
+
+ if (!have_nonempty_title)
+ {
+ KMessageBox::sorry(this,
+ i18n("At least one track title must be entered.\n"\
+ "Please correct the entry and try again."),
+ i18n("Invalid Database Entry"));
+ return false;
+ }
+
+ return true;
+}
+
+void CDDBDlg::updateFromDialog()
+{
+ KCDDB::CDInfo copy = m_dlgBase->info();
+
+ // Playorder...
+ QStringList strlist = QStringList::split( ',', m_dlgBase->m_playOrder->text() );
+
+ bool ret = true;
+
+ QString teststr;
+ bool ok;
+ unsigned num;
+
+ for ( QStringList::Iterator it = strlist.begin();
+ it != strlist.end();
+ ++it )
+ {
+ teststr = *it;
+ num = teststr.toInt(&ok);
+
+ if( !ok || num > cddbInfo.trackInfoList.count() )
+ ret = false;
+ }
+
+ if(!ret)
+ {
+ KMessageBox::sorry(this,
+ i18n("Invalid Playlist\nPlease use valid track numbers, "
+ "separated by commas."));
+ }
+
+ cddbInfo = copy;
+} // updateFromDialog
+
+#include "cddbdlg.moc"
diff --git a/kscd/cddbdlg.h b/kscd/cddbdlg.h
new file mode 100644
index 00000000..bbef6e4e
--- /dev/null
+++ b/kscd/cddbdlg.h
@@ -0,0 +1,46 @@
+#ifndef CDDBDLG_H
+#define CDDBDLG_H
+
+#include <kdialogbase.h>
+
+#include "libkcddb/cdinfo.h"
+#include "libkcddb/cddb.h"
+#include "libkcddb/client.h"
+
+class CDInfoDialogBase;
+
+class CDDBDlg : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ CDDBDlg(QWidget* parent, const char* name = 0);
+ ~CDDBDlg();
+
+ void setData(
+ const KCDDB::CDInfo &_cddbInfo,
+ const KCDDB::TrackOffsetList &_trackStartFrames,
+ const QStringList &_playlist);
+
+ private slots:
+ void save();
+ void upload();
+ void submitFinished(CDDB::Result);
+
+ signals:
+ void cddbQuery();
+ void newCDInfoStored(KCDDB::CDInfo);
+ void play(int i);
+
+ private:
+ bool validInfo();
+ void updateFromDialog();
+ QString framesTime(unsigned frames);
+
+ CDInfoDialogBase *m_dlgBase;
+ KCDDB::CDInfo cddbInfo;
+ KCDDB::TrackOffsetList trackStartFrames;
+ QStringList playlist;
+ KCDDB::Client *cddbClient;
+};
+#endif // CDDBDLG_H
diff --git a/kscd/configWidget.cpp b/kscd/configWidget.cpp
new file mode 100644
index 00000000..84119c6b
--- /dev/null
+++ b/kscd/configWidget.cpp
@@ -0,0 +1,143 @@
+/*
+ * configWidget - the config dialog page for KSCD settings
+ *
+ * $Id:
+ *
+ * Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org>
+ * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <kdebug.h>
+#include <klineedit.h>
+#include <dcopref.h>
+#include <kurlrequester.h>
+#include <qcheckbox.h>
+#include <kcombobox.h>
+#include <qlayout.h>
+
+#include <config.h>
+extern "C" {
+ // We don't have libWorkMan installed already, so get everything
+ // from within our own directory
+#include "libwm/include/wm_config.h"
+}
+
+
+#include "configWidget.h"
+#include "kscd.h"
+#include "prefs.h"
+
+class SpecialComboBox : public KComboBox
+{
+public:
+ SpecialComboBox(QWidget* parent, const char* name)
+ : KComboBox(parent, name)
+ {}
+
+ // QComboBox::setCurrentText replaces the current text if
+ // the list doesn't contain text, while
+ // KComboBox::setCurrentItem doesn't
+ void setCurrentText(const QString& text)
+ {
+ setCurrentItem(text);
+ }
+} ;
+
+/*
+ * Constructs a configWidget which is a child of 'parent', with the
+ * name 'name' and widget flags set to 'f'.
+ *
+ * The dialog will by default be modeless, unless you set 'modal' to
+ * TRUE to construct a modal dialog.
+ */
+configWidget::configWidget(KSCD* player, QWidget* parent, const char* name)
+ : configWidgetUI(parent, name),
+ mPlayer(player)
+{
+ if (!name)
+ {
+ setName("configWidget");
+ }
+
+ kcfg_cdDevice->comboBox()->setEditable(true);
+ kcfg_cdDevice->comboBox()->insertItem(DEFAULT_CD_DEVICE);
+ getMediaDevices();
+
+ (new QVBoxLayout(audioSystemFrame))->setAutoAdd(true);
+ kcfg_AudioSystem = new SpecialComboBox(audioSystemFrame, "kcfg_AudioSystem");
+ textLabel4->setBuddy(kcfg_AudioSystem);
+
+#if defined(BUILD_CDDA)
+ kcfg_DigitalPlayback_toggled(Prefs::digitalPlayback());
+
+ // fill ComboBox audioBackend
+ kcfg_AudioSystem->insertStringList(mPlayer->audioSystems());
+#else
+ kcfg_DigitalPlayback_toggled(false);
+
+ kcfg_DigitalPlayback->setChecked(false);
+ kcfg_DigitalPlayback->hide();
+#endif
+ kcfg_SelectEncoding_toggled(Prefs::selectEncoding());
+}
+
+configWidget::~configWidget()
+{
+}
+
+void configWidget::kcfg_DigitalPlayback_toggled(bool toggle)
+{
+ kcfg_AudioSystem->setEnabled(toggle);
+ textLabel4->setEnabled(toggle);
+ kcfg_AudioDevice->setEnabled(toggle);
+ textLabel5->setEnabled(toggle);
+}
+
+void configWidget::getMediaDevices()
+{
+ DCOPRef ref("kded","mediamanager");
+ DCOPReply rep = ref.call("fullList()");
+ if (!rep.isValid()) {
+ return;
+ }
+ QStringList list = rep;
+ QStringList::const_iterator it = list.begin();
+ QStringList::const_iterator itEnd = list.end();
+ // it would be much better if libmediacommon was in kdelibs
+ while (it != itEnd) {
+ it++;
+ if (it == itEnd) break;
+ QString url="media:/"+(*it); // is it always right? ervin?
+ kdDebug() << "checking " << url << endl;
+ for (int i=0;i<9;i++) ++it; // go to mimetype (MIME_TYPE-NAME from medium.h)
+ kdDebug() << "Mime: " << *it << endl;
+ if (it!=itEnd && (*it)=="media/audiocd") {
+ kcfg_cdDevice->comboBox()->insertItem(url);
+ }
+ while (it !=itEnd && (*it)!="---") ++it; // go to end of current device's properties
+ ++it;
+ }
+}
+
+
+void configWidget::kcfg_SelectEncoding_toggled(bool toggle)
+{
+ kcfg_SelectedEncoding->setEnabled(toggle);
+}
+
+#include "configWidget.moc"
diff --git a/kscd/configWidget.h b/kscd/configWidget.h
new file mode 100644
index 00000000..bd0ea70f
--- /dev/null
+++ b/kscd/configWidget.h
@@ -0,0 +1,50 @@
+/*
+ * configWidget - the config dialog page for KSCD settings
+ *
+ * Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org>
+ * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef CONFIGWIDGET_H
+#define CONFIGWIDGET_H
+
+#include "configWidgetUI.h"
+
+class KSCD;
+class SpecialComboBox;
+
+class configWidget : public configWidgetUI
+{
+ Q_OBJECT
+
+ public:
+ configWidget(KSCD* player, QWidget* parent = 0, const char* name = 0);
+ ~configWidget();
+
+ protected:
+ KSCD* mPlayer;
+ SpecialComboBox* kcfg_AudioSystem;
+
+ public slots:
+ virtual void kcfg_DigitalPlayback_toggled(bool);
+ virtual void kcfg_SelectEncoding_toggled(bool);
+ private:
+ void getMediaDevices();
+};
+
+#endif // CONFIGWIDGET_H
diff --git a/kscd/configWidgetUI.ui b/kscd/configWidgetUI.ui
new file mode 100644
index 00000000..83f9aca0
--- /dev/null
+++ b/kscd/configWidgetUI.ui
@@ -0,0 +1,462 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>configWidgetUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>configWidgetUI</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>539</width>
+ <height>605</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Interface</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_BackColor</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_BackColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The background color that will be used for the LCD display.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_Docking</cstring>
+ </property>
+ <property name="text">
+ <string>Show icon in &amp;system tray</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected an icon will appear in the system tray. Note that KsCD will &lt;i&gt;not&lt;/i&gt; quit when the window is closed if a system tray icon is displayed. You may quit KsCD by clicking the Quit button or right-clicking on the system tray icon and selecting the appropriate entry.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_TrackAnnouncement</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Show &amp;track announcement</string>
+ </property>
+ </widget>
+ <spacer row="1" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>292</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;LCD color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ledColor</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_ledColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The foreground color that will be used in the LCD display.</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_ledFont</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>LCD &amp;font:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_ledFont</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Play Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntNumInput" row="0" column="1">
+ <property name="name">
+ <cstring>kcfg_SkipDelta</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="suffix">
+ <string> seconds</string>
+ </property>
+ <property name="specialValueText">
+ <string>1 second</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option controls the number of seconds KsCD will skip when the skip forwards or backwards buttons are pressed.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Skip &amp;interval:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_SkipDelta</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_Autoplay</cstring>
+ </property>
+ <property name="text">
+ <string>Auto&amp;play when CD inserted</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected the CD will start playing automatically upon being inserted into the CD-ROM.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_EjectOnFinish</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Eject CD when finished playing</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected the CD will automatically eject when it is finished.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_StopExit</cstring>
+ </property>
+ <property name="text">
+ <string>Stop playing CD on e&amp;xit</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected the CD will automatically stop playing when quitting KsCD.</string>
+ </property>
+ </widget>
+ <spacer row="0" column="2" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>171</width>
+ <height>81</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>CD-ROM &amp;Device</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLComboRequester" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_cdDevice</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The CD-ROM device to use when playing CDs. This will typically look something like "/dev/cdrom". To have KsCD autodetect your CD-ROM, leave this field empty.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Select audio de&amp;vice:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kcfg_AudioDevice</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_DigitalPlayback</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Use direct digital playback</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected KsCD will attempt to play the CD using direct digital playback. This option is useful if the CD-ROM is not connected directly to the sound output on the computer. Note that digital playback consumes more system resources than the normal method of playback.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Select &amp;audio backend:</string>
+ </property>
+ <property name="indent">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>kcfg_AudioDevice</cstring>
+ </property>
+ </widget>
+ <widget class="QFrame" row="2" column="2">
+ <property name="name">
+ <cstring>audioSystemFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox9</cstring>
+ </property>
+ <property name="title">
+ <string> Music Information Services </string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_SelectEncoding</cstring>
+ </property>
+ <property name="text">
+ <string>Allow en&amp;coding selection:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When this option is selected, you have the ability to select encoding for the results of a CDDB request. The standard describes CDDB results as being strictly Latin1. This is not true, as non-English speaking users often use other 8-bit encodings.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>AUTO</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>UTF-8</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1250</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1251</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1252</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1253</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1254</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1255</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1256</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CP1257</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>kcfg_SelectedEncoding</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="duplicatesEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>45</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_DigitalPlayback</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>configWidgetUI</receiver>
+ <slot>kcfg_DigitalPlayback_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_Docking</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_TrackAnnouncement</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_SelectEncoding</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>configWidgetUI</receiver>
+ <slot>kcfg_SelectEncoding_toggled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>kcfg_ledColor</tabstop>
+ <tabstop>kcfg_BackColor</tabstop>
+ <tabstop>kcfg_Docking</tabstop>
+ <tabstop>kcfg_TrackAnnouncement</tabstop>
+ <tabstop>kcfg_SkipDelta</tabstop>
+ <tabstop>kcfg_Autoplay</tabstop>
+ <tabstop>kcfg_EjectOnFinish</tabstop>
+ <tabstop>kcfg_StopExit</tabstop>
+ <tabstop>kcfg_cdDevice</tabstop>
+ <tabstop>kcfg_DigitalPlayback</tabstop>
+ <tabstop>kcfg_AudioDevice</tabstop>
+</tabstops>
+<slots>
+ <slot>kcfg_DigitalPlayback_toggled( bool )</slot>
+ <slot>kcfg_SelectEncoding_toggled(bool)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kscd/configure.in.in b/kscd/configure.in.in
new file mode 100644
index 00000000..9f92c1a7
--- /dev/null
+++ b/kscd/configure.in.in
@@ -0,0 +1,68 @@
+dnl +-------------------------+
+dnl | Checks for KSCD |
+dnl +-------------------------+
+AC_CHECK_LIB(cdaudio, main, [LIBCDAUDIO="-lcdaudio -lmediad -lds"],[],[-lmedia -lds]) dnl for Irix
+AC_SUBST(LIBCDAUDIO)
+AC_CHECK_LIB(fpe, main, [LIBFPE="-lfpe"]) dnl for Irix CDDA
+AC_SUBST(LIBFPE) dnl for Irix CDDA
+AC_CHECK_LIB(cdrom, main, [LIBCDROM="-lcdrom"],[],[-lcdrom]) dnl for kscd FreeBSD
+AC_SUBST(LIBCDROM)
+
+dnl Checks for libraries.
+dnl added by wuebben Nov 27 1997
+AC_CHECK_LIB(Alib, main, [LIBALIB="-lAlib"]) dnl HP-UX network audio server
+AC_SUBST(LIBALIB)
+AC_CHECK_LIB(audio,main,[LIBAUDIO="-laudio"]) dnl SunOS audio driver
+AC_SUBST(LIBAUDIO)
+AC_CHECK_LIB(mme, main, [LIBMME="-lmme";EXTRAINCS="-I/usr/include/mme"]) dnl DEC MMS audio server
+AC_SUBST(LIBMME)
+
+AC_SUBST(EXTRAINCS)
+AC_SUBST(EXTRALIBS)
+
+AC_ARG_ENABLE(kscd-defaults,[ --enable-kscd-defaults use kscd default configs [default=yes]],
+[
+if test $enableval = "no"; dnl
+ then use_kscd_defaults="no"
+ else use_kscd_defaults="yes"
+fi
+], use_kscd_defaults="yes"
+)
+
+AC_DEFUN([KDE_COPY_KSCD_DEFAULTS],
+[
+if echo $TOPSUBDIRS | grep "kscd" > /dev/null 2> /dev/null; then
+ if test "$use_kscd_defaults" = "yes"; then
+ test -d kscd || mkdir kscd
+ cp $srcdir/kscd/config.h.std kscd/config.h
+ else
+ cd kscd && $srcdir/kscd-script
+ cd $topdir
+ fi
+fi
+])
+
+AC_OUTPUT_COMMANDS(KDE_COPY_KSCD_DEFAULTS)
+
+dnl +-------------------------+
+dnl | End KSCD checks |
+dnl +-------------------------+
+
+AC_MSG_CHECKING(if kscd can be compiled)
+case "$host" in
+ *-*-linux*) kscd_compile=yes;;
+ *-*-*bsdi*) kscd_compile=yes;;
+ *-*-*freebsd*) kscd_compile=yes;;
+ *-*-*netbsd*) kscd_compile=yes;;
+ *-*-*openbsd*) kscd_compile=yes;;
+ *) kscd_compile=yes;;
+esac
+AC_MSG_RESULT($kscd_compile)
+if test "$kscd_compile" = "no"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kscd"
+fi
+
+if test "x$build_arts" != "xno"; then
+ AC_DEFINE_UNQUOTED(USE_ARTS, 1, [Define if aRts is available])
+fi
+
diff --git a/kscd/cr22-action-cdsmall.png b/kscd/cr22-action-cdsmall.png
new file mode 100644
index 00000000..d9f8f05e
--- /dev/null
+++ b/kscd/cr22-action-cdsmall.png
Binary files differ
diff --git a/kscd/docking.cpp b/kscd/docking.cpp
new file mode 100644
index 00000000..9792ba73
--- /dev/null
+++ b/kscd/docking.cpp
@@ -0,0 +1,158 @@
+/*
+ * KSCD -- a simpole cd player for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This program 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 program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "docking.h"
+#include "kscd.h"
+
+#include <qhbox.h>
+#include <qtooltip.h>
+
+#include <kaboutdata.h>
+#include <kactioncollection.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+#include <kpassivepopup.h>
+
+#include <kdebug.h>
+
+DockWidget::DockWidget( KSCD* parent, const char *name)
+ : KSystemTray( parent, name )
+{
+ m_popup = 0;
+ setPixmap( loadIcon("cdsmall") );
+
+ KActionCollection* actionCollection = parent->actionCollection();
+ m_backAction = actionCollection->action("Previous");
+ m_forwardAction = actionCollection->action("Next");
+ m_backPix = loadIcon("player_start");
+ m_forwardPix = loadIcon("player_end");
+
+ // popup menu for right mouse button
+ QPopupMenu* popup = contextMenu();
+
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_play", KIcon::Small), i18n("Play/Pause"), parent, SLOT(playClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_stop", KIcon::Small), i18n("Stop"), parent, SLOT(stopClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_end", KIcon::Small), i18n("Next"), parent, SLOT(nextClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_start", KIcon::Small), i18n("Previous"), parent, SLOT(prevClicked()));
+ popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_eject", KIcon::Small), i18n("Eject"), parent, SLOT(ejectClicked()));
+
+ QToolTip::add(this, kapp->aboutData()->programName());
+}
+
+DockWidget::~DockWidget()
+{
+}
+
+void DockWidget::createPopup(const QString &songName, bool addButtons)
+{
+ if (!Prefs::trackAnnouncement())
+ return;
+
+ delete m_popup;
+ m_popup = new KPassivePopup(this);
+
+ QHBox* box = new QHBox(m_popup);
+
+ if (addButtons)
+ {
+ QPushButton* backButton = new QPushButton(m_backPix, 0, box, "popup_back");
+ backButton->setFlat(true);
+ connect(backButton, SIGNAL(clicked()), m_backAction, SLOT(activate()));
+ }
+
+ QLabel* l = new QLabel(songName, box);
+ l->setMargin(3);
+
+ if (addButtons)
+ {
+ QPushButton* forwardButton = new QPushButton(m_forwardPix, 0, box, "popup_forward");
+ forwardButton->setFlat(true);
+ connect(forwardButton, SIGNAL(clicked()), m_forwardAction, SLOT(activate()));
+ }
+
+ m_popup->setView(box);
+ m_popup->setAutoDelete(false);
+ m_popup->show();
+}
+
+void DockWidget::setToolTip(const QString& text)
+{
+ if (tip == text)
+ {
+ return;
+ }
+
+ tip = text;
+ QToolTip::remove(this);
+
+ if (text.isEmpty())
+ {
+ QToolTip::add(this, kapp->aboutData()->programName());
+ }
+ else
+ {
+ QToolTip::add(this, text);
+ }
+}
+
+void DockWidget::wheelEvent(QWheelEvent *e)
+{
+ if (e->orientation() == Horizontal)
+ return;
+
+ KSCD* kscd = dynamic_cast<KSCD*>(parent());
+ if (kscd == 0)
+ return;
+
+ switch (e->state())
+ {
+ case ShiftButton:
+ {
+ if (e->delta() > 0)
+ {
+ kscd->incVolume();
+ }
+ else
+ {
+ kscd->decVolume();
+ }
+ break;
+ }
+ default:
+ {
+ if (e->delta() > 0)
+ {
+ kscd->nextClicked();
+ }
+ else
+ {
+ kscd->prevClicked();
+ }
+ }
+ }
+}
+
+#include "docking.moc"
diff --git a/kscd/docking.h b/kscd/docking.h
new file mode 100644
index 00000000..ff946c15
--- /dev/null
+++ b/kscd/docking.h
@@ -0,0 +1,75 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Harri Porten <porten@tu-harburg.de>
+ * Latest changes (dynamic tooltips) by Cristian Tibirna <tibirna@kde.org>
+ *
+ *
+ * This program 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 program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _DOCKING_H_
+#define _DOCKING_H_
+
+#include <stdio.h>
+#include <qapplication.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qpopupmenu.h>
+#include <qpoint.h>
+#include <kdockwindow.h>
+
+class KSCD;
+
+class KAction;
+class KToggleAction;
+class KPassivePopup;
+
+class DockWidget : public KSystemTray
+{
+ Q_OBJECT
+
+public:
+ DockWidget( KSCD* parent, const char *name=0);
+ ~DockWidget();
+
+public slots:
+ void setToolTip(const QString& text);
+ void createPopup(const QString& songName, bool addButtons = true);
+
+private:
+ virtual void wheelEvent( QWheelEvent *e);
+
+ KPassivePopup* m_popup;
+
+ KAction* m_forwardAction;
+ KAction* m_backAction;
+
+ QPixmap m_backPix;
+ QPixmap m_forwardPix;
+
+ QString tip;
+};
+
+#endif
+
+
+
diff --git a/kscd/hi128-app-kscd.png b/kscd/hi128-app-kscd.png
new file mode 100644
index 00000000..25496a41
--- /dev/null
+++ b/kscd/hi128-app-kscd.png
Binary files differ
diff --git a/kscd/hi16-app-kscd.png b/kscd/hi16-app-kscd.png
new file mode 100644
index 00000000..e59a04eb
--- /dev/null
+++ b/kscd/hi16-app-kscd.png
Binary files differ
diff --git a/kscd/hi32-app-kscd.png b/kscd/hi32-app-kscd.png
new file mode 100644
index 00000000..0586fee8
--- /dev/null
+++ b/kscd/hi32-app-kscd.png
Binary files differ
diff --git a/kscd/hi48-app-kscd.png b/kscd/hi48-app-kscd.png
new file mode 100644
index 00000000..4d001825
--- /dev/null
+++ b/kscd/hi48-app-kscd.png
Binary files differ
diff --git a/kscd/hi64-app-kscd.png b/kscd/hi64-app-kscd.png
new file mode 100644
index 00000000..1769ca22
--- /dev/null
+++ b/kscd/hi64-app-kscd.png
Binary files differ
diff --git a/kscd/kcompactdisc.cpp b/kscd/kcompactdisc.cpp
new file mode 100644
index 00000000..b69325a3
--- /dev/null
+++ b/kscd/kcompactdisc.cpp
@@ -0,0 +1,486 @@
+/*
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <qfile.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprotocolmanager.h>
+#include <krun.h>
+#include "kcompactdisc.h"
+#include <netwm.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+/* this is for glibc 2.x which the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#endif
+
+#ifdef __linux__
+#include <mntent.h>
+#define KSCDMAGIC 0
+#endif
+
+#include <kprocess.h>
+#include <config.h>
+
+extern "C"
+{
+// We don't have libWorkMan installed already, so get everything
+// from within our own directory
+#include "libwm/include/wm_cddb.h"
+#include "libwm/include/wm_cdrom.h"
+#include "libwm/include/wm_cdtext.h"
+#include "libwm/include/wm_config.h"
+#include "libwm/include/wm_cdinfo.h"
+#include "libwm/include/wm_helpers.h"
+
+// Sun, Ultrix etc. have a canonical CD device specified in the
+// respective plat_xxx.c file. On those platforms you need not
+// specify the CD device and DEFAULT_CD_DEVICE is not defined
+// in config.h.
+#ifndef DEFAULT_CD_DEVICE
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+#endif
+}
+
+#include <qtextcodec.h>
+#include <fixx11h.h>
+
+// Our internal definition of when we have no disc. Used to guard some
+// internal arrays.
+#define NO_DISC ((m_discId == missingDisc) && (m_previousDiscId == 0))
+
+#define FRAMES_TO_MS(frames) \
+((frames) * 1000 / 75)
+
+#define TRACK_VALID(track) \
+((track) && (track <= m_tracks))
+
+const QString KCompactDisc::defaultDevice = DEFAULT_CD_DEVICE;
+const unsigned KCompactDisc::missingDisc = (unsigned)-1;
+
+KCompactDisc::KCompactDisc(InformationMode infoMode) :
+ m_device(QString::null),
+ m_status(0),
+ m_previousStatus(123456),
+ m_discId(missingDisc),
+ m_previousDiscId(0),
+ m_artist(QString::null),
+ m_title(QString::null),
+ m_track(0),
+ m_previousTrack(99999999),
+ m_infoMode(infoMode)
+{
+ // Debug.
+ // wm_cd_set_verbosity(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS_ALL );
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ connect(&timer, SIGNAL(timeout()), SLOT(timerExpired()));
+}
+
+KCompactDisc::~KCompactDisc()
+{
+ // Ensure nothing else starts happening.
+ timer.stop();
+ wm_cd_stop();
+ wm_cd_set_verbosity(0x0);
+ wm_cd_destroy();
+}
+
+const QString &KCompactDisc::device() const
+{
+ return m_device;
+}
+
+unsigned KCompactDisc::discLength() const
+{
+ if (NO_DISC || !m_tracks)
+ return 0;
+ return FRAMES_TO_MS(m_trackStartFrames[m_tracks+1] - m_trackStartFrames[0]);
+}
+
+unsigned KCompactDisc::discPosition() const
+{
+ return cur_pos_abs * 1000 - FRAMES_TO_MS(m_trackStartFrames[0]);
+}
+
+QString KCompactDisc::discStatus(int status)
+{
+ QString message;
+
+ switch (status)
+ {
+ case WM_CDM_TRACK_DONE: // == WM_CDM_BACK
+ message = i18n("Back/Track Done");
+ break;
+ case WM_CDM_PLAYING:
+ message = i18n("Playing");
+ break;
+ case WM_CDM_FORWARD:
+ message = i18n("Forward");
+ break;
+ case WM_CDM_PAUSED:
+ message = i18n("Paused");
+ break;
+ case WM_CDM_STOPPED:
+ message = i18n("Stopped");
+ break;
+ case WM_CDM_EJECTED:
+ message = i18n("Ejected");
+ break;
+ case WM_CDM_NO_DISC:
+ message = i18n("No Disc");
+ break;
+ case WM_CDM_UNKNOWN:
+ message = i18n("Unknown");
+ break;
+ case WM_CDM_CDDAERROR:
+ message = i18n("CDDA Error");
+ break;
+ case WM_CDM_CDDAACK:
+ message = i18n("CDDA Ack");
+ break;
+ default:
+ if (status <= 0)
+ message = strerror(-status);
+ else
+ message = QString::number(status);
+ break;
+ }
+ return message;
+}
+
+/**
+ * Do everything needed if the user requested to eject the disc.
+ */
+void KCompactDisc::eject()
+{
+ if (m_status == WM_CDM_EJECTED)
+ {
+ emit trayClosing();
+ wm_cd_closetray();
+ }
+ else
+ {
+ wm_cd_stop();
+ wm_cd_eject();
+ }
+}
+
+unsigned KCompactDisc::track() const
+{
+ return m_track;
+}
+
+bool KCompactDisc::isPaused() const
+{
+ return (m_status == WM_CDM_PAUSED);
+}
+
+bool KCompactDisc::isPlaying() const
+{
+ return WM_CDS_DISC_PLAYING(m_status) && (m_status != WM_CDM_PAUSED) && (m_status != WM_CDM_TRACK_DONE);
+}
+
+void KCompactDisc::pause()
+{
+ // wm_cd_pause "does the right thing" by flipping between pause and resume.
+ wm_cd_pause();
+}
+
+void KCompactDisc::play(unsigned startTrack, unsigned startTrackPosition, unsigned endTrack)
+{
+ wm_cd_play(TRACK_VALID(startTrack) ? startTrack : 1, startTrackPosition / 1000, TRACK_VALID(endTrack) ? endTrack : WM_ENDTRACK );
+}
+
+QString KCompactDisc::urlToDevice(const QString& device)
+{
+ KURL deviceUrl(device);
+ if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
+ {
+ kdDebug() << "Asking mediamanager for " << deviceUrl.fileName() << endl;
+ DCOPRef mediamanager("kded", "mediamanager");
+ DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName());
+ QStringList properties = reply;
+ if (!reply.isValid() || properties.count() < 6)
+ {
+ kdError() << "Invalid reply from mediamanager" << endl;
+ return defaultDevice;
+ }
+ else
+ {
+ kdDebug() << "Reply from mediamanager " << properties[5] << endl;
+ return properties[5];
+ }
+ }
+
+ return device;
+}
+
+bool KCompactDisc::setDevice(
+ const QString &device_,
+ unsigned volume,
+ bool digitalPlayback,
+ const QString &audioSystem,
+ const QString &audioDevice)
+{
+ timer.stop();
+
+ QString device = urlToDevice(device_);
+
+#if !defined(BUILD_CDDA)
+ digitalPlayback = false;
+#endif
+ int status = wm_cd_init(
+ digitalPlayback ? WM_CDDA : WM_CDIN,
+ QFile::encodeName(device),
+ digitalPlayback ? audioSystem.ascii() : 0,
+ digitalPlayback ? audioDevice.ascii() : 0,
+ 0);
+ m_device = wm_drive_device();
+ kdDebug() << "Device change: "
+ << (digitalPlayback ? "WM_CDDA, " : "WM_CDIN, ")
+ << m_device << ", "
+ << (digitalPlayback ? audioSystem : QString::null) << ", "
+ << (digitalPlayback ? audioDevice : QString::null) << ", status: "
+ << discStatus(status) << endl;
+
+ if (status < 0)
+ {
+ // Severe (OS-level) error.
+ m_device = QString::null;
+ }
+ else
+ {
+ // Init CD-ROM and display.
+ setVolume(volume);
+ }
+
+ m_previousStatus = m_status = wm_cd_status();
+
+ if (m_infoMode == Asynchronous)
+ timerExpired();
+ else
+ timer.start(1000, true);
+ return m_device != QString::null;
+}
+
+void KCompactDisc::setVolume(unsigned volume)
+{
+ int status = wm_cd_volume(volume, WM_BALANCE_SYMMETRED);
+ kdDebug() << "Volume change: " << volume << ", status: " << discStatus(status) << endl;
+}
+
+void KCompactDisc::stop()
+{
+ wm_cd_stop();
+}
+
+const QString &KCompactDisc::trackArtist() const
+{
+ return trackArtist(m_track);
+}
+
+const QString &KCompactDisc::trackArtist(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return QString::null;
+ return m_trackArtists[track - 1];
+}
+
+unsigned KCompactDisc::trackLength() const
+{
+ return trackLength(m_track);
+}
+
+unsigned KCompactDisc::trackLength(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return 0;
+ return cd->trk[track - 1].length * 1000;
+}
+
+unsigned KCompactDisc::trackPosition() const
+{
+ return cur_pos_rel * 1000;
+}
+
+unsigned KCompactDisc::tracks() const
+{
+ return m_tracks;
+}
+
+const QString &KCompactDisc::trackTitle() const
+{
+ return trackTitle(m_track);
+}
+
+const QString &KCompactDisc::trackTitle(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return QString::null;
+ return m_trackTitles[track - 1];
+}
+
+bool KCompactDisc::isAudio(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return 0;
+ return !(cd->trk[track - 1].data);
+}
+
+/*
+ * timerExpired
+ *
+ * - Data discs not recognized as data discs.
+ *
+ */
+void KCompactDisc::timerExpired()
+{
+ m_status = wm_cd_status();
+
+ if (WM_CDS_NO_DISC(m_status) || (m_device == QString::null))
+ {
+ if (m_previousStatus != m_status)
+ {
+ m_previousStatus = m_status;
+ m_discId = missingDisc;
+ m_previousDiscId = 0;
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ m_tracks = 0;
+ m_track = 0;
+ emit discChanged(m_discId);
+ }
+ }
+ else
+ {
+ m_discId = cddb_discid();
+ if (m_previousDiscId != m_discId)
+ {
+ m_previousDiscId = m_discId;
+ kdDebug() << "New discId=" << m_discId << endl;
+ // Initialise the album and its signature from the CD.
+ struct cdtext_info *info = wm_cd_get_cdtext();
+ if (info && info->valid)
+ {
+ m_artist = reinterpret_cast<char*>(info->blocks[0]->performer[0]);
+ m_title = reinterpret_cast<char*>(info->blocks[0]->name[0]);
+ }
+ else
+ {
+ m_artist = i18n("Unknown Artist");
+ m_title = i18n("Unknown Title");
+ }
+
+ // Read or default CD data.
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ m_tracks = wm_cd_getcountoftracks();
+ for (unsigned i = 1; i <= m_tracks; i++)
+ {
+ if (info && info->valid)
+ {
+ m_trackArtists.append(reinterpret_cast<char*>(info->blocks[0]->performer[i]));
+ m_trackTitles.append(reinterpret_cast<char*>(info->blocks[0]->name[i]));
+ }
+ else
+ {
+ m_trackArtists.append(i18n("Unknown Artist"));
+ m_trackTitles.append(i18n("Track %1").arg(QString::number(i).rightJustify(2, '0')));
+ }
+ // FIXME: KDE4
+ // track.length = cd->trk[i - 1].length;
+ m_trackStartFrames.append(cd->trk[i - 1].start);
+ }
+ m_trackStartFrames.append(cd->trk[0].start);
+ m_trackStartFrames.append(cd->trk[m_tracks].start);
+ emit discChanged(m_discId);
+ }
+
+ // Per-event processing.
+ m_track = wm_cd_getcurtrack();
+ if (m_previousTrack != m_track)
+ {
+ m_previousTrack = m_track;
+
+ // Update the current track and its length.
+ emit trackChanged(m_track, trackLength());
+ }
+ if (isPlaying())
+ {
+ m_previousStatus = m_status;
+ // Update the current playing position.
+ emit trackPlaying(m_track, trackPosition());
+ }
+ else
+ if (m_previousStatus != m_status)
+ {
+ // If we are not playing, then we are either paused, or stopped.
+ switch (m_status)
+ {
+ case WM_CDM_PAUSED:
+ emit trackPaused(m_track, trackPosition());
+ break;
+ case WM_CDM_EJECTED:
+ emit trayOpening();
+ break;
+ default:
+ if (m_previousStatus == WM_CDM_PLAYING || m_previousStatus == WM_CDM_PAUSED
+ && m_status == WM_CDM_STOPPED)
+ {
+ emit discStopped();
+ }
+ break;
+ }
+
+ m_previousStatus = m_status;
+ }
+ }
+
+ // Now that we have incurred any delays caused by the signals, we'll start the timer.
+ timer.start(1000, true);
+}
+
+#include "kcompactdisc.moc"
diff --git a/kscd/kcompactdisc.h b/kscd/kcompactdisc.h
new file mode 100644
index 00000000..595567f4
--- /dev/null
+++ b/kscd/kcompactdisc.h
@@ -0,0 +1,303 @@
+/*
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KCOMPACTDISC_H
+#define KCOMPACTDISC_H
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <qvaluelist.h>
+
+/**
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * The disc lifecycle is modelled by these signals:
+ *
+ * @see #trayClosing(): A disc is being inserted.
+ * @see #discChanged(): A disc was inserted or removed.
+ * @see #trayOpening(): A disc is being removed.
+ *
+ * The progress of playout is modelled by these signals:
+ *
+ * @see #trackPlaying(): A track started playing, or is still playing.
+ * @see #trackPaused(): A track was paused.
+ * @see #discStopped(): The disc stopped.
+ *
+ * All times in this interface are in milliseconds. Valid track numbers are
+ * positive numbers; zero is not a valid track number.
+ */
+class KCompactDisc :
+ public QObject
+{
+ Q_OBJECT
+public:
+ enum InformationMode
+ {
+ Synchronous, // Return and emit signal when cdrom and cddb information arrives.
+ Asynchronous // Block until cdrom and cddb infromation has been obtained
+ };
+
+ KCompactDisc(InformationMode=Synchronous);
+ virtual ~KCompactDisc();
+
+ /**
+ * Open/close tray.
+ */
+ void eject();
+
+ /**
+ * Start playout at given position of track.
+ */
+ void play(unsigned startTrack = 0, unsigned startTrackPosition = 0, unsigned endTrack = 0);
+
+ /**
+ * Pause/resume playout.
+ */
+ void pause();
+
+ /**
+ * If the url is a media:/ or system:/ URL returns
+ * the device it represents, otherwise returns device
+ */
+ static QString urlToDevice(const QString& device);
+
+ /**
+ * @param device Name of CD device, e.g. /dev/cdrom.
+ * @param digitalPlayback Select digial or analogue playback.
+ * @param audioSystem For analogue playback, system to use, e.g. "arts".
+ * @param audioDevice For analogue playback, device to use.
+ * @return true if the device seemed usable.
+ */
+ bool setDevice(
+ const QString &device = defaultDevice,
+ unsigned volume = 50,
+ bool digitalPlayback = true,
+ const QString &audioSystem = QString::null,
+ const QString &audioDevice = QString::null);
+
+ void setVolume(unsigned volume);
+
+ /**
+ * Stop playout.
+ */
+ void stop();
+
+ /**
+ * The default CD for this system.
+ */
+ static const QString defaultDevice;
+
+ /**
+ * Current device.
+ *
+ * @return Null string if no usable device set.
+ */
+ const QString &device() const;
+
+ /**
+ * The discId for a missing disc.
+ */
+ static const unsigned missingDisc;
+
+ /**
+ * Current disc, missingDisc if no disc.
+ */
+ unsigned discId() const { return m_discId; }
+
+ /**
+ * CDDB signature of disc.
+ */
+ const QValueList<unsigned> &discSignature() const { return m_trackStartFrames; }
+
+ /**
+ * Artist for whole disc.
+ *
+ * @return Disc artist or null string.
+ */
+ const QString &discArtist() const { return m_artist; }
+
+ /**
+ * Title of disc.
+ *
+ * @return Disc title or null string.
+ */
+ const QString &discTitle() const { return m_title; }
+
+ /**
+ * Length of disc.
+ *
+ * @return Disc length in milliseconds.
+ */
+ unsigned discLength() const;
+
+ /**
+ * Position in disc.
+ *
+ * @return Position in milliseconds.
+ */
+ unsigned discPosition() const;
+ /**
+ * Artist of current track.
+ *
+ * @return Track artist or null string.
+ */
+ const QString &trackArtist() const;
+
+ /**
+ * Artist of given track.
+ *
+ * @return Track artist or null string.
+ */
+ const QString &trackArtist(unsigned track) const;
+
+ /**
+ * Title of current track.
+ *
+ * @return Track title or null string.
+ */
+ const QString &trackTitle() const;
+
+ /**
+ * Title of given track.
+ *
+ * @return Track title or null string.
+ */
+ const QString &trackTitle(unsigned track) const;
+
+ /**
+ * Current track.
+ *
+ * @return Track number.
+ */
+ unsigned track() const;
+
+ /**
+ * Number of tracks.
+ */
+ unsigned tracks() const;
+
+ /**
+ * @return if the track is actually an audio track.
+ */
+ bool isAudio(unsigned track) const;
+
+ /**
+ * Length of current track.
+ *
+ * @return Track length in milliseconds.
+ */
+ unsigned trackLength() const;
+
+ /**
+ * Length of given track.
+ *
+ * @param track Track number.
+ * @return Track length in milliseconds.
+ */
+ unsigned trackLength(unsigned track) const;
+
+ /**
+ * Position in current track.
+ *
+ * @return Position in milliseconds.
+ */
+ unsigned trackPosition() const;
+
+ bool isPaused() const;
+
+ bool isPlaying() const;
+
+signals:
+
+ /**
+ * A disc is being inserted.
+ */
+ void trayClosing();
+
+ /**
+ * A disc is being removed.
+ */
+ void trayOpening();
+
+ /**
+ * A disc was inserted or removed.
+ *
+ * @param discId Current disc, missingDisc if no disc.
+ */
+ void discChanged(unsigned discId);
+
+ /**
+ * Disc stopped. See @see #trackPaused.
+ */
+ void discStopped();
+
+ /**
+ * The current track changed.
+ *
+ * @param track Track number.
+ * @param trackLength Length within track in milliseconds.
+ */
+ void trackChanged(unsigned track, unsigned trackLength);
+
+ /**
+ * A track started playing, or is still playing. This signal is delivered at
+ * approximately 1 second intervals while a track is playing. At first sight,
+ * this might seem overzealous, but its likely that any CD player UI will use
+ * this to track the second-by-second position, so we may as well do it for
+ * them.
+ *
+ * @param track Track number.
+ * @param trackPosition Position within track in milliseconds.
+ */
+ void trackPlaying(unsigned track, unsigned trackPosition);
+
+ /**
+ * A track paused playing.
+ *
+ * @param track Track number.
+ * @param trackPosition Position within track in milliseconds.
+ */
+ void trackPaused(unsigned track, unsigned trackPosition);
+
+private:
+ QTimer timer;
+ QString m_device;
+ int m_status;
+ int m_previousStatus;
+ unsigned m_discId;
+ unsigned m_previousDiscId;
+ QString m_artist;
+ QString m_title;
+ unsigned m_tracks;
+ QValueList<unsigned> m_trackStartFrames;
+ QStringList m_trackArtists;
+ QStringList m_trackTitles;
+ unsigned m_track;
+ unsigned m_previousTrack;
+ void checkDeviceStatus();
+ QString discStatus(int status);
+ class KCompactDiscPrivate *d;
+ InformationMode m_infoMode;
+
+private slots:
+ void timerExpired();
+};
+
+#endif
diff --git a/kscd/kscd-script b/kscd/kscd-script
new file mode 100755
index 00000000..8e54bc9e
--- /dev/null
+++ b/kscd/kscd-script
@@ -0,0 +1,782 @@
+#!/bin/sh
+#
+# Kscd - A simple cd player for the KDE Project
+#
+# $Id$
+#
+# Copyright (c) 1997 Bernd Johannes Wuebben math.cornell.edu
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# the following is borrowed from configure
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+config_file="config.h"
+ORG_CONFIG="config.h.std"
+
+display_main_menu(){
+
+clear
+echo
+echo " KSCD -- Configuration"
+echo "==================================================================="
+echo
+echo " Linux ...................... L"
+echo " FreeBSD/NetBSD ............. F"
+echo " BSD386 ..................... B"
+echo " Sun ........................ S"
+echo " HPUX ....................... H"
+echo " Irix ....................... I"
+echo " Sony NEWS .................. N"
+echo " OSF/1 ...................... O"
+echo " Ultrix ..................... U"
+echo " Generic SVR4 (not Sun) ..... V"
+echo
+echo " Quit ....................... Q"
+echo
+echo
+echo " Bernd Johannes Wuebben <wuebben@kde.org>"
+echo "=================================================================="
+echo
+echo $ac_n " Please chose acccording to your platform:$ac_c"
+
+read platform
+
+}
+
+give_instructions(){
+
+echo "Please answer all questions. Simply hit [Enter] to select the default"
+echo "values which are displayed in square brackets."
+echo
+
+}
+
+write_config_header(){
+
+echo "" >> $config_file
+echo "/* This header file was automatically generated by the kscd" >> $config_file
+echo " installation script. In case of trouble please edit" >> $config_file
+echo " $ORG_CONFIG in the source directory of the kscd distribution" >> $config_file
+echo " and rename it to $config_file .*/" >> $config_file
+echo "" >> $config_file
+
+}
+
+linux_conf(){
+
+clear
+echo "KSCD configuration for Linux systems"
+echo "------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "#ifdef linux " >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/cdrom]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/cdrom"
+fi
+
+echo "Your choice was: $device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+
+
+echo
+echo "I have made modifications to the workman engine which cause kscd to be"
+echo "a very fast and responsive cd player for an average IDE CDROM drive."
+echo "Answer NO to the next question unless you have problems getting kscd"
+echo "to work."
+echo
+echo $ac_n "Revert to original Workman behavior? (y/n) [n]:$ac_c"
+
+read workman
+
+case "$workman" in
+ "yes" | "y" | "Yes" | "Y") echo "#define WORKMAN_ORIGINAL " >> $config_file ;;
+esac
+
+
+echo
+echo "I have incorporated modifications from Dirk (milliByte@DeathsDoor.com)"
+echo "which will make kscd perform better on any drive using the sbpcd or"
+echo "mcdx drivers. If and only if your kernel uses one of these cd drivers you"
+echo "should try answering YES to the next question. Please peruse the "
+echo "documents in the spbcd directory of this distribution for more "
+echo "information about theses modifications."
+echo
+echo $ac_n "Enable SBPCD_HACK? (y/n) [n]:$ac_c"
+
+read sbpcd
+
+case "$sbpcd" in
+ "yes" | "y" | "Yes" | "Y") echo "#define SBPCD_HACK " >> $config_file ;;
+esac
+
+
+echo
+echo "If and only if you have an SCSI cdrom drive you should probably"
+echo "answer YES to the next question"
+echo
+echo $ac_n "Enable LINUX_SCSI_PASSTHROUGH? (y/n) [n]:$ac_c"
+
+read scsi
+
+case "$scsi" in
+ "yes" | "y" | "Yes" | "Y") echo "#define LINUX_SCSI_PASSTHROUGH " >> $config_file ;;
+esac
+
+echo
+echo "Most cdrom devices have a volume range from 0 to 255."
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Note: Some CDROM device only support a range from 128 to 255."
+echo
+echo $ac_n "MIN_VOLUME [0]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="0"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo
+echo "If you own one a cdrom player whose volume range is 128 to 255,"
+echo "you might want to try answering YES to the next question."
+echo "Enabling 'Curved Volume' will give you a square root like"
+echo "volume slider - volume curve."
+echo
+echo $ac_n "Enable CURVED_VOLUME? (y/n) [n]:$ac_c"
+
+read curved
+
+case "$curved" in
+ "yes" | "y" | "Yes" | "Y") echo "#define CURVED_VOLUME " >> $config_file ;;
+esac
+
+echo "" >> $config_file
+echo "#endif /* linux */" >> $config_file
+
+}
+
+irix_conf(){
+
+clear
+echo "KSCD configuration for Irix"
+echo "---------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(sgi)" >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/scsi/sc0d6l0]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/scsi/sc0d6l0"
+fi
+
+echo "Your choice was: $device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo $ac_n "Enable CDDA? (y/n) [y]:$ac_c"
+
+read curved
+
+case "$curved" in
+ "yes" | "y" | "Yes" | "Y" | "") echo "#define CDDA " >> $config_file ;;
+esac
+echo ""
+echo "That's all for sgi ..."
+
+echo "#endif /* sgi */" >> $config_file
+
+}
+
+hp_conf(){
+
+clear
+echo "KSCD configuration for HPUX systems"
+echo "-----------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(hpux) || defined (__hpux)" >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/rscsi]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/rcsci"
+fi
+
+echo "Your choice was: $device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+echo ""
+echo "That's all for HPUX ..."
+
+echo "#endif /* hpux */" >> $config_file
+
+
+}
+
+bsd386_conf(){
+
+clear
+echo "KSCD configuration for BSD386 systems"
+echo "-------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "#ifdef __bsdi__ " >> $config_file
+
+echo
+echo "Please answer YES to the next question if you have a Sound Blaster cdrom."
+echo
+echo $ac_n "Define SOUNDBALSTER? (y/n) [n]:$ac_c"
+
+read soundblaster
+
+case "$soundblaster" in
+ "yes" | "y" | "Yes" | "Y") echo "#define SOUNDBLASTER " >> $config_file ;;
+esac
+
+echo "#endif /* __bsdi__ */" >> $config_file
+
+}
+
+freebsd_netbsd_conf(){
+
+clear
+echo "KSCD configuration for FreeBSD/NetBSD systems"
+echo "---------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(__FreeBSD__) || defined(__NetBSD__)" >> $config_file
+echo "" >> $config_file
+
+# get default device
+if [ `/usr/bin/uname -s` = "NetBSD" ]; then
+ if [ `/usr/bin/uname -m` = "i386" ]; then
+ def_device=/dev/rcd0d
+ else
+ def_device=/dev/rcd0c
+ fi
+else
+ # FreeBSD
+ def_device=/dev/acd0c
+fi
+
+echo $ac_n "Please specify your cdrom device [$def_device]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="$def_device"
+fi
+
+echo "Your choice was:$device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo
+echo "Some experimental changes to the FreeBSD code were made."
+echo "Please let me know whether answering yes or no to the next"
+echo "question will give you a better functioning cd player."
+echo "Users of NetBSD might want to experiment with this too."
+echo
+echo $ac_n "Define NEW_BSD_PLAYCLICKED? (y/n) [y]:$ac_c"
+
+read free_play_clicked
+
+case "$free_play_clicked" in
+ "yes" | "y" | "Yes" | "Y"| "") echo "#define NEW_BSD_PLAYCLICKED" >> $config_file ;;
+esac
+
+echo "" >> $config_file
+echo "#endif /* FreeBSD/NetBSD */" >> $config_file
+
+}
+
+sun_conf(){
+
+clear
+echo "KSCD configuration for Sun systems"
+echo "----------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(sun) || defined(__sun__) " >> $config_file
+echo "" >> $config_file
+
+echo $ac_n "Are you compiling on Solaris 2.x? (y/n) [y]:$ac_c"
+read solaris
+
+case "$solaris" in
+
+ n | N)
+ echo
+ ;;
+ *)
+ echo "#define SYSV" >> $config_file
+ solaris="y"
+ ;;
+esac
+
+echo ""
+echo "You need to enable the internal audio device if you wish"
+echo "to get sound from your workstation, as opposed to only from"
+echo "the head-phone jack of your CDROM."
+echo ""
+echo $ac_n "Activate internal audio device (CODEC)? (y/n) [y]:$ac_c"
+
+read codec
+
+case "$code" in
+
+ n | N)
+ ;;
+ *)
+ echo "#define CODEC" >> $config_file
+ ;;
+esac
+
+if [ "$solaris" = "y" ]
+then
+ echo
+ echo "Do you wish to enable digital audio capablilites on drives that"
+ echo $ac_n "support it? (y/n) [y]:$ac_c"
+ read cdda
+
+ case "$cdda" in
+
+ n | N)
+ ;;
+ *)
+ echo "#define BUILD_CDDA" >> $config_file
+ ;;
+ esac
+fi
+
+
+echo
+echo "Most cdrom devices have a volume range from 0 to 255."
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Note: Some CDROM device only support a range from 128 to 255."
+echo
+echo $ac_n "MIN_VOLUME [128]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="128"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo
+echo "If you own one a cdrom player whose volume range is 128 to 255,"
+echo "you might want to try answering YES to the next question."
+echo "Enabling 'Curved Volume' will give you a square root like"
+echo "volume slider - volume curve."
+echo
+echo $ac_n "Enable CURVED_VOLUME? (y/n) [n]:$ac_c"
+
+read curved
+
+case "$curved" in
+ "yes" | "y" | "Yes" | "Y") echo "#define CURVED_VOLUME " >> $config_file ;;
+esac
+
+echo "#endif /* Sun*/" >> $config_file
+
+}
+
+sony_conf(){
+
+clear
+echo "KSCD configuration for Sony NEWS systems"
+echo "---------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(__sony_news) || defined(sony_news)" >> $config_file
+echo "" >> $config_file
+
+echo "Unfortunately, I have conflicting reports:"
+echo "You might need a,b or c below."
+echo "Please let me know what is required on Sony NEWS."
+echo
+echo "a) #include <CD.h> "
+echo
+echo "b) #include <newsiodev/scu.h>"
+echo " #include <newsiodev/scsireg.h>"
+echo
+echo "c) both of the above"
+echo ""
+echo $ac_n "Please choose a,b or c. [c]:$ac_c"
+
+read sony_include
+
+case "$sony_include" in
+ "a" | "A" )
+ echo "Your choice was a"
+ echo "#include <CD.h>" >> $config_file
+ ;;
+ "b" | "B" )
+ echo "Your choice was b"
+ echo "#include <newsiodev/scu.h>" >> $config_file
+ echo "#include <newsiodev/scsireg.h>" >> $config_file
+ ;;
+ * )
+ echo "Your choice was c"
+ echo "#include <CD.h>" >> $config_file
+ echo "#include <newsiodev/scu.h>" >> $config_file
+ echo "#include <newsiodev/scsireg.h>" >> $config_file
+ ;;
+esac
+
+echo
+echo $ac_n "Please specify your cdrom device [/dev/rsd/b0i6u0p2\0]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/rsd/b0i6u0p2\0"
+fi
+
+echo "Your choice was:$device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo
+echo "While most CDROM devices have a volume range from 0 to 255,"
+echo "it appears that 128 to 255 is more appropriate for Sony NEWS."
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Don't forget to let me know the result of your experiments."
+echo
+echo $ac_n "MIN_VOLUME [128]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="128"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo "" >> $config_file
+echo "#endif /* sony_news */" >> $config_file
+
+}
+
+ultrix_conf(){
+
+clear
+echo "KSCD configuration for Ultrix systems"
+echo "---------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(ultrix) || defined(__ultrix)" >> $config_file
+echo "" >> $config_file
+
+echo "Unfortunately, I have conflicting reports:"
+echo "You might need a,b or c below."
+echo "Please let me know what is required on Ultrix."
+echo
+echo "a) #include <sys/rzdisk.h>"
+echo " #include <sys/cdrom.h>"
+echo
+echo "b) #include <sys/devio.h>"
+echo " #include <io/cam/cam.h>"
+echo " #include <io/cam/uagt.h>"
+echo " #include <io/cam/dec_cam.h>"
+echo " #include <io/cam/scsi_all.h>"
+echo ""
+echo "c) both of the above"
+echo ""
+echo $ac_n "Please choose a,b or c. [a]:$ac_c"
+
+read ultrix_include
+
+case "$ultrix_include" in
+ "c" | "C" )
+ echo "Your choice was c)"
+ echo ""
+ echo "#include <sys/rzdisk.h>" >> $config_file
+ echo "#include <sys/cdrom.h>">> $config_file
+ echo ""
+ echo "#include <sys/devio.h>" >> $config_file
+ echo "#include <io/cam/cam.h>" >> $config_file
+ echo "#include <io/cam/uagt.h>" >> $config_file
+ echo "#include <io/cam/dec_cam.h>" >> $config_file
+ echo "#include <io/cam/scsi_all.h>" >> $config_file
+ echo "" >> $config_file
+ ;;
+
+ "b" | "B" )
+ echo "Your choice was b"
+ echo "#include <sys/devio.h>" >> $config_file
+ echo "#include <io/cam/cam.h>" >> $config_file
+ echo "#include <io/cam/uagt.h>" >> $config_file
+ echo "#include <io/cam/dec_cam.h>" >> $config_file
+ echo "#include <io/cam/scsi_all.h>" >> $config_file
+ ;;
+ * )
+ echo "Your choice was a"
+ echo "#include <sys/rzdisk.h>" >> $config_file
+ echo "#include <sys/cdrom.h>">> $config_file
+ ;;
+esac
+
+echo
+echo "While most CDROM devices have a volume range from 0 to 255,"
+echo "it appears that 128 to 255 is more appropriate for Ultrix"
+echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME"
+echo "until volume adjustment works to your satisfaction."
+echo "Don't forget to let me know the result of your experiments."
+echo
+echo $ac_n "MIN_VOLUME [128]:$ac_c"
+
+read MINVOLUME
+if [ "$MINVOLUME" = "" ]
+then
+ MINVOLUME="128"
+fi
+
+echo "You entered $MINVOLUME "
+echo "#define MIN_VOLUME $MINVOLUME " >> $config_file
+
+echo
+echo $ac_n "MAX_VOLUME [255]:$ac_c"
+
+read MAXVOLUME
+if [ "$MAXVOLUME" = "" ]
+then
+ MAXVOLUME="255"
+fi
+
+echo "You entered $MAXVOLUME "
+echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file
+
+echo "" >> $config_file
+echo "#endif /* ultrix */" >> $config_file
+
+}
+
+osf_conf(){
+
+clear
+echo "KSCD configuration for OSF systems"
+echo "----------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> config_file
+echo "#if defined(__osf__)" >> $config_file
+
+echo "Nothing to be done for OSF. The CD device should be automatically detected."
+echo "If you have trouble look at plat_osf1.c and let me know what needs to be"
+echo "changed."
+
+echo ""
+echo $ac_n "Press [Enter] to continue.$ac_c"
+read dummy
+
+echo "" >> $config_file
+echo "#endif /* osf */" >> $config_file
+echo "" >> $config_file
+
+#endif
+
+
+}
+
+generic_svr_conf(){
+
+clear
+echo "KSCD configuration for a generic SVR4 system"
+echo "--------------------------------------------"
+echo
+
+give_instructions
+write_config_header
+
+echo "" >> $config_file
+echo "#if defined(SVR4) && !defined(sun) && !defined(__sun__)" >> $config_file
+
+echo $ac_n "Please specify your cdrom device [/dev/rcdrom/cd0]:$ac_c"
+
+read device
+if [ "$device" = "" ]
+then
+ device="/dev/rcdrom/cd0"
+fi
+
+echo "Your choice was:$device"
+echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file
+
+echo "" >> $config_file
+echo "#endif /* SVR4 */" >> $config_file
+echo "" >> $config_file
+
+}
+
+########################################################################
+#
+# main
+#
+########################################################################
+
+if [ -f $config_file ]
+then
+ echo "Found old config file. Will clean up first ...."
+ rm $config_file
+ sleep 1
+fi
+
+display_main_menu
+
+if [ "$platform" = "q" ]
+then
+ clear
+ echo "Good bye!"
+ exit 1
+fi
+
+case "$platform" in
+l|L) linux_conf;;
+f|F) freebsd_netbsd_conf;;
+b|B) bsd386_conf;;
+s|S) sun_conf;;
+h|H) hp_conf;;
+i|I) irix_conf;;
+n|N) sony_conf;;
+o|O) osf_conf;;
+u|U) ultrix_conf;;
+v|V) generic_svr_conf;;
+*) linux_conf;;
+esac
+
+sleep 1
+clear
+echo ""
+echo "I have written your configuration choices to $config_file."
+echo "Should you have trouble with the configuration file this"
+echo "script generates, you can rename $ORG_CONFIG to $config_file"
+echo "and edit it manually to suit your configuration."
+echo ""
+echo "Have fun with kscd!"
+echo ""
+echo "Bernd Johannes Wuebben"
+echo "wuebben@kde.org"
+echo "wuebben@math.cornell.edu"
+echo ""
+echo "Press [Enter] to start the compilation."
+read key
+exit
+
+
+
+
+
+
diff --git a/kscd/kscd.cpp b/kscd/kscd.cpp
new file mode 100644
index 00000000..b462eb2a
--- /dev/null
+++ b/kscd/kscd.cpp
@@ -0,0 +1,1677 @@
+/*
+ * Kscd - A simple cd player for the KDE Project
+ *
+ * Copyright (c) 1997 Bernd Johannes wuebben@math.cornell.edu
+ * Copyright (c) 2002-2003 Aaron J. Seigo <aseigo@kde.org>
+ * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+ * Copyright (c) 2003-2006 Richard Lärkäng <nouseforaname@home.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <qdir.h>
+#include <qregexp.h>
+#include <qtextstream.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qapplication.h>
+#include <qgroupbox.h>
+#include <qsqlpropertymap.h>
+
+#include <dcopclient.h>
+#include <kaboutdata.h>
+#include <kaccel.h>
+#include <kaction.h>
+#include <dcopref.h>
+#include <kcharsets.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kemailsettings.h>
+#include <kglobal.h>
+#include <khelpmenu.h>
+#include <kkeydialog.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kprotocolmanager.h>
+#include <krun.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <kstringhandler.h>
+#include <kurl.h>
+#include <kuniqueapplication.h>
+#include <kglobalsettings.h>
+#include <kcmoduleloader.h>
+#include <kconfigdialog.h>
+
+#include "docking.h"
+#include "kscd.h"
+#include "version.h"
+#include "prefs.h"
+
+#include <kwin.h>
+#include <netwm.h>
+#include <stdlib.h>
+
+#include <config.h>
+
+#include "cddbdlg.h"
+#include "configWidget.h"
+#include <qtextcodec.h>
+#include <kcompactdisc.h>
+#include <fixx11h.h>
+
+static const char description[] = I18N_NOOP("KDE CD player");
+
+bool stoppedByUser = false;
+
+
+/****************************************************************************
+ The GUI part
+*****************************************************************************/
+
+KSCD::KSCD( QWidget *parent, const char *name )
+ : DCOPObject("CDPlayer"),
+ kscdPanelDlg( parent, name, Qt::WDestructiveClose ),
+ configDialog(0L),
+ cddialog(0L), //!!!!
+ jumpToTrack(0L),
+ updateTime(true),
+ m_dockWidget(0)
+{
+ m_cd = new KCompactDisc();
+ cddbInfo.clear(); // The first freedb revision is "0" //!!!!
+ random_current = random_list.begin();
+
+ cddb = new KCDDB::Client();
+ connect(cddb, SIGNAL(finished(CDDB::Result)), this, SLOT(lookupCDDBDone(CDDB::Result)));
+
+#if defined(BUILD_CDDA)
+ audio_systems_list
+ << "arts"
+#if defined(HAVE_ARTS_LIBASOUND2)
+ << "alsa"
+#endif
+#ifdef USE_SUN_AUDIO
+ << "sun"
+#endif
+ ;
+#endif
+
+ readSettings();
+ initFont();
+ drawPanel();
+ setColors();
+
+ // the time slider
+ timeIcon->setPixmap(SmallIcon("player_time"));
+ connect(timeSlider, SIGNAL(sliderPressed()), SLOT(timeSliderPressed()));
+ connect(timeSlider, SIGNAL(sliderReleased()), SLOT(timeSliderReleased()));
+ connect(timeSlider, SIGNAL(sliderMoved(int)), SLOT(timeSliderMoved(int)));
+ connect(timeSlider, SIGNAL(valueChanged(int)), SLOT(jumpToTime(int)));
+
+ // the volume slider
+ volumeIcon->setPixmap(SmallIcon("player_volume"));
+ volumeSlider->setValue(Prefs::volume());
+ QString str;
+ str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) );
+ volumelabel->setText(str);
+ connect(volumeSlider, SIGNAL(valueChanged(int)), SLOT(volChanged(int)));
+
+ /* FIXME check for return value */
+ setDevicePaths(/*Prefs::cdDevice(), Prefs::audioSystem(), Prefs::audioDevice()*/);
+ connect(m_cd, SIGNAL(trackPlaying(unsigned, unsigned)), this, SLOT(trackUpdate(unsigned, unsigned)));
+ connect(m_cd, SIGNAL(trackPaused(unsigned, unsigned)), this, SLOT(trackUpdate(unsigned, unsigned)));
+ connect(m_cd, SIGNAL(trackChanged(unsigned, unsigned)), this, SLOT(trackChanged(unsigned, unsigned)));
+ connect(m_cd, SIGNAL(discStopped()), this, SLOT(discStopped()));
+ connect(m_cd, SIGNAL(discChanged(unsigned)), this, SLOT(discChanged(unsigned)));
+ connect( &queryledtimer, SIGNAL(timeout()), SLOT(togglequeryled()) );
+ connect( &titlelabeltimer, SIGNAL(timeout()), SLOT(titlelabeltimeout()) );
+ connect( &cycletimer, SIGNAL(timeout()), SLOT(cycletimeout()) );
+ connect( &jumpTrackTimer, SIGNAL(timeout()), SLOT(jumpTracks()) );
+/*
+ these are always connected in base class
+ connect( playPB, SIGNAL(clicked()), SLOT(playClicked()) );
+ connect( nextPB, SIGNAL(clicked()), SLOT(nextClicked()) );
+ connect( prevPB, SIGNAL(clicked()), SLOT(prevClicked()) );
+ connect( stopPB, SIGNAL(clicked()), SLOT(stopClicked()) );
+ connect( ejectPB, SIGNAL(clicked()), SLOT(ejectClicked()) );
+*/
+ connect( repeatPB, SIGNAL(clicked()), SLOT(loopClicked()) );
+ connect( songListCB, SIGNAL(activated(int)), SLOT(trackSelected(int)));
+ connect( shufflePB, SIGNAL(clicked()), SLOT(randomSelected()));
+ connect( cddbPB, SIGNAL(clicked()), SLOT(CDDialogSelected()));
+ connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(setColors()));
+ connect(kapp, SIGNAL(iconChanged(int)), this, SLOT(setIcons()));
+ QToolTip::remove(songListCB);
+ QToolTip::add(songListCB, i18n("Track list"));
+
+
+ // set up the actions and keyboard accels
+ m_actions = new KActionCollection(this);
+
+ KAction* action;
+ action = new KAction(i18n("Play/Pause"), Key_P, this, SLOT(playClicked()), m_actions, "Play/Pause");
+ action = new KAction(i18n("Stop"), Key_S, this, SLOT(stopClicked()), m_actions, "Stop");
+ action = new KAction(i18n("Previous"), Key_B, this, SLOT(prevClicked()), m_actions, "Previous");
+ action = new KAction(i18n("Next"), Key_N, this, SLOT(nextClicked()), m_actions, "Next");
+ action = KStdAction::quit(this, SLOT(quitClicked()), m_actions);
+ action = KStdAction::keyBindings(this, SLOT(configureKeys()), m_actions, "options_configure_shortcuts");
+ action = KStdAction::keyBindings(this, SLOT(configureGlobalKeys()), m_actions, "options_configure_globals");
+ action = KStdAction::preferences(this, SLOT(showConfig()), m_actions);
+ action = new KAction(i18n("Loop"), Key_L, this, SLOT(loopClicked()), m_actions, "Loop");
+ action = new KAction(i18n("Eject"), CTRL + Key_E, this, SLOT(ejectClicked()), m_actions, "Eject");
+ action = new KAction(i18n("Increase Volume"), Key_Plus, this, SLOT(incVolume()), m_actions, "IncVolume");
+ KShortcut increaseVolume = action->shortcut();
+ increaseVolume.append( KKey( Key_Equal ) );
+ action->setShortcut( increaseVolume );
+ action = new KAction(i18n("Decrease Volume"), Key_Minus, this, SLOT(decVolume()), m_actions, "DecVolume");
+ action = new KAction(i18n("Options"), CTRL + Key_T, this, SLOT(showConfig()), m_actions, "Options");
+ action = new KAction(i18n("Shuffle"), Key_R, this, SLOT(randomSelected()), m_actions, "Shuffle");
+ action = new KAction(i18n("CDDB"), CTRL + Key_D, this, SLOT(CDDialogSelected()), m_actions, "CDDB");
+
+ m_actions->readShortcutSettings("Shortcuts");
+
+ m_actions->action( "options_configure_globals" )->setText( i18n( "Configure &Global Shortcuts..." ) );
+
+ kapp->installKDEPropertyMap();
+ QSqlPropertyMap *map = QSqlPropertyMap::defaultMap();
+ map->insert("KComboBox", "currentText");
+
+ initGlobalShortcuts();
+
+ setupPopups();
+
+ if (Prefs::looping())
+ {
+ loopled->on();
+ loopled->show();
+ repeatPB->setOn(true);
+ }
+
+ setDocking(Prefs::docking());
+
+ setFocusPolicy(QWidget::NoFocus);
+
+ songListCB->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
+ adjustSize();
+ setFixedHeight(this->height());
+} // KSCD
+
+
+KSCD::~KSCD()
+{
+ delete cddb;
+ delete m_cd;
+} // ~KSCD
+
+
+void KSCD::initGlobalShortcuts() {
+
+ m_globalAccel = new KGlobalAccel( this );
+
+ //Definition of global shortcuts is based on 'local' shortcuts which follow
+ //the WIN key.
+ m_globalAccel->insert("Next", i18n("Next"), 0, KKey("WIN+N"), KKey("WIN+Right"),
+ this, SLOT(nextClicked()));
+ //NOTE: WIN+B collidates with amarok's default global shortcut.
+ m_globalAccel->insert("Previous", i18n("Previous"), 0, KKey("WIN+B"), KKey("WIN+Left"),
+ this, SLOT(prevClicked()));
+ m_globalAccel->insert("Play/Pause", i18n("Play/Pause"), 0, KKey("WIN+P"), 0,
+ this, SLOT(playClicked()));
+ m_globalAccel->insert("Stop", i18n("Stop"), 0, KKey("WIN+S"), 0,
+ this, SLOT(stopClicked()));
+ m_globalAccel->insert("IncVolume", i18n("Increase Volume"), 0, KKey("WIN+Plus"), KKey("WIN+Up"),
+ this, SLOT(incVolume()));
+ m_globalAccel->insert("DecVolume", i18n("Decrease Volume"), 0, KKey("WIN+Minus"), KKey("WIN+Down"),
+ this, SLOT(decVolume()));
+ m_globalAccel->insert("Shuffle", i18n("Shuffle"), 0, KKey("WIN+R"), 0,
+ this, SLOT(incVolume()));
+
+ m_globalAccel->setConfigGroup( "GlobalShortcuts" );
+ m_globalAccel->readSettings( kapp->config() );
+ m_globalAccel->updateConnections();
+}
+
+bool KSCD::digitalPlayback() {
+#if defined(BUILD_CDDA)
+ return !(Prefs::audioSystem().isEmpty());
+#else
+ return false;
+#endif
+}
+
+void KSCD::setVolume(int v)
+{
+ volChanged(v);
+ volumeSlider->setValue(v);
+}
+
+void KSCD::setDevice(const QString& dev)
+{
+ Prefs::self()->setCdDevice(dev);
+ setDevicePaths();
+}
+
+/**
+ * Initialize smallfont which fits into the 13 and 14 pixel widgets.
+ */
+void KSCD::initFont()
+{
+/* int theSmallPtSize = 10;
+
+ // Find a font that fits the 13 and 14 pixel widgets
+ QFont fn( KGlobalSettings::generalFont().family(), theSmallPtSize, QFont::Bold );
+ bool fits = false;
+ while (!fits && theSmallPtSize > 1)
+ {
+ QFontMetrics metrics(fn);
+ if(metrics.height() > 13)
+ {
+ --theSmallPtSize;
+ fn.setPointSize(theSmallPtSize);
+ } else {
+ fits = true;
+ }
+ }
+ smallfont = QFont(KGlobalSettings::generalFont().family(), theSmallPtSize, QFont::Bold);
+*/
+} // initFont()
+
+/**
+ * drawPanel() constructs KSCD's little black LED area
+ * all settings are made via panel.ui
+ */
+void KSCD::drawPanel()
+{
+ setIcons();
+ adjustSize();
+
+ const int D = 6;
+ for (int u = 0; u < 5; u++) {
+ trackTimeLED[u] = new BW_LED_Number(frameleds);
+ trackTimeLED[u]->setLEDoffColor(Prefs::backColor());
+ trackTimeLED[u]->setLEDColor(Prefs::ledColor(), Prefs::backColor());
+ trackTimeLED[u]->setGeometry(2 + u * 18, D, 23, 30);
+ connect(trackTimeLED[u], SIGNAL(clicked()), this, SLOT(cycleplaytimemode()));
+ }
+
+ setLEDs(-1);
+
+ queryled = new LedLamp(symbols);
+ queryled->move(+10, D + 1);
+ queryled->off();
+ queryled->hide();
+
+ loopled = new LedLamp(symbols, LedLamp::Loop);
+ loopled->move(+10, D + 18);
+ loopled->off();
+
+ totaltimelabel->hide();
+} // drawPanel
+
+void KSCD::setIcons()
+{
+ playPB->setIconSet(SmallIconSet("player_play"));
+ stopPB->setIconSet(SmallIconSet("player_stop"));
+ ejectPB->setIconSet(SmallIconSet("player_eject"));
+ prevPB->setIconSet(SmallIconSet("player_start"));
+ nextPB->setIconSet(SmallIconSet("player_end"));
+ cddbPB->setIconSet(SmallIconSet("view_text"));
+ infoPB->setIconSet(SmallIconSet("run"));
+}
+
+void KSCD::setupPopups()
+{
+ QPopupMenu* mainPopup = new QPopupMenu(this);
+ infoPB->setPopup(mainPopup);
+ infoPopup = new QPopupMenu (this);
+
+
+ infoPopup->insertItem("MusicMoz", 0);
+ infoPopup->insertItem("Ultimate Bandlist", 1);
+ infoPopup->insertItem("CD Universe", 2);
+ infoPopup->insertSeparator();
+ infoPopup->insertItem("AlltheWeb", 3);
+ infoPopup->insertItem("Altavista", 4);
+ infoPopup->insertItem("Excite", 5);
+ infoPopup->insertItem("Google", 6);
+ infoPopup->insertItem("Google Groups", 7);
+ infoPopup->insertItem("HotBot", 8);
+ infoPopup->insertItem("Lycos", 9);
+ infoPopup->insertItem("Open Directory", 10);
+ infoPopup->insertItem("Yahoo!", 11);
+
+ m_actions->action(KStdAction::name(KStdAction::Preferences))->plug(mainPopup);
+ //NEW add the shortcut dialogs
+ m_actions->action("options_configure_globals")->plug(mainPopup);
+ m_actions->action("options_configure_shortcuts")->plug(mainPopup);
+ mainPopup->insertSeparator();
+
+ mainPopup->insertItem(i18n("Artist Information"), infoPopup);
+
+ connect( infoPopup, SIGNAL(activated(int)), SLOT(information(int)) );
+
+ KHelpMenu* helpMenu = new KHelpMenu(this, KGlobal::instance()->aboutData(), false);
+ mainPopup->insertItem(SmallIcon("help"),i18n("&Help"), helpMenu->menu());
+ mainPopup->insertSeparator();
+ m_actions->action(KStdAction::name(KStdAction::Quit))->plug(mainPopup);
+} // setupPopups
+
+void KSCD::playClicked()
+{
+ if (m_cd->discId() == KCompactDisc::missingDisc)
+ return;
+
+ kapp->processEvents();
+ kapp->flushX();
+
+ if (!m_cd->isPlaying())
+ {
+ kapp->processEvents();
+ kapp->flushX();
+
+ if (m_cd->isPaused())
+ {
+ // Unpause (!!).
+ m_cd->pause();
+ }
+ else
+ {
+ setLEDs(0);
+ resetTimeSlider(true);
+
+ if(Prefs::randomPlay())
+ {
+ make_random_list();
+ // next clicked handles updating the play button, etc.
+ nextClicked();
+ }
+ else
+ {
+ m_cd->play(0, 0, playlist.isEmpty() ? 0 : 1);
+ }
+ }
+
+ // Update UI to allow a subsequent pause.
+ statuslabel->setText(i18n("Play"));
+ playPB->setIconSet(SmallIconSet("player_pause"));
+ playPB->setText(i18n("Pause"));
+ }
+ else
+ {
+ m_cd->pause();
+
+ // Update UI to allow a subsequent play.
+ statuslabel->setText(i18n("Pause"));
+ playPB->setIconSet(SmallIconSet("player_play"));
+ playPB->setText(i18n("Play"));
+ }
+
+ kapp->processEvents();
+ kapp->flushX();
+} // playClicked()
+
+void KSCD::setShuffle(int shuffle)
+{
+ if (shuffle == 2) {
+ if(Prefs::randomPlay() && m_cd->tracks() > 0) {
+ shufflePB->blockSignals(true);
+ shufflePB->setOn(true);
+ shufflePB->blockSignals(false);
+ make_random_list(); /* koz: Build a unique, once, random list */
+ if(m_cd->isPlaying())
+ nextClicked();
+ }
+
+ return;
+ }
+
+ Prefs::setRandomPlay(shuffle);
+ shufflePB->blockSignals(true);
+ shufflePB->setOn(shuffle);
+ shufflePB->blockSignals(false);
+
+ if (Prefs::randomPlay() && m_cd->tracks() > 0) {
+ make_random_list(); /* koz: Build a unique, once, random list */
+ if(m_cd->isPlaying())
+ nextClicked();
+ }
+}
+
+void KSCD::stopClicked()
+{
+ stoppedByUser = true;
+
+ kapp->processEvents();
+ kapp->flushX();
+ m_cd->stop();
+} // stopClicked()
+
+void KSCD::prevClicked()
+{
+ int track = m_cd->track();
+
+ if (Prefs::randomPlay()) {
+ track = prev_randomtrack();
+ if (track == -1) {
+ return;
+ }
+ } else {
+ if (track <= 1) {
+ if (Prefs::looping()) {
+ track = m_cd->tracks();
+ } else {
+ return;
+ }
+ } else {
+ track--;
+ }
+ }
+
+ kapp->processEvents();
+ kapp->flushX();
+ m_cd->play(track, 0, playlist.isEmpty() ? 0 : track);
+} // prevClicked()
+
+bool KSCD::nextClicked()
+{
+ unsigned track = m_cd->track();
+
+ if (Prefs::randomPlay()) {
+ track = next_randomtrack();
+ if(track == 0) {
+ return false;
+ }
+ } else {
+ if(track < 1) {
+ track = 1;
+ } else if (track >= m_cd->tracks()) {
+ if (Prefs::looping()) {
+ track = 1;
+ } else {
+ return true;
+ }
+ } else {
+ track++;
+ }
+ }
+
+ kapp->processEvents();
+ kapp->flushX();
+ m_cd->play(track, 0, Prefs::randomPlay() || !playlist.isEmpty() ? track + 1 : 0);
+ return true;
+} // nextClicked()
+
+void KSCD::trackChanged(unsigned track, unsigned trackLength)
+{
+ QString tooltip = artistlabel->text();
+ if (track < 1)
+ {
+ setLEDs(-1);
+ resetTimeSlider(true);
+ tracklabel->setText("--/--");
+ titlelabel->clear();
+ }
+ else
+ {
+// if (!nextClicked())
+// {
+// statuslabel->setText(i18n("Disc Finished"));
+// m_cd->stop();
+// }
+// break;
+
+ if (songListCB->count())
+ {
+ songListCB->setCurrentItem(track - 1);
+ // drop the number.
+ // for Mahlah, a picky though otherwise wonderful person - AJS
+ QString justTheName = songListCB->currentText();
+ justTheName = justTheName.right(justTheName.length() - 4);
+
+ QToolTip::remove(songListCB);
+ QToolTip::add(songListCB, i18n("Current track: %1").arg(justTheName));
+ }
+ timeSlider->blockSignals(true);
+ timeSlider->setRange(0, trackLength ? trackLength - 1 : 0);
+ timeSlider->blockSignals(false);
+ QString str;
+ str.sprintf("%02d/%02d", track, m_cd->tracks());
+ tracklabel->setText(str);
+
+ QString title = cddbInfo.trackInfoList[track-1].title;
+ titlelabel->setText(title);
+ tooltip += "/";
+ tooltip += KStringHandler::rsqueeze(title, 30);
+ }
+ emit trackChanged(tooltip);
+} //trackChanged(int track)
+
+
+void KSCD::jumpToTime(int ms, bool forcePlay)
+{
+ kapp->processEvents();
+ kapp->flushX();
+
+ int track = m_cd->track();
+ if ((m_cd->isPlaying() || forcePlay) &&
+ ms < (int)m_cd->trackLength())
+ {
+ if(Prefs::randomPlay() || !playlist.isEmpty())
+ {
+ m_cd->play(track, ms, track + 1);
+ }
+ else
+ {
+ m_cd->play(track, ms);
+ }
+ }
+} // jumpToTime(int ms)
+
+void KSCD::timeSliderPressed()
+{
+ updateTime = false;
+} // timeSliderPressed()
+
+void KSCD::timeSliderMoved(int milliseconds)
+{
+ setLEDs(milliseconds);
+} // timeSliderMoved(int seconds)
+
+void KSCD::timeSliderReleased()
+{
+ updateTime = true;
+} // timeSliderReleased()
+
+void KSCD::quitClicked()
+{
+ // ensure nothing else starts happening
+ queryledtimer.stop();
+ titlelabeltimer.stop();
+ cycletimer.stop();
+ jumpTrackTimer.stop();
+
+ writeSettings();
+ //setShuffle(0);
+ statuslabel->clear();
+ setLEDs(-1);
+
+ // Good GOD this is evil
+ kapp->processEvents();
+ kapp->flushX();
+
+ if(Prefs::stopExit())
+ m_cd->stop();
+
+ delete m_cd;
+
+ kapp->quit();
+} // quitClicked()
+
+bool KSCD::event( QEvent *e )
+{
+ return QWidget::event(e);
+} // event
+
+
+void KSCD::loopOn()
+{
+ Prefs::setLooping(true);
+ loopled->on();
+ loopled->show();
+ kapp->processEvents();
+ kapp->flushX();
+} // loopOn;
+
+void KSCD::loopOff()
+{
+ Prefs::setLooping(false);
+ loopled->off();
+ loopled->show();
+ kapp->processEvents();
+ kapp->flushX();
+} // loopOff;
+
+void KSCD::loopClicked()
+{
+ if(Prefs::looping())
+ {
+ loopOff();
+ }
+ else
+ {
+ loopOn();
+ }
+} // loopClicked
+
+/**
+ * Do everything needed if the user requested to eject the disc.
+ *
+ */
+void KSCD::ejectClicked()
+{
+ m_cd->eject();
+} // ejectClicked
+
+void KSCD::closeEvent(QCloseEvent *e)
+{
+ if (Prefs::docking() && !kapp->sessionSaving())
+ {
+ hide();
+ e->ignore();
+ return;
+ }
+ e->accept();
+}
+
+void KSCD::randomSelected()
+{
+ setShuffle(Prefs::randomPlay()?0:1);
+
+ /* FIXME this helps us to display "Random" in Status line
+ should it maybe to be replaced with symbol "RAND" or something others */
+ statuslabel->setText(Prefs::randomPlay()?i18n("Random"):i18n("Play"));
+} // randomSelected
+
+/**
+ * A Track was selected for playback from the drop down box.
+ *
+ */
+void KSCD::trackSelected( int cb_index )
+{
+ if (cb_index < 0)
+ return;
+
+ unsigned int track = cb_index + 1;
+ setShuffle(0);
+
+ m_cd->play(track, 0);
+} // trackSelected
+
+void KSCD::updateConfigDialog(configWidget* widget)
+{
+ if(!widget)
+ return;
+
+ static QString originalTitleOfGroupBox = widget->groupBox3->title();
+ if(m_cd->isPlaying()) {
+ widget->groupBox3->setEnabled(false);
+ widget->groupBox3->setTitle( i18n( "CD Drive (you must stop playing to change this)" ) );
+ } else {
+ widget->groupBox3->setEnabled(true);
+ widget->groupBox3->setTitle(originalTitleOfGroupBox);
+ }
+}
+
+void KSCD::showConfig()
+{
+ static configWidget* confWidget = 0;
+
+ if (KConfigDialog::showDialog("settings")) {
+ updateConfigDialog(confWidget);
+ return;
+ }
+
+ configDialog = new KConfigDialog(this, "settings", Prefs::self());
+
+ configDialog->setHelp(QString::null);
+
+ confWidget = new configWidget(this, 0, "Kscd");
+
+ // kscd config page
+ configDialog->addPage(confWidget, i18n("CD Player"), "kscd", i18n("Settings & Behavior"));
+
+ // libkcddb page
+ KService::Ptr libkcddb = KService::serviceByDesktopName("libkcddb");
+ if (libkcddb && libkcddb->isValid())
+ {
+ KCModuleInfo info(libkcddb->desktopEntryPath());
+ if (info.service()->isValid())
+ {
+ KCModule *m = KCModuleLoader::loadModule(info, KCModuleLoader::Inline);
+ if (m)
+ {
+ m->load();
+ KCDDB::Config* cfg = new KCDDB::Config();
+ cfg->readConfig();
+ configDialog -> addPage(m, cfg, QString("CDDB"), "cdtrack", i18n("Configure Fetching Items"));
+
+ connect(configDialog, SIGNAL(okClicked()), m, SLOT(save()));
+ connect(configDialog, SIGNAL(applyClicked()), m, SLOT(save()));
+ connect(configDialog, SIGNAL(defaultClicked()), m, SLOT(defaults()));
+ }
+ }
+ }
+
+ updateConfigDialog(confWidget);
+
+ connect(configDialog, SIGNAL(settingsChanged()), this, SLOT(configDone()));
+ configDialog -> show();
+} // showConfig()
+
+void KSCD::configDone()
+{
+ setColors();
+ setDocking(Prefs::docking());
+
+ setDevicePaths();
+
+ volumeIcon->setEnabled(!Prefs::digitalPlayback());
+ volumeSlider->setEnabled(!Prefs::digitalPlayback());
+
+ // dialog deletes itself
+ configDialog = 0L;
+}
+
+void KSCD::configureKeys()
+{
+ KKeyDialog::configure(m_actions, this);
+}
+
+void KSCD::configureGlobalKeys()
+{
+ KKeyDialog::configure(m_globalAccel, true, this, true);
+}
+
+void KSCD::setDevicePaths()
+{
+ if (!m_cd->setDevice(Prefs::cdDevice(), Prefs::volume(), Prefs::digitalPlayback(),
+ Prefs::audioSystem(), Prefs::audioDevice()))
+ {
+ // This device did not seem usable.
+ QString str = i18n("CD-ROM read or access error (or no audio disc in drive).\n"\
+ "Please make sure you have access permissions to:\n%1").arg(
+ KCompactDisc::urlToDevice(Prefs::cdDevice()));
+ KMessageBox::error(this, str, i18n("Error"));
+ }
+} // setDevicePath()
+
+void KSCD::setDocking(bool dock)
+{
+ Prefs::setDocking(dock);
+ if (Prefs::docking())
+ {
+ if (!m_dockWidget)
+ {
+ m_dockWidget = new DockWidget(this, "dockw");
+ connect(m_dockWidget, SIGNAL(quitSelected()), this, SLOT(quitClicked()));
+ }
+
+ m_dockWidget->show();
+ connect(this, SIGNAL(trackChanged(const QString&)),
+ m_dockWidget, SLOT(setToolTip(const QString&)));
+ connect(this, SIGNAL(trackChanged(const QString&)),
+ m_dockWidget, SLOT(createPopup(const QString&)));
+ }
+ else
+ {
+ show();
+ delete m_dockWidget;
+ m_dockWidget = 0;
+ }
+}
+
+void KSCD::incVolume()
+{
+ int v = Prefs::volume() + 5;
+
+ if (v > 100)
+ {
+ v = 100;
+ }
+
+ volChanged(v);
+ volumeSlider->setValue(v);
+} // incVolume
+
+void KSCD::decVolume()
+{
+ int v = Prefs::volume() - 5;
+
+ if (v < 0)
+ {
+ v = 0;
+ }
+
+ volChanged(v);
+ volumeSlider->setValue(v);
+} // decVolume
+
+void KSCD::volChanged( int vol )
+{
+ QString str;
+ str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), vol) );
+ volumelabel->setText(str);
+ m_cd->setVolume(vol);
+ Prefs::setVolume(vol);
+} // volChanged
+
+void KSCD::make_random_list()
+{
+ /* koz: 15/01/00. I want a random list that does not repeat tracks. Ie, */
+ /* a list is created in which each track is listed only once. The tracks */
+ /* are picked off one by one until the end of the list */
+
+ int selected = 0;
+ bool rejected = false;
+
+ //kdDebug(67000) << "Playlist has " << size << " entries\n" << endl;
+ random_list.clear();
+ for(unsigned i = 0; i < m_cd->tracks(); i++)
+ {
+ do {
+ selected = 1 + (int) randSequence.getLong(m_cd->tracks());
+ rejected = (random_list.find(selected) != random_list.end());
+ } while(rejected == true);
+ random_list.append(selected);
+ }
+/*
+ printf("debug: dump random list\n");
+ RandomList::iterator it;
+ for(it = random_list.begin(); it != random_list.end(); it++) {
+ printf("%i ", *it);
+ }
+ printf("\n");
+*/
+ random_current = random_list.end();
+} // make_random_list()
+
+int KSCD::next_randomtrack()
+{
+ /* Check to see if we are at invalid state */
+ if(random_current == random_list.end())
+ {
+ random_current = random_list.begin();
+ }
+ else if(random_current == random_list.fromLast())
+ {
+ if(!Prefs::looping())
+ {
+ m_cd->stop();
+ return 0;
+ }
+ else
+ {
+ // playing the same random list isn't very random, is it?
+ make_random_list();
+ return next_randomtrack();
+ }
+ }
+ else
+ {
+ ++random_current;
+ }
+
+ return *random_current;
+} // next_randomtrack
+
+int KSCD::prev_randomtrack()
+{
+ /* Check to see if we are at invalid state */
+ if(random_current == random_list.end())
+ {
+ random_current = random_list.fromLast();
+ }
+ else if(random_current == random_list.begin())
+ {
+ if(!Prefs::looping())
+ {
+ return -1;
+ }
+ else
+ {
+ // playing the same random list isn't very random, is it?
+ make_random_list();
+ return prev_randomtrack();
+ }
+ }
+ else
+ {
+ --random_current;
+ }
+
+ return *random_current;
+} // prev_randomtrack
+
+void KSCD::discChanged(unsigned discId)
+{
+ cddbInfo.clear();
+ if (discId == KCompactDisc::missingDisc)
+ {
+ statuslabel->setText(i18n("No disc"));
+ }
+ else
+ {
+ cddbInfo.id = QString::number(discId, 16).rightJustify(8,'0');
+ cddbInfo.length = m_cd->discLength() / 1000;
+ cddbInfo.artist = m_cd->discArtist();
+ cddbInfo.title = m_cd->discTitle();
+
+ // If it's a sampler, we'll do artist/title.
+ bool isSampler = (cddbInfo.title.compare("Various") == 0);
+ KCDDB::TrackInfo track;
+ for (unsigned i = 1; i <= m_cd->tracks(); i++)
+ {
+ if (isSampler)
+ {
+ track.title = m_cd->trackArtist(i);
+ track.title.append("/");
+ track.title.append(m_cd->trackTitle(i));
+ }
+ else
+ {
+ track.title = m_cd->trackTitle(i);
+ }
+
+ // FIXME: KDE4
+ // track.length = cd->trk[i - 1].length;
+ cddbInfo.trackInfoList.append(track);
+ }
+ }
+
+ // Set the total time.
+ QTime dml;
+ dml = dml.addSecs(m_cd->discLength() / 1000);
+
+ QString fmt;
+ if(dml.hour() > 0)
+ fmt.sprintf("%02d:%02d:%02d",dml.hour(),dml.minute(),dml.second());
+ else
+ fmt.sprintf("%02d:%02d",dml.minute(),dml.second());
+ totaltimelabel->setText(fmt);
+
+ trackChanged(0, 0);
+ populateSongList("");
+ //totaltimelabel->clear();
+ totaltimelabel->lower();
+
+ if ((Prefs::autoplay() || KCmdLineArgs::parsedArgs()->isSet("start"))
+ && !m_cd->isPlaying())
+ {
+ playClicked();
+ }
+
+ // We just populated the GUI with what we got from the CD. Now look for
+ // more from the Internet...
+ lookupCDDB();
+}
+
+void KSCD::discStopped()
+{
+ if (Prefs::ejectOnFinish() && !stoppedByUser && (m_cd->discId() != KCompactDisc::missingDisc))
+ {
+ ejectClicked();
+ }
+
+ if (!stoppedByUser)
+ {
+ if (Prefs::randomPlay())
+ {
+ // If nextClicked returns false, it was the last
+ // random track, and the player should be stopped
+ if (nextClicked())
+ return;
+ }
+ else if (Prefs::looping())
+ {
+ playClicked();
+ return;
+ }
+ }
+
+ statuslabel->setText(i18n("Stopped"));
+ playPB->setText(i18n("Play"));
+ playPB->setIconSet(SmallIconSet("player_play"));
+
+ /* reset to initial value, only stopclicked() sets this to true */
+ stoppedByUser = false;
+
+ trackChanged(0, 0);
+ populateSongList("");
+ totaltimelabel->clear();
+ totaltimelabel->lower();
+}
+
+void KSCD::setLEDs(int milliseconds)
+{
+ QString symbols;
+
+ if (milliseconds < 0)
+ {
+ symbols = "--:--";
+ }
+ else
+ {
+ unsigned mymin;
+ unsigned mysec;
+ mymin = milliseconds / 60000;
+ mysec = (milliseconds % 60000) / 1000;
+ symbols.sprintf("%02d:%02d", mymin, mysec);
+ }
+
+ for (int i = 0; i < 5; i++)
+ {
+ trackTimeLED[i]->display((char)symbols.local8Bit().at(i));
+ }
+}
+
+void KSCD::resetTimeSlider(bool enabled)
+{
+ timeSlider->setEnabled(enabled);
+ timeSlider->blockSignals(true);
+ timeSlider->setValue(0);
+ timeSlider->blockSignals(false);
+} // resetTimeSlider(bool enabled);
+
+void KSCD::setColors()
+{
+ QColor led_color = Prefs::ledColor();
+ QColor background_color = Prefs::backColor();
+
+ backdrop->setBackgroundColor(background_color);
+
+ QColorGroup colgrp( led_color, background_color, led_color,led_color , led_color,
+ led_color, white );
+
+ titlelabel ->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ artistlabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ volumelabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ statuslabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ tracklabel ->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ totaltimelabel->setPalette( QPalette(colgrp,colgrp,colgrp) );
+
+ queryled->setPalette( QPalette(colgrp,colgrp,colgrp) );
+ loopled->setPalette( QPalette(colgrp,colgrp,colgrp) );
+
+ for (int u = 0; u< 5;u++){
+ trackTimeLED[u]->setLEDoffColor(background_color);
+ trackTimeLED[u]->setLEDColor(led_color,background_color);
+ }
+
+ titlelabel ->setFont( Prefs::ledFont() );
+ artistlabel->setFont( Prefs::ledFont() );
+ volumelabel->setFont( Prefs::ledFont() );
+ statuslabel->setFont( Prefs::ledFont() );
+ tracklabel ->setFont( Prefs::ledFont() );
+ totaltimelabel->setFont( Prefs::ledFont() );
+}
+
+void KSCD::readSettings()
+{
+/*
+ time_display_mode = config->readNumEntry("TimeDisplay", TRACK_SEC);
+*/
+
+#ifndef DEFAULT_CD_DEVICE
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+ // sun ultrix etc have a canonical cd rom device specified in the
+ // respective plat_xxx.c file. On those platforms you need nnot
+ // specify the cd rom device and DEFAULT_CD_DEVICE is not defined
+ // in config.h
+#endif
+
+ if (Prefs::cdDevice().isEmpty())
+ {
+ Prefs::setCdDevice(DEFAULT_CD_DEVICE);
+ }
+
+ volumeIcon->setEnabled(!Prefs::digitalPlayback());
+ volumeSlider->setEnabled(!Prefs::digitalPlayback());
+}
+
+/**
+ * Write KSCD's Configuration into the kderc file.
+ *
+ */
+void KSCD::writeSettings()
+{
+ Prefs::writeConfig();
+} // writeSettings()
+
+void KSCD::CDDialogSelected()
+{
+ if (!cddialog)
+ {
+ cddialog = new CDDBDlg(this);
+
+ cddialog->setData(cddbInfo, m_cd->discSignature(), playlist);
+ connect(cddialog,SIGNAL(cddbQuery()),SLOT(lookupCDDB()));
+ connect(cddialog,SIGNAL(newCDInfoStored(KCDDB::CDInfo)),
+ SLOT(setCDInfo(KCDDB::CDInfo)));
+ connect(cddialog,SIGNAL(finished()),SLOT(CDDialogDone()));
+ connect(cddialog,SIGNAL(play(int)),SLOT(trackSelected(int)));
+ }
+
+ cddialog->show();
+ cddialog->raise();
+}
+
+void KSCD::CDDialogDone()
+{
+ cddialog->delayedDestruct();
+ cddialog = 0L;
+}
+
+void KSCD::lookupCDDB()
+{
+ if (m_cd->discId() == KCompactDisc::missingDisc)
+ return;
+ kdDebug(67000) << "lookupCDDB() called" << endl;
+
+ populateSongList(i18n("Start freedb lookup."));
+
+ setShuffle(2);
+
+ led_on();
+
+ cddb->config().reparse();
+ cddb->setBlockingMode(false);
+ cddb->lookup(m_cd->discSignature());
+} // lookupCDDB
+
+void KSCD::lookupCDDBDone(CDDB::Result result)
+{
+ led_off();
+ if ((result != KCDDB::CDDB::Success) &&
+ (result != KCDDB::CDDB::MultipleRecordFound))
+ {
+ populateSongList(result == CDDB::NoRecordFound ? i18n("No matching freedb entry found.") : i18n("Error getting freedb entry."));
+ return;
+ }
+
+ // The intent of the original code here seems to have been to perform the
+ // lookup, and then to convert all the string data within the CDDB response
+ // using the use Prefs::selectedEncoding() and a QTextCodec. However, that
+ // seems to be irrelevant these days.
+ KCDDB::CDInfo info = cddb->bestLookupResponse();
+ // TODO Why doesn't libcddb not return MultipleRecordFound?
+ //if( result == KCDDB::CDDB::MultipleRecordFound ) {
+ if( cddb->lookupResponse().count() > 1 ) {
+ CDInfoList cddb_info = cddb->lookupResponse();
+ CDInfoList::iterator it;
+ QStringList list;
+ for ( it = cddb_info.begin(); it != cddb_info.end(); ++it ) {
+ list.append( QString("%1, %2, %3").arg((*it).artist).arg((*it).title)
+ .arg((*it).genre));
+ }
+
+ bool ok(false);
+ QString res = KInputDialog::getItem(
+ i18n("Select CDDB Entry"),
+ i18n("Select a CDDB entry:"), list, 0, false, &ok,
+ this );
+ if ( ok ) {
+ // The user selected and item and pressed OK
+ uint c = 0;
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
+ if( *it == res) break;
+ c++;
+ }
+ if( c < cddb_info.size() )
+ info = cddb_info[c];
+ } else {
+ return;
+ // user pressed Cancel
+ }
+ }
+
+ setCDInfo(info);
+
+ // In case the cddb dialog is open, update it
+ if (cddialog)
+ cddialog->setData(cddbInfo, m_cd->discSignature(), playlist);
+} // lookupCDDBDone
+
+void KSCD::setCDInfo(KCDDB::CDInfo info)
+{
+ // Some sanity provisions to ensure that the number of records matches what
+ // the CD actually contains.
+ while (info.trackInfoList.count() < cddbInfo.trackInfoList.count())
+ {
+ info.trackInfoList.append(KCDDB::TrackInfo());
+ }
+ while (info.trackInfoList.count() > cddbInfo.trackInfoList.count())
+ {
+ info.trackInfoList.pop_back();
+ }
+ cddbInfo = info;
+ populateSongList("");
+}
+
+void KSCD::led_off()
+{
+ queryledtimer.stop();
+ queryled->off();
+ queryled->hide();
+ totaltimelabel->raise();
+ totaltimelabel->show();
+} // led_off
+
+void KSCD::led_on()
+{
+ totaltimelabel->hide();
+ totaltimelabel->lower();
+ queryledtimer.start(800);
+ queryled->off();
+ queryled->show();
+ kapp->processEvents();
+ kapp->flushX();
+} // led_on
+
+void KSCD::togglequeryled()
+{
+ queryled->show();
+ queryled->toggle();
+} // togglequeryled
+
+void KSCD::titlelabeltimeout()
+{
+ // clear the cddb error message on the title label.
+ titlelabeltimer.stop();
+ titlelabel->clear();
+
+} // titlelabeltimeout
+
+void KSCD::trayOpening()
+{
+ statuslabel->setText(i18n("Ejected"));
+ trackChanged(0, 0);
+}
+
+int KSCD::currentTrack()
+{
+ return m_cd->track();
+}
+
+int KSCD::currentTrackLength()
+{
+ return m_cd->trackLength();
+}
+
+int KSCD::currentPosition()
+{
+ return m_cd->trackPosition();
+}
+
+int KSCD::getStatus()
+{
+ if (m_cd->isPlaying())
+ return 2;
+ else if (m_cd->isPaused())
+ return 4;
+ else if (m_cd->discId() != KCompactDisc::missingDisc)
+ return 5;
+ else
+ return 6;
+}
+
+bool KSCD::playing()
+{
+ return m_cd->isPlaying();
+}
+
+void KSCD::trackUpdate(unsigned /*track*/, unsigned trackPosition)
+{
+ unsigned tmp;
+
+ switch (Prefs::timeDisplayMode())
+ {
+ case TRACK_REM:
+ tmp = m_cd->trackLength() - trackPosition;
+ break;
+
+ case TOTAL_SEC:
+ tmp = m_cd->discPosition();
+ break;
+
+ case TOTAL_REM:
+ tmp = m_cd->discLength() - m_cd->discPosition();
+ break;
+
+ case TRACK_SEC:
+ default:
+ tmp = trackPosition;
+ break;
+ }
+ if (updateTime)
+ {
+ setLEDs(tmp);
+ timeSlider->blockSignals(true);
+ timeSlider->setValue(trackPosition);
+ timeSlider->blockSignals(false);
+ }
+}
+
+void KSCD::cycleplaytimemode()
+{
+ cycletimer.stop();
+
+ if (Prefs::timeDisplayMode() > 2) {
+ Prefs::setTimeDisplayMode(TRACK_SEC);
+ } else {
+ Prefs::setTimeDisplayMode(Prefs::timeDisplayMode() + 1);
+ }
+
+ switch(Prefs::timeDisplayMode()) {
+
+ case TRACK_REM:
+ volumelabel->setText(i18n("Tra Rem"));
+ break;
+
+ case TOTAL_SEC:
+ volumelabel->setText(i18n("Tot Sec"));
+ break;
+
+ case TOTAL_REM:
+ volumelabel->setText(i18n("Tot Rem"));
+ break;
+
+ case TRACK_SEC:
+ default:
+ volumelabel->setText(i18n("Tra Sec"));
+ break;
+ }
+
+ cycletimer.start(3000, true);
+} // cycleplaymode
+
+void KSCD::cycletimeout()
+{
+ cycletimer.stop();
+ QString str;
+ str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) );
+ volumelabel->setText(str);
+} // cycletimeout
+
+
+void KSCD::information(int i)
+{
+ //kdDebug(67000) << "Information " << i << "\n" << endl;
+
+ if(cddbInfo.artist.isEmpty())
+ return;
+
+ QString encodedArtist = KURL::encode_string_no_slash(cddbInfo.artist);
+
+ QString str;
+
+ switch(i)
+ {
+ case 0:
+ str = QString("http://musicmoz.org/cgi-bin/ext.cgi?artist=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 1:
+ str = QString("http://ubl.artistdirect.com/cgi-bin/gx.cgi/AppLogic+Search?select=MusicArtist&searchstr=%1&searchtype=NormalSearch")
+ .arg(encodedArtist);
+ break;
+
+ case 2:
+ str = QString("http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 3:
+ str = QString("http://www.alltheweb.com/search?cat=web&q=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 4:
+ str = QString("http://altavista.com/web/results?q=%1&kgs=0&kls=1&avkw=xytx")
+ .arg(encodedArtist);
+ break;
+
+ case 5:
+ str = QString("http://msxml.excite.com/_1_2UDOUB70SVHVHR__info.xcite/dog/results?otmpl=dog/webresults.htm&qkw=%1&qcat=web&qk=20&top=1&start=&ver=14060")
+ .arg(encodedArtist);
+ break;
+
+ case 6:
+ str = QString("http://www.google.com/search?q=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 7:
+ str = QString("http://groups.google.com/groups?oi=djq&as_q=%1&num=20")
+ .arg(encodedArtist);
+ break;
+
+ case 8:
+ str = QString("http://www.hotbot.com/default.asp?prov=Inktomi&query=%1&ps=&loc=searchbox&tab=web")
+ .arg(encodedArtist);
+ break;
+
+ case 9:
+ str = QString("http://search.lycos.com/default.asp?lpv=1&loc=searchhp&tab=web&query=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 10:
+ str = QString("http://search.dmoz.org/cgi-bin/search?search=%1")
+ .arg(encodedArtist);
+ break;
+
+ case 11:
+ str = QString("http://search.yahoo.com/bin/search?p=%1")
+ .arg(encodedArtist);
+ break;
+
+ default:
+ return;
+ break;
+ } // switch()
+
+ KRun::runURL(KURL( str ), "text/html");
+} // information
+
+/**
+ * Save state on session termination
+ */
+bool KSCD::saveState(QSessionManager& /*sm*/)
+{
+ writeSettings();
+ KConfig* config = KApplication::kApplication()->sessionConfig();
+ config->setGroup("General");
+ config->writeEntry("Show", isVisible());
+ return true;
+} // saveState
+
+
+/**
+ * Allow the user to type in the number of the track
+ */
+void KSCD::keyPressEvent(QKeyEvent* e)
+{
+ bool isNum;
+ int value = e->text().toInt(&isNum);
+
+ if (e->key() == Qt::Key_F1)
+ {
+ kapp->invokeHelp();
+ }
+ else if (isNum)
+ {
+ value = (jumpToTrack * 10) + value;
+
+ if (value <= (int)cddbInfo.trackInfoList.count())
+ {
+ jumpToTrack = value;
+ jumpTrackTimer.stop();
+ jumpTrackTimer.start(333);
+ }
+ }
+ else
+ {
+ QWidget::keyPressEvent(e);
+ }
+} //keyPressEvent
+
+void KSCD::jumpTracks()
+{
+ if (jumpToTrack > 0 && jumpToTrack <= (int)cddbInfo.trackInfoList.count())
+ {
+ m_cd->play(jumpToTrack, 0, jumpToTrack + 1);
+ }
+
+ jumpToTrack = 0;
+} // jumpTracks
+
+QString KSCD::currentTrackTitle()
+{
+ int track = m_cd->track();
+ return (track > -1) ? cddbInfo.trackInfoList[track-1].title : QString::null;
+}
+
+QString KSCD::currentAlbum()
+{
+ return cddbInfo.title;
+}
+
+QString KSCD::currentArtist()
+{
+ return cddbInfo.artist;
+}
+
+QStringList KSCD::trackList()
+{
+ QStringList tracks;
+ for (TrackInfoList::const_iterator it = cddbInfo.trackInfoList.begin();
+ it != cddbInfo.trackInfoList.end(); ++it)
+ tracks << (*it).title;
+
+ return tracks;
+}
+
+void KSCD::populateSongList(QString infoStatus)
+{
+ // set the artist and title labels as well as the dock tooltip.
+ if (!infoStatus.isEmpty())
+ artistlabel->setText(infoStatus);
+ else
+ artistlabel->setText(QString("%1 - %2").arg(cddbInfo.artist, cddbInfo.title));
+
+ songListCB->clear();
+ for (unsigned i = 0; i < cddbInfo.trackInfoList.count(); i++)
+ {
+ unsigned tmp = m_cd->trackLength(i + 1);
+ unsigned mymin;
+ unsigned mysec;
+ mymin = tmp / 60000;
+ mysec = (tmp % 60000) / 1000;
+ QString str1;
+ str1.sprintf("%02d: ", i + 1);
+ QString str2;
+ str2.sprintf(" (%02d:%02d) ", mymin, mysec);
+ str1.append(cddbInfo.trackInfoList[i].title);
+ str1.append(str2);
+ songListCB->insertItem(str1);
+ }
+
+ emit trackChanged(m_cd->track(), m_cd->trackLength());
+}
+
+static const KCmdLineOptions options[] =
+{
+ {"s",0,0},
+ {"start",I18N_NOOP("Start playing"),0},
+ {"+[device]",I18N_NOOP("CD device, can be a path or a media:/ URL"),0},
+ KCmdLineLastOption
+};
+
+
+/**
+ * main()
+ */
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData("kscd", I18N_NOOP("KsCD"),
+ KSCDVERSION, description,
+ KAboutData::License_GPL,
+ "(c) 2001, Dirk Försterling\n(c) 2003, Aaron J. Seigo");
+ aboutData.addAuthor("Aaron J. Seigo", I18N_NOOP("Current maintainer"), "aseigo@kde.org");
+ aboutData.addAuthor("Alexander Kern",I18N_NOOP("Workman library update, CDTEXT, CDDA"), "kernalex@kde.org");
+ aboutData.addAuthor("Bernd Johannes Wuebben",0, "wuebben@kde.org");
+ aboutData.addAuthor("Dirk Försterling", I18N_NOOP("Workman library, previous maintainer"), "milliByte@gmx.net");
+ aboutData.addCredit("Wilfried Huss", I18N_NOOP("Patches galore"));
+ aboutData.addCredit("Steven Grimm", I18N_NOOP("Workman library"));
+ aboutData.addCredit("Sven Lueppken", I18N_NOOP("UI Work"));
+ aboutData.addCredit("freedb.org", I18N_NOOP("Special thanks to freedb.org for providing a free CDDB-like CD database"), 0, "http://freedb.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions(options);
+ KUniqueApplication::addCmdLineOptions();
+
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+ if (!KUniqueApplication::start())
+ {
+ fprintf(stderr, "kscd is already running\n");
+ if (args->count()>0 || args->isSet("start"))
+ {
+ DCOPClient client;
+ if (client.attach())
+ {
+ // Forward the command line args to the running instance.
+ DCOPRef ref("kscd", "CDPlayer");
+ if (args->count() > 0)
+ {
+ ref.send("setDevice(QString)", QString(args->arg(0)));
+ }
+ if (args->isSet("start"))
+ {
+ ref.send("play()");
+ }
+ }
+ }
+ exit(0);
+ }
+
+ KUniqueApplication a;
+
+ kapp->dcopClient()->setDefaultObject("CDPlayer");
+
+ KSCD *k = new KSCD();
+
+ a.setTopWidget( k );
+ a.setMainWidget( k );
+
+ k->setCaption(a.caption());
+
+ if (kapp->isRestored())
+ {
+ KConfig* config = KApplication::kApplication()->sessionConfig();
+ config->setGroup("General");
+ if (config->readBoolEntry("Show"))
+ k->show();
+ }
+ else
+ {
+ k->show();
+ }
+
+ if (args->count()>0) Prefs::self()->setCdDevice(args->arg(0));
+
+ return a.exec();
+}
+
+#include "kscd.moc"
diff --git a/kscd/kscd.desktop b/kscd/kscd.desktop
new file mode 100644
index 00000000..cf0535e3
--- /dev/null
+++ b/kscd/kscd.desktop
@@ -0,0 +1,85 @@
+[Desktop Entry]
+GenericName=CD Player
+GenericName[af]=Cd Speler
+GenericName[ar]=مشغل الأقراص المدمجة
+GenericName[bg]=Плеър за аудио дискове
+GenericName[bn]=সিডি প্লেয়ার
+GenericName[br]=Ur c'hoarier CD
+GenericName[ca]=Reproductor de CD
+GenericName[cs]=CD přehrávač
+GenericName[cy]=Chwaraewr CD
+GenericName[da]=Cd-afspiller
+GenericName[de]=CD-Wiedergabe
+GenericName[el]=Αναπαραγωγέας CD
+GenericName[eo]=Ludilo por muzikaj lumdiskoj
+GenericName[es]=Reproductor de CDs
+GenericName[et]=CD mängija
+GenericName[eu]=CD erreproduzigailua
+GenericName[fa]=پخش‌کنندۀ دیسک فشرده
+GenericName[fi]=CD-soitin
+GenericName[fr]=Lecteur de CD audio
+GenericName[ga]=Seinnteoir Dlúthdhioscaí
+GenericName[gl]=Reproductor de CD
+GenericName[he]=נגן תקליטורים
+GenericName[hi]=सीडी प्लेयर
+GenericName[hu]=CD-lejátszó
+GenericName[is]=CD spilari
+GenericName[it]=Lettore CD
+GenericName[ja]=CD プレーヤ
+GenericName[kk]=CD ойнатқышы
+GenericName[km]=កម្មវិធី​ចាក់​ស៊ីឌី
+GenericName[ko]=CD 재생기
+GenericName[lt]=CD grotuvas
+GenericName[lv]=CD Atskaņotājs
+GenericName[mk]=CD плеер
+GenericName[ms]=Pemain CD
+GenericName[nb]=CD-spiller
+GenericName[nds]=CD-Afspeler
+GenericName[ne]=सीडी प्लेयर
+GenericName[nl]=CD-speler
+GenericName[nn]=CD-spelar
+GenericName[pa]=CD ਪਲੇਅਰ
+GenericName[pl]=Odtwarzacz CD
+GenericName[pt]=Leitor de CDs
+GenericName[pt_BR]=Reprodutor de CDs
+GenericName[ro]=Redare CD audio
+GenericName[ru]=Проигрыватель аудиодисков
+GenericName[se]=CD-čuojaheaddji
+GenericName[sk]=CD prehrávač
+GenericName[sl]=Predvajalnik CD-jev
+GenericName[sr]=CD плејер
+GenericName[sr@Latn]=CD plejer
+GenericName[sv]=Cd-spelare
+GenericName[ta]=குறுந்தகடு இயக்கி
+GenericName[tg]=Бозингари Диски Фишурда
+GenericName[th]=โปรแกรมเล่นซีดี
+GenericName[tr]=CD Çalar
+GenericName[uk]=Програвач КД
+GenericName[uz]=Kompakt-disk pleyer
+GenericName[uz@cyrillic]=Компакт-диск плейер
+GenericName[ven]=Tshitambi tsha CD
+GenericName[wa]=Djoweu di plakes lazer
+GenericName[xh]=Umdlali We CD
+GenericName[zh_CN]=CD 播放器
+GenericName[zh_HK]=CD 播放器
+GenericName[zh_TW]=CD 播放器
+GenericName[zu]=Umdlali we CD
+Name=KsCD
+Name[af]=Kscd
+Name[bn]=কে-এস-সিডি
+Name[eo]=Lumdiskludilo
+Name[hi]=के-एससीडी
+Name[sv]=Kscd
+Name[ta]=Ksகுறுந்தகடு
+Name[tg]=KsДиски Фишурда
+Name[tr]=Kscd
+Name[zh_TW]=KsCD 播放器
+Exec=kscd -caption "%c" %i %m
+DocPath=kscd/index.html
+Icon=kscd
+Path=
+Type=Application
+Terminal=false
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;AudioVideo;
diff --git a/kscd/kscd.h b/kscd/kscd.h
new file mode 100644
index 00000000..6c94c09d
--- /dev/null
+++ b/kscd/kscd.h
@@ -0,0 +1,258 @@
+/*
+ Kscd - A simple cd player for the KDE Project
+
+ $Id$
+
+ Copyright (c) 1997 Bernd Johannes Wuebben math.cornell.edu
+ Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org>
+ Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KSCD__
+#define __KSCD__
+
+#include "bwlednum.h"
+
+// CD support.
+class KCompactDisc;
+
+// CDDB support via libkcddb
+#include <libkcddb/cddb.h>
+#include <libkcddb/client.h>
+
+
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qdialog.h>
+#include <qapplication.h>
+#include <qtimer.h>
+#include <qcombobox.h>
+#include <qscrollbar.h>
+#include <qslider.h>
+#include <qtabdialog.h>
+#include <qtooltip.h>
+#include <qpopupmenu.h>
+#include <qvaluelist.h>
+
+#include "ledlamp.h"
+#include "panel.h"
+#include "prefs.h"
+#include "configWidget.h"
+#include <kapplication.h>
+#include <kconfigdialog.h>
+#include <kprocess.h>
+#include <krandomsequence.h>
+#include <dcopobject.h>
+#include <kglobalaccel.h>
+
+class CDDBDlg;
+class DockWidget;
+class QGridLayout;
+class KActionCollection;
+class KToggleAction;
+
+using namespace KCDDB;
+
+typedef QValueList<int> RandomList;
+
+class KSCD : public kscdPanelDlg, public KSessionManaged, virtual public DCOPObject {
+
+ Q_OBJECT
+ K_DCOP
+
+ // time display modes
+ enum time_display { TRACK_SEC = 0, TRACK_REM = 1, TOTAL_SEC = 2, TOTAL_REM = 3 };
+
+
+k_dcop:
+ bool playing();
+ void play() { playClicked(); }
+ void stop() { stopClicked(); }
+ void previous() { prevClicked(); }
+ void next() { nextClicked(); }
+ void jumpTo(int seconds) { jumpToTime(seconds); }
+ void eject() { ejectClicked(); }
+ void quit() { quitClicked(); }
+ void toggleLoop() { loopClicked(); }
+ void toggleShuffle() { randomSelected(); }
+ void toggleTimeDisplay() { cycleplaytimemode(); }
+ void cddbDialog() { CDDialogSelected(); }
+ void optionDialog() { showConfig(); }
+ void setTrack(int t) { trackSelected(t > 0 ? t - 1 : 0); }
+ void volumeDown() { decVolume(); }
+ void volumeUp() { incVolume(); }
+ void setVolume(int v);
+ void setDevice(const QString& dev);
+ int getVolume() { return Prefs::volume(); }
+ int currentTrack();
+ int currentTrackLength();
+ int currentPosition();
+ int getStatus();
+ QString currentTrackTitle();
+ QString currentAlbum();
+ QString currentArtist();
+ QStringList trackList();
+
+public:
+ KSCD( QWidget *parent = 0, const char *name = 0 );
+ ~KSCD();
+ virtual bool saveState(QSessionManager& sm);
+
+ void setDocking(bool dock);
+ bool digitalPlayback();
+ void setDevicePaths();
+ QStringList audioSystems() { return audio_systems_list; }
+
+ KActionCollection* actionCollection() { return m_actions; }
+
+signals:
+ void trackChanged(const QString&);
+
+public slots:
+ void setColors();
+ void togglequeryled();
+ void randomSelected();
+ void setShuffle(int shuffle); /* 0 -off, 1 - on, 2 - remake random list */
+ void writeSettings();
+ void playClicked();
+ bool nextClicked();
+ void prevClicked();
+ void stopClicked();
+ void ejectClicked();
+ void jumpToTime(int seconds, bool forcePlay = false);
+ void quitClicked();
+ void loopOn();
+ void loopOff();
+ void loopClicked();
+ void trackSelected(int);
+ void showConfig();
+ void incVolume();
+ void decVolume();
+ void volChanged(int);
+ void led_on();
+ void led_off();
+ void titlelabeltimeout();
+ void cycleplaytimemode();
+ void cycletimeout();
+
+ void information(int);
+ void jumpTracks();
+
+ void make_random_list(); /* koz: 15/01/00 */
+
+protected:
+ // mostly start up stuff
+ void readSettings();
+ void initFont();
+ void drawPanel();
+ void setupPopups();
+ void setLEDs(int milliseconds);
+ void resetTimeSlider(bool enabled);
+
+ void dragTime(int sec);
+
+ void closeEvent(QCloseEvent *e);
+ void keyPressEvent( QKeyEvent* e );
+ bool event( QEvent *e );
+ // void focusOutEvent(QFocusEvent *e);
+ void playtime();
+ void playtime(int seconds);
+ void calculateDisplayedTime();
+ void calculateDisplayedTime(int sec);
+ void setSongListTo(int whichTrack);
+ void populateSongList(QString infoStatus);
+ void updatePlayPB(bool playing);
+
+ void updateConfigDialog(configWidget* widget);
+
+private:
+ KConfigDialog *configDialog;
+ CDDBDlg *cddialog;
+ QPopupMenu *mainPopup;
+ QPopupMenu *infoPopup;
+
+ BW_LED_Number *trackTimeLED[6];
+
+ KCompactDisc *m_cd;
+ QTimer titlelabeltimer;
+ QTimer queryledtimer;
+ QTimer cycletimer;
+ QTimer jumpTrackTimer;
+
+ // random playlists
+ KRandomSequence randSequence;
+ RandomList random_list;
+ RandomList::iterator random_current;
+
+
+ int jumpToTrack;
+ LedLamp *queryled;
+ LedLamp *loopled;
+ bool randomplay_pending;
+ bool updateTime;
+ QStringList audio_systems_list;
+
+ /**
+ * select a random track from the current disc.
+ *
+ */
+ int next_randomtrack();
+ int prev_randomtrack();
+ int real_randomtrack();
+
+ void setTitle(int track);
+
+ /**
+ * Info from CDDB, and exploded versions thereof.
+ */
+ KCDDB::CDInfo cddbInfo;
+ QStringList playlist;
+ KCDDB::Client* cddb;
+ KActionCollection* m_actions;
+ KGlobalAccel* m_globalAccel;
+ KToggleAction* m_togglePopupsAction;
+ DockWidget* m_dockWidget;
+ void lookupDevice();
+ void initGlobalShortcuts();
+public slots:
+ void lookupCDDB();
+
+private slots:
+ void CDDialogSelected();
+ void CDDialogDone();
+ void setCDInfo(KCDDB::CDInfo);
+ void lookupCDDBDone(CDDB::Result);
+ void discStopped();
+ void trackUpdate(unsigned track, unsigned trackPosition);
+ void trackChanged(unsigned track, unsigned trackLength);
+ void discChanged(unsigned discId);
+// void trayClosing();
+ void trayOpening();
+ void configDone();
+ void configureKeys();
+ void configureGlobalKeys();
+ void setIcons();
+
+ void timeSliderPressed();
+ void timeSliderReleased();
+ void timeSliderMoved(int milliseconds);
+};
+
+
+
+#endif
+
diff --git a/kscd/kscd.kcfg b/kscd/kscd.kcfg
new file mode 100644
index 00000000..62a04984
--- /dev/null
+++ b/kscd/kscd.kcfg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kscdrc"/>
+ <group name="GENERAL">
+ <entry name="Volume" type="UInt">
+ <label>Output Volume</label>
+ <default>40</default>
+ </entry>
+ <entry name="RandomPlay" type="Bool">
+ <label>Play random tracks.</label>
+ <whatsthis>When this option is selected the playing order
+ of the CD tracks is chosen at random.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="Docking" type="Bool" key="DOCKING">
+ <label>Show an icon in the system tray.</label>
+ <whatsthis>When this option is selected an icon will appear in the system tray. Note that KsCD will &lt;i&gt;not&lt;/i&gt; quit when the window is closed if a system tray icon is displayed. You may quit KsCD by clicking the Quit button or right-clicking on the system tray icon and selecting the appropriate entry.</whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="TrackAnnouncement" type="Bool">
+ <default>false</default>
+ </entry>
+ <entry name="Autoplay" type="Bool" key="AUTOPLAY">
+ <label>Start playing when a CD is inserted.</label>
+ <whatsthis>When this option is selected the CD will start playing automatically upon being inserted into the CD-ROM.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="StopExit" type="Bool" key="STOPEXIT">
+ <label>Stop playing the CD on program exit.</label>
+ <whatsthis>When this option is selected the CD will automatically stop playing when quitting KsCD.</whatsthis>
+ <default>true</default>
+ </entry>
+ <entry name="EjectOnFinish" type="Bool" key="EJECTONFINISH">
+ <label>Eject CD when playing is finished.</label>
+ <whatsthis>When this option is selected the CD will automatically eject when it is finished.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="Looping" type="Bool">
+ <label>Loop tracks.</label>
+ <default>false</default>
+ </entry>
+ <entry name="SkipDelta" type="UInt">
+ <label>Skip interval.</label>
+ <whatsthis>This option controls the number of seconds KsCD will skip when the skip forwards or backwards buttons are pressed.</whatsthis>
+ <default>30</default>
+ </entry>
+ <entry name="TimeDisplayMode" type="Enum" key="TimeDisplay">
+ <choices>
+ <choice name="TRACK_SEC"/>
+ <choice name="TRACK_REM"/>
+ <choice name="TOTAL_SEC"/>
+ <choice name="TOTAL_REM"/>
+ </choices>
+ <default>TRACK_SEC</default>
+ </entry>
+ <entry name="cdDevice" type="String" key="CDDevice">
+ <label>Name of the CD-ROM device.</label>
+ <whatsthis>The CD-ROM device to use when playing CDs. This will typically look something like "/dev/cdrom". To have KsCD autodetect your CD-ROM, leave this field empty.</whatsthis>
+ </entry>
+ <entry name="AudioSystem" type="String">
+ <label>The audio backend KsCD uses.</label>
+ </entry>
+ <entry name="AudioDevice" type="String">
+ <label>The audio device KsCD uses.</label>
+ </entry>
+ <entry name="DigitalPlayback" type="Bool">
+ <label>Use direct digital playback.</label>
+ <whatsthis>When this option is selected KsCD will attempt to play the CD using direct digital playback. This option is useful if the CD-ROM is not connected directly to the sound output on the computer. Note that digital playback is slower than the normal method of playback.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="ledColor" type="Color">
+ <label>The foreground color that will be used in the LCD display.</label>
+ <default>#e2e0ff</default>
+ </entry>
+ <entry name="ledFont" type="Font">
+ <label>The font that will be used for the LCD display.</label>
+ <default><code>QFont(KGlobalSettings::generalFont().family(), 10, QFont::Bold)</code></default>
+ </entry>
+ <entry name="BackColor" type="Color">
+ <label>The background color that will be used for the LCD display.</label>
+ <default>black</default>
+ </entry>
+ <entry name="SelectEncoding" type="Bool">
+ <label>Allow encoding selection.</label>
+ <whatsthis>When this option is selected, you have the ability to select encoding for the results of a CDDB request. The standard describes CDDB results as being strictly Latin1. This is not true, as non-English speaking users often use other 8-bit encodings.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry name="SelectedEncoding" type="Enum">
+ <choices>
+ <choice name="AUTO"/>
+ <choice name="utf8"/>
+ <choice name="CP1250"/>
+ <choice name="CP1251"/>
+ <choice name="CP1252"/>
+ <choice name="CP1253"/>
+ <choice name="CP1254"/>
+ <choice name="CP1255"/>
+ <choice name="CP1256"/>
+ <choice name="CP1257"/>
+ </choices>
+ <default>utf8</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kscd/kscd.lsm b/kscd/kscd.lsm
new file mode 100644
index 00000000..27cdb0dc
--- /dev/null
+++ b/kscd/kscd.lsm
@@ -0,0 +1,15 @@
+Begin3
+Title: Kscd
+Version: 0.4
+Entered-date: 08/14/97
+Description: A simple CD Player for the KDE Project
+Keywords: KDE, Qt, gui, X11, CD player, sound
+Author: <wuebben@math.cornell.edu> Bernd Johannes Wuebben
+Maintained-by: <wuebben@math.cornell.edu> Bernd Johannes Wuebben
+Primary-site: ftp://fiwi02.wiwi.uni-tuebingen.de/pub/kde/apps/multimedia
+Home-Page: http://math.cornell.edu/~wuebben/kde.html
+Alternate-site: ftp://math.cornell.edu/pub/wuebben/kscd
+Original-site: ftp://fiwi02.wiwi.uni-tuebingen.de/pub/kde/apps/multimedia
+Platform: Unix, Qt
+Copying-policy: GPL
+End
diff --git a/kscd/kscd.profile.xml b/kscd/kscd.profile.xml
new file mode 100644
index 00000000..542c5911
--- /dev/null
+++ b/kscd/kscd.profile.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+<!DOCTYPE profile SYSTEM "profile.dtd">
+
+<profile id="kscd" servicename="KsCD">
+ <name>KsCD</name>
+ <author>Aaron J. Seigo</author>
+ <action objid="CDPlayer" prototype="void play()" class="play" repeat="0" autostart="1">
+ <name>Play/Pause</name>
+ <comment>Toggles between playing and pausing.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void stop()" class="stop" repeat="0" autostart="0">
+ <name>Stop</name>
+ <comment>Stops the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void eject()" class="eject" repeat="0" autostart="0">
+ <name>Eject</name>
+ <comment>Ejects the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void forward()" class="forward" repeat="1" autostart="0">
+ <name>Fast Forward</name>
+ <comment>Skips forwards in the current track.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void backward()" class="rewind" repeat="1" autostart="0">
+ <name>Rewind</name>
+ <comment>Skips backwards in the current track.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void previous()" class="previous" repeat="0" autostart="0">
+ <name>Previous</name>
+ <comment>Skips to the previous track on the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void next()" class="next" repeat="0" autostart="0">
+ <name>Next</name>
+ <comment>Skips to the next track on the CD.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void setTrack(int)" class="number" repeat="0" autostart="1">
+ <name>Set Track</name>
+ <comment>Skips to a specific track on the CD.</comment>
+ <argument type="int">
+ <comment>The number of the track to skip to.</comment>
+ <range min="0" max="100"/>
+ </argument>
+ </action>
+ <action objid="CDPlayer" prototype="void setVolume(int)" repeat="0" autostart="0">
+ <name>Set Volume</name>
+ <comment>Sets the play volume.</comment>
+ <argument type="int">
+ <comment>The volume to set the CD player to.</comment>
+ <range min="0" max="100"/>
+ </argument>
+ </action>
+ <action objid="CDPlayer" prototype="void toggleLoop()" repeat="0" autostart="0">
+ <name>Loop</name>
+ <comment>Toggles looping on and off.</comment>
+ </action>
+ <action objid="CDPlayer" prototype="void toggleShuffle()" repeat="0" autostart="0">
+ <name>Shffule</name>
+ <comment>Toggles track shuffling on and off.</comment>
+ </action>
+</profile>
diff --git a/kscd/kscdmagic/Makefile.am b/kscd/kscdmagic/Makefile.am
new file mode 100644
index 00000000..c5cc3a21
--- /dev/null
+++ b/kscd/kscdmagic/Makefile.am
@@ -0,0 +1,40 @@
+
+# this 10 paths are KDE specific. Use them:
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_appsdir Where your application file (.kdelnk) should go to.
+# kde_icondir Where your icon should go to.
+# kde_minidir Where your mini icon should go to.
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to.(contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to.
+# kde_mimedir Where mimetypes should go to.
+# kde_toolbardir Where general toolbar icons should go to.
+# kde_wallpaperdir Where general wallpapers should go to.
+
+# just set the variable
+APPSDIR = $(kde_appsdir)/Multimedia
+
+# set the include path for X, qt and KDE
+INCLUDES= $(all_includes)
+
+####### This part is very kscdmagic specific
+# you can add here more. This one gets installed
+bin_PROGRAMS = kscdmagic
+
+# Which sources should be compiled for kscdmagic
+kscdmagic_SOURCES = core.cpp main.cpp sound.cpp xlibwrap.cpp xlib.c
+
+# the library search path.
+kscdmagic_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+# the libraries to link against. Be aware of the order. First the libraries,
+# that depend on the following ones.
+kscdmagic_LDADD = -lm $(LIB_KDEUI)
+
+# this option you can leave out. Just, if you use "make dist", you need it
+noinst_HEADERS = symbol.h syna.h xlib.h magicconf.h
+
+messages:
+# $(XGETTEXT) -kTRANS $(kscdmagic_SOURCES)
+
diff --git a/kscd/kscdmagic/README b/kscd/kscdmagic/README
new file mode 100644
index 00000000..64af4f6f
--- /dev/null
+++ b/kscd/kscdmagic/README
@@ -0,0 +1,146 @@
+
+kscdmagic is based on synaesthesia
+by Paul Harrison <pfh@yoyo.cc.monash.edu.au>.
+
+ -dirk <milliByte@gmx.net>
+
+
+
+
+SYNAESTHESIA v2.0
+
+Introduction
+============
+
+This is a program for representing sounds visually from a CD or line
+input or piped from another program. It goes beyond the usual oscilliscope
+style program by combining an FFT and stereo positioning information to
+give a two dimensional display. Some of the shapes I have observed are:
+ * Drums: clouds of color, fairly high
+ * Clean guitar: several horizontal lines, low down
+ * Rough guitar: a cloud, low down
+ * Trumpet: Lots of horizontal lines everywhere
+ * Flute: A single horizontal line, low down
+ * Voice: A vertical line with some internal structure
+ * Synthesizer: All kinds of weird shapes!
+
+Synaesthesia can run in a window in X or full screen using SVGAlib.
+
+The display represents frequency as vertical position on screen,
+left-right position as left-right position on screen. It can also
+understand surround sound encoded music, and shows ambient noise
+in orange.
+
+X-Windows support was added in version 1.3, as well as a major redesign
+of the interface. You can use Synaesthesia as a fully functional
+CD player, suitable for use while working.
+
+There is command line support for play lists and piping from another
+program (such as an mp3 player).
+
+Usage
+=====
+
+Synaesthesia should work on Linux and BSD systems. (Note: I don't
+have access to a BSD system myself, I have to rely on patches -- if it
+doesn't work, please tell me!) LinuxPPC users may have to use the pipe
+mode rather than taking sound input from the CD player, as I believe
+sound recording is not yet implemented.
+
+Compile Synaesthesia by typing
+
+ make
+
+then install it by typing
+
+ make install
+
+This will create three versions of Synaesthesia:
+
+ synaesthesia - full screen SVGAlib version (Linux only)
+ xsynaesthesia - Version that runs as a window in X
+ sdlsynaesthesia - Version that uses the SDL graphics library
+
+If you want to use the SDL version, you need to get SDL from
+http://www.devolution.com/~slouken/SDL.
+
+You will need to run Synaesthesia as root to run it full screen
+with SVGAlib. Other varieties can be run by any user providing you
+provide permissions on /dev/dsp, /dev/cdrom, and /dev/mixer.
+
+Synaesthesia creates a configuration file, named ~/.synaesthesia,
+to store settings such as brightness, color, and window size, as
+well as which devices to use to control sound input.
+
+BSD users will have to edit this file to set the CD-ROM device name
+before using Synaesthesia in order to control the CD.
+
+Run Synaesthesia with no parameters for further information on how to
+use it.
+
+Notes for code rippers
+======================
+
+This program contains code that you may wish to use in your own projects.
+If you want to, please do. (For example, you might want to add some
+snazzy visual effects to your favorite MP3 or CD player)
+
+The actual code to do the mapping from sound to visual display is
+all in core.cpp, it should be fairly easy to disentangle from other
+parts of the program. It does make reference to some globals defined
+in syna.h, namely the #defines m (log2 of the sample size for each
+frame) and brightness, data (which stores the sound input), outputBmp,
+lastOutputBmp and lastLastOutputBmp (which hold the output), outWidth
+and outHeight (size of the bitmaps), and fadeMode, brightnessTwiddler,
+starSize and pointsAreDiamonds (various parameters affecting the display).
+
+The normal way to use it would be:
+
+ Call coreInit() to set up some look-up tables
+ Call setStarSize(starSize) to set up some more look-up tables
+ Loop
+ Put data into the data array
+ Call fade() to apply the fade/wave/heat effect to the output
+ Call coreGo() to add the next fragment of sound input to the output
+ Display contents of outputBmp to screen
+
+There is a simple anti-aliased polygon drawing engine in the file
+polygon.h. sound.cpp contains code for driving the CD. xlib.c and
+xlibwrap.cpp contain code for setting up a window under X (originally
+ripped from the Xaos fractal viewer program :-) ).
+
+Authors
+=======
+
+This program is free. If you like it, or have any suggestions, please
+send me (Paul Harrison) an email (pfh@yoyo.cc.monash.edu.au).
+
+Thanks to Asger Alstrup Nielsen for many great suggestions, and for
+writing optimized 32 bit loops for fading and drawing to screen.
+
+Thanks to Roger Knobbe for porting Synaesthesia to FreeBSD.
+
+Thanks to Ben Gertzfield and Martin Mitchell for some small fixes to the
+CD controlling code.
+
+Thanks to Simon Budig for an improvement to the X code.
+
+Changes
+=======
+
+1.1 - Added surround sound decoding.
+1.2 - Fixed a bug in the ioctl calls to /dev/dsp.
+1.3 - Asger Alstrup Nielsen's optimizations added.
+ Added X-Windows support.
+ More options, redesigned interface.
+1.4 - Bug fixes, including a great reduction in
+ "Sound: Recording overrun" warnings.
+ New command line options: play lists and piping.
+ Support for SDL.
+2.0 - Bug fixes: Fixed problem in xlib.c that caused occasional segfaults,
+ several endianness problems fixed.
+ New effects: Wave, heat, diamond shaped points.
+ Piping sound now longer requires the twiddle factor.
+ Yet another interface redesign.
+ Partial support for LinuxPPC (pipe mode only)
+
diff --git a/kscd/kscdmagic/core.cpp b/kscd/kscdmagic/core.cpp
new file mode 100644
index 00000000..49fce661
--- /dev/null
+++ b/kscd/kscdmagic/core.cpp
@@ -0,0 +1,410 @@
+/* Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "syna.h"
+
+double cosTable[n], negSinTable[n];
+int bitReverse[n];
+int scaleDown[256];
+int maxStarRadius;
+
+int bitReverser(int i) {
+ int sum=0,j;
+ for(j=0;j<m;j++) {
+ sum = (i&1)+sum*2;
+ i >>= 1;
+ }
+ return sum;
+}
+
+void fft(double *x,double *y) {
+ int n2 = n, n1;
+ int twoToTheK;
+ for(twoToTheK=1;twoToTheK<n;twoToTheK*=2) {
+ n1 = n2;
+ n2 /= 2;
+ for(int j=0;j<n2;j++) {
+ double c = cosTable[j*twoToTheK&(n-1)],
+ s = negSinTable[j*twoToTheK&(n-1)];
+ for(int i=j;i<n;i+=n1) {
+ int l = i+n2;
+ double xt = x[i] - x[l];
+ x[i] = (x[i] + x[l]);
+ double yt = y[i] - y[l];
+ y[i] = (y[i] + y[l]);
+ x[l] = xt*c - yt*s;
+ y[l] = xt*s + yt*c;
+ }
+ }
+ }
+}
+
+void coreInit() {
+ int i;
+
+ for(i=0;i<n;i++) {
+ negSinTable[i] = -sin(3.141592*2.0/n*i);
+ cosTable[i] = cos(3.141592*2.0/n*i);
+ bitReverse[i] = bitReverser(i);
+ }
+}
+
+void setStarSize(double size) {
+ //int factor = (fadeMode == Flame ? 100 :
+ // (fadeMode == Wave ? 150 : 200));
+
+ double fadeModeFudge = (fadeMode == Wave ? 0.4 :
+ (fadeMode == Flame ? 0.6 : 0.78));
+
+ int factor;
+ if (size > 0.0)
+ factor = int(exp(log(fadeModeFudge) / (size*8.0))*255);
+ else
+ factor = 0;
+
+ if (factor > 255) factor = 255;
+
+ for(int i=0;i<256;i++)
+ scaleDown[i] = i*factor>>8;
+
+ maxStarRadius = 1;
+ for(int i=255;i;i = scaleDown[i])
+ maxStarRadius++;
+}
+
+inline void addPixel(int x,int y,int br1,int br2) {
+ if (x < 0 || x >= outWidth || y < 0 || y >= outHeight) return;
+
+ unsigned char *p = ucoutput+x*2+y*outWidth*2;
+ if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+ //p += lastOutput-output;
+ //if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ //if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+}
+
+inline void addPixelFast(unsigned char *p,int br1,int br2) {
+ if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+ //p += lastOutput-output;
+ //if (p[0] < 255-br1) p[0] += br1; else p[0] = 255;
+ //if (p[1] < 255-br2) p[1] += br2; else p[1] = 255;
+}
+
+void fadeFade() {
+ register unsigned long *ptr = (unsigned long*)ucoutput;
+ int i = outWidth*outHeight*2/4;
+ do {
+ //Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4;
+ if (*ptr)
+ //if (*ptr & 0xf0f0f0f0ul)
+ *(ptr++) -= ((*ptr & 0xf0f0f0f0ul) >> 4) + ((*ptr & 0xe0e0e0e0ul) >> 5);
+ //else {
+ // *(ptr++) = (*ptr * 14 >> 4) & 0x0f0f0f0ful;
+ //}
+ else
+ ptr++;
+ } while(--i > 0);
+}
+
+inline unsigned char getPixel(int x,int y,int where) {
+ if (x < 0 || y < 0 || x >= outWidth || y >= outHeight) return 0;
+ return lastOutput[where];
+}
+
+inline void fadePixelWave(int x,int y,int where,int step) {
+ short j =
+ ( short(getPixel(x-1,y,where-2))+
+ getPixel(x+1,y,where+2)+
+ getPixel(x,y-1,where-step)+
+ getPixel(x,y+1,where+step)
+ >> 2)
+ +lastOutput[where];
+ if (!j) { ucoutput[where] = 0; return; }
+ j = j
+ -lastLastOutput[where]
+ -1;
+ if (j < 0) ucoutput[where] = 0;
+ else if (j & (255*256)) ucoutput[where] = 255;
+ else ucoutput[where] = j;
+}
+
+void fadeWave() {
+ unsigned short *t = lastLastOutputBmp.data;
+ lastLastOutputBmp.data = lastOutputBmp.data;
+ lastOutputBmp.data = outputBmp.data;
+ outputBmp.data = t;
+
+ int x,y,i,j,start,end;
+ int step = outWidth*2;
+ for(x=0,i=0,j=outWidth*(outHeight-1)*2;x<outWidth;x++,i+=2,j+=2) {
+ fadePixelWave(x,0,i,step);
+ fadePixelWave(x,0,i+1,step);
+ fadePixelWave(x,outHeight-1,j,step);
+ fadePixelWave(x,outHeight-1,j+1,step);
+ }
+
+ for(y=1,i=outWidth*2,j=outWidth*4-2;y<outHeight;y++,i+=step,j+=step) {
+ fadePixelWave(0,y,i,step);
+ fadePixelWave(0,y,i+1,step);
+ fadePixelWave(outWidth-1,y,j,step);
+ fadePixelWave(outWidth-1,y,j+1,step);
+ }
+
+ for(y=1,
+ start=outWidth*2+2,
+ end=outWidth*4-2; y<outHeight-1; y++,start+=step,end+=step) {
+ int i = start;
+ do {
+ short j =
+ ( short(lastOutput[i-2])+
+ lastOutput[i+2]+
+ lastOutput[i-step]+
+ lastOutput[i+step]
+ >> 2)
+ +lastOutput[i];
+ if (!j) {
+ ucoutput[i] = 0;
+ } else {
+ j = j
+ -lastLastOutput[i]
+ -1;
+ if (j < 0) ucoutput[i] = 0;
+ else if (j & (255*256)) ucoutput[i] = 255;
+ else ucoutput[i] = j;
+ }
+ } while(++i < end);
+ }
+}
+
+inline void fadePixelHeat(int x,int y,int where,int step) {
+ short j =
+ ( short(getPixel(x-1,y,where-2))+
+ getPixel(x+1,y,where+2)+
+ getPixel(x,y-1,where-step)+
+ getPixel(x,y+1,where+step)
+ >> 2)
+ +lastOutput[where];
+ if (!j) { ucoutput[where] = 0; return; }
+ j = j
+ -lastLastOutput[where]
+ -1;
+ if (j < 0) ucoutput[where] = 0;
+ else if (j & (255*256)) ucoutput[where] = 255;
+ else ucoutput[where] = j;
+}
+
+void fadeHeat() {
+ unsigned short *t = lastLastOutputBmp.data;
+ lastLastOutputBmp.data = lastOutputBmp.data;
+ lastOutputBmp.data = outputBmp.data;
+ outputBmp.data = t;
+
+ int x,y,i,j,start,end;
+ int step = outWidth*2;
+ for(x=0,i=0,j=outWidth*(outHeight-1)*2;x<outWidth;x++,i+=2,j+=2) {
+ fadePixelHeat(x,0,i,step);
+ fadePixelHeat(x,0,i+1,step);
+ fadePixelHeat(x,outHeight-1,j,step);
+ fadePixelHeat(x,outHeight-1,j+1,step);
+ }
+
+ for(y=1,i=outWidth*2,j=outWidth*4-2;y<outHeight;y++,i+=step,j+=step) {
+ fadePixelHeat(0,y,i,step);
+ fadePixelHeat(0,y,i+1,step);
+ fadePixelHeat(outWidth-1,y,j,step);
+ fadePixelHeat(outWidth-1,y,j+1,step);
+ }
+
+ for(y=1,
+ start=outWidth*2+2,
+ end=outWidth*4-2; y<outHeight-1; y++,start+=step,end+=step) {
+ int i = start;
+ do {
+ short j =
+ ( short(lastOutput[i-2])+
+ lastOutput[i+2]+
+ +lastOutput[i-step]
+ +lastOutput[i+step]
+ >> 2)
+ +lastOutput[i];
+ if (!j) {
+ ucoutput[i] = 0;
+ } else {
+ j = j
+ -lastLastOutput[i]
+ +(lastLastOutput[i]
+ -lastOutput[i]>>2)
+ -1;
+ if (j < 0) ucoutput[i] = 0;
+ else if (j & (255*256)) ucoutput[i] = 255;
+ else ucoutput[i] = j;
+ }
+ } while(++i < end);
+ }
+}
+
+void fade() {
+ switch(fadeMode) {
+ case Stars :
+ fadeFade();
+ break;
+ case Flame :
+ fadeHeat();
+ break;
+ case Wave :
+ fadeWave();
+ break;
+ default:
+ break;
+ }
+}
+
+int coreGo() {
+ double x[n], y[n];
+ double a[n], b[n];
+ int clarity[n]; //Surround sound
+ int i,j,k;
+
+ int brightFactor = int(brightness * brightnessTwiddler /(starSize+0.01));
+
+ if (-1 == getNextFragment())
+ {
+ fprintf(stderr, "no frag\n" );
+ return -1;
+ }
+
+ for(i=0;i<n;i++) {
+ x[i] = data[i*2];
+ y[i] = data[i*2+1];
+ }
+
+ fft(x,y);
+
+ for(i=0 +1;i<n;i++) {
+ double x1 = x[bitReverse[i]],
+ y1 = y[bitReverse[i]],
+ x2 = x[bitReverse[n-i]],
+ y2 = y[bitReverse[n-i]],
+ aa,bb;
+ a[i] = sqrt(aa= (x1+x2)*(x1+x2) + (y1-y2)*(y1-y2) );
+ b[i] = sqrt(bb= (x1-x2)*(x1-x2) + (y1+y2)*(y1+y2) );
+ if (aa+bb != 0.0)
+ clarity[i] = (int)(
+ ( (x1+x2) * (x1-x2) + (y1+y2) * (y1-y2) )/(aa+bb) * 256 );
+ else
+ clarity[i] = 0;
+ }
+
+ // Asger Alstrupt's optimized 32 bit fade
+ // (alstrup@diku.dk)
+ /*register unsigned long *ptr = (unsigned long*)output;
+ i = outWidth*outHeight*2/4;
+ do {
+ //Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4;
+ if (*ptr)
+ if (*ptr & 0xf0f0f0f0ul)
+ *(ptr++) -= ((*ptr & 0xf0f0f0f0ul) >> 4) + ((*ptr & 0xe0e0e0e0ul) >> 5);
+ else {
+ *(ptr++) = (*ptr * 14 >> 4) & 0x0f0f0f0ful;
+ //Should be 29/32 to be consistent. Who cares. This is totally
+ // hacked anyway.
+ //unsigned char *subptr = (unsigned char*)(ptr++);
+ //subptr[0] = (int)subptr[0] * 29 / 32;
+ //subptr[1] = (int)subptr[0] * 29 / 32;
+ //subptr[2] = (int)subptr[0] * 29 / 32;
+ //subptr[3] = (int)subptr[0] * 29 / 32;
+ }
+ else
+ ptr++;
+ } while(--i > 0);
+ */
+
+ int heightFactor = n/2 / outHeight + 1;
+ int actualHeight = n/2/heightFactor;
+ int heightAdd = outHeight + actualHeight >> 1;
+
+ /* Correct for window size */
+ double brightFactor2 = (brightFactor/65536.0/n)*
+ sqrt(actualHeight*outWidth/(320.0*200.0));
+
+ for(i=1;i<n/2;i++) {
+ //int h = (int)( b[i]*280 / (a[i]+b[i]+0.0001)+20 );
+ if (a[i] > 0 || b[i] > 0) {
+ int h = (int)( b[i]*outWidth / (a[i]+b[i]) );
+ int br1, br2, br = (int)(
+ (a[i]+b[i])*i*brightFactor2 );
+ br1 = br*(clarity[i]+128)>>8;
+ br2 = br*(128-clarity[i])>>8;
+ if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255;
+ if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255;
+ //unsigned char *p = output+ h*2+(164-((i<<8)>>m))*(outWidth*2);
+ int px = h,
+ py = heightAdd - i / heightFactor;
+
+ if (pointsAreDiamonds) {
+ addPixel(px,py,br1,br2);
+ br1=scaleDown[br1];br2=scaleDown[br2];
+
+ //TODO: Use addpixelfast
+ for(j=1;br1>0||br2>0;j++,br1=scaleDown[br1],br2=scaleDown[br2]) {
+ for(k=0;k<j;k++) {
+ addPixel(px-j+k,py-k,br1,br2);
+ addPixel(px+k,py-j+k,br1,br2);
+ addPixel(px+j-k,py+k,br1,br2);
+ addPixel(px-k,py+j-k,br1,br2);
+ }
+ }
+ } else {
+ if (px < maxStarRadius || py < maxStarRadius ||
+ px > outWidth-maxStarRadius || py > outHeight-maxStarRadius) {
+ addPixel(px,py,br1,br2);
+ for(j=1;br1>0||br2>0;j++,br1=scaleDown[br1],br2=scaleDown[br2]) {
+ addPixel(px+j,py,br1,br2);
+ addPixel(px,py+j,br1,br2);
+ addPixel(px-j,py,br1,br2);
+ addPixel(px,py-j,br1,br2);
+ }
+ } else {
+ unsigned char *p = ucoutput+px*2+py*outWidth*2, *p1=p, *p2=p, *p3=p, *p4=p;
+ addPixelFast(p,br1,br2);
+ for(;br1>0||br2>0;br1=scaleDown[br1],br2=scaleDown[br2]) {
+ p1 += 2;
+ addPixelFast(p1,br1,br2);
+ p2 -= 2;
+ addPixelFast(p2,br1,br2);
+ p3 += outWidth*2;
+ addPixelFast(p3,br1,br2);
+ p4 -= outWidth*2;
+ addPixelFast(p4,br1,br2);
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/kscd/kscdmagic/logo.h b/kscd/kscdmagic/logo.h
new file mode 100644
index 00000000..478f8c70
--- /dev/null
+++ b/kscd/kscdmagic/logo.h
@@ -0,0 +1,50 @@
+int logo[48][48] = {
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,0,0,0,17,17,0,0,17,0,0,0,17,0,17,17,17,0,0,0,17,0,0,0,17,17,0,0,17,17,17},
+{0,0,0,0,0,0,0,0,17,17,17,17,17,0,0,17,0,0,17,0,17,0,0,17,0,17,17,0,17,17,0,17,0,0,17,0,17,0,17,0,17,0,0,17,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,0,17,0,0,17,0,17,0,17,0,17,0,17,0,0,17,0,17,0,17,0,17,0,0,0,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,17,0,17,17,17,0,0,17,17,17,0,17,0,0,17,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,17,17,0,0,0,17,17,0,0,17,0,0,0,17,0,17,0,0,0,0,17,0,17,0,0,17,17,0,0,0,17,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,17,17,17,17,17,17,17,0,0,0,17,0,0,17,17,17,17,17,0,0,0,0,17,17,17,17,17,17,17,17,17,17,0,0,0,0,17,17,17,17,17,17,17,17,0,0},
+{0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,17,0},
+{17,0,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17},
+{17,0,0,0,0,17,17,17,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,17,17,0,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17,17,17,17,17,17,17,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17,17,17,17,17,17,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,17,17,17,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,0,0,0},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,17,17,17,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,17,17,17,17,17,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,17,17,17,17,17,17,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17},
+{17,0,0,0,0,17,17,17,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,17,17,0,0,0,0,17},
+{17,0,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17},
+{0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,0,17,0,0,0,0,0,0,0,0,17,0},
+{0,0,17,17,17,17,17,17,17,17,17,17,17,0,0,17,17,17,17,17,0,0,17,17,17,17,17,17,17,17,17,17,0,0,0,0,0,0,17,17,17,17,17,17,17,17,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{17,17,17,0,0,17,0,0,17,17,17,0,17,0,17,17,17,0,0,17,0,0,17,0,0,0,0,0,0,17,0,0,17,0,0,17,0,17,17,17,0,0,17,0,0,17,17,0},
+{17,0,0,17,0,17,0,17,0,0,0,0,17,0,0,17,0,0,17,0,17,0,17,0,0,0,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17},
+{17,0,0,17,0,17,0,17,0,17,17,0,17,0,0,17,0,0,17,0,17,0,17,0,0,0,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17},
+{17,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,0,17,17,17,0,17,0,0,0,0,0,17,17,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17},
+{17,17,17,0,0,17,0,0,17,17,17,0,17,0,0,17,0,0,17,0,17,0,17,17,17,0,0,0,17,0,17,0,0,17,17,0,0,17,17,17,0,0,17,0,0,17,17,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
+};
diff --git a/kscd/kscdmagic/magicconf.h b/kscd/kscdmagic/magicconf.h
new file mode 100644
index 00000000..86ed42de
--- /dev/null
+++ b/kscd/kscdmagic/magicconf.h
@@ -0,0 +1,23 @@
+
+#ifndef __MAGIC_CONF_H_
+#define __MAGIC_CONF_H_
+
+/***************************************/
+/* For the incurably fiddle prone: */
+
+/* log2 of sample size */
+#define m 8
+
+/* overlap amount between samples. Set to 1 or 2 if you have a fast computer */
+#define overlap 0
+
+/* Brightness */
+#define brightness 150
+
+/* Sample frequency*/
+#define frequency 22050
+
+#define MITSHM 1 /* use MIT X11 shared memory*/
+
+/***************************************/
+#endif
diff --git a/kscd/kscdmagic/main.cpp b/kscd/kscdmagic/main.cpp
new file mode 100644
index 00000000..7196d3a2
--- /dev/null
+++ b/kscd/kscdmagic/main.cpp
@@ -0,0 +1,300 @@
+/*
+
+ $Id$
+
+ kscdmagic 2.0 Dirk Frsterling <milliByte@gmx.de>
+
+ based on:
+
+ kscdmagic 1.0 Bernd Johannes Wuebben <wuebben@kde.org>
+
+ based on:
+
+ Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#if defined(__linux__) || defined(__svr4__) || defined(__osf__)
+
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <string.h>
+#if defined(__linux__)
+#include <getopt.h>
+#endif
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <dcopclient.h>
+#include <kcmdlineargs.h>
+
+#include "logo.h"
+#include "magicconf.h"
+#include "syna.h"
+#include "version.h"
+
+volatile short *data;
+
+int outWidth, outHeight;
+double brightnessTwiddler;
+SymbolID fadeMode = Stars;
+double starSize = 0.125;
+bool pointsAreDiamonds = true;
+
+
+const int numRows = 4;
+const int rowHeight = 50;
+const int leftColWidth = 40;
+const int rowMaxWidth = 310;
+const int sliderBorder = 20;
+const int sliderWidth = rowMaxWidth - leftColWidth - sliderBorder*2;
+const int numberSpacing = 15;
+const int uiWidth = 330;
+const int uiHeight = 135;
+
+
+static int isExpanded = 0;
+static double bright = 1.0;
+
+Bitmap<unsigned short> outputBmp, lastOutputBmp, lastLastOutputBmp;
+PolygonEngine<unsigned short,combiner,2> polygonEngine;
+
+void
+allocOutput(int w,int h)
+{
+ outputBmp.size(w,h);
+ lastOutputBmp.size(w,h);
+ lastLastOutputBmp.size(w,h);
+ polygonEngine.size(w,h);
+ outWidth = w;
+ outHeight = h;
+} // allocOutput()
+
+void
+setBrightness(double bright)
+{
+ brightnessTwiddler = bright;
+} // setBrightness()
+
+
+
+static void
+cleanup( int sig )
+{
+ (void) sig;
+ closeSound();
+ exit(0);
+} // cleanup()
+
+
+// make sure the pid file is cleaned up when exiting unexpectedly.
+
+void
+catchSignals()
+{
+ signal(SIGHUP, cleanup); /* Hangup */
+ signal(SIGINT, cleanup); /* Interrupt */
+ signal(SIGTERM, cleanup); /* Terminate */
+ signal(SIGPIPE, cleanup);
+ signal(SIGQUIT, cleanup);
+} // catchSignals()
+
+void
+usage(char*)
+{
+ fprintf(stderr, "Valid command line options:\n");
+ fprintf(stderr, " -b set brightness (1 - 10)\n");
+ fprintf(stderr, " -w set width\n");
+ fprintf(stderr, " -h set height\n");
+ exit(1);
+} // usage()
+
+void
+error(const char *str, bool syscall) {
+ fprintf(stderr, PROGNAME ": Error %s\n",str);
+ if (syscall)
+ fprintf(stderr,"(reason for error: %s)\n",strerror(errno));
+ exit(1);
+} // error()
+
+void
+warning(const char *str, bool syscall) {
+ fprintf(stderr, PROGNAME ": Possible error %s\n",str);
+ if (syscall)
+ fprintf(stderr,"(reason for error: %s)\n",strerror(errno));
+} // warning()
+
+
+
+int
+processUserInput()
+{
+
+ int mouseX, mouseY, mouseButtons;
+ char keyHit;
+
+ inputUpdate(mouseX,mouseY,mouseButtons,keyHit);
+
+ if( keyHit == 'q' )
+ return -1;
+
+
+ if (sizeUpdate())
+ {
+ isExpanded = 0;
+ }
+
+ return 0;
+} // processUserInput()
+
+int
+main(int argc, char **argv)
+{
+ int windX=10;
+ int windY=30;
+ int windWidth=uiWidth;
+ int windHeight=uiHeight;
+ int c;
+ int xx, xy;
+ opterr = 0;
+
+ /*
+ KAboutData aboutData( "kscdmagic", I18N_NOOP("kscdmagic"),
+ KSCDMAGICVERSION, I18N_NOOP("sound visualisation"),
+ KAboutData::License_GPL,
+ "(c) 2000, Dirk Frsterling");
+ aboutData.addAuthor("Paul Harrison",0, "pfh@yoyo.cc.monash.edu.au");
+ aboutData.addAuthor("Dirk Frsterling",0, "milliByte@gmx.net");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+
+ KApplication magicApp;
+ */
+
+
+ openSound(SourceCD, 44100, "/dev/dsp", NULL);
+
+ catchSignals();
+
+ while ((c = getopt(argc, argv, "b:h:w:")) != -1){
+ switch (c)
+ {
+ case '?':
+ fprintf(stderr, "%s: unknown option \"%s\"\n",
+ argv[0], argv[optind-1]);
+ usage(argv[0]);
+ exit(1);
+ case 'b':
+ bright = (double) atoi(optarg);
+ bright = bright/10;
+ break;
+ case 'w':
+ windWidth = atoi(optarg);
+ break;
+ case 'h':
+ windHeight = atoi(optarg);
+ break;
+ }
+ }
+
+ if (bright > 1.0)
+ bright = 1.0;
+ else if (bright < 0.0)
+ bright = 0.0;
+
+ if (windWidth < 1)
+ windWidth = uiWidth;
+ if (windHeight < 1)
+ windHeight = uiHeight;
+
+ screenInit(windX,windY,windWidth,windHeight);
+
+ allocOutput(outWidth,outHeight);
+
+ coreInit();
+
+
+ setStarSize(starSize);
+ setBrightness(bright);
+
+ time_t timer = time(NULL);
+
+ int frames = 0;
+
+ for(;;) {
+ fade();
+ if (-1 == coreGo())
+ break;
+
+ polygonEngine.clear();
+
+ for( xy = 0; xy < 48; xy++)
+ {
+ for( xx = 0; xx < 48; xx++)
+ {
+ if ( logo[xy][xx] != 0)
+ {
+ polygonEngine.add(32769, xx+10, xy+3);
+ }
+ }
+ }
+ polygonEngine.apply(outputBmp.data);
+ screenShow();
+
+
+
+ frames++;
+ if(processUserInput() == -1)
+ break;
+ }
+
+
+ timer = time(NULL) - timer;
+ delete ucoutput;
+ closeSound();
+
+ if (timer > 10)
+ fprintf(stderr,"Frames per second: %f\n", double(frames)/timer);
+
+ return 0;
+} // main() /* linux */
+
+#else
+
+int main()
+{
+ fprintf(stderr,"KSCD Magic works currently only on Linux.\n"\
+ "It should however be trivial to port it to other platforms ...\n");
+} // main() /* non-linux */
+
+#endif
+
diff --git a/kscd/kscdmagic/polygon.h b/kscd/kscdmagic/polygon.h
new file mode 100644
index 00000000..4337ab10
--- /dev/null
+++ b/kscd/kscdmagic/polygon.h
@@ -0,0 +1,106 @@
+#include <string.h>
+
+template<class Pixel,int extra=0>
+struct Bitmap {
+ Pixel *data;
+ int width, height;
+
+ Bitmap() : data(0) { };
+ ~Bitmap() { delete[] data; };
+
+ void size(int w,int h) {
+ delete[] data;
+ width = w;
+ height = h;
+ data = new Pixel[w*h+extra];
+ clear();
+ }
+
+ void clear() {
+ memset(data,0,sizeof(Pixel)*(width*height+extra));
+ }
+};
+
+template<class Pixel, Pixel combine(Pixel a,Pixel b), int superSampleShift>
+struct PolygonEngine : public Bitmap<Pixel,1> {
+#define super (1<<superSampleShift)
+ void apply(Pixel *dest) {
+ /* Pixel sum=0; */
+ int count = width*height;
+ Pixel *src = data;
+ /*
+ * I don't really want this.
+ *
+ while(count--) {
+ sum += *(src++);
+ if (sum)
+ *dest = combine(sum,*dest);
+ dest++;
+ }
+ */
+ while(count--)
+ {
+ *dest = combine(*src, *dest);
+ src++;
+ dest++;
+ }
+ }
+
+ void add(Pixel color,int x,int y) {
+ if (y < 0) return;
+ if (y >= height) return;
+ if (x < 0) x = 0;
+ if (x > width) x = width;
+ data[x+y*width] += color;
+ }
+
+ /* Color is char[layers] */
+
+ // zwoosh, yknow, it goes... zwoosh an all these bars and lines and
+ // crap intersect.
+ Pixel colorTable[2][super+1];
+ void pen(Pixel color) {
+ for(int i=0;i<super+1;i++) {
+ colorTable[0][i] = color*i;
+ colorTable[1][i] = -(color*i);
+ }
+ }
+
+ void line(int x1,int y1,int x2,int y2) {
+ Pixel *colors;
+ if (y2 < y1) {
+ int temp;
+ temp = x2; x2 = x1; x1 = temp;
+ temp = y2; y2 = y1; y1 = temp;
+ colors = colorTable[1];
+ } else {
+ if (y1 == y2) return;
+
+ colors= colorTable[0];
+ }
+
+ int slope = (x1-x2 << 16)/(y1-y2);
+ int x = x1<<16, y = y1;
+ while(y < y2) {
+ add(colors[super-((x>>16)&(super-1))],
+ x>>(16+superSampleShift),y>>superSampleShift);
+ add(colors[(x>>16)&(super-1)],
+ 1+(x>>(16+superSampleShift)),y>>superSampleShift);
+ x += slope;
+ y++;
+ }
+ }
+
+ void icon(double icon[][4],Pixel color,double x,double y,
+ double scaleX, double scaleY) {
+ pen(color);
+ x *= super;
+ y *= super;
+ scaleX *= super;
+ scaleY *= super;
+ for(int i=0;icon[i][1] != icon[i][3];i++)
+ line(int(icon[i][0]*scaleX+x),int(icon[i][1]*scaleY+y),
+ int(icon[i][2]*scaleX+x),int(icon[i][3]*scaleY+y));
+ }
+#undef super
+};
diff --git a/kscd/kscdmagic/sound.cpp b/kscd/kscdmagic/sound.cpp
new file mode 100644
index 00000000..0bcb8d8d
--- /dev/null
+++ b/kscd/kscdmagic/sound.cpp
@@ -0,0 +1,252 @@
+/* Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#if defined(__linux__) || defined(__svr4__)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#if defined (__linux__)
+#include <linux/soundcard.h>
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+#undef __STRICT_ANSI__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#endif
+
+#if defined (__svr4__)
+#include <sys/soundcard.h>
+#endif
+
+
+// who knows when we'll need that...
+#if defined (FreeBSD)
+#include <sys/soundcard.h>
+#include <sys/cdio.h>
+#define CDROM_LEADOUT 0xAA
+#define CD_FRAMES 75 /* frames per second */
+#define CDROM_DATA_TRACK 0x4
+#endif
+
+#include <time.h>
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "syna.h"
+#include "magicconf.h"
+
+
+/* Sound Recording ================================================= */
+
+#ifdef LITTLEENDIAN
+#define SOUNDFORMAT AFMT_S16_LE
+#else
+#define SOUNDFORMAT AFMT_S16_BE
+#endif
+
+//If kernel starts running out of sound memory playing mp3s, this could
+//be the problem. OTOH if it is too small, it will start ticking on slow
+//computers
+#define MAXWINDOWSIZE 32
+
+static SoundSource source;
+static int inFrequency, downFactor, windowSize, pipeIn, device;
+static short *dataIn;
+static char *mixer;
+
+void
+openSound(SoundSource source, int inFrequency, const char *dspName,
+ char *mixerName)
+{
+ ::source = source;
+ ::inFrequency = inFrequency;
+ ::windowSize = 1;
+ mixer = mixerName;
+ downFactor = inFrequency / frequency;
+ if (downFactor <= 0)
+ downFactor = 1;
+
+ int format, stereo, fragment, fqc;
+
+#ifdef __FreeBSD__
+ attempt(device = open(dspName,O_WRONLY),"opening dsp device",true);
+ format = SOUNDFORMAT;
+ attempt(ioctl(device,SNDCTL_DSP_SETFMT,&format),"setting format",true);
+ if (format != SOUNDFORMAT) error("setting format (2)");
+ close(device);
+#endif
+ if (source == SourcePipe)
+ attempt(device = open(dspName,O_WRONLY),"opening dsp device",true);
+ else
+ attempt(device = open(dspName,O_RDONLY),"opening dsp device",true);
+
+ //Probably not needed
+ //attemptNoDie(ioctl(device,SNDCTL_DSP_RESET,0),"reseting dsp");
+ format = SOUNDFORMAT;
+ fqc = (source == SourcePipe ? inFrequency : frequency);
+ stereo = 1;
+
+ //int logWindowSize = -1, tmp = windowSize*downFactor;
+ //while(tmp) {
+ // tmp /= 2;
+ // logWindowSize++;
+ //}
+
+ if (source == SourcePipe)
+ //fragment = 0x00020000 + (m-overlap+1)+logWindowSize;
+ fragment = 0x00010000*(MAXWINDOWSIZE+1) + (m-overlap+1);//+logWindowSize;
+ //Soundcard should read in windowSize
+ // blocks of sound before blocking
+ else
+ //fragment = 0x00020000 + (m-overlap+1); //2 fragments of size 2*(2^(m-overlap+1)) bytes
+
+ //Added extra fragments to allow recording overrun (9/7/98)
+ fragment = 0x00080000 + (m-overlap+1); //8 fragments of size 2*(2^(m-overlap+1)) bytes
+
+
+
+
+ //Was 0x00010000 + m;
+
+ attemptNoDie(ioctl(device,SNDCTL_DSP_SETFRAGMENT,&fragment),"setting fragment",true);
+#ifndef __FreeBSD__
+ attempt(ioctl(device,SNDCTL_DSP_SETFMT,&format),"setting format",true);
+ if (format != SOUNDFORMAT) error("setting format (2)");
+#endif
+ attempt(ioctl(device,SNDCTL_DSP_STEREO,&stereo),"setting stereo",true);
+ attemptNoDie(ioctl(device,SNDCTL_DSP_SPEED,&fqc),"setting frequency",true);
+
+ data = new short[n*2];
+
+ if (source == SourcePipe) {
+ dataIn = new short[n*2*downFactor*MAXWINDOWSIZE];
+ memset(dataIn,0,n*4*downFactor*MAXWINDOWSIZE);
+ pipeIn = dup(0);
+ close(0);
+ }
+}
+
+void closeSound() {
+ delete data;
+ if (source == SourcePipe) {
+ delete dataIn;
+ close(pipeIn);
+ }
+ close(device);
+}
+
+int readWholeBlock(int pipe,char *dest,int length) {
+ while(length > 0) {
+ int result = read(pipe,dest,length);
+ if (result < 1)
+ return -1;
+ dest += result;
+ length -= result;
+ }
+ return 0;
+}
+
+int getNextFragment(void) {
+ if (source == SourcePipe) {
+ static int lastTime = 0;
+ int nowTime;
+ timeval timeVal1, timeVal2;
+
+ gettimeofday(&timeVal1,0);
+ write(device, (char*)dataIn, n*4*downFactor*windowSize);
+ gettimeofday(&timeVal2,0);
+
+ nowTime = timeVal1.tv_usec + timeVal1.tv_sec * 1000000;
+ if (nowTime > lastTime) {
+ int optimumFrags =
+ int(double(nowTime-lastTime)*inFrequency/1000000.0/(n*downFactor))
+ +1;
+ if (optimumFrags > MAXWINDOWSIZE)
+ optimumFrags = MAXWINDOWSIZE;
+
+ windowSize = optimumFrags;
+ }
+
+ lastTime = timeVal2.tv_usec + timeVal2.tv_sec * 1000000;
+
+ if (readWholeBlock(pipeIn, ((char*)dataIn), n*4*downFactor*windowSize) == -1)
+ return -1;
+
+ int i,j;
+ for(i=0,j=0;i<n;i++,j+=downFactor)
+ ((long*)data)[i] = ((long*)dataIn)[j];
+ } else {
+ int i;
+ count_info info;
+ if (-1 == ioctl(device,SNDCTL_DSP_GETIPTR,&info))
+ info.blocks = 1;
+ if (info.blocks > 8 || info.blocks < 1) /* Sanity check */
+ info.blocks = 1;
+
+ for(i=0;i<info.blocks;i++) {
+ if (recSize != n)
+ memmove((char*)data,(char*)data+recSize*4,(n-recSize)*4);
+
+ attemptNoDie(
+ readWholeBlock(device,(char*)data+n*4-recSize*4, recSize*4),
+ "reading from soundcard", true);
+ }
+ }
+ return 0;
+}
+
+#else
+
+// generic dummy implementation
+
+#include "syna.h"
+
+int getNextFragment(void) {
+ return 0;
+}
+
+void openSound(SoundSource source, int inFrequency, const char *dspName,
+ char *mixerName)
+{
+}
+
+void closeSound()
+{
+}
+
+#endif // linux || svr4
+
diff --git a/kscd/kscdmagic/symbol.h b/kscd/kscdmagic/symbol.h
new file mode 100644
index 00000000..aad04f47
--- /dev/null
+++ b/kscd/kscdmagic/symbol.h
@@ -0,0 +1,1028 @@
+#ifndef __SYNAES_SYMBOL_H__
+#define __SYNAES_SYMBOL_H__
+#if defined(__linux__) || defined(__svr4__)
+
+#define SYMBOLSWIDTH 586
+#define SYMBOLSHEIGHT 50
+unsigned char Symbols[586*50] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,241,72,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,13,30,37,31,24,5,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,132,254,237,72,0,0,0,0,0,0,0,0,0,0,0,1,7,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,46,0,0,0,0,0,0,0,0,0,0,0,0,0,10,87,
+ 176,225,244,246,244,238,206,135,39,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,
+ 254,236,73,0,0,0,0,0,0,0,0,0,0,3,154,138,58,9,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,36,211,80,0,0,0,0,0,0,0,0,0,0,0,2,77,204,252,254,254,254,
+ 254,254,254,254,254,239,145,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,245,
+ 215,72,0,0,0,0,0,0,0,0,0,0,115,254,249,213,138,58,8,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,36,212,253,80,0,0,0,0,0,0,0,0,0,0,7,136,247,254,254,254,254,
+ 254,254,254,254,254,254,254,254,208,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,
+ 255,225,66,210,72,0,0,0,0,0,0,0,0,0,40,244,254,254,254,249,214,137,57,8,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,37,212,254,254,80,0,0,0,0,0,0,0,0,0,4,150,253,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,227,50,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,132,254,255,225,10,55,217,71,0,0,0,0,0,0,0,0,4,194,254,255,255,
+ 255,255,254,250,214,137,57,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,211,254,254,254,80,0,0,
+ 0,0,0,0,0,0,0,100,250,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,203,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,1,196,187,1,
+ 0,0,0,0,0,0,0,0,115,254,255,255,255,255,255,254,254,254,249,212,137,57,
+ 10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,36,211,254,254,254,254,80,0,0,0,0,0,0,0,0,29,230,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,130,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,41,244,255,255,
+ 255,255,255,255,255,255,254,254,254,249,212,137,58,8,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,212,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,0,134,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,255,232,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,
+ 225,10,1,196,190,1,0,0,0,0,0,0,0,0,4,196,254,255,255,255,255,255,255,255,
+ 255,255,255,255,254,254,249,212,137,57,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,212,254,254,254,254,254,254,80,0,0,
+ 0,0,0,0,0,16,225,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,255,255,255,254,112,0,0,0,0,0,0,0,3,12,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,
+ 0,0,0,0,0,0,7,9,0,0,0,0,0,0,0,0,0,0,0,0,111,155,155,111,0,0,0,16,148,155,
+ 155,58,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,132,254,
+ 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,115,253,254,255,255,255,255,255,
+ 255,255,255,255,255,255,255,254,254,254,250,194,15,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,7,147,99,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,38,179,
+ 38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,211,254,254,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,62,251,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,255,254,182,1,0,0,0,0,0,0,18,171,19,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,1,162,44,0,0,0,0,0,37,159,11,0,0,0,0,0,0,0,0,0,0,1,182,254,
+ 254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,0,0,23,136,1,0,0,0,0,
+ 0,68,92,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 40,244,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254,
+ 232,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,151,253,245,99,1,0,0,
+ 6,50,106,155,166,174,166,162,115,52,11,0,0,0,38,213,254,213,39,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,108,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,255,254,223,9,0,0,0,0,0,0,18,239,185,19,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1,197,219,45,0,0,0,0,38,246,166,11,0,0,0,0,0,0,0,0,0,1,182,254,254,
+ 182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,0,23,192,196,1,0,0,0,0,68,
+ 233,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 4,196,254,255,255,255,255,255,255,255,255,255,255,255,255,255,254,232,65,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,180,252,255,254,244,95,10,97,
+ 197,248,254,254,255,255,255,254,254,248,210,118,20,36,212,254,255,254,213,
+ 39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,211,254,254,254,254,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,117,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,255,255,228,11,0,0,0,0,0,0,18,240,254,185,
+ 19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,0,0,0,0,0,0,0,1,197,254,218,44,0,0,0,38,247,253,165,11,0,0,0,0,0,0,
+ 0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,23,192,
+ 254,196,1,0,0,0,67,234,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,
+ 1,0,0,0,0,0,0,0,0,0,0,116,254,255,255,255,255,255,255,255,255,255,255,255,
+ 255,254,233,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,186,254,255,
+ 254,254,225,74,34,220,254,254,255,255,255,255,255,255,255,254,254,248,95,
+ 44,205,254,254,255,254,213,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,211,254,
+ 254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,122,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,228,
+ 11,0,0,0,0,0,0,18,240,254,254,186,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 17,187,197,197,197,197,197,195,46,0,0,0,0,0,0,0,142,197,197,197,197,197,
+ 197,107,0,0,0,0,0,0,0,81,196,197,197,197,197,197,197,197,197,197,197,197,
+ 197,197,197,197,197,197,197,197,197,194,37,0,0,0,0,0,0,1,197,254,254,219,
+ 44,0,0,38,247,254,253,165,11,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,
+ 254,254,96,0,0,0,0,0,0,0,0,22,192,254,254,196,1,0,0,66,235,254,254,111,
+ 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,39,246,
+ 254,255,255,255,255,255,255,255,255,255,255,254,234,64,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,17,187,254,255,254,254,204,34,0,0,47,222,254,254,
+ 255,255,255,255,255,255,254,248,113,2,0,18,175,254,254,255,254,214,36,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,36,211,254,254,254,254,254,254,254,254,254,254,
+ 254,80,0,0,0,0,0,0,0,114,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,255,255,255,254,227,11,0,0,0,0,0,0,18,240,255,254,254,
+ 185,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,220,43,0,38,247,254,254,253,166,
+ 11,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,
+ 22,193,254,254,254,196,1,0,67,234,254,255,254,111,0,0,0,0,0,0,0,132,254,
+ 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,3,196,254,255,255,255,255,255,
+ 255,255,255,255,255,236,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29,
+ 211,254,255,254,204,30,0,0,0,0,47,221,254,255,255,255,255,255,254,243,112,
+ 2,0,0,0,14,175,253,255,254,231,54,0,0,0,0,0,0,1,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,5,5,1,
+ 0,0,0,0,0,0,0,0,0,0,0,5,5,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,255,
+ 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,74,253,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,254,
+ 194,2,0,0,0,0,0,0,18,240,255,255,255,254,184,19,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,
+ 254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,
+ 254,254,219,44,38,247,254,254,254,253,166,11,0,0,0,0,0,1,182,254,254,182,
+ 1,0,0,27,243,254,254,96,0,0,0,0,0,0,23,192,254,254,254,254,196,1,68,233,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,116,254,255,255,255,255,255,255,255,255,255,255,231,53,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,225,33,0,0,0,0,0,
+ 0,48,221,254,254,255,255,254,237,78,1,0,0,0,0,0,15,198,254,231,61,0,0,0,
+ 0,0,0,0,73,213,214,214,214,214,214,214,214,213,214,214,214,214,214,214,
+ 214,214,214,214,214,214,214,214,213,214,214,214,214,214,214,214,214,214,
+ 156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,8,145,213,214,213,133,5,0,0,0,0,0,0,0,0,0,88,211,213,
+ 214,181,24,0,0,0,0,3,11,11,11,11,11,11,11,40,210,254,255,255,254,254,254,
+ 254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,26,235,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,255,255,255,254,131,0,0,0,0,
+ 0,0,0,18,240,255,255,255,254,254,184,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,
+ 242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,
+ 138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,
+ 254,218,83,247,254,254,254,254,253,165,11,0,0,0,0,1,182,254,254,182,1,0,
+ 0,27,243,254,254,96,0,0,0,0,0,23,191,254,254,254,254,254,197,69,233,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,40,245,255,255,255,255,255,255,255,255,255,255,254,226,
+ 52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,205,79,0,0,0,0,0,0,0,
+ 0,47,222,254,254,254,238,76,0,0,0,0,0,0,0,0,45,212,62,0,0,0,0,0,0,0,0,87,
+ 253,185,168,168,168,168,168,171,243,249,175,168,204,254,255,254,220,168,
+ 170,239,243,171,168,220,254,195,168,168,168,168,168,169,236,185,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,11,11,5,
+ 0,0,0,0,0,0,0,0,0,0,7,11,11,11,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,11,11,11,11,4,0,0,0,0,0,0,0,0,0,0,8,11,11,11,8,0,0,0,0,0,
+ 0,0,0,0,0,4,11,11,11,11,1,0,0,0,0,0,0,0,0,0,1,11,11,11,11,4,0,0,0,0,0,0,
+ 0,0,0,0,8,11,11,11,7,0,0,0,0,0,0,0,0,0,0,5,11,11,11,10,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,151,252,254,254,254,251,136,2,0,
+ 0,0,0,0,0,0,88,242,254,224,246,254,194,22,0,0,0,64,228,229,229,229,229,
+ 229,229,239,254,254,254,254,254,254,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,1,163,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,255,244,45,0,0,0,0,0,0,0,18,240,255,255,255,255,254,
+ 254,185,18,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,237,253,254,254,254,
+ 254,254,253,165,11,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,
+ 0,0,22,191,254,254,254,254,254,254,233,235,254,254,254,255,255,254,111,
+ 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,4,196,
+ 255,255,255,255,255,255,255,255,255,255,255,254,225,52,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,4,0,0,0,0,0,0,0,0,0,48,222,254,239,77,0,
+ 0,0,0,0,0,0,0,0,1,24,0,0,0,0,0,0,0,0,0,87,251,50,0,0,0,0,0,10,223,251,67,
+ 0,53,246,255,253,99,0,31,241,222,9,0,153,253,80,0,0,0,0,0,2,200,185,1,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,224,229,
+ 229,229,179,18,0,0,0,0,0,0,0,0,37,203,229,229,229,214,55,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,98,226,229,229,229,170,14,0,0,0,0,0,0,
+ 0,0,45,208,229,229,229,209,47,0,0,0,0,0,0,0,0,13,167,229,229,229,227,101,
+ 1,0,0,0,0,0,0,0,1,109,227,229,229,229,161,11,0,0,0,0,0,0,0,0,52,213,229,
+ 229,229,205,39,0,0,0,0,0,0,0,0,16,177,229,229,229,225,90,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,251,254,255,255,255,255,238,15,
+ 0,0,0,0,0,0,11,225,254,207,34,104,247,253,80,0,0,0,71,253,255,255,255,255,
+ 254,254,254,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,0,49,243,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,254,161,1,0,0,0,0,0,0,0,18,240,255,255,255,255,254,
+ 254,254,185,18,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,253,165,11,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,
+ 0,0,22,192,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,
+ 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,
+ 0,116,253,254,255,255,255,254,235,219,254,255,255,255,254,226,53,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,21,21,21,21,21,21,21,21,21,
+ 21,21,21,21,21,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,114,2,0,0,0,0,0,
+ 0,0,0,0,47,207,76,0,0,0,0,0,0,0,0,0,0,80,130,0,0,0,0,0,0,0,0,0,87,251,50,
+ 0,0,0,0,0,9,223,254,161,0,5,193,254,235,28,0,116,254,222,9,0,153,254,79,
+ 0,0,0,0,0,1,200,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,88,242,254,254,255,255,254,185,18,0,0,0,0,0,0,37,214,254,254,
+ 255,255,254,228,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,98,246,254,
+ 254,255,255,254,175,14,0,0,0,0,0,0,44,220,254,254,255,255,254,222,47,0,
+ 0,0,0,0,0,13,172,254,254,255,255,254,246,101,1,0,0,0,0,0,1,109,248,254,
+ 254,255,255,253,165,11,0,0,0,0,0,0,53,226,254,254,255,255,254,216,39,0,
+ 0,0,0,0,0,17,182,254,254,255,255,254,243,91,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,69,253,254,255,255,255,255,239,16,0,0,0,0,0,0,12,
+ 230,212,32,0,1,113,253,81,0,0,0,71,253,254,255,255,255,254,255,255,255,
+ 255,254,255,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,
+ 2,141,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 229,36,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,254,254,184,19,0,
+ 0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,
+ 254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,
+ 0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,253,
+ 165,11,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,22,192,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,
+ 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,41,244,254,255,255,254,
+ 233,66,37,212,254,255,255,255,254,226,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,108,240,241,241,241,241,241,241,241,241,241,241,241,241,
+ 241,224,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,199,249,115,2,0,0,0,0,0,0,0,
+ 29,99,119,113,55,4,0,0,0,0,0,0,0,80,240,230,22,0,0,0,0,0,0,0,0,87,251,50,
+ 0,0,0,0,0,9,223,255,228,18,0,97,253,165,1,4,196,254,222,9,0,153,254,79,
+ 0,0,0,0,0,1,200,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,8,53,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,6,176,254,255,255,255,255,254,242,110,18,0,0,0,0,0,106,250,255,
+ 255,255,255,254,252,154,44,0,0,0,0,10,51,0,0,0,0,0,0,0,3,56,2,0,0,0,1,64,
+ 193,254,255,255,255,255,254,237,57,0,0,0,0,0,39,143,251,255,255,255,255,
+ 254,251,123,0,0,0,0,0,0,54,236,254,255,255,255,254,254,196,66,1,0,0,0,2,
+ 69,202,254,255,255,255,255,254,234,98,11,0,0,0,0,43,151,252,255,255,255,
+ 255,254,251,137,35,0,0,0,0,17,108,241,254,255,255,255,254,254,187,61,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,
+ 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,
+ 255,254,255,255,254,255,254,255,254,254,254,254,254,254,254,254,254,254,
+ 80,0,0,0,0,0,0,0,0,0,13,192,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,244,84,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,
+ 255,254,254,184,19,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,253,165,11,1,182,254,254,182,1,0,0,27,243,254,254,96,
+ 0,23,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,
+ 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,4,196,254,255,254,233,66,0,0,37,211,254,255,255,255,254,225,34,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,219,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,
+ 248,254,248,115,2,0,0,0,0,3,112,186,152,158,153,173,163,23,0,0,0,0,0,80,
+ 240,254,253,85,0,0,0,0,0,0,0,0,87,251,50,0,5,36,37,37,45,228,255,252,79,
+ 0,20,226,73,0,40,244,254,222,9,0,153,253,105,37,15,0,1,32,38,208,185,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,8,150,240,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,186,
+ 253,253,253,253,242,144,228,183,19,0,0,0,0,3,126,249,253,253,253,251,170,
+ 192,227,55,0,0,10,161,237,76,0,0,0,0,0,3,123,243,112,2,0,1,98,241,163,201,
+ 253,253,253,253,238,78,0,0,0,0,0,45,219,201,162,250,253,253,253,250,140,
+ 6,0,0,0,0,0,0,0,75,237,253,253,253,253,203,161,242,102,1,0,2,109,243,157,
+ 207,253,253,253,253,234,143,236,164,11,0,0,52,225,194,169,251,253,253,253,
+ 250,158,206,214,40,0,0,17,181,229,143,241,253,253,253,253,196,167,240,91,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,
+ 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,
+ 255,254,254,255,254,255,254,254,254,254,254,254,254,254,254,254,254,254,
+ 80,0,0,0,0,0,0,0,0,0,0,24,188,253,254,254,254,254,254,254,254,254,254,254,
+ 254,254,237,97,1,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,254,
+ 254,254,184,18,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,
+ 0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,253,164,12,182,254,254,182,1,0,0,27,243,254,254,96,
+ 22,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,
+ 255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,116,254,254,233,66,0,0,0,0,37,212,254,255,254,253,161,9,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,253,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,234,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,
+ 254,254,254,248,115,2,0,0,0,113,161,193,249,253,252,224,149,171,14,0,0,
+ 0,80,239,254,255,254,154,0,0,0,0,0,0,0,0,87,251,50,0,38,240,247,247,247,
+ 253,255,254,165,1,0,115,12,0,121,254,255,222,9,0,153,254,249,247,102,0,
+ 8,214,247,253,185,1,0,0,0,0,0,0,0,0,0,0,0,2,145,252,254,241,58,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,17,67,68,68,68,93,228,254,254,171,2,0,0,0,0,3,59,68,
+ 68,68,73,190,254,254,226,27,3,156,253,254,237,48,0,0,0,0,115,250,254,247,
+ 86,0,87,245,254,251,135,67,68,68,68,46,0,0,0,0,0,34,219,254,254,192,67,
+ 68,68,68,62,6,0,0,0,0,0,0,0,0,0,45,68,68,68,68,149,251,254,245,74,0,99,
+ 247,254,250,126,67,68,68,68,105,237,254,253,146,0,41,225,254,254,182,66,
+ 68,68,68,78,205,254,254,211,15,7,179,254,254,229,78,68,68,68,68,158,252,
+ 254,243,63,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,69,253,254,254,254,
+ 254,254,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,
+ 255,255,255,254,255,255,255,255,255,255,254,254,254,254,254,254,254,254,
+ 254,254,80,0,0,0,0,0,0,0,0,0,0,0,11,133,239,254,254,254,254,254,254,254,
+ 254,254,252,200,55,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,
+ 255,255,255,254,254,185,17,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,
+ 60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,253,165,193,254,254,182,1,0,0,27,243,254,
+ 254,118,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,41,244,234,65,0,0,0,0,0,0,36,213,254,253,161,10,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,219,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 159,254,255,255,254,248,115,2,0,31,186,194,254,255,255,254,254,238,162,
+ 105,0,0,80,239,254,255,255,255,201,3,0,0,0,0,0,0,0,87,251,50,0,16,102,116,
+ 246,254,255,255,255,236,26,0,10,0,7,208,254,255,222,9,0,153,255,255,254,
+ 106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,213,254,255,254,88,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,0,0,
+ 0,0,23,241,254,255,250,46,9,223,254,255,253,75,0,0,0,1,184,254,255,254,
+ 122,0,152,254,255,255,154,0,0,0,0,0,0,0,0,0,0,73,253,254,255,224,10,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,254,255,254,108,0,166,254,255,254,
+ 140,0,0,0,0,118,254,255,255,187,1,87,254,255,255,214,6,0,0,0,43,249,254,
+ 255,243,26,20,240,254,255,252,49,0,0,0,3,208,254,255,254,95,0,0,0,0,0,0,
+ 0,0,0,68,189,198,107,5,0,0,0,0,0,0,0,0,69,252,144,104,105,104,183,239,16,
+ 0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,
+ 255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,37,147,225,249,254,254,253,252,242,193,87,8,0,0,0,
+ 0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,255,255,255,254,254,
+ 184,18,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,
+ 254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,
+ 0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,252,254,254,182,1,0,0,27,243,254,254,242,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,
+ 0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,4,176,
+ 65,0,0,0,0,0,0,0,0,36,211,160,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,109,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,233,29,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,255,255,255,255,249,115,
+ 2,103,151,250,255,255,255,255,255,254,169,172,3,80,240,254,255,255,255,
+ 255,211,5,0,0,0,0,0,0,0,87,251,50,0,0,0,19,240,255,255,255,255,253,94,0,
+ 0,0,53,248,255,255,222,9,0,153,255,255,254,106,0,8,220,255,255,185,1,0,
+ 0,0,0,0,0,0,0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,9,
+ 223,255,255,253,75,0,0,0,1,184,255,255,254,122,0,153,255,255,255,154,0,
+ 0,0,0,0,0,0,0,0,0,74,253,255,255,224,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1,197,255,255,254,108,0,166,255,255,254,140,0,0,0,0,118,254,255,255,
+ 187,1,87,254,255,255,214,6,0,0,0,43,249,255,255,243,26,20,240,255,255,252,
+ 49,0,0,0,3,209,255,255,254,95,0,0,0,0,0,0,0,0,34,233,254,254,251,82,0,0,
+ 0,0,0,0,0,0,69,252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,
+ 252,81,0,0,0,71,253,254,255,255,254,254,254,255,254,255,254,254,254,254,
+ 254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,52,
+ 83,84,84,73,32,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,
+ 255,255,255,255,255,254,254,254,156,2,0,0,0,0,0,0,23,242,255,255,255,255,
+ 255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,
+ 104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,182,1,0,0,27,
+ 243,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,26,10,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,218,254,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,178,255,255,255,255,255,255,249,112,123,158,254,255,255,255,255,255,254,
+ 187,177,81,240,254,255,255,255,255,255,211,5,0,0,0,0,0,0,0,87,251,50,0,
+ 0,0,19,240,255,255,255,255,254,161,0,0,0,116,254,255,255,222,9,0,153,255,
+ 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,213,255,255,254,
+ 88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,
+ 0,0,0,0,23,242,255,255,250,46,9,223,255,255,253,75,0,0,0,1,184,255,255,
+ 254,122,0,153,255,255,255,154,0,0,0,0,0,0,0,0,0,0,74,253,255,255,224,10,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,255,255,254,108,0,166,255,255,
+ 254,140,0,0,0,0,118,254,255,255,187,1,87,254,255,255,214,6,0,0,0,43,249,
+ 255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,255,254,95,0,0,0,0,
+ 0,0,0,0,72,253,255,254,254,136,0,0,0,0,0,0,0,0,69,252,121,73,73,73,168,
+ 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,255,255,255,
+ 255,254,255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,
+ 80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,18,240,255,255,255,255,255,255,255,255,255,255,255,254,218,43,0,0,
+ 0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,
+ 254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,182,1,0,0,27,243,254,254,252,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,
+ 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,251,253,253,253,
+ 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,230,29,
+ 0,0,0,0,0,0,0,0,0,0,0,0,170,255,255,255,255,255,254,248,112,120,153,253,
+ 255,255,255,255,255,254,181,176,81,240,254,255,255,255,255,255,211,5,0,
+ 0,0,0,0,0,0,87,251,50,0,0,0,19,240,255,255,255,255,254,113,0,0,0,69,251,
+ 255,255,222,9,0,153,255,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,
+ 0,0,0,0,5,211,254,255,254,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,89,254,
+ 255,255,211,4,0,0,0,0,0,0,1,1,1,23,240,255,255,250,45,9,221,254,255,253,
+ 75,1,1,1,2,181,254,255,254,122,0,150,254,255,254,154,1,1,1,1,0,0,0,0,0,
+ 0,71,253,255,255,224,10,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,194,254,255,
+ 254,108,0,163,254,255,254,140,1,1,1,1,116,254,255,254,187,1,84,253,255,
+ 255,214,7,1,1,1,42,248,255,255,242,26,19,238,255,255,252,49,0,0,0,3,206,
+ 254,255,254,94,0,0,0,0,0,0,0,0,47,244,254,254,253,104,0,0,0,0,0,0,0,0,69,
+ 253,252,252,252,252,253,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,
+ 0,0,71,253,254,255,255,255,254,255,255,255,255,255,255,254,254,254,254,
+ 254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,3,81,94,94,94,94,94,
+ 94,94,94,94,93,19,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,
+ 255,255,255,255,254,219,43,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,
+ 60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,205,208,254,254,182,1,0,0,27,243,254,
+ 254,145,224,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,11,134,158,158,158,158,158,158,158,158,158,158,158,158,
+ 158,158,158,158,158,158,158,158,158,66,0,0,0,0,0,0,0,0,0,0,0,0,167,254,
+ 255,255,255,254,248,115,2,62,170,229,254,254,255,255,255,251,159,145,1,
+ 80,240,254,255,255,255,255,208,4,0,0,0,0,0,0,0,87,251,50,0,14,91,106,245,
+ 254,255,255,255,243,39,0,2,0,14,222,255,255,222,9,0,153,255,255,254,106,
+ 0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,90,243,254,214,32,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,54,184,190,190,190,155,195,254,250,124,1,0,0,0,0,17,
+ 161,190,190,190,176,157,250,254,187,14,1,101,246,254,210,142,190,190,190,
+ 188,140,235,254,229,52,0,46,222,254,240,136,187,190,190,190,127,4,0,0,0,
+ 0,14,174,254,252,162,169,190,190,190,167,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 75,239,254,224,43,0,54,227,254,236,135,187,190,190,190,148,208,254,247,
+ 100,0,17,184,254,251,156,173,190,190,190,169,168,253,253,167,7,2,123,250,
+ 254,190,15,0,0,0,0,85,242,254,218,35,0,0,0,0,0,0,0,0,1,115,233,237,163,
+ 14,0,0,0,0,0,0,0,0,69,252,114,63,63,63,164,239,16,0,0,0,0,0,0,12,230,146,
+ 0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,255,255,254,255,254,255,
+ 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,8,217,
+ 250,250,250,250,250,250,250,250,250,248,51,0,0,0,0,0,0,0,0,0,0,0,0,18,240,
+ 255,255,255,255,255,255,255,255,255,254,218,43,0,0,0,0,0,0,0,0,0,23,242,
+ 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,
+ 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,204,31,182,254,254,
+ 182,1,0,0,27,243,254,254,96,49,223,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,
+ 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,245,252,250,250,250,250,250,250,
+ 250,250,250,250,250,250,250,250,250,250,250,250,250,254,127,0,0,0,0,0,0,
+ 0,0,0,0,0,0,121,254,255,255,254,248,115,2,0,5,170,149,242,254,254,254,252,
+ 181,184,44,0,0,80,239,254,254,255,254,166,0,0,0,0,0,0,0,0,87,251,50,0,38,
+ 243,250,250,250,254,254,254,187,4,0,82,6,0,144,254,254,222,9,0,153,255,
+ 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,90,204,37,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,55,227,254,254,255,255,253,168,191,126,3,0,0,
+ 0,0,17,184,254,254,255,255,254,207,157,184,19,0,0,1,101,203,155,250,254,
+ 254,255,254,238,141,211,57,0,0,0,47,207,142,233,254,254,255,255,252,137,
+ 5,0,0,0,0,14,173,164,199,254,254,255,255,254,195,24,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,76,208,49,0,0,0,55,210,141,237,254,254,255,254,250,157,202,104,
+ 1,0,0,17,182,159,205,254,254,255,255,254,194,168,166,12,0,0,3,123,186,21,
+ 0,0,0,0,0,0,86,205,41,0,0,0,0,0,0,0,0,0,0,0,15,16,1,0,0,0,0,0,0,0,0,0,69,
+ 252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,
+ 253,254,255,254,254,254,254,254,254,255,254,254,254,254,254,254,254,254,
+ 254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,1,38,44,44,44,44,44,44,44,44,
+ 44,44,9,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,255,
+ 254,218,44,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,
+ 0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,
+ 47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,204,30,1,182,254,254,182,1,0,0,27,243,254,254,96,0,49,223,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,111,
+ 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,
+ 245,137,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,62,243,127,
+ 0,0,0,0,0,0,0,0,0,0,0,0,65,251,254,254,248,115,2,0,0,0,27,177,159,174,192,
+ 185,159,183,80,0,0,0,0,80,239,254,254,254,99,0,0,0,0,0,0,0,0,87,251,50,
+ 0,6,43,44,44,52,229,254,253,98,0,11,209,53,0,56,249,254,222,9,0,153,255,
+ 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,3,37,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,31,192,254,255,255,255,255,254,248,85,3,0,0,0,0,0,
+ 118,253,255,255,255,255,254,254,159,31,0,0,0,0,1,63,241,254,255,255,255,
+ 254,254,215,54,0,0,0,0,0,24,201,254,255,255,255,255,254,245,85,5,0,0,0,
+ 0,26,146,253,255,255,255,255,254,253,144,18,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+ 38,0,0,0,0,0,53,212,254,255,255,255,255,254,242,89,5,0,0,0,0,11,148,254,
+ 255,255,255,255,254,253,139,24,0,0,0,0,9,31,0,0,0,0,0,0,0,2,37,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,252,67,0,0,0,133,239,16,0,0,
+ 0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,255,
+ 255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,
+ 255,255,255,255,255,255,255,254,218,44,0,0,0,0,0,0,0,0,0,0,0,23,242,255,
+ 255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,
+ 0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,204,30,0,1,182,254,254,182,
+ 1,0,0,27,243,254,254,96,0,0,49,223,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,
+ 190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,245,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,21,241,127,0,0,0,0,0,0,0,0,0,0,0,0,14,216,254,248,115,2,0,0,0,0,0,
+ 18,115,175,181,180,148,47,0,0,0,0,0,0,80,239,254,239,36,0,0,0,0,0,0,0,0,
+ 87,251,50,0,0,0,0,0,9,223,255,238,28,0,75,252,146,0,8,212,254,222,9,0,153,
+ 255,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,2,111,220,
+ 52,0,0,0,0,0,0,0,0,0,0,0,0,54,220,151,217,254,254,254,254,250,127,3,0,0,
+ 0,0,0,0,11,165,253,254,254,254,254,194,168,201,29,0,0,0,0,1,102,245,254,
+ 254,254,254,230,143,225,75,0,0,0,0,0,47,221,254,254,254,254,248,153,210,
+ 137,5,0,0,23,191,177,186,254,254,254,254,254,188,175,194,24,0,0,0,0,0,0,
+ 0,0,0,0,0,1,96,223,66,0,0,0,72,224,144,228,254,254,254,254,246,149,215,
+ 126,3,0,0,0,0,19,184,254,254,254,254,253,181,181,185,20,0,0,6,144,203,32,
+ 0,0,0,0,0,2,106,221,57,0,0,0,0,0,0,0,0,0,0,2,34,41,5,0,0,0,0,0,0,0,0,0,
+ 69,252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,
+ 71,253,254,255,255,255,254,255,255,255,255,255,255,254,254,254,254,254,
+ 254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,4,118,137,137,137,137,137,
+ 137,137,137,137,135,28,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,
+ 255,254,219,43,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,
+ 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,
+ 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,204,31,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,
+ 0,0,50,224,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,
+ 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,31,245,112,0,44,136,137,137,137,137,137,137,137,137,137,137,137,137,137,
+ 120,6,20,241,127,0,0,0,0,0,0,0,0,0,0,0,0,0,126,248,115,2,0,0,0,0,0,0,0,
+ 0,5,43,9,0,0,0,0,0,0,0,0,0,80,240,169,2,0,0,0,0,0,0,0,0,87,251,50,0,0,0,
+ 0,0,9,223,254,179,2,2,172,254,222,17,0,135,254,222,9,0,153,255,255,254,
+ 106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,1,109,247,254,225,41,0,
+ 0,0,0,0,0,0,0,0,0,51,226,254,247,138,115,116,116,116,90,3,0,0,0,0,0,0,0,
+ 0,11,105,116,116,116,116,167,252,254,202,18,0,0,0,0,1,78,116,116,116,116,
+ 134,241,254,237,64,0,0,0,0,0,43,116,116,116,116,118,214,254,251,129,0,20,
+ 191,254,253,172,111,116,116,116,116,174,253,254,193,14,0,0,0,0,0,0,0,0,
+ 0,0,93,244,254,233,54,0,69,236,254,242,127,116,116,116,116,120,219,254,
+ 250,117,0,0,0,0,0,19,110,116,116,116,116,181,254,254,184,10,4,143,252,254,
+ 205,20,0,0,0,0,104,246,254,228,45,0,0,0,0,0,0,0,0,3,136,242,246,185,20,
+ 0,0,0,0,0,0,0,0,69,252,168,137,137,137,198,239,16,0,0,0,0,0,0,12,230,146,
+ 0,0,0,55,252,81,0,0,0,66,236,237,237,237,237,237,237,247,254,255,254,254,
+ 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,8,206,
+ 237,237,237,237,237,237,237,237,237,235,49,0,0,0,0,0,0,0,0,0,0,0,0,18,240,
+ 255,255,255,255,255,254,219,43,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,
+ 255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,
+ 0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,
+ 251,254,254,254,254,254,254,254,205,31,0,0,0,1,182,254,254,182,1,0,0,27,
+ 243,254,254,96,0,0,0,0,50,224,254,254,254,254,254,254,245,249,254,254,254,
+ 255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,29,228,104,2,170,254,254,254,254,253,252,251,249,251,252,
+ 254,254,254,254,250,76,19,224,118,0,0,0,0,0,0,0,0,0,0,0,0,0,22,76,2,0,0,
+ 0,0,0,0,0,0,0,78,233,114,2,0,0,0,0,0,0,0,0,0,57,43,0,0,0,0,0,0,0,0,0,87,
+ 251,50,0,0,0,0,0,9,223,253,89,0,36,240,254,252,80,0,48,247,222,9,0,153,
+ 254,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,212,254,255,
+ 254,88,0,0,0,0,0,0,0,0,0,0,138,254,255,254,167,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,23,241,254,255,250,46,0,0,0,0,0,0,0,0,0,1,183,254,255,254,
+ 122,0,0,0,0,0,0,0,0,0,0,104,254,255,255,200,1,73,253,254,255,224,10,0,0,
+ 0,32,245,254,255,247,36,0,0,0,0,0,0,0,0,0,1,196,254,255,254,108,0,165,254,
+ 255,254,140,0,0,0,0,117,254,255,254,187,1,0,0,0,0,0,0,0,0,0,42,249,254,
+ 255,243,26,19,239,254,255,252,49,0,0,0,3,207,254,255,254,94,0,0,0,0,0,0,
+ 0,0,53,248,255,254,254,112,0,0,0,0,0,0,0,0,69,253,241,237,237,237,246,239,
+ 16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,4,14,14,14,14,14,14,14,
+ 72,235,254,254,254,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,12,14,14,14,14,14,14,14,14,14,14,2,0,0,0,0,0,0,0,0,0,0,
+ 0,0,18,240,255,255,255,255,254,218,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,
+ 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,
+ 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,
+ 240,119,248,254,254,254,254,254,204,30,0,0,0,0,1,182,254,254,182,1,0,0,
+ 27,243,254,254,96,0,0,0,0,0,50,223,254,254,254,254,254,198,112,247,254,
+ 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,1,14,6,54,246,255,253,219,141,82,49,48,42,49,55,102,172,
+ 241,254,254,194,7,14,7,0,0,0,0,0,0,0,0,0,0,0,0,0,59,43,0,0,0,0,0,0,0,0,
+ 0,79,240,254,249,114,2,0,0,0,0,0,0,0,0,23,78,1,0,0,0,0,0,0,0,0,87,253,177,
+ 158,158,158,158,158,161,242,251,170,158,189,254,254,254,206,158,162,242,
+ 242,161,158,216,254,255,254,198,158,161,241,255,255,185,1,0,0,0,0,0,0,0,
+ 0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,0,0,0,0,0,0,0,
+ 0,0,1,184,255,255,254,122,0,0,0,0,0,0,0,0,0,0,105,254,255,255,201,1,74,
+ 253,255,255,224,10,0,0,0,33,246,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,
+ 255,255,254,108,0,166,255,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,
+ 0,0,0,0,0,0,0,43,249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,
+ 255,254,95,0,0,0,0,0,0,0,0,72,253,255,254,254,136,0,0,0,0,0,0,0,0,69,252,
+ 78,14,14,14,140,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,
+ 0,0,0,0,0,0,0,70,235,254,255,254,254,254,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,18,240,255,255,255,254,218,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,
+ 255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,
+ 0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,240,
+ 81,38,247,254,254,254,254,205,30,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,
+ 254,254,96,0,0,0,0,0,0,50,223,254,254,254,254,196,4,111,247,254,255,255,
+ 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,2,171,254,254,177,18,0,0,0,0,0,0,0,0,1,59,238,255,251,76,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,64,232,199,14,0,0,0,0,0,0,0,78,239,254,255,255,
+ 248,113,2,0,0,0,0,0,0,4,163,244,96,1,0,0,0,0,0,0,0,75,221,221,221,221,221,
+ 221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,
+ 221,221,221,221,221,221,221,221,221,221,161,1,0,0,0,0,0,0,0,0,0,0,0,5,213,
+ 255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,0,0,0,0,0,0,0,0,0,1,184,255,
+ 255,254,122,0,0,0,0,0,0,0,0,0,0,105,254,255,255,201,1,74,253,255,255,224,
+ 10,0,0,0,33,246,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,255,255,254,108,
+ 0,166,255,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,0,0,0,0,0,0,0,43,
+ 249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,255,254,95,0,0,
+ 0,0,0,0,0,0,27,224,254,254,248,71,0,0,0,0,0,0,0,0,69,252,68,0,0,0,134,239,
+ 16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,0,0,0,0,0,0,0,0,70,
+ 235,254,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,
+ 0,0,5,155,179,179,179,179,179,179,179,179,179,177,36,0,0,0,0,0,0,0,0,0,
+ 0,0,0,18,240,255,255,254,219,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,
+ 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,
+ 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,
+ 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,241,81,
+ 0,38,247,254,254,254,204,31,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,
+ 254,254,96,0,0,0,0,0,0,0,50,224,254,254,254,196,1,2,111,248,254,255,254,
+ 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,54,246,254,254,129,1,0,0,0,0,0,0,0,0,0,15,223,255,254,193,6,0,0,0,
+ 0,0,0,0,0,0,0,0,0,64,232,254,253,165,11,0,0,0,0,0,78,239,254,255,255,255,
+ 254,248,113,2,0,0,0,0,4,129,251,254,244,96,1,0,0,0,0,0,0,2,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,0,0,0,0,0,0,0,0,
+ 0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,241,255,255,250,46,0,0,0,0,0,0,0,
+ 0,0,1,183,254,255,254,122,0,0,0,0,0,0,0,0,0,0,104,254,255,255,201,1,73,
+ 253,255,255,224,10,0,0,0,33,245,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,
+ 255,255,254,108,0,166,254,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,
+ 0,0,0,0,0,0,0,43,249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,208,255,
+ 255,254,95,0,0,0,0,0,0,0,0,0,51,162,167,84,2,0,0,0,0,0,0,0,0,69,253,199,
+ 179,179,179,218,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,70,236,254,254,254,254,254,254,254,254,254,254,80,0,0,
+ 0,0,0,0,0,0,0,0,0,0,6,179,206,206,206,206,206,206,206,206,206,204,42,0,
+ 0,0,0,0,0,0,0,0,0,0,0,18,240,255,254,220,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,18,196,206,206,206,206,206,204,48,0,0,0,0,0,0,0,148,206,206,206,206,
+ 206,206,112,0,0,0,0,0,0,0,84,206,206,206,206,206,206,206,206,206,206,206,
+ 206,206,206,206,206,206,206,206,206,206,203,38,0,0,0,0,0,0,1,197,254,254,
+ 241,82,0,0,38,247,254,254,205,31,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,
+ 27,243,254,254,96,0,0,0,0,0,0,0,0,50,224,254,254,196,1,0,2,111,248,254,
+ 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,2,170,254,255,255,242,137,48,10,1,1,1,1,3,19,76,187,253,255,255,
+ 250,75,0,0,0,0,0,0,0,0,0,0,0,39,231,254,254,254,253,164,11,0,0,0,78,238,
+ 254,255,255,255,255,255,254,248,113,2,0,0,4,129,250,255,255,254,244,72,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,127,251,254,234,49,0,0,0,0,0,0,0,0,
+ 0,0,63,236,254,250,143,136,142,142,141,91,1,0,0,0,0,0,0,0,0,6,113,142,142,
+ 142,131,178,253,254,215,23,0,0,0,0,0,0,0,0,0,0,98,246,254,243,75,0,0,0,
+ 0,0,34,138,142,142,141,126,224,254,253,145,0,26,206,254,254,182,123,142,
+ 142,142,130,185,254,254,207,17,0,0,0,0,0,0,0,0,0,0,109,249,254,240,64,0,
+ 84,242,254,247,130,139,142,142,141,128,229,254,252,132,0,0,0,0,0,0,0,0,
+ 0,0,13,185,254,254,198,12,5,161,253,254,220,114,141,142,142,137,152,250,
+ 254,237,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,
+ 255,255,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,70,236,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,2,3,3,3,3,3,3,3,3,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,254,
+ 219,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,3,3,3,3,2,0,0,0,0,0,0,
+ 0,0,2,3,3,3,3,3,2,1,0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 3,3,3,2,0,0,0,0,0,0,0,1,197,254,240,82,0,0,0,38,247,254,205,30,0,0,0,0,
+ 0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,50,
+ 224,254,196,1,0,0,1,111,248,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,
+ 196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,245,255,255,255,254,254,247,225,
+ 200,199,199,199,204,237,252,254,255,255,255,254,192,6,0,0,0,0,0,0,0,0,0,
+ 0,7,151,252,254,255,254,253,188,37,0,75,239,254,255,255,255,255,255,255,
+ 255,254,248,113,2,22,160,251,254,255,255,254,185,17,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,4,130,231,66,0,0,0,0,0,0,0,0,0,0,0,0,68,232,158,207,254,
+ 254,254,254,248,108,1,0,0,0,0,0,0,6,147,252,254,254,254,254,183,179,215,
+ 40,0,0,0,0,0,0,0,0,0,0,0,1,102,236,92,1,0,0,0,0,35,211,254,254,254,254,
+ 245,146,222,155,8,0,0,31,206,188,174,253,254,254,254,253,176,186,208,33,
+ 0,0,0,0,0,0,0,0,0,0,0,2,113,234,81,0,0,0,88,235,148,220,254,254,254,254,
+ 242,144,227,144,6,0,0,0,0,0,0,0,0,0,0,0,18,186,200,28,0,0,10,162,219,148,
+ 247,254,254,254,254,209,156,232,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,230,146,0,0,
+ 0,55,252,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,235,254,254,254,254,254,
+ 254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,6,6,6,6,6,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,18,240,218,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,240,82,0,0,0,0,38,
+ 247,205,30,0,0,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,
+ 0,0,0,0,0,0,0,0,0,0,50,224,196,1,0,0,0,2,111,247,111,0,0,0,0,0,0,0,132,
+ 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,117,193,193,193,193,
+ 193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,189,44,
+ 0,0,0,0,0,0,0,0,0,0,0,7,151,252,254,255,255,254,223,38,128,235,254,254,
+ 255,255,255,255,255,254,254,241,169,19,178,254,254,255,255,254,190,18,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 43,190,254,255,255,255,255,254,246,72,0,0,0,0,0,0,114,252,255,255,255,255,
+ 254,253,155,30,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,0,0,0,0,0,10,196,254,255,
+ 255,255,255,254,243,93,8,0,0,0,0,25,142,253,255,255,255,255,254,253,145,
+ 26,0,0,0,0,0,0,0,0,0,0,0,0,0,2,37,0,0,0,0,0,51,209,254,255,255,255,255,
+ 254,240,85,6,0,0,0,0,0,0,0,0,0,0,0,0,0,17,23,0,0,0,0,10,99,245,255,255,
+ 255,255,254,254,193,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,230,147,0,0,0,55,252,81,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,235,254,254,254,254,254,254,254,80,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,205,216,216,216,215,147,7,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,18,204,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,183,82,0,0,0,0,0,38,198,31,
+ 0,0,0,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,
+ 0,0,0,0,0,0,50,167,1,0,0,0,0,2,112,104,0,0,0,0,0,0,0,132,254,255,225,10,
+ 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,209,217,217,217,217,217,217,217,
+ 217,217,217,217,217,217,217,217,217,217,217,217,217,217,108,0,0,0,0,0,0,
+ 0,0,0,0,0,0,8,151,252,254,254,245,99,1,1,28,104,164,209,218,218,218,214,
+ 173,106,40,2,0,38,212,254,254,254,213,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,234,254,255,255,255,253,
+ 166,11,0,0,0,0,0,0,26,198,254,255,255,255,254,215,41,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,79,239,254,255,255,255,253,155,9,0,0,0,0,0,0,
+ 32,206,254,255,255,255,254,208,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,89,243,254,255,255,255,252,144,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,10,162,253,255,255,255,254,236,73,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,
+ 230,222,45,0,3,131,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,236,254,
+ 254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,86,165,166,166,147,
+ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,0,
+ 0,0,15,27,0,0,0,0,0,0,0,0,0,0,0,0,119,166,166,118,0,0,0,17,158,166,165,
+ 62,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,2,17,0,0,0,0,0,0,0,132,254,255,
+ 225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,159,166,166,166,166,166,
+ 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,82,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,8,151,252,245,99,1,0,0,0,0,0,6,7,7,7,7,1,0,0,0,0,
+ 0,38,213,254,213,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,164,166,166,166,137,11,0,0,0,0,0,0,0,0,26,
+ 153,166,166,166,160,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 75,165,166,166,166,130,9,0,0,0,0,0,0,0,0,32,156,166,166,166,157,34,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,165,166,166,166,123,7,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,135,166,166,166,165,70,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,250,255,255,255,255,
+ 255,237,15,0,0,0,0,0,0,10,221,254,219,49,124,250,253,79,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,70,237,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,
+ 10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,147,99,1,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,38,179,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,131,251,
+ 254,254,254,249,116,1,0,0,0,0,0,0,0,69,237,254,236,250,254,178,15,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,236,254,254,254,254,80,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,
+ 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,105,144,
+ 145,144,96,2,0,0,0,0,0,0,0,0,0,64,144,145,145,129,14,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,71,235,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,
+ 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72,
+ 236,254,253,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,12,209,141,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,237,254,80,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,
+ 224,21,163,155,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,71,236,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,233,173,155,8,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,63,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,
+ 252,155,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,132,254,253,155,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,156,9,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 132,160,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};
+
+#endif // linux || svr4
+#endif // __SYNAES_SYMBOL_H__
diff --git a/kscd/kscdmagic/syna.h b/kscd/kscdmagic/syna.h
new file mode 100644
index 00000000..40128532
--- /dev/null
+++ b/kscd/kscdmagic/syna.h
@@ -0,0 +1,180 @@
+/*
+ $Id$
+
+ Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 author may be contacted at:
+ phar6@student.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#ifndef __SYNA_H__
+#define __SYNA_H__
+
+
+#if defined(__linux__) || defined(__svr4__) || defined(__osf__)
+
+void error(const char *str, bool syscall=false); //Display error and exit
+void warning(const char *str, bool syscall=false); //Display error
+
+//void error(char *str,bool syscall=false);
+inline void attempt(int x, const char *y, bool syscall=false) { if (x == -1) error(y,syscall); }
+
+//void warning(char *str,bool syscall=false);
+inline void attemptNoDie(int x, const char *y, bool syscall=false) { if (x == -1) warning(y,syscall); }
+
+#include "polygon.h"
+#include "magicconf.h"
+
+/***************************************/
+
+
+#define PROGNAME "kscdmagic"
+
+
+#ifdef __FreeBSD__
+
+
+typedef unsigned short sampleType;
+
+#else
+
+typedef short sampleType;
+
+#ifndef __linux__
+
+#warning This target has not been tested!
+
+#endif
+#endif
+
+#ifdef __osf__
+#include <machine/endian.h>
+#else
+#include <endian.h>
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define BIGENDIAN
+#else
+#define LITTLEENDIAN
+#endif
+
+
+#define n (1<<m)
+#define recSize (1<<m-overlap)
+
+/* core */
+extern volatile sampleType *data;
+//extern unsigned char *output, *lastOutput, *lastLastOutput;
+extern Bitmap<unsigned short> outputBmp, lastOutputBmp, lastLastOutputBmp;
+#define ucoutput ((unsigned char*)outputBmp.data)
+#define lastOutput ((unsigned char*)lastOutputBmp.data)
+#define lastLastOutput ((unsigned char*)lastLastOutputBmp.data)
+
+inline unsigned short combiner(unsigned short a,unsigned short b) {
+ //Not that i want to give the compiler a hint or anything...
+ unsigned char ah = a>>8, al = a&255, bh = b>>8, bl = b&255;
+ if (ah < 64) ah *= 4; else ah = 255;
+ if (al < 64) al *= 4; else al = 255;
+ if (bh > ah) ah = bh;
+ if (bl > al) al = bl;
+ return ah*256+al;
+}
+
+extern PolygonEngine<unsigned short,combiner,2> polygonEngine;
+
+extern int outWidth, outHeight;
+
+void allocOutput(int w,int h);
+
+void coreInit();
+void setStarSize(double size);
+int coreGo();
+void fade();
+
+/* *wrap */
+void screenInit(int xHint,int yHint,int widthHint,int heightHint);
+void screenSetPalette(unsigned char *palette);
+void screenEnd(void);
+void screenShow(void);
+int sizeUpdate(void);
+
+void inputUpdate(int &mouseX,int &mouseY,int &mouseButtons,char &keyHit);
+
+/* ui */
+void interfaceInit();
+void interfaceSyncToState();
+void interfaceEnd();
+bool interfaceGo();
+
+enum SymbolID {
+ Speaker, Bulb,
+ Play, Pause, Stop, SkipFwd, SkipBack,
+ Handle, Pointer, Open, NoCD, Exit,
+ Zero, One, Two, Three, Four,
+ Five, Six, Seven, Eight, Nine,
+ Slider, Selector, Plug, Loop, Box, Bar,
+ Flame, Wave, Stars, Star, Diamond, Size, FgColor, BgColor,
+ Save, Reset, TrackSelect,
+ NotASymbol
+};
+
+/* State information */
+
+extern SymbolID state;
+extern int track, frames;
+extern double trackProgress;
+extern char **playList;
+extern int playListLength, playListPosition;
+extern SymbolID fadeMode;
+extern bool pointsAreDiamonds;
+extern double brightnessTwiddler;
+extern double starSize;
+extern double fgRedSlider, fgGreenSlider, bgRedSlider, bgGreenSlider;
+
+extern double volume;
+
+void setStateToDefaults();
+void saveConfig();
+
+void putString(char *string,int x,int y,int red,int blue);
+
+/* sound */
+enum SoundSource { SourceLine, SourceCD, SourcePipe };
+
+void cdOpen(char *cdromName);
+void cdClose(void);
+void cdGetStatus(int &track, int &frames, SymbolID &state);
+void cdPlay(int trackFrame, int endFrame=-1);
+void cdStop(void);
+void cdPause(void);
+void cdResume(void);
+void cdEject(void);
+void cdCloseTray(void);
+int cdGetTrackCount(void);
+int cdGetTrackFrame(int track);
+void openSound(SoundSource sound, int downFactor, const char *dspName, char *mixerName);
+void closeSound();
+void setupMixer(double &loudness);
+void setVolume(double loudness);
+int getNextFragment(void);
+
+
+#endif // linux || svr4
+#endif // __SYNA_H__
diff --git a/kscd/kscdmagic/version.h b/kscd/kscdmagic/version.h
new file mode 100644
index 00000000..aa5df67c
--- /dev/null
+++ b/kscd/kscdmagic/version.h
@@ -0,0 +1 @@
+#define KSCDMAGICVERSION "2.0"
diff --git a/kscd/kscdmagic/xlib.c b/kscd/kscdmagic/xlib.c
new file mode 100644
index 00000000..2fb15c19
--- /dev/null
+++ b/kscd/kscdmagic/xlib.c
@@ -0,0 +1,781 @@
+/*
+ * XaoS, a fast portable realtime fractal zoomer
+ * Copyright (C) 1996,1997 by
+ *
+ * Jan Hubicka (hubicka@paru.cas.cz)
+ * Thomas Marsh (tmarsh@austin.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * Shamelessly ripped for use in xsynaesthesia
+ */
+/*#include "aconfig.h"*/
+#define X11_DRIVER
+/*#define MITSHM*/
+
+#ifdef X11_DRIVER
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __FreeBSD__
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+#include "xlib.h"
+#ifdef AMIGA
+#define XFlush(x) while(0)
+#endif
+
+#undef PIXMAP
+
+#define chkalloc(n) if (!n) fprintf(stderr, "out of memory\n"), exit(-1)
+
+int xupdate_size(xdisplay * d)
+{
+ int tmp;
+ Window wtmp;
+ int width = d->width, height = d->height;
+ XGetGeometry(d->display, d->window, &wtmp, &tmp, &tmp, &d->width, &d->height, (unsigned int *) &tmp, (unsigned int *) &tmp);
+ if ((int)d->width != width || (int)d->height != height)
+ return 1;
+ return 0;
+}
+
+void xflip_buffers(xdisplay * d)
+{
+ d->back = d->vbuffs[d->current];
+ d->current ^= 1;
+ d->vbuff = d->vbuffs[d->current];
+}
+
+void draw_screen(xdisplay * d)
+{
+ switch (d->image[0]->bits_per_pixel) {
+ case 16:{
+ unsigned short *de;
+ unsigned char *s;
+ unsigned char *e;
+ for (s = (unsigned char *) d->vbuffs[d->current],
+ e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height),
+ de = (unsigned short *) d->data[d->current]; s < e; s += 8, de += 8)
+ *de = d->pixels[*s],
+ *(de + 1) = d->pixels[*(s + 1)],
+ *(de + 2) = d->pixels[*(s + 2)],
+ *(de + 3) = d->pixels[*(s + 3)],
+ *(de + 4) = d->pixels[*(s + 4)],
+ *(de + 5) = d->pixels[*(s + 5)],
+ *(de + 6) = d->pixels[*(s + 6)],
+ *(de + 7) = d->pixels[*(s + 7)];
+ s -= 8;
+ de -= 8;
+ for (; s < e; s++, de++)
+ *de = d->pixels[*s];
+ break;
+ }
+ case 24:{
+ unsigned char *de;
+ unsigned char *s;
+ unsigned char *e;
+ for (s = (unsigned char *) d->vbuffs[d->current],
+ e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height),
+ de = (unsigned char *) d->data[d->current]; s < e; s++, de+=3)
+ de[0] = d->pixels[*s],
+ de[1] = d->pixels[*s]>>8,
+ de[2] = d->pixels[*s]>>16;
+
+ break;
+ }
+ case 32:{
+ unsigned long *de;
+ unsigned char *s;
+ unsigned char *e;
+ for (s = (unsigned char *) d->vbuffs[d->current],
+ e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height),
+ de = (unsigned long *) d->data[d->current]; s < e; s += 8, de += 8)
+ *de = d->pixels[*s],
+ *(de + 1) = d->pixels[*(s + 1)],
+ *(de + 2) = d->pixels[*(s + 2)],
+ *(de + 3) = d->pixels[*(s + 3)],
+ *(de + 4) = d->pixels[*(s + 4)],
+ *(de + 5) = d->pixels[*(s + 5)],
+ *(de + 6) = d->pixels[*(s + 6)],
+ *(de + 7) = d->pixels[*(s + 7)];
+ s -= 8;
+ de -= 8;
+ for (; s < e; s++, de++)
+ *de = d->pixels[*s];
+ break;
+ }
+ }
+#ifdef MITSHM
+ if (d->SharedMemFlag) {
+ XShmPutImage(d->display, d->window, d->gc, d->image[d->current], 0, 0, 0,
+ 0, d->width, d->height, True);
+ XFlush(d->display);
+ } else
+#endif
+ {
+ XPutImage(d->display, d->window, d->gc, d->image[d->current], 0, 0, 0, 0, d->width, d->height);
+ XFlush(d->display);
+ }
+ d->screen_changed = 0;
+}
+
+#ifdef MITSHM
+int alloc_shm_image(xdisplay * new)
+{
+ register char *ptr;
+ int temp, size = 0, i;
+ ptr = DisplayString(new->display);
+ if (!ptr || (*ptr == ':') || !strncmp(ptr, "localhost:", 10) ||
+ !strncmp(ptr, "unix:", 5) || !strncmp(ptr, "local:", 6)) {
+ new->SharedMemOption = XQueryExtension(new->display, "MIT-SHM", &temp, &temp, &temp);
+ } else {
+ new->SharedMemOption = False;
+ return 0;
+ }
+ new->SharedMemFlag = False;
+#if 0
+ new->SharedMemOption = True;
+ new->SharedMemFlag = False;
+#endif
+
+ if (new->SharedMemFlag) {
+ XShmDetach(new->display, &new->xshminfo[0]);
+ XShmDetach(new->display, &new->xshminfo[1]);
+ new->image[0]->data = (char *) NULL;
+ new->image[1]->data = (char *) NULL;
+ shmdt(new->xshminfo[0].shmaddr);
+ shmdt(new->xshminfo[1].shmaddr);
+ }
+ for (i = 0; i < 2; i++) {
+ if (new->SharedMemOption) {
+ int mul;
+ if (new->depth == 8)
+ mul = 1;
+ else if (new->depth <= 24)
+ mul = 2;
+ else
+ mul = 4;
+ new->SharedMemFlag = False;
+ new->image[i] = XShmCreateImage(new->display, new->visual, new->depth, ZPixmap,
+ NULL, &new->xshminfo[i], new->width, new->height * mul);
+ if (new->image[i]) {
+ temp = new->image[i]->bytes_per_line * new->image[i]->height;
+ new->linewidth = new->image[i]->bytes_per_line * 8 / new->image[i]->bits_per_pixel;
+ if (temp > size)
+ size = temp;
+ new->xshminfo[i].shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
+ if (new->xshminfo[i].shmid != -1) {
+ new->xshminfo[i].shmaddr = (char *) shmat(new->xshminfo[i].shmid, 0, 0);
+ if (new->xshminfo[i].shmaddr != (char *) -1) {
+ new->image[i]->data = new->xshminfo[i].shmaddr;
+ new->data[i] = new->vbuffs[i] = (char *) new->image[i]->data;
+ new->xshminfo[i].readOnly = True;
+
+ new->SharedMemFlag = XShmAttach(new->display, &new->xshminfo[i]);
+ XSync(new->display, False);
+ if (!new->SharedMemFlag) {
+ XDestroyImage(new->image[i]);
+ new->image[i] = (XImage *) NULL;
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ }
+ /* Always Destroy Shared Memory Ident */
+ shmctl(new->xshminfo[i].shmid, IPC_RMID, 0);
+ }
+ if (!new->SharedMemFlag) {
+ XDestroyImage(new->image[i]);
+ new->image[i] = (XImage *) NULL;
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ } else {
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ } else {
+ new->SharedMemFlag = 0;
+ return 0;
+ }
+ }
+ new->current = 0;
+ xflip_buffers(new);
+ return 1;
+}
+
+void free_shm_image(xdisplay * d)
+{
+ if (d->SharedMemFlag) {
+ XDestroyImage(d->image[0]);
+ XDestroyImage(d->image[1]);
+ XShmDetach(d->display, &d->xshminfo[0]);
+ XShmDetach(d->display, &d->xshminfo[1]);
+ shmdt(d->xshminfo[0].shmaddr);
+ shmdt(d->xshminfo[1].shmaddr);
+ }
+}
+
+#endif
+
+int alloc_image(xdisplay * d)
+{
+ int i;
+#ifdef MITSHM
+ if (!d->params->nomitshm && alloc_shm_image(d)) {
+ if (d->depth != 8) {
+ for (i = 0; i < 2; i++)
+ d->vbuffs[i] = malloc(d->linewidth * d->height);
+ }
+ return 1;
+ }
+#endif
+ for (i = 0; i < 2; i++) {
+ d->image[i] = XCreateImage(d->display, d->visual, d->depth, ZPixmap, 0,
+ NULL, d->width, d->height, 8, 0);
+ if (d->image[i] == NULL) {
+ printf("Out of memory for image..exiting\n");
+ exit(3);
+ }
+ /*Add a little extra memory to catch overruns when dumping image to buffer in draw_screen*/
+ d->image[i]->data = malloc(d->image[i]->bytes_per_line * d->height + 32);
+ memset(d->image[i]->data,0,d->image[i]->bytes_per_line * d->height);
+
+ if (d->image[i]->data == NULL) {
+ printf("Out of memory for image buffers..exiting\n");
+ exit(3);
+ }
+ d->data[i] = d->vbuffs[i] = (char *) d->image[i]->data;
+ d->linewidth = d->image[i]->bytes_per_line * 8 / d->image[i]->bits_per_pixel;
+ }
+ if (d->depth != 8) {
+ for (i = 0; i < 2; i++) {
+ /* Add a little extra memory to catch overruns */
+ /* when dumping image to buffer in draw_screen */
+ d->vbuffs[i] = malloc(d->linewidth * d->height + 32);
+ memset(d->vbuffs[i],0,d->linewidth * d->height);
+
+ if (d->vbuffs[i] == NULL) {
+ printf("Out of memory for image buffers2..exiting\n");
+ exit(3);
+ }
+ }
+ }
+ xflip_buffers(d);
+ return 1;
+}
+
+void free_image(xdisplay * d)
+{
+ if (d->depth != 8)
+ free(d->vbuffs[0]), free(d->vbuffs[1]);
+#ifdef MITSHM
+ if (d->SharedMemFlag) {
+ free_shm_image(d);
+ return;
+ }
+#endif
+ XDestroyImage(d->image[0]);
+ XDestroyImage(d->image[1]);
+}
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+
+xdisplay *xalloc_display(const char *s, int xHint, int yHint, int x, int y, xlibparam * params)
+{
+ xdisplay *xd;
+ Visual *defaultvisual;
+ XVisualInfo vis;
+
+
+ xd = (xdisplay *) calloc(sizeof(xdisplay), 1);
+ chkalloc(xd);
+ xd->display = XOpenDisplay((char *) NULL);
+ if (!xd->display) {
+ free((void *) xd);
+ return NULL;
+ }
+ xd->screen = DefaultScreen(xd->display);
+ xd->attributes = (XSetWindowAttributes *)
+ malloc(sizeof(XSetWindowAttributes));
+ chkalloc(xd->attributes);
+ xd->attributes->background_pixel = BlackPixel(xd->display,
+ xd->screen);
+ xd->attributes->border_pixel = BlackPixel(xd->display, xd->screen);
+ xd->attributes->event_mask = ButtonPressMask | StructureNotifyMask | ButtonReleaseMask | ButtonMotionMask | KeyPressMask | ExposureMask | KeyReleaseMask;
+ xd->attributes->override_redirect = False;
+ xd->attr_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+ xd->classX = InputOutput;
+ xd->xcolor.n = 0;
+ xd->parent_window = RootWindow(xd->display, xd->screen);
+ defaultvisual = DefaultVisual(xd->display, xd->screen);
+ xd->params = params;
+ if (!params->usedefault) {
+ if (defaultvisual->class != PseudoColor || (!XMatchVisualInfo(xd->display, xd->screen, 8, PseudoColor, &vis) && vis.colormap_size > 128)) {
+ xd->fixedcolormap = 1;
+ if (!XMatchVisualInfo(xd->display, xd->screen, 15, TrueColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 16, TrueColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 32, TrueColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 24, TrueColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 8, PseudoColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 7, PseudoColor, &vis)) {
+ if (!XMatchVisualInfo(xd->display, xd->screen, 8, TrueColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 8, StaticColor, &vis) &&
+ !XMatchVisualInfo(xd->display, xd->screen, 8, StaticGray, &vis)) {
+ printf("Display does not support PseudoColor depth 7,8,StaticColor depth 8, StaticGray depth 8, Truecolor depth 8,15,16,24 nor 32!\n");
+ return NULL;
+ } else
+ xd->truecolor = 1;
+ } else
+ xd->fixedcolormap = 0, xd->truecolor = 0;
+ } else
+ xd->truecolor = 1;
+ } else
+ xd->truecolor = 1;
+ } else
+ xd->truecolor = 1;
+ } else {
+ xd->truecolor = 0;
+ }
+ xd->depth = vis.depth;
+ xd->visual = vis.visual;
+ } else { /*usedefault */
+ vis.depth = xd->depth = DefaultDepth(xd->display, xd->screen);
+ xd->visual = defaultvisual;
+ switch (defaultvisual->class) {
+ case PseudoColor:
+ if (xd->depth <= 8) {
+ xd->depth = 8;
+ xd->truecolor = 0;
+ xd->fixedcolormap = 0;
+ } else {
+ printf("Pseudocolor visual on unsuported depth\n");
+ return NULL;
+ }
+ break;
+ case TrueColor:
+ case StaticColor:
+ case StaticGray:
+ xd->truecolor = 1;
+ xd->fixedcolormap = 1;
+ if (xd->depth <= 8)
+ xd->depth = 8;
+ else if (xd->depth <= 16)
+ xd->depth = 16;
+ else if (xd->depth <= 32)
+ xd->depth = 32;
+ else {
+ printf("Truecolor visual on unsuported depth\n");
+ return NULL;
+ }
+ break;
+ default:
+ printf("Unusuported visual\n");
+ break;
+ }
+ }
+ /*xd->visual->map_entries = 256; */
+ xd->colormap = xd->defaultcolormap = DefaultColormap(xd->display, xd->screen);
+
+ xd->window_name = s;
+ xd->height = y;
+ xd->width = x;
+ xd->border_width = 2;
+ xd->lastx = 0;
+ xd->lasty = 0;
+ xd->font_struct = (XFontStruct *) NULL;
+
+ xd->window = XCreateWindow(xd->display, xd->parent_window, xHint, yHint,
+ xd->width, xd->height, xd->border_width,
+ vis.depth, xd->classX, xd->visual,
+ xd->attr_mask, xd->attributes);
+ if (!xd->fixedcolormap && params->privatecolormap) {
+ unsigned long pixels[256];
+ int i;
+ xd->colormap = XCreateColormap(xd->display, xd->window, xd->visual, AllocNone);
+ XAllocColorCells(xd->display, xd->colormap, 1, 0, 0, pixels, MAX(xd->visual->map_entries, 256));
+ for (i = 0; i < 16; i++) {
+ xd->xcolor.c[i].pixel = pixels[i];
+ }
+ XQueryColors(xd->display, xd->defaultcolormap, xd->xcolor.c, 16);
+ XStoreColors(xd->display, xd->colormap, xd->xcolor.c, 16);
+ xd->privatecolormap = 1;
+ }
+ if (!xd->fixedcolormap)
+ XSetWindowColormap(xd->display, xd->window, xd->colormap);
+ xd->gc = XCreateGC(xd->display, xd->window, 0L, &(xd->xgcvalues));
+ XSetBackground(xd->display, xd->gc,
+ BlackPixel(xd->display, xd->screen));
+ XSetForeground(xd->display, xd->gc,
+ WhitePixel(xd->display, xd->screen));
+ XStoreName(xd->display, xd->window, xd->window_name);
+ XMapWindow(xd->display, xd->window);
+#if 1
+ XSelectInput(xd->display, xd->window,
+ /* ExposureMask | */
+ KeyPress |
+ /* KeyRelease | */
+ /* ConfigureRequest | */
+ /* FocusChangeMask | */
+ StructureNotifyMask |
+ ButtonPressMask | ButtonReleaseMask);
+#endif
+#ifdef PIXAMP
+ xd->pixmap = XCreatePixmap(xd->display, xd->window, xd->width,
+ xd->height, xd->depth);
+#endif
+
+ {
+ XColor c;
+ Pixmap p = XCreatePixmap(xd->display, xd->window, 1,1,1);
+ memset(&c,0,sizeof(c));
+ xd->cursor = XCreatePixmapCursor(xd->display, p,p,
+ &c,&c, 0,0);
+ /* We don't need no fancy cursor
+ XDefineCursor(xd->display,xd->window,xd->cursor);
+ */
+ XFreePixmap(xd->display, p);
+ }
+
+ return (xd);
+}
+
+void xsetcolor(xdisplay * d, int col)
+{
+ switch (col) {
+ case 0:
+ XSetForeground(d->display, d->gc,
+ BlackPixel(d->display, d->screen));
+ break;
+ case 1:
+ XSetForeground(d->display, d->gc,
+ WhitePixel(d->display, d->screen));
+ break;
+ default:
+ if ((col - 2) > d->xcolor.n) {
+ fprintf(stderr, "color error\n");
+ exit(3);
+ }
+ XSetForeground(d->display, d->gc,
+ d->xcolor.c[col - 2].pixel);
+ break;
+ }
+}
+void xrotate_palette(xdisplay * d, int direction, unsigned char co[3][256], int ncolors)
+{
+ int i, p;
+
+ if (d->privatecolormap) {
+ for (i = 0; i < d->xcolor.n; i++) {
+ p = d->xcolor.c[i].pixel;
+ d->xcolor.c[i].red = (int) co[0][p] * 256;
+ d->xcolor.c[i].green = (int) co[1][p] * 256;
+ d->xcolor.c[i].blue = (int) co[2][p] * 256;
+ }
+ XStoreColors(d->display, d->colormap, d->xcolor.c, d->xcolor.n);
+ }
+ if (d->truecolor) {
+ unsigned long oldpixels[256];
+ memcpy(oldpixels, d->pixels, sizeof(oldpixels));
+ p = (ncolors - 1 + direction) % (ncolors - 1) + 1;
+ for (i = 1; i < ncolors; i++) { /*this is ugly..I know */
+ d->pixels[i] = oldpixels[p];
+ p++;
+ if (p >= ncolors)
+ p = 1;
+ }
+ draw_screen(d);
+ }
+}
+int xalloc_color(xdisplay * d, int r, int g, int b, int readwrite)
+{
+ d->xcolor.n++;
+ d->xcolor.c[d->xcolor.n - 1].flags = DoRed | DoGreen | DoBlue;
+ d->xcolor.c[d->xcolor.n - 1].red = r;
+ d->xcolor.c[d->xcolor.n - 1].green = g;
+ d->xcolor.c[d->xcolor.n - 1].blue = b;
+ d->xcolor.c[d->xcolor.n - 1].pixel = d->xcolor.n - 1;
+ if ((readwrite && !d->fixedcolormap) || d->privatecolormap) {
+ unsigned long cell;
+ if (d->privatecolormap) {
+ cell = d->xcolor.c[d->xcolor.n - 1].pixel += 16;
+ if (d->xcolor.c[d->xcolor.n - 1].pixel >= d->visual->map_entries) {
+ d->xcolor.n--;
+ return (-1);
+ }
+ } else {
+ if (!XAllocColorCells(d->display, d->colormap, 0, 0, 0, &cell, 1)) {
+ d->xcolor.n--;
+ if (d->xcolor.n <= 32)
+ printf("Colormap is too full! close some colorfull aplications or use -private\n");
+ return (-1);
+ }
+ d->xcolor.c[d->xcolor.n - 1].pixel = cell;
+ }
+ XStoreColor(d->display, d->colormap, &(d->xcolor.c[d->xcolor.n - 1]));
+ return (cell);
+ }
+ if (!XAllocColor(d->display, d->colormap, &(d->xcolor.c[d->xcolor.n - 1]))) {
+ d->xcolor.n--;
+ if (d->xcolor.n <= 32)
+ printf("Colormap is too full! close some colorfull aplications or use -private\n");
+ return (-1);
+ }
+ d->pixels[d->xcolor.n - 1] = d->xcolor.c[d->xcolor.n - 1].pixel;
+ return (d->depth != 8 ? d->xcolor.n - 1 : d->xcolor.c[d->xcolor.n - 1].pixel);
+}
+
+void xfree_colors(xdisplay * d)
+{
+ unsigned long pixels[256];
+ int i;
+ for (i = 0; i < d->xcolor.n; i++)
+ pixels[i] = d->xcolor.c[i].pixel;
+ if (!d->privatecolormap)
+ XFreeColors(d->display, d->colormap, pixels, d->xcolor.n, 0);
+ d->xcolor.n = 0;
+}
+
+void xfree_display(xdisplay * d)
+{
+ XSync(d->display, 0);
+ if (d->font_struct != (XFontStruct *) NULL) {
+ XFreeFont(d->display, d->font_struct);
+ }
+ XUnmapWindow(d->display, d->window);
+#ifdef PIXMAP
+ XFreePixmap(d->display, d->pixmap);
+#endif
+ XDestroyWindow(d->display, d->window);
+ XFreeCursor(d->display, d->cursor);
+ XCloseDisplay(d->display);
+ free((void *) d->attributes);
+ free((void *) d);
+}
+
+#ifdef PIXMAP
+void xline(xdisplay * d, int x1, int y1, int x2, int y2)
+{
+ XDrawLine(d->display, d->pixmap, d->gc, x1, y1, x2, y2);
+ d->lastx = x2, d->lasty = y2;
+ d->screen_changed = 1;
+} void xlineto(xdisplay * d, int x, int y)
+{
+
+ XDrawLine(d->display, d->pixmap, d->gc, d->lastx, d->lasty, x, y);
+ d->lastx = x, d->lasty = y;
+ d->screen_changed = 1;
+} void xrect(xdisplay * d, int x1, int y1, int x2, int y2)
+{
+
+ XDrawRectangle(d->display, d->pixmap, d->gc, x1, y1,
+ (x2 - x1), (y2 - y1));
+ d->lastx = x2, d->lasty = y2;
+ d->screen_changed = 1;
+} void xfillrect(xdisplay * d, int x1, int y1, int x2, int y2)
+{
+
+ XFillRectangle(d->display, d->pixmap, d->gc, x1, y1,
+ (x2 - x1), (y2 - y1));
+ d->lastx = x2, d->lasty = y2;
+ d->screen_changed = 1;
+} void xpoint(xdisplay * d, int x, int y)
+{
+
+ XDrawPoint(d->display, d->pixmap, d->gc, x, y);
+ d->lastx = x, d->lasty = y;
+ d->screen_changed = 1;
+} void xflush(xdisplay * d)
+{
+
+ draw_screen(d);
+ XFlush(d->display);
+}
+
+void xclear_screen(xdisplay * d)
+{
+ xfillrect(d, 0, 0, d->width, d->height);
+ d->screen_changed = 1;
+}
+
+#endif
+void xmoveto(xdisplay * d, int x, int y)
+{
+ d->lastx = x, d->lasty = y;
+} int xsetfont(xdisplay * d, char *font_name)
+{
+
+ if (d->font_struct != (XFontStruct *) NULL) {
+ XFreeFont(d->display, d->font_struct);
+ }
+ d->font_struct = XLoadQueryFont(d->display, font_name);
+ if (!d->font_struct) {
+ fprintf(stderr, "could not load font: %s\n", font_name);
+ exit(3);
+ }
+ return (d->font_struct->max_bounds.ascent + d->font_struct->max_bounds.descent);
+}
+
+void xouttext(xdisplay * d, char *string)
+{
+ int sz;
+
+ sz = strlen(string);
+ XDrawImageString(d->display, d->window, d->gc, d->lastx, d->lasty,
+ string, sz);
+#if 0
+ d->lastx += XTextWidth(d->font_struct, string, sz);
+ d->screen_changed = 1;
+#endif
+} void xresize(xdisplay * d, XEvent * ev)
+{
+
+#ifdef PIXMAP
+ XFreePixmap(d->display, d->pixmap);
+#endif
+ d->width = ev->xconfigure.width;
+ d->height = ev->xconfigure.height;
+#ifdef PIXMAP
+ d->pixmap = XCreatePixmap(d->display, d->window, d->width,
+ d->height, d->depth);
+#endif
+}
+
+#ifdef PIXMAP
+void xarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2)
+{
+ XDrawArc(d->display, d->pixmap, d->gc, x, y, w, h, a1, a2);
+} void xfillarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2)
+{
+ XFillArc(d->display, d->pixmap, d->gc, x, y, w, h, a1, a2);
+}
+#endif
+
+void xsize_set(xdisplay *d, int width, int height)
+{
+ XResizeWindow(d->display, d->window, width, height);
+}
+
+int xmouse_x(xdisplay * d)
+{
+
+ return d->mouse_x;
+}
+
+int xmouse_y(xdisplay * d)
+{
+ return d->mouse_y;
+}
+
+void xmouse_update(xdisplay * d)
+{
+ Window rootreturn, childreturn;
+ int rootx = 0, rooty = 0, buttons = 0;
+
+ XEvent event;
+
+ if (XCheckMaskEvent(d->display,ButtonPressMask | ButtonReleaseMask, &event)) {
+ if (event.type == ButtonPress)
+ d->mouse_buttons |= 1 << ((XButtonEvent*)(&event))->button;
+ else
+ d->mouse_buttons &= ~( 1 << ((XButtonEvent*)(&event))->button );
+ }
+
+ XQueryPointer(d->display, d->window, &rootreturn, &childreturn,
+ &rootx, &rooty, &(d->mouse_x), &(d->mouse_y),
+ &buttons);
+}
+
+char xkeyboard_query(xdisplay * d) {
+ XEvent event;
+
+ if (XCheckMaskEvent(d->display,KeyPressMask | KeyReleaseMask, &event)) {
+ char *str =
+ XKeysymToString(XLookupKeysym((XKeyPressedEvent*)(&event),0));
+
+ if ( ((XKeyPressedEvent*)(&event))->state &
+ (ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask) )
+ return 0;
+
+ if (str) {
+ char key;
+
+ if (strlen(str) == 1)
+ key = str[0];
+ else if (strcmp(str,"equal") == 0)
+ key = '=';
+ else if (strcmp(str,"minus") == 0)
+ key = '-';
+ else if (strcmp(str,"bracketleft") == 0)
+ key = '[';
+ else if (strcmp(str,"bracketright") == 0)
+ key = ']';
+ else if (strcmp(str,"comma") == 0)
+ key = ',';
+ else if (strcmp(str,"period") == 0)
+ key = '.';
+ else if (strcmp(str,"slash") == 0)
+ key = '/';
+ else return 0;
+
+ if ( ((XKeyPressedEvent*)(&event))->state & ShiftMask )
+ switch(key) {
+ case '=' : key = '+'; break;
+ case '[' : key = '{'; break;
+ case ']' : key = '}'; break;
+ case ',' : key = '<'; break;
+ case '/' : key = '?'; break;
+ default :
+ if (key >= 'a' && key <= 'z')
+ key = key+'A'-'a';
+ break;
+ }
+ return key;
+ }
+ }
+
+ return 0;
+}
+
+int xsize_update(xdisplay *d,int *width,int *height) {
+ XEvent event;
+
+ if (XCheckMaskEvent(d->display,StructureNotifyMask, &event)) {
+ if (event.type == ConfigureNotify) {
+ xupdate_size(d);
+ free_image(d);
+ alloc_image(d);
+ *width = d->linewidth;
+ *height = d->height;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+unsigned int xmouse_buttons(xdisplay * d)
+{
+ return d->mouse_buttons;
+}
+#endif
diff --git a/kscd/kscdmagic/xlib.h b/kscd/kscdmagic/xlib.h
new file mode 100644
index 00000000..077a9052
--- /dev/null
+++ b/kscd/kscdmagic/xlib.h
@@ -0,0 +1,139 @@
+/*
+ * XaoS, a fast portable realtime fractal zoomer
+ * Copyright (C) 1996,1997 by
+ *
+ * Jan Hubicka (hubicka@paru.cas.cz)
+ * Thomas Marsh (tmarsh@austin.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef XAOS_X11_H
+#define XAOS_X11_H
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+/*#include "config.h"*/
+/*#define MITSHM*/
+
+#ifdef MITSHM
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif /* MITSHM */
+
+#ifdef n
+#undef n
+#endif
+
+typedef struct {
+ int n;
+ XColor c[256];
+} xcol_t;
+
+typedef struct {
+ int privatecolormap;
+ int usedefault;
+ int nomitshm;
+} xlibparam;
+
+typedef struct {
+ Colormap colormap;
+ Colormap defaultcolormap;
+ int fixedcolormap;
+ int privatecolormap;
+ xlibparam *params;
+ Display *display;
+ Window parent_window;
+ Window window;
+ unsigned int width, height;
+ unsigned int border_width;
+ unsigned long background;
+ int depth;
+ unsigned int classX;
+ Visual *visual;
+ unsigned long valuemask;
+ XSetWindowAttributes *attributes;
+ unsigned long attr_mask;
+ XSizeHints sizehints;
+ int screen;
+ const char *window_name;
+ int status;
+ GC gc;
+ XGCValues xgcvalues;
+ xcol_t xcolor;
+ Pixmap pixmap;
+ XFontStruct *font_struct;
+ int screen_changed;
+ int lastx, lasty;
+ int mouse_x, mouse_y;
+ unsigned int mouse_buttons;
+ int current;
+ XImage *image[2];
+#ifdef MITSHM
+ XShmSegmentInfo xshminfo[2];
+ int SharedMemOption;
+ int SharedMemFlag;
+#endif /* MITSHM */
+ unsigned long pixels[256];
+ char *vbuffs[2];
+ char *data[2];
+ char *vbuff;
+ char *back;
+ int truecolor;
+ int linewidth;
+
+ Cursor cursor;
+} xdisplay;
+
+extern int alloc_shm_image(xdisplay * d);
+extern void free_shm_image(xdisplay * d);
+extern int alloc_image(xdisplay * d);
+extern void free_image(xdisplay * d);
+extern int xupdate_size(xdisplay * d);
+extern void xflip_buffers(xdisplay * d);
+extern xdisplay *xalloc_display(const char *n, int x, int y, int width,int height, xlibparam * p);
+extern void xfree_display(xdisplay * d);
+extern void xsetcolor(xdisplay * d, int col);
+extern int xsetfont(xdisplay * d, char *font_name);
+extern int xalloc_color(xdisplay * d, int r, int g, int b, int readwrite);
+extern void xfree_colors(xdisplay * d);
+extern void xline(xdisplay * d, int x1, int y1, int x2, int y2);
+extern void xmoveto(xdisplay * d, int x, int y);
+extern void xlineto(xdisplay * d, int x, int y);
+extern void xrect(xdisplay * d, int x1, int y1, int x2, int y2);
+extern void xfillrect(xdisplay * d, int x1, int y1, int x2, int y2);
+extern void xarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2);
+extern void xfillarc(xdisplay * d, int x, int y, unsigned int w,
+ unsigned int h, int a1, int a2);
+extern void xpoint(xdisplay * d, int x, int y);
+extern void xflush(xdisplay * d);
+extern void xclear_screen(xdisplay * d);
+extern void xrotate_palette(xdisplay * d, int direction, unsigned char c[3][256], int ncolors);
+extern void draw_screen(xdisplay * d);
+extern void xouttext(xdisplay * d, char *string);
+extern void xresize(xdisplay * d, XEvent * ev);
+extern void xsize_set(xdisplay *d, int width, int height);
+extern int xsize_update(xdisplay *d,int *width,int *height);
+extern int xmouse_x(xdisplay * d);
+extern int xmouse_y(xdisplay * d);
+extern void xmouse_update(xdisplay * d);
+extern char xkeyboard_query(xdisplay * d);
+extern unsigned int xmouse_buttons(xdisplay * d);
+
+#endif /* XAOS_X11_H */
diff --git a/kscd/kscdmagic/xlibwrap.cpp b/kscd/kscdmagic/xlibwrap.cpp
new file mode 100644
index 00000000..b5241326
--- /dev/null
+++ b/kscd/kscdmagic/xlibwrap.cpp
@@ -0,0 +1,209 @@
+/* Synaesthesia - program to display sound graphically
+ Copyright (C) 1997 Paul Francis Harrison
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public 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 author may be contacted at:
+ pfh@yoyo.cc.monash.edu.au
+ or
+ 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia
+*/
+
+#if defined(__linux__) || defined (__svr4__) || defined(__osf__)
+
+#include <string.h>
+
+extern "C" {
+#include "xlib.h"
+}
+
+#include "magicconf.h"
+#include "syna.h"
+
+static xlibparam xparams = { 0, 0, 0 };
+static xdisplay *d;
+
+static bool lowColor, paletteInstalled;
+static unsigned char mapping[64];
+
+int setPalette(int ,int r,int g,int b) {
+ return xalloc_color(d,r*257,g*257,b*257,0);
+}
+
+void screenInit(int xHint,int yHint,int widthHint,int heightHint)
+{
+
+ int i;
+
+ d = xalloc_display("KSCD Magic",xHint,yHint,widthHint,heightHint,&xparams);
+
+ if (d == 0)
+ error("setting up a window");
+
+ if (!alloc_image(d))
+ error("allocating window buffer");
+ outWidth = widthHint;
+ outHeight = heightHint;
+
+ //XMoveWindow(d->display,d->window,xHint,yHint);
+
+#define BOUND(x) ((x) > 255 ? 255 : (x))
+#define PEAKIFY(x) BOUND((x) - (x)*(255-(x))/255/2)
+
+ lowColor = (d->depth <= 8);
+
+ /* Set up palette immediately.
+ * Original code has the following commented out.
+ */
+ if (!lowColor) {
+ for(i=0;i<256;i++)
+ attempt(setPalette(i,PEAKIFY((i&15*16)),
+ PEAKIFY((i&15)*16+(i&15*16)/4),
+ PEAKIFY((i&15)*16)),
+ " in X: could not allocate sufficient palette entries");
+ } else {
+ for(i=0;i<64;i++)
+ attempt(mapping[i] = setPalette(i,PEAKIFY((i&7*8)*4),
+ PEAKIFY((i&7)*32+(i&7*8)*2),
+ PEAKIFY((i&7)*32)),
+ " in X: could not allocate sufficient palette entries");
+ }
+}
+
+void screenSetPalette(unsigned char *palette) {
+ int i;
+
+ if (paletteInstalled)
+ xfree_colors(d);
+
+ if (!lowColor) {
+ for(i=0;i<256;i++)
+ attempt(setPalette(i,palette[i*3],palette[i*3+1],palette[i*3+2]),
+ " in X: could not allocate sufficient palette entries");
+ } else {
+ const int array[8] = {0,2,5,7,9,11,13,15};
+ for(i=0;i<64;i++) {
+ int p = (array[i&7]+array[i/8]*16) *3;
+ attempt(mapping[i] = setPalette(i,palette[p],palette[p+1],palette[p+2]),
+ " in X: could not allocate sufficient palette entries");
+ }
+ }
+
+ paletteInstalled = true;
+}
+
+void screenEnd() {
+ if (paletteInstalled)
+ xfree_colors(d);
+ xfree_display(d);
+}
+
+int sizeUpdate()
+{
+ int newWidth,newHeight;
+
+ if (xsize_update(d,&newWidth,&newHeight))
+ {
+ if (newWidth == outWidth && newHeight == outHeight)
+ return 0;
+ //delete[] output;
+ //outWidth = newWidth;
+ //outHeight = newHeight;
+ //output = new unsigned char [outWidth*outHeight*2];
+ //memset(output,32,outWidth*outHeight*2);
+ allocOutput(newWidth,newHeight);
+ return 1;
+ }
+ return 0;
+}
+
+void inputUpdate(int &mouseX,int &mouseY,int &mouseButtons,char &keyHit) {
+ xmouse_update(d);
+ mouseX = xmouse_x(d);
+ mouseY = xmouse_y(d);
+ mouseButtons = xmouse_buttons(d);
+ keyHit = xkeyboard_query(d);
+}
+
+void screenShow(void) {
+ register unsigned long *ptr2 = (unsigned long*)ucoutput;
+ unsigned long *ptr1 = (unsigned long*)d->back;
+ int i = outWidth*outHeight/4;
+ if (lowColor)
+ do {
+ register unsigned int const r1 = *(ptr2++);
+ register unsigned int const r2 = *(ptr2++);
+
+ //if (r1 || r2) {
+#ifdef LITTLEENDIAN
+ register unsigned int const v =
+ mapping[((r1&0xe0ul)>>5)|((r1&0xe000ul)>>10)]
+ |mapping[((r1&0xe00000ul)>>21)|((r1&0xe0000000ul)>>26)]*256U;
+ *(ptr1++) = v |
+ mapping[((r2&0xe0ul)>>5)|((r2&0xe000ul)>>10)]*65536U
+ |mapping[((r2&0xe00000ul)>>21)|((r2&0xe0000000ul)>>26)]*16777216U;
+#else
+ register unsigned int const v =
+ mapping[((r2&0xe0ul)>>5)|((r2&0xe000ul)>>10)]
+ |mapping[((r2&0xe00000ul)>>21)|((r2&0xe0000000ul)>>26)]*256U;
+ *(ptr1++) = v |
+ mapping[((r1&0xe0ul)>>5)|((r1&0xe000ul)>>10)]*65536U
+ |mapping[((r1&0xe00000ul)>>21)|((r1&0xe0000000ul)>>26)]*16777216U;
+#endif
+ //} else ptr1++;
+ } while (--i);
+ else
+ do {
+ // Asger Alstrup Nielsen's (alstrup@diku.dk)
+ // optimized 32 bit screen loop
+ register unsigned int const r1 = *(ptr2++);
+ register unsigned int const r2 = *(ptr2++);
+
+ //if (r1 || r2) {
+#ifdef LITTLEENDIAN
+ register unsigned int const v =
+ ((r1 & 0x000000f0ul) >> 4)
+ | ((r1 & 0x0000f000ul) >> 8)
+ | ((r1 & 0x00f00000ul) >> 12)
+ | ((r1 & 0xf0000000ul) >> 16);
+ *(ptr1++) = v |
+ ((r2 & 0x000000f0ul) << 16 -4)
+ | ((r2 & 0x0000f000ul) << 16 -8)
+ | ((r2 & 0x00f00000ul) << 16 -12)
+ | ((r2 & 0xf0000000ul) << 16 -16);
+#else
+ register unsigned int const v =
+ ((r2 & 0x000000f0ul) >> 4)
+ | ((r2 & 0x0000f000ul) >> 8)
+ | ((r2 & 0x00f00000ul) >> 12)
+ | ((r2 & 0xf0000000ul) >> 16);
+ *(ptr1++) = v |
+ ((r1 & 0x000000f0ul) << 16 -4)
+ | ((r1 & 0x0000f000ul) << 16 -8)
+ | ((r1 & 0x00f00000ul) << 16 -12)
+ | ((r1 & 0xf0000000ul) << 16 -16);
+#endif
+ //} else ptr1++;
+ } while (--i);
+
+ xflip_buffers(d);
+ draw_screen(d);
+ XFlush(d->display);
+}
+
+
+
+#endif // linux || svr4
+
diff --git a/kscd/ledlamp.cpp b/kscd/ledlamp.cpp
new file mode 100644
index 00000000..706c16cf
--- /dev/null
+++ b/kscd/ledlamp.cpp
@@ -0,0 +1,103 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1997 Richard Moore (moorer@cs.man.ac.uk)
+
+ 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 library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+#include <stdio.h>
+#include <qpainter.h>
+#include <qbrush.h>
+#include <qpen.h>
+#include <qcolor.h>
+#include "ledlamp.h"
+#include "ledlamp.moc"
+
+LedLamp::LedLamp(QWidget *parent, Type t) : QFrame(parent),
+ w( 10 ), h( 7 ), dx( 4 )
+{
+ // Make sure we're in a sane state
+ s = Off;
+
+ // Set the frame style
+ // setFrameStyle(Sunken | Box);
+ setGeometry(0,0,h+1,w+1);
+ ledtype = t;
+} // LedLamp
+
+void
+LedLamp::drawContents(QPainter *painter)
+{
+
+
+ QBrush lightBrush(this->foregroundColor());
+ QBrush darkBrush(this->backgroundColor());
+
+ QPen darkPen(this->backgroundColor(),1);
+ QPen lightPen(this->foregroundColor(), 1);
+
+ switch(s)
+ {
+ case On:
+ painter->setBrush(lightBrush);
+ switch (ledtype)
+ {
+ case Rect:
+ painter->drawRect(1,1,w-3, h-2);
+ break;
+ case Loop:
+ painter->setBrush(lightBrush);
+ painter->setPen(lightPen);
+
+ painter->drawLine(0, 2, 0, h-2); // |
+ painter->drawLine(1, 1, w-4, 1); // ~
+ painter->drawLine(w-3, 2, w-3, h-2); // |
+ painter->drawLine(2, h-2, w-3, h-2); //_
+ painter->drawLine(w-6,0,w-6,2); // ---+
+ painter->drawLine(3,h-2,3,h); // +---
+ break;
+ }
+ break;
+
+ case Off:
+ painter->setBrush(darkBrush);
+ switch (ledtype)
+ {
+ case Rect:
+ painter->drawRect(1,1,w-3, h-2);
+ break;
+ case Loop:
+ painter->setBrush(darkBrush);
+ painter->setPen(darkPen);
+
+ painter->fillRect(0,0,w,h, darkBrush);
+ /*
+ painter->drawLine(0, 2, 0, h-2); // |
+ painter->drawLine(1, 1, w-4, 1); // ~
+ painter->drawLine(w-3, 2, w-3, h-2); // |
+ painter->drawLine(2, h-2, w-3, h-2); //_
+ painter->drawLine(w-6,0,w-6,2); // ---+
+ painter->drawLine(3,h-2,3,h); // +---
+ */
+ break;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "LedLamp: INVALID State (%d)\n", s);
+ }
+} // drawContents
+
diff --git a/kscd/ledlamp.h b/kscd/ledlamp.h
new file mode 100644
index 00000000..15d4b8b4
--- /dev/null
+++ b/kscd/ledlamp.h
@@ -0,0 +1,63 @@
+
+/*
+ This file is part of the KDE libraries
+ Copyright (C) 1997 Richard Moore (moorer@cs.man.ac.uk)
+
+ 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 library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef LED_LAMP_H
+#define LED_LAMP_H
+
+#include <qframe.h>
+
+
+class LedLamp : public QFrame
+{
+ Q_OBJECT
+public:
+
+ enum Type { Rect, Loop };
+
+ LedLamp(QWidget *parent=0, Type t=Rect);
+
+ enum State { On, Off };
+
+
+ State state() const { return s; }
+
+
+ void setState(State state) { s= state; repaint(false); }
+
+
+ void toggleState() { if (s == On) s= Off; else if (s == Off) s= On; repaint(false); }
+public slots:
+ void toggle() { toggleState(); };
+ void on() { setState(On); };
+ void off() { setState(Off); };
+protected:
+ void drawContents(QPainter *);
+private:
+ const int w;
+ const int h;
+ const int dx;
+ State s;
+ int ledtype;
+};
+
+
+#endif
diff --git a/kscd/libwm/Makefile.am b/kscd/libwm/Makefile.am
new file mode 100644
index 00000000..ae2a9144
--- /dev/null
+++ b/kscd/libwm/Makefile.am
@@ -0,0 +1,23 @@
+#
+# Makefile.am for libworkman. Based on the example Makefile.am for a
+# shared library.
+#
+
+subdirs = include
+SUBDIRS = audio
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libworkman.la
+
+libworkman_la_LDFLAGS = $(ARTSC_LIBS) $(all_libraries)
+libworkman_la_LIBADD = audio/libworkmanaudio.la $(ARTS_LIBASOUND)
+
+libworkman_la_SOURCES = cddb.c cdinfo.c cdrom.c wm_helpers.c cdtext.c\
+database.c index.c scsi.c cdda.c plat_linux_cdda.c plat_sun_cdda.c\
+plat_aix.c plat_bsd386.c plat_freebsd.c plat_hpux.c plat_irix.c \
+plat_linux.c plat_svr4.c plat_ultrix.c plat_news.c plat_openbsd.c \
+plat_osf1.c plat_sun.c plat_scor5.c \
+drv_sony.c drv_toshiba.c
+
+#libworkmanincludedir = $(includedir)/libwm
+#libworkmaninclude_HEADERS = include/wm_cdrom.h include/wm_cdtext.h
diff --git a/kscd/libwm/PLAT_IMPL_STATUS b/kscd/libwm/PLAT_IMPL_STATUS
new file mode 100644
index 00000000..d1919728
--- /dev/null
+++ b/kscd/libwm/PLAT_IMPL_STATUS
@@ -0,0 +1,48 @@
+LIBRARY IMPLEMENTATION STATUS
+-----------------------------
+
+This table should show up where the platform codes differ.
+
+- function not available
+X function available
+\ function dummy, null implementation or commented out
+(X) function available in special configurations
+
+function |aix |bsd386|freebsd|hpux |irix |linux|news |openbsd|osf1 |scor5|sun |svr4 |ultrix|
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+find_cdrom() | - | - | - | - | - | - | - | - | X | - | X | X | X |
+gen_init() | \ | \ | \ | \ | (X) | \ | \ | \ | \ | \ | X | \ | \ |
+wmcd_open() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+wmcd_close() | - | - | - | - | - | - | X | - | - | - | - | - | - |
+wmcd_reopen() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+wm_scsi() | \ | \ | \ | X | \ | X | \ | \ | \ | X | X | X | \ |
+keep_cd_open() | - | - | - | - | - | \ | - | - | - | \ | - | - | - |
+gen_get_drive_status() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+gen_get_trackcount() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_trackinfo() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_cdlen() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+gen_play() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_pause() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_resume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_stop() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_eject() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_closetray() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+scale_volume() | X | X | X | - | - | X | - | X | X | - | - | - | X |
+unscale_volume() | X | X | X | - | - | - | - | X | X | - | - | - | X |
+gen_set_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+gen_get_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+cdda_init() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+get_ack() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+cdda_kill() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_direction() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_speed() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_set_loudness() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+gen_save() | - | - | - | - | - | \ | - | - | - | - | X | - | - |
+------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+
+
+
+
diff --git a/kscd/libwm/README b/kscd/libwm/README
new file mode 100644
index 00000000..69b4ae65
--- /dev/null
+++ b/kscd/libwm/README
@@ -0,0 +1,20 @@
+$Id$
+
+This directory contains the WorkMan library.
+
+libworkman is a multi-plaform CD-Player library for creating various
+CD-Player-UIs.
+
+06.03.2003 Alex Kern <alex.kern@gmx.de>
+ I have reworked most part of libworkman, get rid of most of externals
+ add cdda support for linux and, I hope not breaked it for SUN ;-)
+
+ Interface was cleaned, that means for a potentialy application developers
+ new rules:
+ 1. please include only wm_cdrom.h it's all defines and functions, what
+ you need. You have any exceptions.
+ 2. for cdtext, include wm_cdtext.h
+ 3. for cddb, include wm_cddb.h (think, it's not so powerfull and can be replaced with
+ any new external library.
+ 4. wm_cdinfo declares any externals now, I will change it in the future
+ \ No newline at end of file
diff --git a/kscd/libwm/audio/Makefile.am b/kscd/libwm/audio/Makefile.am
new file mode 100644
index 00000000..318cb0a3
--- /dev/null
+++ b/kscd/libwm/audio/Makefile.am
@@ -0,0 +1,5 @@
+INCLUDES = $(all_includes) $(ARTSC_INCLUDE)
+
+noinst_LTLIBRARIES = libworkmanaudio.la
+
+libworkmanaudio_la_SOURCES = audio.c audio_alsa.c audio_arts.c audio_sun.c
diff --git a/kscd/libwm/audio/audio.c b/kscd/libwm/audio/audio.c
new file mode 100644
index 00000000..39c8330d
--- /dev/null
+++ b/kscd/libwm/audio/audio.c
@@ -0,0 +1,29 @@
+#include "audio.h"
+
+#include <string.h>
+
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+struct audio_oops* setup_soundsystem(const char* ss, const char* dev, const char* ctl)
+{
+ if(!ss) {
+ ERRORLOG("audio: Internal error, trying to setup a NULL soundsystem.\n");
+ return NULL;
+ }
+
+#ifdef USE_ARTS
+ if(!strcmp(ss, "arts"))
+ return setup_arts(dev, ctl);
+#endif
+#if defined(HAVE_ARTS_LIBASOUND2)
+ if(!strcmp(ss, "alsa"))
+ return setup_alsa(dev, ctl);
+#endif
+#ifdef USE_SUN_AUDIO
+ if(!strcmp(ss, "sun"))
+ return setup_sun_audio(dev, ctl);
+#endif
+ ERRORLOG("audio: unknown soundsystem '%s'\n", ss);
+ return NULL;
+}
diff --git a/kscd/libwm/audio/audio.h b/kscd/libwm/audio/audio.h
new file mode 100644
index 00000000..089ea116
--- /dev/null
+++ b/kscd/libwm/audio/audio.h
@@ -0,0 +1,21 @@
+/*
+ * Audio 'LIB' defines
+ */
+#include "../include/wm_cdda.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+struct audio_oops {
+ int (*wmaudio_open)(void);
+ int (*wmaudio_close)(void);
+ int (*wmaudio_play)(struct cdda_block*);
+ int (*wmaudio_stop)(void);
+ int (*wmaudio_state)(struct cdda_block*);
+ int (*wmaudio_balance)(int);
+ int (*wmaudio_volume)(int);
+};
+
+extern struct audio_oops* setup_soundsystem(const char*, const char*, const char*);
+
diff --git a/kscd/libwm/audio/audio_alsa.c b/kscd/libwm/audio/audio_alsa.c
new file mode 100644
index 00000000..b1d4e938
--- /dev/null
+++ b/kscd/libwm/audio/audio_alsa.c
@@ -0,0 +1,334 @@
+/*
+ * Driver for Advanced Linux Sound Architecture, http://alsa.jcu.cz
+ *
+ * mpg123 comments:
+ * Code by Anders Semb Hermansen <ahermans@vf.telia.no>
+ * Cleanups by Jaroslav Kysela <perex@jcu.cz>
+ * Ville Syrjala <syrjala@sci.fi>
+ *
+ * adopted for libworkman cdda audio backend from Alexander Kern alex.kern@gmx.de
+ *
+ * Adapted to support both ALSA V0.x and V1.x APIs for PCM calls
+ * (Philip Nelson <teamdba@scotdb.com> 2004-03-15)
+ *
+ * This file comes under GPL license.
+ */
+
+
+
+#include <config.h>
+
+#if defined(HAVE_ARTS_LIBASOUND2)
+
+#include <alsa/asoundlib.h>
+#include "audio.h"
+
+char* device = NULL;
+snd_pcm_t *handle;
+
+snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
+int rate = 44100; /* stream rate */
+int channels = 2; /* count of channels */
+int buffer_time = 2000000; /* ring buffer length in us */
+int period_time = 100000; /* period time in us */
+
+snd_pcm_sframes_t buffer_size;
+snd_pcm_sframes_t period_size;
+
+int alsa_open(void);
+int alsa_close(void);
+int alsa_stop(void);
+int alsa_play(struct cdda_block *blk);
+int alsa_state(struct cdda_block *blk);
+struct audio_oops* setup_alsa(const char *dev, const char *ctl);
+
+static int set_hwparams(snd_pcm_hw_params_t *params,
+ snd_pcm_access_t accesspar)
+{
+ int err, dir, new_rate;
+
+ /* choose all parameters */
+ err = snd_pcm_hw_params_any(handle, params);
+ if (err < 0) {
+ ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the interleaved read/write format */
+ err = snd_pcm_hw_params_set_access(handle, params, accesspar);
+ if (err < 0) {
+ ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the sample format */
+ err = snd_pcm_hw_params_set_format(handle, params, format);
+ if (err < 0) {
+ ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* set the count of channels */
+ err = snd_pcm_hw_params_set_channels(handle, params, channels);
+ if (err < 0) {
+ ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
+ return err;
+ }
+ /* set the stream rate */
+#if (SND_LIB_MAJOR < 1)
+ err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0);
+#else
+ new_rate = rate;
+ err = snd_pcm_hw_params_set_rate_near(handle, params, &new_rate, 0);
+#endif
+ if (err < 0) {
+ ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
+ return err;
+ }
+ if (new_rate != rate) {
+ ERRORLOG("Rate doesn't match (requested %iHz, get %iHz)\n", rate, new_rate);
+ return -EINVAL;
+ }
+ /* set the buffer time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
+ return err;
+ }
+#if (SND_LIB_MAJOR < 1)
+ buffer_size = snd_pcm_hw_params_get_buffer_size(params);
+#else
+ err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err));
+ return err;
+ }
+#endif
+ DEBUGLOG("buffersize %i\n", buffer_size);
+
+ /* set the period time */
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir);
+#else
+ err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
+#endif
+ if (err < 0) {
+ ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
+ return err;
+ }
+
+#if (SND_LIB_MAJOR < 1)
+ period_size = snd_pcm_hw_params_get_period_size(params, &dir);
+#else
+ err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
+ if (err < 0) {
+ ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err));
+ }
+#endif
+
+ DEBUGLOG("period_size %i\n", period_size);
+
+ /* write the parameters to device */
+ err = snd_pcm_hw_params(handle, params);
+ if (err < 0) {
+ ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int set_swparams(snd_pcm_sw_params_t *swparams)
+{
+ int err;
+
+ /* get the current swparams */
+ err = snd_pcm_sw_params_current(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* start the transfer when the buffer is full */
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* allow the transfer when at least period_size samples can be processed */
+ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
+ if (err < 0) {
+ ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* align all transfers to 1 sample */
+ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
+ if (err < 0) {
+ ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ /* write the parameters to the playback device */
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0) {
+ ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+int alsa_open( void )
+{
+ int err;
+
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+
+ DEBUGLOG("alsa_open\n");
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) {
+ ERRORLOG("open failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+ if((err = set_swparams(swparams)) < 0) {
+ ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+int alsa_close( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_close\n");
+
+ err = alsa_stop();
+
+#if (SND_LIB_MAJOR < 1)
+ err = snd_pcm_close(handle);
+#else
+ err = snd_pcm_close(handle);
+#endif
+
+ free(device);
+
+ return err;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+alsa_play(struct cdda_block *blk)
+{
+ signed short *ptr;
+ int err = 0, frames;
+
+ ptr = (signed short *)blk->buf;
+ frames = blk->buflen / (channels * 2);
+ DEBUGLOG("play %i frames, %i bytes\n", frames, blk->buflen);
+ while (frames > 0) {
+ err = snd_pcm_writei(handle, ptr, frames);
+
+ if (err == -EAGAIN)
+ continue;
+ if(err == -EPIPE) {
+ err = snd_pcm_prepare(handle);
+ continue;
+ } else if (err < 0)
+ break;
+
+ ptr += err * channels;
+ frames -= err;
+ DEBUGLOG("played %i, rest %i\n", err / channels, frames);
+ }
+
+ if (err < 0) {
+ ERRORLOG("alsa_write failed: %s\n", snd_strerror(err));
+ err = snd_pcm_prepare(handle);
+
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+ blk->status = WM_CDM_CDDAERROR;
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+alsa_stop( void )
+{
+ int err;
+
+ DEBUGLOG("alsa_stop\n");
+
+ err = snd_pcm_drop(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err));
+ }
+
+ err = snd_pcm_prepare(handle);
+ if (err < 0) {
+ ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err));
+ }
+
+ return err;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+alsa_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ALSA */
+}
+
+static struct audio_oops alsa_oops = {
+ .wmaudio_open = alsa_open,
+ .wmaudio_close = alsa_close,
+ .wmaudio_play = alsa_play,
+ .wmaudio_stop = alsa_stop,
+ .wmaudio_state = alsa_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_alsa(const char *dev, const char *ctl)
+{
+ static int init_complete = 0;
+
+ if(dev && strlen(dev) > 0) {
+ device = strdup(dev);
+ } else {
+ device = strdup("plughw:0,0"); /* playback device */
+ }
+
+ if(init_complete) {
+ ERRORLOG("already initialized\n");
+ return NULL;
+ }
+ if(!alsa_open())
+ init_complete = 1;
+ else
+ return NULL;
+
+ return &alsa_oops;
+}
+
+#endif /* ALSA */
diff --git a/kscd/libwm/audio/audio_arts.c b/kscd/libwm/audio/audio_arts.c
new file mode 100644
index 00000000..a7b033c9
--- /dev/null
+++ b/kscd/libwm/audio/audio_arts.c
@@ -0,0 +1,141 @@
+/*
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ *
+ *
+ * Forget /dev/audio
+ * most modern soundcards accept 16LE with 44.1kHz
+ * Alexander Kern alex.kern@gmx.de
+ */
+
+#include <config.h>
+
+#ifdef USE_ARTS
+
+#include <artsc.h>
+
+#include "audio.h"
+
+arts_stream_t arts_stream = NULL;
+
+int arts_open(void);
+int arts_close(void);
+int arts_stop(void);
+int arts_play(struct cdda_block *blk);
+int arts_state(struct cdda_block *blk);
+struct audio_oops* setup_arts(const char *dev, const char *ctl);
+
+/*
+ * Initialize the audio device.
+ */
+int
+arts_open(void)
+{
+ int err;
+
+ DEBUGLOG("arts_open\n");
+
+ if(!(arts_stream = arts_play_stream(44100, 16, 2, "cddaslave"))) {
+ ERRORLOG("cannot open ARTS stream for playback\n");
+ return -1;
+ }
+ /* 1000 ms because we read 75 frames = 1 sec */
+ if((err = arts_stream_set(arts_stream, ARTS_P_BUFFER_TIME, 1000)) < 0) {
+ ERRORLOG("arts_stream_set failed (%s)\n", arts_error_text(err));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+arts_close(void)
+{
+ arts_stop();
+
+ DEBUGLOG("arts_close\n");
+ arts_close_stream(arts_stream);
+
+ arts_free();
+
+ return 0;
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+arts_play(struct cdda_block *blk)
+{
+ int err;
+
+ if((err = arts_write(arts_stream, blk->buf, blk->buflen)) < 0) {
+ ERRORLOG("arts_write failed (%s)\n", arts_error_text(err));
+ blk->status = WM_CDM_CDDAERROR;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+arts_stop(void)
+{
+ DEBUGLOG("arts_stop\n");
+ return 0;
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+arts_state(struct cdda_block *blk)
+{
+ return -1; /* not implemented yet for ARTS */
+}
+
+static struct audio_oops arts_oops = {
+ .wmaudio_open = arts_open,
+ .wmaudio_close = arts_close,
+ .wmaudio_play = arts_play,
+ .wmaudio_stop = arts_stop,
+ .wmaudio_state = arts_state,
+ .wmaudio_balance = NULL,
+ .wmaudio_volume = NULL
+};
+
+struct audio_oops*
+setup_arts(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = arts_init())) {
+ ERRORLOG("cannot initialize ARTS audio subsystem (%s)\n", arts_error_text(err));
+ return NULL;
+ }
+
+ arts_open();
+
+ return &arts_oops;
+}
+#endif
diff --git a/kscd/libwm/audio/audio_sun.c b/kscd/libwm/audio/audio_sun.c
new file mode 100644
index 00000000..78b1ab21
--- /dev/null
+++ b/kscd/libwm/audio/audio_sun.c
@@ -0,0 +1,525 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include <config.h>
+
+#ifdef USE_SUN_AUDIO
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "audio.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+sun_audio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+sun_audio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WM_CDM_PLAYING;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+int
+sun_audio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+ return 0;
+}
+
+/*
+ * Close the audio device.
+ */
+int
+sun_audio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+ return 0;
+}
+
+/*
+ * Set the volume level.
+ */
+int
+sun_audio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Set the balance level.
+ */
+int
+sun_audio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
+ perror("AUDIO_SETINFO");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+sun_audio_mark_last( void )
+{
+ queue[qtail].status = WM_CDM_TRACK_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+sun_audio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WM_CDM_TRACK_DONE)
+ queue[qhead].status = WM_CDM_PLAYING;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WM_CDM_TRACK_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+ long playablelen;
+
+ alarm(1);
+ playablelen = dev_audio_convert(rawbuf, buflen, blk);
+ while (write(aufd, rawbuf, playablelen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ sun_audio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+int
+sun_audio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+ return 0;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+static struct audio_oops sun_audio_oops = {
+ .wmaudio_open = sun_audio_open,
+ .wmaudio_close = sun_audio_close,
+ .wmaudio_play = sun_audio_play,
+ .wmaudio_stop = sun_audio_stop,
+ .wmaudio_state = sun_audio_state,
+ .wmaudio_balance = sun_audio_balance,
+ .wmaudio_volume = sun_audio_volume
+};
+
+struct audio_oops*
+setup_sun_audio(const char *dev, const char *ctl)
+{
+ int err;
+
+ if((err = sun_audio_init())) {
+ ERRORLOG("cannot initialize SUN /dev/audio subsystem \n");
+ return NULL;
+ }
+
+ sun_audio_open();
+
+ return &sun_audio_oops;
+}
+
+#endif /* USE_SUN_AUDIO */
diff --git a/kscd/libwm/buildindex.c b/kscd/libwm/buildindex.c
new file mode 100644
index 00000000..8edc3684
--- /dev/null
+++ b/kscd/libwm/buildindex.c
@@ -0,0 +1,221 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Build a WorkMan database index file from a flat text file. Requires
+ * 4.4BSD libdb library.
+ */
+
+static char buildindex_id[] = "$Id$";
+
+#include <stdio.h>
+#include <db.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h> /* for htonl() */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+char *strrchr();
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ DB *db;
+ DBT key, data;
+ FILE *fp;
+ int lock = 1, i = 0, locked, frame;
+ char buf[1000], indname[MAXPATHLEN + 100], framebuf[8], *c;
+ unsigned long pos;
+ BTREEINFO bt;
+ struct stat st;
+
+ if (argc > 2 && !strcmp(argv[1], "-n"))
+ {
+ lock = 0;
+ i++;
+ }
+
+ if (argc < i + 2)
+ {
+ fprintf(stderr, "Usage: %s [-n] dbfile [dbfile ...]\n",
+ argv[0]);
+ exit(1);
+ }
+
+ data.data = &pos;
+ data.size = sizeof(pos);
+ key.data = framebuf;
+ key.size = 7; /* %07d */
+
+ while (++i < argc)
+ {
+ fprintf(stderr, "Building index for %s\n", argv[i]);
+
+ if ((fp = fopen(argv[i], "r")) == NULL)
+ {
+ perror(argv[i]);
+ continue;
+ }
+
+ /*
+ * Figure out the file's mode, uid, gid, so we can set the
+ * permissions on the index file to the same thing.
+ */
+ if (fstat(fileno(fp), &st))
+ {
+ sprintf(indname, "%s: fstat", argv[i]);
+ perror(indname);
+ fclose(fp);
+ continue;
+ }
+
+ if (lock && lockit(fileno(fp), F_WRLCK))
+ {
+ sprintf(indname, "%s: Warning: Couldn't lock", argv[i]);
+ perror(indname);
+ locked = 0;
+ }
+ else
+ locked = 1;
+
+ /*
+ * Create a database file.
+ */
+ bt.flags = R_DUP; /* allow duplicate keys */
+ bt.cachesize = 0;
+ bt.psize = 0;
+ bt.lorder = 4321;
+ bt.minkeypage = 0;
+ bt.compare = NULL; /* use lexical comparison */
+ bt.prefix = NULL; /* no prefix comparisons */
+
+ /* Index files have ".ind" extensions */
+ sprintf(indname, "%s.ind", argv[i]);
+ if ((db = dbopen(indname, O_CREAT | O_RDWR | O_TRUNC,
+ st.st_mode, DB_BTREE, &bt)) == NULL)
+ {
+ perror(indname);
+ if (locked)
+ lockit(fileno(fp), F_UNLCK);
+ fclose(fp);
+ continue;
+ }
+
+ /*
+ * Now loop through the text file, inserting a record into
+ * the index file for each "tracks" line.
+ */
+ while (! feof(fp))
+ {
+ pos = ftell(fp);
+ buf[0] = '\0';
+ if (fgets(buf, sizeof(buf), fp) == NULL || ! buf[0])
+ {
+ /* End of file? */
+ if (feof(fp))
+ break;
+
+ /* Nope. A read error. Unlink the database. */
+ perror(argv[i]);
+ (void) unlink(indname);
+ break;
+ }
+
+ if (strncmp(buf, "tracks ", 7))
+ continue;
+
+ /*
+ * Found the start of a record. Figure out the start
+ * time of the last track and put an entry in the
+ * index file with that as the key.
+ */
+ c = strrchr(buf, ' '); /* this will always succeed */
+ *c = '\0';
+ c = strrchr(buf, ' '); /* this should too, but... */
+ if (c == NULL)
+ {
+ fprintf(stderr,
+ "%s: Malformed tracks line at %lu\n",
+ argv[i], pos);
+ continue;
+ }
+ sscanf(c+1, "%d", &frame);
+ sprintf(framebuf, "%07d", frame);
+ pos = htonl(pos);
+
+ if ((db->put)(db, &key, &data, 0))
+ {
+ perror(indname);
+ unlink(indname);
+ break;
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ (void) (db->close)(db);
+ if (locked)
+ lockit(fileno(fp), F_UNLCK);
+ }
+}
+
+/*
+ * Lock a file. Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(fd, type)
+ int fd;
+ int type;
+{
+ struct flock fl;
+ int result, timer = 0;
+
+ fl.l_type = type;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno != EACCES || errno != EAGAIN)
+ break;
+ if (timer++ == 30)
+ {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ sleep(1);
+ }
+
+ return (result);
+}
diff --git a/kscd/libwm/cdda.c b/kscd/libwm/cdda.c
new file mode 100644
index 00000000..ca8d76ba
--- /dev/null
+++ b/kscd/libwm/cdda.c
@@ -0,0 +1,458 @@
+/***************************************************************************
+ cdda.c - description
+ -------------------
+ begin : Mon Jan 27 2003
+ copyright : (C) 2003 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This is a common cddamaster piece of code *
+ * *
+ ***************************************************************************/
+
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+#include "include/wm_cdrom.h"
+#include "audio/audio.h"
+#include <pthread.h>
+
+#if defined(BUILD_CDDA)
+
+static pthread_t thread_read;
+static pthread_t thread_play;
+
+int get_next_block(int x);
+void *cdda_fct_read(void* arg);
+void *cdda_fct_play(void* arg);
+
+#define NUMBLOCKS 2
+#define NUMFRAMES 10
+
+static struct cdda_block blks[NUMBLOCKS];
+static pthread_mutex_t blks_mutex[NUMBLOCKS];
+
+static struct cdda_device dev;
+static pthread_cond_t wakeup_audio;
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+static unsigned int loudness = 0, volume = 32768, level;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+static FILE *output = NULL;
+
+/*
+ * These are driverdependent oops
+ *
+ */
+static struct audio_oops *oops = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+/* had to change #ifdef to #if -> see wm_cdda.h */
+#ifdef __FreeBSD__
+/* Phungus not with htonl on FreeBSD */
+#include <sys/param.h>
+#else
+#if WM_BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(unsigned long);
+#endif
+#endif
+
+/*
+ * Try to initialize the CDDA slave. Returns 0 on success.
+ */
+int
+gen_cdda_init( struct wm_drive *d )
+{
+ int ret = 0;
+
+ if (d->cdda_slave > -1)
+ return 0;
+
+ memset(&blks, 0, sizeof(blks));
+
+ dev.fd = -1;
+ dev.frames_at_once = NUMFRAMES;
+ dev.blocks = blks;
+ dev.numblocks = NUMBLOCKS;
+ dev.status = WM_CDM_UNKNOWN;
+ dev.devname = d->cd_device;
+
+ if ((ret = wmcdda_init(&dev)))
+ return ret;
+
+ oops = setup_soundsystem(d->soundsystem, d->sounddevice, d->ctldevice);
+ if (!oops) {
+ ERRORLOG("cdda: setup_soundsystem failed\n");
+ wmcdda_close(&dev);
+ return -1;
+ }
+
+ if(pthread_create(&thread_read, NULL, cdda_fct_read, &dev)) {
+ ERRORLOG("error by create pthread");
+ oops->wmaudio_close();
+ wmcdda_close(&dev);
+ return -1;
+ }
+
+ if(pthread_create(&thread_play, NULL, cdda_fct_play, &dev)) {
+ ERRORLOG("error by create pthread");
+ oops->wmaudio_close();
+ wmcdda_close(&dev);
+ return -1;
+ }
+ d->cdda_slave = 0;
+ return 0;
+}
+
+int
+cdda_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *frame, int *track, int *ind )
+{
+ if (d->cdda_slave > -1) {
+ if(dev.status)
+ *mode = dev.status;
+ else
+ *mode = oldmode;
+
+ if (*mode == WM_CDM_PLAYING) {
+ *track = dev.track;
+ *ind = dev.index;
+ *frame = dev.frame;
+ } else if (*mode == WM_CDM_CDDAERROR) {
+ /*
+ * An error near the end of the CD probably
+ * just means we hit the end.
+ */
+ *mode = WM_CDM_TRACK_DONE;
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_play( struct wm_drive *d, int start, int end, int realstart )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+
+ wmcdda_setup(start, end, realstart);
+
+ level = 2500;
+ volume = 1 << 15;
+
+ dev.track = -1;
+ dev.index = 0;
+ dev.frame = start;
+ dev.command = WM_CDM_PLAYING;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_pause( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ if(WM_CDM_PLAYING == dev.command) {
+ dev.command = WM_CDM_PAUSED;
+ } else {
+ dev.command = WM_CDM_PLAYING;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_stop( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+ oops->wmaudio_stop();
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_eject( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_EJECTED;
+ oops->wmaudio_stop();
+ /*wmcdda_close(&dev);*/
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+cdda_set_volume( struct wm_drive *d, int left, int right )
+{
+ if (d->cdda_slave > -1) {
+ int bal, vol;
+
+ bal = (right - left) + 100;
+ bal *= 255;
+ bal /= 200;
+ if (right > left)
+ vol = right;
+ else
+ vol = left;
+ vol *= 255;
+ vol /= 100;
+
+ if(oops->wmaudio_balance)
+ oops->wmaudio_balance(bal);
+ if(oops->wmaudio_volume)
+ oops->wmaudio_volume(vol);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+cdda_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ if (d->cdda_slave > -1) {
+ if(!oops->wmaudio_state) {
+ dev.volume = -1;
+ dev.balance = 128;
+ }
+
+ *left = *right = (dev.volume * 100 + 254) / 255;
+
+ if (dev.balance < 110)
+ *right = (((dev.volume * dev.balance + 127) / 128) * 100 + 254) / 255;
+ else if (dev.balance > 146)
+ *left = (((dev.volume * (255 - dev.balance) + 127) / 128) * 100 + 254) / 255;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Turn off the CDDA slave.
+ */
+void
+cdda_kill( struct wm_drive *d )
+{
+ if (d->cdda_slave > -1) {
+ dev.command = WM_CDM_STOPPED;
+ oops->wmaudio_stop();
+ sleep(1);
+ wmcdda_close(&dev);
+ oops->wmaudio_close();
+
+ dev.blocks = NULL;
+ wait(NULL);
+ d->cdda_slave = -1;
+ }
+}
+
+/*
+ * Tell the CDDA slave to set the loudness level.
+ */
+void
+cdda_set_loudness( struct wm_drive *d, int loud )
+{
+ if (d->cdda_slave > -1) {
+ loudness = loud;
+ }
+}
+
+/*
+ * Tell the CDDA slave to start (or stop) saving to a file.
+ */
+void
+cdda_save( struct wm_drive *d, char *filename )
+{
+ #if 0
+ int len;
+
+ if (filename == NULL || filename[0] == '\0')
+ len = 0;
+ else
+ len = strlen(filename);
+ write(d->cdda_slave, "F", 1);
+ write(d->cdda_slave, &len, sizeof(len));
+ if (len)
+ write(d->cdda_slave, filename, len);
+
+
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL) {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen) {
+ filename = malloc(namelen + 1);
+ if (filename == NULL) {
+ perror("cddas");
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL) {
+ perror(filename);
+ } else {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1, output);
+ }
+ free(filename);
+
+#endif
+}
+
+int get_next_block(int x)
+{
+ int y = ++x;
+ return (y < NUMBLOCKS)?y:0;
+}
+
+void *cdda_fct_read(void* arg)
+{
+ struct cdda_device *cddadev = (struct cdda_device*)arg;
+ int i, j, wakeup;
+ long result;
+
+ while (cddadev->blocks) {
+ while(cddadev->command != WM_CDM_PLAYING) {
+ cddadev->status = cddadev->command;
+ sleep(1);
+ }
+
+ i = 0;
+ pthread_mutex_lock(&blks_mutex[i]);
+ wakeup = 1;
+
+ while(cddadev->command == WM_CDM_PLAYING) {
+
+ result = wmcdda_read(cddadev, &blks[i]);
+
+ if (result <= 0 && blks[i].status != WM_CDM_TRACK_DONE) {
+ ERRORLOG("cdda: wmcdda_read failed, stop playing\n");
+ cddadev->command = WM_CDM_STOPPED;
+ break;
+ } else {
+ if (output)
+ fwrite(blks[i].buf, blks[i].buflen, 1, output);
+ }
+
+ j = get_next_block(i);
+ pthread_mutex_lock(&blks_mutex[j]);
+
+ if(wakeup) {
+ wakeup = 0;
+ pthread_cond_signal(&wakeup_audio);
+ }
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ /* audio can start here */
+
+ i = j;
+ }
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ }
+
+ return 0;
+}
+
+void *cdda_fct_play(void* arg)
+{
+ struct cdda_device *cddadev = (struct cdda_device*)arg;
+ int i = 0;
+
+ while (cddadev->blocks) {
+ if(cddadev->command != WM_CDM_PLAYING) {
+ i = 0;
+ pthread_mutex_lock(&blks_mutex[i]);
+ pthread_cond_wait(&wakeup_audio, &blks_mutex[i]);
+ } else {
+ i = get_next_block(i);
+ pthread_mutex_lock(&blks_mutex[i]);
+ }
+
+ if (oops->wmaudio_play(&blks[i])) {
+ oops->wmaudio_stop();
+ ERRORLOG("cdda: wmaudio_play failed\n");
+ cddadev->command = WM_CDM_STOPPED;
+ }
+ cddadev->frame = blks[i].frame;
+ cddadev->track = blks[i].track;
+ cddadev->index = blks[i].index;
+ cddadev->status = blks[i].status;
+
+ pthread_mutex_unlock(&blks_mutex[i]);
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/kscd/libwm/cddaslave.c b/kscd/libwm/cddaslave.c
new file mode 100644
index 00000000..9a008b84
--- /dev/null
+++ b/kscd/libwm/cddaslave.c
@@ -0,0 +1,572 @@
+/*
+ * $id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ******************************************************************
+ *
+ * Digital audio manipulator for WorkMan.
+ *
+ * The CDDA architecture looks like this:
+ *
+ * WorkMan (or another UI!)
+ * ^^^
+ * ||| (separate processes connected by pipe)
+ * vvv
+ * +------------- cddaslave -------------+
+ * | | |
+ * command module CDDA reader audio output
+ * (portable) (per platform) (per platform)
+ *
+ * This source file has the command module and some of the scaffolding
+ * to hold cddaslave together, plus some non-system-dependent audio
+ * processing code. Look in plat_*_cdda.c for system-specific stuff.
+ *
+ */
+
+#include "include/wm_cdda.h"
+
+#ifdef BUILD_CDDA
+
+static char cddaslave_id[] = "$Id$";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+#include "include/wm_platform.h"
+#include "audio/audio.h"
+
+void send_status(struct cdda_block *);
+
+#define SEND_ACK(b); { (b)->status |= WMCDDA_ACK; send_status(b); }
+#define SEND_STATUS(b); { (b)->status &= ~WMCDDA_ACK; send_status(b); }
+#define SEND_STATUS_ACK(b, s); { (b)->status = ((s) | WMCDDA_ACK); send_status(b); }
+
+int receive_command(struct cdda_device *, struct cdda_block *);
+
+int playing = 0; /* Should the CD be playing now? */
+int pausing = 0;
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+int loudness = 0;
+unsigned int volume = 32768;
+unsigned int level;
+
+/*
+ * Playback speed (0 = slow)
+ */
+int speed = 128;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+FILE *output = NULL;
+
+/*
+ * These are driverdependent oops
+ *
+ */
+struct audio_oops *oops = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long u_32;
+struct auheader {
+ u_32 magic;
+ u_32 hdr_size;
+ u_32 data_size;
+ u_32 encoding;
+ u_32 sample_rate;
+ u_32 channels;
+};
+
+/* had to change #ifdef to #if -> see wm_cdda.h */
+#ifdef __FreeBSD__
+/* Phungus not with htonl on FreeBSD */
+#include <sys/param.h>
+#else
+#if WM_BIG_ENDIAN
+# ifndef htonl
+# define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(x);
+#endif
+#endif
+
+void *malloc();
+long cdda_transform();
+
+/*
+ * Send status information upstream.
+ */
+void
+send_status(struct cdda_block *blk)
+{
+ DEBUGLOG("send_status, send %i(%s | %s)\n", blk->status,
+ gen_status(blk->status & WMCDDA_ACK), gen_status(blk->status & ~WMCDDA_ACK));
+ write(1, blk, sizeof(*blk));
+}
+
+/*
+ * Accept a command from our master.
+ *
+ * The protocol is byte-oriented:
+ * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end)
+ * xyz is the msf of the start of this chunk, i.e., the
+ * ending boundary for reverse play.
+ * S Stop.
+ * E Eject. This means we just close the CD device and
+ * open it again later.
+ * Q Quit.
+ * Vn Set volume level (0-255).
+ * Bn Set balance level (0-255).
+ * EnL Set an equalizer level (n = 0 for bass, 255 for treble)
+ * G Get current status.
+ * sn Set speed multiplier to n.
+ * dn Set direction to forward (n = 0) or reverse.
+ * Fllllx... Start saving to a file (length = l, followed by name)
+ * F0000 Stop saving.
+ * Ln Set loudness level (0-255).
+ * A Pause/Resume
+ * I Get status, current frame
+ */
+int
+receive_command(struct cdda_device *dev, struct cdda_block* blk)
+{
+ unsigned char inbuf[10];
+ char *filename;
+ int namelen;
+ struct auheader hdr;
+
+ if (read(0, inbuf, 1) <= 0) {
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+/* ERRORLOG("cddaslave: parent died, exit\n");*/
+ exit(0);
+ }
+
+ DEBUGLOG("cddaslave: CMD %c\n", inbuf[0]);
+
+ switch (inbuf[0]) {
+ case 'I':
+ if(dev->fd < 0) wmcdda_init(dev, blk);
+ SEND_ACK(blk);
+ break;
+ case 'A': /* pause/resume */
+ if(WMCDDA_PLAYING & blk->status) {
+ oops->wmaudio_stop();
+ SEND_STATUS_ACK(blk, WMCDDA_PAUSED);
+ } else if (WMCDDA_PAUSED & blk->status) {
+ SEND_STATUS_ACK(blk, WMCDDA_PLAYING);
+ } else {
+ SEND_ACK(blk);
+ }
+ break;
+ case 'E':
+ oops->wmaudio_stop();
+ wmcdda_close(dev);
+ SEND_ACK(blk);
+ break;
+ case 'P':
+ read(0, inbuf, 9);
+
+ wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
+ inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
+ inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
+
+ level = 2500;
+ volume = 1 << 15;
+
+ blk->track = -1;
+ blk->index = 0;
+ blk->minute = inbuf[6];
+ blk->second = inbuf[7];
+ blk->frame = inbuf[8];
+ SEND_STATUS_ACK(blk, WMCDDA_PLAYING);
+ break;
+ case 'S':
+ oops->wmaudio_stop();
+ SEND_STATUS_ACK(blk, WMCDDA_STOPPED);
+ break;
+ case 'B':
+ read(0, inbuf, 1);
+ if(oops->wmaudio_balance)
+ oops->wmaudio_balance(inbuf[0]);
+ break;
+ case 'V':
+ read(0, inbuf, 1);
+ if(oops->wmaudio_balance)
+ oops->wmaudio_volume(inbuf[0]);
+ break;
+ case 'G':
+ SEND_ACK(blk);
+ if(!oops->wmaudio_state || oops->wmaudio_state(blk) == -1) {
+ blk->volume = -1;
+ blk->balance = 128;
+ }
+ send_status(blk);
+ break;
+ case 'Q':
+ SEND_ACK(blk);
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(0);
+/*
+ case 's':
+ read(0, inbuf, 1);
+ speed = inbuf[0];
+ wmcdda_speed(speed);
+ SEND_STATUS(blk, WMCDDA_ACK);
+ break;
+
+ case 'd':
+ read(0, inbuf, 1);
+ wmcdda_direction(inbuf[0]);
+ SEND_STATUS(blk, WMCDDA_ACK);
+ break;
+*/
+ case 'L':
+ read(0, inbuf, 1);
+ loudness = inbuf[0];
+ SEND_ACK(blk);
+ break;
+ case 'F':
+ read(0, &namelen, sizeof(namelen));
+ if (output != NULL) {
+ fclose(output);
+ output = NULL;
+ }
+ if (namelen) {
+ filename = malloc(namelen + 1);
+ if (filename == NULL) {
+ perror("cddaslave");
+ wmcdda_close(dev);
+ oops->wmaudio_close();
+ exit(1);
+ }
+
+ read(0, filename, namelen);
+ filename[namelen] = '\0';
+ output = fopen(filename, "w");
+ if (output == NULL)
+ perror(filename);
+ else {
+ /* Write an .au file header. */
+ hdr.magic = htonl(0x2e736e64);
+ hdr.hdr_size = htonl(sizeof(hdr) + 28);
+ hdr.data_size = htonl(~0);
+ hdr.encoding = htonl(3); /* linear-16 */
+ hdr.sample_rate = htonl(44100);
+ hdr.channels = htonl(2);
+
+ fwrite(&hdr, sizeof(hdr), 1, output);
+ fwrite("Recorded from CD by WorkMan", 28, 1, output);
+ }
+ free(filename);
+ }
+ SEND_ACK(blk);
+ }
+
+ return(dev->fd);
+}
+
+
+/*
+ * Transform some CDDA data.
+ */
+long
+wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+ long i;
+ long *buf32 = (long *)rawbuf;
+ register short *buf16 = (short *)rawbuf;
+ register int aval;
+
+ /*
+ * Loudness transformation. Basically this is a self-adjusting
+ * volume control; our goal is to keep the average output level
+ * around a certain value (2500 seems to be pleasing.) We do this
+ * by maintaining a decaying average of the recent output levels
+ * (where "recent" is some fraction of a second.) All output levels
+ * are multiplied by the inverse of the decaying average; this has
+ * the volume-leveling effect we desire, and isn't too CPU-intensive.
+ *
+ * This is done by modifying the digital data, rather than adjusting
+ * the system volume control, because (at least on some systems)
+ * tweaking the system volume can generate little pops and clicks.
+ *
+ * There's probably a more elegant way to achieve this effect, but
+ * what the heck, I never took a DSP class and am making this up as
+ * I go along, with a little help from some friends.
+ *
+ * This is all done with fixed-point math, oriented around powers
+ * of two, which with luck will keep the CPU usage to a minimum.
+ * More could probably be done, for example using lookup tables to
+ * replace multiplies and divides; whether the memory hit (128K
+ * for each table) is worthwhile is unclear.
+ */
+ if (loudness)
+ {
+ /* We aren't really going backwards, but i > 0 is fast */
+ for (i = buflen / 2; i > 0; i--)
+ {
+ /*
+ * Adjust this sample to the current level.
+ */
+ aval = (*buf16 = (((long)*buf16) * volume) >> 15);
+ buf16++;
+
+ /*
+ * Don't adjust the decaying average for each sample;
+ * that just spends CPU time for very little benefit.
+ */
+ if (i & 127)
+ continue;
+
+ /*
+ * We want to use absolute values to compute the
+ * decaying average; otherwise it'd sit around 0.
+ */
+ if (aval < 0)
+ aval = -aval;
+
+ /*
+ * Adjust more quickly when we start hitting peaks,
+ * or we'll get clipping when there's a sudden loud
+ * section after lots of quiet.
+ */
+ if (aval & ~8191)
+ aval <<= 3;
+
+ /*
+ * Adjust the decaying average.
+ */
+ level = ((level << 11) - level + aval) >> 11;
+
+ /*
+ * Let *really* quiet sounds play softly, or we'll
+ * amplify background hiss to full volume and blast
+ * the user's speakers when real sound starts up.
+ */
+ if (! (level & ~511))
+ level = 512;
+
+ /*
+ * And adjust the volume setting using the inverse
+ * of the decaying average.
+ */
+ volume = (2500 << 15) / level;
+ }
+ }
+
+ if (speed == 128)
+ return (buflen);
+
+ /*
+ * Half-speed play. Stretch things out.
+ */
+ if (speed == 0)
+ {
+ buflen /= 2; /* Since we're moving 32 bits at a time */
+
+ for (i = buflen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[i / 2];
+ }
+
+ buflen *= 4; /* 2 for doubling the buffer, 2 from above */
+ }
+
+ /*
+ * Slow play; can't optimize it as well as half-speed.
+ */
+ if (speed && speed < 128)
+ {
+ int multiplier = ((speed + 128) * 128) / 256;
+ int newlen;
+ int tally = 0, pos;
+
+ buflen /= 4; /* Get the number of 32-bit values */
+
+ /*
+ * Buffer length doubles when speed is 0, stays the same
+ * when speed is 128.
+ */
+ newlen = (buflen * 128) / multiplier;
+
+ pos = buflen - 1;
+ for (i = newlen - 1; i > 0; i--)
+ {
+ buf32[i] = buf32[pos];
+ tally += multiplier;
+ if (tally & 128)
+ {
+ pos--;
+ tally ^= 128;
+ }
+ }
+
+ buflen = newlen * 4;
+ }
+
+ return (buflen);
+}
+
+
+int main(int argc, char **argv)
+{
+ fd_set readfd, dummyfd;
+ struct timeval timeout;
+ struct cdda_block blockinfo;
+ long result;
+ int nfds;
+ struct cdda_device dev;
+ const char *sondsystem;
+ const char *sounddevice;
+ const char *sounddevicectl;
+
+ memset(&blockinfo, 0, sizeof(struct cdda_block));
+
+ /*
+ * Device name should be the command-line argument.
+ */
+ if (argc < 2)
+ dev.devname = NULL;
+ else
+ dev.devname = argv[1];
+
+ if (argc < 3)
+ sondsystem = "arts";
+ else
+ sondsystem = argv[2];
+
+ if (argc < 4)
+ sounddevice = NULL;
+ else
+ sounddevice = argv[3];
+
+ if (argc < 5)
+ sounddevicectl = NULL;
+ else
+ sounddevicectl = argv[3];
+
+ DEBUGLOG("cddaslave: called with %s %s %s %s\n",
+ dev.devname?dev.devname:"",
+ sondsystem?sondsystem:"",
+ sounddevice?sounddevice:"",
+ sounddevicectl?sounddevicectl:"");
+
+ /*
+ * If we're running setuid root, bump up our priority then lose
+ * superuser access.
+ */
+ nice(-14);
+ setgid(getgid());
+ setuid(getuid());
+ if (geteuid() != getuid())
+ return 255;
+
+ FD_ZERO(&dummyfd);
+ FD_ZERO(&readfd);
+
+ timerclear(&timeout);
+
+ dev.fd = -1;
+ wmcdda_init(&dev, &blockinfo);
+
+ oops = setup_soundsystem(sondsystem, sounddevice, sounddevicectl);
+ if (!oops) {
+ ERRORLOG("cddaslave: setup_soundsystem failed\n");
+ exit(1);
+ }
+
+ DEBUGLOG("cddaslave: sent first ACK\n");
+ SEND_ACK(&blockinfo);
+
+ /*
+ * Accept commands as they come in, and play some sound if we're
+ * supposed to be doing that.
+ */
+ while (1) {
+ FD_SET(0, &readfd);
+
+ /*
+ * If we're playing, we don't want select to block. Otherwise,
+ * wait a little while for the next command.
+ */
+ if (playing)
+ timeout.tv_usec = 0;
+ else
+ timeout.tv_usec = 500000;
+
+ nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
+
+ if (nfds < 0) { /* Broken pipe; our GUI exited. */
+ wmcdda_close(&dev);
+ oops->wmaudio_close();
+ exit(0);
+ }
+
+ if (FD_ISSET(0, &readfd)) {
+ receive_command(&dev, &blockinfo);
+ /*
+ * Process all commands in rapid succession, rather
+ * than possibly waiting for a CDDA read.
+ */
+ continue;
+ }
+
+ if ((blockinfo.status & ~WMCDDA_ACK) == WMCDDA_PLAYING) {
+ result = wmcdda_read(&dev, &blockinfo);
+ if (result <= 0 && blockinfo.status != WMCDDA_DONE) {
+ ERRORLOG("cddaslave: wmcdda_read failed\n");
+ blockinfo.status = WMCDDA_STOPPED;
+ send_status(&blockinfo);
+ } else {
+ result = wmcdda_normalize(&dev, &blockinfo);
+ if (output)
+ fwrite(dev.buf, result, 1, output);
+
+ if (oops->wmaudio_play(dev.buf, dev.buflen, &blockinfo)) {
+ oops->wmaudio_stop();
+ ERRORLOG("cddaslave: wmaudio_play failed\n");
+ blockinfo.status = WMCDDA_STOPPED;
+ send_status(&blockinfo);
+ }
+ }
+ } else
+ send_status(&blockinfo);
+ }
+
+ return 0;
+}
+
+#endif /* BUILD_CDDA */
diff --git a/kscd/libwm/cddb.c b/kscd/libwm/cddb.c
new file mode 100644
index 00000000..657ab6a4
--- /dev/null
+++ b/kscd/libwm/cddb.c
@@ -0,0 +1,588 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * establish connection to cddb server and get data
+ * socket stuff gotten from gnu port of the finger command
+ *
+ * provided by Sven Oliver Moll
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cddb.h"
+
+struct wm_cddb cddb;
+
+extern struct wm_cdinfo thiscd;
+static int cur_cddb_protocol;
+static char *cur_cddb_server;
+static char *cur_cddb_mail_adress;
+static char *cur_cddb_path_to_cgi;
+static char *cur_cddb_proxy_server;
+
+/* local prototypes */
+int cddb_sum(int);
+char *string_split(char *line, char delim);
+void string_makehello(char *line, char delim);
+int connect_open(void);
+void connect_close(void);
+void connect_getline(char *line);
+void connect_read_entry(void);
+void cddbp_send(const char *line);
+void cddbp_read(char *category, unsigned int id);
+void http_send(char* line);
+void http_read(char *category, unsigned int id);
+void cddb_request(void);
+/* local prototypes END */
+
+static int Socket;
+static FILE *Connection;
+
+/*
+ *
+ */
+void
+cddb_cur2struct(void)
+{
+ cddb.protocol = cur_cddb_protocol;
+ strcpy(cddb.cddb_server, cur_cddb_server);
+ strcpy(cddb.mail_adress, cur_cddb_mail_adress);
+ strcpy(cddb.path_to_cgi, cur_cddb_path_to_cgi);
+ strcpy(cddb.proxy_server, cur_cddb_proxy_server);
+} /* cddb_cur2struct() */
+
+/*
+ *
+ */
+void
+cddb_struct2cur(void)
+{
+ cur_cddb_protocol = cddb.protocol;
+ cur_cddb_server = (cddb.cddb_server);
+ cur_cddb_mail_adress = (cddb.mail_adress);
+ cur_cddb_path_to_cgi = (cddb.path_to_cgi);
+ cur_cddb_proxy_server = (cddb.proxy_server);
+} /* cddb_struct2cur() */
+
+
+/*
+ * Subroutine from cddb_discid
+ */
+int
+cddb_sum(int n)
+{
+ char buf[12],
+ *p;
+ int ret = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ sprintf(buf, "%lu", (unsigned long)n);
+ for (p = buf; *p != '\0'; p++)
+ ret += (*p - '0');
+
+ return (ret);
+} /* cddb_sum() */
+
+
+/*
+ * Calculate the discid of a CD according to cddb
+ */
+unsigned long
+cddb_discid(void)
+{
+ int i,
+ t,
+ n = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ for (i = 0; i < thiscd.ntracks; i++) {
+
+ n += cddb_sum(thiscd.trk[i].start / 75);
+ /*
+ * Just for demonstration (See below)
+ *
+ * t += (thiscd.trk[i+1].start / 75) -
+ * (thiscd.trk[i ].start / 75);
+ */
+ }
+
+ /*
+ * Mathematics can be fun. Example: How to reduce a full loop to
+ * a simple statement. The discid algorhythm is so half-hearted
+ * developed that it doesn't even use the full 32bit range.
+ * But it seems to be always this way: The bad standards will be
+ * accepted, the good ones turned down.
+ * Boy, you pulled out the /75. This is not correct here, because
+ * this calculation needs the integer division for both .start
+ * fields.
+ */
+
+ t = (thiscd.trk[thiscd.ntracks].start / 75)
+ - (thiscd.trk[0].start / 75);
+ return ((n % 0xff) << 24 | t << 8 | thiscd.ntracks);
+} /* cddb_discid() */
+
+/*
+ * Split a string into two components according to the first occurrence of
+ * the delimiter.
+ */
+char *
+string_split(char *line, char delim)
+{
+ char *p1;
+
+ for (p1=line;*p1;p1++)
+ {
+ if(*p1 == delim)
+ {
+ *p1 = 0;
+ return ++p1;
+ }
+ }
+ return (NULL);
+} /* string_split() */
+
+/*
+ * Generate the hello string according to the cddb protocol
+ * delimiter is either ' ' (cddbp) or '+' (http)
+ */
+void
+string_makehello(char *line,char delim)
+{
+ char mail[84],*host;
+
+ strcpy(mail,cddb.mail_adress);
+ host=string_split(mail,'@');
+
+ sprintf(line,"%shello%c%s%c%s%c%s%c%s",
+ delim == ' ' ? "cddb " : "&",
+ delim == ' ' ? ' ' : '=',
+ mail,delim,
+ host,delim,
+ WORKMAN_NAME,delim,
+ WORKMAN_VERSION);
+} /* string_makehello() */
+
+/*
+ * Open the TCP connection to the cddb/proxy server
+ */
+int
+connect_open(void)
+{
+ char *host;
+ struct hostent *hp;
+ struct sockaddr_in soc_in;
+ int port;
+
+ if(cddb.protocol == 3) /* http proxy */
+ host = wm_strdup(cddb.proxy_server);
+ else
+ host = wm_strdup(cddb.cddb_server);
+ /*
+ * t=string_split(host,':');
+ */
+ port=atoi(string_split(host,':'));
+ if(!port)
+ port=8880;
+
+#ifndef NDEBUG
+ printf("%s:%d\n",host,port);
+#endif
+ hp =gethostbyname(host);
+
+ if (hp == NULL)
+ {
+ static struct hostent def;
+ static struct in_addr defaddr;
+ static char *alist[1];
+ static char namebuf[128];
+ int inet_addr();
+
+ defaddr.s_addr = inet_addr(host);
+ if ((int) defaddr.s_addr == -1)
+ {
+#ifndef NDEBUG
+ printf("unknown host: %s\n", host);
+#endif
+ return (-1);
+ }
+ strcpy(namebuf, host);
+ def.h_name = namebuf;
+ def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
+ def.h_length = sizeof (struct in_addr);
+ def.h_addrtype = AF_INET;
+ def.h_aliases = 0;
+ hp = &def;
+ }
+ soc_in.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, (char *)&soc_in.sin_addr, hp->h_length);
+ soc_in.sin_port = htons(port);
+ Socket = socket(hp->h_addrtype, SOCK_STREAM, 0);
+ if (Socket < 0)
+ {
+ perror("socket");
+ return (-1);
+ }
+ fflush(stdout);
+ if (connect(Socket, (struct sockaddr *)&soc_in, sizeof (soc_in)) < 0)
+ {
+ perror("connect");
+ close(Socket);
+ return (-1);
+ }
+
+ Connection = fdopen(Socket, "r");
+ return (0);
+} /* connect_open() */
+
+
+/*
+ * Close the connection
+ */
+void
+connect_close(void)
+{
+ (void)fclose(Connection);
+ close(Socket);
+} /* connect_close() */
+
+/*
+ * Get a line from the connection with CR and LF stripped
+ */
+void
+connect_getline(char *line)
+{
+ char c;
+
+ while ((c = getc(Connection)) != '\n')
+ {
+ *line = c;
+ if ((c != '\r') && (c != (char)0xff))
+ line++;
+ }
+ *line=0;
+} /* connect_getline() */
+
+/*
+ * Read the CD data from the server and place them into the cd struct
+ */
+void
+connect_read_entry(void)
+{
+ char type;
+ int trknr;
+
+ char *t,*t2,tempbuf[2000];
+
+ while(strcmp(tempbuf,"."))
+ {
+ connect_getline(tempbuf);
+
+ t=string_split(tempbuf,'=');
+ if(t != NULL)
+ {
+ type=tempbuf[0];
+
+ if(strncmp("TITLE",tempbuf+1,5))
+ continue;
+
+ if('D' == type)
+ {
+ /*
+ * Annahme: "Interpret / Titel" ist falsch.
+ * Daher: NULL-String erwarten.
+ */
+ t2=string_split(t,'/');
+ if(t2 == NULL)
+ t2 = t;
+ if(*t2 == ' ')
+ t2++;
+ strncpy(cd->cdname,t2,sizeof(cd->cdname)-1);
+ cd->cdname[sizeof(cd->cdname)-1]='\0';
+
+ for(t2=t;*t2;t2++)
+ {
+ if((*t2 == ' ') && (*(t2+1) == 0))
+ *t2=0;
+ }
+ strncpy(cd->artist,t,sizeof(cd->artist)-1);
+ cd->artist[sizeof(cd->artist)-1]='\0';
+ }
+
+ if('T' == type)
+ {
+ trknr=atoi(tempbuf+6);
+ /*
+ * printf("Track %d:%s\n",trknr,t);
+ */
+ wm_strmcpy(&cd->trk[trknr].songname,t);
+ }
+ /*
+ * fprintf(stderr, "%s %s\n",tempbuf,t);
+ */
+ }
+ }
+} /* connect_read_entry() */
+
+/*
+ * Send a command to the server using cddbp
+ */
+void
+cddbp_send(const char *line)
+{
+ write(Socket, line, strlen(line));
+ write(Socket, "\n", 1);
+} /* cddbp_send() */
+
+/*
+ * Send the "read from cddb" command to the server using cddbp
+ */
+void
+cddbp_read(char *category, unsigned int id)
+{
+ char tempbuf[84];
+ sprintf(tempbuf, "cddb read %s %08x", category, id);
+ cddbp_send(tempbuf);
+} /* cddbp_read() */
+
+/*
+ * Send a command to the server using http
+ */
+void
+http_send(char* line)
+{
+ char tempbuf[2000];
+
+ write(Socket, "GET ", 4);
+#ifndef NDEBUG
+ printf("GET ");
+#endif
+ if(cddb.protocol == 3)
+ {
+ write(Socket, "http://", 7);
+ write(Socket, cddb.cddb_server, strlen(cddb.cddb_server));
+#ifndef NDEBUG
+ printf("http://%s",cddb.cddb_server);
+#endif
+ }
+ write(Socket, cddb.path_to_cgi, strlen(cddb.path_to_cgi));
+ write(Socket, "?cmd=" ,5);
+ write(Socket, line, strlen(line));
+#ifndef NDEBUG
+ printf("%s?cmd=%s",cddb.path_to_cgi,line);
+#endif
+ string_makehello(tempbuf,'+');
+ write(Socket, tempbuf, strlen(tempbuf));
+#ifndef NDEBUG
+ printf("%s",tempbuf);
+#endif
+ write(Socket, "&proto=1 HTTP/1.0\n\n", 19);
+#ifndef NDEBUG
+ printf("&proto=1 HTTP/1.0\n");
+#endif
+ do
+ connect_getline(tempbuf);
+ while(strcmp(tempbuf,""));
+} /* http_send() */
+
+/*
+ * Send the "read from cddb" command to the server using http
+ */
+void
+http_read(char *category, unsigned int id)
+{
+ char tempbuf[84];
+ sprintf(tempbuf, "cddb+read+%s+%08x", category, id);
+ http_send(tempbuf);
+} /* http_read() */
+
+/*
+ * The main routine called from the ui
+ */
+void
+cddb_request(void)
+{
+ int i;
+ char tempbuf[2000];
+ extern int cur_ntracks;
+
+ int status;
+ char category[21];
+ unsigned int id;
+
+ strcpy(cddb.cddb_server,"localhost:888");
+ strcpy(cddb.mail_adress,"svolli@bigfoot.com");
+ /*
+ * cddb.protocol = 1;
+ */
+ wipe_cdinfo();
+
+ switch(cddb.protocol)
+ {
+ case 1: /* cddbp */
+#ifndef NDEBUG
+ printf("USING CDDBP\n");
+ printf("open\n");
+#endif
+ connect_open();
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ /*
+ * if(atoi(tempbuf) == 201) return;
+ */
+
+ /*
+ * strcpy(tempbuf,"cddb hello svolli bigfoot.com Eierkratzer eins");
+ */
+ string_makehello(tempbuf,' ');
+#ifndef NDEBUG
+ fprintf(stderr, "%s\n", tempbuf);
+#endif
+ cddbp_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+ printf("query\n");
+#endif
+ sprintf(tempbuf, "cddb query %08x %d",thiscd.cddbid,thiscd.ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (thiscd.trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ thiscd.trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), " %d\n", thiscd.length);
+#ifndef NDEBUG
+ printf(">%s<\n",tempbuf);
+#endif
+ cddbp_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ status=atoi(tempbuf);
+ /*
+ * fprintf(stderr, "status:%d\n",status);
+ * fprintf(stderr,"category:%s\n",category);
+ * fprintf(stderr,"id:%s\n",id);
+ */
+ if(status == 200) /* Exact match */
+ {
+ sscanf(tempbuf,"%d %20s %08x",&status,category,&id);
+ cddbp_read(category,id);
+ connect_read_entry();
+ }
+
+ if(status == 211) /* Unexact match, multiple possible
+ * Hack: always use first. */
+ {
+ connect_getline(tempbuf);
+ sscanf(tempbuf,"%20s %08x",category,&id);
+ while(strcmp(tempbuf,"."))
+ connect_getline(tempbuf);
+ cddbp_read(category,id);
+ connect_read_entry();
+ }
+
+ cddbp_send("quit");
+ connect_close();
+#ifndef NDEBUG
+ printf("close\n");
+#endif
+ break;
+ case 2: /* http */
+ case 3: /* http proxy */
+#ifndef NDEBUG
+ printf("USING HTTP%s\n",
+ (cddb.protocol == 3) ? " WITH PROXY" : "");
+ printf("query\n");
+#endif
+ sprintf(tempbuf, "cddb+query+%08x+%d",thiscd.cddbid,thiscd.ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (thiscd.trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), "+%d",
+ thiscd.trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), "+%d", thiscd.length);
+#ifndef NDEBUG
+ printf(">%s<\n",tempbuf);
+#endif
+ connect_open();
+ http_send(tempbuf);
+ connect_getline(tempbuf);
+#ifndef NDEBUG
+ printf("[%s]\n",tempbuf);
+#endif
+ status=atoi(tempbuf);
+ /*
+ * fprintf(stderr, "status:%d\n",status);
+ * fprintf(stderr, "category:%s\n",category);
+ * fprintf(stderr, "id:%s\n",id);
+ */
+
+ if(status == 200) /* Exact match */
+ {
+ connect_close();
+ connect_open();
+ sscanf(tempbuf,"%d %20s %08x",&status,category,&id);
+ http_read(category,id);
+ connect_read_entry();
+ }
+
+ if(status == 211) /* Unexact match, multiple possible
+ * Hack: always use first. */
+ {
+ connect_getline(tempbuf);
+ sscanf(tempbuf,"%20s %08x",category,&id);
+ while(strcmp(tempbuf,"."))
+ connect_getline(tempbuf);
+ connect_close();
+ connect_open();
+ http_read(category,id);
+ connect_read_entry();
+ }
+ /* moved close above break */
+ connect_close();
+ break;
+ default: /* off */
+ break;
+ }
+} /* cddb_request() */
+
diff --git a/kscd/libwm/cdinfo.c b/kscd/libwm/cdinfo.c
new file mode 100644
index 00000000..b04dec69
--- /dev/null
+++ b/kscd/libwm/cdinfo.c
@@ -0,0 +1,890 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Get information about a CD.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "include/wm_config.h"
+
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_database.h"
+#include "include/wm_helpers.h"
+
+struct wm_play *playlist = NULL;
+struct wm_cdinfo thiscd, *cd = &thiscd;
+
+int cur_track = -1; /* Current track number, starting at 1 */
+int cur_index = 0; /* Current index mark */
+int cur_lasttrack = 999; /* Last track to play in current chunk */
+int cur_firsttrack = 0; /* First track of current chunk */
+int cur_pos_abs; /* Current absolute position in seconds */
+int cur_frame; /* Current frame number */
+int cur_pos_rel; /* Current track-relative position in seconds */
+int cur_tracklen; /* Length in seconds of current track */
+int cur_cdlen; /* Length in seconds of entire CD */
+int cur_ntracks; /* Number of tracks on CD (= tracks + sections) */
+int cur_nsections; /* Number of sections currently defined */
+
+int cur_listno; /* Current index into the play list, if playing */
+char * cur_artist; /* Name of current CD's artist */
+char * cur_cdname; /* Album name */
+char * cur_trackname; /* Take a guess */
+char cur_contd; /* Continued flag */
+char cur_avoid; /* Avoid flag */
+
+int exit_on_eject = 0;
+
+int cur_stopmode = -1;
+int info_modified;
+
+/*
+ * insert_trackinfo()
+ *
+ * Add a new track to the CD info structure. Pass the position of the new
+ * entry in the track list -- 0 will make this the first track, 1 the second,
+ * etc. The new entry will be zeroed out.
+ */
+static void
+insert_trackinfo(int num)
+{
+ struct wm_trackinfo *newtrk;
+
+ /* Easy case: the list is empty */
+ if (cd->trk == NULL) {
+ if ((cd->trk = (struct wm_trackinfo *) calloc(1,
+ sizeof(*newtrk))) == NULL)
+ {
+ perror("insert_trackinfo");
+ exit(1);
+ } else {
+ return;
+ } /* if() else */
+ } /* if() */
+ /* Stick the new entry in cd->trk[]. */
+ if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
+ (cur_ntracks + 1))) == NULL)
+ {
+ perror("insert_trackinfo");
+ exit(1);
+ }
+
+ if (num)
+ memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
+ memset(&newtrk[num], 0, sizeof(*newtrk));
+ if (num < cur_ntracks)
+ memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
+ (cur_ntracks - num));
+
+ free(cd->trk);
+ cd->trk = newtrk;
+}
+
+/*
+ * split_trackinfo()
+ *
+ * Split a track in two at a particular position (absolute, in frames). All
+ * internal data structures and variables will be adjusted to the new
+ * numbering scheme. Pass in the track number (>=1) to split, which is also
+ * the index into cd->trk[] of the new entry.
+ *
+ * If pos is within 1 second of the start of another track, the split fails.
+ *
+ * Returns 1 on success, 0 if the track couldn't be inserted.
+ *
+ * Note: updating user interface elements is up to the caller.
+ */
+int
+split_trackinfo( int pos )
+{
+ int i, l, num;
+
+ if (pos < cd->trk[0].start)
+ return (0);
+
+ /* First find the appropriate track. */
+ for (num = 0; num < cur_ntracks; num++)
+ if (cd->trk[num].start - 75 < pos &&
+ cd->trk[num].start + 75 > pos)
+ return (0);
+ else if (cd->trk[num].start > pos)
+ break;
+ if (num == 0)
+ return (0);
+
+ /* Insert the new entry into the track array. */
+ insert_trackinfo(num);
+
+ /* Update the easy variables. */
+ if (cur_track > num)
+ cur_track++;
+ if (cur_firsttrack > num)
+ cur_firsttrack++;
+ if (cur_lasttrack > num)
+ cur_lasttrack++;
+
+ /* Update the user-defined playlists. */
+ if (cd->lists != NULL)
+ for (l = 0; cd->lists[l].name != NULL; l++)
+ if (cd->lists[l].list != NULL)
+ for (i = 0; cd->lists[l].list[i]; i++)
+ if (cd->lists[l].list[i] > num)
+ cd->lists[l].list[i]++;
+
+ /* Update the internal playlist. */
+ if (playlist != NULL)
+ for (i = 0; playlist[i].start; i++)
+ {
+ if (playlist[i].start > num)
+ playlist[i].start++;
+ if (playlist[i].end > num)
+ playlist[i].end++;
+ }
+
+ /* Now adjust the information in cd->trk[]. */
+ cd->trk[num].start = pos;
+ if (num == cur_ntracks)
+ cd->trk[num].length = cur_cdlen - pos / 75;
+ else
+ cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
+ cd->trk[num - 1].length -= cd->trk[num].length;
+ if (cur_track == num)
+ cur_tracklen -= cd->trk[num].length;
+ cd->trk[num].track = cd->trk[num - 1].track;
+ cd->trk[num].data = cd->trk[num - 1].data;
+ cd->trk[num].contd = 1;
+ cd->trk[num].volume = cd->trk[num - 1].volume;
+
+ if (cd->trk[num - 1].section == 0)
+ cd->trk[num - 1].section = 1;
+ cd->trk[num].section = cd->trk[num - 1].section + 1;
+
+ cur_ntracks++;
+ cur_nsections++;
+
+ for (i = num + 1; i < cur_ntracks; i++)
+ if (cd->trk[i].track == cd->trk[num].track)
+ cd->trk[i].section++;
+
+ return (1);
+}
+
+/*
+ * remove_trackinfo()
+ *
+ * Remove a track's internal data. This is similar to split_trackinfo()
+ * above, but simpler. A track's initial section can't be removed. Track
+ * numbers start at 0.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+remove_trackinfo( int num )
+{
+ int i, l;
+
+ if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
+ return (0);
+
+ cd->trk[num - 1].length += cd->trk[num].length;
+
+ for (i = num; i < cur_ntracks - 1; i++)
+ memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
+
+ if (cur_track > num)
+ cur_track--;
+ if (cur_firsttrack > num)
+ cur_firsttrack--;
+ if (cur_lasttrack > num)
+ cur_lasttrack--;
+
+ /* Update the user-defined playlists. */
+ if (cd->lists != NULL)
+ for (l = 0; cd->lists[l].name != NULL; l++)
+ if (cd->lists[l].list != NULL)
+ for (i = 0; cd->lists[l].list[i]; i++)
+ if (cd->lists[l].list[i] > num)
+ cd->lists[l].list[i]--;
+
+ /* Update the internal playlist. */
+ if (playlist != NULL)
+ for (i = 0; playlist[i].start; i++)
+ {
+ if (playlist[i].start > num)
+ playlist[i].start--;
+ if (playlist[i].end > num)
+ playlist[i].end--;
+ }
+
+ cur_ntracks--;
+ cur_nsections--;
+
+ /*
+ * Update the section numbers for this track. If this is the only
+ * user-created section in a track, get rid of the section number
+ * in the track's entry.
+ */
+ if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track)
+ {
+ if (cd->trk[num - 1].section == 1)
+ cd->trk[num - 1].section = 0;
+ }
+ else
+ for (i = num; i < cur_ntracks; i++)
+ if (cd->trk[i].track == cd->trk[num - 1].track)
+ cd->trk[i].section--;
+
+ return (1);
+}
+
+/*
+ * listentry()
+ *
+ * Return a scrolling list entry.
+ */
+char *
+listentry( int num )
+{
+ static char buf[600];
+ const char *name;
+ char tracknum[20];
+ int digits;
+ int sdigits;
+
+ if (num >= 0 && num < cur_ntracks)
+ {
+
+/*
+ if (big_spaces)
+ {
+ digits = 2;
+ sdigits = cur_nsections < 9 ? -1 : -2;
+ }
+ else
+ {
+ digits = cd->trk[num].track < 10 ? 3 : 2;
+ sdigits = cur_nsections < 9 ? -1 : -3;
+ }
+*/
+
+ digits = 2;
+ sdigits = cur_nsections < 9 ? -1 : -2;
+
+ name = cd->trk[num].songname ? cd->trk[num].songname : "";
+
+ if (cur_nsections)
+ {
+ if (cd->trk[num].section > 9)
+ {
+ sprintf(tracknum, "%*d.%d", digits,
+ cd->trk[num].track,
+ cd->trk[num].section);
+ } else {
+ if (cd->trk[num].section)
+ {
+ sprintf(tracknum, "%*d.%*d", digits,
+ cd->trk[num].track, sdigits,
+ cd->trk[num].section);
+ } else {
+ sprintf(tracknum, "%*d%*s", digits,
+ cd->trk[num].track,
+ 2 - sdigits, " ");
+/* 2 - sdigits - big_spaces, " ");*/
+ }
+ }
+ } else {
+ sprintf(tracknum, "%*d", digits, cd->trk[num].track);
+ }
+
+ if (cd->trk[num].data)
+ {
+ sprintf(buf, "%s) %3dMB %s", tracknum,
+ cd->trk[num].length / 1024, name);
+ } else {
+ sprintf(buf, "%s) %02d:%02d %s", tracknum,
+ cd->trk[num].length / 60,
+ cd->trk[num].length % 60, name);
+ }
+
+ return (buf);
+ } else {
+ return (NULL);
+ }
+} /* listentry() */
+
+/*
+ * trackname()
+ *
+ * Return a track's name.
+ */
+const char *
+trackname( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ {
+ if (cd->trk[num].songname)
+ {
+ return (cd->trk[num].songname);
+ } else {
+ return ("");
+ }
+ } else {
+ return (NULL);
+ }
+} /* trackname() */
+
+/*
+ * tracklen()
+ *
+ * Return a track's length in seconds.
+ */
+int
+tracklen( int num )
+{
+ if (cd != NULL && num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].length);
+ else
+ return (0);
+}
+
+/*
+ * get_default_volume()
+ *
+ * Return the default volume (0-32, 0=none) for the CD or a track.
+ */
+int
+get_default_volume( int track )
+{
+ if (! track)
+ return (cd->volume);
+ else if (track <= cur_ntracks)
+ return (cd->trk[track - 1].volume);
+ else
+ return (0);
+}
+
+/*
+ * get_contd()
+ *
+ * Return the contd value for a track.
+ */
+int
+get_contd( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].contd);
+ else
+ return (0);
+}
+
+/*
+ * get_avoid()
+ *
+ * Return the avoid value for a track.
+ */
+int
+get_avoid( int num )
+{
+ if (num >= 0 && num < cur_ntracks)
+ return (cd->trk[num].avoid);
+ else
+ return (0);
+}
+
+/*
+ * get_autoplay()
+ *
+ * Is autoplay set on this disc?
+ */
+int
+get_autoplay( void )
+{
+ return ( cd->autoplay );
+}
+
+/*
+ * get_playmode()
+ *
+ * Return the default playmode for the CD.
+ */
+int
+get_playmode( void )
+{
+ return ( cd->playmode );
+}
+
+/*
+ * get_runtime()
+ *
+ * Return the total running time for the current playlist in seconds.
+ */
+int
+get_runtime( void )
+{
+ int i;
+
+ if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
+ return (cd == NULL ? 0 : cd->length);
+
+ for (i = 0; playlist[i].start; i++)
+ ;
+
+ return (playlist[i].starttime);
+}
+
+/*
+ * default_volume()
+ *
+ * Set the default volume for the CD or a track.
+ */
+void
+default_volume( int track, int vol )
+{
+ if (track == 0)
+ cd->volume = vol;
+ else if (track <= cur_ntracks)
+ cd->trk[track - 1].volume = vol;
+}
+
+/*
+ * Play the next thing on the playlist, if any.
+ */
+void
+play_next_entry( int forward )
+{
+ if (cd == NULL)
+ return;
+ if (playlist != NULL && playlist[cur_listno].start)
+ {
+ wm_cd_play(playlist[cur_listno].start, 0,
+ playlist[cur_listno].end);
+ cur_listno++;
+ }
+ else
+ wm_cd_stop();
+}
+
+/*
+ * Play the next track, following playlists as necessary.
+ */
+void
+play_next_track( int forward )
+{
+ if (cd == NULL)
+ return;
+
+ /* Is the current playlist entry done? Move on, if so. */
+ if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
+ play_next_entry( forward );
+ else
+ wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
+}
+
+/*
+ * Play the previous track, hopping around the playlist as necessary.
+ */
+void
+play_prev_track( int forward )
+{
+ if (cd == NULL)
+ return;
+
+ if (playlist == NULL)
+ return;
+
+ /* If we're in the middle of this playlist entry, go back one track */
+ if (cur_track > playlist[cur_listno - 1].start)
+ wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end);
+ else
+ if (cur_listno > 1)
+ {
+ cur_listno--;
+ wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
+ playlist[cur_listno - 1].end);
+ }
+ else
+ wm_cd_play(playlist[0].start, 0, playlist[0].end);
+}
+
+/*
+ * stash_cdinfo(artist, cdname)
+ */
+void
+stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
+{
+ if (cd != NULL)
+ {
+ if (strcmp(cd->artist, artist))
+ info_modified = 1;
+ strncpy(cd->artist, artist,sizeof(cd->artist)-1);
+ cd->artist[sizeof(cd->artist)-1]='\0';
+
+ if (strcmp(cd->cdname, cdname))
+ info_modified = 1;
+ strncpy(cd->cdname, cdname,sizeof(cd->cdname)-1);
+ cd->cdname[sizeof(cd->cdname)-1]='\0';
+
+ if (!!cd->autoplay != !!autoplay)
+ info_modified = 1;
+ cd->autoplay = autoplay;
+
+ if (!!cd->playmode != !!playmode)
+ info_modified = 1;
+ cd->playmode = playmode;
+ }
+} /* stash_cdinfo() */
+
+/*
+ * wipe_cdinfo()
+ *
+ * Clear out all a CD's soft information (presumably in preparation for
+ * reloading from the database.)
+ */
+void
+wipe_cdinfo( void )
+{
+ struct wm_playlist *l;
+ int i;
+
+ if (cd != NULL)
+ {
+ cd->artist[0] = cd->cdname[0] = '\0';
+ cd->autoplay = cd->playmode = cd->volume = 0;
+ cd->whichdb = NULL;
+ freeup(&cd->otherrc);
+ freeup(&cd->otherdb);
+
+ if (thiscd.lists != NULL)
+ {
+ for (l = thiscd.lists; l->name != NULL; l++)
+ {
+ free(l->name);
+ free(l->list);
+ }
+ free(thiscd.lists);
+ thiscd.lists = NULL;
+ }
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ freeup(&cd->trk[i].songname);
+ freeup(&cd->trk[i].otherrc);
+ freeup(&cd->trk[i].otherdb);
+ cd->trk[i].avoid = cd->trk[i].contd = 0;
+ cd->trk[i].volume = 0;
+ if (cd->trk[i].section > 1)
+ remove_trackinfo(i--);
+ }
+ }
+}
+
+/*
+ * stash_trkinfo(track, songname, contd, avoid)
+ *
+ * Update information about a track on the current CD.
+ */
+void
+stash_trkinfo( int track, char *songname, int contd, int avoid )
+{
+ if (cd != NULL)
+ {
+ track--;
+ if (!!cd->trk[track].contd != !!contd)
+ info_modified = 1;
+ cd->trk[track].contd = track ? contd : 0;
+
+ if (!!cd->trk[track].avoid != !!avoid)
+ info_modified = 1;
+ cd->trk[track].avoid = avoid;
+
+ if ((cd->trk[track].songname == NULL && songname[0]) ||
+ (cd->trk[track].songname != NULL &&
+ strcmp(cd->trk[track].songname, songname)))
+ {
+ info_modified = 1;
+ wm_strmcpy(&cd->trk[track].songname, songname);
+ }
+ }
+}
+
+/*
+ * new_playlist()
+ *
+ * Add a playlist to a CD.
+ */
+struct wm_playlist *
+new_playlist(struct wm_cdinfo* cdinfo, char* listname)
+{
+ int nlists = 0;
+ struct wm_playlist *l;
+
+ if (cdinfo->lists != NULL)
+ {
+ for (nlists = 0; cdinfo->lists[nlists].name != NULL; nlists++)
+ ;
+ l = (struct wm_playlist *)realloc(cdinfo->lists, (nlists + 2) *
+ sizeof (struct wm_playlist));
+ }
+ else
+ l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
+
+ if (l == NULL)
+ return (NULL);
+
+ l[nlists + 1].name = NULL;
+ l[nlists].name = NULL; /* so wm_strmcpy doesn't free() it */
+ wm_strmcpy(&l[nlists].name, listname);
+ l[nlists].list = NULL;
+ cdinfo->lists = l;
+
+ return (&l[nlists]);
+}
+
+/*
+ * make_playlist()
+ *
+ * Construct a playlist for the current CD. If we're in shuffle mode, play
+ * each non-avoided track once, keeping continued tracks in the right order.
+ *
+ * If playmode is 2, use playlist number (playmode-2). XXX should do
+ * bounds checking on this, probably.
+ *
+ * If consecutive tracks are being played, only make one playlist entry for
+ * them, so the CD player won't pause between tracks while we wake up.
+ */
+void
+make_playlist( int playmode, int starttrack )
+{
+ int i, avoiding = 1, entry = 0, count, track,
+ *thislist;
+
+ cur_listno = 0;
+ if (playlist != NULL)
+ free(playlist);
+ playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
+ if (playlist == NULL)
+ {
+ perror("playlist");
+ exit(1);
+ }
+
+ /* If this is a data-only CD, we can't play it. */
+ if ((starttrack && cd->trk[starttrack - 1].data) ||
+ (cur_ntracks == 1 && cd->trk[0].data))
+ {
+ playlist[0].start = 0;
+ playlist[0].end = 0;
+ playlist[1].start = 0;
+ return;
+ }
+
+ if (playmode == 1)
+ {
+ char *done = malloc(cur_ntracks);
+
+ if (done == NULL)
+ {
+ perror("randomizer");
+ exit(1);
+ }
+
+ count = cur_ntracks;
+ if (starttrack && cd->trk[starttrack - 1].avoid)
+ count++;
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].contd || cd->trk[i].avoid ||
+ cd->trk[i].data)
+ {
+ done[i] = 1;
+ count--;
+ }
+ else
+ done[i] = 0;
+
+ for (i = 0; i < count; i++)
+ {
+ int end; /* for readability */
+ if (starttrack)
+ {
+ track = starttrack - 1;
+ starttrack = 0;
+ }
+ else
+ while (done[track = rand() % cur_ntracks])
+ ;
+
+ playlist[i].start = track + 1;
+
+ /* play all subsequent continuation tracks too */
+ for (end = track + 1; end < cur_ntracks + 1; end++)
+ if (! cd->trk[end].contd ||
+ cd->trk[end].avoid ||
+ cd->trk[end].data)
+ break;
+ playlist[i].end = end + 1;
+
+ done[track]++;
+ }
+ playlist[i].start = 0;
+
+ free(done);
+ }
+ else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
+ {
+ count = 2; /* one terminating entry, and one for start */
+ thislist = cd->lists[playmode - 2].list;
+
+ for (i = 0; thislist[i]; i++)
+ if (thislist[i + 1] != thislist[i] + 1)
+ count++;
+
+ if (playlist != NULL)
+ free(playlist);
+ playlist = malloc(sizeof (*playlist) * count);
+ if (playlist == NULL)
+ {
+ perror("playlist");
+ exit(1);
+ }
+
+ count = 0;
+ if (starttrack)
+ {
+ playlist[0].start = starttrack;
+ for (track = 0; thislist[track]; track++)
+ if (starttrack == thislist[track])
+ break;
+ if (! thislist[track])
+ {
+ playlist[0].end = starttrack + 1;
+ playlist[1].start = thislist[0];
+ count = 1;
+ track = 0;
+ }
+ }
+ else
+ {
+ playlist[0].start = thislist[0];
+ track = 0;
+ }
+
+ for (i = track; thislist[i]; i++)
+ if (thislist[i + 1] != thislist[i] + 1)
+ {
+ playlist[count].end = thislist[i] + 1;
+ count++;
+ playlist[count].start = thislist[i + 1];
+ }
+ }
+ else
+ {
+ for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
+ if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
+ {
+ playlist[entry].start = i + 1;
+ avoiding = 0;
+ }
+ else if (! avoiding && (cd->trk[i].avoid ||
+ cd->trk[i].data))
+ {
+ playlist[entry].end = i + 1;
+ avoiding = 1;
+ entry++;
+ }
+ if (! avoiding)
+ playlist[entry].end = i + 1;
+ playlist[entry + !avoiding].start = 0;
+ }
+
+ /*
+ * Now go through the list, whatever its source, and figure out
+ * cumulative starting times for each entry.
+ */
+ entry = count = 0;
+ do {
+ playlist[entry].starttime = count;
+
+ if (playlist[entry].start)
+ for (i = playlist[entry].start; i <
+ playlist[entry].end; i++)
+ count += cd->trk[i - 1].length;
+ } while (playlist[entry++].start);
+}
+
+/*
+ * Find a particular track's location in the current playlist. Sets the
+ * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
+ */
+void
+pl_find_track( int track )
+{
+ int i;
+
+ if (playlist == NULL)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "Null playlist! Huh?\n");
+#endif
+ return;
+ }
+
+ for (i = 0; playlist[i].start; i++)
+ if (track >= playlist[i].start && track < playlist[i].end)
+ {
+ cur_listno = i + 1;
+ cur_firsttrack = playlist[i].start;
+ cur_lasttrack = playlist[i].end - 1;
+ return;
+ }
+
+ /*
+ * Couldn't find the track in question. Make a special entry with
+ * just that track.
+ */
+ if (! playlist[i].start)
+ {
+ playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
+ if (playlist == NULL)
+ {
+ perror("playlist realloc");
+ exit(1);
+ }
+
+ playlist[i + 1].start = playlist[i + 1].end = 0;
+ playlist[i + 1].starttime = playlist[i].starttime +
+ cd->trk[track - 1].length;
+ playlist[i].start = track;
+ playlist[i].end = track + 1;
+ cur_listno = i + 1;
+ cur_firsttrack = track;
+ cur_lasttrack = track;
+ }
+}
diff --git a/kscd/libwm/cdrom.c b/kscd/libwm/cdrom.c
new file mode 100644
index 00000000..980ce3ce
--- /dev/null
+++ b/kscd/libwm/cdrom.c
@@ -0,0 +1,899 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Interface between most of WorkMan and the low-level CD-ROM library
+ * routines defined in plat_*.c and drv_*.c. The goal is to have no
+ * platform- or drive-dependent code here.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+/* #include <sys/time.h> */
+
+#include "config.h"
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cddb.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cdtext.h"
+
+#ifdef CAN_CLOSE
+#include <fcntl.h>
+#endif
+
+/* local prototypes */
+int read_toc(void);
+
+#define WM_MSG_CLASS WM_MSG_CLASS_CDROM
+
+/* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */
+/* toshiba33_proto; <=== Somehow, this got lost */
+
+/*
+ * The supported drive types are listed here. NULL means match anything.
+ * The first match in the list is used, and substring matches are done (so
+ * put long names before their shorter prefixes.)
+ */
+struct drivelist {
+ const char *ven;
+ const char *mod;
+ const char *rev;
+ struct wm_drive_proto *proto;
+} drives[] = {
+{ "TOSHIBA", "XM-3501", NULL, &toshiba_proto },
+{ "TOSHIBA", "XM-3401", NULL, &toshiba_proto },
+{ "TOSHIBA", "XM-3301", NULL, &toshiba_proto },
+{ "SONY", "CDU-8012", NULL, &sony_proto },
+{ "SONY", "CDU 561", NULL, &sony_proto },
+{ "SONY", "CDU-76S", NULL, &sony_proto },
+{ WM_STR_GENVENDOR, WM_STR_GENMODEL, WM_STR_GENREV, &generic_proto },
+{ NULL, NULL, NULL, &generic_proto }
+};
+
+/*
+ * Solaris 2.2 will remove the device out from under us. Getting an ENOENT
+ * is therefore sometimes not a problem.
+ */
+int intermittent_dev = 0;
+
+static int wm_cd_cur_balance = 10;
+static int wm_cur_cdmode = WM_CDM_UNKNOWN;
+static char *wm_cd_device = NULL; /* do not use this extern */
+
+static struct wm_drive drive = {
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ -1,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ NULL
+};
+
+extern struct wm_cdinfo thiscd;
+
+/*
+ * Macro magic
+ *
+ */
+#define FREE(x); if(x) free(x); x = NULL;
+
+#define STRDUP(old, neu); \
+ if(old) free(old); old = NULL;\
+ if(neu) old = strdup(neu);
+
+#define CARRAY(id) ((id)-1)
+
+#define DATATRACK 1
+/*
+ * init the workmanlib
+ */
+int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
+ const char *sounddevice, const char *ctldevice )
+{
+ drive.cdda = (WM_CDDA == cdin);
+#if !defined(BUILD_CDDA)
+ if(drive.cdda) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Libworkman library was compiled without cdda support\n");
+ return -1;
+ }
+#endif
+ wm_cd_destroy();
+
+ STRDUP(wm_cd_device, cd_device);
+ drive.cd_device = wm_cd_device;
+ STRDUP(drive.soundsystem, soundsystem);
+ STRDUP(drive.sounddevice, sounddevice);
+ STRDUP(drive.ctldevice, ctldevice);
+
+ return wm_cd_status();
+}
+
+int wm_cd_destroy( void )
+{
+ free_cdtext();
+
+ if(drive.fd != -1) {
+ /* first free one old */
+ if(drive.proto && drive.proto->gen_close)
+ drive.proto->gen_close(&drive);
+ else
+ close(drive.fd);
+ }
+ drive.fd = -1;
+ FREE(wm_cd_device);
+ FREE(drive.soundsystem);
+ FREE(drive.sounddevice);
+ FREE(drive.ctldevice);
+ FREE(drive.vendor);
+ FREE(drive.model);
+ FREE(drive.revision);
+ drive.proto = NULL;
+
+ return 0;
+}
+/*
+ * Give information about the drive we found during wmcd_open()
+ */
+const char *wm_drive_vendor( void )
+{
+ return drive.vendor?drive.vendor:"";
+}
+
+const char *wm_drive_model( void )
+{
+ return drive.model?drive.model:"";
+}
+
+const char *wm_drive_revision( void )
+{
+ return drive.revision?drive.revision:"";
+}
+
+const char *wm_drive_device( void )
+{
+ return drive.cd_device ? drive.cd_device : "";
+}
+
+/*
+ * Figure out which prototype drive structure we should be using based
+ * on the vendor, model, and revision of the current drive.
+ */
+int
+find_drive_struct(const char *vendor, const char *model, const char *rev)
+{
+ struct drivelist *d;
+
+ for (d = drives; d; d++) {
+ if(((d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven))) ||
+ ((d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod))) ||
+ ((d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev))))
+ continue;
+
+ if(!(d->proto))
+ goto fail;
+
+ STRDUP(drive.vendor, vendor);
+ STRDUP(drive.model, model);
+ STRDUP(drive.revision, rev);
+
+ drive.proto = d->proto;
+ return 0;
+ }
+
+fail:
+ return -1;
+} /* find_drive_struct() */
+
+/*
+ * read_toc()
+ *
+ * Read the table of contents from the CD. Return a pointer to a wm_cdinfo
+ * struct containing the relevant information (minus artist/cdname/etc.)
+ * This is a static struct. Returns NULL if there was an error.
+ *
+ * XXX allocates one trackinfo too many.
+ */
+int
+read_toc( void )
+{
+ struct wm_playlist *l;
+ int i;
+ int pos;
+
+ if(!drive.proto)
+ return -1;
+
+ if(drive.proto && drive.proto->gen_get_trackcount &&
+ (drive.proto->gen_get_trackcount)(&drive, &thiscd.ntracks) < 0) {
+ return -1 ;
+ }
+
+ thiscd.artist[0] = thiscd.cdname[0] = '\0';
+ thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
+ thiscd.length = 0;
+ thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
+
+ /* Free up any left-over playlists. */
+ if (thiscd.lists != NULL) {
+ for (l = thiscd.lists; l->name != NULL; l++) {
+ free(l->name);
+ free(l->list);
+ }
+ free(thiscd.lists);
+ thiscd.lists = NULL;
+ }
+
+ if (thiscd.trk != NULL)
+ free(thiscd.trk);
+
+ thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo));
+ if (thiscd.trk == NULL) {
+ perror("malloc");
+ return -1;
+ }
+
+ for (i = 0; i < thiscd.ntracks; i++) {
+ if(drive.proto && drive.proto->gen_get_trackinfo &&
+ (drive.proto->gen_get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
+ &thiscd.trk[i].start) < 0) {
+ return -1;
+ }
+
+ thiscd.trk[i].avoid = thiscd.trk[i].data;
+ thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+ thiscd.trk[i].songname = thiscd.trk[i].otherrc =
+ thiscd.trk[i].otherdb = NULL;
+ thiscd.trk[i].contd = 0;
+ thiscd.trk[i].volume = 0;
+ thiscd.trk[i].track = i + 1;
+ thiscd.trk[i].section = 0;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "track %i, start frame %i\n",
+ thiscd.trk[i].track, thiscd.trk[i].start);
+ }
+
+ if(drive.proto && drive.proto->gen_get_cdlen &&
+ (drive.proto->gen_get_cdlen)(&drive, &thiscd.trk[i].start) < 0) {
+ return -1;
+ }
+ thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+/* Now compute actual track lengths. */
+ pos = thiscd.trk[0].length;
+ for (i = 0; i < thiscd.ntracks; i++) {
+ thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
+ pos = thiscd.trk[i+1].length;
+ if (thiscd.trk[i].data)
+ thiscd.trk[i].length = (thiscd.trk[i + 1].start - thiscd.trk[i].start) * 2;
+ if (thiscd.trk[i].avoid)
+ wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
+ }
+
+ thiscd.length = thiscd.trk[thiscd.ntracks].length;
+ thiscd.cddbid = cddb_discid();
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "read_toc() successful\n");
+ return 0;
+} /* read_toc() */
+
+/*
+ * wm_cd_status()
+ *
+ * Return values:
+ * see wm_cdrom.h
+ *
+ * Updates variables.
+ */
+int
+wm_cd_status( void )
+{
+ static int oldmode = WM_CDM_UNKNOWN;
+ int mode, err, tmp;
+
+ if(!drive.proto) {
+ oldmode = WM_CDM_UNKNOWN;
+ err = wmcd_open( &drive );
+ if (err < 0) {
+ wm_cur_cdmode = WM_CDM_UNKNOWN;
+ return err;
+ }
+ }
+
+ if(drive.proto && drive.proto->gen_get_drive_status &&
+ (drive.proto->gen_get_drive_status)(&drive, oldmode, &mode, &cur_frame,
+ &(thiscd.curtrack), &cur_index) < 0) {
+ perror("WM gen_get_drive_status");
+ return -1;
+ } else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
+ "gen_get_drive_status returns status %s, track %i, frame %i\n",
+ gen_status(mode), thiscd.curtrack, cur_frame);
+ }
+
+ if(WM_CDS_NO_DISC(oldmode) && WM_CDS_DISC_READY(mode)) {
+ /* device changed */
+ thiscd.ntracks = 0;
+ if(read_toc() || 0 == thiscd.ntracks)
+ {
+ close(drive.fd);
+ drive.fd = -1;
+ mode = WM_CDM_NO_DISC;
+ }
+ else /* refresh cdtext info */
+ get_glob_cdtext(&drive, 1);
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "device status changed() from %s to %s\n",
+ gen_status(oldmode), gen_status(mode));
+ }
+ oldmode = mode;
+
+ /*
+ * it seems all driver have'nt state for stop
+ */
+ if(WM_CDM_PAUSED == mode && 0 == cur_frame) {
+ mode = WM_CDM_STOPPED;
+ thiscd.curtrack = 0;
+ }
+
+ switch (mode) {
+ case WM_CDM_PLAYING:
+ case WM_CDM_PAUSED:
+ cur_pos_abs = cur_frame / 75;
+ /* search for right track */
+ for(tmp = thiscd.ntracks;
+ tmp > 1 && cur_frame < thiscd.trk[CARRAY(tmp)].start;
+ tmp--)
+ ;
+ thiscd.curtrack = tmp;
+ /* Fall through */
+
+
+ case WM_CDM_UNKNOWN:
+ if (mode == WM_CDM_UNKNOWN)
+ {
+ mode = WM_CDM_NO_DISC;
+ cur_lasttrack = cur_firsttrack = -1;
+ }
+ /* Fall through */
+
+ case WM_CDM_STOPPED:
+ if(thiscd.curtrack >= 1 && thiscd.curtrack <= thiscd.ntracks && thiscd.trk != NULL) {
+ cur_trackname = thiscd.trk[CARRAY(thiscd.curtrack)].songname;
+ cur_avoid = thiscd.trk[CARRAY(thiscd.curtrack)].avoid;
+ cur_contd = thiscd.trk[CARRAY(thiscd.curtrack)].contd;
+ cur_pos_rel = (cur_frame - thiscd.trk[CARRAY(thiscd.curtrack)].start) / 75;
+ if (cur_pos_rel < 0)
+ cur_pos_rel = -cur_pos_rel;
+ }
+ if((playlist != NULL) && playlist[0].start & (cur_listno > 0)) {
+ cur_pos_abs -= thiscd.trk[playlist[CARRAY(cur_listno)].start - 1].start / 75;
+ cur_pos_abs += playlist[CARRAY(cur_listno)].starttime;
+ }
+ if (cur_pos_abs < 0)
+ cur_pos_abs = cur_frame = 0;
+
+ if (thiscd.curtrack < 1)
+ thiscd.curtracklen = thiscd.length;
+ else
+ thiscd.curtracklen = thiscd.trk[CARRAY(thiscd.curtrack)].length;
+ /* Fall through */
+
+ case WM_CDM_TRACK_DONE:
+ wm_cur_cdmode = mode;
+ break;
+ case WM_CDM_FORWARD:
+ case WM_CDM_EJECTED:
+ wm_cur_cdmode = mode;
+ break;
+ }
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
+ "wm_cd_status returns %s\n", gen_status(wm_cur_cdmode));
+ return wm_cur_cdmode;
+}
+
+int
+wm_cd_getcurtrack( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+ return thiscd.curtrack;
+}
+
+int
+wm_cd_getcurtracklen( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+
+ return thiscd.curtracklen;
+}
+
+int
+wm_cd_getcountoftracks( void )
+{
+ if(WM_CDS_NO_DISC(wm_cur_cdmode))
+ return 0;
+
+ return thiscd.ntracks;
+}
+
+int wm_cd_gettracklen( int track )
+{
+ if (track < 1 ||
+ track > thiscd.ntracks ||
+ thiscd.trk == NULL)
+ return 0;
+
+ return thiscd.trk[CARRAY(track)].length;
+}
+
+/*
+ * wm_cd_play(starttrack, pos, endtrack)
+ *
+ * Start playing the CD or jump to a new position. "pos" is in seconds,
+ * relative to start of track.
+ */
+int
+wm_cd_play( int start, int pos, int end )
+{
+ int real_start, real_end, status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status) || thiscd.ntracks < 1)
+ return -1;
+
+ /*
+ * check ranges
+ */
+ for(real_end = thiscd.ntracks; (thiscd.trk[CARRAY(real_end)].data == DATATRACK); real_end--)
+ ;
+ for(real_start = 1; (thiscd.trk[CARRAY(real_start)].data == DATATRACK); real_start++)
+ ;
+
+ if(end == WM_ENDTRACK) end = real_end;
+ if(end > real_end) end = real_end;
+
+ /*
+ * handle as overrun
+ */
+ if(start < real_start) start = real_start;
+ if(start > real_end) start = real_end;
+
+ /*
+ * Try to avoid mixed mode and CD-EXTRA data tracks
+ */
+ if(start > end || thiscd.trk[CARRAY(start)].data == DATATRACK) {
+ wm_cd_stop();
+ return -1;
+ }
+
+ cur_firsttrack = start;
+ cur_lasttrack = end;
+
+ wm_cd_play_chunk(thiscd.trk[CARRAY(start)].start + pos * 75, end == thiscd.ntracks ?
+ thiscd.length * 75 : thiscd.trk[CARRAY(end)].start - 1, thiscd.trk[CARRAY(start)].start);
+ /* So we don't update the display with the old frame number */
+ wm_cd_status();
+
+ return thiscd.curtrack;
+}
+
+/*
+ * wm_cd_play_chunk(start, end)
+ *
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+wm_cd_play_chunk( int start, int end, int realstart )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ end--;
+ if (start >= end)
+ start = end-1;
+
+ if(!(drive.proto) || !(drive.proto->gen_play)) {
+ perror("WM gen_play: function pointer NULL");
+ return -1;
+ }
+
+ return (drive.proto->gen_play)(&drive, start, end, realstart);
+}
+
+/*
+ * Set the offset into the current track and play. -1 means end of track
+ * (i.e., go to next track.)
+ */
+int
+wm_cd_play_from_pos( int pos )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if (pos == -1)
+ pos = thiscd.trk[thiscd.curtrack - 1].length - 1;
+
+ if (wm_cur_cdmode == WM_CDM_PLAYING)
+ return wm_cd_play(thiscd.curtrack, pos, playlist[cur_listno-1].end);
+ else
+ return -1;
+} /* wm_cd_play_from_pos() */
+
+/*
+ * wm_cd_pause()
+ *
+ * Pause the CD, if it's in play mode. If it's already paused, go back to
+ * play mode.
+ */
+int
+wm_cd_pause( void )
+{
+ static int paused_pos;
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if(WM_CDM_PLAYING == wm_cur_cdmode) {
+ if(drive.proto && drive.proto->gen_pause)
+ (drive.proto->gen_pause)(&drive);
+
+ paused_pos = cur_pos_rel;
+ } else if(WM_CDM_PAUSED == status) {
+ if(!(drive.proto->gen_resume) || (drive.proto->gen_resume(&drive) > 0)) {
+ wm_cd_play(thiscd.curtrack, paused_pos, playlist[cur_listno-1].end);
+ }
+ } else {
+ return -1;
+ }
+ wm_cd_status();
+ return 0;
+} /* wm_cd_pause() */
+
+/*
+ * wm_cd_stop()
+ *
+ * Stop the CD if it's not already stopped.
+ */
+int
+wm_cd_stop( void )
+{
+ int status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return -1;
+
+ if (status != WM_CDM_STOPPED) {
+
+ if(drive.proto && drive.proto->gen_stop)
+ (drive.proto->gen_stop)(&drive);
+
+ status = wm_cd_status();
+ }
+
+ return (status != WM_CDM_STOPPED);
+} /* wm_cd_stop() */
+
+/*
+ * Eject the current CD, if there is one, and set the mode to 5.
+ *
+ * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
+ * CD contains a mounted filesystem.
+ */
+int
+wm_cd_eject( void )
+{
+ int err = -1;
+
+ wm_cd_stop();
+
+ if(drive.proto && drive.proto->gen_eject)
+ err = (drive.proto->gen_eject)(&drive);
+
+ if (err < 0) {
+ if (err == -3) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+
+ wm_cd_status();
+
+ return 0;
+}
+
+int wm_cd_closetray(void)
+{
+ int status, err = -1;
+
+ status = wm_cd_status();
+ if (status == WM_CDM_UNKNOWN || status == WM_CDM_NO_DISC)
+ return -1;
+
+ if(drive.proto->gen_closetray)
+ err = (drive.proto->gen_closetray)(&drive);
+
+ return(err ? 0 : wm_cd_status()==2 ? 1 : 0);
+} /* wm_cd_closetray() */
+
+struct cdtext_info*
+wm_cd_get_cdtext( void )
+{
+ int status;
+
+ status = wm_cd_status();
+
+ if(WM_CDS_NO_DISC(status))
+ return NULL;
+
+ return get_glob_cdtext(&drive, 0);
+}
+
+/*
+ * find_trkind(track, index, start)
+ *
+ * Start playing at a particular track and index, optionally using a particular
+ * frame as a starting position. Returns a frame number near the start of the
+ * index mark if successful, 0 if the track/index didn't exist.
+ *
+ * This is made significantly more tedious (though probably easier to port)
+ * by the fact that CDROMPLAYTRKIND doesn't work as advertised. The routine
+ * does a binary search of the track, terminating when the interval gets to
+ * around 10 frames or when the next track is encountered, at which point
+ * it's a fair bet the index in question doesn't exist.
+ */
+int
+wm_find_trkind( int track, int ind, int start )
+{
+ int top = 0, bottom, current, interval, ret = 0, i, status;
+
+ status = wm_cd_status();
+ if(WM_CDS_NO_DISC(status))
+ return 0;
+
+ for (i = 0; i < thiscd.ntracks; i++) {
+ if (thiscd.trk[i].track == track)
+ break;
+ }
+
+ bottom = thiscd.trk[i].start;
+
+ for (; i < thiscd.ntracks; i++) {
+ if (thiscd.trk[i].track > track)
+ break;
+ }
+
+ top = i == thiscd.ntracks ? (thiscd.length - 1) * 75 : thiscd.trk[i].start;
+ if (start > bottom && start < top)
+ bottom = start;
+
+ current = (top + bottom) / 2;
+ interval = (top - bottom) / 4;
+
+ do {
+ wm_cd_play_chunk(current, current + 75, current);
+
+ if (wm_cd_status() != 1)
+ return 0;
+ while (cur_frame < current) {
+ if(wm_cd_status() != 1 || wm_cur_cdmode != WM_CDM_PLAYING)
+ return 0;
+ else
+ wm_susleep(1);
+ }
+
+ if (thiscd.trk[thiscd.curtrack - 1].track > track)
+ break;
+
+ if(cur_index >= ind) {
+ ret = current;
+ current -= interval;
+ } else {
+ current += interval;
+ }
+
+ interval /= 2;
+
+ } while (interval > 2);
+
+ return ret;
+} /* find_trkind() */
+
+int
+wm_cd_set_verbosity( int level )
+{
+ wm_lib_set_verbosity(level);
+ return wm_lib_get_verbosity();
+}
+
+/*
+ * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
+ * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
+ */
+
+int
+wm_cd_volume( int vol, int bal )
+{
+ int left, right;
+ const int bal1 = (vol - WM_VOLUME_MUTE)/(WM_BALANCE_ALL_RIGHTS - WM_BALANCE_SYMMETRED);
+
+/*
+ * Set "left" and "right" to volume-slider values accounting for the
+ * balance setting.
+ *
+ */
+ if(vol < WM_VOLUME_MUTE) vol = WM_VOLUME_MUTE;
+ if(vol > WM_VOLUME_MAXIMAL) vol = WM_VOLUME_MAXIMAL;
+ if(bal < WM_BALANCE_ALL_LEFTS) bal = WM_BALANCE_ALL_LEFTS;
+ if(bal > WM_BALANCE_ALL_RIGHTS) bal = WM_BALANCE_ALL_RIGHTS;
+
+ left = vol - (bal * bal1);
+ right = vol + (bal * bal1);
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calculate volume left %i, right %i\n", left, right);
+
+ if (left > WM_VOLUME_MAXIMAL)
+ left = WM_VOLUME_MAXIMAL;
+ if (right > WM_VOLUME_MAXIMAL)
+ right = WM_VOLUME_MAXIMAL;
+
+ if(!(drive.proto) || !(drive.proto->gen_set_volume))
+ return -1;
+ else
+ return (drive.proto->gen_set_volume)(&drive, left, right);
+} /* cd_volume() */
+
+int
+wm_cd_getvolume( void )
+{
+ int left, right;
+
+ if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
+ (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
+ return -1;
+
+ if (left < right) {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
+ return right;
+ } else if (left == right) {
+ wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
+ return left;
+ } else {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
+ return left;
+ }
+}
+
+int
+wm_cd_getbalance( void )
+{
+ int left, right;
+
+ if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
+ (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
+ return WM_BALANCE_SYMMETRED;
+
+ if (left < right) {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
+ } else if (left == right) {
+ wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
+ } else {
+ wm_cd_cur_balance = (right - left) / 2;
+ if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
+ wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
+ }
+ return wm_cd_cur_balance;
+}
+
+/*
+ * Prototype wm_drive structure, with generic functions. The generic functions
+ * will be replaced with drive-specific functions as appropriate once the drive
+ * type has been sensed.
+ */
+struct wm_drive_proto generic_proto = {
+ gen_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ gen_get_volume,
+ gen_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ gen_eject,
+ gen_closetray,
+ gen_get_cdtext
+};
+
+const char*
+gen_status(int status)
+{
+ static char tmp[250];
+ switch(status) {
+ case WM_CDM_TRACK_DONE:
+ return "WM_CDM_TRACK_DONE";
+ case WM_CDM_PLAYING:
+ return "WM_CDM_PLAYING";
+ case WM_CDM_FORWARD:
+ return "WM_CDM_FORWARD";
+ case WM_CDM_PAUSED:
+ return "WM_CDM_PAUSED";
+ case WM_CDM_STOPPED:
+ return "WM_CDM_STOPPED";
+ case WM_CDM_EJECTED:
+ return "WM_CDM_EJECTED";
+ case WM_CDM_DEVICECHANGED:
+ return "WM_CDM_DEVICECHANGED";
+ case WM_CDM_NO_DISC:
+ return "WM_CDM_NO_DISC";
+ case WM_CDM_UNKNOWN:
+ return "WM_CDM_UNKNOWN";
+ case WM_CDM_CDDAERROR:
+ return "WM_CDM_CDDAERROR";
+ case WM_CDM_CDDAACK:
+ return "WM_CDM_CDDAACK";
+ default:
+ {
+ sprintf(tmp, "unexpected status %i", status);
+ return tmp;
+ }
+ }
+}
diff --git a/kscd/libwm/cdtext.c b/kscd/libwm/cdtext.c
new file mode 100644
index 00000000..bb4e19b8
--- /dev/null
+++ b/kscd/libwm/cdtext.c
@@ -0,0 +1,428 @@
+/***************************************************************************
+ cdtext.c - description
+ -------------------
+ begin : Mon Feb 12 2001
+ copyright : (C) 2001,2003 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef libunicode
+ #include <unicode.h>
+#endif
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_MISC
+
+/* local prototypes */
+int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock);
+int free_cdtext_info(struct cdtext_info* cdtextinfo);
+struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks);
+void get_data_from_cdtext_pack(
+ const struct cdtext_pack_data_header *pack,
+ const struct cdtext_pack_data_header *pack_previous,
+ cdtext_string *p_componente);
+
+struct cdtext_info wm_cdtext_info = { 0, 0, 0, 0 };
+
+int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock)
+{
+ if(cdtextinfoblock)
+ {
+ if(cdtextinfoblock->name)
+ free(cdtextinfoblock->name);
+ if(cdtextinfoblock->performer)
+ free(cdtextinfoblock->performer);
+ if(cdtextinfoblock->songwriter)
+ free(cdtextinfoblock->songwriter);
+ if(cdtextinfoblock->composer)
+ free(cdtextinfoblock->composer);
+ if(cdtextinfoblock->arranger)
+ free(cdtextinfoblock->arranger);
+ if(cdtextinfoblock->message)
+ free(cdtextinfoblock->message);
+ if(cdtextinfoblock->UPC_EAN_ISRC_code)
+ free(cdtextinfoblock->UPC_EAN_ISRC_code);
+
+ if(cdtextinfoblock->block_encoding_text)
+ free(cdtextinfoblock->block_encoding_text);
+ }
+
+ return 0;
+}
+
+int free_cdtext_info(struct cdtext_info* cdtextinfo)
+{
+ int i;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: free_cdtext_info() called\n");
+
+ if(cdtextinfo)
+ {
+ for(i = 0; i < 8; i++)
+ {
+ if(cdtextinfo->blocks[i])
+ {
+ free_cdtext_info_block(cdtextinfo->blocks[i]);
+ }
+ }
+ memset(cdtextinfo, 0, sizeof(struct cdtext_info));
+ }
+
+ return 0;
+}
+
+struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks)
+{
+ int memamount;
+ struct cdtext_info_block* lp_block;
+
+ lp_block = malloc(sizeof(struct cdtext_info_block));
+ if(!lp_block)
+ {
+ return (struct cdtext_info_block*)0;
+ }
+ memset(lp_block, 0, sizeof(struct cdtext_info_block));
+
+ memamount = count_of_tracks * sizeof(cdtext_string);
+
+ lp_block->name = malloc(memamount);
+ if(!lp_block->name)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->name, 0, memamount);
+
+ lp_block->performer = malloc(memamount);
+ if(!lp_block->performer)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->performer, 0, memamount);
+
+ lp_block->songwriter = malloc(memamount);
+ if(!lp_block->songwriter)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->songwriter, 0, memamount);
+
+ lp_block->composer = malloc(memamount);
+ if(!lp_block->composer)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->composer, 0, memamount);
+
+ lp_block->arranger = malloc(memamount);
+ if(!lp_block->arranger)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->arranger, 0, memamount);
+
+ lp_block->message = malloc(memamount);
+ if(!lp_block->message)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->message, 0, memamount);
+
+ lp_block->UPC_EAN_ISRC_code = malloc(memamount);
+ if(!lp_block->UPC_EAN_ISRC_code)
+ return (struct cdtext_info_block*)free_cdtext_info_block(lp_block);
+ memset(lp_block->UPC_EAN_ISRC_code, 0, memamount);
+
+ return lp_block;
+}
+
+void get_data_from_cdtext_pack(
+ const struct cdtext_pack_data_header *pack,
+ const struct cdtext_pack_data_header *pack_previous,
+ cdtext_string *p_componente)
+{
+
+ int arr = pack->header_field_id2_tracknumber;
+ int i;
+ int language_block;
+ int unicode;
+
+ language_block = (pack->header_field_id4_block_no >> 4) & 0x07;
+ unicode = pack->header_field_id4_block_no & 0x80;
+
+ if(!unicode)
+ {
+ for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i++)
+ {
+ if(pack->text_data_field[i] == 0x00) /* end marker */
+ {
+ arr++;
+ }
+ else if(pack->text_data_field[i] == 0x09) /* repeat last marker */
+ {
+ /* ASSERT(arr > 0) */
+ strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
+ arr++;
+ }
+ else
+ {
+ strncat((char*)(p_componente[arr]), (char*)(&(pack->text_data_field[i])), 1);
+ }
+ }
+ }
+#ifdef libunicode
+ else /* doublebytes ;-) */
+ {
+ for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i += 2)
+ {
+ if((Uchar)(pack->text_data_field[i]) == 0x0000) /* end marker */
+ {
+ arr++;
+ }
+ else if((Uchar)(pack->text_data_field[i]) == 0x0909) /* repeat last marker */
+ {
+ /* ASSERT(arr > 0) */
+ strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1]));
+ arr++;
+ }
+ else
+ {
+ strncat((char*)(p_componente[arr]), u_uc_to_utf8((Uchar*)(&(pack->text_data_field[i]))), 1);
+ }
+ }
+ }
+#else
+ else {
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, "can't handle unicode");
+ }
+#endif
+}
+
+struct cdtext_info*
+get_glob_cdtext(struct wm_drive *d, int redo)
+{
+ /* alloc cdtext_info */
+
+ unsigned char *buffer;
+ int buffer_length;
+ int ret;
+ int i;
+ struct cdtext_pack_data_header *pack, *pack_previous;
+ cdtext_string *p_componente;
+ struct cdtext_info_block *lp_block;
+ if(!d->proto || d->proto->gen_get_cdtext == NULL || d->proto->gen_get_trackcount == NULL) {
+ return NULL;
+ }
+
+ if(!redo && wm_cdtext_info.valid) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, "CDTEXT DEBUG: recycle cdtext\n");
+ return &wm_cdtext_info;
+ } else
+ free_cdtext_info(&wm_cdtext_info);
+
+ lp_block = 0;
+ p_componente = 0;
+ buffer = 0;
+ buffer_length = 0;
+
+ ret = (d->proto->gen_get_cdtext)(d, &buffer, &buffer_length);
+ if(!ret)
+ {
+ (d->proto->gen_get_trackcount)(d, &(wm_cdtext_info.count_of_entries));
+ if( wm_cdtext_info.count_of_entries < 0 )
+ wm_cdtext_info.count_of_entries = 1;
+ else
+ wm_cdtext_info.count_of_entries++;
+
+ i = 0;
+
+ pack = 0; /* NULL pointer*/
+ while(i < buffer_length)
+ {
+ pack_previous = pack;
+ pack = (struct cdtext_pack_data_header*)(buffer+i);
+ /* to implement: check_crc(pack); */
+
+ /* only for valid packs */
+ if(pack->header_field_id1_typ_of_pack >= 0x80 && pack->header_field_id1_typ_of_pack < 0x90)
+ {
+ int code, j;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT DEBUG: valid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i,
+ pack->header_field_id1_typ_of_pack,
+ pack->header_field_id2_tracknumber,
+ pack->header_field_id3_sequence,
+ pack->header_field_id4_block_no,
+ pack->text_data_field[0],
+ pack->text_data_field[1],
+ pack->text_data_field[2],
+ pack->text_data_field[3],
+ pack->text_data_field[4],
+ pack->text_data_field[5],
+ pack->text_data_field[6],
+ pack->text_data_field[7],
+ pack->text_data_field[8],
+ pack->text_data_field[9],
+ pack->text_data_field[10],
+ pack->text_data_field[11],
+ pack->crc_byte1,
+ pack->crc_byte2);
+ wm_cdtext_info.count_of_valid_packs++;
+
+ code = (pack->header_field_id4_block_no >> 4) & 0x07;
+ if(0 == lp_block || lp_block->block_code != code) /* find or create one new block */
+ {
+ lp_block = 0;
+ for(j = 0; j < MAX_LANGUAGE_BLOCKS && wm_cdtext_info.blocks[j] != 0 && 0 == lp_block; j++)
+ {
+ if(wm_cdtext_info.blocks[j]->block_code == code)
+ {
+ lp_block = wm_cdtext_info.blocks[j];
+ }
+ }
+
+ if(MAX_LANGUAGE_BLOCKS <= j)
+ {
+ free_cdtext_info(&wm_cdtext_info);
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
+ "CDTEXT ERROR: more as 8 languageblocks defined\n");
+ return NULL;
+ }
+
+ if(0 == lp_block)
+ {
+ /* make next new block */
+ lp_block = malloc_cdtext_info_block(wm_cdtext_info.count_of_entries);
+ if(0 == lp_block)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS,
+ "CDTEXT ERROR: out of memory, can't create a new language block\n");
+ free_cdtext_info(&wm_cdtext_info);
+ return NULL /*ENOMEM*/;
+ }
+ else
+ {
+ wm_cdtext_info.blocks[j] = lp_block;
+ wm_cdtext_info.blocks[j]->block_code = code;
+ wm_cdtext_info.blocks[j]->block_unicode = pack->header_field_id4_block_no & 0x80;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: created a new language block; code %i, %s characters\n", code, lp_block->block_unicode?"doublebyte":"singlebyte");
+/*
+ unsigned char block_encoding; not jet!
+ cdtext_string* block_encoding_text;
+*/
+ }
+ }
+ }
+ }
+
+ switch(pack->header_field_id1_typ_of_pack)
+ {
+ case 0x80:
+ p_componente = (lp_block->name);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x81:
+ p_componente = (lp_block->performer);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x82:
+ p_componente = (lp_block->songwriter);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x83:
+ p_componente = (lp_block->composer);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x84:
+ p_componente = (lp_block->arranger);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x85:
+ p_componente = (lp_block->message);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x86:
+ memcpy((char*)(lp_block->binary_disc_identification_info),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ case 0x87:
+ memcpy((char*)(lp_block->binary_genreidentification_info),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ case 0x88:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x88 (TOC)\n");
+ break;
+ case 0x89:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x89 (second TOC)\n");
+ break;
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x%02X (reserved)\n", pack->header_field_id1_typ_of_pack);
+ break;
+ case 0x8D:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT INFO: PACK with code 0x8D (for content provider only)\n");
+ break;
+ case 0x8E:
+ p_componente = (lp_block->UPC_EAN_ISRC_code);
+ get_data_from_cdtext_pack(pack, pack_previous, p_componente);
+ break;
+ case 0x8F:
+ memcpy((char*)(lp_block->binary_size_information),
+ (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK);
+ break;
+ default:
+ wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS,
+ "CDTEXT ERROR: invalid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ i,
+ pack->header_field_id1_typ_of_pack,
+ pack->header_field_id2_tracknumber,
+ pack->header_field_id3_sequence,
+ pack->header_field_id4_block_no,
+ pack->text_data_field[0],
+ pack->text_data_field[1],
+ pack->text_data_field[2],
+ pack->text_data_field[3],
+ pack->text_data_field[4],
+ pack->text_data_field[5],
+ pack->text_data_field[6],
+ pack->text_data_field[7],
+ pack->text_data_field[8],
+ pack->text_data_field[9],
+ pack->text_data_field[10],
+ pack->text_data_field[11],
+ pack->crc_byte1,
+ pack->crc_byte2);
+ wm_cdtext_info.count_of_invalid_packs++;
+ }
+ i += sizeof(struct cdtext_pack_data_header);
+ } /* while */
+ }
+
+ if(0 == ret && wm_cdtext_info.count_of_valid_packs > 0)
+ wm_cdtext_info.valid = 1;
+
+ return &wm_cdtext_info;
+}
+
+void free_cdtext(void)
+{
+ if (wm_cdtext_info.valid)
+ free_cdtext_info(&wm_cdtext_info);
+}
diff --git a/kscd/libwm/configure.in.in b/kscd/libwm/configure.in.in
new file mode 100644
index 00000000..a0ce3ab7
--- /dev/null
+++ b/kscd/libwm/configure.in.in
@@ -0,0 +1,72 @@
+dnl +-------------------------------+
+dnl | Checks for LIBWORKMAN |
+dnl +-------------------------------+
+AC_MSG_CHECKING(for CDDA)
+
+AC_ARG_WITH(kscd-cdda, [ --with-kscd-cdda build CDDA support in kscd [default=yes]],
+[
+ if test $withval = yes; then
+ libwm_with_cdda=yes
+ else
+ libwm_with_cdda=no
+ fi
+],libwm_with_cdda=yes)
+
+if test "$libwm_with_cdda" = "yes"; then
+case $host in
+ *-*-linux*)
+ AC_CHECK_HEADERS(pthread.h)
+ AC_TRY_COMPILE(
+ [
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <linux/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+ ],[
+#if defined(__linux__)
+ioctl(1, CDROMREADAUDIO, 0);
+#else
+ #error platform?
+#endif
+ ],, libwm_with_cdda=no)
+ ;;
+ *-*-sunos*)
+ AC_CHECK_HEADERS(pthread.h)
+ AC_TRY_COMPILE(
+ [
+#include <sys/types.h>
+#include <sys/cdio.h>
+ ],[
+#if defined(__sun) || defined(sun)
+ioctl(1, CDROMCDDA, 0);
+#else
+ #error platform?
+#endif
+ ],, libwm_with_cdda=no)
+ ;;
+ *)
+ libwm_with_cdda=no
+ ;;
+esac
+fi
+
+if test "$libwm_with_cdda" = "yes"; then
+ AC_DEFINE(BUILD_CDDA, 1, [Define if you will CDDA support in kscd])
+fi
+AM_CONDITIONAL(libwm_with_cdda, test "$libwm_with_cdda" = "yes")
+
+AC_MSG_RESULT($libwm_with_cdda)
+
+
+dnl +-------------------------------+
+dnl | End LIBWORKMAN checks |
+dnl +-------------------------------+
diff --git a/kscd/libwm/database.c b/kscd/libwm/database.c
new file mode 100644
index 00000000..f7f4e7cc
--- /dev/null
+++ b/kscd/libwm/database.c
@@ -0,0 +1,1543 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Manage the CD database. All these routines assume that the "cd" global
+ * structure contains current information (as far as the outside world knows;
+ * obviously it won't contain track titles just after a CD is inserted.)
+ */
+
+#define RCFILE "/.workmanrc"
+#define DBFILE "/.workmandb"
+#define FUZZFRAMES 75
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <time.h>
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cddb.h"
+#include "include/wm_index.h"
+#include "include/wm_database.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_DB
+
+#define SWALLOW_LINE(fp) { int _c; while ((_c = getc(fp)) != '\n' && _c != EOF); }
+
+/* local prototypes */
+char *print_cdinfo(struct wm_cdinfo *pcd, int prefs);
+FILE *open_rcfile(const char *name, const char *mode);
+int *reset_tracks(void);
+int search_db( FILE *fp, int prefs, int scan, int holesize_wanted );
+void spinwheels(int secs);
+int lockit(int fd, int type);
+void save_globals(FILE *fp);
+int save_entry(char *filename, int pref);
+/* local prototypes END */
+
+static int suppress_locking = 0; /* Turn off locking of datafile (dangerous) */
+
+static char *rcfile = NULL; /* Personal rcfile */
+static char *dbfiles = NULL; /* Colon-separated list of databases */
+static char **databases = NULL; /* NULL-terminated list of databases */
+
+static char *otherrc = NULL; /* Unrecognized cruft from start of rcfile */
+
+static long rcpos, rclen; /* XXX */
+
+static int found_in_db, found_in_rc;
+static long holepos, firstpos;
+
+static int fuzz_frames = FUZZFRAMES;
+
+static int wm_db_save_disabled = FALSE;
+
+static int cur_playnew = -1;
+
+extern int cur_ntracks, cur_nsections;
+
+int mark_a = 0;
+int mark_b = 0;
+
+
+/*
+ *
+ */
+int wm_db_get_playnew( void )
+{
+ return 0;
+}
+
+/*
+ * split_workmandb()
+ *
+ * Split the WORKMANDB environment variable, if any, into a list of database
+ * files in the global "databases". If WORKMANDB is not available, make
+ * a single entry with $HOME/DBFILE.
+ *
+ * Also, fill the "rcfile" global with the personal preferences filename.
+ *
+ * The environment variables should have already been read and placed in the
+ * "rcfile" and "dbfiles" globals, respectively.
+ */
+void
+split_workmandb( void )
+{
+ int ndbs, i;
+ char *home, *wmdb;
+ int no_rc = 0, no_db = 0;
+
+ if (rcfile == NULL)
+ {
+ if ((home = getenv("HOME")) != NULL)
+ {
+ rcfile = malloc(strlen(home) + sizeof(RCFILE));
+ if (rcfile == NULL)
+ {
+
+nomem:
+ perror("split_workmandb()");
+ exit(1);
+ }
+
+ strcpy(rcfile, home);
+ strcat(rcfile, RCFILE);
+ }
+ else
+ no_rc = 1;
+
+ }
+
+ if ((wmdb = dbfiles) == NULL)
+ {
+ if ((home = getenv("HOME")) != NULL)
+ {
+ wmdb = malloc(strlen(home) + sizeof(DBFILE));
+ if (wmdb == NULL)
+ goto nomem;
+
+ databases = malloc(2 * sizeof (databases[0]));
+ if (databases == NULL)
+ goto nomem;
+
+ strcpy(wmdb, home);
+ strcat(wmdb, DBFILE);
+ databases[0] = wmdb;
+ databases[1] = NULL;
+ }
+ else
+ {
+ static char *emptydb = NULL;
+
+ databases = &emptydb;
+ no_db = 1;
+ }
+ }
+ else
+ {
+ ndbs = 1;
+ for (home = wmdb; *home; home++)
+ if (*home == ':')
+ {
+ *home = '\0';
+ ndbs++;
+ }
+
+ databases = malloc((ndbs + 1) * sizeof(databases[0]));
+ if (databases == NULL)
+ goto nomem;
+
+ for (i = 0; i < ndbs; i++)
+ {
+ databases[i] = wmdb;
+ wmdb += strlen(wmdb) + 1;
+ }
+
+ databases[i] = NULL;
+ }
+
+ if (no_db || no_rc)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,
+"WorkMan was run without a home directory, probably by a system daemon.\n");
+ fprintf(stderr, "It doesn't know where to find ");
+ if (no_rc)
+ {
+ fprintf(stderr, "your personal preferences file ");
+ if (no_db)
+ fprintf(stderr, "or the\ndatabase of CD descriptions");
+ }
+ else
+ fprintf(stderr, "the database of CD descriptions");
+
+ fprintf(stderr,
+".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n");
+#endif
+ wm_db_save_disabled = TRUE;
+ }
+}
+
+/*
+ * print_cdinfo(cd, prefs)
+ *
+ * cd A pointer to a cdinfo struct.
+ * prefs Flag: write personal preferences?
+ *
+ * Print a CD's information (in more or less readable form) to a buffer.
+ * Returns a pointer to the buffer.
+ *
+ * XXX - could be more efficient about calling wm_strmcat() and strlen().
+ */
+char *
+print_cdinfo(struct wm_cdinfo *cd, int prefs)
+{
+ int i;
+ char tempbuf[2000]; /* XXX - is this always big enough? */
+ static char *cdibuf = NULL;
+ struct wm_playlist *l;
+
+ sprintf(tempbuf, "\ntracks %d", cd->ntracks);
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].section < 2)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ cd->trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length);
+
+ wm_strmcpy(&cdibuf, tempbuf);
+
+ if (cur_nsections)
+ {
+ sprintf(tempbuf, "sections %d", cur_nsections);
+ /* fixed a bug here */
+ for (i = 0; i < cur_ntracks; i++)
+ if (cd->trk[i].section > 1)
+ sprintf(tempbuf + strlen(tempbuf), " %d",
+ cd->trk[i].start);
+ sprintf(tempbuf + strlen(tempbuf), "\n");
+
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (prefs)
+ {
+ if (cd->autoplay)
+ wm_strmcat(&cdibuf, "autoplay\n");
+ for (l = cd->lists; l != NULL && l->name != NULL; l++)
+ {
+ wm_strmcat(&cdibuf, "playlist ");
+
+ i = strlen(cdibuf) - 1;
+ wm_strmcat(&cdibuf, l->name);
+ while (cdibuf[++i])
+ if (cdibuf[i] == ' ' || cdibuf[i] == '\t')
+ cdibuf[i] = '_';
+
+ if (l->list != NULL)
+ {
+ for (i = 0; l->list[i]; i++)
+ ;
+ sprintf(tempbuf, " %d", i);
+ wm_strmcat(&cdibuf, tempbuf);
+ for (i = 0; l->list[i]; i++)
+ {
+ sprintf(tempbuf, " %d", l->list[i]);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ wm_strmcat(&cdibuf, "\n");
+ }
+ else
+ wm_strmcat(&cdibuf, " 0\n");
+ }
+
+ if (cd->volume)
+ {
+ /*
+ * Have to maintain compatibility with old versions,
+ * where volume was 0-32.
+ */
+ sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (cd->playmode)
+ {
+ sprintf(tempbuf, "playmode %d\n", cd->playmode);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (mark_a)
+ {
+ sprintf(tempbuf, "mark %d START\n", mark_a);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (mark_b)
+ {
+ sprintf(tempbuf, "mark %d END\n", mark_b);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+
+ if (cd->otherrc)
+ wm_strmcat(&cdibuf, cd->otherrc);
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ if (cd->trk[i].avoid)
+ {
+ sprintf(tempbuf, "dontplay %d\n", i + 1);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (cd->trk[i].volume)
+ {
+ sprintf(tempbuf, "volume %d %d\n", i + 1,
+ (cd->trk[i].volume * 32) / 100);
+ wm_strmcat(&cdibuf, tempbuf);
+ }
+ if (cd->trk[i].otherrc)
+ wm_strmcat(&cdibuf, cd->trk[i].otherrc);
+ }
+ }
+ else
+ {
+ if (cd->cdname[0])
+ {
+ wm_strmcat(&cdibuf, "cdname ");
+ wm_strmcat(&cdibuf, cd->cdname);
+ wm_strmcat(&cdibuf, "\n");
+ }
+
+ if (cd->artist[0])
+ {
+ wm_strmcat(&cdibuf, "artist ");
+ wm_strmcat(&cdibuf, cd->artist);
+ wm_strmcat(&cdibuf, "\n");
+ }
+
+ if (cd->otherdb)
+ wm_strmcat(&cdibuf, cd->otherdb);
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ if (cd->trk[i].section > 1)
+ wm_strmcat(&cdibuf, "s-");
+ wm_strmcat(&cdibuf, "track ");
+ if (cd->trk[i].songname != NULL)
+ wm_strmcat(&cdibuf, cd->trk[i].songname);
+ wm_strmcat(&cdibuf, "\n");
+ if (cd->trk[i].contd)
+ {
+ if (cd->trk[i].section > 1)
+ wm_strmcat(&cdibuf, "s-");
+ wm_strmcat(&cdibuf, "continue\n");
+ }
+ if (cd->trk[i].otherdb)
+ wm_strmcat(&cdibuf, cd->trk[i].otherdb);
+ }
+ }
+
+ return (cdibuf);
+} /* print_cdinfo() */
+
+/*
+ * Open the rcfile for reading or writing.
+ *
+ * name Filename
+ * mode "r" or "w"
+ */
+FILE *
+open_rcfile(const char *name, const char *mode)
+{
+ FILE *fp;
+ struct stat st;
+
+ fp = fopen(name, mode);
+ if (fp == NULL)
+ {
+ if (errno != ENOENT || mode[0] == 'w')
+ perror(name);
+ }
+ else
+ {
+ /* Don't let people open directories or devices */
+ if (fstat(fileno(fp), &st) < 0)
+ {
+ perror(name);
+ fclose(fp);
+ return (NULL);
+ }
+
+#ifdef S_ISREG
+ if (! S_ISREG(st.st_mode))
+#else
+ if ((st.st_mode & S_IFMT) != S_IFREG)
+#endif
+ {
+ errno = EISDIR;
+ perror(name);
+ fclose(fp);
+ return (NULL);
+ }
+
+ if (mode[0] == 'w') /* create -- put data in so locks work */
+ {
+ fputs("# WorkMan database file\n", fp);
+ fclose(fp);
+ fp = fopen(name, "r+");
+ if (fp == NULL)
+ if (errno != ENOENT)
+ perror(name);
+ }
+ }
+
+ return (fp);
+}
+
+/*
+ *
+ * allocate and clear "trackmap".
+ *
+ */
+int *reset_tracks(void)
+{
+ int i, j;
+ int *trackmap;
+ /*
+ * Since we access track numbers indirectly (to handle sections
+ * with at least a little elegance), the track mapping needs to be
+ * set up before we read anything. Initially it must assume that
+ * no sections will appear in this datafile.
+ */
+ trackmap = malloc(sizeof(int) * cur_ntracks);
+ if (trackmap == NULL)
+ {
+ perror("trackmap");
+ exit(1);
+ }
+ j = 0;
+ for (i = 0; i < cd->ntracks; i++)
+ {
+ trackmap[i] = j;
+ while (cd->trk[++j].section > 1)
+ ;
+ }
+ return trackmap;
+} /* reset_tracks() */
+
+/*
+ * Load a new-format database file, searching for a match with the currently
+ * inserted CD. Modify the in-core copy of the CD info based on what's found
+ * in the database.
+ *
+ * Returns 1 if there was a match or 0 if not.
+ *
+ * fp FILE* of database or rcfile.
+ * prefs 1 if we're searching .workmanrc, 0 for .workmandb,
+ * 2 if just reading settings
+ * scan Scan for "tracks" location and entry size only
+ * holesize_wanted How big a hole we're looking for, if any.
+ *
+ * If a hole was found along the way, update the global "holepos" with its
+ * starting offset in the file. A hole is defined as a bunch of blank lines
+ * preceding a "tracks" line. Holepos will contain the best match.
+ *
+ * In addition, "firstpos" will be filled with the position of the first
+ * "tracks" keyword, so we know how much room is available for global
+ * settings at the rcfile's start.
+ */
+int
+search_db( FILE *fp, int prefs, int scan, int holesize_wanted )
+
+{
+ char keyword[64], listname[64], *c;
+ int b, i, j, track = 0, listsize, ntracks, scratch, searching = 1;
+ int *trackmap = 0, gotsections = 0;
+ int fudge, maxfudge, sizediff, bestfudge = 0;
+ long pos = 0, thisholepos = -1, holesize = 99991239;
+ struct wm_playlist *l;
+
+ rclen = 0;
+
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: Reached search_db()\n" );
+
+ /* We may not find any holes at all! */
+ if (holesize_wanted)
+ holepos = -1;
+
+ if( prefs != 2 )
+ trackmap = reset_tracks();
+
+ if (prefs)
+ freeup(&otherrc);
+ firstpos = -1;
+ while (! feof(fp))
+ {
+ pos = ftell(fp);
+ keyword[0] = '\0';
+ do
+ b = getc(fp);
+ while (b != EOF && b != '\n' && isspace(b));
+
+ if (b == EOF || feof(fp))
+ break;
+
+ if (b != '\n')
+ {
+ keyword[0] = b;
+ fscanf(fp, "%62s", &keyword[1]);
+ }
+ if (keyword[0] == '\0') /* Blank line. */
+ {
+ if (thisholepos < 0)
+ thisholepos = pos;
+ continue;
+ }
+
+ /* Strip off "s-" if we've seen a "sections" keyword */
+ if (gotsections && keyword[0] == 's' && keyword[1] == '-')
+ {
+ for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++)
+ ;
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: stripped off the 's-'. Result is %s\n", keyword);
+ }
+
+ /* If this is the start of a CD entry, see if it matches. */
+ if (! strcmp(keyword, "tracks"))
+ {
+ if (prefs == 2)
+ break;
+
+ /* Is this the end of a hole? */
+ if (holesize_wanted && (thisholepos >= 0))
+ {
+ /* Yep. Is it better than the last one? */
+ if (pos - thisholepos < holesize && pos -
+ thisholepos >= holesize_wanted)
+ {
+ holepos = thisholepos;
+ holesize = pos - thisholepos;
+ }
+ thisholepos = -1;
+ }
+
+ /* Is it the start of the CD entries? */
+ if (firstpos == -1)
+ firstpos = pos;
+
+ /* Is this the end of the entry we really wanted? */
+ if (! searching)
+ {
+ rclen = pos - rcpos;
+ break;
+ }
+
+ /* If we have a near match, indicate that we
+ should stop reading tracks, etc now */
+ if (searching == 2)
+ {
+ searching = 3;
+ continue;
+ }
+
+ fscanf(fp, "%d", &ntracks);
+
+ if (ntracks != cd->ntracks)
+ {
+chomp:
+ SWALLOW_LINE(fp);
+ continue;
+ }
+
+ fudge = 0;
+ maxfudge = (ntracks * fuzz_frames) >> 1;
+ track = 0;
+ for (i = 0; i < ntracks; i++)
+ {
+ fscanf(fp, "%d", &scratch);
+ if (scratch != cd->trk[track].start)
+ {
+ sizediff = abs(scratch - cd->trk[track].start);
+ if (sizediff > fuzz_frames ||
+ (sizediff && scan))
+ break;
+ fudge += sizediff;
+ }
+ while (cd->trk[++track].section > 1)
+ ;
+ }
+ if (i != ntracks)
+ goto chomp;
+
+ if (fudge > 0) /* best near match? */
+ {
+ if (fudge > maxfudge)
+ goto chomp;
+ if (bestfudge == 0 || fudge < bestfudge)
+ bestfudge = fudge;
+ else
+ goto chomp;
+ rcpos = pos;
+ track = 0;
+ searching = 2;
+ }
+ else /* probably exact match */
+ {
+ fscanf(fp, "%d", &scratch);
+
+ if (scratch != -1 && scratch != cd->length)
+ goto chomp;
+
+ /* Found it! */
+ rcpos = pos;
+ track = 0;
+ searching = 0;
+ }
+
+ SWALLOW_LINE(fp); /* Get rid of newline */
+ }
+
+ /* Global mode stuff goes here */
+ else if (! strcmp(keyword, "cddbprotocol"))
+ {
+ getc(fp);
+ i = getc(fp); /* only first letter is used */
+ cddb.protocol = i == 'c' ? 1 :
+ i == 'h' ? 2 : 3 ;
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ }
+
+ else if (! strcmp(keyword, "cddbserver"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.cddb_server[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.cddb_server,
+ sizeof(cddb.cddb_server), fp);
+ if ((i = strlen(cddb.cddb_server)))
+ cddb.cddb_server[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbmailadress"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.mail_adress[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.mail_adress,
+ sizeof(cddb.mail_adress), fp);
+ if ((i = strlen(cddb.mail_adress)))
+ cddb.mail_adress[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbpathtocgi"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.path_to_cgi[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.path_to_cgi,
+ sizeof(cddb.path_to_cgi), fp);
+ if ((i = strlen(cddb.path_to_cgi)))
+ cddb.path_to_cgi[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cddbproxy"))
+ {
+ getc(fp); /* lose the space */
+ if (cddb.proxy_server[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cddb.proxy_server,
+ sizeof(cddb.proxy_server), fp);
+ if ((i = strlen(cddb.proxy_server)))
+ cddb.proxy_server[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "whendone"))
+ {
+ getc(fp);
+ i = getc(fp); /* only first letter is used */
+ if (cur_stopmode == -1) /* user's setting preferred */
+ cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2;
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ }
+
+ else if (! strcmp(keyword, "playnew"))
+ {
+ if (cur_playnew == -1)
+ cur_playnew = 1;
+ SWALLOW_LINE(fp);
+ }
+
+ /* If we're searching, skip to the next "tracks" line. */
+ else if (((searching & 1)|| scan)
+ && !(prefs && firstpos == -1))
+ SWALLOW_LINE(fp)
+
+ else if (! strcmp(keyword, "sections"))
+ {
+ gotsections = 1;
+ fscanf(fp, "%d", &ntracks);
+
+ free(trackmap);
+ trackmap = (int *) malloc(sizeof(int) *
+ (cur_ntracks + ntracks));
+ if (trackmap == NULL)
+ {
+ perror("section mapping");
+ exit(1);
+ }
+
+ /*
+ * If sections are already defined, use these as a
+ * reference, mapping this CD entry's section numbers
+ * to the ones in core.
+ *
+ * Otherwise, split the CD up according to the sections
+ * listed here.
+ */
+ if (cur_nsections)
+ {
+ track = 0;
+ i = 0;
+ while (ntracks)
+ {
+ ntracks--;
+ fscanf(fp, "%d", &scratch);
+ while (scratch > cd->trk[track].start)
+ {
+ if (cd->trk[track].section < 2)
+ trackmap[i++] = track;
+ ++track;
+
+ if (track == cur_ntracks)
+ break;
+ }
+
+ /* rc has later sections than db... */
+ if (track == cur_ntracks)
+ break;
+
+ /* Matches can be approximate */
+ if (scratch+75 > cd->trk[track].start &&
+ scratch-75 < cd->trk[track].start)
+ trackmap[i++] = track++;
+ else
+ trackmap[i++] = -1;
+
+ if (track == cur_ntracks)
+ break;
+ }
+
+ /* This only happens if track == cur_ntracks */
+ while (ntracks--)
+ trackmap[i++] = -1;
+
+ while (track < cur_ntracks)
+ {
+ if (cd->trk[track].section < 2)
+ trackmap[i++] = track;
+ track++;
+ }
+
+ track = 0;
+ SWALLOW_LINE(fp);
+ }
+ else
+ {
+ while (ntracks--)
+ {
+ fscanf(fp, "%d", &scratch);
+ split_trackinfo(scratch);
+ }
+
+ for (i = 0; i < cur_ntracks; i++)
+ {
+ trackmap[i] = i;
+ /* split_trackinfo() sets this */
+ cd->trk[i].contd = 0;
+ }
+
+ SWALLOW_LINE(fp);
+ }
+ }
+
+ else if (! strcmp(keyword, "track"))
+ {
+ char buf[502];
+
+ getc(fp); /* lose the space */
+
+ /* don't overwrite existing track names. */
+ /* However, overwrite them if there was a "bad" fuzzy match before */
+ if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2))
+ SWALLOW_LINE(fp)
+ else if (cd->trk[trackmap[track]].songname &&
+ cd->trk[trackmap[track]].songname[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(buf, sizeof(buf), fp);
+ wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG, "found track %s\n", buf);
+ if( (i = strlen(buf)) )
+ buf[i - 1] = '\0';
+ wm_strmcpy(&cd->trk[trackmap[track]].songname, buf);
+ }
+ track++;
+ }
+
+ else if (! strcmp(keyword, "playmode"))
+ fscanf(fp, "%d", &cd->playmode);
+
+ else if (! strcmp(keyword, "autoplay"))
+ cd->autoplay = 1;
+
+ else if (! strcmp(keyword, "cdname"))
+ {
+ /* because of fuzzy matching that may change
+ the disk contents, we reset everything when
+ we find the name, in hopes that we will recover
+ most, if not all, of the information from the
+ file. */
+/*
+ * nasty bug was here. Was it? BUGBUGBUG
+ *
+ * wipe_cdinfo();
+ * trackmap = reset_tracks();
+ */
+
+ getc(fp); /* lose the space */
+ /* don't overwrite existing cd name. */
+ if (cd->cdname[0] && (searching == 2))
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ if (searching > 1)
+ {
+ strcpy(cd->cdname, "Probably://");
+ fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp);
+ }
+ else
+ {
+ fgets(cd->cdname, sizeof(cd->cdname), fp);
+ }
+ if ( (i = strlen(cd->cdname)) )
+ cd->cdname[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "artist"))
+ {
+ getc(fp); /* lose the space */
+ /* don't overwrite existing artist names. */
+ if (cd->artist[0])
+ do
+ i = getc(fp);
+ while (i != '\n' && i != EOF);
+ else
+ {
+ fgets(cd->artist, sizeof(cd->artist), fp);
+ if( (i = strlen(cd->artist)) )
+ cd->artist[i - 1] = '\0';
+ }
+ }
+
+ else if (! strcmp(keyword, "cdvolume"))
+ {
+ fscanf(fp, "%d", &cd->volume);
+ cd->volume = (cd->volume * 100) / 32;
+ }
+
+ else if (! strcmp(keyword, "dontplay"))
+ {
+ fscanf(fp, "%d", &i);
+ if (trackmap[i - 1] != -1)
+ cd->trk[trackmap[i - 1]].avoid = 1;
+ }
+
+ else if (! strcmp(keyword, "continue"))
+ {
+ if (trackmap[track - 1] != -1)
+ cd->trk[trackmap[track - 1]].contd = 1;
+ }
+
+ else if (! strcmp(keyword, "volume"))
+ {
+ fscanf(fp, "%d", &i);
+ if (trackmap[i - 1] == -1)
+ SWALLOW_LINE(fp)
+ else
+ {
+ i = trackmap[i - 1];
+ fscanf(fp, "%d", &cd->trk[i].volume);
+ cd->trk[i].volume = (cd->trk[i].volume*100)/32;
+ if (cd->trk[i].volume > 32)
+ cd->trk[i].volume = 0;
+ }
+ }
+
+ else if (! strcmp(keyword, "playlist"))
+ {
+ getc(fp);
+ fscanf(fp, "%63s", listname);
+
+/* XXX take this out at some point */
+ if (! strcmp(listname, "Default"))
+ strcpy(listname, "List A");
+
+ for (i = 0; listname[i]; i++)
+ if (listname[i] == '_')
+ listname[i] = ' ';
+
+ l = new_playlist(cd, listname);
+ if (l == NULL)
+ {
+plnomem:
+ perror("playlist read");
+ exit(1);
+ }
+
+ fscanf(fp, "%d", &listsize);
+
+ l->list = malloc(sizeof(int) * (listsize + 1));
+ if (l->list == NULL)
+ goto plnomem;
+
+ /* Leave out tracks that weren't in .workmandb. */
+ j = 0;
+ for (i = 0; i < listsize; i++)
+ {
+ fscanf(fp, "%d", &scratch);
+ scratch = trackmap[scratch - 1];
+ if (scratch != -1)
+ l->list[j++] = scratch + 1;
+ }
+
+ l->list[j] = 0;
+ }
+
+ else if (! strcmp(keyword, "mark"))
+ {
+ int mark_val = -1, mark_namelen;
+ char mark_name[32];
+
+ fscanf(fp, "%d", &mark_val);
+ if (mark_val == -1)
+ goto chomp;
+
+ if (getc(fp) != ' ')
+ continue;
+
+ fgets(mark_name, sizeof(mark_name), fp);
+ if( ( mark_namelen = strlen(mark_name)) )
+ mark_name[mark_namelen - 1] = '\0';
+
+/*
+ if (! strcmp(mark_name, "START"))
+ set_abtimer(0, mark_val);
+ else if (! strcmp(mark_name, "END"))
+ set_abtimer(1, mark_val);
+
+*/
+ }
+
+ /* Unrecognized keyword. Put it in the right place. */
+ else
+ {
+ char **buf, input[BUFSIZ];
+
+ if (track && trackmap[track - 1] == -1)
+ {
+ SWALLOW_LINE(fp);
+ continue;
+ }
+
+ i = track ? trackmap[track - 1] : 0;
+ buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc :
+ i ? &cd->trk[i].otherdb : &cd->otherdb;
+ if (firstpos == -1) {
+ if (prefs) {
+ buf = &otherrc;
+ } else {
+ goto chomp;
+ } /* if() else */
+ } /* if() */
+ wm_strmcat(buf, keyword);
+ do {
+ input[sizeof(input) - 1] = 'x';
+ fgets(input, sizeof(input), fp);
+ wm_strmcat(buf, input);
+ } while (input[sizeof(input) - 1] != 'x');
+ }
+ }
+
+ if (rclen == 0 && !searching)
+ rclen = pos - rcpos;
+
+ if (searching > 1) /* A near match has been found. Good enough. */
+ searching = 0;
+
+ cddb_struct2cur();
+ return (! searching);
+
+} /* search_db() */
+
+/*
+ * Delay some amount of time without using interval timers.
+ */
+void
+spinwheels(int secs) {
+ struct timeval tv;
+
+ tv.tv_usec = 0;
+ tv.tv_sec = secs;
+ select(0, NULL, NULL, NULL, &tv);
+} /* spinwheels() */
+
+/*
+ * lockit(fd, type)
+ *
+ * fd file descriptor
+ * type lock type
+ *
+ * Lock a file. Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(int fd, int type)
+{
+ struct flock fl;
+ int result, timer = 0;
+
+ if (suppress_locking)
+ return (0);
+
+ fl.l_type = type;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+
+ while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno != EACCES || errno != EAGAIN)
+ break;
+ if (timer++ == 30)
+ {
+ errno = ETIMEDOUT;
+ break;
+ }
+
+ spinwheels(1);
+ }
+
+ return (result);
+} /* lockit() */
+
+/*
+ * Search all the database files and our personal preference file for
+ * more information about the current CD.
+ */
+void
+load( void )
+{
+ FILE *fp;
+ char **dbfile;
+ int locked = 0;
+ int dbfound = 0, *trklist, i;
+ unsigned long dbpos;
+
+/* This is some kind of profiling code. I don't change it
+ to wm_lib_message() for now... */
+#ifndef NDEBUG
+ long t1, t2;
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t1);
+ printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1);
+ fflush(stdout);
+ }
+#endif
+
+ dbfile = databases;
+
+ found_in_db = 0;
+
+ /* Turn the cd->trk array into a simple array of ints. */
+ trklist = (int *)malloc(sizeof(int) * cd->ntracks);
+ for (i = 0; i < cd->ntracks; i++)
+ trklist[i] = cd->trk[i].start;
+
+ do {
+ if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist,
+ cd->length * 75, 0, &dbpos) == 0)
+ dbfound = 1;
+
+ fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (db) lock");
+ else
+ locked = 1;
+
+ if (dbfound)
+ fseek(fp, dbpos, 0);
+
+ if (search_db(fp, 0, 0, 0))
+ {
+ found_in_db = 1;
+ cd->whichdb = *dbfile;
+ }
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (db) lock");
+
+ fclose(fp);
+ }
+ } while (*++dbfile != NULL && cd->whichdb == NULL);
+
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t2);
+ printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+ fflush(stdout);
+ }
+#endif
+
+ fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ locked = 0;
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (rc) lock");
+ else
+ locked = 1;
+
+ rcpos = 0;
+ found_in_rc = search_db(fp, 1, 0, 0);
+ if (! found_in_rc)
+ cd->autoplay = wm_db_get_playnew();
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (rc) lock");
+
+ fclose(fp);
+ }
+
+ free(trklist);
+
+ if (cur_playnew == -1)
+ cur_playnew = 0;
+
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ {
+ time(&t2);
+ printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+ fflush(stdout);
+ }
+#endif
+} /* load() */
+
+/*
+ * Load program settings from the rcfile.
+ */
+void
+load_settings( void )
+{
+ FILE *fp;
+ int locked;
+
+ fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+ if (fp != NULL)
+ {
+ locked = 0;
+ if (lockit(fileno(fp), F_RDLCK))
+ perror("Couldn't get read (rc) lock");
+ else
+ locked = 1;
+
+ rcpos = 0;
+ found_in_rc = search_db(fp, 2, 0, 0);
+ if (! found_in_rc)
+ cd->autoplay = wm_db_get_playnew();
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Couldn't relinquish (rc) lock");
+
+ fclose(fp);
+ }
+} /* load_settings() */
+
+/*
+ * save_globals()
+ *
+ * Save the global preferences, scooting CD entries to the end if needed.
+ * The assumption here is that the rcfile is locked, and that firstpos has
+ * been set by a previous scan.
+ */
+void
+save_globals(FILE *fp)
+{
+ char *globes = NULL, *cdentry = NULL, temp[100];
+ long curpos;
+ int globesize, hit_cdent = 0, c = 0;
+
+ if (otherrc)
+ wm_strmcpy(&globes, otherrc);
+
+ if (cddb.protocol)
+ {
+ sprintf(temp, "cddbprotocol ");
+ switch(cddb.protocol)
+ {
+ case 1: /* cddbp */
+ sprintf(temp + strlen(temp), "cddbp\n");
+ break;
+ case 2: /* http */
+ sprintf(temp + strlen(temp), "http\n");
+ break;
+ case 3: /* proxy */
+ sprintf(temp + strlen(temp), "proxy\n");
+ break;
+ default:
+ break;
+ }
+ wm_strmcat(&globes, temp);
+
+ if(cddb.mail_adress[0])
+ {
+ sprintf(temp,"cddbmailadress %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.cddb_server[0])
+ {
+ sprintf(temp,"cddbserver %s\n",
+ cddb.cddb_server);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.path_to_cgi[0])
+ {
+ sprintf(temp,"cddbpathtocgi %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+
+ if(cddb.proxy_server[0])
+ {
+ sprintf(temp,"cddbproxy %s\n",
+ cddb.mail_adress);
+ wm_strmcat(&globes, temp);
+ }
+ }
+
+ if (cur_stopmode == 1 || cur_stopmode == 2)
+ {
+ sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" :
+ "eject");
+ wm_strmcat(&globes, temp);
+ }
+
+ if (cur_playnew == 1)
+ wm_strmcat(&globes, "playnew\n");
+
+ curpos = firstpos;
+ if (curpos < 0)
+ curpos = 0;
+
+ fseek(fp, curpos, SEEK_SET);
+
+ if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0))
+ {
+ while (1)
+ {
+ temp[sizeof(temp)-1] = 'x';
+
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ {
+ fseek(fp, 0, SEEK_SET);
+ if (globes != NULL)
+ {
+ fwrite(globes, globesize, 1, fp);
+ free(globes);
+ }
+ if (cdentry != NULL)
+ {
+ fwrite(cdentry, strlen(cdentry), 1, fp);
+ free(cdentry);
+ }
+ return;
+ }
+
+ if (! strncmp(temp, "tracks ", 7))
+ {
+ hit_cdent = 1;
+ if (curpos >= globesize)
+ break;
+ }
+
+ if (! hit_cdent)
+ {
+ curpos += strlen(temp);
+ if (temp[sizeof(temp)-1] == '\0')
+ while ((c = getc(fp)) != '\n' &&
+ c != EOF)
+ curpos++;
+ if (c == '\n')
+ curpos++;
+
+ continue;
+ }
+
+ wm_strmcat(&cdentry, temp);
+ curpos += strlen(temp);
+ while (temp[sizeof(temp)-1] == '\0')
+ {
+ temp[sizeof(temp)-1] = 'x';
+ if (fgets(temp, sizeof(temp), fp) == NULL)
+ break;
+ wm_strmcat(&cdentry, temp);
+ curpos += strlen(temp);
+ }
+ }
+
+ if (cdentry != NULL)
+ {
+ fseek(fp, 0, SEEK_END);
+ fwrite(cdentry, strlen(cdentry), 1, fp);
+ free(cdentry);
+ }
+ }
+
+ if (globes != NULL)
+ {
+ fseek(fp, 0, SEEK_SET);
+ fwrite(globes, globesize, 1, fp);
+ free(globes);
+ }
+
+ while (globesize++ < curpos)
+ putc('\n', fp);
+} /* save_globals() */
+
+/*
+ * save_entry()
+ *
+ * Save the CD information to one database.
+ *
+ * filename Database to save to.
+ * pref 0 for hard data, 1 for preferences.
+ *
+ * If an entry for this CD exists already, overwrite it with the new entry
+ * if the new entry is the same size or smaller, or with newlines if the new
+ * entry is larger (in which case the new entry is appended to the file.)
+ *
+ * Also, if the preference information is being updated, save it to the
+ * file while we've got it locked. Scoot stuff from the beginning of
+ * the file to the end as needed to facilitate this.
+ *
+ * XXX Preference-saving should probably be done elsewhere, like in an
+ * Apply button on the Goodies popup, and in any case needs to go to a
+ * different file (.Xdefaults?)
+ *
+ * Returns 0 on success.
+ */
+int
+save_entry(char *filename, int pref)
+{
+ FILE *fp;
+ char *buf;
+ int len, i, locked = 0;
+
+
+ if( filename == NULL )
+ return (-1);
+
+ fp = open_rcfile(filename, "r+");
+ if (fp == NULL)
+ {
+ if (errno == ENOENT) /* doesn't exist already */
+ fp = open_rcfile(filename, "w");
+ if (fp == NULL)
+ return (-1);
+ }
+
+ if (lockit(fileno(fp), F_WRLCK))
+ perror("Warning: Couldn't get write lock");
+ else
+ locked = 1;
+
+ buf = print_cdinfo(cd, pref);
+ len = strlen(buf); /* doesn't return if there's an error */
+
+ rcpos = -1;
+ search_db(fp, pref, 1, len);
+ if (rcpos != -1) /* XXX */
+ {
+ /*
+ * Jump to the entry's position in the database file, if
+ * it was found.
+ */
+ fseek(fp, rcpos, SEEK_SET);
+
+ if (rclen >= len && holepos == -1)
+ {
+ /*
+ * If the new entry will fit in the space occupied by
+ * the old one, overwrite the old one and make a hole
+ * of the appropriate size at its end.
+ *
+ * No need to update the index file in this case, as
+ * the entry's position hasn't changed.
+ */
+ fputs(buf, fp);
+ for (i = len; i < rclen; i++)
+ fputc('\n', fp);
+ }
+ else
+ {
+ /*
+ * Overwrite the old entry with a hole and delete
+ * its pointer in the index file.
+ */
+ for (i = 0; i < rclen; i++)
+ fputc('\n', fp);
+ idx_delete_entry(filename, cd->trk[cd->ntracks-1].start,
+ 0, rcpos);
+
+ rcpos = -1;
+ }
+ }
+
+ /*
+ * Old entry wasn't found, or its new version wouldn't fit where
+ * the old one was.
+ */
+ if (rcpos == -1)
+ {
+ /*
+ * Write the new entry in a hole, if there is one,
+ * or at the end of the file.
+ */
+ if (holepos >= 0)
+ {
+ fseek(fp, holepos, SEEK_SET);
+ if (holepos < firstpos)
+ firstpos = holepos;
+ }
+ else
+ {
+ fseek(fp, 0, SEEK_END);
+ holepos = ftell(fp);
+ }
+ fputs(buf, fp);
+
+ /*
+ * Write a new index entry for this CD.
+ */
+ idx_write_entry(filename, cd->trk[cd->ntracks - 1].start,
+ holepos);
+ }
+
+ if (pref)
+ save_globals(fp);
+
+ fflush(fp);
+
+ if (locked && lockit(fileno(fp), F_UNLCK))
+ perror("Warning: Couldn't relinquish write lock");
+
+ fclose(fp);
+
+ return (0);
+} /* save_entry() */
+
+/*
+ * save()
+ *
+ * Save CD information to the appropriate datafile (the first file in the
+ * list, unless the entry came from another database file) and to the
+ * personal prefs file.
+ */
+int
+save( void )
+{
+
+ if( wm_db_save_disabled == FALSE )
+ {
+ if (save_entry(rcfile, 1))
+ return (0);
+
+ if (cd->whichdb == NULL || access(cd->whichdb, W_OK))
+ cd->whichdb = databases[0];
+
+ if (save_entry(cd->whichdb, 0))
+ return (0);
+
+ return( WM_DB_SAVE_ERROR );
+ } else {
+ return( WM_DB_SAVE_DISABLED );
+ }
+} /* save() */
diff --git a/kscd/libwm/drv_sony.c b/kscd/libwm/drv_sony.c
new file mode 100644
index 00000000..a9db5d73
--- /dev/null
+++ b/kscd/libwm/drv_sony.c
@@ -0,0 +1,166 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Vendor-specific drive control routines for Sony CDU-8012 series.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define PAGE_AUDIO 0x0e
+
+/* local prototypes */
+static int sony_init( struct wm_drive *d );
+static int sony_set_volume(struct wm_drive *d, int left, int right );
+static int sony_get_volume( struct wm_drive *d, int *left, int *right );
+
+
+extern int min_volume, max_volume;
+
+struct wm_drive_proto sony_proto = {
+ sony_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ sony_get_volume,
+ sony_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ gen_eject,
+ gen_closetray,
+ NULL /* gen_get_cdtext */
+};
+
+/*
+ * Initialize the driver.
+ */
+static int sony_init( struct wm_drive *d ) {
+
+/* Sun use Sony drives as well */
+#if defined(SYSV) && defined(CODEC)
+ codec_init();
+#endif
+ min_volume = 128;
+ max_volume = 255;
+ return( 0 );
+} /* sony_init() */
+
+/*
+ * On the Sony CDU-8012 drive, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. Additionally, only the top half
+ * of the volume scale is valid; the bottom half is all silent. The actual
+ * formula looks like
+ *
+ * max^2 - (max - vol)^2 max
+ * v = --------------------- + ---
+ * max * 2 2
+ *
+ * Where "max" is the maximum value of the volume scale, usually 100.
+ */
+static int
+scale_volume(vol, max)
+ int vol, max;
+{
+ vol = (max*max - (max - vol) * (max - vol)) / max;
+ return ((vol + max) / 2);
+}
+
+/*
+ * Given a value between min_volume and max_volume, return the standard-scale
+ * volume value needed to achieve that hardware value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(cd_vol, max)
+ int cd_vol, max;
+{
+ int vol = 0, top = max, bot = 0, scaled = 0;
+
+ cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol <= scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ /* Might have looked down too far for repeated scaled values */
+ if (cd_vol < scaled)
+ vol++;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+}
+
+/*
+ * Get the volume. Sun's CD-ROM driver doesn't support this operation, even
+ * though their drive does. Dumb.
+ */
+static int
+sony_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ unsigned char mode[16];
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ *left = unscale_volume(mode[9], 100);
+ *right = unscale_volume(mode[11], 100);
+
+ return (0);
+}
+
+/*
+ * Set the volume using the wacky scale outlined above. The Sony drive
+ * responds to the standard set-volume command.
+ */
+static int
+sony_set_volume(struct wm_drive *d, int left, int right )
+{
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+ return (gen_set_volume(d, left, right));
+}
diff --git a/kscd/libwm/drv_toshiba.c b/kscd/libwm/drv_toshiba.c
new file mode 100644
index 00000000..ef009867
--- /dev/null
+++ b/kscd/libwm/drv_toshiba.c
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Vendor-specific drive control routines for Toshiba XM-3401 series.
+ */
+
+static char drv_toshiba_id[] = "$Id$";
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define SCMD_TOSH_EJECT 0xc4
+
+/* local prototypes */
+static int tosh_init( struct wm_drive *d );
+static int tosh_eject( struct wm_drive *d );
+static int tosh_set_volume( struct wm_drive *d, int left, int right );
+static int tosh_get_volume( struct wm_drive *d, int *left, int *right );
+
+struct wm_drive_proto toshiba_proto = {
+ tosh_init, /* functions... */
+ gen_close,
+ gen_get_trackcount,
+ gen_get_cdlen,
+ gen_get_trackinfo,
+ gen_get_drive_status,
+ tosh_get_volume,
+ tosh_set_volume,
+ gen_pause,
+ gen_resume,
+ gen_stop,
+ gen_play,
+ tosh_eject,
+ gen_closetray,
+ NULL /* gen_get_cdtext */
+};
+
+/*
+ * Initialize the driver.
+ */
+static int
+tosh_init( struct wm_drive *d )
+{
+ extern int min_volume;
+
+/* Sun use Toshiba drives as well */
+#if defined(SYSV) && defined(CODEC)
+ codec_init();
+#endif
+ min_volume = 0;
+ return ( 0 );
+}
+
+/*
+ * Send the Toshiba code to eject the CD.
+ */
+static int
+tosh_eject( struct wm_drive *d )
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_TOSH_EJECT, 1, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Set the volume. The low end of the scale is more sensitive than the high
+ * end, so make up for that by transforming the volume parameters to a square
+ * curve.
+ */
+static int
+tosh_set_volume( struct wm_drive *d, int left, int right )
+{
+ left = (left * left * left) / 10000;
+ right = (right * right * right) / 10000;
+ return (gen_set_volume(d, left, right));
+}
+
+/*
+ * Undo the transformation above using a binary search (so no floating-point
+ * math is required.)
+ */
+static int
+unscale_volume(cd_vol, max)
+ int cd_vol, max;
+{
+ int vol = 0, top = max, bot = 0, scaled = 0;
+
+ /*cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;*/
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = (vol * vol) / max;
+ if (cd_vol <= scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ /* Might have looked down too far for repeated scaled values */
+ if (cd_vol < scaled)
+ vol++;
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+}
+
+/*
+ * Get the volume.
+ */
+static int
+tosh_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ int status;
+
+ status = gen_get_volume(d, left, right);
+ if (status < 0)
+ return (status);
+ *left = unscale_volume(*left, 100);
+ *right = unscale_volume(*right, 100);
+
+ return (0);
+}
diff --git a/kscd/libwm/include/wm_cdda.h b/kscd/libwm/include/wm_cdda.h
new file mode 100644
index 00000000..11c8eb3e
--- /dev/null
+++ b/kscd/libwm/include/wm_cdda.h
@@ -0,0 +1,203 @@
+#ifndef WM_CDDA_H
+#define WM_CDDA_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Information about a particular block of CDDA data.
+ */
+struct cdda_block {
+ unsigned char status;
+ unsigned char track;
+ unsigned char index;
+ unsigned char reserved;
+
+ int frame;
+ char *buf;
+ long buflen;
+};
+
+struct cdda_device {
+ int fd;
+ const char *devname;
+
+ unsigned char status;
+ unsigned char track;
+ unsigned char index;
+ unsigned char command;
+
+ int frame;
+ int frames_at_once;
+
+ /* Average volume levels, for level meters */
+ unsigned char lev_chan0;
+ unsigned char lev_chan1;
+
+ /* Current volume setting (0-255) */
+ unsigned char volume;
+
+ /* Current balance setting (0-255, 128 = balanced) */
+ unsigned char balance;
+
+ struct cdda_block *blocks;
+ int numblocks;
+};
+
+#include "wm_cdrom.h"
+#include "wm_config.h"
+#include "wm_struct.h"
+/*
+ * cdda_block status codes.
+ */
+
+/*
+ * Enable or disable CDDA building depending on platform capabilities, and
+ * determine endianness based on architecture. (Gross!)
+ *
+ * For header-comfort, the macros LITTLE_ENDIAN and BIG_ENDIAN had to be
+ * renamed. At least Linux does have bytesex.h and endian.h for easy
+ * byte-order examination.
+ */
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+ #include <machine/endian.h>
+ #if BYTE_ORDER == LITTLE_ENDIAN
+ #define WM_LITTLE_ENDIAN 1
+ #define WM_BIG_ENDIAN 0
+ #else
+ #define WM_LITTLE_ENDIAN 0
+ #define WM_BIG_ENDIAN 1
+ #endif
+#elif defined(__sun) || defined(sun)
+# ifdef SYSV
+# include <sys/types.h>
+# include <sys/cdio.h>
+# ifndef CDROMCDDA
+# undef BUILD_CDDA
+# endif
+# ifdef i386
+# define WM_LITTLE_ENDIAN 1
+# define WM_BIG_ENDIAN 0
+# else
+# define WM_BIG_ENDIAN 1
+# define WM_LITTLE_ENDIAN 0
+# endif
+# else
+# undef BUILD_CDDA
+# endif
+
+/* Linux only allows definition of endianness, because there's no
+ * standard interface for CDROM CDDA functions that aren't available
+ * if there is no support.
+ */
+#elif defined(__linux__)
+/*# include <bytesex.h>*/
+# include <endian.h>
+/*
+ * XXX could this be a problem? The results are only 0 and 1 because
+ * of the ! operator. How about other linux compilers than gcc ?
+ */
+# define WM_LITTLE_ENDIAN !(__BYTE_ORDER - __LITTLE_ENDIAN)
+# define WM_BIG_ENDIAN !(__BYTE_ORDER - __BIG_ENDIAN)
+#elif defined WORDS_BIGENDIAN
+ #define WM_LITTLE_ENDIAN 0
+ #define WM_BIG_ENDIAN 1
+#else
+ #define WM_LITTLE_ENDIAN 1
+ #define WM_BIG_ENDIAN 0
+#endif
+
+/*
+ * The following code shouldn't take effect now.
+ * In 1998, the WorkMan platforms don't support __PDP_ENDIAN
+ * architectures.
+ *
+ */
+
+#if !defined(WM_LITTLE_ENDIAN)
+# if !defined(WM_BIG_ENDIAN)
+# error yet unsupported architecture
+ foo bar this is to stop the compiler.
+# endif
+#endif
+
+#if defined(BUILD_CDDA)
+/*
+ * The following code support us by optimize cdda operations
+ */
+#define CDDARETURN(x) if(x && x->cdda == 1) return
+#define IFCDDA(x) if(x && x->cdda == 1)
+int cdda_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind);
+int cdda_play(struct wm_drive *d, int start, int end, int realstart);
+int cdda_pause(struct wm_drive *d);
+int cdda_stop(struct wm_drive *d);
+int cdda_eject(struct wm_drive *d);
+int cdda_set_volume(struct wm_drive *d, int left, int right);
+int cdda_get_volume(struct wm_drive *d, int *left, int *right);
+void cdda_kill(struct wm_drive *d);
+void cdda_save(struct wm_drive *d, char *filename);
+int cdda_get_ack(int);
+int gen_cdda_init(struct wm_drive *d );
+
+void cdda_set_direction(struct wm_drive *d, int newdir);
+void cdda_set_speed(struct wm_drive *d, int speed);
+void cdda_set_loudness(struct wm_drive *d, int loud);
+
+
+int wmcdda_init(struct cdda_device*);
+int wmcdda_open(const char*);
+int wmcdda_close(struct cdda_device*);
+int wmcdda_setup(int start, int end, int realstart);
+long wmcdda_read(struct cdda_device*, struct cdda_block *block);
+void wmcdda_speed(int speed);
+void wmcdda_direction(int newdir);
+
+#else
+ #define CDDARETURN(x)
+ #define IFCDDA(x)
+ #define cdda_get_drive_status
+ #define cdda_play
+ #define cdda_pause
+ #define cdda_resume
+ #define cdda_stop
+ #define cdda_eject
+ #define cdda_set_volume
+ #define cdda_get_volume
+ #define cdda_kill
+ #define cdda_save
+ #define cdda_get_ack
+#endif /* defined(BUILD_CDDA) */
+
+#include <stdio.h>
+
+#ifdef DEBUG
+ #define DEBUGLOG(fmt, args...) fprintf(stderr, fmt, ##args)
+#else
+ #define DEBUGLOG(fmt, args...)
+#endif
+#define ERRORLOG(fmt, args...) fprintf(stderr, fmt, ##args)
+
+#endif /* WM_CDDA_H */
diff --git a/kscd/libwm/include/wm_cddb.h b/kscd/libwm/include/wm_cddb.h
new file mode 100644
index 00000000..26a0c2d9
--- /dev/null
+++ b/kscd/libwm/include/wm_cddb.h
@@ -0,0 +1,36 @@
+#ifndef WM_CDDB_H
+#define WM_CDDB_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+unsigned long cddb_discid(void);
+void cddb_struct2cur(void);
+void cddb_cur2struct(void);
+void cddb_select(void);
+void connect_cddb(void);
+void update_cddbserver(void);
+void cddb_request(void);
+
+#endif /* WM_CDDB_H */
diff --git a/kscd/libwm/include/wm_cdinfo.h b/kscd/libwm/include/wm_cdinfo.h
new file mode 100644
index 00000000..dbb8e949
--- /dev/null
+++ b/kscd/libwm/include/wm_cdinfo.h
@@ -0,0 +1,76 @@
+#ifndef WM_CDINFO_H
+#define WM_CDINFO_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes from cdinfo.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+extern char *cur_trackname; /* Take a guess */
+extern int cur_index; /* Current index mark */
+extern int cur_frame; /* Current frame number */
+extern struct wm_play *playlist; /* = NULL */
+
+/*extern int cur_track;*/ /* Current track number, starting at 1 */
+extern char *cur_artist; /* Name of current CD's artist */
+extern char cur_avoid; /* Avoid flag */
+extern char cur_contd; /* Continued flag */
+extern char *cur_cdname; /* Album name */
+extern int cur_nsections; /* Number of sections currently defined */
+extern int exit_on_eject;
+extern int cur_pos_abs;
+extern int cur_pos_rel;
+extern int cur_cdlen;
+
+extern int cur_ntracks;
+extern int cur_lasttrack;
+extern int cur_firsttrack;
+extern int cur_listno;
+extern int cur_stopmode;
+
+void wipe_cdinfo( void );
+void play_next_entry( int forward );
+void make_playlist( int playmode, int starttrack );
+int get_autoplay( void );
+int get_playmode( void );
+void pl_find_track( int track );
+void play_prev_track( int forward );
+void play_next_track( int forward );
+int tracklen( int num );
+int get_default_volume( int track );
+int split_trackinfo( int pos );
+int remove_trackinfo( int num );
+void freeup( char **x );
+int get_runtime( void );
+const char *trackname( int num );
+void stash_cdinfo( char *artist, char *cdname, int autoplay, int playmode );
+void stash_trkinfo( int track, char *songname, int contd, int avoid );
+int get_avoid( int num );
+int get_contd( int num );
+void default_volume( int track, int vol );
+char *listentry( int num );
+
+#endif /* WM_CDINFO_H */
diff --git a/kscd/libwm/include/wm_cdrom.h b/kscd/libwm/include/wm_cdrom.h
new file mode 100644
index 00000000..d38f8b68
--- /dev/null
+++ b/kscd/libwm/include/wm_cdrom.h
@@ -0,0 +1,114 @@
+#ifndef WM_CDROM_H
+#define WM_CDROM_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes from cdrom.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+#define WM_CDS_NO_DISC(status) (status == WM_CDM_UNKNOWN ||\
+ status == WM_CDM_EJECTED ||\
+ status == WM_CDM_NO_DISC)
+
+#define WM_CDS_DISC_READY(status)(status == WM_CDM_TRACK_DONE ||\
+ status == WM_CDM_PLAYING ||\
+ status == WM_CDM_FORWARD ||\
+ status == WM_CDM_PAUSED ||\
+ status == WM_CDM_STOPPED)
+
+#define WM_CDS_DISC_PLAYING(status)(status == WM_CDM_TRACK_DONE ||\
+ status == WM_CDM_PLAYING ||\
+ status == WM_CDM_FORWARD ||\
+ status == WM_CDM_PAUSED)
+#define WM_CDM_BACK 1
+#define WM_CDM_TRACK_DONE 1
+#define WM_CDM_PLAYING 2
+#define WM_CDM_FORWARD 3
+#define WM_CDM_PAUSED 4
+#define WM_CDM_STOPPED 5
+#define WM_CDM_EJECTED 6
+#define WM_CDM_DEVICECHANGED 9 /* deprecated */
+#define WM_CDM_NO_DISC 10
+#define WM_CDM_UNKNOWN 11
+#define WM_CDM_CDDAERROR 12
+#define WM_CDM_CDDAACK 0xF0
+
+#define WM_CDIN 0
+#define WM_CDDA 1
+
+#define WM_ENDTRACK 0
+
+#define WM_BALANCE_SYMMETRED 0
+#define WM_BALANCE_ALL_LEFTS -10
+#define WM_BALANCE_ALL_RIGHTS 10
+
+#define WM_VOLUME_MUTE 0
+#define WM_VOLUME_MAXIMAL 100
+
+int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
+ const char *sounddevice, const char *ctldevice );
+int wm_cd_destroy( void );
+
+int wm_cd_status( void );
+
+int wm_cd_getcurtrack( void );
+int wm_cd_getcurtracklen( void );
+int wm_cd_getcountoftracks( void );
+
+int wm_cd_gettracklen( int track );
+
+int wm_cd_play( int start, int pos, int end );
+int wm_cd_play_chunk( int start, int end, int realstart );
+int wm_cd_play_from_pos( int pos );
+int wm_cd_pause( void );
+int wm_cd_stop( void );
+int wm_cd_eject( void );
+int wm_cd_closetray( void );
+
+int wm_find_trkind( int, int, int );
+
+/*
+ * for vaild values see wm_helpers.h
+ */
+int wm_cd_set_verbosity( int );
+
+const char * wm_drive_vendor( void );
+const char * wm_drive_model( void );
+const char * wm_drive_revision( void );
+const char * wm_drive_device( void );
+
+/*
+ * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
+ * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
+ */
+int wm_cd_volume( int volume, int balance);
+
+/*
+ * please notice, that more OSs don't allow to read balance and volume
+ * in this case you get -1 for volume and WM_BALANCE_SYMMETRED for balance
+ */
+int wm_cd_getvolume( void );
+int wm_cd_getbalance( void );
+
+#endif /* WM_CDROM_H */
diff --git a/kscd/libwm/include/wm_cdtext.h b/kscd/libwm/include/wm_cdtext.h
new file mode 100644
index 00000000..e843ee95
--- /dev/null
+++ b/kscd/libwm/include/wm_cdtext.h
@@ -0,0 +1,105 @@
+#ifndef WM_CDTEXT_H
+#define WM_CDTEXT_H
+
+/***************************************************************************
+ wm_cdtext.h - description
+ -------------------
+ begin : Mon Feb 12 2001
+ copyright : (C) 2001 by Alex Kern
+ email : alex.kern@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+/*
+ * cdtext base structure and defines
+ */
+
+#define MAX_LENGHT_OF_CDTEXT_STRING 162 /* max 160 bytes + 2 * 0x00 by UNICODES */
+#define DATAFIELD_LENGHT_IN_PACK 12
+#define MAX_LANGUAGE_BLOCKS 8
+
+struct cdtext_pack_data_header {
+ unsigned char header_field_id1_typ_of_pack;
+ unsigned char header_field_id2_tracknumber;
+ unsigned char header_field_id3_sequence;
+ unsigned char header_field_id4_block_no;
+ unsigned char text_data_field[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char crc_byte1;
+ unsigned char crc_byte2;
+};
+
+typedef unsigned char cdtext_string[MAX_LENGHT_OF_CDTEXT_STRING];
+
+/* meke it more generic
+ it can be up to 8 blocks with different encoding */
+
+struct cdtext_info_block {
+ /* management */
+ unsigned char block_code;
+ unsigned char block_unicode; /* 0 - single chars, 1 - doublebytes */
+ unsigned char block_encoding; /* orange book -? */
+ cdtext_string* block_encoding_text;
+
+ /* variable part of cdtext */
+ cdtext_string* name;
+ cdtext_string* performer;
+ cdtext_string* songwriter;
+ cdtext_string* composer;
+ cdtext_string* arranger;
+ cdtext_string* message;
+ cdtext_string* UPC_EAN_ISRC_code;
+
+ /* fix part of cdtext */
+ unsigned char binary_disc_identification_info[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char binary_genreidentification_info[DATAFIELD_LENGHT_IN_PACK];
+ unsigned char binary_size_information[DATAFIELD_LENGHT_IN_PACK];
+};
+
+struct cdtext_info {
+ /* somethimes i get hunderts of bytes, without anyone valid pack
+ my CDU-561 for example */
+ int count_of_entries; /* one more because album need one too */
+ int count_of_valid_packs;
+ int count_of_invalid_packs;
+ int valid;
+
+ struct cdtext_info_block *blocks[MAX_LANGUAGE_BLOCKS];
+};
+
+#ifndef IGNORE_FEATURE_LIST
+
+struct feature_list_header {
+ unsigned char lenght_msb;
+ unsigned char lenght_1sb;
+ unsigned char lenght_2sb;
+ unsigned char lenght_lsb;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned char profile_msb;
+ unsigned char profile_lsb;
+};
+
+struct feature_descriptor_cdread {
+ unsigned char feature_code_msb;
+ unsigned char feature_code_lsb;
+ unsigned char settings;
+ unsigned char add_lenght;
+ unsigned char add_settings;
+ unsigned char reserved1;
+ unsigned char reserved2;
+ unsigned char reserved3;
+};
+
+#endif /* IGNORE_FEATURE_LIST */
+
+struct cdtext_info* wm_cd_get_cdtext( void );
+
+#endif /* WM_CDTEXT_H */
diff --git a/kscd/libwm/include/wm_config.h b/kscd/libwm/include/wm_config.h
new file mode 100644
index 00000000..b04027f4
--- /dev/null
+++ b/kscd/libwm/include/wm_config.h
@@ -0,0 +1,377 @@
+#ifndef WM_CONFIG_H
+#define WM_CONFIG_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ **********************************************************************
+ *
+ * This file consists of several parts. First, there's a generic,
+ * platform independent part. Set needed options there.
+ * The following parts are platform dependent. You may search for the
+ * names listed below and then set your OS specific options there.
+ * Don't be surprised, if there are no options for your OS. They aren't
+ * needed in any case.
+ *
+ * The default values should produce a functional WorkMan on every
+ * platform.
+ *
+ *********************
+ * Current platforms:
+ *********************
+ * BSD386
+ * FreeBSD
+ * HP-UX
+ * Irix (SGI)
+ * Linux
+ * News (Sony NewsOS)
+ * OpenBSD
+ * OSF1
+ * Sun (SunOS/Solaris, Sparc or x86)
+ * SVR4
+ * Ultrix
+ * AIX
+ *
+ * The above order corresponds with the order of the platform specific
+ * options below.
+ */
+
+#include <config.h>
+/******************************************************************
+ * generic options
+ ******************************************************************
+ ** ** *** **** ** **** ** * ** ** ** * **
+ * * * *** **** * * *** * ** ** *** * * * * * * **
+ * * *** **** ** *** ** ** ** * * ** * * **
+ * * * *** **** **** *** * ** ** *** * * * * *** **
+ * * * * ** **** * * ** ** **** ** * * *** **
+ ******************************************************************/
+
+/*
+ * This option is obvious. But please do not forget the original
+ * WorkMan version string if you need support.
+ */
+
+#define WORKMAN_NAME "LibWorkMan"
+#define WORKMAN_VERSION "1.4.0"
+
+/*
+ * If your CD-ROM drive closes its tray if the device is opened, then
+ * the next define can make WorkMans "Eject" button an "open/close"
+ * button. If it disturbs you, just comment it out.
+ *
+ * ### this is preliminary. It may have no effect for you ###
+ */
+#define CAN_CLOSE 1
+
+/*
+ * Define the following if you want the balance slider to
+ * decrease one channel's volume while increasing the other's
+ */
+/* #define SYMETRIC_BALANCE 1 */
+
+
+/*
+ * Define this if you want CDDA support. Supported systems are:
+ *
+ * - Solaris (2.4 or newer)
+ * --> Linux is on the way. Don't define it now. It would lead to errors only.
+ */
+/*#define BUILD_CDDA 1*/
+
+
+
+/******************************************************************
+ * BSD386
+ ******************************************************************
+ *** **** *** ******* ** **** **** ************
+ *** ** ** ****** * ***** ****** ** ** ** ***************
+ *** ****** **** ** *** ***** ***** *** ************
+ *** ** ****** ** * *** ******** ** ** ** ** ***********
+ *** **** *** *** ****** **** **** ************
+ ******************************************************************/
+#if defined(__bsdi__) || defined(__bsdi)
+
+/*
+ * This lets you use the SoundBlaster Mixer on BSD/386
+ */
+#define SOUNDBLASTER 1
+
+#define DEFAULT_CD_DEVICE "/dev/rsr2c"
+
+#endif /* __bsdi__ (BSD/386) */
+
+/******************************************************************
+ * FreeBSD
+ ******************************************************************
+ *** ** *** ** ** **** *** ***********
+ *** ****** ** ** ****** ****** ** ** ****** * **********
+ *** **** *** **** **** ****** **** ** *********
+ *** ****** ** ** ****** ****** ** ****** ** * **********
+ *** ****** ** ** ** ** **** *** ***********
+ ******************************************************************/
+#if defined(__FreeBSD__) || defined(__FreeBSD)
+
+#if __FreeBSD_version >= 500100
+#define DEFAULT_CD_DEVICE "/dev/acd0"
+#else
+#define DEFAULT_CD_DEVICE "/dev/acd0c"
+#endif
+
+#endif /* freebsd */
+
+/* DragonFly */
+#if defined(__DragonFly__)
+#define DEFAULT_CD_DEVICE "/dev/acd0c"
+#endif
+
+/******************************************************************
+ * NetBSD
+ ******************************************************************
+ *** *** ** ** ** **** *** *****************
+ *** ** ** ******** **** ** ** ****** * ****************
+ *** * * ** ****** **** ****** **** ** ***************
+ *** ** ** ******** **** ** ****** ** * ****************
+ *** *** ** **** **** **** *** *****************
+ ******************************************************************/
+#if defined(__NetBSD__) || defined(__NetBSD)
+
+#if defined(__i386__)
+ #define DEFAULT_CD_DEVICE "/dev/rcd0d"
+#else
+ #define DEFAULT_CD_DEVICE "/dev/rcd0c"
+#endif
+
+#endif /* netbsd */
+
+/******************************************************************
+ * HP-UX
+ ******************************************************************
+ *** ** ** ********* ** ** ** ***************************
+ *** ** ** ** ******** ** *** ****************************
+ *** ** *** ** ** **** *****************************
+ *** ** ** ************ ** *** ****************************
+ *** ** ** ************* *** ** ***************************
+ ******************************************************************/
+#if defined(hpux) || defined (__hpux)
+
+#define DEFAULT_CD_DEVICE "/dev/rscsi"
+
+#endif /* hpux */
+
+/******************************************************************
+ * Irix
+ ******************************************************************
+ *** ** *** ** ** *********************************
+ ***** **** ** **** ***** **********************************
+ ***** **** ***** ****** ***********************************
+ ***** **** ** **** ***** **********************************
+ *** ** ** ** ** ** *********************************
+ ******************************************************************/
+#if defined(sgi) || defined(__sgi)
+
+#define DEFAULT_CD_DEVICE "/dev/scsi/sc0d6l0"
+
+#endif /* sgi IRIX */
+
+/******************************************************************
+ * Linux
+ ******************************************************************
+ *** ****** ** *** ** ** ** ** ***********************
+ *** ******** **** ** ** ** *** ************************
+ *** ******** **** * * ** ** **** *************************
+ *** ******** **** ** ** ** *** ************************
+ *** ** ** *** *** *** ** ***********************
+ ******************************************************************/
+#if defined(__linux__)
+
+/*
+ * Uncomment the following line to have WorkMan send SCSI commands
+ * directly to the CD-ROM drive. If you have a SCSI drive you
+ * probably want this, but it will cause WorkMan to not work on IDE
+ * drives.
+ */
+/*#define LINUX_SCSI_PASSTHROUGH 1*/
+
+/*
+ * Which device should be opened by WorkMan at default?
+ */
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+
+/*
+ * Uncomment the following if you use the sbpcd or mcdx device driver.
+ * It shouldn't hurt if you use it on other devices. It'll be nice to
+ * hear from non-sbpcd (or mcdx) users if this is right.
+ */
+/*#define SBPCD_HACK 1*/
+
+/*
+ * Linux Soundcard support
+ * Disabled by default, because some people rely on it
+ */
+/* #define OSS_SUPPORT 1 */
+
+/*
+ * This has nothing to do with the above.
+ */
+
+/* #define CURVED_VOLUME */
+
+/*
+ * Uncomment the following if you want to try out a better responding
+ * WorkMan, especially with IDE drives. This may work with non-IDE
+ * drives as well. But it may be possible, that it doesn't work at all.
+ * If your drive/driver combination cannot handle the faster access,
+ * the driver will usually hang and you have to reboot your machine.
+ */
+/* #define FAST_IDE 1 */
+
+/*
+ * There are two alternative ways of checking a device containing a
+ * mounted filesystem. Define BSD_MOUNTTEST for the test using
+ * getmntent(). Undefine it for using the SVR4 ustat().
+ * I built in the choice, because it's not clear which method should
+ * be used in Linux. The ustat manpage tells us since 1995, that
+ * fstat() should be used, but I'm too dumb to do so.
+ */
+
+#define BSD_MOUNTTEST
+
+#endif /* __linux */
+
+/******************************************************************
+ * Sony NewsOS
+ ******************************************************************
+ *** *** ** ** ***** *** *****************************
+ *** ** ** ****** ***** ** ********************************
+ *** * * ** **** ** ** **** ******************************
+ *** ** ** ****** * * ****** ****************************
+ *** *** ** ** * *** *****************************
+ ******************************************************************/
+#if defined(__sony_news) || defined(sony_news)
+
+#define DEFAULT_CD_DEVICE "/dev/rsd/b0i6u0p2\0"
+
+#endif
+
+/******************************************************************
+ * OSF1
+ ******************************************************************
+ **** **** *** *** *** *******************************
+ *** ** ** ****** ****** ** *******************************
+ *** ** **** **** *** ***** *******************************
+ *** ** ****** ** **** ****** *******************************
+ **** **** *** *** ***** *****************************
+ ******************************************************************/
+#if defined(__osf__) || defined(__osf)
+
+/* not needed here, it's figured out by plat_osf1.c */
+/* #define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0" */
+
+#endif
+
+/******************************************************************
+ * SunOS/Solaris
+ ******************************************************************
+ **** *** ** ** *** ***************************************
+ *** ****** ** ** ** ***************************************
+ ***** **** ** ** * * ***************************************
+ ******* ** ** ** ** ***************************************
+ **** **** *** *** ***************************************
+ ******************************************************************/
+#if defined(sun) || defined(__sun)
+
+/*
+ * Define the following for Solaris 2.x
+ * If you don't want WorkMan to try to activate the SPARCstation 5
+ * internal audio input so you get sound from the workstation, comment
+ * out the CODEC define.
+ */
+
+#define SYSV 1
+#define CODEC 1
+#define DEFAULT_CD_DEVICE "/vol/dev/aliases/cdrom0"
+
+/*
+ * set the following to "SUNW,CS4231" for Sun and to "SUNW,sb16"
+ * for PC (with SoundBlaster 16) running Solaris x86
+ * (only important if you define CODEC above)
+ */
+#define SUN_AUD_DEV "SUNW,CS4231"
+/*#define SUN_AUD_DEV "SUNW,sbpro"*/
+
+
+#endif
+
+/******************************************************************
+ * SVR4
+ ******************************************************************
+ **** *** **** ** *** * ********************************
+ *** ****** **** ** ** ** * ********************************
+ ***** ***** ** *** *** *******************************
+ ******* *** ** *** ** ***** ********************************
+ **** ***** **** ** ***** ********************************
+ ******************************************************************/
+#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news)
+
+#define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * Ultrix
+ ******************************************************************
+ *** ** ** ***** ** *** ** ** ******************
+ *** ** ** ******* **** ** **** ***** *******************
+ *** ** ** ******* **** ***** ****** ********************
+ *** ** ** ******* **** ** **** ***** *******************
+ **** *** *** **** ** ** ** ** ******************
+ ******************************************************************/
+#if defined(ultrix) || defined(__ultrix)
+
+#endif
+
+/******************************************************************
+ * IBM AIX
+ ******************************************************************
+ **** *** ** ** *****************************************
+ *** ** **** ***** ******************************************
+ *** **** ****** *******************************************
+ *** ** **** ***** ******************************************
+ *** ** ** ** ** *****************************************
+ ******************************************************************/
+#if defined(AIXV3) || defined(__AIXV3)
+
+#define DEFAULT_CD_DEVICE "/dev/cd0"
+
+#endif /* IBM AIX */
+
+/******************************************************************/
+#endif /* WM_CONFIG_H */
+
+
+
+
+
+
+
+
diff --git a/kscd/libwm/include/wm_database.h b/kscd/libwm/include/wm_database.h
new file mode 100644
index 00000000..401f12bc
--- /dev/null
+++ b/kscd/libwm/include/wm_database.h
@@ -0,0 +1,42 @@
+#ifndef WM_DATABASE_H
+#define WM_DATABASE_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes for WorkMan database
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+
+#define WM_DB_SAVE_ERROR 1
+#define WM_DB_SAVE_DISABLED 2
+
+int wm_db_get_playnew( void );
+void split_workmandb( void );
+int save( void );
+void load( void );
+void load_settings( void );
+
+
+#endif /* WM_DATABASE_H */
diff --git a/kscd/libwm/include/wm_helpers.h b/kscd/libwm/include/wm_helpers.h
new file mode 100644
index 00000000..ad98a365
--- /dev/null
+++ b/kscd/libwm/include/wm_helpers.h
@@ -0,0 +1,109 @@
+#ifndef WM_HELPERS_H
+#define WM_HELPERS_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Here to be found: Prototypes. Including variable names to be easier
+ * to read.
+ * This is just one more step to a more modular and understandable code.
+ *
+ */
+
+/*
+ * LibWorkMan message levels. I'm not sure how to call them all and which
+ * use they should fulfill. This is not very urgent now, because there
+ * aren't many messages in LibWorkMan now.
+ */
+#define WM_MSG_LEVEL_NONE 0 /**/
+#define WM_MSG_LEVEL_ERROR 1 /**/
+#define WM_MSG_LEVEL_TWO 2
+#define WM_MSG_LEVEL_THREE 3
+#define WM_MSG_LEVEL_FOUR 4
+#define WM_MSG_LEVEL_INFO 5 /**/
+#define WM_MSG_LEVEL_SIX 6
+#define WM_MSG_LEVEL_VERB 7 /**/
+#define WM_MSG_LEVEL_EIGHT 8
+#define WM_MSG_LEVEL_DEBUG 9 /**/
+
+/*
+ * Message classes. This is somehow a definition of
+ * the message's source.
+ */
+
+#define WM_MSG_CLASS_PLATFORM 0x010
+#define WM_MSG_CLASS_SCSI 0x020
+#define WM_MSG_CLASS_CDROM 0x040
+#define WM_MSG_CLASS_DB 0x080
+#define WM_MSG_CLASS_MISC 0x100
+
+#define WM_MSG_CLASS_ALL 0xff0
+
+extern int wm_lib_verbosity;
+
+/*
+ * I did not know any better place...
+ */
+#ifdef DEBUG
+#define CHECKPOINT(t) fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, t );
+#else
+#define CHECKPOINT(t)
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef __linux__
+#include <signal.h>
+/* Linux doesn't have a SIGEMT */
+#if !defined( SIGEMT )
+# define SIGEMT SIGUNUSED
+#endif
+#endif /* linux */
+
+void freeup( char **x );
+void wm_strmcat( char **t, const char *s);
+void wm_strmcpy( char **t, const char *s );
+char * wm_strdup( char *s );
+/* Somebody's version query unsatisfied? */
+int wm_libver_major( void ); /* return major internal version number */
+int wm_libver_minor( void ); /* return minor internal version number */
+int wm_libver_pl( void ); /* return internal patchlevel number */
+char * wm_libver_name( void ); /* return internal name (LibWorkMan) */
+char * wm_libver_number( void ); /* returns string: "<major>.<minor>.<pl>" */
+char * wm_libver_string( void ); /* returns string: "<name> <number>" */
+char * wm_libver_date( void ); /* returns string: date of compilation */
+void wm_lib_set_verbosity( int level ); /* set verbosity level */
+int wm_lib_get_verbosity( void ); /* get verbosity level */
+void wm_lib_message( unsigned int level, const char *format, ... )
+#ifdef __GNUC__
+ __attribute__ ((format(printf,2,3)))
+#endif
+ ; /* put out a message on stderr */
+int wm_susleep( int usec );
+
+#endif /* WM_HELPERS_H */
diff --git a/kscd/libwm/include/wm_index.h b/kscd/libwm/include/wm_index.h
new file mode 100644
index 00000000..7c348b75
--- /dev/null
+++ b/kscd/libwm/include/wm_index.h
@@ -0,0 +1,36 @@
+#ifndef WM_INDEX_H
+#define WM_INDEX_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Prototypes for wm_index.c
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+int idx_find_entry( char *file, int ntracks, int *tracks,
+ int len, int fuzz, unsigned long *pos );
+int idx_delete_entry(char *file, int track, int fuzz, unsigned long pos );
+int idx_write_entry( char *file, int track, unsigned long pos );
+
+#endif /* WM_INDEX_H */
diff --git a/kscd/libwm/include/wm_platform.h b/kscd/libwm/include/wm_platform.h
new file mode 100644
index 00000000..edce8119
--- /dev/null
+++ b/kscd/libwm/include/wm_platform.h
@@ -0,0 +1,51 @@
+#ifndef WM_PLATFORM_H
+#define WM_PLATFORM_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * The platform interface
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+int wmcd_open( struct wm_drive *d );
+int wmcd_reopen( struct wm_drive *d );
+
+/*
+ * void keep_cd_open( void );
+ */
+int wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply );
+
+/****************************************
+ *
+ * The drive prototypes.
+ *
+ */
+extern struct wm_drive_proto generic_proto;
+extern struct wm_drive_proto sony_proto;
+extern struct wm_drive_proto toshiba_proto;
+
+#endif /* WM_PLATFORM_H */
diff --git a/kscd/libwm/include/wm_scsi.h b/kscd/libwm/include/wm_scsi.h
new file mode 100644
index 00000000..0acc62ef
--- /dev/null
+++ b/kscd/libwm/include/wm_scsi.h
@@ -0,0 +1,50 @@
+#ifndef WM_SCSI_H
+#define WM_SCSI_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * SCSI prototypes (scsi.c)
+ *
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+#define WM_ERR_SCSI_INQUIRY_FAILED -1
+
+int wm_scsi_mode_sense( struct wm_drive *d, unsigned char page,
+ unsigned char *buf );
+int sendscsi( struct wm_drive *d, void *buf,
+ unsigned int len, int dir,
+ unsigned char a0, unsigned char a1,
+ unsigned char a2, unsigned char a3,
+ unsigned char a4, unsigned char a5,
+ unsigned char a6, unsigned char a7,
+ unsigned char a8, unsigned char a9,
+ unsigned char a10, unsigned char a11 );
+ int wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+ char *model, char *rev );
+int wm_scsi_get_cdtext( struct wm_drive *d,
+ unsigned char **pp_buffer, int *p_buffer_length );
+
+#endif /* WM_SCSI_H */
diff --git a/kscd/libwm/include/wm_struct.h b/kscd/libwm/include/wm_struct.h
new file mode 100644
index 00000000..790de3b0
--- /dev/null
+++ b/kscd/libwm/include/wm_struct.h
@@ -0,0 +1,198 @@
+#ifndef WM_STRUCT_H
+#define WM_STRUCT_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+
+/*
+ * Structure for a single track. This is pretty much self-explanatory --
+ * one of these exists for each track on the current CD.
+ */
+struct wm_trackinfo
+{
+ char *songname; /* Name of song, dynamically allocated */
+ char *otherdb; /* Unrecognized info for this track */
+ char *otherrc;
+ int length; /* Length of track in seconds or Kbytes */
+ int start; /* Starting position (f+s*75+m*60*75) */
+ int volume; /* Per-track volume (1-32, 0 to disable) */
+ int track; /* Physical track number */
+ int section; /* Section number (0 if track not split) */
+ int contd; /* Flag: continuation of previous track */
+ int avoid; /* Flag: don't play this track. */
+ int data; /* Flag: data track */
+};
+
+/*
+ * Structure for internal playlist management. The internal playlist is
+ * simply the list of track ranges that are being played currently. This
+ * is built whenever the CD starts playing; it's used in normal and shuffle
+ * modes as well as playlist mode.
+ *
+ * The "starttime" element represents how much time has elapsed by the time
+ * we get to this entry. For instance, if the list begins with a 5-minute
+ * track and a 3-minute track, the third entry would have a starttime of 8
+ * minutes. This is used so that the elapsed play time can be displayed
+ * even in shuffle or playlist modes.
+ *
+ * The last member of the list has a start track of 0, and its starttime is
+ * the total playing time of the playlist (which will usually be overestimated,
+ * since we don't play leadouts in some cases.)
+ */
+struct wm_play
+{
+ int start; /* Start track, or 0 if end of list */
+ int end; /* last track plus 1 */
+ int starttime; /* Number of seconds elapsed previously */
+};
+
+/*
+ * Structure for playlists (as seen by the user.) This is simply a name
+ * followed by a zero-terminated list of track numbers to play. The list
+ * is terminated by a NULL name.
+ */
+struct wm_playlist
+{
+ char *name; /* Name of this playlist */
+ int *list; /* List of tracks */
+};
+
+struct wm_cdinfo
+{
+ char artist[84]; /* Artist's name */
+ char cdname[84]; /* Disc's name */
+ int ntracks; /* Number of tracks on the disc */
+ int curtrack;
+ int curtracklen;
+ int length; /* Total running time in seconds */
+ int autoplay; /* Start playing CD immediately */
+ int playmode; /* How to play the CD */
+ int volume; /* Default volume (1-32, 0 for none) */
+ struct wm_trackinfo *trk; /* struct wm_trackinfo[ntracks] */
+ struct wm_playlist *lists; /* User-specified playlists */
+ char *whichdb; /* Which database is this entry from? */
+ char *otherdb; /* Unrecognized lines from this entry */
+ char *otherrc;
+ char *user; /* Name of originating user */
+ unsigned int cddbid; /* CDDB-ID of the current disc */
+ struct cdinfo *next; /* For browsers, etc. */
+};
+
+/* The global variable "cd" points to the struct for the CD that's playing. */
+extern struct wm_cdinfo *cd;
+
+extern struct wm_playlist *new_playlist();
+
+#define WM_STR_GENVENDOR "Generic"
+#define WM_STR_GENMODEL "drive"
+#define WM_STR_GENREV "type"
+
+
+/*
+ * Drive descriptor structure. Used for access to low-level routines.
+ */
+struct wm_drive_proto
+{
+ int (*gen_init)();
+ int (*gen_close)();
+ int (*gen_get_trackcount)();
+ int (*gen_get_cdlen)();
+ int (*gen_get_trackinfo)();
+ int (*gen_get_drive_status)();
+ int (*gen_get_volume)();
+ int (*gen_set_volume)();
+ int (*gen_pause)();
+ int (*gen_resume)();
+ int (*gen_stop)();
+ int (*gen_play)();
+ int (*gen_eject)();
+ int (*gen_closetray)();
+ int (*gen_get_cdtext)();
+};
+
+struct wm_drive
+{
+ int cdda; /* cdda 1, cdin 0 */
+ const char *cd_device;
+ char *soundsystem;
+ char *sounddevice;
+ char *ctldevice;
+ int fd; /* File descriptor, if used by platform */
+ int cdda_slave; /* File descriptor for CDDA */
+
+ char *vendor; /* Vendor name */
+ char *model; /* Drive model */
+ char *revision; /* Revision of the drive */
+ void *aux; /* Pointer to optional platform-specific info */
+ void *daux; /* Pointer to optional drive-specific info */
+
+ struct wm_drive_proto *proto;
+};
+
+/*
+ * Structure for information of the usage of cddb.
+ */
+struct wm_cddb {
+ int protocol; /* 0-off, 1-cddbp, 2-http, 3-htproxy */
+ char cddb_server[84]; /* host.domain.name:port */
+ char mail_adress[84]; /* user@domain.name */
+ char path_to_cgi[84]; /* (/)path/to/cddb.cgi */
+ char proxy_server[84]; /* host.domain.name:port */
+};
+extern struct wm_cddb cddb;
+
+
+/*
+ * Each platform has to define generic functions, so may as well declare
+ * them all here to save space.
+ * These functions should never be seen outside libworkman. So I don't care
+ * about the wm_ naming convention here.
+ */
+int gen_init( struct wm_drive *d );
+int gen_close( struct wm_drive *d );
+int gen_get_trackcount(struct wm_drive *d, int *tracks);
+int gen_get_cdlen(struct wm_drive *d, int *frames);
+int gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
+int gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind );
+int gen_set_volume( struct wm_drive *d, int left, int right );
+int gen_get_volume( struct wm_drive *d, int *left, int *right );
+int gen_pause(struct wm_drive *d);
+int gen_resume(struct wm_drive *d);
+int gen_stop(struct wm_drive *d);
+int gen_play(struct wm_drive *d, int start, int end, int realstart);
+int gen_eject(struct wm_drive *d);
+int gen_closetray(struct wm_drive *d);
+int gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght);
+
+int find_drive_struct(const char *vendor, const char *model, const char *rev);
+
+
+struct cdtext_info* get_glob_cdtext(struct wm_drive*, int);
+void free_cdtext(void);
+const char* gen_status(int);
+
+#endif /* WM_STRUCT_H */
diff --git a/kscd/libwm/include/wm_version.h b/kscd/libwm/include/wm_version.h
new file mode 100644
index 00000000..82e500c5
--- /dev/null
+++ b/kscd/libwm/include/wm_version.h
@@ -0,0 +1,35 @@
+#ifndef WM_VERSION_H
+#define WM_VERSION_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Version information
+ *
+ */
+
+#define WM_LIBVER_MAJOR 1
+#define WM_LIBVER_MINOR 4
+#define WM_LIBVER_PL 3
+#define WM_LIBVER_NAME "LibWorkMan"
+
+#endif /* WM_VERSION_H */
diff --git a/kscd/libwm/include/workman.h b/kscd/libwm/include/workman.h
new file mode 100644
index 00000000..e7c3807d
--- /dev/null
+++ b/kscd/libwm/include/workman.h
@@ -0,0 +1,52 @@
+#ifndef WORKMAN_H
+#define WORKMAN_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * all-in-one libworkman include file.
+ *
+ */
+
+
+/*
+ * wm_config should always be included first
+ */
+#include "wm_config.h"
+
+#include "workman_defs.h"
+#ifdef BUILD_CDDA
+#include "wm_cdda.h"
+#endif
+#include "wm_cddb.h"
+#include "wm_cdinfo.h"
+#include "wm_cdrom.h"
+#include "wm_database.h"
+#include "wm_helpers.h"
+#include "wm_index.h"
+#include "wm_platform.h"
+#include "wm_scsi.h"
+#include "wm_struct.h"
+#include "wm_cdtext.h"
+
+#endif /* WORKMAN_H */
+
diff --git a/kscd/libwm/include/workman_defs.h b/kscd/libwm/include/workman_defs.h
new file mode 100644
index 00000000..9da47f8f
--- /dev/null
+++ b/kscd/libwm/include/workman_defs.h
@@ -0,0 +1,30 @@
+#ifndef WORKMAN_DEFS_H
+#define WORKMAN_DEFS_H
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player program
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * #defined CONSTANTS
+ *
+ * Too bad this file seems to be so empty...
+ *
+ */
+
+#include "wm_version.h"
+
+#endif /* WORKMAN_DEFS_H */
diff --git a/kscd/libwm/index.c b/kscd/libwm/index.c
new file mode 100644
index 00000000..e15312b7
--- /dev/null
+++ b/kscd/libwm/index.c
@@ -0,0 +1,383 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Maintain an external index file for a WorkMan CD database.
+ * Uses the Berkeley libdb library, available from ftp.cs.berkeley.edu.
+ */
+
+#ifdef LIBDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <db.h>
+#include <fcntl.h>
+#include <string.h>
+#include <netinet/in.h> /* for htonl() */
+#include "include/wm_config.h"
+#include "include/wm_index.h"
+
+extern int suppress_locking;
+
+/*
+ * idx_find_entry()
+ *
+ * Find an entry in the index file.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * ntracks Number of tracks on the CD we're looking for.
+ * tracks Array of track start times.
+ * len CD length in frames.
+ * fuzz Fuzz factor (tolerance value).
+ * pos Pointer to return value.
+ *
+ * Output:
+ * 1 No matching record found.
+ * 0 Record found; *pos contains offset into text file.
+ * -1 Index file out of date or inaccessible, or another error.
+ */
+int
+idx_find_entry( char *file, int ntracks, int *tracks,
+ int len, int fuzz, unsigned long *pos )
+{
+ unsigned long dbpos;
+ char *indexname = NULL, keyval[8];
+ int c;
+ FILE *text;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * First, see if the text file is accessible. Lock it if so.
+ */
+ text = fopen(file, "r");
+ if (text == NULL)
+ return (-1);
+ if ((c = getc(text)) == EOF)
+ {
+ fclose(text);
+ return (-1);
+ }
+ if (! suppress_locking)
+ if (lockit(fileno(text), F_RDLCK))
+ {
+ fclose(text);
+ return (-1);
+ }
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ {
+ fclose(text);
+ return (-1);
+ }
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+ bti.flags = 0;
+ bti.cachesize = 0;
+ bti.minkeypage = bti.maxkeypage = 0;
+ bti.psize = bti.lorder = 0;
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDONLY, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ {
+ fclose(text);
+ return (-1);
+ }
+
+ /*
+ * Search for the first matching entry.
+ */
+ sprintf(keyval, "%07d", tracks[ntracks - 1] - fuzz);
+ key.data = keyval;
+ key.size = 7;
+ if (c = (index->seq)(index, &key, &data, R_CURSOR))
+ {
+ (index->close)(index);
+ fclose(text);
+ return (c);
+ }
+
+ /*
+ * Now loop through all the possible matches, collecting them into
+ * memory.
+ */
+ do {
+ char tracksline[750], *s;
+ int i, val;
+
+ /* Hit the end of the valid entries? */
+ sscanf(key.data, "%d", &val);
+ if (val > tracks[ntracks - 1] + fuzz)
+ break;
+
+ dbpos = ntohl(*((unsigned long *) data.data));
+ if (fseek(text, dbpos, 0))
+ break;
+
+ fgets(tracksline, sizeof(tracksline), text);
+ if (strncmp(tracksline, "tracks ", 7))
+ break;
+ (void) strtok(tracksline, " \t");
+
+ /* Got a valid tracks line. See if it matches the CD. */
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ break;
+ if (atoi(s) != ntracks)
+ continue;
+
+ for (i = 0; i < ntracks; i++)
+ {
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ break;
+ val = atoi(s);
+ if (val + fuzz < tracks[i] || val - fuzz > tracks[i])
+ break;
+ }
+ if (i != ntracks)
+ continue;
+
+ s = strtok(NULL, " \t");
+ if (s == NULL)
+ continue;
+ val = atoi(s);
+ if (val + fuzz / 75 < len / 75 || val + fuzz / 75 > len / 75)
+ continue;
+
+ /* XXX - add to sorted list! */
+ *pos = dbpos;
+ (index->close)(index);
+ fclose(text);
+ return (0);
+ } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+ if (c == 0)
+ {
+ /* An error. */
+ (index->close)(index);
+ fclose(text);
+ return (-1);
+ }
+
+ (index->close)(index);
+ fclose(text);
+ return (1);
+}
+
+/*
+ * idx_delete_entry()
+ *
+ * Delete an entry from the index file.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * track Last track's start time (database key).
+ * fuzz Fuzz factor (tolerance value).
+ * pos Position of CD in database file.
+ *
+ * Output:
+ * 1 No matching record found.
+ * 0 Record deleted.
+ * -1 Index file out of date or inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_delete_entry(char *file, int track, int fuzz, unsigned long pos )
+{
+ unsigned long dbpos;
+ char *indexname = NULL, keyval[8];
+ int c, status;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ return (-1);
+
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+
+ bti.flags = 0;
+ bti.cachesize = 0;
+ bti.minkeypage = bti.maxkeypage = 0;
+ bti.psize = bti.lorder = 0;
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ return (-1);
+
+ /*
+ * Search for the first matching entry.
+ */
+ sprintf(keyval, "%07d", track - fuzz);
+ key.data = keyval;
+ key.size = 7;
+ if (c = (index->seq)(index, &key, &data, R_CURSOR))
+ {
+ /*
+ * Nothing matched!
+ */
+ (index->close)(index);
+ return (c);
+ }
+
+ /*
+ * Look for the entry the user wants to delete.
+ */
+ do {
+ int val;
+
+ /* Hit the end of the valid entries? */
+ sscanf(key.data, "%d", &val);
+ if (val > track + fuzz)
+ break;
+
+ /* Is this the entry we want? */
+ if (pos == ntohl(*((unsigned long *) data.data)))
+ {
+ /*
+ * Yep! Delete it.
+ */
+ status = (index->del)(index, &key, R_CURSOR);
+ (index->close)(index);
+ return (status);
+ }
+ } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+ if (c == 0)
+ {
+ /* An error. */
+ (index->close)(index);
+ return (-1);
+ }
+
+ (index->close)(index);
+ return (1);
+}
+
+/*
+ * idx_write_entry()
+ *
+ * Write out an index file entry.
+ *
+ * Input:
+ * file Name of database file (text version).
+ * track Start time of last track (database key).
+ * pos Position of entry in text file.
+ *
+ * Output:
+ * 0 Record written.
+ * -1 Index file inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_write_entry( char *file, int track, unsigned long pos )
+{
+ char *indexname, keyval[8];
+ int status;
+ DB *index;
+ DBT key, data;
+ BTREEINFO bti;
+
+ /*
+ * Open the index file.
+ */
+ indexname = malloc(strlen(file) + sizeof(".ind"));
+ if (indexname == NULL)
+ return (-1);
+
+ strcpy(indexname, file);
+ strcat(indexname, ".ind");
+
+ bti.flags = R_DUP;
+ bti.cachesize = 0;
+ bti.minkeypage = 0;
+ bti.maxkeypage = 0;
+ bti.psize = 0;
+ bti.lorder = 4321; /* network byte order */
+ bti.compare = NULL;
+ bti.prefix = NULL;
+ index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+ free(indexname);
+ if (index == NULL)
+ return (-1);
+
+ /*
+ * Create a new key and value.
+ */
+ pos = htonl(pos);
+ data.data = &pos;
+ data.size = sizeof(pos);
+ key.data = keyval;
+ key.size = 7;
+
+ sprintf(keyval, "%07d", track);
+
+ status = (index->put)(index, &key, &data, 0);
+
+ (index->close)(index);
+ return (status);
+}
+
+#else /* LIBDB */
+
+int
+idx_find_entry()
+{
+ return (1); /* no record found; text file will be searched. */
+}
+
+int
+idx_delete_entry()
+{
+ return (0);
+}
+
+int
+idx_write_entry()
+{
+ return (0);
+}
+
+#endif /* LIBDB */
diff --git a/kscd/libwm/plat_aix.c b/kscd/libwm/plat_aix.c
new file mode 100644
index 00000000..93b596a1
--- /dev/null
+++ b/kscd/libwm/plat_aix.c
@@ -0,0 +1,489 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * plat_aix - AIX 4.x IDE and SCSI support 16 Dec 1998
+ *
+ * AIX 4.x Port: Erik O'Shaughnessy
+ * Original AIX IDE Code: Cloyce Spradling (xmcd libdi_d/aixioc.c )
+ *
+ * Taken from the ascd distribution.
+ *
+ */
+
+
+#if defined(AIXV3) || defined(__AIXV3)
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdrom.h>
+#include <sys/devinfo.h>
+#include <sys/scsi.h>
+#include <sys/scdisk.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define LEADOUT 0xaa
+
+int min_volume = 128;
+int max_volume = 255;
+
+
+
+/* NAME: gen_init
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return 0;
+} /* gen_init() */
+
+
+/* NAME: wmcd_open
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ int fd;
+
+ if( ! d )
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if(d->fd > -1) /* device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return 0;
+ }
+
+ if( d->cd_device == (char *)NULL )
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ if( (fd = openx(d->cd_device,O_RDONLY,NULL,SC_SINGLE)) < 0 )
+ {
+ perror("openx");
+ return -6;
+ /* return 1 */
+ }
+
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+ d->init(d);
+ return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/* NAME: wm_scsi
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+wm_scsi(struct wm_drive *d,
+ uchar_t *cdb, int cdblen,
+ void *retbuf, int retbuflen,
+ int getreply)
+{
+ return 0;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/* NAME: gen_get_drive_status
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_drive_status(struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos,
+ int *track,
+ int *index)
+{
+ struct cd_audio_cmd cmd;
+
+ *mode = WM_CDM_EJECTED;
+
+ if(d->fd < 0)
+ switch( wmcd_open(d) )
+ {
+ case -1:
+ return -1;
+ case 1:
+ return 0;
+ }
+
+ cmd.audio_cmds = CD_INFO_AUDIO;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ switch(cmd.status)
+ {
+ case CD_PLAY_AUDIO:
+ *mode = WM_CDM_PLAYING;
+ *track = cmd.indexing.info_audio.current_track;
+ *index = cmd.indexing.info_audio.current_index;
+ *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+ cmd.indexing.info_audio.current_secs * 75 +
+ cmd.indexing.info_audio.current_frames;
+ break;
+
+ case CD_PAUSE_AUDIO:
+ *mode = WM_CDM_PAUSED;
+ *track = cmd.indexing.info_audio.current_track;
+ *index = cmd.indexing.info_audio.current_index;
+ *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+ cmd.indexing.info_audio.current_secs * 75 +
+ cmd.indexing.info_audio.current_frames;
+
+ break;
+ case CD_NO_AUDIO: /* no play audio in progress */
+ case CD_COMPLETED: /* play operation completed successfully */
+ case CD_STATUS_ERROR: /* invalid status or play stopped due to err */
+ case CD_NOT_VALID: /* audio status is invalid or not supported */
+ *mode = WM_CDM_STOPPED;
+ break;
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+
+ return 0;
+} /* gen_get_drive_status() */
+
+
+
+/* NAME: gen_get_trackcount
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_trackcount(struct wm_drive *d,int *tracks)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_TRK_INFO_AUDIO;
+ cmd.msf_flag = 0;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("DKAUDIO");
+ return -1;
+ }
+
+ *tracks = cmd.indexing.track_index.last_track;
+
+ return 0;
+} /* gen_get_trackcount() */
+
+/* NAME: gen_get_trackinfo
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_GET_TRK_MSF;
+ cmd.msf_flag = 1;
+
+ cmd.indexing.track_msf.track = track;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ *startframe = cmd.indexing.track_msf.mins * 60 * 75 +
+ cmd.indexing.track_msf.secs * 75 +
+ cmd.indexing.track_msf.frames;
+
+ *data = 0;
+
+ return 0;
+} /* gen_get_trackinfo() */
+
+/* NAME: gen_get_cdlen
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+ int tmp;
+
+ return gen_get_trackinfo(d,LEADOUT,&tmp,frames);
+} /* gen_get_cdlen() */
+
+
+/* NAME: gen_play
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_play(struct wm_drive *d,int start,int end)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_PLAY_AUDIO;
+ cmd.msf_flag = 1;
+
+ cmd.indexing.msf.first_mins = start / (60*75);
+ cmd.indexing.msf.first_secs = (start % (60*75)) / 75;
+ cmd.indexing.msf.first_frames = start % 75;
+
+ cmd.indexing.msf.last_mins = end / (60*75);
+ cmd.indexing.msf.last_secs = (end % (60*75)) / 75;
+ cmd.indexing.msf.last_frames = end % 75;
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("DKAUDIO:CD_PLAY_AUDIO");
+ return -1;
+ }
+ return 0;
+} /* gen_play() */
+
+/* NAME: gen_pause
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_PAUSE_AUDIO;
+
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_pause() */
+
+
+/* NAME: gen_resume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_RESUME_AUDIO;
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_resume() */
+
+/* NAME: gen_stop
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_STOP_AUDIO;
+ return ioctl(d->fd,DKAUDIO,&cmd);
+} /* gen_stop() */
+
+/* NAME: gen_eject
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return ioctl(d->fd,DKEJECT,NULL);
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+int
+scale_volume(int vol,int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+int
+unscale_volume(int vol,int max)
+{
+ int n;
+ n = ( vol - min_volume ) * max_volume / (max - min_volume);
+ return (n <0)?0:n;
+}
+
+/* NAME: gen_set_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_set_volume(struct wm_drive *d,int left,int right)
+{
+ struct cd_audio_cmd cmd;
+
+ cmd.audio_cmds = CD_SET_VOLUME;
+ cmd.volume_type = CD_VOLUME_CHNLS;
+
+ cmd.out_port_0_vol = scale_volume(left,100);
+ cmd.out_port_1_vol = scale_volume(right,100);
+
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ {
+ perror("CD_SET_VOLUME");
+ return -1;
+ }
+
+ return 0;
+} /* gen_set_volume() */
+
+/* NAME: gen_get_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+int
+gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+ struct cd_audio_cmd cmd;
+ int l,r;
+
+ fprintf(stderr,"gen_get_volume\n");
+
+ cmd.audio_cmds = CD_INFO_AUDIO;
+ if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+ return -1;
+
+ *left = unscale_volume(cmd.out_port_0_vol,100);
+ *right = unscale_volume(cmd.out_port_1_vol,100);
+
+ return 0;
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+
+#endif /* _AIX */
+
+
+
+
+
diff --git a/kscd/libwm/plat_bsd386.c b/kscd/libwm/plat_bsd386.c
new file mode 100644
index 00000000..492d394c
--- /dev/null
+++ b/kscd/libwm/plat_bsd386.c
@@ -0,0 +1,510 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * BSD/386-specific drive control routines.
+ */
+
+static char plat_bsd386_id[] = "$Id$";
+
+#if defined(__bsdi__) || defined(__bsdi)
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * The following is included from the Linux module. However, I didn't
+ * see a check here if the CD to be ejected is mounted...
+ */
+#if defined(BSD_MOUNTTEST)
+ #include <mntent.h>
+#endif
+
+
+#include <sys/time.h>
+#include <string.h>
+#include <cdrom.h>
+#ifdef SOUNDBLASTER
+# include <sys/soundcard.h>
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+/*
+ * Since we can't sense the drive type with libcdrom anyway, and since the
+ * library doesn't provide "pause" or "resume" functions, use the daux field
+ * to point to the frame number at which we paused.
+ */
+struct pause_info
+{
+ int frame;
+ int endframe;
+};
+
+#define PAUSE_FRAME (((struct pause_info *) d->daux)->frame)
+#define END_FRAME (((struct pause_info *) d->daux)->endframe)
+#define CUR_CD ((struct cdinfo *) d->aux)
+
+void *malloc();
+
+#ifdef SOUNDBLASTER
+ int min_volume = 0;
+ int max_volume = 100;
+ int min_volume_drive = 10; /* Toshiba drive does low values. */
+ int max_volume_drive = 255;
+#else
+ int min_volume = 10;
+ int max_volume = 255;
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*-----------------------------------------------------------------------*
+ * Open the CD device. We can't determine the drive type under BSD/386.
+ *-----------------------------------------------------------------------*/
+int
+wmcd_open(struct wm_drvie *d)
+{
+ void *aux = NULL, *daux = NULL;
+ int fd = -1;
+
+ if (d->aux) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (aux=%d)]\n", d->aux);
+ return (0);
+ }
+
+ if ((aux = cdopen(d->cd_device)) == NULL)
+ {
+ fprintf(stderr, "No cdrom found by libcdrom\n");
+ return (-6);
+ }
+
+ if ((daux = malloc(sizeof(struct pause_info))) == NULL)
+ return (-1);
+
+#ifdef SOUNDBLASTER
+ fd = open("/dev/mixer", O_RDWR, 0);
+#endif
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->aux = aux;
+ d->daux = daux;
+ d->fd = fd;
+ PAUSE_FRAME = 0;
+ END_FRAME = 0;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ /* Don't know how to do SCSI passthrough... */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd); /* close mixer if open */
+ d->fd = -1;
+ cdclose( d->aux ); /* close the cdrom drive! */
+ d->aux = NULL;
+ free(d->daux);
+ d->daux = NULL;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+#define DOPOS \
+ *pos = status.abs_frame; \
+*track = status.track_num; \
+*index = status.index_num
+
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cdstatus status;
+ extern enum wm_cd_modes cur_cdmode;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->aux == NULL)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (cdstatus (CUR_CD, &status) < 0)
+ {
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ return (0);
+ }
+
+ switch (status.state)
+ {
+ case cdstate_playing:
+ *mode = WM_CDM_PLAYING;
+ DOPOS;
+ break;
+
+ case cdstate_stopped:
+ /* the MITSUMI drive doesn't have a "paused" state,
+ so it always comes here and not to the paused section.
+ The PAUSE_FRAME stuff below (in gen_pause())
+ fakes out the paused state. */
+ if (oldmode == WM_CDM_PLAYING)
+ {
+ *mode = WM_CDM_TRACK_DONE;
+ break;
+ } else if (cur_cdmode != WM_CDM_PAUSED) {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ break;
+ }
+ /* fall through if paused */
+
+ case cdstate_paused:
+ /* the SCSI2 code in the cdrom library only pauses with
+ cdstop(); it never truly stops a disc (until an in-progress
+ play reaches the end). So it always comes here. */
+ if (cur_cdmode == WM_CDM_STOPPED)
+ {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ break;
+ }
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ DOPOS;
+ } else {
+ *mode = WM_CDM_STOPPED;
+ DOPOS;
+ }
+ break;
+
+ default:
+ *mode = WM_CDM_STOPPED;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ *tracks = CUR_CD->ntracks;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ *data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0;
+ *startframe = CUR_CD->tracks[track - 1].start_frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ *frames = CUR_CD->total_frames;
+
+ return (0);
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ END_FRAME = end;
+ if (cdplay(d->aux, start, end) < 0)
+ return (-1);
+ else
+ return (0);
+} /* gen_play() */
+
+/*--------------------------------------------------------------------*
+ * Pause the CD. This is a bit of a trick since there's no cdpause()
+ * function in the library. We fake it by saving the frame number
+ * and stopping.
+ *--------------------------------------------------------------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+ struct cdstatus status;
+
+ if (cdstatus(d->aux, &status) < 0)
+ return (-1);
+ if (status.state != cdstate_playing)
+ PAUSE_FRAME = CUR_CD->tracks[0].start_frame;
+ else
+ PAUSE_FRAME = status.abs_frame;
+ if (cdstop(d->aux) < 0)
+ return (-1);
+
+ return (0);
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+ int status;
+
+ status = (d->play)(d, PAUSE_FRAME, END_FRAME);
+ PAUSE_FRAME = 0;
+ return (status);
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+ return cdstop(d->aux);
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+ cdeject(d->aux);
+ cdclose(d->aux);
+ d->aux = NULL;
+ free(d->daux);
+ d->daux = NULL;
+
+ if (d->fd >= 0)
+ close(d->fd); /* close mixer */
+ d->fd = -1;
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if (!cdload(d->aux))
+ return(0);
+ return(-1);
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*------------------------------------------------------------------------*
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+ /* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume(int cd_vol, int max)
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ int level;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+ level = right << 8 | left;
+
+ /* Send a Mixer IOCTL */
+ if (d->fd >= 0)
+ (void) ioctl(d->fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &level);
+#ifdef notnow
+ /* NOTE: the cdvolume2() call is an addition to the cdrom library.
+ Pick it up from the archives on bsdi.com */
+ cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left,
+ right < 0 ? 0 : right > 255 ? 255 : right);
+
+#endif
+ return (0);
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ int level;
+
+ /* Most systems can't seem to do this... */
+ *left = *right = -1;
+
+ /* Send a Mixer IOCTL */
+ if (d->fd >= 0) {
+ if (ioctl(d->fd, MIXER_READ(SOUND_MIXER_VOLUME), &level) == 0) {
+ *left = unscale_volume((level & 0xff) & 0xff, 100);
+ *right = unscale_volume((level >> 8) & 0xff, 100);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif /* __bsdi__ */
diff --git a/kscd/libwm/plat_freebsd.c b/kscd/libwm/plat_freebsd.c
new file mode 100644
index 00000000..b1028a75
--- /dev/null
+++ b/kscd/libwm/plat_freebsd.c
@@ -0,0 +1,560 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * plat_freebsd.c
+ *
+ * FreeBSD-specific drive control routines.
+ *
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__FreeBSD__) || defined(__FreeBSD) || defined(__NetBSD__) || defined (__NetBSD) || defined(__DragonFly__)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+
+#if defined(__OpenBSD__)
+# define MSF_MINUTES 1
+# define MSF_SECONDS 2
+# define MSF_FRAMES 3
+# include <sys/scsiio.h>
+# include "/sys/scsi/scsi_all.h"
+# include "/sys/scsi/scsi_cd.h"
+#elif defined(__NetBSD__)
+#include <sys/scsiio.h>
+#include <dev/scsipi/scsipi_cd.h>
+#else
+# define LEFT_PORT 0
+# define RIGHT_PORT 1
+# if defined(__FreeBSD_version) && __FreeBSD_version < 300000
+# include <scsi.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int min_volume = 10;
+int max_volume = 255;
+
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+
+/*-------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *-------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ find_drive_struct(vendor, model, rev);
+
+ /*(d->init)(d); */
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ if(status == -EACCES || status == 1)
+ return status;
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct ioc_read_subchannel sc;
+ struct cd_sub_channel_info scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.address_format = CD_MSF_FORMAT;
+ sc.data_format = CD_CURRENT_POSITION;
+ sc.track = 0;
+ sc.data_len = sizeof(scd);
+ sc.data = (struct cd_sub_channel_info *)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc))
+ {
+ /*
+ * #ifdef __NetBSD__
+ *
+ * Denis Bourez <denis@rsn.fdn.fr> told me, that closing the
+ * device is mandatory for FreeBSD, too.
+ */
+ /* we need to release the device so the kernel will notice
+ reloaded media */
+ (void) close(d->fd);
+ d->fd = -1;
+ /*
+ * #endif
+ */
+ return (0); /* ejected */
+ }
+
+ switch (scd.header.audio_status)
+ {
+ case CD_AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+ scd.what.position.absaddr.msf.second * 75 +
+ scd.what.position.absaddr.msf.frame;
+ *track = scd.what.position.track_number;
+ *index = scd.what.position.index_number;
+ break;
+
+ case CD_AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CD_AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CD_AS_NO_STATUS:
+ case 0:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct ioc_toc_header hdr;
+
+ if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+ return (-1);
+
+ *tracks = hdr.ending_track - hdr.starting_track + 1;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*-----------------------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ *-----------------------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct ioc_read_toc_entry toc;
+ struct cd_toc_entry toc_buffer;
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.address_format = CD_MSF_FORMAT;
+ toc.starting_track = track;
+ toc.data_len = sizeof(toc_buffer);
+ toc.data = &toc_buffer;
+
+ if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+ return (-1);
+
+ *data = ((toc_buffer.control & 0x4) != 0);
+
+ *startframe = toc_buffer.addr.msf.minute*60*75 +
+ toc_buffer.addr.msf.second * 75 +
+ toc_buffer.addr.msf.frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ struct ioc_toc_header hdr;
+ int status;
+
+#define LEADOUT 0xaa /* see scsi.c. what a hack! */
+ return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end, int realstart)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = start / (60*75);
+ msf.start_s = (start % (60*75)) / 75;
+ msf.start_f = start % 75;
+ msf.end_m = end / (60*75);
+ msf.end_s = (end % (60*75)) / 75;
+ msf.end_f = end % 75;
+
+ if (ioctl(d->fd, CDIOCSTART))
+ return (-1);
+
+ if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDIOCPAUSE));
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDIOCRESUME));
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCSTOP));
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct statfs buf;
+ int rval;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (fstatfs(stbuf.st_rdev, &buf) == 0)
+ return (-3);
+
+ rval = ioctl(d->fd, CDIOCALLOW);
+
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCEJECT);
+
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCPREVENT);
+
+ (void) close(d->fd);
+
+ return rval;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ *---------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct ioc_vol vol;
+
+ if (left < 0) /* don't laugh, I saw this happen once! */
+ left = 0;
+ if (right < 0)
+ right = 0;
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&vol, sizeof(vol));
+
+ vol.vol[LEFT_PORT] = left;
+ vol.vol[RIGHT_PORT] = right;
+
+ if (ioctl(d->fd, CDIOCSETVOL, &vol))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct ioc_vol vol;
+
+ if (d->fd >= 0)
+ {
+ bzero((char *)&vol, sizeof(vol));
+
+ if (ioctl(d->fd, CDIOCGETVOL, &vol))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(vol.vol[LEFT_PORT], 100);
+ *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+ }
+ } else {
+ *left = *right = -1;
+ }
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/plat_hpux.c b/kscd/libwm/plat_hpux.c
new file mode 100644
index 00000000..53aaee40
--- /dev/null
+++ b/kscd/libwm/plat_hpux.c
@@ -0,0 +1,358 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * HP/UX-specific drive control routines.
+ */
+
+#if defined(hpux) || defined(__hpux)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * this is for glibc 2.x which the ust structure in
+ * ustat.h not stat.h
+ */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/scsi.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd, flag = 1;
+ static int warned = 0;
+ /* unsigned ? */
+ char vendor[32], model[32], rev[32];
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = open(d->cd_device, O_RDWR);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Prepare the device to receive raw SCSI commands. */
+ if (ioctl(d->fd, SIOC_CMD_MODE, &flag) < 0)
+ {
+ fprintf(stderr, "%s: SIOC_CMD_MODE: true: %s\n",
+ d->cd_device, strerror(errno));
+ /*exit(1);*/
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ /* Default drive is the HP one, which might not respond to INQUIRY */
+ strcpy(&vendor, "TOSHIBA");
+ strcpy(&model, "XM-3301");
+ rev[0] = '\0';
+ wm_scsi_get_drive_type(d, vendor, model, rev);
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+#ifdef SIOC_IO
+ struct sctl_io cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cdb_length = cdblen;
+ cmd.data = retbuf;
+ cmd.data_length = retbuflen;
+ cmd.max_msecs = 1000;
+ cmd.flags = getreply ? SCTL_READ : 0;
+ memcpy(cmd.cdb, cdb, cdblen);
+
+ return (ioctl(d->fd, SIOC_IO, &cmd));
+#else
+ /* this code, for pre-9.0, is BROKEN! ugh. */
+ char reply_buf[12];
+ struct scsi_cmd_parms cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.clock_ticks = 500;
+ cmd.cmd_mode = 1;
+ cmd.cmd_type = cdblen;
+ memcpy(cmd.command, cdb, cdblen);
+ if (ioctl(d->fd, SIOC_SET_CMD, &cmd) < 0)
+ return (-1);
+
+ if (! retbuf || ! retbuflen)
+ (void) read(d->fd, reply_buf, sizeof(reply_buf));
+ else if (getreply)
+ {
+ if (read(d->fd, retbuf, retbuflen) < 0)
+ return (-1);
+ }
+ else
+ if (write(d->fd, retbuf, retbuflen) < 0)
+ return (-1);
+
+ return (0);
+#endif
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode, int *mode,
+ int *pos, int *track, int *index )
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks )
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int *track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ return (wm_scsi2_closetray(d));
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested ! */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+
+#endif
+
+
diff --git a/kscd/libwm/plat_irix.c b/kscd/libwm/plat_irix.c
new file mode 100644
index 00000000..7f1e3fc7
--- /dev/null
+++ b/kscd/libwm/plat_irix.c
@@ -0,0 +1,474 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * IRIX specific.
+ *
+ * Taken from the kscd distribution
+ *
+ * Paul Kendall
+ * paul@orion.co.nz, or
+ * paul@kcbbs.gen.nz
+ */
+
+#if defined(sgi) || defined(__sgi)
+
+#include "include/wm_config.h"
+
+/*
+ * Yes, it was designed for WorkMan 1.4b3
+ * Because I did start over from 1.3a, I disable it here.
+ * There is no guarantee of getting working code by defining
+ * CDDA yourself.
+ *
+ */
+#undef CDDA
+/*#define CDDA*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sigfpe.h>
+#include <dmedia/cdaudio.h>
+#include <dmedia/audio.h>
+#include <errno.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+#ifdef CDDA
+static int playing = STOPPED;
+static CDPLAYER *icd;
+static CDPARSER *icdp;
+static CDFRAME cdbuf[12];
+static ALport audioport;
+static ALconfig aconfig;
+static struct itimerval audiotimer = { {0,0}, {0,25000} };
+static int cdtrack=0;
+static int cdframe=0;
+static int cdstopframe=0;
+
+/*
+ * Platform specific internal functions for CDDA
+ */
+void
+cbprognum(void *arg, CDDATATYPES type, CDPROGNUM* prognum)
+{
+ cdtrack = prognum->value;
+} /* cbprognum() */
+
+void
+cbabstime(void *arg, CDDATATYPES type, struct cdtimecode* atime)
+{
+ cdframe = CDtctoframe(atime);
+ if( cdframe == cdstopframe )
+ playing = STOPPED;
+} /* cbabstime() */
+
+void
+cbplayaudio(void *arg, CDDATATYPES type, short* audio)
+{
+ if(playing != PLAYING) return;
+ ALwritesamps(audioport, audio, CDDA_NUMSAMPLES);
+} /* cbplayaudio() */
+
+static void
+alarmsignal()
+{
+ int n, i;
+ if(playing != PLAYING) return;
+ if( ALgetfilled(audioport) < CDDA_NUMSAMPLES*8 )
+ {
+ /* Only get more samples and play them if we're getting low
+ * this ensures that the CD stays close to the sound
+ */
+ n = CDreadda(icd, cdbuf, 12);
+ if( n == 0 ) return;
+ for( i=0 ; i<12 ; i++ )
+ CDparseframe(icdp, &cdbuf[i]);
+ }
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+} /* alarmsignal() */
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+#ifdef CDDA
+ long Param[4];
+ /* Set the audio rate to 44100Hz 16bit 2s-comp stereo */
+ aconfig = ALnewconfig();
+ ALsetwidth(aconfig, AL_SAMPLE_16);
+ ALsetsampfmt(aconfig, AL_SAMPFMT_TWOSCOMP);
+ ALsetchannels(aconfig, 2);
+ Param[0] = AL_OUTPUT_RATE; Param[1] = AL_RATE_44100;
+ Param[2] = AL_CHANNEL_MODE; Param[3] = AL_STEREO;
+ ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+ audioport = ALopenport("KDE KSCD Audio", "w", aconfig);
+
+ /* setup cdparser */
+ icdp = CDcreateparser();
+ CDaddcallback(icdp, cd_audio, (CDCALLBACKFUNC)cbplayaudio, 0);
+ CDaddcallback(icdp, cd_pnum, (CDCALLBACKFUNC)cbprognum, 0);
+ CDaddcallback(icdp, cd_atime, (CDCALLBACKFUNC)cbabstime, 0);
+
+ /* Lets handle those floating point exceptions expeditiously. */
+ sigfpe_[_UNDERFL].repls = _ZERO;
+ handle_sigfpes(_ON, _EN_UNDERFL, NULL, _ABORT_ON_ERROR, NULL);
+#endif
+ return 0;
+} /* gen_init() */
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ CDSTATUS s;
+
+ if (d->fd < 0) /* Device already open? */
+ {
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = 1;
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+ find_drive_struct("", "", "");
+ d->fd = fd;
+ (d->init)(d);
+
+ d->daux = CDopen(d->cd_device,"r");
+ if (d->daux == 0)
+ {
+ return (-6);
+ }
+#ifdef CDDA
+ icd = d->daux;
+#endif
+ } else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ }
+
+ CDgetstatus(d->daux, &s);
+ if( s.state==CD_NODISC || s.state==CD_ERROR )
+ return 1;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ return -1;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track,
+ int *index )
+{
+#ifdef CDDA
+ *mode = playing;
+ *track = cdtrack;
+ *pos = cdframe;
+ *index = 0;
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *pos = CDmsftoframe(s.min,s.sec,s.frame);
+ *track = s.track;
+ *index = 0;
+ switch( s.state )
+ {
+ case CD_READY: *mode = WM_CDM_STOPPED;
+ break;
+ case CD_STILL:
+ case CD_PAUSED: *mode = WM_CDM_PAUSED;
+ break;
+ case CD_PLAYING: *mode = WM_CDM_PLAYING;
+ break;
+ default: *mode = WM_CDM_UNKNOWN;
+ }
+#endif
+ return 0;
+} /* gen_get_drive_status() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *tracks = s.last;
+ return 0;
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+ CDTRACKINFO i;
+ int ret = CDgettrackinfo(d->daux, track, &i);
+ if( ret == 0 )
+ return -1;
+ *data = 0;
+ *startframe = CDmsftoframe(i.start_min,i.start_sec,i.start_frame);
+ return 0;
+} /* gen_get_trackinfo() */
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ *frames = CDmsftoframe(s.total_min,s.total_sec,s.total_frame);
+ return 0;
+} /* gen_get_cdlen() */
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+#ifdef CDDA
+ int m, s, f;
+ CDframetomsf(start, &m, &s, &f);
+ CDseek(icd, m, s, f);
+ cdstopframe = end;
+ playing = PLAYING;
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+ int m, s, f;
+ CDframetomsf(start, &m, &s, &f);
+ CDplayabs(d->daux, m, s, f, 1);
+#endif
+ return 0;
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_PAUSED;
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ if(s.state == CD_PLAYING)
+ CDtogglepause(d->daux);
+#endif
+ return 0;
+} /* gen_pause() */
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_PLAYING;
+ signal(SIGALRM, alarmsignal);
+ setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+ CDSTATUS s;
+ if( CDgetstatus(d->daux, &s)==0 )
+ return -1;
+ if(s.state == CD_PAUSED)
+ CDtogglepause(d->daux);
+#endif
+ return 0;
+} /* gen_resume() */
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_STOPPED;
+#else
+ CDstop(d->daux);
+#endif
+ return 0;
+} /* gen_stop() */
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+#ifdef CDDA
+ playing = WM_CDM_STOPPED;
+#endif
+ CDeject(d->daux);
+ return 0;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@Deathsdoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ long Param[4];
+ Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = left*255/100;
+ Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = right*255/100;
+ ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+ return 0;
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ long Param[4];
+ Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = 0;
+ Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = 0;
+ ALgetparams(AL_DEFAULT_DEVICE, Param, 4);
+ *left = Param[1] * 100 / 255;
+ *right = Param[3] * 100 / 255;
+ return 0;
+} /* gen_get_volume() */
+
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
+
diff --git a/kscd/libwm/plat_linux.c b/kscd/libwm/plat_linux.c
new file mode 100644
index 00000000..58768a40
--- /dev/null
+++ b/kscd/libwm/plat_linux.c
@@ -0,0 +1,803 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux-specific drive control routines. Very similar to the Sun module.
+ */
+
+#if defined(__linux__)
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+/* Try to get around bug #29274 */
+#include <linux/version.h>
+#if 0
+/* this breaks the build on ia64 and s390 for example.
+ sys/types.h is already included and should provide __u64.
+ please tell where we really need this and let's try to find
+ a working #if case for everyone ... adrian@suse.de */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,50)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#undef __GNUC__
+typedef unsigned long long __u64;
+#endif
+#endif
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#if defined(BSD_MOUNTTEST)
+ #include <mntent.h>
+#else
+ /*
+ * this is for glibc 2.x which defines ust structure in
+ * ustat.h not stat.h
+ */
+ #ifdef __GLIBC__
+ #include <sys/ustat.h>
+ #endif
+#endif
+
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+
+#include "include/wm_cdda.h"
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+
+#ifdef OSS_SUPPORT
+#include <linux/soundcard.h>
+#define CD_CHANNEL SOUND_MIXER_CD
+#endif
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#ifdef LINUX_SCSI_PASSTHROUGH
+/* this is from <scsi/scsi_ioctl.h> */
+# define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+#ifdef BUILD_CDDA
+int gen_cdda_init( struct wm_drive *d );
+#endif
+
+int min_volume = 0;
+int max_volume = 255;
+
+#ifdef OSS_SUPPORT
+int mixer;
+char mixer_dev_name[20] = "/dev/mixer";
+#endif
+
+/*-------------------------------------------------------*
+ *
+ *
+ * CD-ROM drive functions.
+ *
+ *
+ *-------------------------------------------------------*/
+
+/*--------------------------------------------------------*
+ * Initialize the drive. A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*---------------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *---------------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ char vendor[32], model[32], rev[32];
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+
+ if (d->fd >= 0) { /* Device already open? */
+/* wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);*/
+ return (0);
+ }
+
+ fd = open(d->cd_device, O_RDONLY | O_NONBLOCK);
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): device=%s fd=%d\n", d->cd_device, fd);
+
+ if (fd < 0)
+ return -errno;
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ d->fd = fd;
+
+ /*
+ * See if we can do digital audio.
+ */
+#if defined(BUILD_CDDA)
+ if(d->cdda && gen_cdda_init(d)) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n");
+ gen_close(d);
+ return -1;
+ }
+#endif
+
+ /* Can we figure out the drive type? */
+ if (wm_scsi_get_drive_type(d, vendor, model, rev)) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): inquiry failed\n");
+ strcpy(vendor, "Generic");
+ strcpy(model, "drive type");
+ strcpy(rev, "");
+ }
+
+ if(find_drive_struct(vendor, model, rev) < 0) {
+ gen_close(d);
+ return -1;
+ }
+
+ if(d->proto->gen_init)
+ return (d->proto->gen_init)(d);
+
+ return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ gen_close(d);
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+#ifdef LINUX_SCSI_PASSTHROUGH
+
+ char *cmd;
+ int cmdsize;
+
+ cmdsize = 2 * sizeof(int);
+ if (retbuf)
+ {
+ if (getreply) cmdsize += max(cdblen, retbuflen);
+ else cmdsize += (cdblen + retbuflen);
+ }
+ else cmdsize += cdblen;
+
+ cmd = malloc(cmdsize);
+ if (cmd == NULL)
+ return (-1);
+
+ ((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0);
+ ((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0);
+
+ memcpy(cmd + 2*sizeof(int), cdb, cdblen);
+ if (retbuf && !getreply)
+ memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen);
+
+ if (ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl() failure\n", __FILE__);
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n");
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
+ free(cmd);
+ return (-1);
+ }
+
+ if (retbuf && getreply)
+ memcpy(retbuf, cmd + 2*sizeof(int), retbuflen);
+
+ free(cmd);
+ return 0;
+
+#else /* Linux SCSI passthrough*/
+/*----------------------------------------*
+ * send packet over cdrom interface
+ * kernel >= 2.2.16
+ *----------------------------------------*/
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,15))
+
+ struct cdrom_generic_command cdc;
+ struct request_sense sense;
+ int capability;
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi over CDROM_SEND_PACKET entered\n");
+
+ capability = ioctl(d->fd, CDROM_GET_CAPABILITY);
+
+ if(!(capability & CDC_GENERIC_PACKET))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "your CDROM or/and kernel don't support CDC_GENERIC_PACKET ...\n");
+ return -1;
+ }
+
+ memset(&cdc, 0, sizeof(struct cdrom_generic_command));
+ memset(&sense, 0, sizeof(struct request_sense));
+
+ memcpy(cdc.cmd, cdb, cdblen);
+
+ cdc.buffer = retbuf;
+ cdc.buflen = retbuflen;
+ cdc.stat = 0;
+ cdc.sense = &sense;
+ cdc.data_direction = getreply?CGC_DATA_READ:CGC_DATA_WRITE;
+
+ /* sendpacket_over_cdrom_interface() */
+ return ioctl(d->fd, CDROM_SEND_PACKET, &cdc);
+#endif /* CDROM_SEND_PACKET */
+ printf("ERROR: this binary was compiled without CDROM GENERIC PACKET SUPPORT. kernel version < 2.2.16?\n");
+ printf("ERROR: if you have a SCSI CDROM, rebuild it with a #define LINUX_SCSI_PASSTHROUGH\n");
+ return (-1);
+#endif
+} /* wm_scsi */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*--------------------------------*
+ * Keep the CD open all the time.
+ * disabled, analogous to 1.4b3
+ *--------------------------------*
+void
+keep_cd_open( void )
+{
+ int fd;
+ struct flock fl;
+ extern end;
+
+
+ for (fd = 0; fd < 256; fd++)
+ close(fd);
+
+ if (fork())
+ exit(0);
+
+#if defined(O_NOFOLLOW)
+ if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT | O_NOFOLLOW, 0666)) < 0)
+#else
+ if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT, 0666)) < 0)
+#endif
+ exit(0);
+ fl.l_type = F_WRLCK;
+ fl.l_whence = 0;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ exit(0);
+
+ if (open(cd_device, 0) >= 0)
+ {
+ brk(&end);
+ pause();
+ }
+
+ exit(0);
+}
+*/
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind )
+{
+ struct cdrom_subchnl sc;
+ int ret;
+
+#ifdef SBPCD_HACK
+ static int prevpos = 0;
+#endif
+
+
+ /* Is the device open? */
+ if (d->fd < 0) {
+ ret = wmcd_open(d);
+ if(ret < 0) /* error */
+ return ret;
+
+ if(ret == 1) {
+ /* retry */
+ *mode = WM_CDM_UNKNOWN;
+ return 0;
+ }
+ }
+
+ /* Try to get rid of the door locking */
+ /* Don't care about return value. If it */
+ /* works - fine. If not - ... */
+ ioctl(d->fd, CDROM_LOCKDOOR, 0);
+
+ *mode = WM_CDM_UNKNOWN;
+
+ sc.cdsc_format = CDROM_MSF;
+
+#if defined(BUILD_CDDA)
+ IFCDDA(d) {
+ if(!cdda_get_drive_status(d, oldmode, mode, pos, track, ind)) {
+ if(*mode == WM_CDM_STOPPED)
+ *mode = WM_CDM_UNKNOWN; /* dont believe */
+ }
+ } else
+#endif
+ if(!ioctl(d->fd, CDROMSUBCHNL, &sc)) {
+ switch (sc.cdsc_audiostatus) {
+ case CDROM_AUDIO_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = sc.cdsc_trk;
+ *ind = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+#ifdef SBPCD_HACK
+ if( *pos < prevpos ) {
+ if( (prevpos - *pos) < 75 ) {
+ *mode = WM_CDM_TRACK_DONE;
+ }
+ }
+
+ prevpos = *pos;
+#endif
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.cdsc_trk;
+ *ind = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ } else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDROM_AUDIO_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDROM_AUDIO_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CDROM_AUDIO_INVALID: /**/
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+ }
+
+ if(WM_CDS_NO_DISC(*mode)) {
+ /* verify status of drive */
+ ret = ioctl(d->fd, CDROM_DRIVE_STATUS, 0/* slot */);
+ if(ret == CDS_DISC_OK)
+ ret = ioctl(d->fd, CDROM_DISC_STATUS, 0);
+ switch(ret) {
+ case CDS_NO_DISC:
+ *mode = WM_CDM_NO_DISC;
+ break;
+ case CDS_TRAY_OPEN:
+ *mode = WM_CDM_EJECTED;
+ break;
+ case CDS_AUDIO:
+ case CDS_MIXED:
+ *mode = WM_CDM_STOPPED;
+ break;
+ case CDS_DRIVE_NOT_READY:
+ case CDS_NO_INFO:
+ case CDS_DATA_1:
+ case CDS_DATA_2:
+ case CDS_XA_2_1:
+ case CDS_XA_2_2:
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ }
+ }
+
+ return (0);
+} /* gen_get_drive_status */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct cdrom_tochdr hdr;
+
+ if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+ return (-1);
+
+ *tracks = hdr.cdth_trk1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cdrom_tocentry entry;
+
+ entry.cdte_track = track;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+ return (-1);
+
+ *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
+ entry.cdte_addr.msf.second * 75 +
+ entry.cdte_addr.msf.frame;
+ *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+
+ return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return gen_get_trackinfo( d, CDROM_LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end, int realstart)
+{
+ struct cdrom_msf msf;
+
+ CDDARETURN(d) cdda_play(d, start, end, realstart);
+
+ msf.cdmsf_min0 = start / (60*75);
+ msf.cdmsf_sec0 = (start % (60*75)) / 75;
+ msf.cdmsf_frame0 = start % 75;
+ msf.cdmsf_min1 = end / (60*75);
+ msf.cdmsf_sec1 = (end % (60*75)) / 75;
+ msf.cdmsf_frame1 = end % 75;
+
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf)) {
+ if (ioctl(d->fd, CDROMSTART))
+ return (-1);
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+ return (-2);
+ }
+
+ /*
+ * I hope no drive gets really confused after CDROMSTART
+ * If so, I need to make this run-time configurable.
+ *
+#ifndef FAST_IDE
+ if (ioctl( d->fd, CDROMSTART))
+ return (-1);
+#endif
+ if (ioctl( d->fd, CDROMPLAYMSF, &msf ))
+ return (-2);
+ */
+
+ return (0);
+} /* gen_play() */
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_pause(d);
+ return (ioctl(d->fd, CDROMPAUSE));
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_pause(d);
+ return (ioctl(d->fd, CDROMRESUME));
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+ CDDARETURN(d) cdda_stop(d);
+ return (ioctl(d->fd, CDROMSTOP));
+}
+
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+ struct stat stbuf;
+#if !defined(BSD_MOUNTTEST)
+ struct ustat ust;
+#else
+ struct mntent *mnt;
+ FILE *fp;
+#endif
+
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "ejecting?\n");
+
+ if (fstat(d->fd, &stbuf) != 0) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "that weird fstat() thingy\n");
+ return (-2);
+ }
+
+ /* Is this a mounted filesystem? */
+#if !defined(BSD_MOUNTTEST)
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+#else
+ /*
+ * This is the same test as in the WorkBone interface.
+ * I should eliminate it there, because there is no need
+ * for it in the UI
+ */
+ /* check if drive is mounted (from Mark Buckaway's cdplayer code) */
+ /* Changed it again (look at XPLAYCD from ???? */
+ /* It's better to check the device name rather than one device is */
+ /* mounted as iso9660. That prevents "no playing" if you have more*/
+ /* than one CD-ROM, and one of them is mounted, but it's not the */
+ /* audio CD -dirk */
+ if ((fp = setmntent (MOUNTED, "r")) == NULL)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "Could not open %s: %s\n", MOUNTED, strerror (errno));
+ return(-3);
+ }
+ while ((mnt = getmntent (fp)) != NULL)
+ {
+ if (strcmp (mnt->mnt_fsname, d->cd_device) == 0)
+ {
+ wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "CDROM already mounted (according to mtab). Operation aborted.\n");
+ endmntent (fp);
+ return(-3);
+ }
+ }
+ endmntent (fp);
+#endif /* BSD_MOUNTTEST */
+
+ IFCDDA(d) {
+ cdda_eject(d);
+ }
+
+ ioctl( d->fd, CDROM_LOCKDOOR, 0 );
+
+ if (ioctl(d->fd, CDROMEJECT))
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "eject failed (%s).\n", strerror(errno));
+ return (-1);
+ }
+
+ /*------------------
+ * Things in "foobar_one" are left over from 1.4b3
+ * I put them here for further observation. In 1.4b3, however,
+ * that workaround didn't help at least for /dev/sbpcd
+ * (The tray closed just after ejecting because re-opening the
+ * device causes the tray to close)
+ *------------------*/
+#ifdef foobar_one
+ extern int intermittent_dev
+ /*
+ * Some drives (drivers?) won't recognize a new CD if we leave the
+ * device open.
+ */
+ if (intermittent_dev)
+ gen_close(d);
+#endif
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+#ifdef CDROMCLOSETRAY
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n");
+ if (ioctl(d->fd, CDROMCLOSETRAY))
+ return (-1);
+#else
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen() closing tray...\n");
+ if(!gen_close(d))
+ {
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#endif /* CDROMCLOSETRAY */
+#endif /* CAN_CLOSE */
+ /* Always succeed if the drive can't close. */
+ return(0);
+} /* gen_closetray() */
+
+
+/*------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ * This is not used if sound card support is enabled.
+ *
+ *------------------------------------------------------------------------*/
+static int
+scale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+#else
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+#endif
+} /* scale_volume() */
+
+static int
+unscale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+ /* FIXME do it simpler */
+ int tmp = (((max_volume - min_volume - vol) * max * max) - (vol + min_volume));
+ return max - sqrt((tmp/(max_volume - min_volume)));
+#else
+ return (((vol - min_volume) * max) / (max_volume - min_volume));
+#endif
+} /* unscale_volume() */
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cdrom_volctrl v;
+
+ CDDARETURN(d) cdda_set_volume(d, left, right);
+
+ /* Adjust the volume to make up for the CD-ROM drive's weirdness. */
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left;
+ v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ return (ioctl(d->fd, CDROMVOLCTRL, &v));
+} /* gen_set_volume() */
+
+/*---------------------------------------------------------------------*
+ * Read the volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct cdrom_volctrl v;
+
+ CDDARETURN(d) cdda_get_volume(d, left, right);
+
+#if defined(CDROMVOLREAD)
+ if(!ioctl(d->fd, CDROMVOLREAD, &v)) {
+ *left = unscale_volume((v.channel0 + v.channel2)/2, 100);
+ *right = unscale_volume((v.channel1 + v.channel3)/2, 100);
+ } else
+#endif
+ /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+ *left = *right = -1;
+
+ return 0;
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *
+ * Return a buffer with cdtext-stream. buffer will be allocated and filled
+ *
+ * needs send packet interface -> for IDE, linux at 2.2.16
+ * depends on scsi.c which depends on wm_scsi defined in here
+ * (which also takes care of IDE drives)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* __linux__ */
diff --git a/kscd/libwm/plat_linux_audio.c b/kscd/libwm/plat_linux_audio.c
new file mode 100644
index 00000000..7316a691
--- /dev/null
+++ b/kscd/libwm/plat_linux_audio.c
@@ -0,0 +1,489 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+static char plat_linux_audio_id[] = "$Id$";
+
+#if defined(__linux__) && defined(BUILD_CDDA) /* { */
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wm_cdda.h */
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL)
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+ queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WMCDDA_DONE)
+ queue[qhead].status = WMCDDA_PLAYED;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+
+ alarm(1);
+
+ while (write(aufd, rawbuf, buflen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ wmaudio_stop( void );
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WMCDDA_ERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/kscd/libwm/plat_linux_cdda.c b/kscd/libwm/plat_linux_cdda.c
new file mode 100644
index 00000000..6456e10a
--- /dev/null
+++ b/kscd/libwm/plat_linux_cdda.c
@@ -0,0 +1,253 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk F�sterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Linux CDDA functions. Derived from the SUN module.
+ */
+
+#include "include/wm_cdda.h"
+
+#if defined(__linux__) && defined(BUILD_CDDA)
+
+#ifndef __GNUC__
+#define __GNUC__ 1
+#endif
+/* needed for vanilla kernel headers, which do provide __u64 only
+ for ansi */
+#undef __STRICT_ANSI__
+/* needed for non-ansi kernel headers */
+#define asm __asm__
+#define inline __inline__
+#include <asm/types.h>
+#include <linux/cdrom.h>
+#undef asm
+#undef inline
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2352
+
+/* Address of next block to read. */
+static int current_position;
+
+/* Address of last block to read. */
+static int ending_position;
+
+static struct cdrom_read_audio cdda;
+static long wmcdda_normalize(struct cdda_block *block);
+
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ */
+int
+wmcdda_init(struct cdda_device* pdev)
+{
+ int i;
+
+ if (pdev->fd > -1)
+ return -1;
+
+ if(!pdev->devname)
+ return -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ /* in Linux const */
+ pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
+ pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
+ if (!pdev->blocks[i].buf) {
+ ERRORLOG("wmcdda_init ENOMEM\n");
+ return -ENOMEM;
+ }
+ }
+
+ pdev->fd = open(pdev->devname, O_RDONLY | O_NONBLOCK);
+
+ if (pdev->fd > -1) {
+ cdda.addr_format = CDROM_LBA;
+ cdda.addr.lba = 200;
+ cdda.nframes = 1;
+ cdda.buf = (unsigned char*)pdev->blocks[0].buf;
+
+ pdev->status = WM_CDM_STOPPED;
+ if((ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0)) {
+ if (errno == ENXIO) {
+ /* CD ejected! */
+ pdev->status = WM_CDM_EJECTED;
+ return 0;
+ } else {
+ /* Sometimes it fails once, dunno why */
+ pdev->status = WM_CDM_CDDAERROR;
+ return 0;
+ }
+ }
+ } else {
+ ERRORLOG("canot open device, errno %i\n", errno);
+ pdev->status = WM_CDM_UNKNOWN;
+ return -1;
+ }
+
+ pdev->status = WM_CDM_UNKNOWN;
+ return 0;
+}
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+int
+wmcdda_close(struct cdda_device* pdev)
+{
+ int i;
+
+ if(-1 == pdev->fd)
+ return -1;
+
+ close(pdev->fd);
+ pdev->fd = -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ free(pdev->blocks[i].buf);
+ pdev->blocks[i].buf = 0;
+ pdev->blocks[i].buflen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up for playing the CD. Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+int
+wmcdda_setup(int start, int end, int realstart)
+{
+ current_position = start;
+ ending_position = end;
+
+ return 0;
+}
+
+/*
+ * Read some blocks from the CD. Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
+{
+ if (pdev->fd < 0 && wmcdda_init(pdev)) {
+ return -1;
+ }
+
+ /* Hit the end of the CD, probably. */
+ if (current_position >= ending_position) {
+ block->status = WM_CDM_TRACK_DONE;
+ return 0;
+ }
+
+ cdda.addr_format = CDROM_LBA;
+ cdda.addr.lba = current_position - CD_MSF_OFFSET;
+ if (ending_position && current_position + pdev->frames_at_once > ending_position)
+ cdda.nframes = ending_position - current_position;
+ else
+ cdda.nframes = pdev->frames_at_once;
+
+ cdda.buf = (unsigned char*)block->buf;
+
+ if (ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0) {
+ if (errno == ENXIO) {
+ /* CD ejected! */
+ block->status = WM_CDM_EJECTED;
+ return 0;
+ } else {
+ /* Sometimes it fails once, dunno why */
+ block->status = WM_CDM_CDDAERROR;
+ return 0;
+ }
+ }
+
+ block->track = -1;
+ block->index = 0;
+ block->frame = current_position;
+ block->status = WM_CDM_PLAYING;
+ block->buflen = cdda.nframes * CDDABLKSIZE;
+
+ current_position = current_position + cdda.nframes;
+
+ return wmcdda_normalize(block);
+}
+
+/*
+ * Normalize a bunch of CDDA data. Basically this means doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ */
+long
+wmcdda_normalize(struct cdda_block *block)
+{
+#if WM_BIG_ENDIAN
+ int i;
+ int blocks = block->buflen / CDDABLKSIZE;
+ char *rawbuf = block->buf;
+ char *dest = block->buf;
+
+ while (blocks--) {
+ for (i = 0; i < CDDABLKSIZE / 2; i++) {
+ *dest++ = rawbuf[1];
+ *dest++ = rawbuf[0];
+ rawbuf += 2;
+ }
+ }
+#endif
+ return block->buflen;
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+}
+
+#endif
diff --git a/kscd/libwm/plat_news.c b/kscd/libwm/plat_news.c
new file mode 100644
index 00000000..0e95b01d
--- /dev/null
+++ b/kscd/libwm/plat_news.c
@@ -0,0 +1,442 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sony NEWS-specific drive control routines.
+ */
+
+static char plat_news_id[] = "$Id$";
+
+#if defined( __sony_news) || defined(sony_news)
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <CD.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+extern int intermittent_dev;
+
+int min_volume = 128;
+int max_volume = 255;
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ intermittent_dev = 1;
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ if ((d->fd = CD_Open(d->cd_device, 0)) < 0)
+ {
+ /* Solaris 2.2 volume manager moves links around */
+ if (errno == ENOENT && intermittent_dev)
+ return (0);
+
+ if (errno == EACCES)
+ {
+ if (!warned)
+ {
+ /*
+ char realname[MAXPATHLEN];
+
+ if (realpath(cd_device, realname) == NULL)
+ {
+ perror("realpath");
+ return 1;
+ }
+ */
+ return -EACCES;
+ }
+ }
+ else if (errno != EIO) /* defined at top */
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ /* Figure out the drive type, if possible */
+ wm_scsi_get_drive_type(d, vendor, model, rev);
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Pass SCSI commands to the device.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ unsigned char *buf, int buflen, int getreply)
+{
+ /* NEWS can't do SCSI passthrough... or can it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ int ret = 0;
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device.\n");
+ ret = CD_Close(d->fd);
+ d->fd = -1;
+ wm_susleep(3000000);
+ }
+ return (ret);
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct CD_Status sc;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d)) {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ /* Disc is ejected. Close the device. */
+ if (CD_GetStatus(d->fd, &sc))
+ {
+ gen_close(d);
+ return (0);
+ }
+
+ switch (sc.status) {
+ case CDSTAT_PLAY:
+ *mode = PLAYING;
+ *track = sc.tno;
+ *index = sc.index;
+ *pos = sc.baddr;
+ break;
+
+ case CDSTAT_PAUSE:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.tno;
+ *index = sc.index;
+ *pos = sc.baddr;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CDSTAT_STOP:
+ if (oldmode == WM_CDM_PLAYING)
+ {
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+ }
+ /* fall through */
+
+ default:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct CD_Capacity cc;
+
+ if (CD_GetCapacity(d->fd, &cc))
+ return (-1);
+
+ *tracks = cc.etrack - 1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe )
+{
+ struct CD_TOCinfo hdr;
+ struct CD_TOCdata ent;
+
+ hdr.strack = track;
+ hdr.ntrack = 1;
+ hdr.data = &ent;
+ if (CD_ReadTOC(d->fd, &hdr))
+ return (-1);
+
+ *data = (ent.control & 4) ? 1 : 0;
+ *startframe = ent.baddr;
+
+ return (0);
+} /* gen_get_trackinfo */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ if ((d->get_trackcount)(d, &tmp))
+ return (-1);
+
+ return (gen_get_trackinfo(d, tmp + 1, &tmp, frames));
+} /* gen_get_cdlen() */
+
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct CD_PlayAddr msf;
+
+ msf.addrmode = CD_MSF;
+ msf.addr.msf.startmsf.min = start / (60*75);
+ msf.addr.msf.startmsf.sec = (start % (60*75)) / 75;
+ msf.addr.msf.startmsf.frame = start % 75;
+ msf.addr.msf.endmsf.min = end / (60*75);
+ msf.addr.msf.endmsf.sec = (end % (60*75)) / 75;
+ msf.addr.msf.endmsf.frame = end % 75;
+
+ if (CD_Play(d->fd, &msf))
+ {
+ printf("wm_cd_play_chunk(%d,%d)\n",start,end);
+ printf("msf = %d:%d:%d %d:%d:%d\n",
+ msf.addr.msf.startmsf.min,
+ msf.addr.msf.startmsf.sec,
+ msf.addr.msf.startmsf.frame,
+ msf.addr.msf.endmsf.min,
+ msf.addr.msf.endmsf.sec,
+ msf.addr.msf.endmsf.frame);
+ perror("CD_Play");
+ return (-1);
+ }
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ CD_Pause(d->fd);
+ return (0);
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ CD_Restart(d->fd);
+ return (0);
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ CD_Stop(d->fd);
+ return (0);
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ if (CD_AutoEject(d->fd))
+ return (-1);
+
+ /* Close the device if it needs to vanish. */
+ if (intermittent_dev)
+ {
+ gen_close(d);
+ }
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!gen_close(d))
+ {
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right)
+{
+ /* NEWS can't adjust volume! */
+ return (0);
+}
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, omt *left, int *right)
+{
+ /* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+ *left = *right = -1;
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_openbsd.c b/kscd/libwm/plat_openbsd.c
new file mode 100644
index 00000000..a3b0fd47
--- /dev/null
+++ b/kscd/libwm/plat_openbsd.c
@@ -0,0 +1,545 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * OpenBSD-specific drive control routines. (Based on plat_freebsd.c)
+ *
+ * Michael Shalayeff, 7/24/96
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__OpenBSD__) || defined(__OpenBSD)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_helpers.h"
+
+/* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#include <sys/scsiio.h>
+#include <scsi/scsi_all.h>
+#include <scsi/cd.h>
+#include <scsi/scsi_cd.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int min_volume = 10;
+int max_volume = 255;
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+
+char *cds[] = {"/dev/rcd0c", "/dev/rcd1c", "/dev/acd0c", NULL};
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ int fd;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+ int i;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ {
+ for (i = 0; cds[i] != NULL; i++)
+ {
+ d->cd_device = cds[i];
+ d->fd = open(d->cd_device, O_RDONLY);
+ if (d->fd >= 0)
+ break;
+ }
+ }
+ else
+ d->fd = open(d->cd_device, O_RDONLY);
+ if (d->fd < 0)
+ {
+ if (errno == EIO)
+ /* No CD in drive. */
+ return 1;
+ else
+ return -errno;
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ fd = d->fd;
+
+ find_drive_struct(vendor, model, rev);
+
+ (d->init)(d);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ *
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb,
+ int cdblen, void *retbuf, int retbuflen, int getreply)
+{
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ }
+ d->fd = -1;
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct ioc_read_subchannel sc;
+ struct cd_sub_channel_info scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.address_format = CD_MSF_FORMAT;
+ sc.data_format = CD_CURRENT_POSITION;
+ sc.track = 0;
+ sc.data_len = sizeof(scd);
+ sc.data = (struct cd_sub_channel_info *)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d)) {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc))
+ {
+ /* we need to release the device so the kernel will notice
+ reloaded media */
+ (void) close(d->fd);
+ d->fd = -1;
+ return (0); /* ejected */
+ }
+
+ switch (scd.header.audio_status)
+ {
+ case CD_AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+ scd.what.position.absaddr.msf.second * 75 +
+ scd.what.position.absaddr.msf.frame;
+ *track = scd.what.position.track_number;
+ *index = scd.what.position.index_number;
+ break;
+
+ case CD_AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case CD_AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case CD_AS_NO_STATUS:
+ case 0:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct ioc_toc_header hdr;
+
+ if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+ return (-1);
+
+ *tracks = hdr.ending_track - hdr.starting_track + 1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct ioc_read_toc_entry toc;
+ struct cd_toc_entry toc_buffer;
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.address_format = CD_MSF_FORMAT;
+ toc.starting_track = track;
+ toc.data_len = sizeof(toc_buffer);
+ toc.data = &toc_buffer;
+
+ if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+ return (-1);
+
+ *data = ((toc_buffer.control & 0x4) != 0);
+
+ *startframe = toc_buffer.addr.msf.minute*60*75 +
+ toc_buffer.addr.msf.second * 75 +
+ toc_buffer.addr.msf.frame;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ struct ioc_toc_header hdr;
+ int status;
+
+#define LEADOUT 0xaa /* see scsi.c. what a hack! */
+ return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ struct ioc_play_msf msf;
+
+ msf.start_m = start / (60*75);
+ msf.start_s = (start % (60*75)) / 75;
+ msf.start_f = start % 75;
+ msf.end_m = end / (60*75);
+ msf.end_s = (end % (60*75)) / 75;
+ msf.end_f = end % 75;
+
+ if (ioctl(d->fd, CDIOCSTART))
+ return (-1);
+
+ if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCPAUSE));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCRESUME));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (ioctl(d->fd, CDIOCSTOP));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct statfs buf;
+ int rval;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (fstatfs(stbuf.st_rdev, &buf) == 0)
+ return (-3);
+
+ rval = ioctl(d->fd, CDIOCALLOW);
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCEJECT);
+ if (rval == 0)
+ rval = ioctl(d->fd, CDIOCPREVENT);
+ if (rval == 0)
+ rval = close(d->fd);
+ if (rval == 0)
+ d->fd = -1;
+ return rval;
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+static int
+scale_volume(int vol, int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(int cd_vol, int max)
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct ioc_vol vol;
+
+ if (left < 0) /* don't laugh, I saw this happen once! */
+ left = 0;
+ if (right < 0)
+ right = 0;
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&vol, sizeof(vol));
+
+ vol.vol[LEFT_PORT] = left;
+ vol.vol[RIGHT_PORT] = right;
+
+ if (ioctl(d->fd, CDIOCSETVOL, &vol))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ struct ioc_vol vol;
+
+ if (d->fd >= 0)
+ {
+ bzero((char *)&vol, sizeof(vol));
+
+ if (ioctl(d->fd, CDIOCGETVOL, &vol))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(vol.vol[LEFT_PORT], 100);
+ *right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_osf1.c b/kscd/libwm/plat_osf1.c
new file mode 100644
index 00000000..a2cc0afb
--- /dev/null
+++ b/kscd/libwm/plat_osf1.c
@@ -0,0 +1,674 @@
+/*
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * OSF drive control routines.
+ */
+
+
+#if defined(__osf__) || defined(__osf)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+/* #include <sys/rzdisk.h>
+#include <sys/cdrom.h> */
+#include <io/cam/rzdisk.h>
+#include <io/cam/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdtext.h"
+
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ * - Chris Ross (cross@eng.umd.edu)
+ */
+struct cd_toc_header_and_entries
+{
+ struct cd_toc_header cdth;
+ struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int min_volume = 128;
+int max_volume = 255;
+static char* osf_cd_device = NULL;
+
+/*
+ * fgetline()
+ *
+ * Simulate fgets, but joining continued lines in the output of uerf.
+ *
+ * platform specific internal function
+ *
+ */
+
+#define BUF_SIZE 85 /* Max length of a (real) line */
+
+char *
+fgetline( FILE *fp )
+{
+ static char *retval = NULL;
+ static char holdbuf[BUF_SIZE + 1];
+ char tmp[BUF_SIZE + 1];
+ char *stmp;
+
+ if (!retval)
+ {
+ retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
+ if (!retval)
+ return(NULL);
+ else
+ *retval = '\0';
+ }
+
+ if (*holdbuf)
+ {
+ strcpy(retval, holdbuf);
+ retval[strlen(retval)-1] = '\0';
+ memset(holdbuf, 0, BUF_SIZE+1);
+ }
+
+ while (fgets(tmp, BUF_SIZE, fp))
+ {
+ stmp = tmp + strspn(tmp, " \t");
+ if (*stmp == '_') { /* Continuation line */
+ retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
+ strcat(retval, stmp+1);
+ } else {
+ if (*retval)
+ {
+ strcpy(holdbuf, tmp);
+ holdbuf[strlen(holdbuf)-1] = -1;
+ return retval;
+ } else { /* First line read, keep reading */
+ strcat(retval, stmp);
+ retval[strlen(retval)-1] = '\0';
+ }
+ }
+ }
+ return NULL;
+} /* fgetline() */
+
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM.
+ */
+const char*
+find_cdrom()
+{
+ char *data;
+ FILE *uerf;
+ int fds[2];
+ int pid;
+ extern char *getenv();
+ const char *device = NULL;
+
+ pipe(fds);
+
+ device = getenv("CDROM");
+ /*
+ ** the path of the device has to start w/ /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ if ( device == NULL ||
+ strncmp("/dev/", device, 5) ||
+ strstr(device, "/../")
+ )
+ return NULL;
+
+ if ((pid = fork()) == 0)
+ {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ return NULL; /* _exit(1); */
+ } else if (pid < 0) {
+ perror("fork");
+ return NULL;
+ }
+
+ close(fds[1]);
+ uerf = fdopen(fds[0], "r");
+
+ while (data = fgetline(uerf))
+ if (strstr(data, "RRD42"))
+ {
+ char *device_p;
+
+ osf_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+ strcpy(osf_cd_device, "/dev/r");
+ device_p = strstr(data, "rz");
+ device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
+ strcat(osf_cd_device, device_p);
+ strcat(osf_cd_device, "c");
+ device = osf_cd_device;
+ break;
+ }
+
+ fclose(uerf);
+
+ if (device == NULL)
+ {
+ fprintf(stderr,
+ "No cdrom (RRD42) is installed on this system\n");
+ return NULL;
+ }
+
+ kill(pid, 15);
+ (void)wait((int *)NULL);
+ return device;
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, O_RDWR);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return (-6);
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply)
+{
+ /* OSF1 doesn't have a SCSI passthrough interface, does it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.sch_address_format = CDROM_MSF_FORMAT;
+ sc.sch_data_format = CDROM_CURRENT_POSITION;
+ sc.sch_track_number = 0;
+ sc.sch_alloc_length = sizeof(scd);
+ sc.sch_buffer = (caddr_t)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+ return (0); /* ejected */
+
+ switch (scd.scd_header.sh_audio_status)
+ {
+ case AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+ dopos:
+ *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+ scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+ scd.scd_position_data.scp_absaddr.msf.f_units;
+ *track = scd.scd_position_data.scp_track_number;
+ *index = scd.scd_position_data.scp_index_number;
+ break;
+
+ case AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case AS_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+ default:
+ abort();
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct cd_play_audio_msf msf;
+
+ msf.msf_starting_M_unit = start / (60*75);
+ msf.msf_starting_S_unit = (start % (60*75)) / 75;
+ msf.msf_starting_F_unit = start % 75;
+ msf.msf_ending_M_unit = end / (60*75);
+ msf.msf_ending_S_unit = (end % (60*75)) / 75;
+ msf.msf_ending_F_unit = end % 75;
+
+ if (ioctl(d->fd, SCSI_START_UNIT))
+ return (-1);
+ if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_RESUME_PLAY, 0));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ return (ioctl(d->fd, SCSI_STOP_UNIT, 0));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (ioctl(d->fd, CDROM_EJECT_CADDY, 0));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ struct cd_toc_header hdr;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ *tracks = hdr.th_ending_track;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_header_and_entries toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.toc_address_format = CDROM_MSF_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+ hdr.th_data_len0) & 0xfff) + 2;
+ toc.toc_buffer = (caddr_t)&toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+ return (-1);
+
+ if (track == 0)
+ track = hdr.th_ending_track + 1;
+
+ *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+ *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ return (gen_get_trackinfo(d, 0, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume(int vol, int max)
+{
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+} /* scale_volume() */
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+ struct cd_playback_control pc;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+ bzero((char *)&pc, sizeof(pc));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ return (-1);
+
+ pc.pc_chan0_select = ps.ps_chan0_select;
+ pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : left;
+ pc.pc_chan1_select = ps.ps_chan1_select;
+ pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : right;
+
+ pb.pb_alloc_length = sizeof(pc);
+ pb.pb_buffer = (caddr_t)&pc;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (d->fd >= 0)
+ {
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(ps.ps_chan0_volume, 100);
+ *right = unscale_volume(ps.ps_chan1_volume, 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* no SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/plat_scor5.c b/kscd/libwm/plat_scor5.c
new file mode 100644
index 00000000..1e5be6e2
--- /dev/null
+++ b/kscd/libwm/plat_scor5.c
@@ -0,0 +1,426 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SCO Openserver R5 specific. Derived from the KSCD plat_scor5.c
+ *
+ */
+
+
+static char plat_linux_id[] = "$Id$";
+
+#if defined(M_UNIX) || defined(__M_UNIX)
+
+
+#include "include/wm_config.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/scsi.h>
+#include <sys/scsicmd.h>
+#include <errno.h>
+#include <macros.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define SENSE_SZ EXTD_SENSE_LEN
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+/*
+ * platformspecific internal function
+ */
+static int
+create_cdrom_node(char *dev_name)
+{
+ char pass_through[100];
+ int file_des;
+ struct stat sbuf;
+ int err;
+ int ccode;
+
+
+ strncpy(pass_through, dev_name, sizeof(pass_through)-2);
+ strcat(pass_through, "p" );
+
+ if (setreuid(-1,0) < 0)
+ {
+ perror("setregid/setreuid/access");
+ return -1;
+ }
+
+ ccode = access(pass_through, F_OK);
+
+ if (ccode < 0)
+ {
+
+ if (stat(dev_name, &sbuf) < 0)
+ {
+ perror("Call to get pass-through device number failed");
+ return -1;
+ }
+
+ if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+ sbuf.st_rdev) < 0)
+ {
+ perror("Unable to make pass-through node");
+ return -1;
+ }
+
+ if (chown(pass_through, 0 , 0) < 0)
+ {
+ perror("chown");
+ return -1;
+ }
+
+ if (chmod(pass_through, 0660 ) < 0)
+ {
+ perror("chmod");
+ return -1;
+ }
+ }
+
+ file_des = open( pass_through, O_RDONLY);
+ err = errno;
+
+ /*
+ if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+ {
+ perror("setreuid/setregid");
+ exit(1);
+ }
+
+ */
+
+ errno = err;
+ return file_des;
+} /* create_cdrom_node() */
+
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d)
+{
+ int fd;
+ static int warned = 0;
+ char vendor[9], model[17], rev[5];
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ {
+ fprintf(stderr,"cd_device string empty\n");
+ return (-1);
+ }
+
+
+ d->fd = create_cdrom_node(d->cd_device); /* this will do open */
+
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ if (! warned)
+ {
+ fprintf(stderr,"Cannot access %s\n",d->cd_device);
+ warned++;
+ }
+ }
+ else if (errno != EINTR)
+ {
+ perror(d->cd_device);
+ return( -6 );
+ }
+
+ /* can not acces CDROM device */
+ return (-1);
+ }
+
+ if (warned)
+ {
+ warned = 0;
+ fprintf(stderr, "Thank you.\n");
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+
+ fd = d->fd;
+
+ if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+ {
+ perror("Cannot inquiry drive for it's type");
+ return (-1);
+ }
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open */
+
+/*
+ * Re-Open the device
+ */
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+ int tries = 0;
+ do {
+ gen_close(d);
+ susleep( 1000 );
+ status = wmcd_open( d );
+ susleep( 1000 );
+ tries++;
+ } while ( (status != 0) && (tries < 10) );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ int ccode;
+ int file_des = d->fd;
+ unsigned char sense_buffer[ SENSE_SZ ];
+
+ /* getreply == 1 is read, == 0 is write */
+
+ struct scsicmd2 sb; /* Use command with automatic sense */
+
+ if (cdblen > SCSICMDLEN)
+ {
+ fprintf(stderr,"Cannot handle longer commands than %d bytes.\n", SCSICMDLEN);
+ exit(-1);
+ }
+
+ /* Get the command */
+ memcpy(sb.cmd.cdb, xcdb, cdblen);
+ sb.cmd.cdb_len = cdblen;
+
+ /* Point to data buffer */
+ sb.cmd.data_ptr = retbuf;
+ sb.cmd.data_len = retbuflen;
+
+ /* Is this write or read ? */
+ sb.cmd.is_write = (getreply==1) ? 0 : 1;
+
+ /* Zero out return status fields */
+ sb.cmd.host_sts = 0;
+ sb.cmd.target_sts = 0;
+
+ /* Set up for possible sense info */
+
+ sb.sense_ptr = sense_buffer;
+ sb.sense_len = sizeof(sense_buffer);
+
+ ccode = ioctl(file_des, SCSIUSERCMD2, &sb);
+
+ if ( sb.cmd.target_sts != 02 )
+ return ccode;
+
+ return 0;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+void
+keep_cd_open() { }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(wm_drive *d, int oldmode, int *mode,
+ int *pos, int *track, int *index)
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ int stat;
+
+ stat = wm_scsi2_eject(d);
+ sleep(2);
+ return (stat);
+} /* gen_eject() */
+
+
+int
+gen_closetray(struct wm_drive *d)
+{
+ return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested! */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+
+
+#endif /* M_UNIX */
+
+
+
+
diff --git a/kscd/libwm/plat_sun.c b/kscd/libwm/plat_sun.c
new file mode 100644
index 00000000..f83a6cd8
--- /dev/null
+++ b/kscd/libwm/plat_sun.c
@@ -0,0 +1,972 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun-specific drive control routines.
+ */
+
+static char plat_sun_id[] = "$Id$";
+
+#if defined(sun) || defined(__sun)
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdtext.h"
+
+#include <ustat.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef solbourne
+# include <mfg/dklabel.h>
+# include <mfg/dkio.h>
+# include <sys/unistd.h>
+# include <dev/srvar.h>
+#else /* A real Sun */
+# ifdef SYSV
+# include <poll.h>
+# include <stdlib.h>
+# include <sys/cdio.h>
+# include <sys/socket.h>
+# include <sys/scsi/impl/uscsi.h>
+# include "include/wm_cdda.h"
+# else
+# include <sys/buf.h>
+# include <sun/dkio.h>
+# include <scsi/targets/srdef.h>
+# include <scsi/impl/uscsi.h>
+# include <scsi/generic/commands.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+int min_volume = 0;
+int max_volume = 255;
+
+static const char *sun_cd_device = NULL;
+extern int intermittent_dev;
+
+int current_end;
+
+#if defined(SYSV) && defined(SIGTHAW)
+#ifdef __GNUC__
+void sigthawinit(void) __attribute__ ((constructor));
+#else
+#pragma init(sigthawinit)
+#endif /* GNUC */
+
+static int last_left, last_right;
+static struct wm_drive *thecd = NULL;
+
+/*
+ * Handling for Sun's Suspend functionality
+ */
+static void
+thawme(int sig)
+{
+// Just leave this line in as a reminder for a missing
+// functionality in the GUI.
+// change_mode(NULL, WM_CDM_STOPPED, NULL);
+ codec_init();
+ if( thecd )
+ gen_set_volume(thecd, last_left, last_right);
+} /* thawme() */
+
+void
+sigthawinit( void )
+{
+ struct sigaction sa;
+
+ sa.sa_handler = thawme;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ sigaction(SIGTHAW, &sa, NULL);
+} /* sigthawinit() */
+
+#endif /* SYSV && SIGTHAW */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0
+ * that exists. (Check for /vol/dev/aliases, not cdrom0, since it won't be
+ * there if there's no CD in the drive.) This is done so a single SunOS 4.x
+ * binary can be used on any 4.x or higher Sun system.
+ */
+const char*
+find_cdrom( void )
+{
+ if (access("/vol/dev/aliases", X_OK) == 0)
+ {
+ /* Volume manager. Device might not be there. */
+ intermittent_dev = 1;
+
+ /* If vold is running us, it'll tell us the device name. */
+ sun_cd_device = getenv("VOLUME_DEVICE");
+ /*
+ ** the path of the device has to include /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ if (sun_cd_device == NULL ||
+ strncmp("/vol/dev/", sun_cd_device, 9) ||
+ strstr(sun_cd_device, "/../") )
+ return "/vol/dev/aliases/cdrom0";
+ else
+ return sun_cd_device;
+ }
+ else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0)
+ {
+ /* Solaris 2.x w/o volume manager. */
+ return "/dev/rdsk/c0t6d0s2";
+ }
+ else if (access("/dev/rcd0", F_OK) == 0)
+ {
+ return "/dev/rcd0";
+ }
+ else if (access("/dev/rsr0", F_OK) == 0)
+ return "/dev/rsr0";
+ else
+ {
+ fprintf(stderr, "Couldn't find a CD device!\n");
+ return NULL;
+ }
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ codec_init();
+ return (0);
+} /* gen_init() */
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ /* Solaris 2.2 volume manager moves links around */
+ if (errno == ENOENT && intermittent_dev)
+ return (1);
+
+ if (errno == EACCES)
+ {
+ if (!warned)
+ {
+ /*
+ char realname[MAXPATHLEN];
+
+ if (realpath(cd_device, realname) == NULL)
+ {
+ perror("realpath");
+ return (1);
+ }
+ */
+ return -EACCES;
+ }
+ }
+ else if (errno != ENXIO)
+ {
+ return( -6 );
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /*
+ * See if we can do digital audio.
+ */
+#if defined(BUILD_CDDA)
+ if(d->cdda) {
+ if (!gen_cdda_init(d))
+ /* WARNING: Old GUI call. How could this survive? */
+ enable_cdda_controls(1);
+ else {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n");
+ gen_close(d);
+ return -1;
+ }
+#endif
+
+ /* Can we figure out the drive type? */
+ if (wm_scsi_get_drive_type(d, vendor, model, rev))
+ {
+ if (errno == EPERM)
+ {
+ /*
+ * Solaris 2.4 seems to refuse to do USCSICMD ioctls
+ * when not running as root. SunOS 4.x allows it
+ * as an unprivileged user, though.
+ */
+ fprintf(stderr, "Warning: WorkMan can't adapt itself to your drive unless it runs as root.\n");
+ } else {
+ fprintf(stderr, "Warning: WorkMan couldn't determine drive type\n");
+ }
+ strcpy(vendor, "Generic");
+ strcpy(model, "drive type");
+ strcpy(rev, "");
+ }
+
+ find_drive_struct(vendor, model, rev);
+
+ (d->proto->gen_init)(d);
+ thecd = d;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ gen_close(d);
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+#ifndef solbourne
+/*
+ * Send an arbitrary SCSI command out the bus and optionally wait for
+ * a reply if "retbuf" isn't NULL.
+ */
+int
+wm_scsi( struct wm_drive *d,
+ unsigned char *cdb,
+ int cdblen, void *retbuf,
+ int retbuflen, int getreply )
+{
+ char x;
+ struct uscsi_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.uscsi_cdb = (void *) cdb;
+ cmd.uscsi_cdblen = cdblen;
+ cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x;
+ cmd.uscsi_buflen = retbuf ? retbuflen : 0;
+ cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT;
+ if (getreply)
+ cmd.uscsi_flags |= USCSI_READ;
+
+ if (ioctl(d->fd, USCSICMD, &cmd))
+ return (-1);
+
+ if (cmd.uscsi_status)
+ return (-1);
+
+ return (0);
+}
+#else
+int wm_scsi() { return (-1); }
+#endif
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/* Alarm signal handler. */
+static void do_nothing( int x ) { x++; }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos, int *track, int *index )
+{
+ struct cdrom_subchnl sc;
+ struct itimerval old_timer, new_timer;
+ struct sigaction old_sig, new_sig;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+#if defined(BUILD_CDDA)
+ if (oldmode == WM_CDM_PAUSED || oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_STOPPED) {
+ CDDARETURN(d) cdda_get_drive_status(d, oldmode, mode, pos, track, index);
+ }
+#endif
+
+ /*
+ * Solaris 2.2 hangs on this ioctl if someone else ejects the CD.
+ * So we schedule a signal to break out of the hang if the call
+ * takes an unreasonable amount of time. The signal handler and
+ * timer are restored immediately to avoid interfering with XView.
+ */
+ if (intermittent_dev)
+ {
+ /*
+ * First clear out the timer so XView's signal doesn't happen
+ * while we're diddling with the signal handler.
+ */
+ timerclear(&new_timer.it_interval);
+ timerclear(&new_timer.it_value);
+ setitimer(ITIMER_REAL, &new_timer, &old_timer);
+
+ /*
+ * Now install the no-op signal handler.
+ */
+ new_sig.sa_handler = do_nothing;
+ memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask));
+ new_sig.sa_flags = 0;
+ if (sigaction(SIGALRM, &new_sig, &old_sig))
+ perror("sigaction");
+
+ /*
+ * And finally, set the timer.
+ */
+ new_timer.it_value.tv_sec = 2;
+ setitimer(ITIMER_REAL, &new_timer, NULL);
+ }
+
+ sc.cdsc_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMSUBCHNL, &sc))
+ {
+ if (intermittent_dev)
+ {
+ sigaction(SIGALRM, &old_sig, NULL);
+ setitimer(ITIMER_REAL, &old_timer, NULL);
+
+ /* If the device can disappear, let it do so. */
+ close(d->fd);
+ d->fd = -1;
+ }
+
+ return (0);
+ }
+
+ if (intermittent_dev)
+ {
+ sigaction(SIGALRM, &old_sig, NULL);
+ setitimer(ITIMER_REAL, &old_timer, NULL);
+ }
+
+ switch (sc.cdsc_audiostatus) {
+ case CDROM_AUDIO_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = sc.cdsc_trk;
+ *index = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ break;
+
+ case CDROM_AUDIO_PAUSED:
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_NO_STATUS:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = sc.cdsc_trk;
+ *index = sc.cdsc_ind;
+ *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+ sc.cdsc_absaddr.msf.second * 75 +
+ sc.cdsc_absaddr.msf.frame;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /* CD ejected manually during play. */
+ case CDROM_AUDIO_ERROR:
+ break;
+
+ case CDROM_AUDIO_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ default:
+ *mode = WM_CDM_UNKNOWN;
+ break;
+ }
+
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ struct cdrom_tochdr hdr;
+
+ if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+ return (-1);
+
+ *tracks = hdr.cdth_trk1;
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cdrom_tocentry entry;
+
+ entry.cdte_track = track;
+ entry.cdte_format = CDROM_MSF;
+
+ if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+ return (-1);
+
+ *startframe = entry.cdte_addr.msf.minute * 60 * 75 +
+ entry.cdte_addr.msf.second * 75 +
+ entry.cdte_addr.msf.frame;
+ *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames )
+{
+ int tmp;
+
+ return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another.
+ *
+ * d Drive structure.
+ * start Frame to start playing at.
+ * end End of this chunk.
+ * realstart Beginning of this chunk (<= start)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end, int realstart)
+{
+ struct cdrom_msf msf;
+ unsigned char cmdbuf[10];
+
+ current_end = end;
+
+ CDDARETURN(d) cdda_play(d, start, end, realstart);
+
+ msf.cdmsf_min0 = start / (60*75);
+ msf.cdmsf_sec0 = (start % (60*75)) / 75;
+ msf.cdmsf_frame0 = start % 75;
+ msf.cdmsf_min1 = end / (60*75);
+ msf.cdmsf_sec1 = (end % (60*75)) / 75;
+ msf.cdmsf_frame1 = end % 75;
+
+ codec_start();
+ if (ioctl(d->fd, CDROMSTART))
+ return (-1);
+ if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_pause(d);
+
+ codec_stop();
+ return (ioctl(d->fd, CDROMPAUSE));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_pause(d);
+
+ codec_start();
+ return (ioctl(d->fd, CDROMRESUME));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ CDDARETURN(d) cdda_stop(d);
+
+ codec_stop();
+ return (ioctl(d->fd, CDROMSTOP));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ IFCDDA(d) {
+ cdda_eject(d);
+ }
+
+ if (ioctl(d->fd, CDROMEJECT))
+ return (-1);
+
+ /* Close the device if it needs to vanish. */
+ if (intermittent_dev)
+ {
+ close(d->fd);
+ d->fd = -1;
+ /* Also remember to tell the cddaslave since volume
+ manager switches links around on us */
+ if (d->cdda_slave > -1)
+ {
+ write(d->cdda_slave, "E", 1);
+ cdda_get_ack(d->cdda_slave);
+ }
+ }
+
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cdrom_volctrl v;
+
+#if defined(SIGTHAW) && defined(SYSV)
+ last_left = left;
+ last_right = right;
+ thecd = d;
+#endif
+
+ CDDARETURN(d) cdda_set_volume(d, left, right);
+
+ left = (left * (max_volume - min_volume)) / 100 + min_volume;
+ right = (right * (max_volume - min_volume)) / 100 + min_volume;
+
+ v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left;
+ v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ return (ioctl(d->fd, CDROMVOLCTRL, &v));
+} /* gen_set_volume() */
+
+/*
+ * Read the volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+ CDDARETURN(d) cdda_get_volume(d, left, right);
+
+ *left = *right = -1;
+
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+#ifdef BUILD_CDDA
+
+/*
+ * Try to initialize the CDDA slave. Returns 0 on success.
+ */
+int
+gen_cdda_init( struct wm_drive *d )
+{
+ int slavefds[2];
+
+ if (d->cdda_slave > -1)
+ return (0);
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds))
+ {
+ perror("socketpair");
+ return (-1);
+ }
+
+ switch (fork())
+ {
+ case 0:
+ close(slavefds[0]);
+ dup2(slavefds[1], 1);
+ dup2(slavefds[1], 0);
+ close(slavefds[1]);
+ close(d->fd);
+ /* Try the default path first. */
+ execl(cddaslave_path, cddaslave_path, d->cd_device, (void *)0);
+ /* Search $PATH if that didn't work. */
+ execlp("cddaslave", "cddaslave", d->cd_device, (void *)0);
+ perror(cddaslave_path);
+ exit(1);
+
+ case -1:
+ close(slavefds[0]);
+ close(slavefds[1]);
+ perror("fork");
+ return (-2);
+ }
+
+ close(slavefds[1]);
+ d->cdda_slave = slavefds[0];
+
+ if (!cdda_get_ack(d->cdda_slave))
+ {
+ d->cdda_slave = -1;
+ codec_start();
+ return (-3);
+ }
+
+ return (0);
+}
+
+#endif /* BUILD_CDDA */
+
+/*
+ * The following code activates the internal CD audio passthrough on
+ * SPARCstation 5 systems (and possibly others.)
+ *
+ * Thanks to <stevep@ctc.ih.att.com>, Roger Oscarsson <roger@cs.umu.se>
+ * and Steve McKinty <>
+ *
+ * Most CD drives have a headphone socket on the front, but it
+ * is often more convenient to route the audio though the
+ * built-in audio device. That way the user can leave their
+ * headphones plugged-in to the base system, for use with
+ * other audio stuff like ShowMeTV
+ */
+
+#ifdef CODEC /* { */
+#ifdef SYSV /* { */
+
+# include <sys/ioctl.h>
+# include <sys/audioio.h>
+# include <stdlib.h>
+
+#else /* } { */
+
+# include <sun/audioio.h>
+# define AUDIO_DEV_SS5STYLE 5
+typedef int audio_device_t;
+
+#endif /* } */
+#endif /* } */
+
+/*
+ * Don't do anything with /dev/audio if we can't set it to high quality.
+ * Also, don't do anything real if it's not Solaris.
+ */
+#if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */
+codec_init() { return 0; }
+codec_start() { return 0; }
+codec_stop() { return 0; }
+#else
+
+#ifndef AUDIO_INTERNAL_CD_IN
+#define AUDIO_INTERNAL_CD_IN 0x4
+#endif
+
+static char* devname = 0;
+static char* ctlname = 0;
+static int ctl_fd = -1;
+static int port = AUDIO_LINE_IN;
+int internal_audio = 1;
+
+codec_init( void )
+{
+ register int i;
+ char* ctlname;
+ audio_info_t foo;
+ audio_device_t aud_dev;
+
+ if (internal_audio == 0)
+ {
+ ctl_fd = -1;
+ return(0);
+ }
+
+ if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio";
+ ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl");
+ if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0)
+ {
+ perror(ctlname);
+ return -1;
+ }
+ if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0)
+ {
+ close(ctl_fd);
+ ctl_fd = -1;
+ return -1;
+ }
+ /*
+ * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones.
+ * Not sure if this is all correct.
+ */
+#ifdef SYSV
+ if (strcmp(aud_dev.name, "SUNW,CS4231") &&
+ strcmp(aud_dev.name, "SUNW,sb16") &&
+ strcmp(aud_dev.name, "SUNW,sbpro"))
+#else
+ if (aud_dev != AUDIO_DEV_SS5STYLE)
+#endif
+ {
+ close(ctl_fd);
+ ctl_fd = -1;
+ return 0; /* but it's okay */
+ }
+
+ /*
+ * Does the chosen device have an internal CD port?
+ * If so, use it. If not then try and use the
+ * Line In port.
+ */
+ if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+ {
+ perror("AUDIO_GETINFO");
+ close(ctl_fd);
+ ctl_fd = -1;
+ return(-1);
+ }
+ if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN)
+ port = AUDIO_INTERNAL_CD_IN;
+ else
+ port = AUDIO_LINE_IN;
+
+ /*
+ * now set it up to use it. See audio(7I)
+ */
+
+ AUDIO_INITINFO(&foo);
+ foo.record.port = port;
+ foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE;
+#ifdef BUILD_CDDA
+ if (d->cdda_slave > -1)
+ foo.monitor_gain = 0;
+ else
+#endif
+ foo.monitor_gain = AUDIO_MAX_GAIN;
+ /*
+ * These next ones are tricky. The voulme will depend on the CD drive
+ * volume (set by the knob on the drive and/or by workman's volume
+ * control), the audio device record gain and the audio device
+ * play gain. For simplicity we set the latter two to something
+ * reasonable, but we don't force them to be reset if the user
+ * wants to change them.
+ */
+ foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100;
+ foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100;
+
+ ioctl(ctl_fd, AUDIO_SETINFO, &foo);
+ return 0;
+}
+
+static int
+kick_codec( void )
+{
+ audio_info_t foo;
+ int dev_fd;
+ int retval = 0;
+
+ /*
+ * Open the audio device, not the control device. This
+ * will fail if someone else has taken it.
+ */
+
+ if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0)
+ {
+ perror(devname);
+ return -1;
+ }
+
+ AUDIO_INITINFO(&foo);
+ foo.record.port = port;
+ foo.monitor_gain = AUDIO_MAX_GAIN;
+
+ /* These can only be set on the real device */
+ foo.play.sample_rate = 44100;
+ foo.play.channels = 2;
+ foo.play.precision = 16;
+ foo.play.encoding = AUDIO_ENCODING_LINEAR;
+
+ if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0)
+ perror(devname);
+
+ close(dev_fd);
+ return retval;
+} /* kick_codec() */
+
+codec_start( void )
+{
+ audio_info_t foo;
+
+ if (ctl_fd < 0)
+ return 0;
+
+ if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+ return -1;
+
+ if (foo.play.channels != 2) return kick_codec();
+ if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+ if (foo.play.precision != 16) return kick_codec();
+ if (foo.play.sample_rate != 44100) return kick_codec();
+
+ if (foo.record.channels != 2) return kick_codec();
+ if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+ if (foo.record.precision != 16) return kick_codec();
+ if (foo.record.sample_rate != 44100) return kick_codec();
+
+ if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec();
+ if (foo.record.port != port) return kick_codec();
+
+ return 0;
+} /* codec_start() */
+
+codec_stop( void ) { return 0; }
+
+#endif /* CODEC } */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* sun */
diff --git a/kscd/libwm/plat_sun_audio.c b/kscd/libwm/plat_sun_audio.c
new file mode 100644
index 00000000..da9fe12f
--- /dev/null
+++ b/kscd/libwm/plat_sun_audio.c
@@ -0,0 +1,493 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+static char plat_sun_audio_id[] = "$Id$";
+
+#if defined(sun) || defined(__sun) && defined(SYSV) && defined(BUILD_CDDA) && defined(WMCDDA_DONE)
+
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wmcdda.h */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker. Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information. When we write some
+ * sound to the audio device, we put its status info into the queue. We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure. When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block queue[QSIZE];
+int qtail;
+int qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int aufd, aucfd;
+static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
+
+/*
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+ signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+ audio_info_t info;
+ char *audiodev, *acdev;
+ int linval;
+
+ audiodev = getenv("AUDIODEV");
+ if (audiodev == NULL ||
+ strncmp("/dev/", audiodev, 5) ||
+ strstr(audiodev, "/../") )
+ audiodev = "/dev/audio";
+
+ acdev = malloc(strlen(audiodev) + 4);
+ if (acdev == NULL)
+ {
+ perror("Can't allocate audio control filename");
+ exit(1);
+ }
+ strcpy(acdev, audiodev);
+ strcat(acdev, "ctl");
+
+ aucfd = open(acdev, O_WRONLY, 0);
+ if (aucfd < 0)
+ {
+ perror(acdev);
+ exit(1);
+ }
+ free(acdev);
+
+ aufd = open(audiodev, O_WRONLY, 0);
+ if (aufd < 0)
+ {
+ perror(audiodev);
+ exit(1);
+ }
+
+ signal(SIGALRM, dummy);
+
+ /*
+ * Try to set the device to CD-style audio; we can process it
+ * with the least CPU overhead.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 44100;
+ info.play.channels = 2;
+ info.play.precision = 16;
+ info.play.encoding = AUDIO_ENCODING_LINEAR;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ if (errno == EINVAL)
+ {
+ /*
+ * Oh well, so much for that idea.
+ */
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = 8000;
+ info.play.channels = 1;
+ info.play.precision = 8;
+ info.play.encoding = AUDIO_ENCODING_ULAW;
+ info.play.pause = 0;
+ info.record.pause = 0;
+ info.monitor_gain = 0;
+ if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+ {
+ perror("Can't set up audio device");
+ exit(1);
+ }
+
+ /*
+ * Initialize the linear-to-ulaw mapping table.
+ */
+ if (ulawmap == NULL)
+ ulawmap = malloc(65536);
+ if (ulawmap == NULL)
+ {
+ perror("malloc");
+ exit(1);
+ }
+ for (linval = 0; linval < 65536; linval++)
+ ulawmap[linval] = linear_to_ulaw(linval-32768);
+ ulawmap += 32768;
+ raw_audio = 0;
+ }
+ else
+ {
+ perror(audiodev);
+ exit(1);
+ }
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+ audio_info_t info;
+
+ /*
+ * Start at the correct queue position.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ qtail = info.play.eof % QSIZE;
+ qstart = qtail;
+
+ queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+ if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+ perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+ wmaudio_stop();
+ close(aufd);
+ close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ info.play.gain = level;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+ audio_info_t info;
+
+ AUDIO_INITINFO(&info);
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+ level *= AUDIO_RIGHT_BALANCE;
+ info.play.balance = level / 255;
+ if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+ queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+ audio_info_t info;
+ int qhead;
+
+ /*
+ * Now send the most current status information to our parent.
+ */
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ qhead = info.play.eof % QSIZE;
+
+ if (qhead != qstart && playing)
+ {
+ int balance;
+
+ if (queue[qhead].status != WMCDDA_DONE)
+ queue[qhead].status = WMCDDA_PLAYED;
+ queue[qhead].volume = info.play.gain;
+ queue[qhead].balance = (info.play.balance * 255) /
+ AUDIO_RIGHT_BALANCE;
+
+ send_status(queue + qhead);
+ qstart = -1;
+ }
+
+ return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ int i;
+ short *buf16;
+ int alarmcount = 0;
+ struct itimerval it;
+
+ alarm(1);
+
+ while (write(aufd, rawbuf, buflen) <= 0)
+ if (errno == EINTR)
+ {
+ if (! raw_audio && alarmcount++ < 5)
+ {
+ /*
+ * 8KHz /dev/audio blocks for several seconds
+ * waiting for its queue to drop below a low
+ * water mark.
+ */
+ wmaudio_send_status();
+ timerclear(&it.it_interval);
+ timerclear(&it.it_value);
+ it.it_value.tv_usec = 500000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ continue;
+ }
+
+/* close(aufd);
+ close(aucfd);
+ wmaudio_init();
+*/ wmaudio_stop();
+ alarm(2);
+ continue;
+ }
+ else
+ {
+ blk->status = WMCDDA_ERROR;
+ return (-1);
+ }
+ alarm(0);
+
+ /*
+ * Mark this spot in the audio stream.
+ *
+ * Marks don't always succeed (if the audio buffer is empty
+ * this call will block forever) so do it asynchronously.
+ */
+ fcntl(aufd, F_SETFL, O_NONBLOCK);
+ if (write(aufd, rawbuf, 0) < 0)
+ {
+ if (errno != EAGAIN)
+ perror("audio mark");
+ }
+ else
+ qtail = (qtail + 1) % QSIZE;
+
+ fcntl(aufd, F_SETFL, 0);
+
+ queue[qtail] = *blk;
+
+ if (wmaudio_send_status() < 0)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+ audio_info_t info;
+ int balance;
+
+ if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+ perror("AUDIO_GETINFO");
+ blk->volume = info.play.gain;
+ blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if ( sign != 0 ) sample = -sample; /* get magnitude */
+ if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+ ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+ if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+ short *buf16 = (short *)rawbuf;
+ int i, j, samples;
+ int mono_value;
+ unsigned char *rbend = rawbuf + buflen;
+
+ /* Don't do anything if the audio device can take the raw values. */
+ if (raw_audio)
+ return (buflen);
+
+ for (i = 0; buf16 < (short *)(rbend); i++)
+ {
+ /* Downsampling to 8KHz is a little irregular. */
+ samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+ /* And unfortunately, we don't always end on a nice boundary. */
+ if (buf16 + samples > (short *)(rbend))
+ samples = ((short *)rbend) - buf16;
+
+ /*
+ * No need to average all the values; taking the first one
+ * is sufficient and less CPU-intensive. But we do need to
+ * do both channels.
+ */
+ mono_value = (buf16[0] + buf16[1]) / 2;
+ buf16 += samples;
+ rawbuf[i] = ulawmap[mono_value];
+ }
+
+ return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/kscd/libwm/plat_sun_cdda.c b/kscd/libwm/plat_sun_cdda.c
new file mode 100644
index 00000000..3f669a8b
--- /dev/null
+++ b/kscd/libwm/plat_sun_cdda.c
@@ -0,0 +1,380 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Sun (really Solaris) CDDA functions.
+ */
+
+#include "include/wm_cdda.h"
+
+#if defined(sun) || defined(__sun__) && defined(SYSV) && defined(BUILD_CDDA)
+
+
+#include "include/wm_struct.h"
+#include "include/wm_cdda.h"
+/* types.h and cdio.h are included by wm_cdda.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <errno.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2368
+#define SAMPLES_PER_BLK 588
+
+/* Address of next block to read. */
+int current_position = 0;
+
+/* Address of first and last blocks to read. */
+int starting_position = 0;
+int ending_position = 0;
+
+/* Playback direction. */
+int direction = 1;
+
+/* Number of blocks to read at once; initialize to the maximum. */
+/* (was 30. Set to 15 for INTeL. Maybe config option? */
+int numblocks = 15;
+
+/*
+ * This is the fastest way to convert from BCD to 8-bit.
+ */
+unsigned char unbcd[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,0,0,0,0,0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,0,0,0,0,0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0,0,0,0,0,0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,0,0,0,0,0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0,0,0,0,0,0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0,0,0,0,0,0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0,0,0,0,0,0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0,0,0,0,0,0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0,0,0,0,0,0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+static long wmcdda_normalize(struct cdda_block *block);
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ * NOTE: We allocate twice as much space as we need to actually read a block;
+ * this lets us do audio manipulations without bothering to malloc a second
+ * buffer.
+ *
+ * Also, test to see if we can actually *do* CDDA on this drive; if not, we
+ * need to exit right away so the UI doesn't show the user any CDDA controls.
+ */
+int
+wmcdda_init(struct cdda_device* pdev, struct cdda_block *block)
+{
+ struct cdrom_cdda cdda;
+ int i;
+
+ if (pdev->fd > -1)
+ return -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ /* in Linux const */
+ pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE;
+ pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen);
+ if (!pdev->blocks[i].buf)
+ return -ENOMEM;
+ }
+
+ pdev->fd = open(pdev->devname, 0);
+ if (pdev->fd == -1)
+ pdev->fd = open("/dev/rdsk/c0t6d0s2", 0);
+
+ if (pdev->fd > -1)
+ {
+ cdda.cdda_addr = 200;
+ cdda.cdda_length = 1;
+ cdda.cdda_data = pdev->blocks[0].buf;
+ cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ block->status = WM_CDM_STOPPED;
+ return -1;
+ } else {
+ block->status = WM_CDM_STOPPED;
+ return 0;
+ }
+ } else {
+ block->status = WM_CDM_EJECTED;
+ return -1;
+ }
+}
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+int
+wmcdda_close(struct cdda_device* pdev)
+{
+ int i;
+
+ if(-1 == pdev->fd)
+ return -1;
+
+ close(pdev->fd);
+ pdev->fd = -1;
+
+ for (i = 0; i < pdev->numblocks; i++) {
+ free(pdev->blocks[i].buf);
+ pdev->blocks[i].buf = 0;
+ pdev->blocks[i].buflen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Set up for playing the CD. Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+int
+wmcdda_setup(int start, int end, int realstart)
+{
+ current_position = start - 150;
+ ending_position = end - 150;
+ starting_position = realstart - 150;
+
+ /*
+ * Special case: don't start at the "end" of a track if we're
+ * playing backwards!
+ */
+ if (direction == -1 && start == realstart)
+ current_position = ending_position - numblocks;
+ return 0;
+}
+
+/*
+ * Read some blocks from the CD. Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(struct cdda_device* pdev, struct cdda_block *block)
+{
+ struct cdrom_cdda cdda;
+ int blk;
+ unsigned char *q;
+ extern int speed;
+ unsigned char* rawbuf = block->buf;
+
+ if(pdev->fd < 0 && (wmcdda_init(pdev, block) < 0)) {
+ return -1;
+ }
+
+ /*
+ * Hit the end of the CD, probably.
+ */
+ if ((direction > 0 && current_position >= ending_position) ||
+ (direction < 0 && current_position < starting_position))
+ {
+ block->status = WM_CDM_TRACK_DONE;
+ return (0);
+ }
+
+ cdda.cdda_addr = current_position;
+ if (ending_position && current_position + pdev->frames_at_once > ending_position)
+ cdda.cdda_length = ending_position - current_position;
+ else
+ cdda.cdda_length = pdev->frames_at_once;
+ cdda.cdda_data = (unsigned char*)block->buf;
+ cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (errno == ENXIO) /* CD ejected! */
+ {
+ block->status = WM_CDM_EJECTED;
+ return (-1);
+ }
+
+ /* Sometimes it fails once, dunno why */
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0)
+ {
+ perror("CDROMCDDA");
+ block->status = WM_CDM_CDDAERROR;
+ return (-1);
+ }
+ }
+ }
+ }
+
+ if (speed > 148)
+ {
+ /*
+ * We want speed=148 to advance by cdda_length, but
+ * speed=256 to advance cdda_length * 4.
+ */
+ current_position = current_position +
+ (cdda.cdda_length * direction * (speed - 112)) / 36;
+ }
+ else
+ current_position = current_position + cdda.cdda_length * direction;
+
+ for (blk = 0; blk < numblocks; blk++)
+ {
+ /*
+ * New valid Q-subchannel information? Update the block
+ * status.
+ */
+ q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
+ if (*q == 1)
+ {
+ block->track = unbcd[q[1]];
+ block->index = unbcd[q[2]];
+ /*block->minute = unbcd[q[7]];
+ block->second = unbcd[q[8]];*/
+ block->frame = unbcd[q[9]];
+ block->status = WM_CDM_PLAYING;
+ block->buflen = cdda.cdda_length;
+ }
+ }
+
+ return wmcdda_normalize(block);
+}
+
+/*
+ * Normalize a bunch of CDDA data. Basically this means ripping out the
+ * Q subchannel data and doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ *
+ * Scanning is handled here too.
+ *
+ * XXX - do byte swapping on Intel boxes?
+ */
+long
+wmcdda_normalize(struct cdda_block *block)
+{
+ int i, nextq;
+ long buflen = block->buflen;
+ int blocks = buflen / CDDABLKSIZE;
+ unsigned char *rawbuf = block->buf;
+ unsigned char *dest = rawbuf;
+ unsigned char tmp;
+ long *buf32 = (long *)rawbuf, tmp32;
+
+/*
+ * this was #ifndef LITTLEENDIAN
+ * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
+ */
+#if WM_BIG_ENDIAN
+ if (blocks--)
+ for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+ {
+ /* Only need to use temp buffer on first block. */
+ tmp = *rawbuf++;
+ *dest++ = *rawbuf++;
+ *dest++ = tmp;
+ }
+#endif
+
+ while (blocks--)
+ {
+ /* Skip over Q data. */
+ rawbuf += 16;
+
+ for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+ {
+#if WM_LITTLE_ENDIAN
+ *dest++ = *rawbuf++;
+ *dest++ = *rawbuf++;
+#else
+ *dest++ = rawbuf[1];
+ *dest++ = rawbuf[0];
+ rawbuf += 2;
+#endif
+ }
+ }
+
+ buflen -= ((buflen / CDDABLKSIZE) * 16);
+
+ /*
+ * Reverse the data here if we're playing backwards.
+ * XXX - ideally this should be done above.
+ */
+ if (direction < 0)
+ {
+ buflen /= 4; /* we can move 32 bits at a time. */
+
+ for (i = 0; i < buflen / 2; i++)
+ {
+ tmp32 = buf32[i];
+ buf32[i] = buf32[buflen - i - 1];
+ buf32[buflen - i - 1] = tmp32;
+ }
+
+ buflen *= 4;
+ }
+
+ return (buflen);
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+ if (newdir == 0)
+ {
+ numblocks = 20;
+ direction = 1;
+ }
+ else
+ {
+ numblocks = 30;
+ direction = -1;
+ }
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+ if (speed > 128)
+ numblocks = 12;
+ else
+ numblocks = direction > 0 ? 20 : 30;
+}
+
+#endif /* } */
diff --git a/kscd/libwm/plat_svr4.c b/kscd/libwm/plat_svr4.c
new file mode 100644
index 00000000..ad6e488c
--- /dev/null
+++ b/kscd/libwm/plat_svr4.c
@@ -0,0 +1,482 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SVR4 specific. Much of this is similar to plat_hpux.c.
+ */
+
+static char plat_svr4_id[] = "$Id$";
+
+#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news)
+
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/sdi.h>
+#include <sys/sdi_edt.h>
+#include <sys/scsi.h>
+#include <errno.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+void *malloc();
+char *strchr();
+
+int min_volume = 0;
+int max_volume = 255;
+
+static int
+create_cdrom_node(char *dev_name)
+{
+ char pass_through[100];
+ int file_des;
+ dev_t pass_thru_device;
+ int err;
+ int ccode;
+
+
+ strncpy(pass_through, dev_name, sizeof(pass_through) - 2);
+ strcat(pass_through, "p" );
+
+ if (setreuid(-1,0) < 0)
+ {
+ perror("setregid/setreuid/access");
+ return -1;
+ }
+
+ ccode = access(pass_through, F_OK);
+
+ if (ccode < 0)
+ {
+ if ((file_des = open(dev_name, O_RDONLY)) < 0)
+ {
+ perror("open cdrom devices failed");
+ return -1;
+ }
+
+ if (ioctl(file_des, B_GETDEV, &pass_thru_device) < 0)
+ {
+ perror("Call to get pass-through device number failed");
+ return -1;
+ }
+
+ (void)close(file_des);
+
+ if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+ pass_thru_device) < 0)
+ {
+ perror("Unable to make pass-through node");
+ return -1;
+ }
+
+ if (chown(pass_through, 0 , 0) < 0)
+ {
+ perror("chown");
+ return -1;
+ }
+
+ if (chmod(pass_through, 0660 ) < 0)
+ {
+ perror("chmod");
+ return -1;
+ }
+ }
+
+ file_des = open( pass_through, O_RDWR);
+ err = errno;
+
+ if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+ {
+ perror("setreuid/setregid");
+ return -1;
+ }
+ errno = err;
+ return file_des;
+} /* create_cdrom_node() */
+
+const char*
+find_cdrom()
+{
+ /*
+ ** the path of the device has to start w/ /dev
+ ** otherwise we are vulnerable to race conditions
+ ** Thomas Biege <thomas@suse.de>
+ */
+ const char* device = NULL;
+
+ device = getenv("CDROM");
+ if ( (device != NULL) &&
+ !(strncmp("/dev/", device, 5) ||
+ strstr(_device, "/../") ))
+ return device;
+
+ if (access("/dev/cdrom/cdrom1", F_OK) == 0)
+ {
+ return "/dev/cdrom/cdrom1";
+ }
+ else if (access("/dev/cdrom/cdrom2", F_OK) == 0)
+ {
+ return "/dev/cdrom/cdrom2";
+ }
+ else
+ {
+ fprintf(stderr, "Couldn't find a CD device!\n");
+ return NULL;
+ }
+} /* find_cdrom() */
+
+/*
+ * Initialize the drive. A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ int fd, flag = 1;
+ static int warned = 0;
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ d->fd = create_cdrom_node(d->cd_device); /* this will do open */
+
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ if (! warned)
+ {
+ fprintf(stderr,"Cannot access %s\n",d->cd_device);
+ warned++;
+ }
+ }
+ else if (errno != EINTR)
+ {
+ return ( -6 );
+ }
+
+ /* No CD in drive. (Is this true also for svr4 ? XXX ) */
+ return (1);
+ }
+
+ if (warned)
+ {
+ warned = 0;
+ fprintf(stderr, "Thank you.\n");
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+
+ fd = d->fd;
+
+ if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+ {
+ perror("Cannot inquiry drive for it's type");
+ exit(1);
+ }
+ find_drive_struct(vendor, model, rev);
+
+ d->fd = fd;
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int
+wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen,
+ char *retbuf, int retbuflen, int getreply)
+{
+ int ccode;
+ int file_des = d->fd;
+ int i,j;
+ unsigned char sense_buffer[ SENSE_SZ ];
+ int errno_save;
+
+ /* getreply == 1 is read, == 0 is write */
+
+ struct sb sb;
+ struct scs scs;
+
+ sb.sb_type = ISCB_TYPE;
+
+ sb.SCB.sc_comp_code = SDI_PROGRES;
+ sb.SCB.sc_int = NULL;
+ sb.SCB.sc_wd = 0;
+ sb.SCB.sc_dev.sa_major = 0;
+ sb.SCB.sc_dev.sa_minor = 0;
+ sb.SCB.sc_dev.sa_lun = 0;
+ sb.SCB.sc_dev.sa_exlun = 0;
+ sb.SCB.sc_status = 0;
+ sb.SCB.sc_link = (struct sb *) NULL;
+ sb.SCB.sc_resid = 0;
+
+ sb.SCB.sc_cmdpt = (void *)xcdb;
+ sb.SCB.sc_cmdsz = cdblen;
+
+ sb.SCB.sc_datapt = retbuf ;
+ sb.SCB.sc_datasz = retbuflen ;
+
+ if (getreply == 1)
+ sb.SCB.sc_mode = SCB_READ;
+ else
+ sb.SCB.sc_mode = SCB_WRITE;
+
+ sb.SCB.sc_time = 500;
+
+ ccode = ioctl(file_des, SDI_SEND, &sb);
+
+ if ( (sb.SCB.sc_comp_code != 0xd000000e ) ||
+ ( sb.SCB.sc_status != 02) )
+ return ccode;
+
+ errno_save = errno;
+
+ sb.SCB.sc_comp_code = SDI_PROGRES;
+ sb.SCB.sc_int = NULL;
+ sb.SCB.sc_wd = 0;
+ sb.SCB.sc_dev.sa_major = 0;
+ sb.SCB.sc_dev.sa_minor = 0;
+ sb.SCB.sc_dev.sa_lun = 0;
+ sb.SCB.sc_dev.sa_exlun = 0;
+ sb.SCB.sc_status = 0;
+ sb.SCB.sc_link = (struct sb *) NULL;
+ sb.SCB.sc_resid = 0;
+
+ scs.ss_op = SS_REQSEN;
+ scs.ss_lun = 0;
+ scs.ss_addr1 = 0;
+ scs.ss_addr = 0;
+ scs.ss_len = SENSE_SZ;
+ scs.ss_cont = 0;
+
+ sb.SCB.sc_cmdpt = SCS_AD(&scs);
+ sb.SCB.sc_cmdsz = SCS_SZ;
+ sb.SCB.sc_datapt = sense_buffer;
+ sb.SCB.sc_datasz = 18;
+ sb.SCB.sc_mode = SCB_READ;
+ sb.SCB.sc_time = 5000;
+
+ if (ioctl(file_des, SDI_SEND, &sb) < 0)
+ {
+ fprintf(stderr,"Cannot read sense.\n");
+ exit(-1);
+ }
+
+ errno=errno_save;
+ return -1;
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ return (wm_scsi2_get_trackcount(d, tracks));
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+
+ return (wm_scsi2_get_cdlen(d, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+ return (wm_scsi2_play(d, start, end));
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return (wm_scsi2_pause(d));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (wm_scsi2_resume(d));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (wm_scsi2_stop(d));
+} /* gen_stop() */
+
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*
+ * Close the tray.
+ * please review scsi.c / wm_scsi2_closetray()
+ * and send changes to milliByte@DeathsDoor.com
+ */
+int
+gen_closetray( struct wm_drive *d )
+{
+ return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+ return (wm_scsi2_set_volume(d, left, right));
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ return (wm_scsi2_get_volume(d, left, right));
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ /* This needs to be tested */
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif
diff --git a/kscd/libwm/plat_template.c b/kscd/libwm/plat_template.c
new file mode 100644
index 00000000..bd47c20b
--- /dev/null
+++ b/kscd/libwm/plat_template.c
@@ -0,0 +1,295 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This file surely contains nonsense. It's the porter's part to fill
+ * the gaps and assure that the resulting code makes sense.
+ *
+ */
+
+
+static char plat_template_id[] = "$Id$"
+
+#if [TEMPLATESYSTEM]
+
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdtext.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * gen_init();
+ *
+ */
+int
+gen_init(struct wm_drive *d)
+{
+ return (0);
+} /* gen_init() */
+
+/*
+ * wmcd_open()
+ *
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+ char vendor[32] = WM_STR_GENVENDOR;
+ char model[32] = WM_STR_GENMODEL;
+ char rev[32] = WM_STR_GENREV;
+
+ if( ! d )
+ {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if(d->fd > -1) /* device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return 0;
+ }
+
+ if( d->cd_device == (char *)NULL )
+ d->cd_device = DEFAULT_CD_DEVICE;
+
+ /* open() goes here */
+
+ if(find_drive_struct(vendor, model, rev)) {
+ gen_close(d);
+ return -1;
+ }
+
+ d->init(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * wm_scsi()
+ *
+ */
+int
+wm_scsi(struct wm_drive *d,
+ uchar_t *cdb, int cdblen,void *retbuf,int retbuflen,int getreply)
+{
+ return (0);
+} /* wm_scsi() */
+
+/*
+ * close the CD device
+ */
+
+int
+gen_close(struct wm_drive *d)
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+} /* gen_close() */
+
+/*
+ * gen_get_drive_status()
+ *
+ */
+int
+gen_get_drive_status(struct wm_drive *d,
+ int oldmode,
+ int *mode,
+ int *pos,
+ int *track,
+ int *index)
+{
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * gen_get_trackcount()
+ *
+ */
+int
+gen_get_trackcount(struct wm_drive *d,int *tracks)
+{
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * gen_get_trackinfo()
+ *
+ */
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * gen_get_cdlen()
+ *
+ */
+int
+gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+ return (0);
+} /* gen_get_cdlen() */
+
+/*
+ * gen_play()
+ *
+ */
+int
+gen_play(struct wm_drive *d,int start,int end)
+{
+ return (0);
+} /* gen_play() */
+
+/*
+ * gen_pause()
+ *
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+ return ioctl( 0 );
+} /* gen_pause() */
+
+/*
+ * gen_resume
+ *
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+ return (0);
+} /* gen_resume() */
+
+/*
+ * gen_stop()
+ *
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+ return (0);
+} /* gen_stop() */
+
+/*
+ * gen_eject()
+ *
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!wmcd_close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+int
+scale_volume(int vol,int max)
+{
+ return ((vol * (max_volume - min_volume)) / max + min_volume);
+} /* scale_volume() */
+
+int
+unscale_volume(int vol,int max)
+{
+ int n;
+ n = ( vol - min_volume ) * max_volume / (max - min_volume);
+ return (n <0)?0:n;
+} /* unscale_volume() */
+
+/*
+ * gen_set_volume()
+ *
+ */
+int
+gen_set_volume(struct wm_drive *d,int left,int right)
+{
+ return (0);
+} /* gen_set_volume() */
+
+/*
+ * gen_get_volume()
+ *
+ */
+int
+gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *
+ * For systems without working wm_scsi(), this should return -1
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght);
+} /* gen_get_cdtext() */
+
+#endif /* TEMPLATESYSTEM */
diff --git a/kscd/libwm/plat_ultrix.c b/kscd/libwm/plat_ultrix.c
new file mode 100644
index 00000000..2685b0f5
--- /dev/null
+++ b/kscd/libwm/plat_ultrix.c
@@ -0,0 +1,663 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * ULTRIX 4.2 drive control routines.
+ */
+
+static char plat_ultrix_id[] = "$Id$";
+
+#if defined(ultrix) || defined(__ultrix)
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+#include <sys/rzdisk.h>
+#include <sys/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_cdtext.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ * - Chris Ross (cross@eng.umd.edu)
+ */
+struct cd_toc_header_and_entries
+{
+ struct cd_toc_header cdth;
+ struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int min_volume = 128;
+int max_volume = 255;
+
+char * ultrix_cd_device = NULL;
+/*
+ * fgetline()
+ *
+ * Simulate fgets, but joining continued lines in the output of uerf.
+ */
+
+#define BUF_SIZE 85 /* Max length of a (real) line */
+
+char *
+fgetline( FILE *fp )
+{
+ static char *retval = NULL;
+ static char holdbuf[BUF_SIZE + 1];
+ char tmp[BUF_SIZE + 1];
+ char *stmp;
+
+ if (!retval)
+ {
+ retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
+ if (!retval)
+ return(NULL);
+ else
+ *retval = '\0';
+ }
+
+ if (*holdbuf)
+ {
+ strcpy(retval, holdbuf);
+ retval[strlen(retval)-1] = '\0';
+ memset(holdbuf, 0, BUF_SIZE+1);
+ }
+
+ while (fgets(tmp, BUF_SIZE, fp))
+ {
+ stmp = tmp + strspn(tmp, " \t");
+ if (*stmp == '_')
+ { /* Continuation line */
+ retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
+ strcat(retval, stmp+1);
+ } else {
+ if (*retval)
+ {
+ strcpy(holdbuf, tmp);
+ holdbuf[strlen(holdbuf)-1] = -1;
+ return retval;
+ } else { /* First line read, keep reading */
+ strcat(retval, stmp);
+ retval[strlen(retval)-1] = '\0';
+ }
+ }
+ }
+ return NULL;
+} /* fgetline() */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM. If the "CDROM" environment variable is set,
+ * use that instead.
+ */
+const char*
+find_cdrom()
+{
+ char *data;
+ FILE *uerf;
+ int fds[2];
+ int pid;
+ const char* device = NULL;
+
+ device = getenv("CDROM");
+
+ if (device != NULL)
+ {
+ if(strncmp("/dev/", device, 5) || strstr(device, "/../"))
+ return NULL;
+ }
+
+ pipe(fds);
+
+ if ((pid = fork()) == 0)
+ {
+ close(fds[0]);
+ dup2(fds[1], 1);
+ execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
+ return NULL; /* _exit(1); */
+ } else if (pid < 0) {
+ perror("fork");
+ return NULL; /* exit(1); */
+ }
+
+ close(fds[1]);
+ uerf = fdopen(fds[0], "r");
+
+ while (data = fgetline(uerf))
+ if (strstr(data, "RRD42"))
+ {
+ char *device_p;
+
+ ultrix_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+ strcpy(ultrix_cd_device, "/dev/r");
+ device_p = strstr(data, "rz");
+ device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
+ strcat(ultrix_cd_device, device_p);
+ strcat(ultrix_cd_device, "c");
+ device = ultrix_cd_device;
+ break;
+ }
+
+ fclose(uerf);
+
+ if (device == NULL)
+ {
+ fprintf(stderr, "No cdrom (RRD42) is installed on this system\n");
+ return NULL; /* exit(1); */
+ }
+
+ kill(pid, 15);
+ (void)wait((int *)NULL);
+ return device;
+} /* find_cdrom() */
+
+/*
+ * initialize the drive. a no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+ return (0);
+} /* gen_init() */
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+ int fd;
+ static int warned = 0;
+
+ if (d->fd >= 0) /* Device already open? */
+ {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+ return (0);
+ }
+
+ if (d->cd_device == NULL)
+ d->cd_device = find_cdrom();
+
+ d->fd = open(d->cd_device, 0);
+ if (d->fd < 0)
+ {
+ if (errno == EACCES)
+ {
+ return -EACCES;
+ }
+ else if (errno != EINTR)
+ {
+ return( -6 );
+ }
+
+ /* No CD in drive. */
+ return (1);
+ }
+
+ /* Now fill in the relevant parts of the wm_drive structure. */
+ find_drive_struct("", "", "");
+ d->fd = fd;
+
+ (d->init)(d);
+
+ return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+ int status;
+
+ do {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n");
+ status = gen_close( d );
+ wm_susleep( 1000 );
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n");
+ status = wmcd_open( d ); /* open it as usual */
+ wm_susleep( 1000 );
+ } while ( status != 0 );
+ return status;
+} /* wmcd_reopen() */
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+ void *retbuf, int retbuflen, int getreply )
+{
+ /* ULTRIX doesn't have a SCSI passthrough interface, does it? */
+ return (-1);
+} /* wm_scsi() */
+
+int
+gen_close( struct wm_drive *d )
+{
+ if(d->fd != -1) {
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
+ close(d->fd);
+ d->fd = -1;
+ }
+ return 0;
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *index)
+{
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ sc.sch_address_format = CDROM_MSF_FORMAT;
+ sc.sch_data_format = CDROM_CURRENT_POSITION;
+ sc.sch_track_number = 0;
+ sc.sch_alloc_length = sizeof(scd);
+ sc.sch_buffer = (caddr_t)&scd;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+ switch (wmcd_open(d))
+ {
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+ return (0); /* ejected */
+
+ switch (scd.scd_header.sh_audio_status)
+ {
+ case AS_PLAY_IN_PROGRESS:
+ *mode = WM_CDM_PLAYING;
+dopos:
+ *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+ scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+ scd.scd_position_data.scp_absaddr.msf.f_units;
+ *track = scd.scd_position_data.scp_track_number;
+ *index = scd.scd_position_data.scp_index_number;
+ break;
+
+ case AS_PLAY_PAUSED:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ goto dopos;
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ case AS_PLAY_COMPLETED:
+ *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+ break;
+
+ case AS_NO_STATUS:
+ *mode = WM_CDM_STOPPED;
+ break;
+ }
+ return (0);
+} /* gen_get_drive_status() */
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+ struct cd_toc_header hdr;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ *tracks = hdr.th_ending_track;
+
+ return (0);
+} /* gen_get_trackcount() */
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_header_and_entries toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+ return (-1);
+
+ bzero((char *)&toc_buffer, sizeof(toc_buffer));
+ toc.toc_address_format = CDROM_MSF_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+ hdr.th_data_len0) & 0xfff) + 2;
+ toc.toc_buffer = (caddr_t)&toc_buffer;
+
+ if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+ return (-1);
+
+ if (track == 0)
+ track = hdr.th_ending_track + 1;
+
+ *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+ *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+ toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+ return (0);
+} /* gen_get_trackinfo() */
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+ int tmp;
+ return (gen_get_trackinfo(d, 0, &tmp, frames));
+} /* gen_get_cdlen() */
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+ struct cd_play_audio_msf msf;
+
+ msf.msf_starting_M_unit = start / (60*75);
+ msf.msf_starting_S_unit = (start % (60*75)) / 75;
+ msf.msf_starting_F_unit = start % 75;
+ msf.msf_ending_M_unit = end / (60*75);
+ msf.msf_ending_S_unit = (end % (60*75)) / 75;
+ msf.msf_ending_F_unit = end % 75;
+
+ if (ioctl(d->fd, SCSI_START_UNIT))
+ return (-1);
+ if (ioctl(d->fd, CDROM_PLAY_MSF, &msf))
+ return (-2);
+
+ return (0);
+} /* gen_play() */
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_PAUSE_PLAY));
+} /* gen_pause() */
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+ return (ioctl(d->fd, CDROM_RESUME_PLAY));
+} /* gen_resume() */
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+ return (ioctl(d->fd, SCSI_STOP_UNIT));
+} /* gen_stop() */
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+ /* On some systems, we can check to see if the CD is mounted. */
+ struct stat stbuf;
+ struct ustat ust;
+
+ if (fstat(d->fd, &stbuf) != 0)
+ return (-2);
+
+ /* Is this a mounted filesystem? */
+ if (ustat(stbuf.st_rdev, &ust) == 0)
+ return (-3);
+
+ return (ioctl(d->fd, CDROM_EJECT_CADDY));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * milliByte@DeathsDoor.com
+ *----------------------------------------*/
+
+int
+gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+ if(!close(d->fd))
+ {
+ d->fd=-1;
+ return(wmcd_reopen(d));
+ } else {
+ return(-1);
+ }
+#else
+ /* Always succeed if the drive can't close */
+ return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive. "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom. To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting. The actual formula looks like
+ *
+ * (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ * max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ * return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume( int vol, int max )
+{
+ return ((max * max - (max - vol) * (max - vol)) *
+ (max_volume - min_volume) / (max * max) + min_volume);
+} /* scale_volume() */
+
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume( int cd_vol, int max )
+{
+ int vol = 0, top = max, bot = 0, scaled;
+
+ while (bot <= top)
+ {
+ vol = (top + bot) / 2;
+ scaled = scale_volume(vol, max);
+ if (cd_vol == scaled)
+ break;
+ if (cd_vol < scaled)
+ top = vol - 1;
+ else
+ bot = vol + 1;
+ }
+
+ if (vol < 0)
+ vol = 0;
+ else if (vol > max)
+ vol = max;
+
+ return (vol);
+} /* unscale_volume() */
+
+/*
+ * Set the volume level for the left and right channels. Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+ struct cd_playback_control pc;
+
+ left = scale_volume(left, 100);
+ right = scale_volume(right, 100);
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+ bzero((char *)&pc, sizeof(pc));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ return (-1);
+
+ pc.pc_chan0_select = ps.ps_chan0_select;
+ pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : left;
+ pc.pc_chan1_select = ps.ps_chan1_select;
+ pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+ CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+ CDROM_MAX_VOLUME : right;
+
+ pb.pb_alloc_length = sizeof(pc);
+ pb.pb_buffer = (caddr_t)&pc;
+
+ if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+ return (-1);
+
+ return (0);
+} /* gen_set_volume() */
+
+/*
+ * Read the initial volume from the drive, if available. Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ struct cd_playback pb;
+ struct cd_playback_status ps;
+
+ bzero((char *)&pb, sizeof(pb));
+ bzero((char *)&ps, sizeof(ps));
+
+ pb.pb_alloc_length = sizeof(ps);
+ pb.pb_buffer = (caddr_t)&ps;
+
+ if (d->fd >= 0)
+ {
+ if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+ *left = *right = -1;
+ else
+ {
+ *left = unscale_volume(ps.ps_chan0_volume, 100);
+ *right = unscale_volume(ps.ps_chan1_volume, 100);
+ }
+ }
+ else
+ *left = *right = -1;
+
+ return (0);
+} /* gen_get_volume() */
+
+/*------------------------------------------------------------------------*
+ * gen_get_cdtext(drive, buffer, lenght)
+ *------------------------------------------------------------------------*/
+
+int
+gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght)
+{
+ return -1; /* No SCSI, no CDTEXT */
+} /* gen_get_cdtext() */
+
+
+#endif
diff --git a/kscd/libwm/scsi.c b/kscd/libwm/scsi.c
new file mode 100644
index 00000000..4e0dd759
--- /dev/null
+++ b/kscd/libwm/scsi.c
@@ -0,0 +1,667 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
+ * These depend on wm_scsi(), which should be defined in each platform
+ * module.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdtext.h"
+
+#define SCMD_INQUIRY 0x12
+#define SCMD_MODE_SELECT 0x15
+#define SCMD_MODE_SENSE 0x1a
+#define SCMD_START_STOP 0x1b
+#define SCMD_PREVENT 0x1e
+#define SCMD_READ_SUBCHANNEL 0x42
+#define SCMD_READ_TOC 0x43
+#define SCMD_PLAY_AUDIO_MSF 0x47
+#define SCMD_PAUSE_RESUME 0x4b
+
+#define SUBQ_STATUS_INVALID 0x00
+#define SUBQ_STATUS_PLAY 0x11
+#define SUBQ_STATUS_PAUSE 0x12
+#define SUBQ_STATUS_DONE 0x13
+#define SUBQ_STATUS_ERROR 0x14
+#define SUBQ_STATUS_NONE 0x15
+#define SUBQ_STATUS_NO_DISC 0x17 /* Illegal, but Toshiba returns it. */
+#define SUBQ_ILLEGAL 0xff
+
+#define PAGE_AUDIO 0x0e
+#define LEADOUT 0xaa
+
+#define WM_MSG_CLASS WM_MSG_CLASS_SCSI
+
+/* local prototypes */
+int wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len );
+int wm_scsi2_pause_resume(struct wm_drive *d, int resume);
+int wm_scsi2_prevent(struct wm_drive *d, int prevent);
+int wm_scsi2_play(struct wm_drive *d, int sframe, int eframe);
+int wm_scsi2_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe);
+int wm_scsi2_get_cdlen(struct wm_drive *d, int frames);
+int wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind);
+int wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks);
+int wm_scsi2_pause(struct wm_drive *d);
+int wm_scsi2_resume(struct wm_drive *d);
+int wm_scsi2_stop(struct wm_drive *d);
+int wm_scsi2_eject(struct wm_drive *d);
+int wm_scsi2_closetray(struct wm_drive *d);
+int wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right);
+int wm_scsi2_set_volume(struct wm_drive *d, int left, int right);
+/* local prototypes END */
+
+/*
+ * Send a SCSI command over the bus, with all the CDB bytes specified
+ * as unsigned char parameters. This doesn't use varargs because some
+ * systems have stdargs instead and the number of bytes in a CDB is
+ * limited to 12 anyway.
+ *
+ * d Drive structure
+ * buf Buffer for data, both sending and receiving
+ * len Size of buffer
+ * dir TRUE if the command expects data to be returned in the buffer.
+ * a0- CDB bytes. Either 6, 10, or 12 of them, depending on the command.
+ */
+/*VARARGS4*/
+int
+sendscsi( struct wm_drive *d, void *buf,
+ unsigned int len, int dir,
+ unsigned char a0, unsigned char a1,
+ unsigned char a2, unsigned char a3,
+ unsigned char a4, unsigned char a5,
+ unsigned char a6, unsigned char a7,
+ unsigned char a8, unsigned char a9,
+ unsigned char a10, unsigned char a11 )
+
+{
+ int cdblen = 0;
+ unsigned char cdb[12];
+
+ cdb[0] = a0;
+ cdb[1] = a1;
+ cdb[2] = a2;
+ cdb[3] = a3;
+ cdb[4] = a4;
+ cdb[5] = a5;
+
+ switch ((a0 >> 5) & 7) {
+ case 0:
+ cdblen = 6;
+ break;
+
+ case 5:
+ cdb[10] = a10;
+ cdb[11] = a11;
+ cdblen = 12;
+
+ case 1:
+ case 2:
+ case 6: /* assume 10-byte vendor-specific codes for now */
+ cdb[6] = a6;
+ cdb[7] = a7;
+ cdb[8] = a8;
+ cdb[9] = a9;
+ if (! cdblen)
+ cdblen = 10;
+ break;
+ }
+
+ return (wm_scsi(d, cdb, cdblen, buf, len, dir));
+}
+
+/*
+ * Send a MODE SENSE command and return the results (minus header cruft)
+ * in a user buffer.
+ *
+ * d Drive structure
+ * page Number of page to query (plus page control bits, if any)
+ * buf Result buffer
+ */
+int
+wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf )
+{
+ unsigned char pagebuf[255];
+ int status, i, len, offset;
+
+ status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
+ page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0);
+ if (status < 0)
+ return (status);
+
+ /*
+ * The first byte of the returned data is the transfer length. Then
+ * two more bytes and the length of whatever header blocks are in
+ * front of the page we want.
+ */
+ len = pagebuf[0] - pagebuf[3] - 3;
+ offset = pagebuf[3] + 4;
+ for (i = 0; i < len; i++)
+ buf[i] = pagebuf[offset + i];
+
+ return (0);
+}
+
+/*
+ * Send a MODE SELECT command.
+ *
+ * d Drive structure
+ * buf Page buffer (no need to put on block descriptors)
+ * len Size of page
+ */
+int
+wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len )
+{
+ unsigned char pagebuf[255];
+ int i;
+
+ pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
+ for (i = 0; i < (int) len; i++)
+ pagebuf[i + 4] = buf[i];
+
+ return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
+ 0, len + 4, 0,0,0,0,0,0,0));
+}
+
+/*
+ * Send an INQUIRY command to get the drive type.
+ *
+ * d Drive structure
+ * vendor Buffer for vendor name (8 bytes + null)
+ * model Buffer for model name (16 bytes + null)
+ * rev Buffer for revision level (4 bytes + null)
+ *
+ * The above string lengths apply to the SCSI INQUIRY command. The
+ * actual WorkMan drive structure reserves 31 bytes + NULL per entry.
+ *
+ * If the model name begins with "CD-ROM" and zero or more spaces, that will
+ * all be stripped off since it's just extra junk to WorkMan.
+ */
+int
+wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+ char *model, char *rev )
+{
+/* removed unsigned*/
+ char *s, *t, buf[36];
+ memset(buf, 0, 36);
+
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "Sending SCSI inquiry command...\n");
+ if (sendscsi(d, buf, 36, 1, SCMD_INQUIRY, 0, 0, 0, 36, 0,0,0,0,0,0,0))
+ {
+ sprintf( vendor, WM_STR_GENVENDOR);
+ sprintf( model, WM_STR_GENMODEL);
+ sprintf( rev, WM_STR_GENREV);
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_ERROR, "SCSI Inquiry command not supported in this context\n");
+ return -1;
+ }
+
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_DEBUG, "sent.\n");
+
+ memcpy(vendor, buf + 8, 8);
+ vendor[8] = '\0';
+ memcpy(model, buf + 16, 16);
+ model[16] = '\0';
+ memcpy(rev, buf + 32, 4);
+ rev[4] = '\0';
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "SCSI Inquiry result: [%s|%s|%s]\n", vendor, model, rev);
+
+
+ /* Remove "CD-ROM " from the model. */
+ if (! strncmp(model, "CD-ROM", 6))
+ {
+ s = model + 6;
+ t = model;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ while( (*t++ = *s++) )
+ ;
+ }
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "scsi: Cooked data: %s %s rev. %s\n", vendor, model,rev);
+ return (0);
+} /* wm_scsi_get_drive_type() */
+
+/*
+ * Send a SCSI-2 PAUSE/RESUME command. "resume" is 1 to resume, 0 to pause.
+ */
+int
+wm_scsi2_pause_resume(struct wm_drive *d, int resume)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
+ 0, resume ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 "prevent media removal" command. "prevent" is 1 to lock
+ * caddy in.
+ */
+int
+wm_scsi2_prevent(struct wm_drive *d, int prevent)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
+ 0, prevent ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 PLAY AUDIO MSF command. Pass the starting and ending
+ * frame numbers.
+ */
+int
+wm_scsi2_play(struct wm_drive *d, int sframe, int eframe)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
+ sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
+ eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
+ 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 READ TOC command to get the data for a particular track.
+ * Fill in track information from the returned data.
+ */
+int
+wm_scsi2_get_trackinfo(struct wm_drive *d, int track,
+ int *data, int *startframe)
+{
+ unsigned char buf[12]; /* one track's worth of info */
+
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
+ 0, 0, 0, 0, track, sizeof(buf) / 256,
+ sizeof(buf) % 256, 0,0,0))
+ return (-1);
+
+ *data = buf[5] & 4 ? 1 : 0;
+ *startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+
+ return (0);
+}
+
+/*
+ * Get the starting frame for the leadout area (which should be the same as
+ * the length of the disc as far as WorkMan is concerned).
+ */
+int
+wm_scsi2_get_cdlen(struct wm_drive *d, int frames)
+{
+ int tmp;
+ return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, &frames));
+}
+
+/*
+ * Get the current status of the drive by sending the appropriate SCSI-2
+ * READ SUB-CHANNEL command.
+ */
+int
+wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode,
+ int *mode, int *pos, int *track, int *ind)
+{
+ unsigned char buf[48];
+
+ /* If we can't get status, the CD is ejected, so default to that. */
+ *mode = WM_CDM_EJECTED;
+
+ /* Is the device open? */
+ if (d->fd < 0)
+ {
+/*
+ * stupid somehow, but necessary this time
+ */
+ switch( wmcd_open( d ) ) {
+
+ case -1: /* error */
+ return (-1);
+
+ case 1: /* retry */
+ return (0);
+ }
+ }
+
+ /* If we can't read status, the CD has been ejected. */
+ buf[1] = SUBQ_ILLEGAL;
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
+ 0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0))
+ return (0);
+
+ switch (buf[1]) {
+ case SUBQ_STATUS_PLAY:
+ *mode = WM_CDM_PLAYING;
+ *track = buf[6];
+ *ind = buf[7];
+ *pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+ break;
+
+ case SUBQ_STATUS_PAUSE:
+ if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+ {
+ *mode = WM_CDM_PAUSED;
+ *track = buf[6];
+ *ind = buf[7];
+ *pos = buf[9] * 60 * 75 +
+ buf[10] * 75 +
+ buf[11];
+ }
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /*
+ * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
+ * even though the spec says it should only be returned when an
+ * audio play operation finishes.
+ */
+ case SUBQ_STATUS_DONE:
+ case SUBQ_STATUS_NONE:
+ case SUBQ_STATUS_INVALID:
+ if (oldmode == WM_CDM_PLAYING)
+ *mode = WM_CDM_TRACK_DONE;
+ else
+ *mode = WM_CDM_STOPPED;
+ break;
+
+ /*
+ * This usually means there's no disc in the drive.
+ */
+ case SUBQ_STATUS_NO_DISC:
+ break;
+
+ /*
+ * This usually means the user ejected the CD manually.
+ */
+ case SUBQ_STATUS_ERROR:
+ break;
+
+ case SUBQ_ILLEGAL: /* call didn't really succeed */
+ break;
+
+ default:
+ *mode = WM_CDM_UNKNOWN;
+#ifndef NDEBUG
+ if( getenv( "WORKMAN_DEBUG" ) != NULL )
+ printf("wm_scsi2_get_drive_status: status is 0x%x\n",
+ buf[1]);
+#endif
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
+ */
+int
+wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks)
+{
+ unsigned char buf[4];
+
+ if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
+ 0, 0, 0, 0, 0, sizeof(buf) / 256,
+ sizeof(buf) % 256, 0,0,0))
+ return (-1);
+
+ *tracks = buf[3] - buf[2] + 1;
+ return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+wm_scsi2_pause(struct wm_drive *d)
+{
+ return (wm_scsi2_pause_resume(d, 0));
+}
+
+/*
+ * Resume playing after a pause.
+ */
+int
+wm_scsi2_resume(struct wm_drive *d)
+{
+ return (wm_scsi2_pause_resume(d, 1));
+}
+
+/*
+ * Stop playing the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_stop(struct wm_drive *d)
+{
+ return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Eject the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_eject(struct wm_drive *d)
+{
+ /* Unlock the disc (possibly unnecessary). */
+ if (wm_scsi2_prevent(d, 0))
+ return (-1);
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for ejecting...\n");
+ return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Something like a dummy. The SCSI-2 specs are too hard for me to
+ * understand here...
+ *
+ * If you have the correct command handy, please send the code to
+ * milliByte@DeathsDoor.com
+ */
+int
+wm_scsi2_closetray(struct wm_drive *d)
+{
+ wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for closing...\n");
+ return (sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Get the volume by doing a MODE SENSE command.
+ */
+int
+wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right)
+{
+ unsigned char mode[16];
+
+ *left = *right = -1;
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ *left = ((int) mode[9] * 100) / 255;
+ *right = ((int) mode[11] * 100) / 255;
+
+ return (0);
+}
+
+/*
+ * Set the volume by doing a MODE SELECT command.
+ */
+int
+wm_scsi2_set_volume(struct wm_drive *d, int left, int right)
+{
+ unsigned char mode[16];
+
+ /* Get the current audio parameters first. */
+ if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+ return (-1);
+
+ /* Tweak the volume part of the parameters. */
+ mode[9] = (left * 255) / 100;
+ mode[11] = (right * 255) / 100;
+
+ /* And send them back to the drive. */
+ return (wm_scsi_mode_select(d, mode, sizeof(mode)));
+}
+
+/*------------------------------------------------------------------------*
+ * wm_scsi_get_cdtext(drive, buffer, lenght)
+ *
+ * Return a buffer with cdtext-stream. buffer mus be allocated and filled
+ *
+ *
+ *------------------------------------------------------------------------*/
+
+int
+wm_scsi_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_length)
+{
+ int ret;
+ unsigned char temp[8];
+ unsigned char *dynamic_temp;
+ int cdtext_possible;
+ unsigned short cdtext_data_length;
+ unsigned long feature_list_length;
+#define IGNORE_FEATURE_LIST
+#ifndef IGNORE_FEATURE_LIST
+ struct feature_list_header *pHeader;
+ struct feature_descriptor_cdread *pDescriptor;
+#endif /* IGNORE_FEATURE_LIST */
+
+ dynamic_temp = NULL;
+ cdtext_possible = 0;
+ wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi_get_cdtext entered\n");
+
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: use GET_FEATURY_LIST(0x46)...\n");
+ ret = sendscsi(d, temp, 8, 1,
+ 0x46, 0x02, 0x00, 0x1E, 0,
+ 0, 0, 0, 8, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: GET_FEATURY_LIST(0x46) not implemented or broken. ret = %i!\n", ret);
+#ifndef IGNORE_FEATURE_LIST
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: Try #define IGNORE_FEATURE_LIST in libwm/scsi.c\n");
+#else
+ cdtext_possible = 1;
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) ignored. It's OK, because many CDROMS don't implement this feature\n");
+#endif /* IGNORE_FEATURE_LIST */
+ }
+ else
+ {
+ feature_list_length = temp[0]*0xFFFFFF + temp[1]*0xFFFF + temp[2]*0xFF + temp[3] + 4;
+
+ dynamic_temp = malloc(feature_list_length);
+
+ if(!dynamic_temp)
+ return -1;
+
+ memset(dynamic_temp, 0, feature_list_length);
+ ret = sendscsi(d, dynamic_temp, feature_list_length, 1,
+ 0x46, 0x02, 0x00, 0x1E, 0, 0,
+ 0, (feature_list_length>>8) & 0xFF, feature_list_length & 0xFF, 0, 0, 0);
+
+
+#ifndef IGNORE_FEATURE_LIST
+ if(!ret)
+ {
+ pHeader = (struct feature_list_header*)dynamic_temp;
+/* printf("length = %i, profile = 0x%02X%02X\n", pHeader->lenght_lsb, pHeader->profile_msb, pHeader->profile_lsb);*/
+ pDescriptor = (struct feature_descriptor_cdread*)(dynamic_temp + sizeof(struct feature_list_header));
+/* printf("code = 0x%02X%02X, settings = 0x%02X, add_length = %i, add_settings = 0x%02X \n",
+ pDescriptor->feature_code_msb, pDescriptor->feature_code_lsb, pDescriptor->settings,
+ pDescriptor->add_lenght, pDescriptor->add_settings);*/
+
+ cdtext_possible = pDescriptor->add_settings & 0x01;
+ }
+ else
+ {
+ cdtext_possible = 0;
+ }
+
+#else
+ cdtext_possible = 1;
+#endif /* IGNORE_FEATURE_LIST */
+
+ free (dynamic_temp);
+ dynamic_temp = 0;
+ }
+
+ if(!cdtext_possible)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) says, CDTEXT is not present!\n");
+ return EXIT_SUCCESS;
+ }
+
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read, how long CDTEXT is?\n");
+ ret = sendscsi(d, temp, 4, 1,
+ SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
+ 0, 0, 4, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
+ "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
+ }
+ else
+ {
+ cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
+ /* cdtext_data_length%18 == 0;? */
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: CDTEXT is %i byte(s) long\n", cdtext_data_length);
+ /* cdc_buffer[2]; cdc_buffer[3]; reserwed */
+ dynamic_temp = malloc(cdtext_data_length);
+ if(!dynamic_temp)
+ return -1;
+
+ memset(dynamic_temp, 0, cdtext_data_length);
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read CDTEXT\n");
+ ret = sendscsi(d, dynamic_temp, cdtext_data_length, 1,
+ SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0,
+ 0, (cdtext_data_length>>8) & 0xFF, cdtext_data_length & 0xFF, 0, 0, 0);
+
+ if(ret)
+ {
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS,
+ "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret);
+ }
+ else
+ {
+ cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */
+ wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: read %i byte(s) of CDTEXT\n", cdtext_data_length);
+
+ /* send cdtext only 18 bytes packs * ? */
+ *(p_buffer_length) = cdtext_data_length - 4;
+ *pp_buffer = malloc(*p_buffer_length);
+ if(!(*pp_buffer))
+ {
+ return -1;
+ }
+ memcpy(*pp_buffer, dynamic_temp + 4, *p_buffer_length);
+ }
+ free(dynamic_temp);
+ dynamic_temp = 0;
+ }
+
+ return ret;
+} /* wm_scsi_get_cdtext() */
diff --git a/kscd/libwm/wm_helpers.c b/kscd/libwm/wm_helpers.c
new file mode 100644
index 00000000..109efe1b
--- /dev/null
+++ b/kscd/libwm/wm_helpers.c
@@ -0,0 +1,238 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Frsterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * milliByte@DeathsDoor.com
+ *
+ * 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 library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Some helpful functions...
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include "include/workman_defs.h"
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_MISC
+
+int wm_lib_verbosity = WM_MSG_LEVEL_NONE;
+
+/*
+ * Some seleced functions of version reporting follow...
+ */
+
+int wm_libver_major( void ){return WM_LIBVER_MAJOR;}
+int wm_libver_minor( void ){return WM_LIBVER_MINOR;}
+int wm_libver_pl( void ){return WM_LIBVER_PL;}
+
+char *wm_libver_name( void )
+{
+ char *s = NULL;
+
+ wm_strmcat(&s, WM_LIBVER_NAME);
+ return s;
+} /* wm_libver_name() */
+
+char *wm_libver_number( void )
+{
+ char *s = NULL;
+
+ s = malloc(10);
+ /* this is not used very often, so don't care about speed...*/
+ sprintf(s, "%d.%d.%d", wm_libver_major(), wm_libver_minor(), wm_libver_pl());
+ return s;
+} /* wm_libver_number() */
+
+char *wm_libver_date( void )
+{
+ char *s = NULL;
+ wm_strmcat(&s, __DATE__);
+ return s;
+} /* wm_libver_date() */
+
+char *wm_libver_string( void )
+{
+ char *s = NULL;
+
+ wm_strmcat( &s, wm_libver_name() );
+ wm_strmcat( &s, " " );
+ wm_strmcat( &s, wm_libver_number() );
+ return s;
+} /* wm_libver_string() */
+
+
+/*
+ *
+ * Now for some memory management...
+ *
+ */
+
+/* Free some memory and set a pointer to null. */
+void freeup( char **x )
+{
+ if (*x != NULL)
+ {
+ free(*x);
+ *x = NULL;
+ }
+} /* freeup() */
+
+/* Copy into a malloced string. */
+void
+wm_strmcpy( char **t, const char *s )
+{
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy(%s, '%s')\n", *t, s);
+ if (*t != NULL)
+ {
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy freeing pointer %p\n", *t);
+ free(*t);
+ }
+
+ *t = malloc(strlen(s) + 1);
+ if (*t == NULL)
+ {
+ perror("wm_strmcpy");
+ exit(1);
+ }
+
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy finally copying (%p, '%s')\n", *t, s);
+ strncpy(*t, s, strlen(s));
+} /* wm_strmcpy() */
+
+/* Add to a malloced string. */
+void
+wm_strmcat( char **t, const char *s)
+{
+ int len = strlen(s) + 1;
+
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcat(%s, %s)\n", *t, s);
+
+ if (*s == '\0')
+ return;
+
+ if (*t != NULL)
+ {
+ len += strlen(*t);
+ *t = realloc(*t, len);
+ if (*t == NULL)
+ {
+ perror("wm_strmcat");
+ exit(1);
+ }
+ strcat(*t, s);
+ }
+ else
+ wm_strmcpy(t, s);
+} /* wm_strmcat() */
+
+/* Duplicate a string. Some systems have this in libc, but not all. */
+char *
+wm_strdup( char *s )
+{
+ char *new;
+
+ new = malloc(strlen(s) + 1);
+ if (new)
+ strcpy(new, s);
+ return (new);
+} /* wm_strdup() */
+
+
+/*
+ * set and get verbosity level.
+ */
+void wm_lib_set_verbosity( int level )
+{
+ if( WM_MSG_LEVEL_NONE <= level && level <= WM_MSG_LEVEL_DEBUG )
+ {
+ wm_lib_verbosity = level;
+ wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "Verbosity set to %d|%d\n", WM_MSG_LEVEL_DEBUG, level & WM_MSG_CLASS_ALL);
+ }
+} /* wm_lib_set_verbosity */
+
+int wm_lib_get_verbosity( void )
+{
+ return wm_lib_verbosity;
+}
+
+/*
+ * wm_lib_message().
+ *
+ * any message that falls into allowed classes and has at least
+ * verbosity level wm_lib_verbosity & 0xf will be printed.
+ *
+ * Usage:
+ *
+ * wm_lib_message( WM_MSG_LEVEL | WM_MSG_CLASS, "format", contents);
+ *
+ * To simplify the usage, you may simply use WM_MSG_CLASS. It should be
+ * defined in each module to reflect the correct message class.
+ *
+ */
+void wm_lib_message( unsigned int level, const char *fmt, ... )
+{
+ va_list ap;
+ /* verbosity level */
+ unsigned int vlevel = wm_lib_verbosity & 0xf;
+ /* allowed classes */
+ unsigned int vclass = (level & WM_MSG_CLASS_ALL) & (wm_lib_verbosity & WM_MSG_CLASS_ALL);
+
+ /*
+ * just give me the level
+ */
+ level &= 0xf;
+ if(level <= WM_MSG_LEVEL_NONE)
+ {
+ fprintf(stderr, "LibWorkMan warning: A LibWorkMan programmer specified an invalid message level.\n");
+ }
+ /*
+ * print it only if level and class are allowed.
+ */
+ if( (level <= vlevel) && (vclass != 0) )
+ {
+ fprintf(stderr, "libWorkMan: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+} /* wm_lib_message() */
+
+/*
+ * Simulate usleep() using select().
+ */
+int
+wm_susleep( int usec )
+{
+ struct timeval tv;
+
+ timerclear(&tv);
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ return (select(0, NULL, NULL, NULL, &tv));
+} /* wm_susleep() */
+
+
diff --git a/kscd/panel.ui b/kscd/panel.ui
new file mode 100644
index 00000000..3b6b218a
--- /dev/null
+++ b/kscd/panel.ui
@@ -0,0 +1,468 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>kscdPanelDlg</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>kscdPanelDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>516</width>
+ <height>198</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>KsCD</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>songListCB</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>timeIcon</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Track progress</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>timeSlider</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="tracking">
+ <bool>false</bool>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Track progress</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>volumeIcon</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Volume control</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>volumeSlider</cstring>
+ </property>
+ <property name="maxValue">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Volume control</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>ejectPB</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;ject</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QFrame" row="0" column="2" rowspan="3" colspan="2">
+ <property name="name">
+ <cstring>backdrop</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>270</width>
+ <height>80</height>
+ </size>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>9</pointsize>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame" row="0" column="0" rowspan="2" colspan="2">
+ <property name="name">
+ <cstring>frameleds</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>35</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>statuslabel</cstring>
+ </property>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>volumelabel</cstring>
+ </property>
+ <property name="text">
+ <string>Vol: --</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>totaltimelabel</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>tracklabel</cstring>
+ </property>
+ <property name="text">
+ <string>--/--</string>
+ </property>
+ </widget>
+ <widget class="QWidget" row="0" column="4" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>symbols</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text" stdset="0">
+ <string>Vol: --</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>artistlabel</cstring>
+ </property>
+ <property name="text">
+ <string>Artist</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>titlelabel</cstring>
+ </property>
+ <property name="text">
+ <string>Title</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QPushButton" row="3" column="0">
+ <property name="name">
+ <cstring>shufflePB</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;andom</string>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="3">
+ <property name="name">
+ <cstring>infoPB</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xtras</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>playPB</cstring>
+ </property>
+ <property name="text">
+ <string>Pla&amp;y</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>stopPB</cstring>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="2">
+ <property name="name">
+ <cstring>cddbPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;CDDB</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>repeatPB</cstring>
+ </property>
+ <property name="text">
+ <string>Loop</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>nextPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Next</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>prevPB</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;vious</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>playPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>playClicked()</slot>
+ </connection>
+ <connection>
+ <sender>nextPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>nextClicked()</slot>
+ </connection>
+ <connection>
+ <sender>prevPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>prevClicked()</slot>
+ </connection>
+ <connection>
+ <sender>stopPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>stopClicked()</slot>
+ </connection>
+ <connection>
+ <sender>ejectPB</sender>
+ <signal>clicked()</signal>
+ <receiver>kscdPanelDlg</receiver>
+ <slot>ejectClicked()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>songListCB</tabstop>
+ <tabstop>timeSlider</tabstop>
+ <tabstop>volumeSlider</tabstop>
+ <tabstop>playPB</tabstop>
+ <tabstop>stopPB</tabstop>
+ <tabstop>ejectPB</tabstop>
+ <tabstop>prevPB</tabstop>
+ <tabstop>nextPB</tabstop>
+ <tabstop>shufflePB</tabstop>
+ <tabstop>repeatPB</tabstop>
+ <tabstop>cddbPB</tabstop>
+ <tabstop>infoPB</tabstop>
+</tabstops>
+<slots>
+ <slot>playClicked()</slot>
+ <slot>stopClicked()</slot>
+ <slot>prevClicked()</slot>
+ <slot>ejectClicked()</slot>
+ <slot returnType="bool">nextClicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kscd/prefs.kcfgc b/kscd/prefs.kcfgc
new file mode 100644
index 00000000..89d94319
--- /dev/null
+++ b/kscd/prefs.kcfgc
@@ -0,0 +1,5 @@
+# Code generation options for kconfig_compiler
+File=kscd.kcfg
+ClassName=Prefs
+Singleton=true
+Mutators=true
diff --git a/kscd/version.h b/kscd/version.h
new file mode 100644
index 00000000..9a87fa75
--- /dev/null
+++ b/kscd/version.h
@@ -0,0 +1 @@
+#define KSCDVERSION "1.6"
diff --git a/kscd/workman2cddb.pl b/kscd/workman2cddb.pl
new file mode 100755
index 00000000..539ce929
--- /dev/null
+++ b/kscd/workman2cddb.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+#
+# usage: just start it. Then copy the files to the local database.
+#
+# I have a extra cddb category (x-converted) for them, becauce I'm to
+# lazy to sort them all into the right category...
+#
+# make sure the output directory does *not* exist, the script refuses
+# to work if it does (just to make sure that it does not overwrite
+# something important)
+#
+# (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> / Copying: GNU GPL
+#
+
+# config: output directory
+$dir="/tmp/cddb";
+
+#############################################################################
+
+$out=0;
+open(IN,"$ENV{'HOME'}/.workmandb") ||\
+ die "open $ENV{'HOME'}/.workmandb: $!";
+mkdir("$dir",0777) || die "mkdir $dir: $!";
+
+while (<IN>) {
+ if (/^tracks/) {
+ if ($out) {
+ print OUT "EXTD=\n";
+ for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ printf OUT "EXTT%d=\n",$i-2;
+ }
+ print OUT "PLAYORDER=\n";
+ close OUT;
+ }
+ @t = split;
+ for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ $len = $t[$i]/75;
+ for ($quer = 0; $len > 0; $quer += $len % 10, $len /= 10) {};
+ $n += $quer + ($len % 60);
+ }
+ $m = ($t[$t[1]+2] - int($t[2]/75));
+ $id = (($n % 0xff) << 24) | ($m << 8) | $t[1];
+ $magic = sprintf("%08x",$id);
+ open(OUT,">$dir/$magic") || die "open $dir/$magic: $!";
+ $out=1;
+ }
+
+ $cdname = $1 if (/^cdname (.*)/);
+ if (/^artist (.*)/) {
+ $artist = $1;
+
+ print OUT "# xmcd 2.0 CD database file\n";
+ print OUT "#\n";
+ print OUT "# Track frame offsets:\n";
+ for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ print OUT "#\t$t[$i]\n";
+ }
+ print OUT "#\n";
+ print OUT "# Disc length: $t[$t[1]+2] seconds\n";
+ print OUT "#\n";
+ print OUT "# Revision: 1\n";
+ print OUT "# Submitted via: wdb2cddb 1.0\n";
+ print OUT "#\n";
+
+ print OUT "DISCID=$magic\n";
+ print OUT "DTITLE=$artist / $cdname\n";
+ print "$magic - $artist / $cdname\n";
+ $track=0;
+ }
+ if (/^track (.*)/) {
+ print OUT "TTITLE$track=$1\n";
+ $track++;
+ }
+}
+
+close IN;
+
+print OUT "EXTD=\n";
+for ($i = 2, $n = 0; $i < $t[1]+2; $i++) {
+ printf OUT "EXTT%d=\n",$i-2;
+}
+print OUT "PLAYORDER=\n";
+close OUT;
+
+print "\n*** The CDDB files are in $dir ***\n\n";
+
diff --git a/kscd/xmcd.desktop b/kscd/xmcd.desktop
new file mode 100644
index 00000000..a89db6f2
--- /dev/null
+++ b/kscd/xmcd.desktop
@@ -0,0 +1,75 @@
+[Desktop Entry]
+Comment=CD Database File
+Comment[af]=Cd Databasis Lêer
+Comment[az]=CD Databeyzi Faylı
+Comment[bg]=Файл с база от данни за КД
+Comment[bn]=সিডি ডেটাবেস ফাইল
+Comment[br]=Restr Stlennvon CD
+Comment[bs]=CD baza podataka
+Comment[ca]=Fitxer de base de dades de CD
+Comment[cs]=Soubor CD databáze
+Comment[cy]=Ffeil Cronfa Ddata CD
+Comment[da]=Cd-databasefil
+Comment[de]=CD-Datenbank-Datei
+Comment[el]=Αρχείο βάσης δεδομένων CD
+Comment[eo]=Lumdiskdatumbazo-dosiero
+Comment[es]=Base de datos de CDs
+Comment[et]=CD andmebaasi fail
+Comment[eu]=CD datubase fitxategia
+Comment[fa]=پروندۀ دادگان دیسک فشرده
+Comment[fi]=CD-tietokantatiedosto
+Comment[fr]=Banque de données pour CD
+Comment[gl]=Ficheiro de base de datos de CD
+Comment[he]=קובץ מסד נתוני תקליטור
+Comment[hi]=सीडी डाटाबेस फ़ाइल
+Comment[hr]=CD baza datoteka
+Comment[hu]=CD-adatbázis fájl
+Comment[id]=File Database CD
+Comment[is]=CD gagnasafnsskrá
+Comment[it]=File di database CD
+Comment[ja]=CD データベースファイル
+Comment[kk]=CD деректер қоры файлы
+Comment[km]=ឯកសារ​មូលដ្ឋាន​ទិន្នន័យ​ស៊ីឌី
+Comment[ko]=CD 데이터베이스 파일
+Comment[lt]=CD duomenų bazės Byla
+Comment[lv]=CD Datubāzes Fails
+Comment[mk]=Датотека за база на податоци за CD
+Comment[ms]=Fail Pangkalan Data CD
+Comment[mt]=Fajl Database tas-CDs
+Comment[nb]=CD-databasefil
+Comment[nds]=CD-Datenbankdatei
+Comment[ne]=CD डाटाबेस फाइल
+Comment[nl]=Bestand met cd-database
+Comment[nn]=CD-databasefil
+Comment[pa]=CD ਡਾਟਾਬੇਸ ਫਾਇਲ
+Comment[pl]=Baza danych CD
+Comment[pt]=Ficheiro de dados de CDs
+Comment[pt_BR]=Arquivo de Dados de CDs
+Comment[ro]=Fişier bază de date CD
+Comment[ru]=Файл базы данных CD
+Comment[se]=CD-diehtovuođđofiila
+Comment[sk]=Databázový súbor CD
+Comment[sl]=Zbirka podatkov o CD
+Comment[sr]=Фајл CD базе података
+Comment[sr@Latn]=Fajl CD baze podataka
+Comment[sv]=Cd-databasfil
+Comment[ta]=குறுந்தகடு தரவுத்தளக் கோப்பு
+Comment[tg]=Файли Манбаъи Додаҳои Диски Фишурда
+Comment[th]=แฟ้มฐานข้อมูลซีดี
+Comment[tr]=CD Veritabanı Dosyası
+Comment[uk]=Файл бази даних CD
+Comment[uz]=CD maʼlumot baza fayli
+Comment[uz@cyrillic]=CD маълумот база файли
+Comment[ven]=CD ya databeizi ya faela
+Comment[wa]=Båze di dnêyes des plakes
+Comment[xh]=Ifayile yesiseko sedata ye CD
+Comment[zh_CN]=CD 数据库文件
+Comment[zh_HK]=CD 資料庫檔案
+Comment[zh_TW]=CD 資料庫檔案
+Comment[zu]=Ifayela yesiseko sedata ye CD
+DefaultApp=kedit
+Icon=cdtrack
+Type=MimeType
+MimeType=text/xmcd
+Patterns=
+