Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNY00123 <NY00123@vogons.org>2018-12-22 21:14:00 +0300
committerPatryk Obara <dreamer.tan@gmail.com>2019-09-15 22:13:21 +0300
commitf1b82ef55a2ec21148af6e82fa0e3693a5661707 (patch)
tree382b4c628a68fbbae60b61629e7a611eff1dfe7d
parentc9f79add62a16d9b4a43346bf5e13ef5b4c1150b (diff)
Implement an adaptation to SDL 2.0vogons/sdl2-android-r4178
Alpha-level Android build attached Imported from: https://www.vogons.org/viewtopic.php?f=41&t=34770&start=140
-rw-r--r--acinclude.m4328
-rw-r--r--android-project/AndroidManifest.xml29
-rw-r--r--android-project/ant.properties17
-rw-r--r--android-project/build.properties17
-rw-r--r--android-project/build.xml93
-rw-r--r--android-project/default.properties11
-rw-r--r--android-project/instructions.txt90
-rw-r--r--android-project/jni/Android.mk1
-rw-r--r--android-project/jni/Application.mk6
-rw-r--r--android-project/jni/src/Android.mk82
-rw-r--r--android-project/jni/src/config.h320
-rw-r--r--android-project/proguard-project.txt20
-rw-r--r--android-project/project.properties14
-rw-r--r--android-project/res/layout/main.xml13
-rw-r--r--android-project/res/values/strings.xml4
-rw-r--r--android-project/src/com/dosbox/emu/DOSBoxActivity.java59
-rw-r--r--android-project/src/org/libsdl/app/SDLActivity.java1077
-rw-r--r--android-project/src/org/libsdl/app/note.txt4
-rw-r--r--configure.ac106
-rw-r--r--src/Makefile.am5
-rw-r--r--src/dos/cdrom.cpp9
-rw-r--r--src/dos/cdrom.h13
-rw-r--r--src/dos/cdrom_aspi_win32.cpp6
-rw-r--r--src/dos/cdrom_ioctl_linux.cpp6
-rw-r--r--src/dos/cdrom_ioctl_os2.cpp6
-rw-r--r--src/dos/cdrom_ioctl_win32.cpp9
-rw-r--r--src/dos/dos_mscdex.cpp6
-rw-r--r--src/dos/dos_programs.cpp49
-rw-r--r--src/dos/drive_local.cpp15
-rw-r--r--src/gui/sdl_mapper.cpp314
-rw-r--r--src/gui/sdlmain.cpp1199
-rw-r--r--src/hardware/mixer.cpp58
-rw-r--r--src/misc/cross.cpp13
-rw-r--r--src/sdl_cdrom/Makefile.am10
-rw-r--r--src/sdl_cdrom/SDL_syscdrom.h76
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_aix.c665
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_beos.cc414
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_bsdi.c547
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_dc.c172
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_dummy.c48
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_freebsd.c411
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_linux.c569
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_mint.c324
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_openbsd.c421
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_os2.c398
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_osf.c449
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_qnx.c556
-rw-r--r--src/sdl_cdrom/SDL_syscdrom_win32.c391
-rw-r--r--src/sdl_cdrom/compat_SDL_cdrom.c347
-rw-r--r--src/sdl_cdrom/compat_SDL_cdrom.h206
-rw-r--r--src/sdl_cdrom/macos/Makefile.am5
-rw-r--r--src/sdl_cdrom/macos/SDL_syscdrom.c530
-rw-r--r--src/sdl_cdrom/macos/SDL_syscdrom_c.h140
-rw-r--r--src/sdl_cdrom/macosx/AudioFilePlayer.c366
-rw-r--r--src/sdl_cdrom/macosx/AudioFilePlayer.h178
-rw-r--r--src/sdl_cdrom/macosx/AudioFileReaderThread.c616
-rw-r--r--src/sdl_cdrom/macosx/CDPlayer.c643
-rw-r--r--src/sdl_cdrom/macosx/CDPlayer.h69
-rw-r--r--src/sdl_cdrom/macosx/Makefile.am5
-rw-r--r--src/sdl_cdrom/macosx/SDLOSXCAGuard.c205
-rw-r--r--src/sdl_cdrom/macosx/SDLOSXCAGuard.h116
-rw-r--r--src/sdl_cdrom/macosx/SDL_syscdrom.c519
-rw-r--r--src/sdl_cdrom/macosx/SDL_syscdrom_c.h136
63 files changed, 13254 insertions, 277 deletions
diff --git a/acinclude.m4 b/acinclude.m4
index 69adc0f03..32b2aa352 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -1,164 +1,147 @@
-dnl AM_PATH_SDL([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]])
-dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS
-dnl
-AC_DEFUN([AM_PATH_SDL],
-[dnl
-dnl Get the cflags and libraries from the sdl-config script
-dnl
-AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)],
- sdl_prefix="$withval", sdl_prefix="")
-AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)],
- sdl_exec_prefix="$withval", sdl_exec_prefix="")
-AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program],
- , enable_sdltest=yes)
-
+dnl Check if we have SDL (sdl-config, header and library) version >= 1.2.0
+dnl Extra options: --with-sdl-prefix=PATH and --with-sdl={sdl12,sdl2}
+dnl Output:
+dnl SDL_CFLAGS and SDL_LIBS are set and AC_SUBST-ed
+dnl HAVE_SDL_H is AC_DEFINE-d
+
+AC_DEFUN([EXULT_CHECK_SDL],[
+ exult_backupcppflags="$CPPFLAGS"
+ exult_backupldflags="$LDFLAGS"
+ exult_backuplibs="$LIBS"
+
+ exult_sdlok=yes
+
+ AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], sdl_prefix="$withval", sdl_prefix="")
+ AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], sdl_exec_prefix="$withval", sdl_exec_prefix="")
+ AC_ARG_WITH(sdl, [ --with-sdl=sdl12,sdl2 Select a specific version of SDL to use (optional)], sdl_ver="$withval", sdl_ver="")
+
+ dnl First: find sdl-config or sdl2-config
+ exult_extra_path=$prefix/bin:$prefix/usr/bin
+ sdl_args=""
if test x$sdl_exec_prefix != x ; then
sdl_args="$sdl_args --exec-prefix=$sdl_exec_prefix"
- if test x${SDL_CONFIG+set} != xset ; then
- SDL_CONFIG=$sdl_exec_prefix/bin/sdl-config
- fi
+ exult_extra_path=$sdl_exec_prefix/bin
fi
if test x$sdl_prefix != x ; then
sdl_args="$sdl_args --prefix=$sdl_prefix"
- if test x${SDL_CONFIG+set} != xset ; then
- SDL_CONFIG=$sdl_prefix/bin/sdl-config
- fi
+ exult_extra_path=$sdl_prefix/bin
fi
-
- AC_PATH_PROG(SDL_CONFIG, sdl-config, no)
- min_sdl_version=ifelse([$1], ,0.11.0,$1)
- AC_MSG_CHECKING(for SDL - version >= $min_sdl_version)
- no_sdl=""
+ if test x"$sdl_ver" = xsdl12 ; then
+ exult_sdl_progs=sdl-config
+ elif test x"$sdl_ver" = xsdl2 ; then
+ exult_sdl_progs=sdl2-config
+ else
+ dnl NB: This line implies we prefer SDL 1.2 to SDL 2.0
+ exult_sdl_progs="sdl-config sdl2-config"
+ fi
+ AC_PATH_PROGS(SDL_CONFIG, $exult_sdl_progs, no, [$exult_extra_path:$PATH])
if test "$SDL_CONFIG" = "no" ; then
- no_sdl=yes
+ exult_sdlok=no
else
- SDL_CFLAGS=`$SDL_CONFIG $sdlconf_args --cflags`
- SDL_LIBS=`$SDL_CONFIG $sdlconf_args --libs`
+ SDL_CFLAGS=`$SDL_CONFIG $sdl_args --cflags`
+ SDL_LIBS=`$SDL_CONFIG $sdl_args --libs`
sdl_major_version=`$SDL_CONFIG $sdl_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'`
sdl_minor_version=`$SDL_CONFIG $sdl_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'`
- sdl_micro_version=`$SDL_CONFIG $sdl_config_args --version | \
+ sdl_patchlevel=`$SDL_CONFIG $sdl_args --version | \
sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'`
- if test "x$enable_sdltest" = "xyes" ; then
- ac_save_CFLAGS="$CFLAGS"
- ac_save_LIBS="$LIBS"
- CFLAGS="$CFLAGS $SDL_CFLAGS"
- LIBS="$LIBS $SDL_LIBS"
-dnl
-dnl Now check if the installed SDL is sufficiently new. (Also sanity
-dnl checks the results of sdl-config to some extent
-dnl
- rm -f conf.sdltest
- AC_TRY_RUN([
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "SDL.h"
-
-char*
-my_strdup (char *str)
-{
- char *new_str;
-
- if (str)
- {
- new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char));
- strcpy (new_str, str);
- }
+ if test $sdl_major_version -eq 1 ; then
+ sdl_ver=sdl12
+ else
+ sdl_ver=sdl2
+ fi
+ fi
+
+ if test x"$sdl_ver" = xsdl2 ; then
+ REQ_MAJOR=2
+ REQ_MINOR=0
+ REQ_PATCHLEVEL=0
else
- new_str = NULL;
-
- return new_str;
-}
-
-int main (int argc, char *argv[])
-{
- int major, minor, micro;
- char *tmp_version;
-
- /* This hangs on some systems (?)
- system ("touch conf.sdltest");
- */
- { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); }
-
- /* HP/UX 9 (%@#!) writes to sscanf strings */
- tmp_version = my_strdup("$min_sdl_version");
- if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
- printf("%s, bad version string\n", "$min_sdl_version");
- exit(1);
- }
-
- if (($sdl_major_version > major) ||
- (($sdl_major_version == major) && ($sdl_minor_version > minor)) ||
- (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro)))
+ REQ_MAJOR=1
+ REQ_MINOR=2
+ REQ_PATCHLEVEL=0
+ fi
+ REQ_VERSION=$REQ_MAJOR.$REQ_MINOR.$REQ_PATCHLEVEL
+
+ AC_MSG_CHECKING([for SDL - version >= $REQ_VERSION])
+
+
+ dnl Second: include "SDL.h"
+
+ if test x$exult_sdlok = xyes ; then
+ CPPFLAGS="$CPPFLAGS $SDL_CFLAGS"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+ #include "SDL.h"
+
+ int main(int argc, char *argv[])
{
return 0;
}
- else
- {
- printf("\n*** 'sdl-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version);
- printf("*** of SDL required is %d.%d.%d. If sdl-config is correct, then it is\n", major, minor, micro);
- printf("*** best to upgrade to the required version.\n");
- printf("*** If sdl-config was wrong, set the environment variable SDL_CONFIG\n");
- printf("*** to point to the correct copy of sdl-config, and remove the file\n");
- printf("*** config.cache before re-running configure\n");
- return 1;
- }
-}
+ ]],)],sdlh_found=yes,sdlh_found=no)
+
+ if test x$sdlh_found = xno; then
+ exult_sdlok=no
+ else
+ AC_DEFINE(HAVE_SDL_H, 1, [Define to 1 if you have the "SDL.h" header file])
+ fi
+ fi
+
+ dnl Next: version check (cross-compile-friendly idea borrowed from autoconf)
+ dnl (First check version reported by sdl-config, then confirm
+ dnl the version in SDL.h matches it)
+
+ if test x$exult_sdlok = xyes ; then
+
+ if test ! \( \( $sdl_major_version -gt $REQ_MAJOR \) -o \( \( $sdl_major_version -eq $REQ_MAJOR \) -a \( \( $sdl_minor_version -gt $REQ_MINOR \) -o \( \( $sdl_minor_version -eq $REQ_MINOR \) -a \( $sdl_patchlevel -gt $REQ_PATCHLEVEL \) \) \) \) \); then
+ exult_sdlok="no, version < $REQ_VERSION found"
+ else
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+ #include "SDL.h"
+
+ int main(int argc, char *argv[])
+ {
+ static int test_array[1-2*!(SDL_MAJOR_VERSION==$sdl_major_version&&SDL_MINOR_VERSION==$sdl_minor_version&&SDL_PATCHLEVEL==$sdl_patchlevel)];
+ test_array[0] = 0;
+ return 0;
+ }
+ ]])],,[[exult_sdlok="no, version of SDL.h doesn't match that of sdl-config"]])
+ fi
+ fi
+
+ dnl Next: try linking
+
+ if test "x$exult_sdlok" = xyes; then
+ LIBS="$LIBS $SDL_LIBS"
+
+ AC_LINK_IFELSE([AC_LANG_SOURCE([[
+ #include "SDL.h"
-],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
- CFLAGS="$ac_save_CFLAGS"
- LIBS="$ac_save_LIBS"
- fi
+ int main(int argc, char* argv[]) {
+ SDL_Init(0);
+ return 0;
+ }
+ ]])],sdllinkok=yes,sdllinkok=no)
+ if test x$sdllinkok = xno; then
+ exult_sdlok=no
+ fi
fi
- if test "x$no_sdl" = x ; then
- AC_MSG_RESULT(yes)
- ifelse([$2], , :, [$2])
+
+ AC_MSG_RESULT($exult_sdlok)
+
+ LDFLAGS="$exult_backupldflags"
+ CPPFLAGS="$exult_backupcppflags"
+ LIBS="$exult_backuplibs"
+
+ if test "x$exult_sdlok" = xyes; then
+ AC_SUBST(SDL_CFLAGS)
+ AC_SUBST(SDL_LIBS)
+ ifelse([$1], , :, [$1])
else
- AC_MSG_RESULT(no)
- if test "$SDL_CONFIG" = "no" ; then
- echo "*** The sdl-config script installed by SDL could not be found"
- echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in"
- echo "*** your path, or set the SDL_CONFIG environment variable to the"
- echo "*** full path to sdl-config."
- else
- if test -f conf.sdltest ; then
- :
- else
- echo "*** Could not run SDL test program, checking why..."
- CFLAGS="$CFLAGS $SDL_CFLAGS"
- LIBS="$LIBS $SDL_LIBS"
- AC_TRY_LINK([
-#include <stdio.h>
-#include "SDL.h"
-], [ return 0; ],
- [ echo "*** The test program compiled, but did not run. This usually means"
- echo "*** that the run-time linker is not finding SDL or finding the wrong"
- echo "*** version of SDL. If it is not finding SDL, you'll need to set your"
- echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
- echo "*** to the installed location Also, make sure you have run ldconfig if that"
- echo "*** is required on your system"
- echo "***"
- echo "*** If you have an old version installed, it is best to remove it, although"
- echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
- [ echo "*** The test program failed to compile or link. See the file config.log for the"
- echo "*** exact error that occurred. This usually means SDL was incorrectly installed"
- echo "*** or that you have moved SDL since it was installed. In the latter case, you"
- echo "*** may want to edit the sdl-config script: $SDL_CONFIG" ])
- CFLAGS="$ac_save_CFLAGS"
- LIBS="$ac_save_LIBS"
- fi
- fi
- SDL_CFLAGS=""
- SDL_LIBS=""
- ifelse([$3], , :, [$3])
+ ifelse([$2], , :, [$2])
fi
- AC_SUBST(SDL_CFLAGS)
- AC_SUBST(SDL_LIBS)
- rm -f conf.sdltest
-])
+]);
dnl Configure Paths for Alsa
dnl Some modifications by Richard Boulton <richard-alsa@tartarus.org>
@@ -303,6 +286,73 @@ AC_SUBST(ALSA_CFLAGS)
AC_SUBST(ALSA_LIBS)
])
+dnl Configure platform for SDL_cdrom compatibility layer.
+dnl MUST be called after SDL version check (defined only for SDL 2.0).
+dnl Taken off configure.in from SDL 1.2 and then modified
+AC_DEFUN([COMPAT_SDL_CDROM_GET_PLATFORM],[
+
+if test x"$sdl_ver" = xsdl12 ; then
+ compat_sdl_cdrom_arch=undefined
+elif test x"$sdl_ver" = xsdl2 ; then
+ case "$host" in
+ arm-*-elf*) # FIXME: Can we get more specific for iPodLinux?
+ compat_sdl_cdrom_arch=linux
+ ;;
+ *-*-linux*|*-*-uclinux*|*-*-gnu*|*-*-k*bsd*-gnu|*-*-bsdi*|*-*-freebsd*|*-*-dragonfly*|*-*-netbsd*|*-*-openbsd*|*-*-sysv5*|*-*-solaris*|*-*-hpux*|*-*-irix*|*-*-aix*|*-*-osf*)
+ case "$host" in
+ *-*-linux*) compat_sdl_cdrom_arch=linux ;;
+ *-*-uclinux*) compat_sdl_cdrom_arch=linux ;;
+ *-*-kfreebsd*-gnu) compat_sdl_cdrom_arch=COMPAT_SDL_CDROM_PLATFORM_KFREEBSD_GNU ;;
+ *-*-knetbsd*-gnu) compat_sdl_cdrom_arch=knetbsd-gnu ;;
+ *-*-kopenbsd*-gnu) compat_sdl_cdrom_arch=kopenbsd-gnu ;;
+ *-*-gnu*) compat_sdl_cdrom_arch=gnu ;; # must be last of the gnu variants
+ *-*-bsdi*) compat_sdl_cdrom_arch=bsdi ;;
+ *-*-freebsd*) compat_sdl_cdrom_arch=freebsd ;;
+ *-*-dragonfly*) compat_sdl_cdrom_arch=freebsd ;;
+ *-*-netbsd*) compat_sdl_cdrom_arch=netbsd ;;
+ *-*-openbsd*) compat_sdl_cdrom_arch=openbsd ;;
+ *-*-sysv5*) compat_sdl_cdrom_arch=sysv5 ;;
+ *-*-solaris*) compat_sdl_cdrom_arch=solaris ;;
+ *-*-hpux*) compat_sdl_cdrom_arch=hpux ;;
+ *-*-irix*) compat_sdl_cdrom_arch=irix ;;
+ *-*-aix*) compat_sdl_cdrom_arch=aix ;;
+ *-*-osf*) compat_sdl_cdrom_arch=osf ;;
+ esac
+ ;;
+ *-*-qnx*)
+ compat_sdl_cdrom_arch=qnx
+ ;;
+ *-*-cygwin* | *-*-mingw32*)
+ compat_sdl_cdrom_arch=win32
+ ;;
+ *-wince*)
+ compat_sdl_cdrom_arch=win32
+ ;;
+ *-*-beos* | *-*-haiku*)
+ compat_sdl_cdrom_arch=beos
+ ;;
+ *-*-darwin* )
+ # This could be either full "Mac OS X", or plain "Darwin" which is
+ # just the OS X kernel sans upper layers like Carbon and Cocoa.
+ # Next line is broken
+ compat_sdl_cdrom_arch=macosx
+ ;;
+ *-*-mint*)
+ compat_sdl_cdrom_arch=mint
+ ;;
+ *-riscos)
+ compat_sdl_cdrom_arch=riscos
+ ;;
+ *)
+ compat_sdl_cdrom_arch=undefined
+ ;;
+ esac
+else
+ AC_MSG_ERROR([Compatible SDL version not found])
+fi
+
+]);
+
AH_TOP([
/*
* Copyright (C) 2002-2018 The DOSBox Team
diff --git a/android-project/AndroidManifest.xml b/android-project/AndroidManifest.xml
new file mode 100644
index 000000000..c4b2f1b49
--- /dev/null
+++ b/android-project/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.dosbox.emu"
+ android:versionCode="1"
+ android:versionName="1.0"
+ android:installLocation="auto">
+
+ <application android:label="@string/app_name"
+ android:icon="@drawable/icon"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ <activity android:name="DOSBoxActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <!-- Android 2.3.3 - minimum, 4.2 - target -->
+ <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
+
+ <!-- OpenGL ES 2.0 -->
+ <uses-feature android:glEsVersion="0x00020000" />
+
+ <!-- Allow writing to external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/android-project/ant.properties b/android-project/ant.properties
new file mode 100644
index 000000000..b0971e891
--- /dev/null
+++ b/android-project/ant.properties
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked into Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
diff --git a/android-project/build.properties b/android-project/build.properties
new file mode 100644
index 000000000..edc7f2305
--- /dev/null
+++ b/android-project/build.properties
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked in Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
diff --git a/android-project/build.xml b/android-project/build.xml
new file mode 100644
index 000000000..00b9bcaa1
--- /dev/null
+++ b/android-project/build.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This should be changed to the name of your project -->
+<project name="DOSBox" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/android-project/default.properties b/android-project/default.properties
new file mode 100644
index 000000000..4d6614c6f
--- /dev/null
+++ b/android-project/default.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-17
diff --git a/android-project/instructions.txt b/android-project/instructions.txt
new file mode 100644
index 000000000..1cee31e58
--- /dev/null
+++ b/android-project/instructions.txt
@@ -0,0 +1,90 @@
+NOTE: Chances are the (unofficially modified) DOSBox app *will* crash sooner or
+later, although one may still build a proper package.
+A default for DOSBox settings have been selected to make this (somewhat)
+more stable on Android and perform better: output=texture.
+Furthermore, cycles=max is the default on Android with this patch, so DOSBox
+is (again somewhat) faster out-of-the-box. Do *not* expect great performance,
+though.
+
+Instructions for building DOSBox (unofficial modification) for Android
+(originally done on a GNU/Linux desktop, may work on other platforms):
+
+1. Move/symlink SDL2 into the jni subdirectory. The directory name should be
+"SDL" (yeah, without the '2'). The SDL2 library itself can be obtained from
+the SDL website: http://www.libsdl.org/download-2.0.php
+
+2. Also move/symlink SDL2_net into the jni directory. The dir name should be
+"SDL_net" (again with no '2'). It can be obtained from the SDL_net webpage:
+http://www.libsdl.org/projects/SDL_net/
+
+In case of issues you can (probably) disable the usage of SDL_net in config.h,
+by disabling the various networking features.
+
+3. In general, you may want to play with the config.h file.
+Note that right now, SDL_sound is not used.
+
+4. Ensure that the "res" subdirectory has ready icons, as these are *not*
+bundled with the patch itself (which is textual). For instance, you should have
+a file given by the relative path android-project/res/drawable-ldpi/icon.png.
+
+5. With the exception of the first three steps (copying the android-project
+directory, moving/symlinking SDL and adding source files to Android.mk),
+follow all steps from the file docs/README-android.md bundled with SDL 2.0.
+
+6. Assuming it has actually been built successfully, there are great chances
+that the app is going to crash at some point. Maybe the splash screen can be
+seen for a little while before the crash. If the app has survived so far, you
+should find a dosbox-SVN.conf file on the (internal) SD card that you may like
+to edit. On many single-user devices it should be located at the relative path
+Android/data/com.dosbox.emu/files/ (relatively to the SD card's root).
+Playing with settings like "output", "machine" and "nosound" can help here. In
+fact, playing with any DOSBox setting you can think of can help. There are very
+great chances that a crash will occur at some later point, though.
+
+7. Considering this revision of the patch, an on-screen keyboard can be toggled
+using the Back button. Furthermore, physical keyboards can also be used
+(e.g. via Bluetooth or USB-OTG). For testing only the following "WiFi Keyboard"
+app can partially be used like a physical keyboard is connected directly:
+https://play.google.com/store/apps/details?id=com.volosyukivan
+
+Please note that in earlier revisions of this patch, the on-screen keyboard
+could be toggled using the Menu button, while the Back button simulated a
+client Escape key press by default. However, some newer Android-powered devices
+may come with built-in system buttons, but not such a Menu button, implying
+that a (relatively) large portion of the screen may be wasted simply for
+displaying an on-screen Menu button (and no more). So, the Back button is used
+for toggling the on-screen keyboard now. If you are interested in Escape key
+emulation, see the last point (and rough sketch) regarding mouse emulation.
+
+8. Assuming you do get a working command prompt within DOSBox, you may also
+type the following command in order to find the location of the dosbox-SVN.conf
+file (and save the currently set settings):
+
+config -writeconf
+
+9. Be warned that, at least on newer Android setups, the configuration file
+(along with the whole directory in which it resides) gets DELETED once you
+uninstall DOSBox. In fact, this is the behavior with Android apps in general
+(on such setups).
+
+10. A few very-basic changes have been applied to the mapper UI, but it is
+wrong to say it is fully ready. In particular, an accelerometer sensor
+(currently identified as an SDL joystick) may interfere with remaps. In such a
+case you may wish to manually edit your own customized mapper file or disable
+the (host) accelerometer/joystick subsystem.
+
+11. As for mouse emulation, mouse motion and button presses/releases are
+currently emulated by tapping on specific "invisible" portions of the screen.
+Yeah, it may not be the best way, but this is what you have for now.
+Note that mouse motion is emulated by using the touchscreen the way one uses a
+laptop's trackpad, i.e. in a "relative" fashion. For instance, to move a mouse
+cursor in some DOS game to the left, tap with a finger on an arbitrary location
+on the relevant part of the screen and then drag the finger to the left.
+
+Here are parts of the touch screen (the whole display) and what each of them
+emulates. These are all client mouse events, with the exception of the Escape
+key, which is a *host* key event and can be mapped to various things.
+
+/-----------------------------------------------------------------------\
+| Left | (H)Escape | Motion | Motion | Middle | Right |
+\-----------------------------------------------------------------------/
diff --git a/android-project/jni/Android.mk b/android-project/jni/Android.mk
new file mode 100644
index 000000000..5053e7d64
--- /dev/null
+++ b/android-project/jni/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/android-project/jni/Application.mk b/android-project/jni/Application.mk
new file mode 100644
index 000000000..e60d424df
--- /dev/null
+++ b/android-project/jni/Application.mk
@@ -0,0 +1,6 @@
+
+# Uncomment this if you're using STL in your project
+# See CPLUSPLUS-SUPPORT.html in the NDK documentation for more information
+# APP_STL := stlport_static
+APP_STL := stlport_shared
+APP_ABI := armeabi armeabi-v7a x86
diff --git a/android-project/jni/src/Android.mk b/android-project/jni/src/Android.mk
new file mode 100644
index 000000000..a5f9e61e2
--- /dev/null
+++ b/android-project/jni/src/Android.mk
@@ -0,0 +1,82 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := main
+
+SDL_PATH := ../SDL
+SDL_NET_PATH := ../SDL_net
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include \
+ $(LOCAL_PATH)/$(SDL_NET_PATH) \
+ ../../../include
+
+ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
+ LOCAL_CPPFLAGS += -DC_TARGETCPU=ARMV7LE
+endif
+ifeq ($(TARGET_ARCH_ABI),armeabi)
+ LOCAL_CPPFLAGS += -DC_TARGETCPU=ARMV4LE
+endif
+ifeq ($(TARGET_ARCH_ABI),x86)
+ LOCAL_CPPFLAGS += -DC_TARGETCPU=X86 -DC_FPU_X86=1
+endif
+ifeq ($(TARGET_ARCH_ABI),mips)
+ LOCAL_CPPFLAGS += -DC_TARGETCPU=MIPSEL
+endif
+# Some sizes for a few types (for now architecture independent but possibly not)
+LOCAL_CPPFLAGS += -DSIZEOF_INT_P=4 \
+ -DSIZEOF_UNSIGNED_CHAR=1 \
+ -DSIZEOF_UNSIGNED_INT=4 \
+ -DSIZEOF_UNSIGNED_LONG=4 \
+ -DSIZEOF_UNSIGNED_LONG_LONG=8 \
+ -DSIZEOF_UNSIGNED_SHORT=2
+
+LOCAL_SRC_PATH := ../../../src
+
+LOCAL_CPP_FEATURES += exceptions
+
+# Note: We don't want any cdrom cpp file differing from cdrom_image.cpp.
+# Furthermore, we compile zmbv.cpp only... and even that can't be done
+# without zlib. So, let's give up for now.
+LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
+ $(wildcard $(LOCAL_SRC_PATH)/dosbox.cpp) \
+ \
+ $(wildcard $(LOCAL_SRC_PATH)/cpu/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/cpu/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_dynrec/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_dynrec/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_dyn_x86/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_dyn_x86/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_full/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_full/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_normal/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/core_normal/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/debug/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/debug/*.cpp) \
+ \
+ $(wildcard $(LOCAL_SRC_PATH)/dos/cdrom.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/dos/cdrom_image.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/dos/d*.cpp) \
+ \
+ $(wildcard $(LOCAL_SRC_PATH)/fpu/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/fpu/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/gui/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/gui/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/hardware/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/hardware/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/hardware/serialport/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/hardware/serialport/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/ints/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/ints/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/libs/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/libs/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/misc/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/misc/*.cpp) \
+ $(wildcard $(LOCAL_SRC_PATH)/shell/*.c) \
+ $(wildcard $(LOCAL_SRC_PATH)/shell/*.cpp)
+
+LOCAL_SHARED_LIBRARIES := SDL2 SDL2_net
+
+LOCAL_LDLIBS := -lGLESv1_CM -llog
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android-project/jni/src/config.h b/android-project/jni/src/config.h
new file mode 100644
index 000000000..e2264b83a
--- /dev/null
+++ b/android-project/jni/src/config.h
@@ -0,0 +1,320 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+
+/*
+ * Copyright (C) 2002-2015 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Compiling on BSD */
+/* #undef BSD */
+
+/* Determines if the compilers supports always_inline attribute. */
+#define C_ATTRIBUTE_ALWAYS_INLINE 1
+
+/* Determines if the compilers supports fastcall attribute. */
+/* #undef C_ATTRIBUTE_FASTCALL */
+
+/* Platform for SDL_cdrom compatibility layer (SDL 2.0 only) */
+/* #undef C_COMPAT_SDL_CDROM_PLATFORM */
+
+/* Define to 1 to use inlined memory functions in cpu core */
+/* #undef C_CORE_INLINE */
+
+/* Define to 1 to enable internal debugger, requires libcurses */
+/* #undef C_DEBUG */
+
+/* Define to 1 to use x86 dynamic cpu core */
+/* NOTE: Right now it doesn't compile (possibly due to ASM code), so don't use.
+#ifdef __i386__
+#define C_DYNAMIC_X86 1
+#endif
+*/
+
+/* Define to 1 to use recompiling cpu core. Can not be used together with the
+ dynamic-x86 core */
+/* NOTE: For now we use this even on x86. */
+#define C_DYNREC 1
+
+/* Define to 1 to enable floating point emulation */
+#define C_FPU 1
+
+/* Define to 1 to use a x86 assembly fpu core */
+/* NOTE: Defined in Android.mk if desired. */
+/* #undef C_FPU_X86 */
+
+/* Determines if the compilers supports attributes for structures. */
+#define C_HAS_ATTRIBUTE 1
+
+/* Determines if the compilers supports __builtin_expect for branch
+ prediction. */
+#define C_HAS_BUILTIN_EXPECT 1
+
+/* Define to 1 if you have the mprotect function */
+#define C_HAVE_MPROTECT 1
+
+/* Define to 1 to enable heavy debugging, also have to enable C_DEBUG */
+/* #undef C_HEAVY_DEBUG */
+
+/* Define to 1 to enable IPX over Internet networking, requires SDL_net */
+#define C_IPX 1
+
+/* Define to 1 to enable internal modem support, requires SDL_net */
+#define C_MODEM 1
+
+/* Define to 1 to use opengl display output support */
+/* (actually OpenGL ES 1.1 on Android) */
+#define C_OPENGL 1
+
+/* Physical CD-ROM mounting support */
+/* #undef C_PHYSICAL_CDROM_MOUNT */
+
+/* Define to 1 to enable SDL_sound support */
+/* #undef C_SDL_SOUND */
+
+/* Define to 1 if you have setpriority support */
+/* #undef C_SET_PRIORITY 1 */
+
+/* Define to 1 to enable screenshots, requires libpng */
+/* #undef C_SSHOT */
+
+/* The type of cpu this target has */
+/* NOTE: Defined from Android.mk. */
+#ifndef C_TARGETCPU
+#error "Target (host) CPU is unknown! Please check the contents of Android.mk."
+#endif
+
+/* Define to 1 to use a unaligned memory access */
+#define C_UNALIGNED_MEMORY 1
+
+/* libm doesn't include powf */
+/* #undef DB_HAVE_NO_POWF */
+
+/* struct dirent has d_type */
+#define DIRENT_HAS_D_TYPE 1
+
+/* environ can be included */
+/* #undef ENVIRON_INCLUDED */
+
+/* environ can be linked */
+#define ENVIRON_LINKED 1
+
+/* Define to 1 if you have the <ddraw.h> header file. */
+/* #undef HAVE_DDRAW_H */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Compiling on GNU/Linux */
+#define LINUX 1
+
+/* Compiling on Mac OS X */
+/* #undef MACOSX */
+
+/* Compiling on OS/2 EMX */
+/* #undef OS2 */
+
+/* Name of package */
+#define PACKAGE "dosbox"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "dosbox"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "dosbox SVN"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "dosbox"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "SVN"
+
+/* The size of `int *', as computed by sizeof. */
+/* NOTE: Defined in Android.mk. */
+/* #undef SIZEOF_INT_P */
+
+/* The size of `unsigned char', as computed by sizeof. */
+/* NOTE: Defined in Android.mk. */
+/* #undef SIZEOF_UNSIGNED_CHAR */
+
+/* The size of `unsigned int', as computed by sizeof. */
+/* NOTE: Defined in Android.mk. */
+/* #undef SIZEOF_UNSIGNED_INT */
+
+/* The size of `unsigned long', as computed by sizeof. */
+/* NOTE: Defined in Android.mk. */
+/* #undef SIZEOF_UNSIGNED_LONG */
+
+/* The size of `unsigned long long', as computed by sizeof. */
+/* NOTE: Defined in Android.mk. */
+/* #undef SIZEOF_UNSIGNED_LONG_LONG */
+
+/* The size of `unsigned short', as computed by sizeof. */
+/* NOTE: Defined in Android.mk. */
+/* #undef SIZEOF_UNSIGNED_SHORT */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Version number of package */
+#define VERSION "SVN"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int` if you don't have socklen_t */
+/* #undef socklen_t */
+
+#if C_ATTRIBUTE_ALWAYS_INLINE
+#define INLINE inline __attribute__((always_inline))
+#else
+#define INLINE inline
+#endif
+
+#if C_ATTRIBUTE_FASTCALL
+#define DB_FASTCALL __attribute__((fastcall))
+#else
+#define DB_FASTCALL
+#endif
+
+#if C_HAS_ATTRIBUTE
+#define GCC_ATTRIBUTE(x) __attribute__ ((x))
+#else
+#define GCC_ATTRIBUTE(x) /* attribute not supported */
+#endif
+
+#if C_HAS_BUILTIN_EXPECT
+#define GCC_UNLIKELY(x) __builtin_expect((x),0)
+#define GCC_LIKELY(x) __builtin_expect((x),1)
+#else
+#define GCC_UNLIKELY(x) (x)
+#define GCC_LIKELY(x) (x)
+#endif
+
+
+typedef double Real64;
+
+#if SIZEOF_UNSIGNED_CHAR != 1
+# error "sizeof (unsigned char) != 1"
+#else
+ typedef unsigned char Bit8u;
+ typedef signed char Bit8s;
+#endif
+
+#if SIZEOF_UNSIGNED_SHORT != 2
+# error "sizeof (unsigned short) != 2"
+#else
+ typedef unsigned short Bit16u;
+ typedef signed short Bit16s;
+#endif
+
+#if SIZEOF_UNSIGNED_INT == 4
+ typedef unsigned int Bit32u;
+ typedef signed int Bit32s;
+#elif SIZEOF_UNSIGNED_LONG == 4
+ typedef unsigned long Bit32u;
+ typedef signed long Bit32s;
+#else
+# error "can't find sizeof(type) of 4 bytes!"
+#endif
+
+#if SIZEOF_UNSIGNED_LONG == 8
+ typedef unsigned long Bit64u;
+ typedef signed long Bit64s;
+#elif SIZEOF_UNSIGNED_LONG_LONG == 8
+ typedef unsigned long long Bit64u;
+ typedef signed long long Bit64s;
+#else
+# error "can't find data type of 8 bytes"
+#endif
+
+#if SIZEOF_INT_P == 4
+ typedef Bit32u Bitu;
+ typedef Bit32s Bits;
+#else
+ typedef Bit64u Bitu;
+ typedef Bit64s Bits;
+#endif
+
+
diff --git a/android-project/proguard-project.txt b/android-project/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/android-project/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/android-project/project.properties b/android-project/project.properties
new file mode 100644
index 000000000..a3ee5ab64
--- /dev/null
+++ b/android-project/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17
diff --git a/android-project/res/layout/main.xml b/android-project/res/layout/main.xml
new file mode 100644
index 000000000..123c4b6ea
--- /dev/null
+++ b/android-project/res/layout/main.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Hello World, SDLActivity"
+ />
+</LinearLayout>
+
diff --git a/android-project/res/values/strings.xml b/android-project/res/values/strings.xml
new file mode 100644
index 000000000..2b6a7346e
--- /dev/null
+++ b/android-project/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">DOSBox</string>
+</resources>
diff --git a/android-project/src/com/dosbox/emu/DOSBoxActivity.java b/android-project/src/com/dosbox/emu/DOSBoxActivity.java
new file mode 100644
index 000000000..5be8621b2
--- /dev/null
+++ b/android-project/src/com/dosbox/emu/DOSBoxActivity.java
@@ -0,0 +1,59 @@
+package com.dosbox.emu;
+
+import org.libsdl.app.SDLActivity;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.Surface;
+import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
+
+/*
+ * A sample wrapper class that just calls SDLActivity
+ */
+
+public class DOSBoxActivity extends SDLActivity {
+ /* Based on volume keys related patch from bug report:
+ http://bugzilla.libsdl.org/show_bug.cgi?id=1569 */
+
+ // enable to intercept keys before SDL gets them
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ switch (event.getKeyCode()) {
+ // Show/Hide on-screen keyboard (but don't toggle text input mode)
+ case KeyEvent.KEYCODE_BACK:
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ toggleOnScreenKeyboard();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ // Fix the initial orientation
+ protected void onCreate(Bundle savedInstanceState) {
+ /* Use deprecated getOrientation() rather
+ than getRotation() to support API<8 */
+/*
+ int rotation = getWindowManager().getDefaultDisplay().getOrientation();
+ if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if ((rotation == Surface.ROTATION_0) || (rotation == Surface.ROTATION_90))
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ else
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
+ } else { // Landscape
+ if ((rotation == Surface.ROTATION_0) || (rotation == Surface.ROTATION_90))
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ else
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ }
+*/
+ super.onCreate(savedInstanceState); // Initialize the rest (e.g. SDL)
+ }
+
+ public void toggleOnScreenKeyboard() {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(0, 0);
+ }
+}
diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java
new file mode 100644
index 000000000..7be7917f6
--- /dev/null
+++ b/android-project/src/org/libsdl/app/SDLActivity.java
@@ -0,0 +1,1077 @@
+package org.libsdl.app;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import android.app.*;
+import android.content.*;
+import android.view.*;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AbsoluteLayout;
+import android.os.*;
+import android.util.Log;
+import android.graphics.*;
+import android.media.*;
+import android.hardware.*;
+
+
+/**
+ SDL Activity
+*/
+public class SDLActivity extends Activity {
+ private static final String TAG = "SDL";
+
+ // Keep track of the paused state
+ public static boolean mIsPaused, mIsSurfaceReady, mHasFocus;
+ public static boolean mExitCalledFromJava;
+
+ // Main components
+ protected static SDLActivity mSingleton;
+ protected static SDLSurface mSurface;
+ protected static View mTextEdit;
+ protected static ViewGroup mLayout;
+ protected static SDLJoystickHandler mJoystickHandler;
+
+ // This is what SDL runs in. It invokes SDL_main(), eventually
+ protected static Thread mSDLThread;
+
+ // Audio
+ protected static AudioTrack mAudioTrack;
+
+ // Load the .so
+ static {
+ System.loadLibrary("stlport_shared");
+ System.loadLibrary("SDL2");
+ //System.loadLibrary("SDL2_image");
+ //System.loadLibrary("SDL2_mixer");
+ System.loadLibrary("SDL2_net");
+ //System.loadLibrary("SDL2_ttf");
+ System.loadLibrary("main");
+ }
+
+
+ public static void initialize() {
+ // The static nature of the singleton and Android quirkyness force us to initialize everything here
+ // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
+ mSingleton = null;
+ mSurface = null;
+ mTextEdit = null;
+ mLayout = null;
+ mJoystickHandler = null;
+ mSDLThread = null;
+ mAudioTrack = null;
+ mExitCalledFromJava = false;
+ mIsPaused = false;
+ mIsSurfaceReady = false;
+ mHasFocus = true;
+ }
+
+ // Setup
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.v("SDL", "onCreate():" + mSingleton);
+ super.onCreate(savedInstanceState);
+
+ SDLActivity.initialize();
+ // So we can call stuff from static callbacks
+ mSingleton = this;
+
+ // Set up the surface
+ mSurface = new SDLSurface(getApplication());
+
+ if(Build.VERSION.SDK_INT >= 12) {
+ mJoystickHandler = new SDLJoystickHandler_API12();
+ }
+ else {
+ mJoystickHandler = new SDLJoystickHandler();
+ }
+
+ mLayout = new AbsoluteLayout(this);
+ mLayout.addView(mSurface);
+
+ setContentView(mLayout);
+ }
+
+ // Events
+ @Override
+ protected void onPause() {
+ Log.v("SDL", "onPause()");
+ super.onPause();
+ SDLActivity.handlePause();
+ }
+
+ @Override
+ protected void onResume() {
+ Log.v("SDL", "onResume()");
+ super.onResume();
+ SDLActivity.handleResume();
+ }
+
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ Log.v("SDL", "onWindowFocusChanged(): " + hasFocus);
+
+ SDLActivity.mHasFocus = hasFocus;
+ if (hasFocus) {
+ SDLActivity.handleResume();
+ }
+ }
+
+ @Override
+ public void onLowMemory() {
+ Log.v("SDL", "onLowMemory()");
+ super.onLowMemory();
+ SDLActivity.nativeLowMemory();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.v("SDL", "onDestroy()");
+ // Send a quit message to the application
+ SDLActivity.mExitCalledFromJava = true;
+ SDLActivity.nativeQuit();
+
+ // Now wait for the SDL thread to quit
+ if (SDLActivity.mSDLThread != null) {
+ try {
+ SDLActivity.mSDLThread.join();
+ } catch(Exception e) {
+ Log.v("SDL", "Problem stopping thread: " + e);
+ }
+ SDLActivity.mSDLThread = null;
+
+ //Log.v("SDL", "Finished waiting for SDL thread");
+ }
+
+ super.onDestroy();
+ // Reset everything in case the user re opens the app
+ SDLActivity.initialize();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ int keyCode = event.getKeyCode();
+ // Ignore certain special keys so they're handled by Android
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
+ keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+ keyCode == KeyEvent.KEYCODE_CAMERA ||
+ keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */
+ keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */
+ ) {
+ return false;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ /** Called by onPause or surfaceDestroyed. Even if surfaceDestroyed
+ * is the first to be called, mIsSurfaceReady should still be set
+ * to 'true' during the call to onPause (in a usual scenario).
+ */
+ public static void handlePause() {
+ if (!SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) {
+ SDLActivity.mIsPaused = true;
+ SDLActivity.nativePause();
+ mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+ }
+ }
+
+ /** Called by onResume or surfaceCreated. An actual resume should be done only when the surface is ready.
+ * Note: Some Android variants may send multiple surfaceChanged events, so we don't need to resume
+ * every time we get one of those events, only if it comes after surfaceDestroyed
+ */
+ public static void handleResume() {
+ if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) {
+ SDLActivity.mIsPaused = false;
+ SDLActivity.nativeResume();
+ mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+ }
+ }
+
+ /* The native thread has finished */
+ public static void handleNativeExit() {
+ SDLActivity.mSDLThread = null;
+ mSingleton.finish();
+ }
+
+
+ // Messages from the SDLMain thread
+ static final int COMMAND_CHANGE_TITLE = 1;
+ static final int COMMAND_UNUSED = 2;
+ static final int COMMAND_TEXTEDIT_HIDE = 3;
+
+ protected static final int COMMAND_USER = 0x8000;
+
+ /**
+ * This method is called by SDL if SDL did not handle a message itself.
+ * This happens if a received message contains an unsupported command.
+ * Method can be overwritten to handle Messages in a different class.
+ * @param command the command of the message.
+ * @param param the parameter of the message. May be null.
+ * @return if the message was handled in overridden method.
+ */
+ protected boolean onUnhandledMessage(int command, Object param) {
+ return false;
+ }
+
+ /**
+ * A Handler class for Messages from native SDL applications.
+ * It uses current Activities as target (e.g. for the title).
+ * static to prevent implicit references to enclosing object.
+ */
+ protected static class SDLCommandHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ Context context = getContext();
+ if (context == null) {
+ Log.e(TAG, "error handling message, getContext() returned null");
+ return;
+ }
+ switch (msg.arg1) {
+ case COMMAND_CHANGE_TITLE:
+ if (context instanceof Activity) {
+ ((Activity) context).setTitle((String)msg.obj);
+ } else {
+ Log.e(TAG, "error handling message, getContext() returned no Activity");
+ }
+ break;
+ case COMMAND_TEXTEDIT_HIDE:
+ if (mTextEdit != null) {
+ mTextEdit.setVisibility(View.GONE);
+
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
+ }
+ break;
+
+ default:
+ if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
+ Log.e(TAG, "error handling message, command is " + msg.arg1);
+ }
+ }
+ }
+ }
+
+ // Handler for the messages
+ Handler commandHandler = new SDLCommandHandler();
+
+ // Send a message from the SDLMain thread
+ boolean sendCommand(int command, Object data) {
+ Message msg = commandHandler.obtainMessage();
+ msg.arg1 = command;
+ msg.obj = data;
+ return commandHandler.sendMessage(msg);
+ }
+
+ // C functions we call
+ public static native void nativeInit();
+ public static native void nativeLowMemory();
+ public static native void nativeQuit();
+ public static native void nativePause();
+ public static native void nativeResume();
+ public static native void onNativeResize(int x, int y, int format);
+ public static native int onNativePadDown(int device_id, int keycode);
+ public static native int onNativePadUp(int device_id, int keycode);
+ public static native void onNativeJoy(int device_id, int axis,
+ float value);
+ public static native void onNativeHat(int device_id, int hat_id,
+ int x, int y);
+ public static native void onNativeKeyDown(int keycode);
+ public static native void onNativeKeyUp(int keycode);
+ public static native void onNativeKeyboardFocusLost();
+ public static native void onNativeTouch(int touchDevId, int pointerFingerId,
+ int action, float x,
+ float y, float p);
+ public static native void onNativeAccel(float x, float y, float z);
+ public static native void onNativeSurfaceChanged();
+ public static native void onNativeSurfaceDestroyed();
+ public static native void nativeFlipBuffers();
+ public static native int nativeAddJoystick(int device_id, String name,
+ int is_accelerometer, int nbuttons,
+ int naxes, int nhats, int nballs);
+ public static native int nativeRemoveJoystick(int device_id);
+
+ public static void flipBuffers() {
+ SDLActivity.nativeFlipBuffers();
+ }
+
+ public static boolean setActivityTitle(String title) {
+ // Called from SDLMain() thread and can't directly affect the view
+ return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
+ }
+
+ public static boolean sendMessage(int command, int param) {
+ return mSingleton.sendCommand(command, Integer.valueOf(param));
+ }
+
+ public static Context getContext() {
+ return mSingleton;
+ }
+
+ /**
+ * @return result of getSystemService(name) but executed on UI thread.
+ */
+ public Object getSystemServiceFromUiThread(final String name) {
+ final Object lock = new Object();
+ final Object[] results = new Object[2]; // array for writable variables
+ synchronized (lock) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (lock) {
+ results[0] = getSystemService(name);
+ results[1] = Boolean.TRUE;
+ lock.notify();
+ }
+ }
+ });
+ if (results[1] == null) {
+ try {
+ lock.wait();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ return results[0];
+ }
+
+ static class ShowTextInputTask implements Runnable {
+ /*
+ * This is used to regulate the pan&scan method to have some offset from
+ * the bottom edge of the input region and the top edge of an input
+ * method (soft keyboard)
+ */
+ static final int HEIGHT_PADDING = 15;
+
+ public int x, y, w, h;
+
+ public ShowTextInputTask(int x, int y, int w, int h) {
+ this.x = x;
+ this.y = y;
+ this.w = w;
+ this.h = h;
+ }
+
+ @Override
+ public void run() {
+ AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(
+ w, h + HEIGHT_PADDING, x, y);
+
+ if (mTextEdit == null) {
+ mTextEdit = new DummyEdit(getContext());
+
+ mLayout.addView(mTextEdit, params);
+ } else {
+ mTextEdit.setLayoutParams(params);
+ }
+
+ mTextEdit.setVisibility(View.VISIBLE);
+ mTextEdit.requestFocus();
+
+ InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mTextEdit, 0);
+ }
+ }
+
+ public static boolean showTextInput(int x, int y, int w, int h) {
+ // Transfer the task to the main thread as a Runnable
+ return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
+ }
+
+ public static Surface getNativeSurface() {
+ return SDLActivity.mSurface.getNativeSurface();
+ }
+
+ // Audio
+ public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+ int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+ int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+
+ Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+ // Let the user pick a larger buffer if they really want -- but ye
+ // gods they probably shouldn't, the minimums are horrifyingly high
+ // latency already
+ desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+
+ if (mAudioTrack == null) {
+ mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
+ channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+
+ // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
+ // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
+ // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
+
+ if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+ Log.e("SDL", "Failed during initialization of Audio Track");
+ mAudioTrack = null;
+ return -1;
+ }
+
+ mAudioTrack.play();
+ }
+
+ Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+ return 0;
+ }
+
+ public static void audioWriteShortBuffer(short[] buffer) {
+ for (int i = 0; i < buffer.length; ) {
+ int result = mAudioTrack.write(buffer, i, buffer.length - i);
+ if (result > 0) {
+ i += result;
+ } else if (result == 0) {
+ try {
+ Thread.sleep(1);
+ } catch(InterruptedException e) {
+ // Nom nom
+ }
+ } else {
+ Log.w("SDL", "SDL audio: error return from write(short)");
+ return;
+ }
+ }
+ }
+
+ public static void audioWriteByteBuffer(byte[] buffer) {
+ for (int i = 0; i < buffer.length; ) {
+ int result = mAudioTrack.write(buffer, i, buffer.length - i);
+ if (result > 0) {
+ i += result;
+ } else if (result == 0) {
+ try {
+ Thread.sleep(1);
+ } catch(InterruptedException e) {
+ // Nom nom
+ }
+ } else {
+ Log.w("SDL", "SDL audio: error return from write(byte)");
+ return;
+ }
+ }
+ }
+
+ public static void audioQuit() {
+ if (mAudioTrack != null) {
+ mAudioTrack.stop();
+ mAudioTrack = null;
+ }
+ }
+
+ // Input
+
+ /**
+ * @return an array which may be empty but is never null.
+ */
+ public static int[] inputGetInputDeviceIds(int sources) {
+ int[] ids = InputDevice.getDeviceIds();
+ int[] filtered = new int[ids.length];
+ int used = 0;
+ for (int i = 0; i < ids.length; ++i) {
+ InputDevice device = InputDevice.getDevice(ids[i]);
+ if ((device != null) && ((device.getSources() & sources) != 0)) {
+ filtered[used++] = device.getId();
+ }
+ }
+ return Arrays.copyOf(filtered, used);
+ }
+
+ // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
+ public static boolean handleJoystickMotionEvent(MotionEvent event) {
+ return mJoystickHandler.handleMotionEvent(event);
+ }
+
+ public static void pollInputDevices() {
+ if (SDLActivity.mSDLThread != null) {
+ mJoystickHandler.pollInputDevices();
+ }
+ }
+
+}
+
+/**
+ Simple nativeInit() runnable
+*/
+class SDLMain implements Runnable {
+ @Override
+ public void run() {
+ // Runs SDL_main()
+ SDLActivity.nativeInit();
+
+ //Log.v("SDL", "SDL thread terminated");
+ }
+}
+
+
+/**
+ SDLSurface. This is what we draw on, so we need to know when it's created
+ in order to do anything useful.
+
+ Because of this, that's where we set up the SDL thread
+*/
+class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
+ View.OnKeyListener, View.OnTouchListener, SensorEventListener {
+
+ // Sensors
+ protected static SensorManager mSensorManager;
+ protected static Display mDisplay;
+
+ // Keep track of the surface size to normalize touch events
+ protected static float mWidth, mHeight;
+
+ // Startup
+ public SDLSurface(Context context) {
+ super(context);
+ getHolder().addCallback(this);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ requestFocus();
+ setOnKeyListener(this);
+ setOnTouchListener(this);
+
+ mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+ mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+
+ if(Build.VERSION.SDK_INT >= 12) {
+ setOnGenericMotionListener(new SDLGenericMotionListener_API12());
+ }
+
+ // Some arbitrary defaults to avoid a potential division by zero
+ mWidth = 1.0f;
+ mHeight = 1.0f;
+ }
+
+ public Surface getNativeSurface() {
+ return getHolder().getSurface();
+ }
+
+ // Called when we have a valid drawing surface
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.v("SDL", "surfaceCreated()");
+ holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ }
+
+ // Called when we lose the surface
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.v("SDL", "surfaceDestroyed()");
+ // Call this *before* setting mIsSurfaceReady to 'false'
+ SDLActivity.handlePause();
+ SDLActivity.mIsSurfaceReady = false;
+ SDLActivity.onNativeSurfaceDestroyed();
+ }
+
+ // Called when the surface is resized
+ @Override
+ public void surfaceChanged(SurfaceHolder holder,
+ int format, int width, int height) {
+ Log.v("SDL", "surfaceChanged()");
+
+ int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
+ switch (format) {
+ case PixelFormat.A_8:
+ Log.v("SDL", "pixel format A_8");
+ break;
+ case PixelFormat.LA_88:
+ Log.v("SDL", "pixel format LA_88");
+ break;
+ case PixelFormat.L_8:
+ Log.v("SDL", "pixel format L_8");
+ break;
+ case PixelFormat.RGBA_4444:
+ Log.v("SDL", "pixel format RGBA_4444");
+ sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
+ break;
+ case PixelFormat.RGBA_5551:
+ Log.v("SDL", "pixel format RGBA_5551");
+ sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
+ break;
+ case PixelFormat.RGBA_8888:
+ Log.v("SDL", "pixel format RGBA_8888");
+ sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
+ break;
+ case PixelFormat.RGBX_8888:
+ Log.v("SDL", "pixel format RGBX_8888");
+ sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
+ break;
+ case PixelFormat.RGB_332:
+ Log.v("SDL", "pixel format RGB_332");
+ sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
+ break;
+ case PixelFormat.RGB_565:
+ Log.v("SDL", "pixel format RGB_565");
+ sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
+ break;
+ case PixelFormat.RGB_888:
+ Log.v("SDL", "pixel format RGB_888");
+ // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
+ sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
+ break;
+ default:
+ Log.v("SDL", "pixel format unknown " + format);
+ break;
+ }
+
+ mWidth = width;
+ mHeight = height;
+ SDLActivity.onNativeResize(width, height, sdlFormat);
+ Log.v("SDL", "Window size:" + width + "x"+height);
+
+ // Set mIsSurfaceReady to 'true' *before* making a call to handleResume
+ SDLActivity.mIsSurfaceReady = true;
+ SDLActivity.onNativeSurfaceChanged();
+
+
+ if (SDLActivity.mSDLThread == null) {
+ // This is the entry point to the C app.
+ // Start up the C app thread and enable sensor input for the first time
+
+ SDLActivity.mSDLThread = new Thread(new SDLMain(), "SDLThread");
+ enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+ SDLActivity.mSDLThread.start();
+
+ // Set up a listener thread to catch when the native thread ends
+ new Thread(new Runnable(){
+ @Override
+ public void run(){
+ try {
+ SDLActivity.mSDLThread.join();
+ }
+ catch(Exception e){}
+ finally{
+ // Native thread has finished
+ if (! SDLActivity.mExitCalledFromJava) {
+ SDLActivity.handleNativeExit();
+ }
+ }
+ }
+ }).start();
+ }
+ }
+
+ // unused
+ @Override
+ public void onDraw(Canvas canvas) {}
+
+
+ // Key events
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // Dispatch the different events depending on where they come from
+ // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
+ // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD
+
+ if ( (event.getSource() & 0x00000401) != 0 || /* API 12: SOURCE_GAMEPAD */
+ (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
+ return true;
+ }
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
+ return true;
+ }
+ }
+ }
+
+ // HACK: Get input from all keys of a soft-keyboard,
+ // even if InputDevice.SOURCE_KEYBOARD is not the source.
+// if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ //Log.v("SDL", "key down: " + keyCode);
+ SDLActivity.onNativeKeyDown(keyCode);
+ return true;
+ }
+ else if (event.getAction() == KeyEvent.ACTION_UP) {
+ //Log.v("SDL", "key up: " + keyCode);
+ SDLActivity.onNativeKeyUp(keyCode);
+ return true;
+ }
+// }
+
+ return false;
+ }
+
+ // Touch events
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ /* Ref: http://developer.android.com/training/gestures/multi.html */
+ final int touchDevId = event.getDeviceId();
+ final int pointerCount = event.getPointerCount();
+ int action = event.getActionMasked();
+ int pointerFingerId;
+ int i = -1;
+ float x,y,p;
+
+ switch(action) {
+ case MotionEvent.ACTION_MOVE:
+ for (i = 0; i < pointerCount; i++) {
+ pointerFingerId = event.getPointerId(i);
+ x = event.getX(i) / mWidth;
+ y = event.getY(i) / mHeight;
+ p = event.getPressure(i);
+ SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_DOWN:
+ // Primary pointer up/down, the index is always zero
+ i = 0;
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // Non primary pointer up/down
+ if (i == -1) {
+ i = event.getActionIndex();
+ }
+
+ pointerFingerId = event.getPointerId(i);
+ x = event.getX(i) / mWidth;
+ y = event.getY(i) / mHeight;
+ p = event.getPressure(i);
+ SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+
+ // Sensor events
+ public void enableSensor(int sensortype, boolean enabled) {
+ // TODO: This uses getDefaultSensor - what if we have >1 accels?
+ if (enabled) {
+ mSensorManager.registerListener(this,
+ mSensorManager.getDefaultSensor(sensortype),
+ SensorManager.SENSOR_DELAY_GAME, null);
+ } else {
+ mSensorManager.unregisterListener(this,
+ mSensorManager.getDefaultSensor(sensortype));
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // TODO
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+ float x, y;
+ switch (mDisplay.getRotation()) {
+ case Surface.ROTATION_90:
+ x = -event.values[1];
+ y = event.values[0];
+ break;
+ case Surface.ROTATION_270:
+ x = event.values[1];
+ y = -event.values[0];
+ break;
+ case Surface.ROTATION_180:
+ x = -event.values[1];
+ y = -event.values[0];
+ break;
+ default:
+ x = event.values[0];
+ y = event.values[1];
+ break;
+ }
+ SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
+ y / SensorManager.GRAVITY_EARTH,
+ event.values[2] / SensorManager.GRAVITY_EARTH - 1);
+ }
+ }
+}
+
+/* This is a fake invisible editor view that receives the input and defines the
+ * pan&scan region
+ */
+class DummyEdit extends View implements View.OnKeyListener {
+ InputConnection ic;
+
+ public DummyEdit(Context context) {
+ super(context);
+ setFocusableInTouchMode(true);
+ setFocusable(true);
+ setOnKeyListener(this);
+ }
+
+ @Override
+ public boolean onCheckIsTextEditor() {
+ return true;
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+
+ // This handles the hardware keyboard input
+ if (event.isPrintingKey()) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
+ }
+ return true;
+ }
+
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ SDLActivity.onNativeKeyDown(keyCode);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+ SDLActivity.onNativeKeyUp(keyCode);
+ return true;
+ }
+
+ return false;
+ }
+
+ //
+ @Override
+ public boolean onKeyPreIme (int keyCode, KeyEvent event) {
+ // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
+ // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
+ // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
+ // FIXME: A more effective solution would be to change our Layout from AbsoluteLayout to Relative or Linear
+ // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
+ // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
+ if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+ if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
+ SDLActivity.onNativeKeyboardFocusLost();
+ }
+ }
+ return super.onKeyPreIme(keyCode, event);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ ic = new SDLInputConnection(this, true);
+
+ outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+ | 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
+
+ return ic;
+ }
+}
+
+class SDLInputConnection extends BaseInputConnection {
+
+ public SDLInputConnection(View targetView, boolean fullEditor) {
+ super(targetView, fullEditor);
+
+ }
+
+ @Override
+ public boolean sendKeyEvent(KeyEvent event) {
+
+ /*
+ * This handles the keycodes from soft keyboard (and IME-translated
+ * input from hardkeyboard)
+ */
+ int keyCode = event.getKeyCode();
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (event.isPrintingKey()) {
+ commitText(String.valueOf((char) event.getUnicodeChar()), 1);
+ }
+ SDLActivity.onNativeKeyDown(keyCode);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP) {
+
+ SDLActivity.onNativeKeyUp(keyCode);
+ return true;
+ }
+ return super.sendKeyEvent(event);
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition) {
+
+ nativeCommitText(text.toString(), newCursorPosition);
+
+ return super.commitText(text, newCursorPosition);
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+
+ nativeSetComposingText(text.toString(), newCursorPosition);
+
+ return super.setComposingText(text, newCursorPosition);
+ }
+
+ public native void nativeCommitText(String text, int newCursorPosition);
+
+ public native void nativeSetComposingText(String text, int newCursorPosition);
+
+ @Override
+ public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+ // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
+ if (beforeLength == 1 && afterLength == 0) {
+ // backspace
+ return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
+ && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
+ }
+
+ return super.deleteSurroundingText(beforeLength, afterLength);
+ }
+}
+
+/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
+class SDLJoystickHandler {
+
+ public boolean handleMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ public void pollInputDevices() {
+ }
+}
+
+/* Actual joystick functionality available for API >= 12 devices */
+class SDLJoystickHandler_API12 extends SDLJoystickHandler {
+
+ class SDLJoystick {
+ public int device_id;
+ public String name;
+ public ArrayList<InputDevice.MotionRange> axes;
+ public ArrayList<InputDevice.MotionRange> hats;
+ }
+ class RangeComparator implements Comparator<InputDevice.MotionRange>
+ {
+ @Override
+ public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
+ return arg0.getAxis() - arg1.getAxis();
+ }
+ }
+
+ private ArrayList<SDLJoystick> mJoysticks;
+
+ public SDLJoystickHandler_API12() {
+
+ mJoysticks = new ArrayList<SDLJoystick>();
+ }
+
+ @Override
+ public void pollInputDevices() {
+ int[] deviceIds = InputDevice.getDeviceIds();
+ // It helps processing the device ids in reverse order
+ // For example, in the case of the XBox 360 wireless dongle,
+ // so the first controller seen by SDL matches what the receiver
+ // considers to be the first controller
+
+ for(int i=deviceIds.length-1; i>-1; i--) {
+ SDLJoystick joystick = getJoystick(deviceIds[i]);
+ if (joystick == null) {
+ joystick = new SDLJoystick();
+ InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
+ if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ joystick.device_id = deviceIds[i];
+ joystick.name = joystickDevice.getName();
+ joystick.axes = new ArrayList<InputDevice.MotionRange>();
+ joystick.hats = new ArrayList<InputDevice.MotionRange>();
+
+ List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
+ Collections.sort(ranges, new RangeComparator());
+ for (InputDevice.MotionRange range : ranges ) {
+ if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) {
+ if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
+ range.getAxis() == MotionEvent.AXIS_HAT_Y) {
+ joystick.hats.add(range);
+ }
+ else {
+ joystick.axes.add(range);
+ }
+ }
+ }
+
+ mJoysticks.add(joystick);
+ SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1,
+ joystick.axes.size(), joystick.hats.size()/2, 0);
+ }
+ }
+ }
+
+ /* Check removed devices */
+ ArrayList<Integer> removedDevices = new ArrayList<Integer>();
+ for(int i=0; i < mJoysticks.size(); i++) {
+ int device_id = mJoysticks.get(i).device_id;
+ int j;
+ for (j=0; j < deviceIds.length; j++) {
+ if (device_id == deviceIds[j]) break;
+ }
+ if (j == deviceIds.length) {
+ removedDevices.add(device_id);
+ }
+ }
+
+ for(int i=0; i < removedDevices.size(); i++) {
+ int device_id = removedDevices.get(i);
+ SDLActivity.nativeRemoveJoystick(device_id);
+ for (int j=0; j < mJoysticks.size(); j++) {
+ if (mJoysticks.get(j).device_id == device_id) {
+ mJoysticks.remove(j);
+ break;
+ }
+ }
+ }
+ }
+
+ protected SDLJoystick getJoystick(int device_id) {
+ for(int i=0; i < mJoysticks.size(); i++) {
+ if (mJoysticks.get(i).device_id == device_id) {
+ return mJoysticks.get(i);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean handleMotionEvent(MotionEvent event) {
+ if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
+ int actionPointerIndex = event.getActionIndex();
+ int action = event.getActionMasked();
+ switch(action) {
+ case MotionEvent.ACTION_MOVE:
+ SDLJoystick joystick = getJoystick(event.getDeviceId());
+ if ( joystick != null ) {
+ for (int i = 0; i < joystick.axes.size(); i++) {
+ InputDevice.MotionRange range = joystick.axes.get(i);
+ /* Normalize the value to -1...1 */
+ float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
+ SDLActivity.onNativeJoy(joystick.device_id, i, value );
+ }
+ for (int i = 0; i < joystick.hats.size(); i+=2) {
+ int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
+ int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
+ SDLActivity.onNativeHat(joystick.device_id, i/2, hatX, hatY );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+ }
+}
+
+class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
+ // Generic Motion (mouse hover, joystick...) events go here
+ // We only have joysticks yet
+ @Override
+ public boolean onGenericMotion(View v, MotionEvent event) {
+ return SDLActivity.handleJoystickMotionEvent(event);
+ }
+}
diff --git a/android-project/src/org/libsdl/app/note.txt b/android-project/src/org/libsdl/app/note.txt
new file mode 100644
index 000000000..83fcedb46
--- /dev/null
+++ b/android-project/src/org/libsdl/app/note.txt
@@ -0,0 +1,4 @@
+Modifications to SDLActivity.java:
+1. stlport_shared and SDL2_net are loaded.
+2. Non-gamepad/dpad keycodes that don't have any source are still assumed to
+come from a keyboard (a workaround for full input from soft keyboard).
diff --git a/configure.ac b/configure.ac
index fb8d28fbe..691fbf467 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,27 +27,94 @@ if test x$host = xi386-pc-os2-emx ; then
fi
dnl Check for SDL
-SDL_VERSION=1.2.0
-AM_PATH_SDL($SDL_VERSION,
- :,
- AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!])
-)
+EXULT_CHECK_SDL(:,AC_MSG_ERROR([[*** SDL not found!]]))
LIBS="$LIBS $SDL_LIBS"
CPPFLAGS="$CPPFLAGS $SDL_CFLAGS"
-dnl Check if SDL is 1.2.x (1.3 not supported)
-AC_MSG_CHECKING([SDL version only being 1.2.X])
+dnl Check if SDL is 1.2.x, 2.0.x
+AC_MSG_CHECKING([for SDL version being 1.2.x or 2.0.x])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include "SDL.h"
void blah(){
-#if SDL_MINOR_VERSION != 2
-#error "Only SDL 1.2 supported"
+#if !((SDL_MAJOR_VERSION == 1) && (SDL_MINOR_VERSION == 2)) && !((SDL_MAJOR_VERSION == 2) && (SDL_MINOR_VERSION == 0))
+#error "Only SDL 1.2 and 2.0 supported"
#endif
;
}
])],AC_MSG_RESULT([yes]),[
AC_MSG_RESULT([no])
- AC_MSG_ERROR([Only libSDL 1.2.X supported])])
+ AC_MSG_ERROR([Only libSDL 1.2.x or 2.0.x supported])])
+
+dnl Check for SDL_cdrom compatibility layer platform (used with SDL 2.0 only)
+AC_MSG_CHECKING(for SDL_cdrom compatibility layer platform)
+COMPAT_SDL_CDROM_GET_PLATFORM(:)
+AH_TEMPLATE(C_COMPAT_SDL_CDROM_PLATFORM,[Platform for SDL_cdrom compatibility layer (SDL 2.0 only)])
+
+physical_cdrom_mount=1
+
+case $compat_sdl_cdrom_arch in
+ linux|solaris)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_LINUX])
+ AC_MSG_RESULT([linux])
+ ;;
+ *freebsd*)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_FREEBSD])
+ AC_MSG_RESULT([freebsd])
+ ;;
+ *openbsd*|*netbsd*)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_OPENBSD])
+ AC_MSG_RESULT([openbsd])
+ ;;
+ bsdi)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_BSDI])
+ AC_MSG_RESULT([bsdi])
+ ;;
+ aix)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_AIX])
+ AC_MSG_RESULT([aix])
+ ;;
+ osf)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_OSF])
+ AC_MSG_RESULT([osf])
+ ;;
+ qnx)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_QNX])
+ AC_MSG_RESULT([qnx])
+ ;;
+ win32)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_WIN32])
+ AC_MSG_RESULT([win32])
+ ;;
+ beos)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_BEOS])
+ AC_MSG_RESULT([beos])
+ ;;
+ macosx)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_MACOSX])
+ AC_MSG_RESULT([macosx])
+ ;;
+ mint)
+ AC_DEFINE(C_COMPAT_SDL_CDROM_PLATFORM,[COMPAT_SDL_CDROM_PLATFORM_MINT])
+ AC_MSG_RESULT([mint])
+ ;;
+ *)
+ physical_cdrom_mount=0
+ AC_MSG_RESULT([disabled])
+esac
+
+if test x"$sdl_ver" = xsdl12 ; then
+ physical_cdrom_mount=1
+fi
+
+AC_MSG_CHECKING(for physical cdrom mounting support)
+AH_TEMPLATE(C_PHYSICAL_CDROM_MOUNT,[Physical CD-ROM mounting support])
+if test x"$physical_cdrom_mount" = x1 ; then
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(C_PHYSICAL_CDROM_MOUNT,[1])
+else
+ AC_MSG_RESULT(no)
+fi
+
dnl Checks for header files.
@@ -437,23 +504,29 @@ AH_TEMPLATE(C_MODEM,[Define to 1 to enable internal modem support, requires SDL_
AH_TEMPLATE(C_IPX,[Define to 1 to enable IPX over Internet networking, requires SDL_net])
AC_CHECK_HEADER(SDL_net.h,have_sdl_net_h=yes,)
+if test x"$sdl_ver" = xsdl2 ; then
+ LIBSDL_HEADER="SDL2"
+else
+ LIBSDL_HEADER="SDL"
+fi
+
if test x$host = xi386-pc-os2-emx ; then
- AC_MSG_CHECKING(for SDLNet_Init in SDL_net);
+ AC_MSG_CHECKING(for SDLNet_Init in ${LIBSDL_HEADER}_net);
LIBS_BACKUP=$LIBS;
- LIBS="$LIBS -lSDL_Net";
+ LIBS="$LIBS -l${LIBSDL_HEADER}_Net";
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <SDL_Net.h>]],[[
SDLNet_Init ();
]])], [AC_MSG_RESULT(yes); have_sdl_net_lib=yes], AC_MSG_RESULT(no))
LIBS=$LIBS_BACKUP
else
-AC_CHECK_LIB(SDL_net, SDLNet_Init, have_sdl_net_lib=yes, , )
+AC_CHECK_LIB(${LIBSDL_HEADER}_net, SDLNet_Init, have_sdl_net_lib=yes, , )
fi
if test x$have_sdl_net_lib = xyes -a x$have_sdl_net_h = xyes ; then
- LIBS="$LIBS -lSDL_net"
+ LIBS="$LIBS -l${LIBSDL_HEADER}_net"
AC_DEFINE(C_MODEM,1)
AC_DEFINE(C_IPX,1)
else
- AC_MSG_WARN([Can't find SDL_net, internal modem and ipx disabled])
+ AC_MSG_WARN([Can't find ${LIBSDL_HEADER}_net, internal modem and ipx disabled])
fi
AH_TEMPLATE(C_X11_XKB,[define to 1 if you have XKBlib.h and X11 lib])
@@ -625,6 +698,9 @@ src/misc/Makefile
src/shell/Makefile
src/platform/Makefile
src/platform/visualc/Makefile
+src/sdl_cdrom/Makefile
+src/sdl_cdrom/macos/Makefile
+src/sdl_cdrom/macosx/Makefile
visualc_net/Makefile
include/Makefile
docs/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index a4029e835..d33c06668 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
-SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform
+SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform sdl_cdrom
bin_PROGRAMS = dosbox
@@ -14,7 +14,8 @@ endif
dosbox_SOURCES = dosbox.cpp $(ico_stuff)
dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \
ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \
- hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a
+ hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a \
+ sdl_cdrom/libcsdlcdrom.a sdl_cdrom/macos/libcsdlcdrommacos.a sdl_cdrom/macosx/libcsdlcdrommacosx.a
EXTRA_DIST = winres.rc dosbox.ico
diff --git a/src/dos/cdrom.cpp b/src/dos/cdrom.cpp
index 9608e87d1..c42a8e77f 100644
--- a/src/dos/cdrom.cpp
+++ b/src/dos/cdrom.cpp
@@ -21,6 +21,7 @@
// SDL CDROM
// ******************************************************
+
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -30,6 +31,8 @@
#include "support.h"
#include "cdrom.h"
+#if C_PHYSICAL_CDROM_MOUNT
+
CDROM_Interface_SDL::CDROM_Interface_SDL(void) {
driveID = 0;
oldLeadOut = 0;
@@ -143,6 +146,8 @@ bool CDROM_Interface_SDL::LoadUnloadMedia(bool unload) {
return success;
}
+#endif /* C_PHYSICAL_CDROM_MOUNT */
+
int CDROM_GetMountType(char* path, int forceCD) {
// 0 - physical CDROM
// 1 - Iso file
@@ -157,6 +162,7 @@ int CDROM_GetMountType(char* path, int forceCD) {
upcase(buffer);
#endif
+#if C_PHYSICAL_CDROM_MOUNT
int num = SDL_CDNumDrives();
// If cd drive is forced then check if its in range and return 0
if ((forceCD>=0) && (forceCD<num)) {
@@ -169,6 +175,7 @@ int CDROM_GetMountType(char* path, int forceCD) {
cdName = SDL_CDName(i);
if (strcmp(buffer,cdName)==0) return 0;
};
+#endif
// Detect ISO
struct stat file_stat;
@@ -214,5 +221,3 @@ bool CDROM_Interface_Fake :: GetMediaTrayStatus(bool& mediaPresent, bool& mediaC
trayOpen = false;
return true;
}
-
-
diff --git a/src/dos/cdrom.h b/src/dos/cdrom.h
index 803c7aa2f..e797ef6eb 100644
--- a/src/dos/cdrom.h
+++ b/src/dos/cdrom.h
@@ -32,6 +32,11 @@
#include "mem.h"
#include "mixer.h"
#include "SDL.h"
+#if SDL_VERSION_ATLEAST(2,0,0)
+/* Do include this, even if C_PHYSICAL_CDROM_MOUNT is *not* defined,
+due to MSF_TO_FRAMES and FRAMES_TO_MSF (and indirectly also CD_FPS). */
+#include "../sdl_cdrom/compat_SDL_cdrom.h"
+#endif
#include "SDL_thread.h"
#if defined(C_SDL_SOUND)
@@ -41,7 +46,9 @@
#define RAW_SECTOR_SIZE 2352
#define COOKED_SECTOR_SIZE 2048
+#if C_PHYSICAL_CDROM_MOUNT
enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI };
+#endif
typedef struct SMSF {
unsigned char min;
@@ -84,6 +91,7 @@ public:
virtual void InitNewMedia (void) {};
};
+#if C_PHYSICAL_CDROM_MOUNT
class CDROM_Interface_SDL : public CDROM_Interface
{
public:
@@ -112,6 +120,7 @@ private:
int driveID;
Uint32 oldLeadOut;
};
+#endif /* C_PHYSICAL_CDROM_MOUNT */
class CDROM_Interface_Fake : public CDROM_Interface
{
@@ -237,6 +246,8 @@ typedef std::vector<Track>::iterator track_it;
Bit8u subUnit;
};
+#if C_PHYSICAL_CDROM_MOUNT
+
#if defined (WIN32) /* Win 32 */
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
@@ -394,4 +405,6 @@ private:
#endif /* LINUX */
+#endif /* C_PHYSICAL_CDROM_MOUNT */
+
#endif /* __CDROM_INTERFACE__ */
diff --git a/src/dos/cdrom_aspi_win32.cpp b/src/dos/cdrom_aspi_win32.cpp
index cc8de4e35..99b52a1ec 100644
--- a/src/dos/cdrom_aspi_win32.cpp
+++ b/src/dos/cdrom_aspi_win32.cpp
@@ -17,11 +17,14 @@
*/
+#include "dosbox.h"
+
+#if C_PHYSICAL_CDROM_MOUNT
+
#if defined (WIN32)
#include <ctype.h>
-#include "dosbox.h"
#include "cdrom.h"
#include "support.h"
@@ -766,3 +769,4 @@ bool CDROM_Interface_Aspi::ReadSectors(PhysPt buffer, bool raw, unsigned long se
};
#endif
+#endif /* C_PHYSICAL_CDROM_MOUNT */
diff --git a/src/dos/cdrom_ioctl_linux.cpp b/src/dos/cdrom_ioctl_linux.cpp
index fb73986e8..079b440e4 100644
--- a/src/dos/cdrom_ioctl_linux.cpp
+++ b/src/dos/cdrom_ioctl_linux.cpp
@@ -17,8 +17,11 @@
*/
-#include <string.h>
#include "cdrom.h"
+
+#if C_PHYSICAL_CDROM_MOUNT
+
+#include <string.h>
#include "support.h"
#if defined (LINUX)
@@ -95,3 +98,4 @@ bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD)
}
#endif
+#endif /* C_PHYSICAL_CDROM_MOUNT */
diff --git a/src/dos/cdrom_ioctl_os2.cpp b/src/dos/cdrom_ioctl_os2.cpp
index 6e32af55b..30eb1a50a 100644
--- a/src/dos/cdrom_ioctl_os2.cpp
+++ b/src/dos/cdrom_ioctl_os2.cpp
@@ -17,8 +17,11 @@
*/
-#include <string.h>
#include "dosbox.h"
+
+#if C_PHYSICAL_CDROM_MOUNT
+
+#include <string.h>
#include "cdrom.h"
#if defined (OS2)
@@ -150,3 +153,4 @@ bool CDROM_Interface_Ioctl::SetDevice(char* path, int forceCD) {
}
#endif
+#endif /* C_PHYSICAL_CDROM_MOUNT */
diff --git a/src/dos/cdrom_ioctl_win32.cpp b/src/dos/cdrom_ioctl_win32.cpp
index 465e6bd62..bd277bcc0 100644
--- a/src/dos/cdrom_ioctl_win32.cpp
+++ b/src/dos/cdrom_ioctl_win32.cpp
@@ -17,6 +17,10 @@
*/
+#include "cdrom.h"
+
+#if C_PHYSICAL_CDROM_MOUNT
+
#if defined (WIN32)
// *****************************************************************
@@ -27,16 +31,14 @@
#include <io.h>
#if (defined (_MSC_VER)) || (defined __MINGW64_VERSION_MAJOR)
-#include <ntddcdrm.h> // Ioctl stuff
#include <winioctl.h> // Ioctl stuff
+#include <ntddcdrm.h> // Ioctl stuff
#else
#include "ddk/ntddcdrm.h" // Ioctl stuff
#endif
#include <mmsystem.h>
-#include "cdrom.h"
-
// for a more sophisticated implementation of the mci cdda functionality
// see the SDL sources, which the mci_ functions are based on
@@ -621,3 +623,4 @@ void CDROM_Interface_Ioctl::Close(void) {
}
#endif
+#endif /* C_PHYSICAL_CDROM_MOUNT */
diff --git a/src/dos/dos_mscdex.cpp b/src/dos/dos_mscdex.cpp
index a6d1b9c32..004afdf22 100644
--- a/src/dos/dos_mscdex.cpp
+++ b/src/dos/dos_mscdex.cpp
@@ -48,7 +48,9 @@
#define REQUEST_STATUS_ERROR 0x8000
// Use cdrom Interface
+#if C_PHYSICAL_CDROM_MOUNT
int useCdromInterface = CDROM_USE_SDL;
+#endif
int forceCD = -1;
static Bitu MSCDEX_Strategy_Handler(void);
@@ -253,6 +255,7 @@ int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit)
int result = 0;
// Get Mounttype and init needed cdrom interface
switch (CDROM_GetMountType(physicalPath,forceCD)) {
+#if C_PHYSICAL_CDROM_MOUNT
case 0x00: {
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting physical cdrom: %s" ,physicalPath);
#if defined (WIN32)
@@ -295,6 +298,7 @@ int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit)
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: SDL Interface.");
#endif
} break;
+#endif /* C_PHYSICAL_CDROM_MOUNT */
case 0x01: // iso cdrom interface
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting iso file as cdrom: %s", physicalPath);
cdrom[numDrives] = new CDROM_Interface_Image((Bit8u)numDrives);
@@ -1327,10 +1331,12 @@ bool MSCDEX_HasMediaChanged(Bit8u subUnit)
return true;
}
+#if C_PHYSICAL_CDROM_MOUNT
void MSCDEX_SetCDInterface(int intNr, int numCD) {
useCdromInterface = intNr;
forceCD = numCD;
}
+#endif
void MSCDEX_ShutDown(Section* /*sec*/) {
delete mscdex;
diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp
index cdea5d7b1..517e7886b 100644
--- a/src/dos/dos_programs.cpp
+++ b/src/dos/dos_programs.cpp
@@ -56,7 +56,12 @@
Bitu DEBUG_EnableDebugger(void);
#endif
+#if C_PHYSICAL_CDROM_MOUNT
+#define PROGRAM_INTRO_CDROM_SUFFIX "PROGRAM_INTRO_CDROM_PHYS"
void MSCDEX_SetCDInterface(int intNr, int forceCD);
+#else
+#define PROGRAM_INTRO_CDROM_SUFFIX "PROGRAM_INTRO_CDROM_NOPHYS"
+#endif
static Bitu ZDRIVE_NUM = 25;
class MOUNT : public Program {
@@ -184,11 +189,15 @@ public:
}
/* Show list of cdroms */
if (cmd->FindExist("-cd",false)) {
+#if C_PHYSICAL_CDROM_MOUNT
int num = SDL_CDNumDrives();
WriteOut(MSG_Get("PROGRAM_MOUNT_CDROMS_FOUND"),num);
for (int i=0; i<num; i++) {
WriteOut("%2d. %s\n",i,SDL_CDName(i));
};
+#else
+ WriteOut(MSG_Get("PROGRAM_MOUNT_PHYS_CDROMS_NOT_SUPPORTED"));
+#endif
return;
}
@@ -326,9 +335,10 @@ public:
if (temp_line[temp_line.size()-1]!=CROSS_FILESPLIT) temp_line+=CROSS_FILESPLIT;
Bit8u bit8size=(Bit8u) sizes[1];
if (type=="cdrom") {
+ int error = 0;
+#if C_PHYSICAL_CDROM_MOUNT
int num = -1;
cmd->FindInt("-usecd",num,true);
- int error = 0;
if (cmd->FindExist("-aspi",false)) {
MSCDEX_SetCDInterface(CDROM_USE_ASPI, num);
} else if (cmd->FindExist("-ioctl_dio",false)) {
@@ -357,6 +367,20 @@ public:
MSCDEX_SetCDInterface(CDROM_USE_IOCTL_DIO, num);
#endif
}
+#else /* C_PHYSICAL_CDROM_MOUNT */
+ if (cmd->FindExist("-usecd",false)
+ || cmd->FindExist("-aspi",false)
+ || cmd->FindExist("-ioctl_dio",false)
+ || cmd->FindExist("-ioctl_dx",false)
+#if defined (WIN32)
+ || cmd->FindExist("-ioctl_mci",false)
+#endif
+ || cmd->FindExist("-noioctl",false)
+ ) {
+ WriteOut(MSG_Get("PROGRAM_MOUNT_PHYS_CDROMS_NOT_SUPPORTED"));
+ /* Just ignore, mount anyway */
+ }
+#endif /* C_PHYSICAL_CDROM_MOUNT */
newdrive = new cdromDrive(drive,temp_line.c_str(),sizes[0],bit8size,sizes[2],0,mediaid,error);
// Check Mscdex, if it worked out...
switch (error) {
@@ -1069,7 +1093,8 @@ public:
/* Only run if called from the first shell (Xcom TFTD runs any intro file in the path) */
if(DOS_PSP(dos.psp()).GetParent() != DOS_PSP(DOS_PSP(dos.psp()).GetParent()).GetParent()) return;
if(cmd->FindExist("cdrom",false)) {
- WriteOut(MSG_Get("PROGRAM_INTRO_CDROM"));
+ WriteOut(MSG_Get("PROGRAM_INTRO_CDROM_PREFIX"));
+ WriteOut(MSG_Get(PROGRAM_INTRO_CDROM_SUFFIX));
return;
}
if(cmd->FindExist("mount",false)) {
@@ -1087,7 +1112,8 @@ public:
DOS_ReadFile (STDIN,&c,&n);
DisplayMount();
DOS_ReadFile (STDIN,&c,&n);
- WriteOut(MSG_Get("PROGRAM_INTRO_CDROM"));
+ WriteOut(MSG_Get("PROGRAM_INTRO_CDROM_PREFIX"));
+ WriteOut(MSG_Get(PROGRAM_INTRO_CDROM_SUFFIX));
DOS_ReadFile (STDIN,&c,&n);
WriteOut(MSG_Get("PROGRAM_INTRO_SPECIAL"));
}
@@ -1359,7 +1385,9 @@ public:
WriteOut(MSG_Get("PROGRAM_IMGMOUNT_ALREADY_MOUNTED"));
return;
}
+#if C_PHYSICAL_CDROM_MOUNT
MSCDEX_SetCDInterface(CDROM_USE_SDL, -1);
+#endif
// create new drives for all images
std::vector<DOS_Drive*> isoDisks;
std::vector<std::string>::size_type i;
@@ -1514,7 +1542,8 @@ static void KEYB_ProgramStart(Program * * make) {
void DOS_SetupPrograms(void) {
/*Add Messages */
- MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n");
+ MSG_Add("PROGRAM_MOUNT_CDROMS_FOUND","CDROMs found: %d\n"); // C_PHYSICAL_CDROM_MOUNT
+ MSG_Add("PROGRAM_MOUNT_PHYS_CDROMS_NOT_SUPPORTED","Physical CDROMs aren't fully supported. IMGMOUNT may be more useful.\n"); // !C_PHYSICAL_CDROM_MOUNT
MSG_Add("PROGRAM_MOUNT_STATUS_FORMAT","%-5s %-58s %-12s\n");
MSG_Add("PROGRAM_MOUNT_STATUS_2","Drive %c is mounted as %s\n");
MSG_Add("PROGRAM_MOUNT_STATUS_1","The currently mounted drives are:\n");
@@ -1603,7 +1632,7 @@ void DOS_SetupPrograms(void) {
"enter a directory (recognised by the \033[33;1m[]\033[0m in a directory listing).\n"
"You can run programs/files which end with \033[31m.exe .bat\033[0m and \033[31m.com\033[0m.\n"
);
- MSG_Add("PROGRAM_INTRO_CDROM",
+ MSG_Add("PROGRAM_INTRO_CDROM_PREFIX",
"\033[2J\033[32;1mHow to mount a Real/Virtual CD-ROM Drive in DOSBox:\033[0m\n"
"DOSBox provides CD-ROM emulation on several levels.\n"
"\n"
@@ -1614,6 +1643,8 @@ void DOS_SetupPrograms(void) {
"If it doesn't work you might have to tell DOSBox the label of the CD-ROM:\n"
"\033[34;1mmount d C:\\example -t cdrom -label CDLABEL\033[0m\n"
"\n"
+ );
+ MSG_Add("PROGRAM_INTRO_CDROM_PHYS", // C_PHYSICAL_CDROM_MOUNT
"The \033[33mnext\033[0m level adds some low-level support.\n"
"Therefore only works on CD-ROM drives:\n"
"\033[34;1mmount d \033[0;31mD:\\\033[34;1m -t cdrom -usecd \033[33m0\033[0m\n"
@@ -1628,6 +1659,14 @@ void DOS_SetupPrograms(void) {
"Replace the \033[33;1m0\033[0m in \033[34;1m-usecd \033[33m0\033[0m with the number reported for your CD-ROM if you type:\n"
"\033[34;1mmount -cd\033[0m\n"
);
+ MSG_Add("PROGRAM_INTRO_CDROM_NOPHYS", // !C_PHYSICAL_CDROM_MOUNT
+ "The \033[33mhigher\033[0m level adds CD-ROM image mounting support.\n"
+ "Therefore only works on supported CD-ROM images:\n"
+ "\033[34;1mimgmount d \033[0;31mD:\\example.img\033[34;1m -t cdrom\033[0m\n"
+ "\n"
+ "Replace \033[0;31mD:\\\033[0m with the location of your CD-ROM.\n"
+ "Replace \033[0;31mD:\\example.img\033[0m with the location of your CD-ROM image.\n"
+ );
MSG_Add("PROGRAM_INTRO_SPECIAL",
"\033[2J\033[32;1mSpecial keys:\033[0m\n"
"These are the default keybindings.\n"
diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp
index 46ebb7df5..b8956eb40 100644
--- a/src/dos/drive_local.cpp
+++ b/src/dos/drive_local.cpp
@@ -273,7 +273,12 @@ again:
find_size=(Bit32u) stat_block.st_size;
struct tm *time;
+#ifdef __ANDROID__ // temp_stat.st_mtime is of type unsigned long, not time_t
+ time_t rawtime=stat_block.st_mtime;
+ if((time=localtime(&rawtime))!=0){
+#else
if((time=localtime(&stat_block.st_mtime))!=0){
+#endif
find_date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
find_time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
} else {
@@ -391,7 +396,12 @@ bool localDrive::FileStat(const char* name, FileStat_Block * const stat_block) {
if(stat(newname,&temp_stat)!=0) return false;
/* Convert the stat to a FileStat */
struct tm *time;
+#ifdef __ANDROID__ // temp_stat.st_mtime is of type unsigned long, not time_t
+ time_t rawtime=temp_stat.st_mtime;
+ if((time=localtime(&rawtime))!=0) {
+#else
if((time=localtime(&temp_stat.st_mtime))!=0) {
+#endif
stat_block->time=DOS_PackTime((Bit16u)time->tm_hour,(Bit16u)time->tm_min,(Bit16u)time->tm_sec);
stat_block->date=DOS_PackDate((Bit16u)(time->tm_year+1900),(Bit16u)(time->tm_mon+1),(Bit16u)time->tm_mday);
} else {
@@ -530,7 +540,12 @@ bool localFile::UpdateDateTimeFromHost(void) {
struct stat temp_stat;
fstat(fileno(fhandle),&temp_stat);
struct tm * ltime;
+#ifdef __ANDROID__ // temp_stat.st_mtime is of type unsigned long, not time_t
+ time_t rawtime=temp_stat.st_mtime;
+ if((ltime=localtime(&rawtime))!=0) {
+#else
if((ltime=localtime(&temp_stat.st_mtime))!=0) {
+#endif
time=DOS_PackTime((Bit16u)ltime->tm_hour,(Bit16u)ltime->tm_min,(Bit16u)ltime->tm_sec);
date=DOS_PackDate((Bit16u)(ltime->tm_year+1900),(Bit16u)(ltime->tm_mon+1),(Bit16u)ltime->tm_mday);
} else {
diff --git a/src/gui/sdl_mapper.cpp b/src/gui/sdl_mapper.cpp
index 2e6f7def9..0f51cb99a 100644
--- a/src/gui/sdl_mapper.cpp
+++ b/src/gui/sdl_mapper.cpp
@@ -44,7 +44,8 @@ enum {
CLR_WHITE=2,
CLR_RED=3,
CLR_BLUE=4,
- CLR_GREEN=5
+ CLR_GREEN=5,
+ CLR_LAST
};
enum BB_Types {
@@ -67,7 +68,8 @@ enum BC_Types {
#define MAXSTICKS 8
#define MAXACTIVE 16
-#define MAXBUTTON 32
+// Use 36 for Android (KEYCODE_BUTTON_1..16 are mapped to SDL buttons 20..35)
+#define MAXBUTTON 36
#define MAXBUTTON_CAP 16
class CEvent;
@@ -299,6 +301,8 @@ protected:
};
+#if !SDL_VERSION_ATLEAST(2,0,0)
+
#define MAX_SDLKEYS 323
static bool usescancodes;
@@ -469,23 +473,41 @@ Bitu GetKeyCode(SDL_keysym keysym) {
}
}
+#endif // SDL 1.2
+
class CKeyBind;
class CKeyBindGroup;
class CKeyBind : public CBind {
public:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ CKeyBind(CBindList * _list,SDL_Scancode _key) : CBind(_list) {
+#else
CKeyBind(CBindList * _list,SDLKey _key) : CBind(_list) {
+#endif
key = _key;
}
void BindName(char * buf) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sprintf(buf,"Key %s",SDL_GetScancodeName(key));
+#else
sprintf(buf,"Key %s",SDL_GetKeyName(MapSDLCode((Bitu)key)));
+#endif
}
void ConfigName(char * buf) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sprintf(buf,"key %d",key);
+#else
sprintf(buf,"key %d",MapSDLCode((Bitu)key));
+#endif
}
public:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_Scancode key;
+#else
SDLKey key;
+#endif
};
class CKeyBindGroup : public CBindGroup {
@@ -501,28 +523,44 @@ public:
if (strncasecmp(buf,configname,strlen(configname))) return 0;
StripWord(buf);char * num=StripWord(buf);
Bitu code=ConvDecWord(num);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ CBind * bind=CreateKeyBind((SDL_Scancode)code);
+#else
if (usescancodes) {
if (code<MAX_SDLKEYS) code=scancode_map[code];
else code=0;
}
CBind * bind=CreateKeyBind((SDLKey)code);
+#endif
return bind;
}
CBind * CreateEventBind(SDL_Event * event) {
if (event->type!=SDL_KEYDOWN) return 0;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ return CreateKeyBind(event->key.keysym.scancode);
+#else
return CreateKeyBind((SDLKey)GetKeyCode(event->key.keysym));
+#endif
};
bool CheckEvent(SDL_Event * event) {
if (event->type!=SDL_KEYDOWN && event->type!=SDL_KEYUP) return false;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ Bitu key = event->key.keysym.scancode;
+#else // SDL 1.2
Bitu key=GetKeyCode(event->key.keysym);
// LOG_MSG("key type %i is %x [%x %x]",event->type,key,event->key.keysym.sym,event->key.keysym.scancode);
assert(Bitu(event->key.keysym.sym)<keys);
+#endif // SDL 2.0/1.2
if (event->type==SDL_KEYDOWN) ActivateBindList(&lists[key],0x7fff,true);
else DeactivateBindList(&lists[key],true);
return 0;
}
+#if SDL_VERSION_ATLEAST(2,0,0)
+ CBind * CreateKeyBind(SDL_Scancode _key) {
+#else
CBind * CreateKeyBind(SDLKey _key) {
if (!usescancodes) assert((Bitu)_key<keys);
+#endif
return new CKeyBind(&lists[(Bitu)_key],_key);
}
private:
@@ -675,7 +713,11 @@ public:
if (axes_cap>axes) axes_cap=axes;
hats_cap=emulated_hats;
if (hats_cap>hats) hats_cap=hats;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickNameForIndex(stick),axes,buttons,hats);
+#else
LOG_MSG("Using joystick %s with %d axes, %d buttons and %d hat(s)",SDL_JoystickName(stick),axes,buttons,hats);
+#endif
}
~CStickBindGroup() {
SDL_JoystickClose(sdl_joystick);
@@ -712,7 +754,7 @@ public:
if (abs(event->jaxis.value)<25000) return 0;
return CreateAxisBind(event->jaxis.axis,event->jaxis.value>0);
} else if (event->type==SDL_JOYBUTTONDOWN) {
- if (event->button.which!=stick) return 0;
+ if (event->jbutton.which!=stick) return 0;
#if defined (REDUCE_JOYSTICK_POLLING)
return CreateButtonBind(event->jbutton.button%button_wrap);
#else
@@ -881,7 +923,11 @@ private:
return configname;
}
const char * BindStart(void) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (sdl_joystick!=NULL) return SDL_JoystickNameForIndex(stick);
+#else
if (sdl_joystick!=NULL) return SDL_JoystickName(stick);
+#endif
else return "[missing joystick]";
}
@@ -1240,6 +1286,11 @@ protected:
};
static struct CMapper {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_Window * window;
+ SDL_Rect draw_rect;
+ SDL_Surface * draw_surface_nonpaletted; // Needed for SDL_BlitScaled
+#endif
SDL_Surface * surface;
SDL_Surface * draw_surface;
bool exit;
@@ -1277,7 +1328,7 @@ void CBindGroup::DeactivateBindList(CBindList * list,bool ev_trigger) {
}
static void DrawText(Bitu x,Bitu y,const char * text,Bit8u color) {
- Bit8u * draw=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
+ Bit8u * draw=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
while (*text) {
Bit8u * font=&int10_font_14[(*text)*14];
Bitu i,j;Bit8u * draw_line=draw;
@@ -1288,7 +1339,7 @@ static void DrawText(Bitu x,Bitu y,const char * text,Bit8u color) {
else *(draw_line+j)=CLR_BLACK;
map<<=1;
}
- draw_line+=mapper.surface->pitch;
+ draw_line+=mapper.draw_surface->w;
}
text++;draw+=8;
}
@@ -1305,14 +1356,14 @@ public:
}
virtual void Draw(void) {
if (!enabled) return;
- Bit8u * point=((Bit8u *)mapper.surface->pixels)+(y*mapper.surface->pitch)+x;
+ Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+(y*mapper.draw_surface->w)+x;
for (Bitu lines=0;lines<dy;lines++) {
if (lines==0 || lines==(dy-1)) {
for (Bitu cols=0;cols<dx;cols++) *(point+cols)=color;
} else {
*point=color;*(point+dx-1)=color;
}
- point+=mapper.surface->pitch;
+ point+=mapper.draw_surface->w;
}
}
virtual bool OnTop(Bitu _x,Bitu _y) {
@@ -1343,13 +1394,21 @@ protected:
const char * text;
};
+class CClickableTextButton : public CTextButton {
+public:
+ CClickableTextButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text) : CTextButton(_x,_y,_dx,_dy,_text) {}
+ void BindColor(void) {
+ this->SetColor(CLR_WHITE);
+ }
+};
+
class CEventButton;
static CEventButton * last_clicked = NULL;
-class CEventButton : public CTextButton {
+class CEventButton : public CClickableTextButton {
public:
CEventButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,CEvent * _event)
- : CTextButton(_x,_y,_dx,_dy,_text) {
+ : CClickableTextButton(_x,_y,_dx,_dy,_text) {
event=_event;
}
void BindColor(void) {
@@ -1391,10 +1450,10 @@ void CCaptionButton::Change(const char * format,...) {
static void change_action_text(const char* text,Bit8u col);
static void MAPPER_SaveBinds(void);
-class CBindButton : public CTextButton {
+class CBindButton : public CClickableTextButton {
public:
CBindButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BB_Types _type)
- : CTextButton(_x,_y,_dx,_dy,_text) {
+ : CClickableTextButton(_x,_y,_dx,_dy,_text) {
type=_type;
}
void Click(void) {
@@ -1433,10 +1492,10 @@ protected:
BB_Types type;
};
-class CCheckButton : public CTextButton {
+class CCheckButton : public CClickableTextButton {
public:
CCheckButton(Bitu _x,Bitu _y,Bitu _dx,Bitu _dy,const char * _text,BC_Types _type)
- : CTextButton(_x,_y,_dx,_dy,_text) {
+ : CClickableTextButton(_x,_y,_dx,_dy,_text) {
type=_type;
}
void Draw(void) {
@@ -1457,13 +1516,13 @@ public:
break;
}
if (checked) {
- Bit8u * point=((Bit8u *)mapper.surface->pixels)+((y+2)*mapper.surface->pitch)+x+dx-dy+2;
+ Bit8u * point=((Bit8u *)mapper.draw_surface->pixels)+((y+2)*mapper.draw_surface->w)+x+dx-dy+2;
for (Bitu lines=0;lines<(dy-4);lines++) {
memset(point,color,dy-4);
- point+=mapper.surface->pitch;
+ point+=mapper.draw_surface->w;
}
}
- CTextButton::Draw();
+ CClickableTextButton::Draw();
}
void Click(void) {
switch (type) {
@@ -1589,25 +1648,53 @@ public:
case MK_f1:case MK_f2:case MK_f3:case MK_f4:
case MK_f5:case MK_f6:case MK_f7:case MK_f8:
case MK_f9:case MK_f10:case MK_f11:case MK_f12:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_F1+(defkey-MK_f1);
+#else
key=SDLK_F1+(defkey-MK_f1);
+#endif
break;
case MK_return:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_RETURN;
+#else
key=SDLK_RETURN;
+#endif
break;
case MK_kpminus:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_KP_MINUS;
+#else
key=SDLK_KP_MINUS;
+#endif
break;
case MK_scrolllock:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_SCROLLLOCK;
+#else
key=SDLK_SCROLLOCK;
+#endif
break;
case MK_pause:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_PAUSE;
+#else
key=SDLK_PAUSE;
+#endif
break;
case MK_printscreen:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_PRINTSCREEN;
+#else
key=SDLK_PRINT;
+#endif
break;
case MK_home:
+#if SDL_VERSION_ATLEAST(2,0,0)
+ key=SDL_SCANCODE_HOME;
+#else
key=SDLK_HOME;
+#endif
break;
}
sprintf(buf,"%s \"key %d%s%s%s\"",
@@ -1689,14 +1776,27 @@ static void SetActiveEvent(CEvent * event) {
}
}
+#if SDL_VERSION_ATLEAST(2,0,0)
+extern SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height);
+extern SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height);
+extern void GFX_UpdateDisplayDimensions(int width, int height);
+#endif
+
static void DrawButtons(void) {
- SDL_FillRect(mapper.surface,0,0);
- SDL_LockSurface(mapper.surface);
+ SDL_FillRect(mapper.draw_surface,0,CLR_BLACK);
for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) {
(*but_it)->Draw();
}
- SDL_UnlockSurface(mapper.surface);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ // We can't just use SDL_BlitScaled (say for Android) in one step
+ SDL_BlitSurface(mapper.draw_surface, NULL, mapper.draw_surface_nonpaletted, NULL);
+ SDL_BlitScaled(mapper.draw_surface_nonpaletted, NULL, mapper.surface, &mapper.draw_rect);
+ //SDL_BlitSurface(mapper.draw_surface, NULL, mapper.surface, NULL);
+ SDL_UpdateWindowSurface(mapper.window);
+#else
+ SDL_BlitSurface(mapper.draw_surface, NULL, mapper.surface, NULL);
SDL_Flip(mapper.surface);
+#endif
}
static CKeyEvent * AddKeyButtonEvent(Bitu x,Bitu y,Bitu dx,Bitu dy,char const * const title,char const * const entry,KBD_KEYS key) {
@@ -2003,7 +2103,7 @@ static void CreateLayout(void) {
bind_but.bind_title->Change("Bind Title");
}
-static SDL_Color map_pal[6]={
+static SDL_Color map_pal[CLR_LAST]={
{0x00,0x00,0x00,0x00}, //0=black
{0x7f,0x7f,0x7f,0x00}, //1=grey
{0xff,0xff,0xff,0x00}, //2=white
@@ -2042,6 +2142,49 @@ static struct {
const char * eventend;
Bitu key;
} DefaultKeys[]={
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+
+ {"f1",SDL_SCANCODE_F1}, {"f2",SDL_SCANCODE_F2}, {"f3",SDL_SCANCODE_F3}, {"f4",SDL_SCANCODE_F4},
+ {"f5",SDL_SCANCODE_F5}, {"f6",SDL_SCANCODE_F6}, {"f7",SDL_SCANCODE_F7}, {"f8",SDL_SCANCODE_F8},
+ {"f9",SDL_SCANCODE_F9}, {"f10",SDL_SCANCODE_F10}, {"f11",SDL_SCANCODE_F11}, {"f12",SDL_SCANCODE_F12},
+
+ {"1",SDL_SCANCODE_1}, {"2",SDL_SCANCODE_2}, {"3",SDL_SCANCODE_3}, {"4",SDL_SCANCODE_4},
+ {"5",SDL_SCANCODE_5}, {"6",SDL_SCANCODE_6}, {"7",SDL_SCANCODE_7}, {"8",SDL_SCANCODE_8},
+ {"9",SDL_SCANCODE_9}, {"0",SDL_SCANCODE_0},
+
+ {"a",SDL_SCANCODE_A}, {"b",SDL_SCANCODE_B}, {"c",SDL_SCANCODE_C}, {"d",SDL_SCANCODE_D},
+ {"e",SDL_SCANCODE_E}, {"f",SDL_SCANCODE_F}, {"g",SDL_SCANCODE_G}, {"h",SDL_SCANCODE_H},
+ {"i",SDL_SCANCODE_I}, {"j",SDL_SCANCODE_J}, {"k",SDL_SCANCODE_K}, {"l",SDL_SCANCODE_L},
+ {"m",SDL_SCANCODE_M}, {"n",SDL_SCANCODE_N}, {"o",SDL_SCANCODE_O}, {"p",SDL_SCANCODE_P},
+ {"q",SDL_SCANCODE_Q}, {"r",SDL_SCANCODE_R}, {"s",SDL_SCANCODE_S}, {"t",SDL_SCANCODE_T},
+ {"u",SDL_SCANCODE_U}, {"v",SDL_SCANCODE_V}, {"w",SDL_SCANCODE_W}, {"x",SDL_SCANCODE_X},
+ {"y",SDL_SCANCODE_Y}, {"z",SDL_SCANCODE_Z}, {"space",SDL_SCANCODE_SPACE},
+ {"esc",SDL_SCANCODE_ESCAPE}, {"equals",SDL_SCANCODE_EQUALS}, {"grave",SDL_SCANCODE_GRAVE},
+ {"tab",SDL_SCANCODE_TAB}, {"enter",SDL_SCANCODE_RETURN}, {"bspace",SDL_SCANCODE_BACKSPACE},
+ {"lbracket",SDL_SCANCODE_LEFTBRACKET}, {"rbracket",SDL_SCANCODE_RIGHTBRACKET},
+ {"minus",SDL_SCANCODE_MINUS}, {"capslock",SDL_SCANCODE_CAPSLOCK}, {"semicolon",SDL_SCANCODE_SEMICOLON},
+ {"quote", SDL_SCANCODE_APOSTROPHE}, {"backslash",SDL_SCANCODE_BACKSLASH}, {"lshift",SDL_SCANCODE_LSHIFT},
+ {"rshift",SDL_SCANCODE_RSHIFT}, {"lalt",SDL_SCANCODE_LALT}, {"ralt",SDL_SCANCODE_RALT},
+ {"lctrl",SDL_SCANCODE_LCTRL}, {"rctrl",SDL_SCANCODE_RCTRL}, {"comma",SDL_SCANCODE_COMMA},
+ {"period",SDL_SCANCODE_PERIOD}, {"slash",SDL_SCANCODE_SLASH}, {"printscreen",SDL_SCANCODE_PRINTSCREEN},
+ {"scrolllock",SDL_SCANCODE_SCROLLLOCK}, {"pause",SDL_SCANCODE_PAUSE}, {"pagedown",SDL_SCANCODE_PAGEDOWN},
+ {"pageup",SDL_SCANCODE_PAGEUP}, {"insert",SDL_SCANCODE_INSERT}, {"home",SDL_SCANCODE_HOME},
+ {"delete",SDL_SCANCODE_DELETE}, {"end",SDL_SCANCODE_END}, {"up",SDL_SCANCODE_UP},
+ {"left",SDL_SCANCODE_LEFT}, {"down",SDL_SCANCODE_DOWN}, {"right",SDL_SCANCODE_RIGHT},
+ {"kp_0",SDL_SCANCODE_KP_0}, {"kp_1",SDL_SCANCODE_KP_1}, {"kp_2",SDL_SCANCODE_KP_2}, {"kp_3",SDL_SCANCODE_KP_3},
+ {"kp_4",SDL_SCANCODE_KP_4}, {"kp_5",SDL_SCANCODE_KP_5}, {"kp_6",SDL_SCANCODE_KP_6}, {"kp_7",SDL_SCANCODE_KP_7},
+ {"kp_8",SDL_SCANCODE_KP_8}, {"kp_9",SDL_SCANCODE_KP_9}, {"numlock",SDL_SCANCODE_NUMLOCKCLEAR},
+ {"kp_divide",SDL_SCANCODE_KP_DIVIDE}, {"kp_multiply",SDL_SCANCODE_KP_MULTIPLY},
+ {"kp_minus",SDL_SCANCODE_KP_MINUS}, {"kp_plus",SDL_SCANCODE_KP_PLUS},
+ {"kp_period",SDL_SCANCODE_KP_PERIOD}, {"kp_enter",SDL_SCANCODE_KP_ENTER},
+
+ /* Is that the extra backslash key ("less than" key) */
+ /* on some keyboards with the 102-keys layout?? */
+ {"lessthan",SDL_SCANCODE_NONUSBACKSLASH},
+
+#else // !SDL_VERSION_ATLEAST(2,0,0)
+
{"f1",SDLK_F1}, {"f2",SDLK_F2}, {"f3",SDLK_F3}, {"f4",SDLK_F4},
{"f5",SDLK_F5}, {"f6",SDLK_F6}, {"f7",SDLK_F7}, {"f8",SDLK_F8},
{"f9",SDLK_F9}, {"f10",SDLK_F10}, {"f11",SDLK_F11}, {"f12",SDLK_F12},
@@ -2083,6 +2226,8 @@ static struct {
{"lessthan",SDLK_LESS},
#endif
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
+
{0,0}
};
@@ -2094,10 +2239,17 @@ static void CreateDefaultBinds(void) {
CreateStringBind(buffer);
i++;
}
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_RCTRL);CreateStringBind(buffer);
+ sprintf(buffer,"mod_1 \"key %d\"",SDL_SCANCODE_LCTRL);CreateStringBind(buffer);
+ sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_RALT);CreateStringBind(buffer);
+ sprintf(buffer,"mod_2 \"key %d\"",SDL_SCANCODE_LALT);CreateStringBind(buffer);
+#else
sprintf(buffer,"mod_1 \"key %d\"",SDLK_RCTRL);CreateStringBind(buffer);
sprintf(buffer,"mod_1 \"key %d\"",SDLK_LCTRL);CreateStringBind(buffer);
sprintf(buffer,"mod_2 \"key %d\"",SDLK_RALT);CreateStringBind(buffer);
sprintf(buffer,"mod_2 \"key %d\"",SDLK_LALT);CreateStringBind(buffer);
+#endif
for (CHandlerEventVector_it hit=handlergroup.begin();hit!=handlergroup.end();hit++) {
(*hit)->MakeDefaultBind(buffer);
CreateStringBind(buffer);
@@ -2190,17 +2342,92 @@ void MAPPER_CheckEvent(SDL_Event * event) {
void BIND_MappingEvents(void) {
SDL_Event event;
+ static bool isButtonPressed = false;
+ static CButton *lastHoveredButton = NULL;
while (SDL_PollEvent(&event)) {
switch (event.type) {
+ case SDL_MOUSEBUTTONDOWN:
+ isButtonPressed = true;
+ /* Further check where are we pointing at right now */
+ case SDL_MOUSEMOTION:
+ if (!isButtonPressed)
+ break;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ /* Normalize position in case a scaled sub-window is used (say on Android) */
+ event.button.x=(event.button.x-mapper.draw_rect.x)*mapper.draw_surface->w/mapper.draw_rect.w;
+ if ((event.button.x<0) || (event.button.x>=mapper.draw_surface->w))
+ break;
+ event.button.y=(event.button.y-mapper.draw_rect.y)*mapper.draw_surface->h/mapper.draw_rect.h;
+ if ((event.button.y<0) || (event.button.y>=mapper.draw_surface->h))
+ break;
+#endif
+ /* Maybe we have been pointing at a specific button for a little while */
+ if (lastHoveredButton) {
+ /* Check if there's any change */
+ if (lastHoveredButton->OnTop(event.button.x,event.button.y))
+ break;
+ /* Not pointing at given button anymore */
+ if (lastHoveredButton == last_clicked)
+ lastHoveredButton->Click();
+ else
+ lastHoveredButton->BindColor();
+ mapper.redraw=true;
+ lastHoveredButton=NULL;
+ }
+ /* Check which button are we currently pointing at */
+ for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) {
+ if (dynamic_cast<CClickableTextButton *>(*but_it) && (*but_it)->OnTop(event.button.x,event.button.y)) {
+ (*but_it)->SetColor(CLR_RED);
+ mapper.redraw=true;
+ lastHoveredButton=*but_it;
+ break;
+ }
+ }
+ break;
case SDL_MOUSEBUTTONUP:
+ isButtonPressed = false;
+ if (lastHoveredButton) {
+ /* For most buttons the actual new color is going to be green; But not for a few others. */
+ lastHoveredButton->BindColor();
+ mapper.redraw=true;
+ lastHoveredButton = NULL;
+ }
+#if SDL_VERSION_ATLEAST(2,0,0)
+ /* Normalize position in case a scaled sub-window is used (say on Android) */
+ event.button.x=(event.button.x-mapper.draw_rect.x)*mapper.draw_surface->w/mapper.draw_rect.w;
+ if ((event.button.x<0) || (event.button.x>=mapper.draw_surface->w))
+ break;
+ event.button.y=(event.button.y-mapper.draw_rect.y)*mapper.draw_surface->h/mapper.draw_rect.h;
+ if ((event.button.y<0) || (event.button.y>=mapper.draw_surface->h))
+ break;
+#endif
/* Check the press */
for (CButton_it but_it = buttons.begin();but_it!=buttons.end();but_it++) {
- if ((*but_it)->OnTop(event.button.x,event.button.y)) {
+ if (dynamic_cast<CClickableTextButton *>(*but_it) && (*but_it)->OnTop(event.button.x,event.button.y)) {
(*but_it)->Click();
+ break;
}
- }
+ }
break;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SDL_WINDOWEVENT:
+ /* The resize event MAY arrive e.g. when the mapper is
+ * toggled, at least on X11. Furthermore, the restore
+ * event should be handled on Android.
+ */
+ if ((event.window.event == SDL_WINDOWEVENT_RESIZED)
+ || (event.window.event == SDL_WINDOWEVENT_RESTORED)) {
+ mapper.surface = SDL_GetWindowSurface(mapper.window);
+ if (mapper.surface == NULL) E_Exit("Couldn't refresh mapper window surface after resize or restoration: %s",SDL_GetError());
+ GFX_UpdateDisplayDimensions(event.window.data1, event.window.data2);
+ mapper.draw_rect=GFX_GetSDLSurfaceSubwindowDims(640,480);
+ DrawButtons();
+ }
+ break;
+#endif
case SDL_QUIT:
+ isButtonPressed = false;
+ lastHoveredButton = NULL;
mapper.exit=true;
break;
default:
@@ -2275,7 +2502,11 @@ static void InitializeJoysticks(void) {
static void CreateBindGroups(void) {
bindgroups.clear();
+#if SDL_VERSION_ATLEAST(2,0,0)
+ new CKeyBindGroup(SDL_NUM_SCANCODES);
+#else
new CKeyBindGroup(SDLK_LAST);
+#endif
if (joytype != JOY_NONE) {
#if defined (REDUCE_JOYSTICK_POLLING)
// direct access to the SDL joystick, thus removed from the event handling
@@ -2358,11 +2589,29 @@ void MAPPER_RunInternal() {
/* Be sure that there is no update in progress */
GFX_EndUpdate( 0 );
- mapper.surface=SDL_SetVideoMode_Wrap(640,480,8,0);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ mapper.window=GFX_SetSDLSurfaceWindow(640,480);
+ if (mapper.window == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
+ mapper.surface=SDL_GetWindowSurface(mapper.window);
+ if (mapper.surface == NULL) E_Exit("Could not retrieve window surface for mapper: %s",SDL_GetError());
+#else
+ mapper.surface=SDL_SetVideoMode_Wrap(640,480,0,0);
if (mapper.surface == NULL) E_Exit("Could not initialize video mode for mapper: %s",SDL_GetError());
+#endif
/* Set some palette entries */
- SDL_SetPalette(mapper.surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, 6);
+ mapper.draw_surface=SDL_CreateRGBSurface(0,640,480,8,0,0,0,0);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ // Needed for SDL_BlitScaled
+ mapper.draw_surface_nonpaletted=SDL_CreateRGBSurface(0,640,480,32,0x0000ff00,0x00ff0000,0xff000000,0);
+ mapper.draw_rect=GFX_GetSDLSurfaceSubwindowDims(640,480);
+ // Sorry, but SDL_SetSurfacePalette requires a full palette.
+ SDL_Palette *sdl2_map_pal_ptr = SDL_AllocPalette(256);
+ SDL_SetPaletteColors(sdl2_map_pal_ptr, map_pal, 0, CLR_LAST);
+ SDL_SetSurfacePalette(mapper.draw_surface, sdl2_map_pal_ptr);
+#else // !SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetPalette(mapper.draw_surface, SDL_LOGPAL|SDL_PHYSPAL, map_pal, 0, CLR_LAST);
+#endif
if (last_clicked) {
last_clicked->BindColor();
last_clicked=NULL;
@@ -2378,10 +2627,23 @@ void MAPPER_RunInternal() {
if (mapper.redraw) {
mapper.redraw=false;
DrawButtons();
+ } else {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UpdateWindowSurface(mapper.window);
+#else
+ SDL_Flip(mapper.surface);
+#endif
}
BIND_MappingEvents();
SDL_Delay(1);
}
+ /* ONE SHOULD NOT FORGET TO DO THIS!
+ Unless a memory leak is desired... */
+ SDL_FreeSurface(mapper.draw_surface);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_FreeSurface(mapper.draw_surface_nonpaletted);
+ SDL_FreePalette(sdl2_map_pal_ptr);
+#endif
#if defined (REDUCE_JOYSTICK_POLLING)
SDL_JoystickEventState(SDL_DISABLE);
#endif
@@ -2430,6 +2692,8 @@ void MAPPER_StartUp(Section * sec) {
mapper.sticks.num_groups=0;
memset(&virtual_joysticks,0,sizeof(virtual_joysticks));
+#if !SDL_VERSION_ATLEAST(2,0,0)
+
usescancodes = false;
if (section->Get_bool("usescancodes")) {
@@ -2554,6 +2818,8 @@ void MAPPER_StartUp(Section * sec) {
}
}
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
+
Prop_path* pp = section->Get_path("mapperfile");
mapper.filename = pp->realpath;
MAPPER_AddHandler(&MAPPER_Run,MK_f1,MMOD1,"mapper","Mapper");
diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp
index 9dde5dcb4..53d73d8de 100644
--- a/src/gui/sdlmain.cpp
+++ b/src/gui/sdlmain.cpp
@@ -31,9 +31,15 @@
#include <signal.h>
#include <process.h>
#endif
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
#include "cross.h"
#include "SDL.h"
+#if SDL_VERSION_ATLEAST(2,0,0) && C_PHYSICAL_CDROM_MOUNT
+#include "../sdl_cdrom/compat_SDL_cdrom.h"
+#endif
#include "dosbox.h"
#include "video.h"
@@ -50,11 +56,19 @@
#include "cross.h"
#include "control.h"
+#if SDL_VERSION_ATLEAST(2,0,0)
+#define MAPPERFILE "mapper-sdl2-" VERSION ".map"
+#else
#define MAPPERFILE "mapper-" VERSION ".map"
+#endif
//#define DISABLE_JOYSTICK
#if C_OPENGL
+#ifdef __ANDROID__
+#include "SDL_opengles.h"
+#else
#include "SDL_opengl.h"
+#endif
#ifndef APIENTRY
#define APIENTRY
@@ -81,12 +95,14 @@ typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access)
typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target);
#endif
+#ifndef __ANDROID__
PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL;
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL;
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL;
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL;
PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL;
PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL;
+#endif
#endif //C_OPENGL
@@ -129,8 +145,12 @@ struct private_hwdata {
enum SCREEN_TYPES {
SCREEN_SURFACE,
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SCREEN_TEXTURE,
+#else
SCREEN_SURFACE_DDRAW,
SCREEN_OVERLAY,
+#endif
SCREEN_OPENGL
};
@@ -148,10 +168,13 @@ struct SDL_Block {
bool inited;
bool active; //If this isn't set don't draw
bool updating;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ bool update_display_contents;
+ bool resizing_window;
+#endif
struct {
Bit32u width;
Bit32u height;
- Bit32u bpp;
Bitu flags;
double scalex,scaley;
GFX_CallBack_t callback;
@@ -161,45 +184,72 @@ struct SDL_Block {
struct {
Bit16u width, height;
bool fixed;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ bool display_res;
+#endif
} full;
struct {
Bit16u width, height;
} window;
Bit8u bpp;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ Bit32u sdl2pixelFormat;
+#endif
bool fullscreen;
bool lazy_fullscreen;
bool lazy_fullscreen_req;
- bool doublebuf;
+ bool vsync;
SCREEN_TYPES type;
SCREEN_TYPES want_type;
} desktop;
#if C_OPENGL
struct {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_GLContext context;
+#endif
Bitu pitch;
void * framebuf;
GLuint buffer;
GLuint texture;
+#ifdef __ANDROID__ // OpenGL ES
+ GLfloat vertCoords[8];
+ GLfloat texCoords[8];
+#else // OpenGL (not ES)
GLuint displaylist;
+#endif
GLint max_texsize;
bool bilinear;
bool packed_pixel;
bool paletted_texture;
bool pixel_buffer_object;
} opengl;
-#endif
+#endif // C_OPENGL
+#if !SDL_VERSION_ATLEAST(2,0,0)
struct {
SDL_Surface * surface;
#if C_DDRAW
RECT rect;
#endif
} blit;
+#endif // Not SDL v2.0
struct {
PRIORITY_LEVELS focus;
PRIORITY_LEVELS nofocus;
} priority;
SDL_Rect clip;
SDL_Surface * surface;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_Window * window;
+ SDL_Renderer * renderer;
+ const char * rendererDriver;
+ int displayNumber;
+ struct {
+ SDL_Texture * texture;
+ SDL_PixelFormat * pixelFormat;
+ } texture;
+#else
SDL_Overlay * overlay;
+#endif
SDL_cond *cond;
struct {
bool autolock;
@@ -207,6 +257,13 @@ struct SDL_Block {
bool requestlock;
bool locked;
Bitu sensitivity;
+#ifdef __ANDROID__
+ SDL_FingerID leftMouseFingerID, rightMouseFingerID,
+ middleMouseFingerID, mouseMotionFingerID,
+ escKeyFingerID;
+ bool isLeftMouseFingerUsed, isRightMouseFingerUsed,
+ isMiddleMouseFingerUsed, isEscKeyFingerUsed;
+#endif
} mouse;
SDL_Rect updateRects[1024];
Bitu num_joysticks;
@@ -216,12 +273,14 @@ struct SDL_Block {
Bit32u focus_ticks;
#endif
// state of alt-keys for certain special handlings
- Bit8u laltstate;
- Bit8u raltstate;
+ SDL_EventType laltstate;
+ SDL_EventType raltstate;
};
static SDL_Block sdl;
+#if !SDL_VERSION_ATLEAST(2,0,0)
+
#define SETMODE_SAVES 1 //Don't set Video Mode if nothing changes.
#define SETMODE_SAVES_CLEAR 1 //Clear the screen, when the Video Mode is reused
SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags){
@@ -279,6 +338,42 @@ SDL_Surface* SDL_SetVideoMode_Wrap(int width,int height,int bpp,Bit32u flags){
return s;
}
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+static int SDL_Init_Wrapper(void)
+{
+ // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again)
+ // Please report problems with audio and other things.
+ int result = ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO /* | SDL_INIT_TIMER */
+ |SDL_INIT_NOPARACHUTE
+ ));
+#if C_PHYSICAL_CDROM_MOUNT
+ return (result < 0) ? result : Compat_SDL_CDROMInit();
+#else
+ return result;
+#endif
+}
+#else
+static int SDL_Init_Wrapper(void)
+{
+ // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again)
+ // Please report problems with audio and other things.
+ return (SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO /* | SDL_INIT_TIMER */
+ |SDL_INIT_CDROM
+ |SDL_INIT_NOPARACHUTE
+ ));
+}
+#endif
+
+static void SDL_Quit_Wrapper(void)
+{
+#if SDL_VERSION_ATLEAST(2,0,0) && C_PHYSICAL_CDROM_MOUNT
+ Compat_SDL_CDROMQuit();
+#endif
+ SDL_Quit();
+}
+
extern const char* RunningProgram;
extern bool CPU_CycleAutoAdjust;
//Globals for keyboard initialisation
@@ -298,7 +393,11 @@ void GFX_SetTitle(Bit32s cycles,Bits frameskip,bool paused){
}
if(paused) strcat(title," PAUSED");
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetWindowTitle(sdl.window,title);
+#else
SDL_WM_SetCaption(title,VERSION);
+#endif
}
static unsigned char logo[32*32*4]= {
@@ -314,8 +413,13 @@ static void GFX_SetIcon() {
#else
SDL_Surface* logos= SDL_CreateRGBSurfaceFrom((void*)logo,32,32,32,128,0x000000ff,0x0000ff00,0x00ff0000,0);
#endif
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetWindowIcon(sdl.window, logos);
+#else
SDL_WM_SetIcon(logos,NULL);
-#endif
+#endif // SDL_VERSION_ATLEAST(2,0,0)
+#endif // !defined(MACOSX)
}
@@ -336,12 +440,21 @@ static void PauseDOSBox(bool pressed) {
while (SDL_PollEvent(&event)) {
// flush event queue.
}
-
+ /* NOTE: This is one of the few places where we use SDL key codes
+ with SDL 2.0, rather than scan codes. Is that the correct behavior? */
while (paused) {
SDL_WaitEvent(&event); // since we're not polling, cpu usage drops to 0.
switch (event.type) {
case SDL_QUIT: KillSwitch(true); break;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SDL_WINDOWEVENT:
+ if (event.window.event == SDL_WINDOWEVENT_RESTORED) {
+ // We may need to re-create a texture and more
+ GFX_ResetScreen();
+ }
+ break;
+#endif
case SDL_KEYDOWN: // Must use Pause/Break Key to resume.
case SDL_KEYUP:
if(event.key.keysym.sym == SDLK_PAUSE) {
@@ -351,7 +464,15 @@ static void PauseDOSBox(bool pressed) {
break;
}
#if defined (MACOSX)
- if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
+ if (event.key.keysym.sym == SDLK_q &&
+#if SDL_VERSION_ATLEAST(2,0,0)
+ (event.key.keysym.mod == KMOD_RGUI ||
+ event.key.keysym.mod == KMOD_LGUI)
+#else
+ (event.key.keysym.mod == KMOD_RMETA ||
+ event.key.keysym.mod == KMOD_LMETA)
+#endif
+ ) {
/* On macs, all aps exit when pressing cmd-q */
KillSwitch(true);
break;
@@ -361,19 +482,26 @@ static void PauseDOSBox(bool pressed) {
}
}
+#if !SDL_VERSION_ATLEAST(2,0,0)
#if defined (WIN32)
bool GFX_SDLUsingWinDIB(void) {
return sdl.using_windib;
}
#endif
+#endif
/* Reset the screen with current values in the sdl structure */
Bitu GFX_GetBestMode(Bitu flags) {
- Bitu testbpp,gotbpp;
+#if !SDL_VERSION_ATLEAST(2,0,0)
+ Bitu testbpp,gotbpp;
+#endif
switch (sdl.desktop.want_type) {
case SCREEN_SURFACE:
check_surface:
flags &= ~GFX_LOVE_8; //Disable love for 8bpp modes
+#if !SDL_VERSION_ATLEAST(2,0,0)
+ /* TODO: Maybe remove support for output=surface with SDL 2.0? */
+
/* Check if we can satisfy the depth it loves */
if (flags & GFX_LOVE_8) testbpp=8;
else if (flags & GFX_LOVE_15) testbpp=15;
@@ -386,7 +514,11 @@ check_gotbpp:
if (sdl.desktop.fullscreen) gotbpp=SDL_VideoModeOK(640,480,testbpp,SDL_FULLSCREEN|SDL_HWSURFACE|SDL_HWPALETTE);
else gotbpp=sdl.desktop.bpp;
/* If we can't get our favorite mode check for another working one */
- switch (gotbpp) {
+ switch (gotbpp)
+#else // SDL_VERSION_ATLEAST(2,0,0)
+ switch (sdl.desktop.bpp)
+#endif
+ {
case 8:
if (flags & GFX_CAN_8) flags&=~(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32);
break;
@@ -403,6 +535,7 @@ check_gotbpp:
}
flags |= GFX_CAN_RANDOM;
break;
+#if !SDL_VERSION_ATLEAST(2,0,0)
#if C_DDRAW
case SCREEN_SURFACE_DDRAW:
if (!(flags&(GFX_CAN_15|GFX_CAN_16|GFX_CAN_32))) goto check_surface;
@@ -420,8 +553,14 @@ check_gotbpp:
flags|=GFX_SCALING;
flags&=~(GFX_CAN_8|GFX_CAN_15|GFX_CAN_16);
break;
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
+#if C_OPENGL || SDL_VERSION_ATLEAST(2,0,0)
#if C_OPENGL
case SCREEN_OPENGL:
+#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SCREEN_TEXTURE:
+#endif
//We only accept 32bit output from the scalers here
if (!(flags&GFX_CAN_32)) goto check_surface;
flags|=GFX_SCALING;
@@ -461,19 +600,160 @@ static int int_log2 (int val) {
return log;
}
+#if SDL_VERSION_ATLEAST(2,0,0)
+
+static SDL_Window * GFX_SetSDLWindowMode(Bit16u width, Bit16u height, bool fullscreen, SCREEN_TYPES screenType) {
+ static SCREEN_TYPES lastType = SCREEN_SURFACE;
+ if (sdl.renderer) {
+ SDL_DestroyRenderer(sdl.renderer);
+ sdl.renderer=0;
+ }
+ if (sdl.texture.pixelFormat) {
+ SDL_FreeFormat(sdl.texture.pixelFormat);
+ sdl.texture.pixelFormat = 0;
+ }
+ if (sdl.texture.texture) {
+ SDL_DestroyTexture(sdl.texture.texture);
+ sdl.texture.texture=0;
+ }
+#if C_OPENGL
+ if (sdl.opengl.context) {
+ SDL_GL_DeleteContext(sdl.opengl.context);
+ sdl.opengl.context=0;
+ }
+#endif
+ int currWidth, currHeight;
+ if (sdl.window && sdl.resizing_window) {
+ SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
+ sdl.update_display_contents = ((width <= currWidth) && (height <= currHeight));
+
+ return sdl.window;
+ }
+ /* If we change screen type, recreate the window. Furthermore, if
+ * it is our very first time then we simply create a new window.
+ */
+ if (!sdl.window || (lastType != screenType)) {
+ lastType = screenType;
+ if (sdl.window) {
+ SDL_DestroyWindow(sdl.window);
+ }
+ /* Don't create a fullscreen immediately. Reasons:
+ * 1. This theoretically allows us to set window resolution and
+ * then let SDL2 remember it for later (even if not actually done).
+ * 2. It's a bit less glitchy to set a custom display mode for a
+ * full screen, albeit it's still not perfect (at least on X11).
+ */
+ sdl.window = SDL_CreateWindow("",
+ SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber),
+ SDL_WINDOWPOS_UNDEFINED_DISPLAY(sdl.displayNumber),
+ width, height,
+ ((screenType == SCREEN_OPENGL) ? SDL_WINDOW_OPENGL : 0) | SDL_WINDOW_SHOWN
+ );
+ if (!sdl.window) {
+ return sdl.window;
+ }
+ GFX_SetTitle(-1,-1,false); //refresh title.
+ if (!fullscreen) {
+ goto finish;
+ }
+ }
+ /* Fullscreen mode switching has its limits, and is also problematic on
+ * some window managers. For now, the following may work up to some
+ * level. On X11, SDL_VIDEO_X11_LEGACY_FULLSCREEN=1 can also help,
+ * although it has its own issues.
+ * Suggestion: Use the desktop res if possible, with output=surface
+ * if one is not interested in scaling.
+ * On Android, desktop res is the only way.
+ */
+ if (fullscreen) {
+ SDL_DisplayMode displayMode;
+ SDL_GetWindowDisplayMode(sdl.window, &displayMode);
+ displayMode.w = width;
+ displayMode.h = height;
+ SDL_SetWindowDisplayMode(sdl.window, &displayMode);
+
+ SDL_SetWindowFullscreen(sdl.window, sdl.desktop.full.display_res ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN);
+ } else {
+ SDL_SetWindowFullscreen(sdl.window, 0);
+
+ SDL_SetWindowSize(sdl.window, width, height);
+ }
+ // Maybe some requested fullscreen resolution is unsupported?
+finish:
+ SDL_GetWindowSize(sdl.window, &currWidth, &currHeight);
+ sdl.update_display_contents = ((width <= currWidth) && (height <= currHeight));
+ return sdl.window;
+}
+
+// Used for the mapper UI and more: Creates a fullscreen window with desktop res
+// on Android, and a non-fullscreen window with the input dimensions otherwise.
+SDL_Window * GFX_SetSDLSurfaceWindow(Bit16u width, Bit16u height) {
+#ifdef __ANDROID__
+ return GFX_SetSDLWindowMode(sdl.desktop.full.width, sdl.desktop.full.height, true, SCREEN_SURFACE);
+#else
+ return GFX_SetSDLWindowMode(width, height, false, SCREEN_SURFACE);
+#endif
+}
+
+// Returns the rectangle in the current window to be used for scaling a
+// sub-window with the given dimensions, like the mapper UI.
+SDL_Rect GFX_GetSDLSurfaceSubwindowDims(Bit16u width, Bit16u height) {
+ SDL_Rect rect;
+#ifdef __ANDROID__
+ // Wider than width:height
+ if (height*sdl.desktop.full.width > sdl.desktop.full.height*width) {
+ rect.w=sdl.desktop.full.height*width/height;
+ rect.h=sdl.desktop.full.height;
+ rect.x=(sdl.desktop.full.width-rect.w)/2;
+ rect.y=0;
+ } else { // NOT wider than width:height
+ rect.w=sdl.desktop.full.width;
+ rect.h=sdl.desktop.full.width*height/width;
+ rect.x=0;
+ rect.y=(sdl.desktop.full.height-rect.h)/2;
+ }
+#else
+ rect.x=rect.y=0;
+ rect.w=width;
+ rect.h=height;
+#endif
+ return rect;
+}
-static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) {
+// Currently used for an initial test here
+static SDL_Window * GFX_SetSDLOpenGLWindow(Bit16u width, Bit16u height) {
+#ifdef __ANDROID__
+ return GFX_SetSDLWindowMode(sdl.desktop.full.width, sdl.desktop.full.height, true, SCREEN_OPENGL);
+#else
+ return GFX_SetSDLWindowMode(width, height, false, SCREEN_OPENGL);
+#endif
+}
+
+#endif // SDL_VERSION_ATLEAST(2,0,0)
+
+// Different functions, similar function bodies (SDL 1.2 vs 2.0)
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+static SDL_Window * GFX_SetupWindowScaled(SCREEN_TYPES screenType)
+#else
+static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp)
+#endif
+{
Bit16u fixedWidth;
Bit16u fixedHeight;
if (sdl.desktop.fullscreen) {
fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0;
fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0;
+#if !SDL_VERSION_ATLEAST(2,0,0)
sdl_flags |= SDL_FULLSCREEN|SDL_HWSURFACE;
+#endif
} else {
fixedWidth = sdl.desktop.window.width;
fixedHeight = sdl.desktop.window.height;
+#if !SDL_VERSION_ATLEAST(2,0,0)
sdl_flags |= SDL_HWSURFACE;
+#endif
}
if (fixedWidth && fixedHeight) {
double ratio_w=(double)fixedWidth/(sdl.draw.width*sdl.draw.scalex);
@@ -489,27 +769,59 @@ static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) {
sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex*ratio_h + 0.4);
sdl.clip.h=(Bit16u)fixedHeight;
}
- if (sdl.desktop.fullscreen)
+ if (sdl.desktop.fullscreen) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.window = GFX_SetSDLWindowMode(fixedWidth, fixedHeight, sdl.desktop.fullscreen, screenType);
+#else
sdl.surface = SDL_SetVideoMode_Wrap(fixedWidth,fixedHeight,bpp,sdl_flags);
- else
+#endif
+ } else {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.window = GFX_SetSDLWindowMode(sdl.clip.w, sdl.clip.h, sdl.desktop.fullscreen, screenType);
+#else
sdl.surface = SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags);
+#endif
+ }
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (sdl.window && SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN) {
+ int windowWidth;
+ SDL_GetWindowSize(sdl.window, &windowWidth, NULL);
+ sdl.clip.x=(Sint16)((windowWidth-sdl.clip.w)/2);
+#else
if (sdl.surface && sdl.surface->flags & SDL_FULLSCREEN) {
sdl.clip.x=(Sint16)((sdl.surface->w-sdl.clip.w)/2);
- sdl.clip.y=(Sint16)((sdl.surface->h-sdl.clip.h)/2);
+#endif
+#ifdef __ANDROID__
+ /* Portrait orientation and on-screen keyboards
+ are commonly found on that platform */
+ sdl.clip.y=0;
+#else
+ sdl.clip.y=(Sint16)((fixedHeight-sdl.clip.h)/2);
+#endif
} else {
sdl.clip.x = 0;
sdl.clip.y = 0;
}
+#if SDL_VERSION_ATLEAST(2,0,0)
+ return sdl.window;
+#else
return sdl.surface;
+#endif
} else {
sdl.clip.x=0;sdl.clip.y=0;
sdl.clip.w=(Bit16u)(sdl.draw.width*sdl.draw.scalex);
sdl.clip.h=(Bit16u)(sdl.draw.height*sdl.draw.scaley);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.window = GFX_SetSDLWindowMode(sdl.clip.w, sdl.clip.h, sdl.desktop.fullscreen, screenType);
+ return sdl.window;
+#else
sdl.surface=SDL_SetVideoMode_Wrap(sdl.clip.w,sdl.clip.h,bpp,sdl_flags);
return sdl.surface;
+#endif
}
}
+#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Do we need this? Never used and can't be used as-is with SDL 2.0
void GFX_TearDown(void) {
if (sdl.updating)
GFX_EndUpdate( 0 );
@@ -519,6 +831,7 @@ void GFX_TearDown(void) {
sdl.blit.surface=0;
}
}
+#endif
Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) {
if (sdl.updating)
@@ -530,41 +843,77 @@ Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,G
sdl.draw.scalex=scalex;
sdl.draw.scaley=scaley;
- int bpp=0;
Bitu retFlags = 0;
+#if !SDL_VERSION_ATLEAST(2,0,0)
+ int bpp=0;
if (sdl.blit.surface) {
SDL_FreeSurface(sdl.blit.surface);
sdl.blit.surface=0;
}
+#endif
switch (sdl.desktop.want_type) {
case SCREEN_SURFACE:
dosurface:
+#if !SDL_VERSION_ATLEAST(2,0,0)
if (flags & GFX_CAN_8) bpp=8;
if (flags & GFX_CAN_15) bpp=15;
if (flags & GFX_CAN_16) bpp=16;
if (flags & GFX_CAN_32) bpp=32;
+#endif
sdl.desktop.type=SCREEN_SURFACE;
sdl.clip.w=width;
sdl.clip.h=height;
if (sdl.desktop.fullscreen) {
if (sdl.desktop.full.fixed) {
sdl.clip.x=(Sint16)((sdl.desktop.full.width-width)/2);
+#ifdef __ANDROID__
+ /* Portrait orientation and on-screen keyboards
+ are commonly found on that platform */
+ sdl.clip.y=0;
+#else
sdl.clip.y=(Sint16)((sdl.desktop.full.height-height)/2);
+#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.window = GFX_SetSDLWindowMode(sdl.desktop.full.width,
+ sdl.desktop.full.height,
+ sdl.desktop.fullscreen, sdl.desktop.type);
+ if (sdl.window == NULL)
+ E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,sdl.desktop.bpp,SDL_GetError());
+ /* This may be required after an ALT-TAB leading to a window
+ minimize, which further effectively shrinks its size */
+ if ((sdl.clip.x < 0) || (sdl.clip.y < 0)) {
+ sdl.update_display_contents = false;
+ }
+#else
sdl.surface=SDL_SetVideoMode_Wrap(sdl.desktop.full.width,sdl.desktop.full.height,bpp,
SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
- (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE);
+ (sdl.desktop.vsync ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0) | SDL_HWPALETTE);
if (sdl.surface == NULL) E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",sdl.desktop.full.width,sdl.desktop.full.height,bpp,SDL_GetError());
+#endif
} else {
sdl.clip.x=0;sdl.clip.y=0;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.window = GFX_SetSDLWindowMode(width, height,
+ sdl.desktop.fullscreen, sdl.desktop.type);
+ if (sdl.window == NULL)
+ E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",(int)width,(int)height,sdl.desktop.bpp,SDL_GetError());
+#else
sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp,
SDL_FULLSCREEN | ((flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE) |
- (sdl.desktop.doublebuf ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE);
+ (sdl.desktop.vsync ? SDL_DOUBLEBUF|SDL_ASYNCBLIT : 0)|SDL_HWPALETTE);
if (sdl.surface == NULL)
E_Exit("Could not set fullscreen video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError());
+#endif
}
} else {
sdl.clip.x=0;sdl.clip.y=0;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.window = GFX_SetSDLWindowMode(width, height,
+ sdl.desktop.fullscreen, sdl.desktop.type);
+ if (sdl.window == NULL)
+ E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,sdl.desktop.bpp,SDL_GetError());
+#else
sdl.surface=SDL_SetVideoMode_Wrap(width,height,bpp,(flags & GFX_CAN_RANDOM) ? SDL_SWSURFACE : SDL_HWSURFACE);
#ifdef WIN32
if (sdl.surface == NULL) {
@@ -586,42 +935,116 @@ dosurface:
#endif
if (sdl.surface == NULL)
E_Exit("Could not set windowed video mode %ix%i-%i: %s",(int)width,(int)height,bpp,SDL_GetError());
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
}
- if (sdl.surface) {
- switch (sdl.surface->format->BitsPerPixel) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.surface = SDL_GetWindowSurface(sdl.window);
+ if (sdl.surface == NULL)
+ E_Exit("Could not retrieve window surface: %s",SDL_GetError());
+#endif
+ switch (sdl.surface->format->BitsPerPixel) {
case 8:
retFlags = GFX_CAN_8;
- break;
+ break;
case 15:
retFlags = GFX_CAN_15;
break;
case 16:
retFlags = GFX_CAN_16;
- break;
+ break;
case 32:
retFlags = GFX_CAN_32;
- break;
- }
- if (retFlags && (sdl.surface->flags & SDL_HWSURFACE))
- retFlags |= GFX_HARDWARE;
- if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) {
- sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE,
- sdl.draw.width, sdl.draw.height,
- sdl.surface->format->BitsPerPixel,
- sdl.surface->format->Rmask,
- sdl.surface->format->Gmask,
- sdl.surface->format->Bmask,
- 0);
- /* If this one fails be ready for some flickering... */
- }
+ break;
+ }
+#if SDL_VERSION_ATLEAST(2,0,0)
+ /* Fix a glitch with aspect=true occuring when
+ changing between modes with different dimensions */
+ SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+ SDL_UpdateWindowSurface(sdl.window);
+#else
+ if (retFlags && (sdl.surface->flags & SDL_HWSURFACE))
+ retFlags |= GFX_HARDWARE;
+ if (retFlags && (sdl.surface->flags & SDL_DOUBLEBUF)) {
+ sdl.blit.surface=SDL_CreateRGBSurface(SDL_HWSURFACE,
+ sdl.draw.width, sdl.draw.height,
+ sdl.surface->format->BitsPerPixel,
+ sdl.surface->format->Rmask,
+ sdl.surface->format->Gmask,
+ sdl.surface->format->Bmask,
+ 0);
+ /* If this one fails be ready for some flickering... */
+ }
+#endif
+ break;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SCREEN_TEXTURE:
+ {
+ if (!GFX_SetupWindowScaled(sdl.desktop.want_type)) {
+ LOG_MSG("SDL:Can't set video mode, falling back to surface");
+ goto dosurface;
+ }
+ if (strcmp(sdl.rendererDriver, "auto"))
+ SDL_SetHint(SDL_HINT_RENDER_DRIVER, sdl.rendererDriver);
+ sdl.renderer = SDL_CreateRenderer(sdl.window, -1,
+ SDL_RENDERER_ACCELERATED |
+ (sdl.desktop.vsync ? SDL_RENDERER_PRESENTVSYNC : 0));
+ if (!sdl.renderer) {
+ LOG_MSG("%s\n", SDL_GetError());
+ LOG_MSG("SDL:Can't create renderer, falling back to surface");
+ goto dosurface;
+ }
+ /* SDL_PIXELFORMAT_ARGB8888 is possible with most
+ rendering drivers, "opengles" being a notable exception */
+ sdl.texture.texture = SDL_CreateTexture(sdl.renderer, SDL_PIXELFORMAT_ARGB8888,
+ SDL_TEXTUREACCESS_STREAMING, width, height);
+ /* SDL_PIXELFORMAT_ABGR8888 (not RGB) is the
+ only supported format for the "opengles" driver */
+ if (!sdl.texture.texture) {
+ if (flags & GFX_RGBONLY) goto dosurface;
+ sdl.texture.texture = SDL_CreateTexture(sdl.renderer, SDL_PIXELFORMAT_ABGR8888,
+ SDL_TEXTUREACCESS_STREAMING, width, height);
+ }
+ if (!sdl.texture.texture) {
+ SDL_DestroyRenderer(sdl.renderer);
+ sdl.renderer = NULL;
+ LOG_MSG("SDL:Can't create texture, falling back to surface");
+ goto dosurface;
+ }
+ SDL_SetRenderDrawColor(sdl.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
+ sdl.desktop.type=SCREEN_TEXTURE;
+ Uint32 pixelFormat;
+ SDL_QueryTexture(sdl.texture.texture, &pixelFormat, NULL, NULL, NULL);
+ sdl.texture.pixelFormat = SDL_AllocFormat(pixelFormat);
+ switch (SDL_BITSPERPIXEL(pixelFormat)) {
+ case 8:
+ retFlags = GFX_CAN_8;
+ break;
+ case 15:
+ retFlags = GFX_CAN_15;
+ break;
+ case 16:
+ retFlags = GFX_CAN_16;
+ break;
+ case 24: /* SDL_BYTESPERPIXEL is probably 4, though. */
+ case 32:
+ retFlags = GFX_CAN_32;
+ break;
}
+ retFlags |= GFX_SCALING;
+ SDL_RendererInfo rendererInfo;
+ SDL_GetRendererInfo(sdl.renderer, &rendererInfo);
+ LOG_MSG("Using driver \"%s\" for renderer", rendererInfo.name);
+ if (rendererInfo.flags & SDL_RENDERER_ACCELERATED)
+ retFlags |= GFX_HARDWARE;
break;
+ }
+#else // !SDL_VERSION_ATLEAST(2,0,0)
#if C_DDRAW
case SCREEN_SURFACE_DDRAW:
if (flags & GFX_CAN_15) bpp=15;
if (flags & GFX_CAN_16) bpp=16;
if (flags & GFX_CAN_32) bpp=32;
- if (!GFX_SetupSurfaceScaled((sdl.desktop.doublebuf && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface;
+ if (!GFX_SetupSurfaceScaled((sdl.desktop.vsync && sdl.desktop.fullscreen) ? SDL_DOUBLEBUF : 0,bpp)) goto dosurface;
sdl.blit.rect.top=sdl.clip.y;
sdl.blit.rect.left=sdl.clip.x;
sdl.blit.rect.right=sdl.clip.x+sdl.clip.w;
@@ -669,13 +1092,17 @@ dosurface:
sdl.desktop.type=SCREEN_OVERLAY;
retFlags = GFX_CAN_32 | GFX_SCALING | GFX_HARDWARE;
break;
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
#if C_OPENGL
case SCREEN_OPENGL:
{
+#ifndef __ANDROID__
if (sdl.opengl.pixel_buffer_object) {
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
if (sdl.opengl.buffer) glDeleteBuffersARB(1, &sdl.opengl.buffer);
- } else if (sdl.opengl.framebuf) {
+ } else
+#endif
+ if (sdl.opengl.framebuf) {
free(sdl.opengl.framebuf);
}
sdl.opengl.framebuf=0;
@@ -686,30 +1113,67 @@ dosurface:
goto dosurface;
}
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
+#ifdef __ANDROID__
+ /* WARNING: OpenGL ES v2.0 is NOT backwards compatible
+ * with v1.1! For simplicity we force v1.1 for now,
+ * although v2.0 is probably the better way.
+ */
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+ GFX_SetupWindowScaled(sdl.desktop.want_type);
+ /* We may simply use SDL_BYTESPERPIXEL
+ here rather than SDL_BITSPERPIXEL */
+ if (!sdl.window || SDL_BYTESPERPIXEL(SDL_GetWindowPixelFormat(sdl.window))<2) {
+ LOG_MSG("SDL:OPENGL:Can't open drawing window, are you running in 16bpp(or higher) mode?");
+ goto dosurface;
+ }
+ sdl.opengl.context = SDL_GL_CreateContext(sdl.window);
+ if (sdl.opengl.context == NULL) {
+ LOG_MSG("SDL:OPENGL:Can't create OpenGL context, falling back to surface");
+ goto dosurface;
+ }
+ /* Sync to VBlank if desired */
+ SDL_GL_SetSwapInterval(sdl.desktop.vsync ? 1 : 0);
+#else // !SDL_VERSION_ATLEAST(2,0,0)
#if SDL_VERSION_ATLEAST(1, 2, 11)
- SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 0 );
+ SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, sdl.desktop.vsync ? 1 : 0 );
#endif
GFX_SetupSurfaceScaled(SDL_OPENGL,0);
if (!sdl.surface || sdl.surface->format->BitsPerPixel<15) {
LOG_MSG("SDL:OPENGL: Can't open drawing surface, are you running in 16bpp (or higher) mode?");
goto dosurface;
}
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
/* Create the texture and display list */
+#ifndef __ANDROID__
if (sdl.opengl.pixel_buffer_object) {
glGenBuffersARB(1, &sdl.opengl.buffer);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer);
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_EXT, width*height*4, NULL, GL_STREAM_DRAW_ARB);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
- } else {
+ } else
+#endif
+ {
sdl.opengl.framebuf=malloc(width*height*4); //32 bit color
}
sdl.opengl.pitch=width*4;
- if(sdl.clip.x ==0 && sdl.clip.y ==0 && sdl.desktop.fullscreen && !sdl.desktop.full.fixed && (sdl.clip.w != sdl.surface->w || sdl.clip.h != sdl.surface->h)) {
-// LOG_MSG("attempting to fix the centering to %d %d %d %d",(sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h);
- glViewport((sdl.surface->w-sdl.clip.w)/2,(sdl.surface->h-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ int windowWidth, windowHeight;
+ SDL_GetWindowSize(sdl.window, &windowWidth, &windowHeight);
+#else
+ int windowWidth = sdl.surface->w, windowHeight = sdl.surface->h;
+#endif
+ if(sdl.clip.x ==0 && sdl.clip.y ==0 && sdl.desktop.fullscreen && !sdl.desktop.full.fixed && (sdl.clip.w != windowWidth || sdl.clip.h != windowHeight)) {
+// LOG_MSG("attempting to fix the centering to %d %d %d %d",(windowWidth-sdl.clip.w)/2,(windowHeight-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h);
+ glViewport((windowWidth-sdl.clip.w)/2,(windowHeight-sdl.clip.h)/2,sdl.clip.w,sdl.clip.h);
} else {
- glViewport(sdl.clip.x,sdl.clip.y,sdl.clip.w,sdl.clip.h);
+ /* We don't just pass sdl.clip.y as-is, so we cover the case of non-vertical
+ * centering on Android (in order to leave room for the on-screen keyboard)
+ */
+ glViewport(sdl.clip.x,windowHeight-(sdl.clip.y+sdl.clip.h),sdl.clip.w,sdl.clip.h);
}
glMatrixMode (GL_PROJECTION);
@@ -717,8 +1181,15 @@ dosurface:
glGenTextures(1,&sdl.opengl.texture);
glBindTexture(GL_TEXTURE_2D,sdl.opengl.texture);
// No borders
+#ifdef __ANDROID__
+ /* Plain OpenGL ES (v1.1) has no mention
+ of GL_CLAMP, so use GL_CLAMP_TO_EDGE */
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+#endif
if (!sdl.opengl.bilinear || ( (sdl.clip.h % height) == 0 && (sdl.clip.w % width) == 0) ) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -727,12 +1198,13 @@ dosurface:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
+#ifdef __ANDROID__ // OpenGL ES
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texsize, texsize, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texsize, texsize, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, 0);
+#endif
glClearColor (0.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
- SDL_GL_SwapBuffers();
- glClear(GL_COLOR_BUFFER_BIT);
glShadeModel (GL_FLAT);
glDisable (GL_DEPTH_TEST);
glDisable (GL_LIGHTING);
@@ -744,9 +1216,18 @@ dosurface:
GLfloat tex_width=((GLfloat)(width)/(GLfloat)texsize);
GLfloat tex_height=((GLfloat)(height)/(GLfloat)texsize);
+#ifdef __ANDROID__
+ /* Display lists are not available with OpenGL ES (1.0, 2.0).
+ Furthermore, we can't use glBegin, glTexCoord2f and glEnd. */
+ sdl.opengl.texCoords[0] = 0; sdl.opengl.texCoords[1] = tex_height; // lower left
+ sdl.opengl.texCoords[2] = tex_width; sdl.opengl.texCoords[3] = tex_height; // lower right
+ sdl.opengl.texCoords[4] = tex_width; sdl.opengl.texCoords[5] = 0; // upper right
+ sdl.opengl.texCoords[6] = 0; sdl.opengl.texCoords[7] = 0; // upper left
+#else
if (glIsList(sdl.opengl.displaylist)) glDeleteLists(sdl.opengl.displaylist, 1);
sdl.opengl.displaylist = glGenLists(1);
glNewList(sdl.opengl.displaylist, GL_COMPILE);
+ glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
glBegin(GL_QUADS);
// lower left
@@ -759,10 +1240,13 @@ dosurface:
glTexCoord2f(0,0); glVertex2f(-1.0f, 1.0f);
glEnd();
glEndList();
+#endif
sdl.desktop.type=SCREEN_OPENGL;
retFlags = GFX_CAN_32 | GFX_SCALING;
+#ifndef __ANDROID__
if (sdl.opengl.pixel_buffer_object)
retFlags |= GFX_HARDWARE;
+#endif
break;
}//OPENGL
#endif //C_OPENGL
@@ -776,18 +1260,28 @@ dosurface:
return retFlags;
}
+
void GFX_CaptureMouse(void) {
sdl.mouse.locked=!sdl.mouse.locked;
if (sdl.mouse.locked) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetRelativeMouseMode(SDL_TRUE);
+#else
SDL_WM_GrabInput(SDL_GRAB_ON);
+#endif
SDL_ShowCursor(SDL_DISABLE);
} else {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetRelativeMouseMode(SDL_FALSE);
+#else
SDL_WM_GrabInput(SDL_GRAB_OFF);
+#endif
if (sdl.mouse.autoenable || !sdl.mouse.autolock) SDL_ShowCursor(SDL_ENABLE);
}
mouselocked=sdl.mouse.locked;
}
+#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Do we need this?
void GFX_UpdateSDLCaptureState(void) {
if (sdl.mouse.locked) {
SDL_WM_GrabInput(SDL_GRAB_ON);
@@ -799,6 +1293,7 @@ void GFX_UpdateSDLCaptureState(void) {
CPU_Reset_AutoAdjust();
GFX_SetTitle(-1,-1,false);
}
+#endif // SDL 1.2
bool mouselocked; //Global variable for mapper
static void CaptureMouse(bool pressed) {
@@ -871,25 +1366,36 @@ bool GFX_LazyFullscreenRequested(void) {
return false;
}
+#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Do we need this?
void GFX_RestoreMode(void) {
GFX_SetSize(sdl.draw.width,sdl.draw.height,sdl.draw.flags,sdl.draw.scalex,sdl.draw.scaley,sdl.draw.callback);
GFX_UpdateSDLCaptureState();
}
+#endif
bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (!sdl.update_display_contents)
+ return false;
+#endif
if (!sdl.active || sdl.updating)
return false;
switch (sdl.desktop.type) {
case SCREEN_SURFACE:
+#if !SDL_VERSION_ATLEAST(2,0,0)
if (sdl.blit.surface) {
if (SDL_MUSTLOCK(sdl.blit.surface) && SDL_LockSurface(sdl.blit.surface))
return false;
pixels=(Bit8u *)sdl.blit.surface->pixels;
pitch=sdl.blit.surface->pitch;
- } else {
+ } else
+#endif
+ {
+#if !SDL_VERSION_ATLEAST(2,0,0)
if (SDL_MUSTLOCK(sdl.surface) && SDL_LockSurface(sdl.surface))
return false;
+#endif
pixels=(Bit8u *)sdl.surface->pixels;
pixels+=sdl.clip.y*sdl.surface->pitch;
pixels+=sdl.clip.x*sdl.surface->format->BytesPerPixel;
@@ -897,6 +1403,19 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) {
}
sdl.updating=true;
return true;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SCREEN_TEXTURE:
+ {
+ void * texPixels;
+ int texPitch;
+ if (SDL_LockTexture(sdl.texture.texture, NULL, &texPixels, &texPitch) < 0)
+ return false;
+ pixels = (Bit8u *)texPixels;
+ pitch = texPitch;
+ sdl.updating=true;
+ return true;
+ }
+#else // !SDL_VERSION_ATLEAST(2,0,0)
#if C_DDRAW
case SCREEN_SURFACE_DDRAW:
if (SDL_LockSurface(sdl.blit.surface)) {
@@ -907,20 +1426,25 @@ bool GFX_StartUpdate(Bit8u * & pixels,Bitu & pitch) {
pitch=sdl.blit.surface->pitch;
sdl.updating=true;
return true;
-#endif
+#endif // DirectDraw
case SCREEN_OVERLAY:
if (SDL_LockYUVOverlay(sdl.overlay)) return false;
pixels=(Bit8u *)*(sdl.overlay->pixels);
pitch=*(sdl.overlay->pitches);
sdl.updating=true;
return true;
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
#if C_OPENGL
case SCREEN_OPENGL:
+#ifndef __ANDROID__
if(sdl.opengl.pixel_buffer_object) {
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, sdl.opengl.buffer);
pixels=(Bit8u *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY);
} else
+#endif
+ {
pixels=(Bit8u *)sdl.opengl.framebuf;
+ }
pitch=sdl.opengl.pitch;
sdl.updating=true;
return true;
@@ -936,11 +1460,16 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
#if C_DDRAW
int ret;
#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (!sdl.update_display_contents)
+ return;
+#endif
if (!sdl.updating)
return;
sdl.updating=false;
switch (sdl.desktop.type) {
case SCREEN_SURFACE:
+#if !SDL_VERSION_ATLEAST(2,0,0)
if (SDL_MUSTLOCK(sdl.surface)) {
if (sdl.blit.surface) {
SDL_UnlockSurface(sdl.blit.surface);
@@ -950,7 +1479,9 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
SDL_UnlockSurface(sdl.surface);
}
SDL_Flip(sdl.surface);
- } else if (changedLines) {
+ } else
+#endif
+ if (changedLines) {
Bitu y = 0, index = 0, rectCount = 0;
while (y < sdl.draw.height) {
if (!(index & 1)) {
@@ -971,9 +1502,21 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
index++;
}
if (rectCount)
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UpdateWindowSurfaceRects( sdl.window, sdl.updateRects, rectCount );
+#else
SDL_UpdateRects( sdl.surface, rectCount, sdl.updateRects );
+#endif
}
break;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SCREEN_TEXTURE:
+ SDL_UnlockTexture(sdl.texture.texture);
+ SDL_RenderClear(sdl.renderer);
+ SDL_RenderCopy(sdl.renderer, sdl.texture.texture, NULL, &sdl.clip);
+ SDL_RenderPresent(sdl.renderer);
+ break;
+#else // !SDL_VERSION_ATLEAST(2,0,0)
#if C_DDRAW
case SCREEN_SURFACE_DDRAW:
SDL_UnlockSurface(sdl.blit.surface);
@@ -993,13 +1536,15 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
}
SDL_Flip(sdl.surface);
break;
-#endif
+#endif // DirectDraw
case SCREEN_OVERLAY:
SDL_UnlockYUVOverlay(sdl.overlay);
SDL_DisplayYUVOverlay(sdl.overlay,&sdl.clip);
break;
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
#if C_OPENGL
case SCREEN_OPENGL:
+#ifndef __ANDROID__
if (sdl.opengl.pixel_buffer_object) {
glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT);
glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
@@ -1008,8 +1553,14 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
GL_UNSIGNED_INT_8_8_8_8_REV, 0);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_EXT, 0);
glCallList(sdl.opengl.displaylist);
- SDL_GL_SwapBuffers();
- } else if (changedLines) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_GL_SwapWindow(sdl.window);
+#else
+ SDL_GL_SwapBuffers();
+#endif
+ } else
+#endif // ifndef __ANDROID__
+ if (changedLines) {
Bitu y = 0, index = 0;
glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
while (y < sdl.draw.height) {
@@ -1018,15 +1569,43 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
} else {
Bit8u *pixels = (Bit8u *)sdl.opengl.framebuf + y * sdl.opengl.pitch;
Bitu height = changedLines[index];
+#ifdef __ANDROID__
+ /* Try GL_UNSIGNED_BYTE... */
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y,
+ sdl.draw.width, height, GL_RGBA,
+ GL_UNSIGNED_BYTE, pixels );
+#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y,
sdl.draw.width, height, GL_BGRA_EXT,
GL_UNSIGNED_INT_8_8_8_8_REV, pixels );
+#endif
y += height;
}
index++;
}
+#ifdef __ANDROID__
+ /* We can't use display lists with OpenGL ES
+ and we should use Vertex Buffer Arrays */
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBindTexture(GL_TEXTURE_2D, sdl.opengl.texture);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ glVertexPointer(2, GL_FLOAT, 0, sdl.opengl.vertCoords);
+ glTexCoordPointer(2, GL_FLOAT, 0, sdl.opengl.texCoords);
+ glDrawArrays(GL_TRIANGLE_FAN,0,4);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+#else // !__ANDROID__
glCallList(sdl.opengl.displaylist);
+#endif // !__ANDROID__
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_GL_SwapWindow(sdl.window);
+#else
SDL_GL_SwapBuffers();
+#endif
}
break;
#endif
@@ -1037,6 +1616,7 @@ void GFX_EndUpdate( const Bit16u *changedLines ) {
void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) {
+#if !SDL_VERSION_ATLEAST(2,0,0) // NOTE: Better remove this if SDL 1.2 isn't supported?
/* I should probably not change the GFX_PalEntry :) */
if (sdl.surface->flags & SDL_HWPALETTE) {
if (!SDL_SetPalette(sdl.surface,SDL_PHYSPAL,(SDL_Color *)entries,start,count)) {
@@ -1047,13 +1627,20 @@ void GFX_SetPalette(Bitu start,Bitu count,GFX_PalEntry * entries) {
E_Exit("SDL:Can't set palette");
}
}
+#endif
}
Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) {
switch (sdl.desktop.type) {
case SCREEN_SURFACE:
+#if !SDL_VERSION_ATLEAST(2,0,0)
case SCREEN_SURFACE_DDRAW:
+#endif
return SDL_MapRGB(sdl.surface->format,red,green,blue);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SCREEN_TEXTURE:
+ return SDL_MapRGB(sdl.texture.pixelFormat,red,green,blue);
+#else
case SCREEN_OVERLAY:
{
Bit8u y = ( 9797*(red) + 19237*(green) + 3734*(blue) ) >> 15;
@@ -1065,10 +1652,15 @@ Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue) {
return (u << 0) | (y << 8) | (v << 16) | (y << 24);
#endif
}
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
case SCREEN_OPENGL:
-// return ((red << 0) | (green << 8) | (blue << 16)) | (255 << 24);
- //USE BGRA
+#ifdef __ANDROID__
+ //Use RGBA on Android with OpenGL ES v1.1
+ return ((red << 0) | (green << 8) | (blue << 16)) | (255 << 24);
+#else
+ //USE BGRA otherwise
return ((blue << 0) | (green << 8) | (red << 16)) | (255 << 24);
+#endif
}
return 0;
}
@@ -1083,6 +1675,36 @@ void GFX_Start() {
sdl.active=true;
}
+/* NOTE: The following appears to do its job on Android only *before*
+ * a screen rotation occurs. After that, the old dimensions are retrieved.
+ * For the updated dimensions we should listen to a window resize event.
+ */
+#if SDL_VERSION_ATLEAST(2,0,0)
+void GFX_ObtainDisplayDimensions() {
+ SDL_Rect displayDimensions;
+ SDL_GetDisplayBounds(sdl.displayNumber, &displayDimensions);
+ sdl.desktop.full.width = displayDimensions.w;
+ sdl.desktop.full.height = displayDimensions.h;
+
+}
+
+/* Manually update display dimensions in case of a window resize,
+ * IF there is the need for that ("yes" on Android, "no" otherwise).
+ * Used for the mapper UI on Android.
+ * Reason is the usage of GFX_GetSDLSurfaceSubwindowDims, as well as a
+ * mere notification of the fact that the window's dimensions are modified.
+ */
+void GFX_UpdateDisplayDimensions(int width, int height) {
+ if (sdl.desktop.full.display_res && sdl.desktop.fullscreen) {
+ /* Note: We should not use GFX_ObtainDisplayDimensions
+ (SDL_GetDisplayBounds) on Android after a screen rotation:
+ The older values from application startup are returned. */
+ sdl.desktop.full.width = width;
+ sdl.desktop.full.height = height;
+ }
+}
+#endif
+
static void GUI_ShutDown(Section * /*sec*/) {
GFX_Stop();
if (sdl.draw.callback) (sdl.draw.callback)( GFX_CallBackStop );
@@ -1173,13 +1795,21 @@ static void GUI_StartUp(Section * sec) {
Section_prop * section=static_cast<Section_prop *>(sec);
sdl.active=false;
sdl.updating=false;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.resizing_window=false;
+ sdl.update_display_contents=true;
+#endif
GFX_SetIcon();
sdl.desktop.lazy_fullscreen=false;
sdl.desktop.lazy_fullscreen_req=false;
+#ifdef __ANDROID__
+ sdl.desktop.fullscreen=true;
+#else
sdl.desktop.fullscreen=section->Get_bool("fullscreen");
+#endif
sdl.wait_on_error=section->Get_bool("waitonerror");
Prop_multival* p=section->Get_multival("priority");
@@ -1208,10 +1838,18 @@ static void GUI_StartUp(Section * sec) {
sdl.mouse.locked=false;
mouselocked=false; //Global for mapper
sdl.mouse.requestlock=false;
+#ifdef __ANDROID__
+ sdl.mouse.isLeftMouseFingerUsed=sdl.mouse.isRightMouseFingerUsed=false;
+ sdl.mouse.isMiddleMouseFingerUsed=sdl.mouse.isEscKeyFingerUsed=false;
+ /* We force fullscreen desktop resolution */
+ sdl.desktop.full.fixed=true;
+#else
sdl.desktop.full.fixed=false;
const char* fullresolution=section->Get_string("fullresolution");
+#endif
sdl.desktop.full.width = 0;
sdl.desktop.full.height = 0;
+#ifndef __ANDROID__
if(fullresolution && *fullresolution) {
char res[100];
safe_strncpy( res, fullresolution, sizeof( res ));
@@ -1228,9 +1866,10 @@ static void GUI_StartUp(Section * sec) {
}
}
}
-
+#endif
sdl.desktop.window.width = 0;
sdl.desktop.window.height = 0;
+#ifndef __ANDROID__
const char* windowresolution=section->Get_string("windowresolution");
if(windowresolution && *windowresolution) {
char res[100];
@@ -1245,7 +1884,24 @@ static void GUI_StartUp(Section * sec) {
}
}
}
- sdl.desktop.doublebuf=section->Get_bool("fulldouble");
+#endif
+
+ sdl.desktop.vsync=section->Get_bool("vsync");
+
+#if SDL_VERSION_ATLEAST(2,0,0)
+
+ sdl.displayNumber=section->Get_int("display");
+ if ((sdl.displayNumber < 0) || (sdl.displayNumber >= SDL_GetNumVideoDisplays())) {
+ sdl.displayNumber = 0;
+ LOG_MSG("SDL:Display number out of bounds, switching back to 0");
+ }
+ sdl.desktop.full.display_res = sdl.desktop.full.fixed && (!sdl.desktop.full.width || !sdl.desktop.full.height);
+ if (sdl.desktop.full.display_res) {
+ GFX_ObtainDisplayDimensions();
+ }
+
+#else // !SDL_VERSION_ATLEAST(2,0,0)
+
#if SDL_VERSION_ATLEAST(1, 2, 10)
#ifdef WIN32
const SDL_VideoInfo* vidinfo = SDL_GetVideoInfo();
@@ -1286,6 +1942,8 @@ static void GUI_StartUp(Section * sec) {
sdl.desktop.full.height=768;
#endif
}
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
+
sdl.mouse.autoenable=section->Get_bool("autolock");
if (!sdl.mouse.autoenable) SDL_ShowCursor(SDL_DISABLE);
sdl.mouse.autolock=false;
@@ -1297,12 +1955,22 @@ static void GUI_StartUp(Section * sec) {
if (output == "surface") {
sdl.desktop.want_type=SCREEN_SURFACE;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ } else if (output == "texture") {
+ sdl.desktop.want_type=SCREEN_TEXTURE;
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
+ } else if (output == "texturenb") {
+ sdl.desktop.want_type=SCREEN_TEXTURE;
+ // Currently the default, but... oh well
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
+#else // !SDL_VERSION_ATLEAST(2,0,0)
#if C_DDRAW
} else if (output == "ddraw") {
sdl.desktop.want_type=SCREEN_SURFACE_DDRAW;
#endif
} else if (output == "overlay") {
sdl.desktop.want_type=SCREEN_OVERLAY;
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
#if C_OPENGL
} else if (output == "opengl") {
sdl.desktop.want_type=SCREEN_OPENGL;
@@ -1316,19 +1984,56 @@ static void GUI_StartUp(Section * sec) {
sdl.desktop.want_type=SCREEN_SURFACE;//SHOULDN'T BE POSSIBLE anymore
}
+#if SDL_VERSION_ATLEAST(2,0,0)
+ sdl.texture.texture=0;
+ sdl.texture.pixelFormat=0;
+ sdl.window=0;
+ sdl.renderer=0;
+ sdl.rendererDriver = section->Get_string("renderer");
+#else
sdl.overlay=0;
+#endif
+
+#if (defined C_OPENGL) && (defined __ANDROID__) // OpenGL ES
+ static const GLfloat vertCoords[] = {
+ -1, -1, // lower left
+ 1, -1, // lower right
+ 1, 1, // upper right
+ -1, 1 // upper left
+ };
+ memcpy(sdl.opengl.vertCoords, vertCoords, sizeof(vertCoords));
+#endif
+
#if C_OPENGL
if(sdl.desktop.want_type==SCREEN_OPENGL){ /* OPENGL is requested */
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (!GFX_SetSDLOpenGLWindow(640,400)) {
+ LOG_MSG("Could not create OpenGL window, switching back to surface");
+ sdl.desktop.want_type=SCREEN_SURFACE;
+ } else {
+ sdl.opengl.context = SDL_GL_CreateContext(sdl.window);
+ if (sdl.opengl.context == 0) {
+ LOG_MSG("Could not create OpenGL context, switching back to surface");
+ sdl.desktop.want_type=SCREEN_SURFACE;
+ }
+ }
+ if (sdl.desktop.want_type==SCREEN_OPENGL) {
+#else // Same story but for SDL 1.2
sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,SDL_OPENGL);
if (sdl.surface == NULL) {
LOG_MSG("Could not initialize OpenGL, switching back to surface");
sdl.desktop.want_type=SCREEN_SURFACE;
} else {
+#endif // End of SDL specific video mode check. Let's assume it has passed...
sdl.opengl.buffer=0;
sdl.opengl.framebuf=0;
sdl.opengl.texture=0;
+#ifndef __ANDROID__
sdl.opengl.displaylist=0;
+#endif
+
glGetIntegerv (GL_MAX_TEXTURE_SIZE, &sdl.opengl.max_texsize);
+#ifndef __ANDROID__
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)SDL_GL_GetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC)SDL_GL_GetProcAddress("glBindBufferARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)SDL_GL_GetProcAddress("glDeleteBuffersARB");
@@ -1345,19 +2050,38 @@ static void GUI_StartUp(Section * sec) {
} else {
sdl.opengl.packed_pixel=sdl.opengl.paletted_texture=false;
}
+#endif // ifndef __ANDROID__
}
} /* OPENGL is requested end */
#endif //OPENGL
/* Initialize screen for first time */
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (!GFX_SetSDLSurfaceWindow(640,400))
+ E_Exit("Could not initialize video: %s",SDL_GetError());
+ sdl.surface = SDL_GetWindowSurface(sdl.window);
+ if (sdl.surface == NULL) E_Exit("Could not retrieve window surface: %s",SDL_GetError());
+ SDL_Rect splash_rect=GFX_GetSDLSurfaceSubwindowDims(640,400);
+ sdl.desktop.sdl2pixelFormat = sdl.surface->format->format;
+ LOG_MSG("SDL:Current window pixel format: %s", SDL_GetPixelFormatName(sdl.desktop.sdl2pixelFormat));
+ /* Do NOT use SDL_BITSPERPIXEL here - It returns 24 for
+ SDL_PIXELFORMAT_RGB888, while SDL_BYTESPERPIXEL returns 4.
+ To compare, with SDL 1.2 the detected desktop color depth is 32 bpp. */
+ sdl.desktop.bpp=8*SDL_BYTESPERPIXEL(sdl.desktop.sdl2pixelFormat);
+#else
sdl.surface=SDL_SetVideoMode_Wrap(640,400,0,0);
if (sdl.surface == NULL) E_Exit("Could not initialize video: %s",SDL_GetError());
sdl.desktop.bpp=sdl.surface->format->BitsPerPixel;
+#endif
if (sdl.desktop.bpp==24) {
LOG_MSG("SDL: You are running in 24 bpp mode, this will slow down things!");
}
GFX_Stop();
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetWindowTitle(sdl.window,"DOSBox");
+#else
SDL_WM_SetCaption("DOSBox",VERSION);
+#endif
/* The endian part is intentionally disabled as somehow it produces correct results without according to rhoenie*/
//#if SDL_BYTEORDER == SDL_BIG_ENDIAN
@@ -1373,6 +2097,9 @@ static void GUI_StartUp(Section * sec) {
/* Please leave the Splash screen stuff in working order in DOSBox. We spend a lot of time making DOSBox. */
SDL_Surface* splash_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 400, 32, rmask, gmask, bmask, 0);
if (splash_surf) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetSurfaceBlendMode(splash_surf, SDL_BLENDMODE_BLEND);
+#endif
SDL_FillRect(splash_surf, NULL, SDL_MapRGB(splash_surf->format, 0, 0, 0));
Bit8u* tmpbufp = new Bit8u[640*400*3];
@@ -1408,23 +2135,51 @@ static void GUI_StartUp(Section * sec) {
if (ct<1) {
SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetSurfaceAlphaMod(splash_surf, 255);
+ SDL_BlitScaled(splash_surf, NULL, sdl.surface, &splash_rect);
+#else
SDL_SetAlpha(splash_surf, SDL_SRCALPHA,255);
SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
+#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UpdateWindowSurface(sdl.window);
+#else
SDL_Flip(sdl.surface);
+#endif
} else if (ct>=max_splash_loop-splash_fade) {
if (use_fadeout) {
SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetSurfaceAlphaMod(splash_surf, (Bit8u)((max_splash_loop-1-ct)*255/(splash_fade-1)));
+ SDL_BlitScaled(splash_surf, NULL, sdl.surface, &splash_rect);
+#else
SDL_SetAlpha(splash_surf, SDL_SRCALPHA, (Bit8u)((max_splash_loop-1-ct)*255/(splash_fade-1)));
SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
+#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UpdateWindowSurface(sdl.window);
+#else
SDL_Flip(sdl.surface);
+#endif
}
+ } else { // Fix a possible glitch
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UpdateWindowSurface(sdl.window);
+#else
+ SDL_Flip(sdl.surface);
+#endif
}
}
if (use_fadeout) {
SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UpdateWindowSurface(sdl.window);
+#else
SDL_Flip(sdl.surface);
+#endif
}
SDL_FreeSurface(splash_surf);
delete [] tmpbufp;
@@ -1442,7 +2197,11 @@ static void GUI_StartUp(Section * sec) {
MAPPER_AddHandler(&PauseDOSBox, MK_pause, MMOD2, "pause", "Pause DBox");
#endif
/* Get Keyboard state of numlock and capslock */
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_Keymod keystate = SDL_GetModState();
+#else
SDLMod keystate = SDL_GetModState();
+#endif
if(keystate&KMOD_NUM) startup_state_numlock = true;
if(keystate&KMOD_CAPS) startup_state_capslock = true;
}
@@ -1456,6 +2215,83 @@ void Mouse_AutoLock(bool enable) {
}
}
+#if defined(__ANDROID__)
+/* The way mouse emulation is done here is based on the
+following (horizontal) partitioning of the touchscreen:
+
+/-----------------------------------------------------------------------\
+| Left | (H)Escape | Motion | Motion | Middle | Right |
+\-----------------------------------------------------------------------/
+
+Note that the simulated Escape key is the host one
+(not the emulator's), so it can be re-mapped.
+Furthermore, that should be considered a hack for now. */
+static void HandleTouchFinger(SDL_TouchFingerEvent * tfinger) {
+ void MAPPER_CheckEvent(SDL_Event * event);
+ switch (tfinger->type) {
+ case SDL_FINGERDOWN:
+ if (tfinger->x >= 0.83f) { // Right button
+ sdl.mouse.rightMouseFingerID = tfinger->fingerId;
+ if (!sdl.mouse.isRightMouseFingerUsed) {
+ Mouse_ButtonPressed(1);
+ sdl.mouse.isRightMouseFingerUsed = true;
+ }
+ } else if (tfinger->x >= 0.67f) { // Middle button
+ sdl.mouse.middleMouseFingerID = tfinger->fingerId;
+ if (!sdl.mouse.isMiddleMouseFingerUsed) {
+ Mouse_ButtonPressed(2);
+ sdl.mouse.isMiddleMouseFingerUsed = true;
+ }
+ } else if (tfinger->x >= 0.33f) { // Motion
+ sdl.mouse.mouseMotionFingerID = tfinger->fingerId;
+ } else if (tfinger->x >= 0.17f) { // (Host) Escape key
+ sdl.mouse.escKeyFingerID = tfinger->fingerId;
+ if (!sdl.mouse.isEscKeyFingerUsed) {
+ SDL_Event event;
+ event.type = SDL_KEYDOWN;
+ event.key.keysym.scancode = SDL_SCANCODE_ESCAPE;
+ MAPPER_CheckEvent(&event);
+ sdl.mouse.isEscKeyFingerUsed = true;
+ }
+ } else { // Left button
+ sdl.mouse.leftMouseFingerID = tfinger->fingerId;
+ if (!sdl.mouse.isLeftMouseFingerUsed) {
+ Mouse_ButtonPressed(0);
+ sdl.mouse.isLeftMouseFingerUsed = true;
+ }
+ }
+ break;
+ case SDL_FINGERUP:
+ if ((sdl.mouse.leftMouseFingerID == tfinger->fingerId) && sdl.mouse.isLeftMouseFingerUsed) {
+ Mouse_ButtonReleased(0);
+ sdl.mouse.isLeftMouseFingerUsed = false;
+ } else if ((sdl.mouse.rightMouseFingerID == tfinger->fingerId) && sdl.mouse.isRightMouseFingerUsed) {
+ Mouse_ButtonReleased(1);
+ sdl.mouse.isRightMouseFingerUsed = false;
+ } else if ((sdl.mouse.middleMouseFingerID == tfinger->fingerId) && sdl.mouse.isMiddleMouseFingerUsed) {
+ Mouse_ButtonReleased(2);
+ sdl.mouse.isMiddleMouseFingerUsed = false;
+ } else if ((sdl.mouse.escKeyFingerID == tfinger->fingerId) && sdl.mouse.isEscKeyFingerUsed) {
+ SDL_Event event;
+ event.type = SDL_KEYUP;
+ event.key.keysym.scancode = SDL_SCANCODE_ESCAPE;
+ MAPPER_CheckEvent(&event);
+ sdl.mouse.isEscKeyFingerUsed = false;
+ }
+ break;
+ case SDL_FINGERMOTION:
+ /* We basically IGNORE the absolute coordinates, since
+ we emulate relative mouse movement all the time. */
+ if (sdl.mouse.mouseMotionFingerID == tfinger->fingerId) {
+ Mouse_CursorMoved((float)tfinger->dx*sdl.clip.w*sdl.mouse.sensitivity/100.0f,
+ (float)tfinger->dy*sdl.clip.h*sdl.mouse.sensitivity/100.0f,
+ 0, 0, true);
+ }
+ break;
+ }
+}
+
+#else // Not on Android
static void HandleMouseMotion(SDL_MouseMotionEvent * motion) {
if (sdl.mouse.locked || !sdl.mouse.autoenable)
Mouse_CursorMoved((float)motion->xrel*sdl.mouse.sensitivity/100.0f,
@@ -1504,6 +2340,7 @@ static void HandleMouseButton(SDL_MouseButtonEvent * button) {
break;
}
}
+#endif // End of touch/mouse differentiation
void GFX_LosingFocus(void) {
sdl.laltstate=SDL_KEYUP;
@@ -1522,12 +2359,40 @@ bool GFX_IsFullscreen(void) {
#define DB_POLLSKIP 1
#endif
-#if defined(LINUX)
+#if (defined(LINUX) && !SDL_VERSION_ATLEAST(2,0,0))
#define SDL_XORG_FIX 1
#else
#define SDL_XORG_FIX 0
#endif
+#if SDL_VERSION_ATLEAST(2,0,0)
+void GFX_HandleVideoResize(int width, int height) {
+ /* Maybe a screen rotation has just occurred, so we simply resize.
+ There may be a different cause for a forced resized, though. */
+ if (sdl.desktop.full.display_res && sdl.desktop.fullscreen) {
+ /* Note: We should not use GFX_ObtainDisplayDimensions
+ (SDL_GetDisplayBounds) on Android after a screen rotation:
+ The older values from application startup are returned. */
+ sdl.desktop.full.width = width;
+ sdl.desktop.full.height = height;
+ }
+ /* Even if the new window's dimensions are actually the desired ones
+ * we may still need to re-obtain a new window surface or do
+ * a different thing. So we basically reset the screen, but without
+ * touching the window itself (or else we may end in an infinite loop).
+ *
+ * Furthermore, if the new dimensions are *not* the desired ones, we
+ * don't fight it. Rather than attempting to resize it back, we simply
+ * keep the window as-is and disable screen updates. This is done
+ * in SDL_SetSDLWindowSurface by setting sdl.update_display_contents
+ * to false.
+ */
+ sdl.resizing_window = true;
+ GFX_ResetScreen();
+ sdl.resizing_window = false;
+}
+#endif
+
void GFX_Events() {
//Don't poll too often. This can be heavy on the OS, especially Macs.
//In idle mode 3000-4000 polls are done per second without this check.
@@ -1565,6 +2430,98 @@ void GFX_Events() {
#endif
switch (event.type) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_RESTORED:
+ /* We may need to re-create a texture
+ * and more on Android. Another case:
+ * Update surface while using X11.
+ */
+ GFX_ResetScreen();
+ continue;
+ case SDL_WINDOWEVENT_RESIZED:
+ GFX_HandleVideoResize(event.window.data1, event.window.data2);
+ continue;
+ case SDL_WINDOWEVENT_EXPOSED:
+ if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
+ continue;
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ if (sdl.desktop.fullscreen && !sdl.mouse.locked)
+ GFX_CaptureMouse();
+ SetPriority(sdl.priority.focus);
+ CPU_Disable_SkipAutoAdjust();
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ if (sdl.mouse.locked) {
+#ifdef WIN32
+ if (sdl.desktop.fullscreen) {
+ VGA_KillDrawing();
+ GFX_ForceFullscreenExit();
+ }
+#endif
+ GFX_CaptureMouse();
+ }
+ SetPriority(sdl.priority.nofocus);
+ GFX_LosingFocus();
+ CPU_Enable_SkipAutoAdjust();
+ break;
+ default: ;
+ }
+
+ /* Non-focus priority is set to pause; check to see if we've lost window or input focus
+ * i.e. has the window been minimised or made inactive?
+ */
+ if (sdl.priority.nofocus == PRIORITY_LEVEL_PAUSE) {
+ if ((event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (event.window.event == SDL_WINDOWEVENT_MINIMIZED)) {
+ /* Window has lost focus, pause the emulator.
+ * This is similar to what PauseDOSBox() does, but the exit criteria is different.
+ * Instead of waiting for the user to hit Alt-Break, we wait for the window to
+ * regain window or input focus.
+ */
+ bool paused = true;
+ SDL_Event ev;
+
+ GFX_SetTitle(-1,-1,true);
+ KEYBOARD_ClrBuffer();
+// SDL_Delay(500);
+// while (SDL_PollEvent(&ev)) {
+ // flush event queue.
+// }
+
+ while (paused) {
+ // WaitEvent waits for an event rather than polling, so CPU usage drops to zero
+ SDL_WaitEvent(&ev);
+
+ switch (ev.type) {
+ case SDL_QUIT: throw(0); break; // a bit redundant at linux at least as the active events gets before the quit event.
+ case SDL_WINDOWEVENT: // wait until we get window focus back
+ if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_LOST) || (ev.window.event == SDL_WINDOWEVENT_MINIMIZED) || (ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) {
+ // We've got focus back, so unpause and break out of the loop
+ if ((ev.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) || (ev.window.event == SDL_WINDOWEVENT_RESTORED) || (ev.window.event == SDL_WINDOWEVENT_EXPOSED)) {
+ paused = false;
+ GFX_SetTitle(-1,-1,false);
+ }
+
+ /* Now poke a "release ALT" command into the keyboard buffer
+ * we have to do this, otherwise ALT will 'stick' and cause
+ * problems with the app running in the DOSBox.
+ */
+ KEYBOARD_AddKey(KBD_leftalt, false);
+ KEYBOARD_AddKey(KBD_rightalt, false);
+ if (ev.window.event == SDL_WINDOWEVENT_RESTORED) {
+ // We may need to re-create a texture and more
+ GFX_ResetScreen();
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+#endif // SDL_VERSION_ATLEAST(2,0,0)
+#if !SDL_VERSION_ATLEAST(2,0,0)
case SDL_ACTIVEEVENT:
if (event.active.state & SDL_APPINPUTFOCUS) {
if (event.active.gain) {
@@ -1638,6 +2595,14 @@ void GFX_Events() {
}
}
break;
+#endif // !SDL_VERSION_ATLEAST(2,0,0)
+#if defined(__ANDROID__)
+ case SDL_FINGERDOWN:
+ case SDL_FINGERUP:
+ case SDL_FINGERMOTION:
+ HandleTouchFinger(&event.tfinger);
+ break;
+#else
case SDL_MOUSEMOTION:
HandleMouseMotion(&event.motion);
break;
@@ -1645,21 +2610,26 @@ void GFX_Events() {
case SDL_MOUSEBUTTONUP:
HandleMouseButton(&event.button);
break;
+#endif
+#if !SDL_VERSION_ATLEAST(2,0,0)
case SDL_VIDEORESIZE:
-// HandleVideoResize(&event.resize);
+ //HandleVideoResize(event.resize.w, event.resize.h);
break;
+#endif
case SDL_QUIT:
throw(0);
break;
+#if !SDL_VERSION_ATLEAST(2,0,0)
case SDL_VIDEOEXPOSE:
if (sdl.draw.callback) sdl.draw.callback( GFX_CallBackRedraw );
break;
+#endif
#ifdef WIN32
case SDL_KEYDOWN:
case SDL_KEYUP:
// ignore event alt+tab
- if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = event.key.type;
- if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = event.key.type;
+ if (event.key.keysym.sym==SDLK_LALT) sdl.laltstate = (SDL_EventType)event.key.type;
+ if (event.key.keysym.sym==SDLK_RALT) sdl.raltstate = (SDL_EventType)event.key.type;
if (((event.key.keysym.sym==SDLK_TAB)) &&
((sdl.laltstate==SDL_KEYDOWN) || (sdl.raltstate==SDL_KEYDOWN))) break;
// This can happen as well.
@@ -1671,7 +2641,15 @@ void GFX_Events() {
case SDL_KEYDOWN:
case SDL_KEYUP:
/* On macs CMD-Q is the default key to close an application */
- if (event.key.keysym.sym == SDLK_q && (event.key.keysym.mod == KMOD_RMETA || event.key.keysym.mod == KMOD_LMETA) ) {
+ if (event.key.keysym.sym == SDLK_q &&
+#if SDL_VERSION_ATLEAST(2,0,0)
+ (event.key.keysym.mod == KMOD_RGUI ||
+ event.key.keysym.mod == KMOD_LGUI)
+#else
+ (event.key.keysym.mod == KMOD_RMETA ||
+ event.key.keysym.mod == KMOD_LMETA)
+#endif
+ ) {
KillSwitch(true);
break;
}
@@ -1710,7 +2688,11 @@ void GFX_ShowMsg(char const* format,...) {
vsprintf(buf,format,msg);
strcat(buf,"\n");
va_end(msg);
+#ifdef __ANDROID__
+ __android_log_print(ANDROID_LOG_DEBUG, "DOSBox", buf);
+#else
if(!no_stdout) printf("%s",buf); //Else buf is parsed again.
+#endif
}
@@ -1722,13 +2704,17 @@ void Config_Add_SDL() {
Prop_int* Pint;
Prop_multival* Pmulti;
+#ifndef __ANDROID__
Pbool = sdl_sec->Add_bool("fullscreen",Property::Changeable::Always,false);
Pbool->Set_help("Start dosbox directly in fullscreen. (Press ALT-Enter to go back)");
+#endif
- Pbool = sdl_sec->Add_bool("fulldouble",Property::Changeable::Always,false);
- Pbool->Set_help("Use double buffering in fullscreen. It can reduce screen flickering, but it can also result in a slow DOSBox.");
+ Pbool = sdl_sec->Add_bool("vsync",Property::Changeable::Always,false);
+ Pbool->Set_help("Sync to Vblank IF supported by the output device and renderer.\n"
+ "It can reduce screen flickering, but it can also result in a slow DOSBox.");
- Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"original");
+#ifndef __ANDROID__
+ Pstring = sdl_sec->Add_string("fullresolution",Property::Changeable::Always,"0x0");
Pstring->Set_help("What resolution to use for fullscreen: original, desktop or a fixed size (e.g. 1024x768).\n"
"Using your monitor's native resolution with aspect=true might give the best results.\n"
"If you end up with small window on a large screen, try an output different from surface."
@@ -1738,20 +2724,53 @@ void Config_Add_SDL() {
Pstring = sdl_sec->Add_string("windowresolution",Property::Changeable::Always,"original");
Pstring->Set_help("Scale the window to this size IF the output device supports hardware scaling.\n"
"(output=surface does not!)");
+#endif
const char* outputs[] = {
- "surface", "overlay",
-#if C_OPENGL
- "opengl", "openglnb",
-#endif
+ "surface",
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ "texture",
+ "texturenb",
+#else // !SDL_VERSION_ATLEAST(2, 0, 0)
+ "overlay",
#if C_DDRAW
"ddraw",
#endif
+#endif // !SDL_VERSION_ATLEAST(2, 0, 0)
+#if C_OPENGL
+ "opengl", "openglnb",
+#endif
0 };
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ Pstring = sdl_sec->Add_string("output",Property::Changeable::Always,"texture");
+#else
Pstring = sdl_sec->Add_string("output",Property::Changeable::Always,"surface");
+#endif
Pstring->Set_help("What video system to use for output.");
Pstring->Set_values(outputs);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ const char* renderers[] = {
+ "auto",
+#ifdef WIN32
+ "direct3d",
+#endif
+#ifdef __ANDROID__
+ "opengles2",
+ "opengles",
+#else // On any platform other than Android
+ "opengl",
+#endif
+#ifdef MACOSX
+ "metal",
+#endif
+ "software",
+ 0 };
+ Pstring = sdl_sec->Add_string("renderer",Property::Changeable::Always,"auto");
+ Pstring->Set_help("Choose a renderer driver if output=texture or output=texturenb. Use output=auto for an automatic choice.");
+ Pstring->Set_values(renderers);
+#endif // SDL_VERSION_ATLEAST(2,0,0)
+
Pbool = sdl_sec->Add_bool("autolock",Property::Changeable::Always,true);
Pbool->Set_help("Mouse will automatically lock, if you click on the screen. (Press CTRL-F10 to unlock)");
@@ -1778,8 +2797,10 @@ void Config_Add_SDL() {
Pstring = sdl_sec->Add_path("mapperfile",Property::Changeable::Always,MAPPERFILE);
Pstring->Set_help("File used to load/save the key/event mappings from. Resetmapper only works with the default value.");
+#if !SDL_VERSION_ATLEAST(2,0,0)
Pbool = sdl_sec->Add_bool("usescancodes",Property::Changeable::Always,true);
Pbool->Set_help("Avoid usage of symkeys, might not work on all operating systems.");
+#endif
}
static void show_warning(char const * const message) {
@@ -1791,8 +2812,15 @@ static void show_warning(char const * const message) {
#endif
printf("%s",message);
if(textonly) return;
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ if (!sdl.window)
+ if (!GFX_SetSDLSurfaceWindow(640,400)) return;
+ sdl.surface = SDL_GetWindowSurface(sdl.window);
+#else
if(!sdl.surface) sdl.surface = SDL_SetVideoMode_Wrap(640,400,0,0);
+#endif
if(!sdl.surface) return;
+
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
Bit32u rmask = 0xff000000;
Bit32u gmask = 0x00ff0000;
@@ -1820,7 +2848,11 @@ static void show_warning(char const * const message) {
}
SDL_BlitSurface(splash_surf, NULL, sdl.surface, NULL);
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+ SDL_UpdateWindowSurface(sdl.window);
+#else
SDL_Flip(sdl.surface);
+#endif
SDL_Delay(12000);
}
@@ -1850,6 +2882,8 @@ static void launcheditor() {
extern void DEBUG_ShutDown(Section * /*sec*/);
#endif
+void MIXER_CloseAudioDevice(void);
+
void restart_program(std::vector<std::string> & parameters) {
char** newargs = new char* [parameters.size()+1];
// parameter 0 is the executable path
@@ -1857,9 +2891,9 @@ void restart_program(std::vector<std::string> & parameters) {
// last one is NULL
for(Bitu i = 0; i < parameters.size(); i++) newargs[i]=(char*)parameters[i].c_str();
newargs[parameters.size()] = NULL;
- SDL_CloseAudio();
+ MIXER_CloseAudioDevice();
SDL_Delay(50);
- SDL_Quit();
+ SDL_Quit_Wrapper();
#if C_DEBUG
// shutdown curses
DEBUG_ShutDown(NULL);
@@ -2043,14 +3077,16 @@ int main(int argc, char* argv[]) {
*/
putenv(const_cast<char*>("SDL_DISABLE_LOCK_KEYS=1"));
#endif
- // Don't init timers, GetTicks seems to work fine and they can use a fair amount of power (Macs again)
- // Please report problems with audio and other things.
- if ( SDL_Init( SDL_INIT_AUDIO|SDL_INIT_VIDEO | /*SDL_INIT_TIMER |*/ SDL_INIT_CDROM
- |SDL_INIT_NOPARACHUTE
- ) < 0 ) E_Exit("Can't init SDL %s",SDL_GetError());
+ if ( SDL_Init_Wrapper() < 0 )
+ E_Exit("Can't init SDL %s",SDL_GetError());
sdl.inited = true;
#ifndef DISABLE_JOYSTICK
+#ifdef __ANDROID__
+ // Disable accelerometer-as-joystick emulation
+ // (available for backwards compatibility)
+ SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
+#endif
//Initialise Joystick separately. This way we can warn when it fails instead
//of exiting the application
if( SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) LOG_MSG("Failed to init joystick support");
@@ -2059,6 +3095,7 @@ int main(int argc, char* argv[]) {
sdl.laltstate = SDL_KEYUP;
sdl.raltstate = SDL_KEYUP;
+#if !SDL_VERSION_ATLEAST(2, 0, 0) // Not relevant for SDL 2.0 at the moment
#if defined (WIN32)
#if SDL_VERSION_ATLEAST(1, 2, 10)
sdl.using_windib=true;
@@ -2087,7 +3124,8 @@ int main(int argc, char* argv[]) {
if (SDL_VideoDriverName(sdl_drv_name,128)!=NULL) {
if (strcmp(sdl_drv_name,"windib")==0) LOG_MSG("SDL_Init: Starting up with SDL windib video driver.\n Try to update your video card and directx drivers!");
}
-#endif
+#endif // WIN32
+#endif // !SDL_VERSION_ATLEAST(2, 0, 0)
sdl.num_joysticks=SDL_NumJoysticks();
/* Parse configuration files */
@@ -2200,10 +3238,17 @@ int main(int argc, char* argv[]) {
sticky_keys(true); //Might not be needed if the shutdown function switches to windowed mode, but it doesn't hurt
#endif
//Force visible mouse to end user. Somehow this sometimes doesn't happen
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_SetRelativeMouseMode(SDL_FALSE);
+#else
SDL_WM_GrabInput(SDL_GRAB_OFF);
+#endif
SDL_ShowCursor(SDL_ENABLE);
- SDL_Quit();//Let's hope sdl will quit as well when it catches an exception
+ SDL_Quit_Wrapper();//Let's hope sdl will quit as well when it catches an exception
+#ifdef __ANDROID__
+ exit(0); // Actually quits application... and hopefully(?) removes static values
+#endif
return 0;
}
diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp
index 1fbe4443f..ff40e1c94 100644
--- a/src/hardware/mixer.cpp
+++ b/src/hardware/mixer.cpp
@@ -86,6 +86,10 @@ static struct {
bool nosound;
Bit32u freq;
Bit32u blocksize;
+#if SDL_VERSION_ATLEAST(2,0,0)
+ //Note: As stated earlier, all sdl code shall rather be in sdlmain
+ SDL_AudioDeviceID sdldevice;
+#endif
} mixer;
Bit8u MixTemp[MIXER_BUFSIZE];
@@ -127,6 +131,22 @@ void MIXER_DelChannel(MixerChannel* delchan) {
}
}
+static void MIXER_LockAudioDevice(void) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_LockAudioDevice(mixer.sdldevice);
+#else
+ SDL_LockAudio();
+#endif
+}
+
+static void MIXER_UnlockAudioDevice(void) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_UnlockAudioDevice(mixer.sdldevice);
+#else
+ SDL_UnlockAudio();
+#endif
+}
+
void MixerChannel::UpdateVolume(void) {
volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]*mixer.mastervol[0]);
volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]*mixer.mastervol[1]);
@@ -148,9 +168,9 @@ void MixerChannel::Enable(bool _yesno) {
enabled=_yesno;
if (enabled) {
freq_counter = 0;
- SDL_LockAudio();
+ MIXER_LockAudioDevice();
if (done<mixer.done) done=mixer.done;
- SDL_UnlockAudio();
+ MIXER_UnlockAudioDevice();
}
}
@@ -382,14 +402,14 @@ void MixerChannel::AddSamples_s32_nonnative(Bitu len,const Bit32s * data) {
}
void MixerChannel::FillUp(void) {
- SDL_LockAudio();
+ MIXER_LockAudioDevice();
if (!enabled || done<mixer.done) {
- SDL_UnlockAudio();
+ MIXER_UnlockAudioDevice();
return;
}
float index=PIC_TickIndex();
Mix((Bitu)(index*mixer.needed));
- SDL_UnlockAudio();
+ MIXER_UnlockAudioDevice();
}
extern bool ticksLocked;
@@ -439,12 +459,12 @@ static void MIXER_MixData(Bitu needed) {
}
static void MIXER_Mix(void) {
- SDL_LockAudio();
+ MIXER_LockAudioDevice();
MIXER_MixData(mixer.needed);
mixer.tick_counter += mixer.tick_add;
mixer.needed+=(mixer.tick_counter >> TICK_SHIFT);
mixer.tick_counter &= TICK_MASK;
- SDL_UnlockAudio();
+ MIXER_UnlockAudioDevice();
}
static void MIXER_Mix_NoSound(void) {
@@ -468,6 +488,9 @@ static void MIXER_Mix_NoSound(void) {
}
static void SDLCALL MIXER_CallBack(void * userdata, Uint8 *stream, int len) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ memset(stream, 0, len);
+#endif
Bitu need=(Bitu)len/MIXER_SSIZE;
Bit16s * output=(Bit16s *)stream;
Bitu reduce;
@@ -696,7 +719,11 @@ void MIXER_Init(Section* sec) {
LOG_MSG("MIXER: No Sound Mode Selected.");
mixer.tick_add=calc_tickadd(mixer.freq);
TIMER_AddTickHandler(MIXER_Mix_NoSound);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ } else if ((mixer.sdldevice = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) ==0 ) {
+#else
} else if (SDL_OpenAudio(&spec, &obtained) <0 ) {
+#endif
mixer.nosound = true;
LOG_MSG("MIXER: Can't open audio: %s , running in nosound mode.",SDL_GetError());
mixer.tick_add=calc_tickadd(mixer.freq);
@@ -708,7 +735,11 @@ void MIXER_Init(Section* sec) {
mixer.blocksize=obtained.samples;
mixer.tick_add=calc_tickadd(mixer.freq);
TIMER_AddTickHandler(MIXER_Mix);
+#if SDL_VERSION_ATLEAST(2,0,0)
+ SDL_PauseAudioDevice(mixer.sdldevice, 0);
+#else
SDL_PauseAudio(0);
+#endif
}
mixer.min_needed=section->Get_int("prebuffer");
if (mixer.min_needed>100) mixer.min_needed=100;
@@ -717,3 +748,16 @@ void MIXER_Init(Section* sec) {
mixer.needed=mixer.min_needed+1;
PROGRAMS_MakeFile("MIXER.COM",MIXER_ProgramStart);
}
+
+void MIXER_CloseAudioDevice(void) {
+ if (!mixer.nosound) {
+#if SDL_VERSION_ATLEAST(2,0,0)
+ if (mixer.sdldevice != 0) {
+ SDL_CloseAudioDevice(mixer.sdldevice);
+ mixer.sdldevice = 0;
+ }
+#else
+ SDL_CloseAudio();
+#endif
+ }
+}
diff --git a/src/misc/cross.cpp b/src/misc/cross.cpp
index 021d8f35c..4e3fe89a6 100644
--- a/src/misc/cross.cpp
+++ b/src/misc/cross.cpp
@@ -30,6 +30,10 @@
#include <shlobj.h>
#endif
+#if defined(__ANDROID__)
+#include "SDL_system.h" // For SDL_AndroidGetExternalStoragePath
+#endif
+
#if defined HAVE_SYS_TYPES_H && defined HAVE_PWD_H
#include <sys/types.h>
#include <pwd.h>
@@ -61,6 +65,10 @@ void Cross::GetPlatformConfigDir(std::string& in) {
#elif defined(MACOSX)
in = "~/Library/Preferences";
ResolveHomedir(in);
+#elif defined(__ANDROID__)
+ in = SDL_AndroidGetExternalStoragePath();
+ //Assume external storage (possibly internal) is available
+ ResolveHomedir(in);
#else
in = "~/.dosbox";
ResolveHomedir(in);
@@ -88,6 +96,11 @@ void Cross::CreatePlatformConfigDir(std::string& in) {
in = "~/Library/Preferences/";
ResolveHomedir(in);
//Don't create it. Assume it exists
+#elif defined(__ANDROID__)
+ in = SDL_AndroidGetExternalStoragePath();
+ //Assume external storage (possibly internal) is available
+ ResolveHomedir(in);
+ mkdir(in.c_str(),0770);
#else
in = "~/.dosbox";
ResolveHomedir(in);
diff --git a/src/sdl_cdrom/Makefile.am b/src/sdl_cdrom/Makefile.am
new file mode 100644
index 000000000..2cd2eb3ff
--- /dev/null
+++ b/src/sdl_cdrom/Makefile.am
@@ -0,0 +1,10 @@
+SUBDIRS = macos macosx
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libcsdlcdrom.a
+EXTRA_DIST = compat_SDL_cdrom.h SDL_syscdrom.h
+libcsdlcdrom_a_SOURCES = compat_SDL_cdrom.c SDL_syscdrom_aix.c SDL_syscdrom_beos.cc \
+ SDL_syscdrom_bsdi.c SDL_syscdrom_dc.c SDL_syscdrom_dummy.c \
+ SDL_syscdrom_freebsd.c SDL_syscdrom_linux.c SDL_syscdrom_mint.c \
+ SDL_syscdrom_openbsd.c SDL_syscdrom_os2.c SDL_syscdrom_osf.c \
+ SDL_syscdrom_qnx.c SDL_syscdrom_win32.c
diff --git a/src/sdl_cdrom/SDL_syscdrom.h b/src/sdl_cdrom/SDL_syscdrom.h
new file mode 100644
index 000000000..0feeee5b4
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom.h
@@ -0,0 +1,76 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is SDL_free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* This is the system specific header for the SDL CD-ROM API */
+
+/* Structure of CD audio control functions */
+extern struct CDcaps {
+ /* Get the name of the specified drive */
+ const char *(*Name)(int drive);
+
+ /* Open the specified drive, returning a drive id, or -1 on error */
+ int (*Open)(int drive);
+
+ /* Get table-of-contents (number of tracks + track info) for disk.
+ The TOC information should be stored in the cdrom structure.
+ This function should return 0 on success, or -1 on error.
+ */
+ int (*GetTOC)(SDL_CD *cdrom);
+
+ /* Return the current status and play position, in frames, of the
+ drive. 'position' may be NULL, and if so, should be ignored.
+ */
+ CDstatus (*Status)(SDL_CD *cdrom, int *position);
+
+ /* Play from frame 'start' to 'start+len' */
+ int (*Play)(SDL_CD *cdrom, int start, int len);
+
+ /* Pause play */
+ int (*Pause)(SDL_CD *cdrom);
+
+ /* Resume play */
+ int (*Resume)(SDL_CD *cdrom);
+
+ /* Stop play */
+ int (*Stop)(SDL_CD *cdrom);
+
+ /* Eject the current disk */
+ int (*Eject)(SDL_CD *cdrom);
+
+ /* Close the specified drive */
+ void (*Close)(SDL_CD *cdrom);
+} SDL_CDcaps;
+
+/* The number of available CD-ROM drives on the system */
+extern int SDL_numcds;
+
+/* Function to scan the system for CD-ROM drives and fill SDL_CDcaps.
+ * This function should set SDL_numcds to the number of available CD
+ * drives. Drive 0 should be the system default CD-ROM.
+ * It should return 0, or -1 on an unrecoverable fatal error.
+*/
+extern int SDL_SYS_CDInit(void);
+
+/* Function to perform any system-specific CD-ROM related cleanup */
+extern void SDL_SYS_CDQuit(void);
+
diff --git a/src/sdl_cdrom/SDL_syscdrom_aix.c b/src/sdl_cdrom/SDL_syscdrom_aix.c
new file mode 100644
index 000000000..70d2bbef1
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_aix.c
@@ -0,0 +1,665 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Carsten Griwodz
+ griff@kom.tu-darmstadt.de
+
+ based on linux/SDL_syscdrom.c by Sam Lantinga
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_AIX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_AIX)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_AIX*/
+
+/* Functions for system-level CD-ROM audio control */
+
+/*#define DEBUG_CDROM 1*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/devinfo.h>
+#include <sys/mntctl.h>
+#include <sys/statfs.h>
+#include <sys/vmount.h>
+#include <fstab.h>
+#include <sys/scdisk.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+static int SDL_SYS_CDioctl(int id, int command, void *arg);
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive, struct stat *stbuf)
+{
+ int is_cd;
+ int cdfd;
+ int ret;
+ struct devinfo info;
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, stbuf) < 0 ) {
+ return -1;
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ is_cd = 0;
+ if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
+ cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
+ if ( cdfd >= 0 ) {
+ ret = SDL_SYS_CDioctl( cdfd, IOCINFO, &info );
+ if ( ret < 0 ) {
+ /* Some kind of error */
+ is_cd = 0;
+ } else {
+ if ( info.devtype == DD_CDROM ) {
+ is_cd = 1;
+ } else {
+ is_cd = 0;
+ }
+ }
+ close(cdfd);
+ }
+#ifdef DEBUG_CDROM
+ else
+ {
+ fprintf(stderr, "Could not open drive %s (%s)\n", drive, strerror(errno));
+ }
+#endif
+ }
+ return is_cd;
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Check to make sure it's not already in our list.
+ This can happen when we see a drive via symbolic link.
+ */
+ for ( i=0; i<SDL_numcds; ++i ) {
+ if ( stbuf->st_rdev == SDL_cdmode[i] ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
+#endif
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+static void CheckMounts()
+{
+ char* buffer;
+ int bufsz;
+ struct vmount* ptr;
+ int ret;
+
+ buffer = (char*)SDL_malloc(10);
+ bufsz = 10;
+ if ( buffer==NULL )
+ {
+ fprintf(stderr, "Could not allocate 10 bytes in aix/SDL_syscdrom.c:CheckMounts\n" );
+ exit ( -10 );
+ }
+
+ do
+ {
+ /* mntctrl() returns an array of all mounted filesystems */
+ ret = mntctl ( MCTL_QUERY, bufsz, buffer );
+ if ( ret == 0 )
+ {
+ /* Buffer was too small, realloc. */
+ bufsz = *(int*)buffer; /* Required size is in first word. */
+ /* (whatever a word is in AIX 4.3.3) */
+ /* int seems to be OK in 32bit mode. */
+ SDL_free(buffer);
+ buffer = (char*)SDL_malloc(bufsz);
+ if ( buffer==NULL )
+ {
+ fprintf(stderr,
+ "Could not allocate %d bytes in aix/SDL_syscdrom.c:CheckMounts\n",
+ bufsz );
+ exit ( -10 );
+ }
+ }
+ else if ( ret < 0 )
+ {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Error reading vmount structures\n");
+#endif
+ return;
+ }
+ }
+ while ( ret == 0 );
+
+#ifdef DEBUG_CDROM
+ fprintf ( stderr, "Read %d vmount structures\n",ret );
+#endif
+ ptr = (struct vmount*)buffer;
+ do
+ {
+ switch(ptr->vmt_gfstype)
+ {
+ case MNT_CDROM :
+ {
+ struct stat stbuf;
+ char* text;
+
+ text = (char*)ptr + ptr->vmt_data[VMT_OBJECT].vmt_off;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Checking mount path: %s mounted on %s\n",
+ text, (char*)ptr + ptr->vmt_data[VMT_STUB].vmt_off );
+#endif
+ if ( CheckDrive( text, &stbuf) > 0)
+ {
+ AddDrive( text, &stbuf);
+ }
+ }
+ break;
+ default :
+ break;
+ }
+ ptr = (struct vmount*)((char*)ptr + ptr->vmt_length);
+ ret--;
+ }
+ while ( ret > 0 );
+
+ free ( buffer );
+}
+
+static int CheckNonmounts()
+{
+#ifdef _THREAD_SAFE
+ AFILE_t fsFile = NULL;
+ int passNo = 0;
+ int ret;
+ struct fstab entry;
+ struct stat stbuf;
+
+ ret = setfsent_r( &fsFile, &passNo );
+ if ( ret != 0 ) return -1;
+ do
+ {
+ ret = getfsent_r ( &entry, &fsFile, &passNo );
+ if ( ret == 0 ) {
+ char* l = SDL_strrchr(entry.fs_spec,'/');
+ if ( l != NULL ) {
+ if ( !SDL_strncmp("cd",++l,2) ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr,
+ "Found unmounted CD ROM drive with device name %s\n",
+ entry.fs_spec);
+#endif
+ if ( CheckDrive( entry.fs_spec, &stbuf) > 0)
+ {
+ AddDrive( entry.fs_spec, &stbuf);
+ }
+ }
+ }
+ }
+ }
+ while ( ret == 0 );
+ ret = endfsent_r ( &fsFile );
+ if ( ret != 0 ) return -1;
+ return 0;
+#else
+ struct fstab* entry;
+ struct stat stbuf;
+
+ setfsent();
+ do
+ {
+ entry = getfsent();
+ if ( entry != NULL ) {
+ char* l = SDL_strrchr(entry->fs_spec,'/');
+ if ( l != NULL ) {
+ if ( !SDL_strncmp("cd",++l,2) ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr,"Found unmounted CD ROM drive with device name %s", entry->fs_spec);
+#endif
+ if ( CheckDrive( entry->fs_spec, &stbuf) > 0)
+ {
+ AddDrive( entry->fs_spec, &stbuf);
+ }
+ }
+ }
+ }
+ }
+ while ( entry != NULL );
+ endfsent();
+#endif
+}
+
+int SDL_SYS_CDInit(void)
+{
+ char *SDLcdrom;
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom);
+#endif
+ if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+
+ CheckMounts();
+ CheckNonmounts();
+
+ return 0;
+}
+
+/* General ioctl() CD-ROM command function */
+static int SDL_SYS_CDioctl(int id, int command, void *arg)
+{
+ int retval;
+
+ retval = ioctl(id, command, arg);
+ if ( retval < 0 ) {
+ SDL_SetError("ioctl() error: %s", strerror(errno));
+ }
+ return retval;
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ int fd;
+ char* lastsl;
+ char* cdromname;
+ size_t len;
+
+ /*
+ * We found /dev/cd? drives and that is in our list. But we can
+ * open only the /dev/rcd? versions of those devices for Audio CD.
+ */
+ len = SDL_strlen(SDL_cdlist[drive])+2;
+ cdromname = (char*)SDL_malloc(len);
+ SDL_strlcpy(cdromname,SDL_cdlist[drive],len);
+ lastsl = SDL_strrchr(cdromname,'/');
+ if (lastsl) {
+ *lastsl = 0;
+ SDL_strlcat(cdromname,"/r",len);
+ lastsl = SDL_strrchr(SDL_cdlist[drive],'/');
+ if (lastsl) {
+ lastsl++;
+ SDL_strlcat(cdromname,lastsl,len);
+ }
+ }
+
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Should open drive %s, opening %s\n", SDL_cdlist[drive], cdromname);
+#endif
+
+ /*
+ * Use exclusive access. Don't use SC_DIAGNOSTICS as xmcd does because they
+ * require root priviledges, and we don't want that. SC_SINGLE provides
+ * exclusive access with less trouble.
+ */
+ fd = openx(cdromname, O_RDONLY, NULL, SC_SINGLE);
+ if ( fd < 0 )
+ {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Could not open drive %s (%s)\n", cdromname, strerror(errno));
+#endif
+ }
+ else
+ {
+ struct mode_form_op cdMode;
+ int ret;
+#ifdef DEBUG_CDROM
+ cdMode.action = CD_GET_MODE;
+ ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode);
+ if ( ret < 0 ) {
+ fprintf(stderr,
+ "Could not get drive mode for %s (%s)\n",
+ cdromname, strerror(errno));
+ } else {
+ switch(cdMode.cd_mode_form) {
+ case CD_MODE1 :
+ fprintf(stderr,
+ "Drive mode for %s is %s\n",
+ cdromname, "CD-ROM Data Mode 1");
+ break;
+ case CD_MODE2_FORM1 :
+ fprintf(stderr,
+ "Drive mode for %s is %s\n",
+ cdromname, "CD-ROM XA Data Mode 2 Form 1");
+ break;
+ case CD_MODE2_FORM2 :
+ fprintf(stderr,
+ "Drive mode for %s is %s\n",
+ cdromname, "CD-ROM XA Data Mode 2 Form 2");
+ break;
+ case CD_DA :
+ fprintf(stderr,
+ "Drive mode for %s is %s\n",
+ cdromname, "CD-DA");
+ break;
+ default :
+ fprintf(stderr,
+ "Drive mode for %s is %s\n",
+ cdromname, "unknown");
+ break;
+ }
+ }
+#endif
+
+ cdMode.action = CD_CHG_MODE;
+ cdMode.cd_mode_form = CD_DA;
+ ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode);
+ if ( ret < 0 ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr,
+ "Could not set drive mode for %s (%s)\n",
+ cdromname, strerror(errno));
+#endif
+ SDL_SetError("ioctl() error: Could not set CD drive mode, %s",
+ strerror(errno));
+ } else {
+#ifdef DEBUG_CDROM
+ fprintf(stderr,
+ "Drive mode for %s set to CD_DA\n",
+ cdromname);
+#endif
+ }
+ }
+ SDL_free(cdromname);
+ return fd;
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ struct cd_audio_cmd cmd;
+ struct cd_audio_cmd entry;
+ int i;
+ int okay;
+
+ cmd.audio_cmds = CD_TRK_INFO_AUDIO;
+ cmd.msf_flag = FALSE;
+ if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) {
+ return -1;
+ }
+
+ okay = 0;
+ cdrom->numtracks = cmd.indexing.track_index.last_track
+ - cmd.indexing.track_index.first_track+1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+
+ /* Read all the track TOC entries */
+ for ( i=0; i<=cdrom->numtracks; ++i ) {
+ if ( i == cdrom->numtracks ) {
+ cdrom->track[i].id = 0xAA;;
+ } else {
+ cdrom->track[i].id = cmd.indexing.track_index.first_track+i;
+ }
+ entry.audio_cmds = CD_GET_TRK_MSF;
+ entry.indexing.track_msf.track = cdrom->track[i].id;
+ if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &entry) < 0 ) {
+ break;
+ } else {
+ cdrom->track[i].type = 0; /* don't know how to detect 0x04 data track */
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ entry.indexing.track_msf.mins,
+ entry.indexing.track_msf.secs,
+ entry.indexing.track_msf.frames);
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length = cdrom->track[i].offset
+ - cdrom->track[i-1].offset;
+ }
+ }
+ }
+ if ( i == (cdrom->numtracks+1) ) {
+ okay = 1;
+ }
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ struct cd_audio_cmd cmd;
+ cmd.audio_cmds = CD_INFO_AUDIO;
+
+ if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "ioctl failed in SDL_SYS_CDStatus (%s)\n", SDL_GetError());
+#endif
+ status = CD_ERROR;
+ } else {
+ switch (cmd.status) {
+ case CD_NO_AUDIO:
+ case CD_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case CD_PLAY_AUDIO:
+ status = CD_PLAYING;
+ break;
+ case CD_PAUSE_AUDIO:
+ status = CD_PAUSED;
+ break;
+ case CD_NOT_VALID:
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "cdStatus failed with CD_NOT_VALID\n");
+#endif
+ status = CD_ERROR;
+ break;
+ case CD_STATUS_ERROR:
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "cdStatus failed with CD_STATUS_ERROR\n");
+#endif
+ status = CD_ERROR;
+ break;
+ default:
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "cdStatus failed with unknown error\n");
+#endif
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if ( position ) {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
+ *position = MSF_TO_FRAMES( cmd.indexing.info_audio.current_mins,
+ cmd.indexing.info_audio.current_secs,
+ cmd.indexing.info_audio.current_frames);
+ } else {
+ *position = 0;
+ }
+ }
+ return status;
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ struct cd_audio_cmd cmd;
+
+ /*
+ * My CD Rom is muted by default. I think I read that this is new with
+ * AIX 4.3. SDL does not change the volume, so I need a kludge. Maybe
+ * its better to do this elsewhere?
+ */
+ cmd.audio_cmds = CD_PLAY_AUDIO | CD_SET_VOLUME;
+ cmd.msf_flag = TRUE;
+ FRAMES_TO_MSF(start,
+ &cmd.indexing.msf.first_mins,
+ &cmd.indexing.msf.first_secs,
+ &cmd.indexing.msf.first_frames);
+ FRAMES_TO_MSF(start+length,
+ &cmd.indexing.msf.last_mins,
+ &cmd.indexing.msf.last_secs,
+ &cmd.indexing.msf.last_frames);
+ cmd.volume_type = CD_VOLUME_ALL;
+ cmd.all_channel_vol = 255; /* This is a uchar. What is a good value? No docu! */
+ cmd.out_port_0_sel = CD_AUDIO_CHNL_0;
+ cmd.out_port_1_sel = CD_AUDIO_CHNL_1;
+ cmd.out_port_2_sel = CD_AUDIO_CHNL_2;
+ cmd.out_port_3_sel = CD_AUDIO_CHNL_3;
+
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
+ cmd.indexing.msf.first_mins,
+ cmd.indexing.msf.first_secs,
+ cmd.indexing.msf.first_frames,
+ cmd.indexing.msf.last_mins,
+ cmd.indexing.msf.last_secs,
+ cmd.indexing.msf.last_frames);
+#endif
+ return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ struct cd_audio_cmd cmd;
+ cmd.audio_cmds = CD_PAUSE_AUDIO;
+ return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ struct cd_audio_cmd cmd;
+ cmd.audio_cmds = CD_RESUME_AUDIO;
+ return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ struct cd_audio_cmd cmd;
+ cmd.audio_cmds = CD_STOP_AUDIO;
+ return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, DKEJECT, 0));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_AIX */
diff --git a/src/sdl_cdrom/SDL_syscdrom_beos.cc b/src/sdl_cdrom/SDL_syscdrom_beos.cc
new file mode 100644
index 000000000..5f3fdaaae
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_beos.cc
@@ -0,0 +1,414 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define BEOS 1
+#if (C_COMPAT_SDL_CDROM_PLATFORM == BEOS)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_BEOS*/
+
+/* Functions for system-level CD-ROM audio control on BeOS
+ (not completely implemented yet)
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <scsi.h>
+#include <Directory.h>
+#include <Entry.h>
+#include <Path.h>
+
+#include "compat_SDL_cdrom.h"
+extern "C" {
+#include "SDL_syscdrom.h"
+}
+
+/* Constants to help us get at the SCSI table-of-contents info */
+#define CD_NUMTRACKS(toc) toc.toc_data[3]
+#define CD_TRACK(toc, track) (&toc.toc_data[6+(track)*8])
+#define CD_TRACK_N(toc, track) CD_TRACK(toc, track)[0]
+#define CD_TRACK_M(toc, track) CD_TRACK(toc, track)[3]
+#define CD_TRACK_S(toc, track) CD_TRACK(toc, track)[4]
+#define CD_TRACK_F(toc, track) CD_TRACK(toc, track)[5]
+
+/* Constants to help us get at the SCSI position info */
+#define POS_TRACK(pos) pos.position[6]
+#define POS_ABS_M(pos) pos.position[9]
+#define POS_ABS_S(pos) pos.position[10]
+#define POS_ABS_F(pos) pos.position[11]
+#define POS_REL_M(pos) pos.position[13]
+#define POS_REL_S(pos) pos.position[14]
+#define POS_REL_F(pos) pos.position[15]
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+int try_dir(const char *directory);
+
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive)
+{
+ struct stat stbuf;
+ int is_cd, cdfd;
+ device_geometry info;
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, &stbuf) < 0 ) {
+ return(-1);
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ is_cd = 0;
+ cdfd = open(drive, 0);
+ if ( cdfd >= 0 ) {
+ if ( ioctl(cdfd, B_GET_GEOMETRY, &info) == B_NO_ERROR ) {
+ if ( info.device_type == B_CD ) {
+ is_cd = 1;
+ }
+ }
+ close(cdfd);
+ } else {
+ /* This can happen when the drive is open .. (?) */;
+ is_cd = 1;
+ }
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive)
+{
+ int i;
+ size_t len;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ len = SDL_strlen(drive)+1;
+ SDL_cdlist[i] = (char *)SDL_malloc(len);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_strlcpy(SDL_cdlist[i], drive, len);
+ ++SDL_numcds;
+#ifdef CDROM_DEBUG
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+/* IDE bus scanning magic */
+enum {
+ IDE_GET_DEVICES_INFO = B_DEVICE_OP_CODES_END + 50,
+};
+struct ide_ctrl_info {
+ bool ide_0_present;
+ bool ide_0_master_present;
+ bool ide_0_slave_present;
+ int ide_0_master_type;
+ int ide_0_slave_type;
+ bool ide_1_present;
+ bool ide_1_master_present;
+ bool ide_1_slave_present;
+ int ide_1_master_type;
+ int ide_1_slave_type;
+};
+
+int SDL_SYS_CDInit(void)
+{
+ char *SDLcdrom;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+ if ( CheckDrive(SDLcdrom) > 0 ) {
+ AddDrive(SDLcdrom);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+
+ /* Scan the system for CD-ROM drives */
+ try_dir("/dev/disk");
+ return 0;
+}
+
+
+int try_dir(const char *directory)
+{
+ BDirectory dir;
+ dir.SetTo(directory);
+ if(dir.InitCheck() != B_NO_ERROR) {
+ return false;
+ }
+ dir.Rewind();
+ BEntry entry;
+ while(dir.GetNextEntry(&entry) >= 0) {
+ BPath path;
+ const char *name;
+ entry_ref e;
+
+ if(entry.GetPath(&path) != B_NO_ERROR)
+ continue;
+ name = path.Path();
+
+ if(entry.GetRef(&e) != B_NO_ERROR)
+ continue;
+
+ if(entry.IsDirectory()) {
+ if(SDL_strcmp(e.name, "floppy") == 0)
+ continue; /* ignore floppy (it is not silent) */
+ int devfd = try_dir(name);
+ if(devfd >= 0)
+ return devfd;
+ }
+ else {
+ int devfd;
+ device_geometry g;
+
+ if(SDL_strcmp(e.name, "raw") != 0)
+ continue; /* ignore partitions */
+
+ devfd = open(name, O_RDONLY);
+ if(devfd < 0)
+ continue;
+
+ if(ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) {
+ if(g.device_type == B_CD)
+ {
+ AddDrive(strdup(name));
+ }
+ }
+ close(devfd);
+ }
+ }
+ return B_ERROR;
+}
+
+
+/* General ioctl() CD-ROM command function */
+static int SDL_SYS_CDioctl(int index, int command, void *arg)
+{
+ int okay;
+ int fd;
+
+ okay = 0;
+ fd = open(SDL_cdlist[index], 0);
+ if ( fd >= 0 ) {
+ if ( ioctl(fd, command, arg) == B_NO_ERROR ) {
+ okay = 1;
+ }
+ close(fd);
+ }
+ return(okay ? 0 : -1);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(drive);
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ int i;
+ scsi_toc toc;
+
+ if ( SDL_SYS_CDioctl(cdrom->id, B_SCSI_GET_TOC, &toc) == 0 ) {
+ cdrom->numtracks = CD_NUMTRACKS(toc);
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+ for ( i=0; i<=cdrom->numtracks; ++i ) {
+ cdrom->track[i].id = CD_TRACK_N(toc, i);
+ /* FIXME: How do we tell on BeOS? */
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ CD_TRACK_M(toc, i),
+ CD_TRACK_S(toc, i),
+ CD_TRACK_F(toc, i));
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length =
+ cdrom->track[i].offset-
+ cdrom->track[i-1].offset;
+ }
+ }
+ return(0);
+ } else {
+ return(-1);
+ }
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ int fd;
+ int cur_frame;
+ scsi_position pos;
+
+ fd = open(SDL_cdlist[cdrom->id], 0);
+ cur_frame = 0;
+ if ( fd >= 0 ) {
+ if ( ioctl(fd, B_SCSI_GET_POSITION, &pos) == B_NO_ERROR ) {
+ cur_frame = MSF_TO_FRAMES(
+ POS_ABS_M(pos), POS_ABS_S(pos), POS_ABS_F(pos));
+ }
+ if ( ! pos.position[1] || (pos.position[1] >= 0x13) ||
+ ((pos.position[1] == 0x12) && (!pos.position[6])) ) {
+ status = CD_STOPPED;
+ } else
+ if ( pos.position[1] == 0x11 ) {
+ status = CD_PLAYING;
+ } else {
+ status = CD_PAUSED;
+ }
+ close(fd);
+ } else {
+ status = CD_TRAYEMPTY;
+ }
+ if ( position ) {
+ *position = cur_frame;
+ }
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ int okay;
+ int fd;
+ scsi_play_position pos;
+
+ okay = 0;
+ fd = open(SDL_cdlist[cdrom->id], 0);
+ if ( fd >= 0 ) {
+ FRAMES_TO_MSF(start, &pos.start_m, &pos.start_s, &pos.start_f);
+ FRAMES_TO_MSF(start+length, &pos.end_m, &pos.end_s, &pos.end_f);
+ if ( ioctl(fd, B_SCSI_PLAY_POSITION, &pos) == B_NO_ERROR ) {
+ okay = 1;
+ }
+ close(fd);
+ }
+ return(okay ? 0 : -1);
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_PAUSE_AUDIO, 0));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_RESUME_AUDIO, 0));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_STOP_AUDIO, 0));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_EJECT, 0));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_BEOS */
diff --git a/src/sdl_cdrom/SDL_syscdrom_bsdi.c b/src/sdl_cdrom/SDL_syscdrom_bsdi.c
new file mode 100644
index 000000000..da5c99c8f
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_bsdi.c
@@ -0,0 +1,547 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_BSDI 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_BSDI)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_BSDI*/
+
+/*
+ * Functions for system-level CD-ROM audio control for BSD/OS 4.x
+ * This started life out as a copy of the freebsd/SDL_cdrom.c file but was
+ * heavily modified. Works for standard (MMC) SCSI and ATAPI CDrom drives.
+ *
+ * Steven Schultz - sms@to.gd-es.com
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <err.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include </sys/dev/scsi/scsi.h>
+#include </sys/dev/scsi/scsi_ioctl.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/*
+ * The msf_to_frame and frame_to_msf were yanked from libcdrom and inlined
+ * here so that -lcdrom doesn't have to be dragged in for something so simple.
+*/
+
+#define FRAMES_PER_SECOND 75
+#define FRAMES_PER_MINUTE (FRAMES_PER_SECOND * 60)
+
+int
+msf_to_frame(int minute, int second, int frame)
+ {
+ return(minute * FRAMES_PER_MINUTE + second * FRAMES_PER_SECOND + frame);
+ }
+
+void
+frame_to_msf(int frame, int *minp, int *secp, int *framep)
+ {
+ *minp = frame / FRAMES_PER_MINUTE;
+ *secp = (frame % FRAMES_PER_MINUTE) / FRAMES_PER_SECOND;
+ *framep = frame % FRAMES_PER_SECOND;
+ }
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+typedef struct scsi_cdb cdb_t;
+
+static int scsi_cmd(int fd,
+ struct scsi_cdb *cdb,
+ int cdblen,
+ int rw,
+ caddr_t data,
+ int datalen,
+ struct scsi_user_cdb *sus)
+ {
+ int scsistatus;
+ unsigned char *cp;
+ struct scsi_user_cdb suc;
+
+ /* safety checks */
+ if (!cdb) return(-1);
+ if (rw != SUC_READ && rw != SUC_WRITE) return(-1);
+
+ suc.suc_flags = rw;
+ suc.suc_cdblen = cdblen;
+ bcopy(cdb, suc.suc_cdb, cdblen);
+ suc.suc_datalen = datalen;
+ suc.suc_data = data;
+ suc.suc_timeout = 10; /* 10 secs max for TUR or SENSE */
+ if (ioctl(fd, SCSIRAWCDB, &suc) == -1)
+ return(-11);
+ scsistatus = suc.suc_sus.sus_status;
+ cp = suc.suc_sus.sus_sense;
+
+/*
+ * If a place to copy the sense data back to has been provided then the
+ * caller is responsible for checking the errors and printing any information
+ * out if the status was not successful.
+*/
+ if (scsistatus != 0 && !sus)
+ {
+ fprintf(stderr,"scsistatus = %x cmd = %x\n",
+ scsistatus, cdb[0]);
+ fprintf(stderr, "sense %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5],
+ cp[6], cp[7], cp[8], cp[9], cp[10], cp[11],
+ cp[12], cp[13], cp[14], cp[15]);
+ return(1);
+ }
+ if (sus)
+ bcopy(&suc, sus, sizeof (struct scsi_user_cdb));
+ if (scsistatus)
+ return(1); /* Return non-zero for unsuccessful status */
+ return(0);
+ }
+
+/* request vendor brand and model */
+unsigned char *Inquiry(int fd)
+ {
+ static struct scsi_cdb6 cdb =
+ {
+ 0x12,
+ 0, 0, 0,
+ 56,
+ 0
+ };
+ static unsigned char Inqbuffer[56];
+
+ if (scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, Inqbuffer,
+ sizeof(Inqbuffer), 0))
+ return("\377");
+ return(Inqbuffer);
+ }
+
+#define ADD_SENSECODE 12
+#define ADD_SC_QUALIFIER 13
+
+int TestForMedium(int fd)
+ {
+ int sts, asc, ascq;
+ struct scsi_user_cdb sus;
+ static struct scsi_cdb6 cdb =
+ {
+ CMD_TEST_UNIT_READY, /* command */
+ 0, /* reserved */
+ 0, /* reserved */
+ 0, /* reserved */
+ 0, /* reserved */
+ 0 /* reserved */
+ };
+
+again: sts = scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, 0, 0, &sus);
+ asc = sus.suc_sus.sus_sense[ADD_SENSECODE];
+ ascq = sus.suc_sus.sus_sense[ADD_SC_QUALIFIER];
+ if (asc == 0x3a && ascq == 0x0) /* no medium */
+ return(0);
+ if (asc == 0x28 && ascq == 0x0) /* medium changed */
+ goto again;
+ if (asc == 0x4 && ascq == 0x1 ) /* coming ready */
+ {
+ sleep(2);
+ goto again;
+ }
+ return(1);
+ }
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive, struct stat *stbuf)
+{
+ int is_cd = 0, cdfd;
+ char *p;
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, stbuf) < 0 ) {
+ return(-1);
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
+ if ( cdfd >= 0 ) {
+ p = Inquiry(cdfd);
+ if (*p == TYPE_ROM)
+ is_cd = 1;
+ close(cdfd);
+ }
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Check to make sure it's not already in our list.
+ This can happen when we see a drive via symbolic link.
+ */
+ for ( i=0; i<SDL_numcds; ++i ) {
+ if ( stbuf->st_rdev == SDL_cdmode[i] ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
+#endif
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+int SDL_SYS_CDInit(void)
+{
+ /* checklist: /dev/rsr?c */
+ static char *checklist[] = {
+ "?0 rsr?", NULL
+ };
+ char *SDLcdrom;
+ int i, j, exists;
+ char drive[32];
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+ if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+
+ /* Scan the system for CD-ROM drives */
+ for ( i=0; checklist[i]; ++i ) {
+ if ( checklist[i][0] == '?' ) {
+ char *insert;
+ exists = 1;
+ for ( j=checklist[i][1]; exists; ++j ) {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%sc", &checklist[i][3]);
+ insert = SDL_strchr(drive, '?');
+ if ( insert != NULL ) {
+ *insert = j;
+ }
+ switch (CheckDrive(drive, &stbuf)) {
+ /* Drive exists and is a CD-ROM */
+ case 1:
+ AddDrive(drive, &stbuf);
+ break;
+ /* Drive exists, but isn't a CD-ROM */
+ case 0:
+ break;
+ /* Drive doesn't exist */
+ case -1:
+ exists = 0;
+ break;
+ }
+ }
+ } else {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
+ if ( CheckDrive(drive, &stbuf) > 0 ) {
+ AddDrive(drive, &stbuf);
+ }
+ }
+ }
+ return(0);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(open(SDL_cdlist[drive], O_RDONLY | O_NONBLOCK | O_EXCL, 0));
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+ {
+ u_char cdb[10], buf[4], *p, *toc;
+ struct scsi_user_cdb sus;
+ int i, sts, first_track, last_track, ntracks, toc_size;
+
+ bzero(cdb, sizeof (cdb));
+ cdb[0] = 0x43; /* Read TOC */
+ cdb[1] = 0x2; /* MSF */
+ cdb[8] = 4; /* size TOC header */
+ sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, 4, &sus);
+ if (sts < 0)
+ return(-1);
+ first_track = buf[2];
+ last_track = buf[3];
+ ntracks = last_track - first_track + 1;
+ cdrom->numtracks = ntracks;
+ toc_size = 4 + (ntracks + 1) * 8;
+ toc = (u_char *)SDL_malloc(toc_size);
+ if (toc == NULL)
+ return(-1);
+ bzero(cdb, sizeof (cdb));
+ cdb[0] = 0x43;
+ cdb[1] = 0x2;
+ cdb[6] = first_track;
+ cdb[7] = toc_size >> 8;
+ cdb[8] = toc_size & 0xff;
+ sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, toc, toc_size,
+ &sus);
+ if (sts < 0)
+ {
+ SDL_free(toc);
+ return(-1);
+ }
+
+ for (i = 0, p = toc+4; i <= ntracks; i++, p+= 8)
+ {
+ if (i == ntracks)
+ cdrom->track[i].id = 0xAA; /* Leadout */
+ else
+ cdrom->track[i].id = first_track + i;
+ if (p[1] & 0x20)
+ cdrom->track[i].type = SDL_DATA_TRACK;
+ else
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+ cdrom->track[i].offset = msf_to_frame(p[5], p[6], p[7]);
+ cdrom->track[i].length = 0;
+ if (i > 0)
+ cdrom->track[i-1].length = cdrom->track[i].offset -
+ cdrom->track[i-1].offset;
+ }
+ SDL_free(toc);
+ return(0);
+ }
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+ {
+ CDstatus status;
+ u_char cdb[10], buf[16];
+ int sts;
+ struct scsi_user_cdb sus;
+
+ bzero(cdb, sizeof (cdb));
+ cdb[0] = 0x42; /* read subq */
+ cdb[1] = 0x2; /* MSF */
+ cdb[2] = 0x40; /* q channel */
+ cdb[3] = 1; /* current pos */
+ cdb[7] = sizeof (buf) >> 8;
+ cdb[8] = sizeof (buf) & 0xff;
+ sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, sizeof (buf),
+ &sus);
+ if (sts < 0)
+ return(-1);
+ if (sts)
+ {
+ if (TestForMedium(cdrom->id) == 0)
+ status = CD_TRAYEMPTY;
+ else
+ status = CD_ERROR;
+ }
+ else
+ {
+ switch (buf[1])
+ {
+ case 0x11:
+ status = CD_PLAYING;
+ break;
+ case 0x12:
+ status = CD_PAUSED;
+ break;
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ status = CD_STOPPED;
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if (position)
+ {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) )
+ *position = msf_to_frame(buf[9], buf[10], buf[11]);
+ else
+ *position = 0;
+ }
+ return(status);
+ }
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+ {
+ u_char cdb[10];
+ int sts, minute, second, frame, eminute, esecond, eframe;
+ struct scsi_user_cdb sus;
+
+ bzero(cdb, sizeof(cdb));
+ cdb[0] = 0x47; /* Play */
+ frame_to_msf(start, &minute, &second, &frame);
+ frame_to_msf(start + length, &eminute, &esecond, &eframe);
+ cdb[3] = minute;
+ cdb[4] = second;
+ cdb[5] = frame;
+ cdb[6] = eminute;
+ cdb[7] = esecond;
+ cdb[8] = eframe;
+ sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus);
+ return(sts);
+ }
+
+static int
+pauseresume(SDL_CD *cdrom, int flag)
+ {
+ u_char cdb[10];
+ struct scsi_user_cdb sus;
+
+ bzero(cdb, sizeof (cdb));
+ cdb[0] = 0x4b;
+ cdb[8] = flag & 0x1;
+ return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus));
+ }
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return(pauseresume(cdrom, 0));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return(pauseresume(cdrom, 1));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ u_char cdb[6];
+ struct scsi_user_cdb sus;
+
+ bzero(cdb, sizeof (cdb));
+ cdb[0] = 0x1b; /* stop */
+ cdb[1] = 1; /* immediate */
+ return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ u_char cdb[6];
+ struct scsi_user_cdb sus;
+
+ bzero(cdb, sizeof (cdb));
+ cdb[0] = 0x1b; /* stop */
+ cdb[1] = 1; /* immediate */
+ cdb[4] = 2; /* eject */
+ return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+ {
+ close(cdrom->id);
+ }
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ }
+ SDL_numcds = 0;
+}
+
+#endif /* SDL_CDROM_BSDI */
diff --git a/src/sdl_cdrom/SDL_syscdrom_dc.c b/src/sdl_cdrom/SDL_syscdrom_dc.c
new file mode 100644
index 000000000..d4d6283f5
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_dc.c
@@ -0,0 +1,172 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_DC 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_DC)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_DC*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#include <dc/cdrom.h>
+#include <dc/spu.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+
+int SDL_SYS_CDInit(void)
+{
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ return(0);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return "/cd";
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(drive);
+}
+
+#define TRACK_CDDA 0
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ CDROM_TOC toc;
+ int ret,i;
+
+ ret = cdrom_read_toc(&toc,0);
+ if (ret!=ERR_OK) {
+ return -1;
+ }
+
+ cdrom->numtracks = TOC_TRACK(toc.last)-TOC_TRACK(toc.first)+1;
+ for(i=0;i<cdrom->numtracks;i++) {
+ unsigned long entry = toc.entry[i];
+ cdrom->track[i].id = i+1;
+ cdrom->track[i].type = (TOC_CTRL(toc.entry[i])==TRACK_CDDA)?SDL_AUDIO_TRACK:SDL_DATA_TRACK;
+ cdrom->track[i].offset = TOC_LBA(entry)-150;
+ cdrom->track[i].length = TOC_LBA((i+1<toc.last)?toc.entry[i+1]:toc.leadout_sector)-TOC_LBA(entry);
+ }
+
+ return 0;
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ int ret,dc_status,disc_type;
+
+ ret = cdrom_get_status(&dc_status,&disc_type);
+ if (ret!=ERR_OK) return CD_ERROR;
+
+ switch(dc_status) {
+// case CD_STATUS_BUSY:
+ case CD_STATUS_PAUSED:
+ return CD_PAUSED;
+ case CD_STATUS_STANDBY:
+ return CD_STOPPED;
+ case CD_STATUS_PLAYING:
+ return CD_PLAYING;
+// case CD_STATUS_SEEKING:
+// case CD_STATUS_SCANING:
+ case CD_STATUS_OPEN:
+ case CD_STATUS_NO_DISC:
+ return CD_TRAYEMPTY;
+ default:
+ return CD_ERROR;
+ }
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ int ret = cdrom_cdda_play(start-150,start-150+length,1,CDDA_SECTORS);
+ return ret==ERR_OK?0:-1;
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ int ret=cdrom_cdda_pause();
+ return ret==ERR_OK?0:-1;
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ int ret=cdrom_cdda_resume();
+ return ret==ERR_OK?0:-1;
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ int ret=cdrom_spin_down();
+ return ret==ERR_OK?0:-1;
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return -1;
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+}
+
+void SDL_SYS_CDQuit(void)
+{
+
+}
+
+#endif /* SDL_CDROM_DC */
diff --git a/src/sdl_cdrom/SDL_syscdrom_dummy.c b/src/sdl_cdrom/SDL_syscdrom_dummy.c
new file mode 100644
index 000000000..b32f9a8c8
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_dummy.c
@@ -0,0 +1,48 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_DUMMY 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_DUMMY)
+
+#include "SDL_config.h"
+
+/*#if defined(SDL_CDROM_DUMMY) || defined(SDL_CDROM_DISABLED)*/
+
+/* Stub functions for system-level CD-ROM audio control */
+
+/*
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+*/
+
+int SDL_SYS_CDInit(void)
+{
+ return(0);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ return;
+}
+
+#endif /* SDL_CDROM_DUMMY || SDL_CDROM_DISABLED */
diff --git a/src/sdl_cdrom/SDL_syscdrom_freebsd.c b/src/sdl_cdrom/SDL_syscdrom_freebsd.c
new file mode 100644
index 000000000..0fa2c45f8
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_freebsd.c
@@ -0,0 +1,411 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_FREEBSD 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_FREEBSD)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_FREEBSD*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/cdio.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+/* Some ioctl() errno values which occur when the tray is empty */
+#define ERRNO_TRAYEMPTY(errno) \
+ ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL))
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive, struct stat *stbuf)
+{
+ int is_cd, cdfd;
+ struct ioc_read_subchannel info;
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, stbuf) < 0 ) {
+ return(-1);
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ is_cd = 0;
+ if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
+ cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
+ if ( cdfd >= 0 ) {
+ info.address_format = CD_MSF_FORMAT;
+ info.data_format = CD_CURRENT_POSITION;
+ info.data_len = 0;
+ info.data = NULL;
+ /* Under Linux, EIO occurs when a disk is not present.
+ This isn't 100% reliable, so we use the USE_MNTENT
+ code above instead.
+ */
+ if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) ||
+ ERRNO_TRAYEMPTY(errno) ) {
+ is_cd = 1;
+ }
+ close(cdfd);
+ }
+ }
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Check to make sure it's not already in our list.
+ This can happen when we see a drive via symbolic link.
+ */
+ for ( i=0; i<SDL_numcds; ++i ) {
+ if ( stbuf->st_rdev == SDL_cdmode[i] ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
+#endif
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+int SDL_SYS_CDInit(void)
+{
+ /* checklist: /dev/cdrom,/dev/cd?c /dev/acd?c
+ /dev/matcd?c /dev/mcd?c /dev/scd?c */
+ static char *checklist[] = {
+ "cdrom", "?0 cd?", "?0 acd?", "?0 matcd?", "?0 mcd?", "?0 scd?",NULL
+ };
+ char *SDLcdrom;
+ int i, j, exists;
+ char drive[32];
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+ if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+
+ /* Scan the system for CD-ROM drives */
+ for ( i=0; checklist[i]; ++i ) {
+ if ( checklist[i][0] == '?' ) {
+ char *insert;
+ exists = 1;
+ for ( j=checklist[i][1]; exists; ++j ) {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
+ insert = SDL_strchr(drive, '?');
+ if ( insert != NULL ) {
+ *insert = j;
+ }
+ switch (CheckDrive(drive, &stbuf)) {
+ /* Drive exists and is a CD-ROM */
+ case 1:
+ AddDrive(drive, &stbuf);
+ break;
+ /* Drive exists, but isn't a CD-ROM */
+ case 0:
+ break;
+ /* Drive doesn't exist */
+ case -1:
+ exists = 0;
+ break;
+ }
+ }
+ } else {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
+ if ( CheckDrive(drive, &stbuf) > 0 ) {
+ AddDrive(drive, &stbuf);
+ }
+ }
+ }
+ return(0);
+}
+
+/* General ioctl() CD-ROM command function */
+static int SDL_SYS_CDioctl(int id, int command, void *arg)
+{
+ int retval;
+
+ retval = ioctl(id, command, arg);
+ if ( retval < 0 ) {
+ SDL_SetError("ioctl() error: %s", strerror(errno));
+ }
+ return(retval);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0));
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ struct ioc_toc_header toc;
+ int i, okay;
+ struct ioc_read_toc_entry entry;
+ struct cd_toc_entry data;
+
+ okay = 0;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) {
+ cdrom->numtracks = toc.ending_track-toc.starting_track+1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+ /* Read all the track TOC entries */
+ for ( i=0; i<=cdrom->numtracks; ++i ) {
+ if ( i == cdrom->numtracks ) {
+ cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */
+ } else {
+ cdrom->track[i].id = toc.starting_track+i;
+ }
+ entry.starting_track = cdrom->track[i].id;
+ entry.address_format = CD_MSF_FORMAT;
+ entry.data_len = sizeof(data);
+ entry.data = &data;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS,
+ &entry) < 0 ) {
+ break;
+ } else {
+ cdrom->track[i].type = data.control;
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ data.addr.msf.minute,
+ data.addr.msf.second,
+ data.addr.msf.frame);
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length =
+ cdrom->track[i].offset-
+ cdrom->track[i-1].offset;
+ }
+ }
+ }
+ if ( i == (cdrom->numtracks+1) ) {
+ okay = 1;
+ }
+ }
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ struct ioc_toc_header toc;
+ struct ioc_read_subchannel info;
+ struct cd_sub_channel_info data;
+
+ info.address_format = CD_MSF_FORMAT;
+ info.data_format = CD_CURRENT_POSITION;
+ info.track = 0;
+ info.data_len = sizeof(data);
+ info.data = &data;
+ if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) {
+ if ( ERRNO_TRAYEMPTY(errno) ) {
+ status = CD_TRAYEMPTY;
+ } else {
+ status = CD_ERROR;
+ }
+ } else {
+ switch (data.header.audio_status) {
+ case CD_AS_AUDIO_INVALID:
+ case CD_AS_NO_STATUS:
+ /* Try to determine if there's a CD available */
+ if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0)
+ status = CD_STOPPED;
+ else
+ status = CD_TRAYEMPTY;
+ break;
+ case CD_AS_PLAY_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case CD_AS_PLAY_IN_PROGRESS:
+ status = CD_PLAYING;
+ break;
+ case CD_AS_PLAY_PAUSED:
+ status = CD_PAUSED;
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if ( position ) {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
+ *position = MSF_TO_FRAMES(
+ data.what.position.absaddr.msf.minute,
+ data.what.position.absaddr.msf.second,
+ data.what.position.absaddr.msf.frame);
+ } else {
+ *position = 0;
+ }
+ }
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ struct ioc_play_msf playtime;
+
+ FRAMES_TO_MSF(start,
+ &playtime.start_m, &playtime.start_s, &playtime.start_f);
+ FRAMES_TO_MSF(start+length,
+ &playtime.end_m, &playtime.end_s, &playtime.end_f);
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
+ playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
+ playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
+#endif
+ ioctl(cdrom->id, CDIOCSTART, 0);
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime));
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_FREEBSD */
diff --git a/src/sdl_cdrom/SDL_syscdrom_linux.c b/src/sdl_cdrom/SDL_syscdrom_linux.c
new file mode 100644
index 000000000..f05ea376f
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_linux.c
@@ -0,0 +1,569 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_LINUX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_LINUX)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_LINUX*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#include <string.h> /* For strerror() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#ifdef __LINUX__
+#ifdef HAVE_LINUX_VERSION_H
+/* linux 2.6.9 workaround */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,9)
+#include <asm/types.h>
+#define __le64 __u64
+#define __le32 __u32
+#define __le16 __u16
+#define __be64 __u64
+#define __be32 __u32
+#define __be16 __u16
+#endif /* linux 2.6.9 workaround */
+#endif /* HAVE_LINUX_VERSION_H */
+#include <linux/cdrom.h>
+#endif
+#ifdef __SVR4
+#include <sys/cdio.h>
+#endif
+
+/* Define this to use the alternative getmntent() code */
+#ifndef __SVR4
+#define USE_MNTENT
+#endif
+
+#ifdef USE_MNTENT
+#if defined(__USLC__)
+#include <sys/mntent.h>
+#else
+#include <mntent.h>
+#endif
+
+#ifndef _PATH_MNTTAB
+#ifdef MNTTAB
+#define _PATH_MNTTAB MNTTAB
+#else
+#define _PATH_MNTTAB "/etc/fstab"
+#endif
+#endif /* !_PATH_MNTTAB */
+
+#ifndef _PATH_MOUNTED
+#define _PATH_MOUNTED "/etc/mtab"
+#endif /* !_PATH_MOUNTED */
+
+#ifndef MNTTYPE_CDROM
+#define MNTTYPE_CDROM "iso9660"
+#endif
+#ifndef MNTTYPE_SUPER
+#define MNTTYPE_SUPER "supermount"
+#endif
+#endif /* USE_MNTENT */
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+/* Some ioctl() errno values which occur when the tray is empty */
+#ifndef ENOMEDIUM
+#define ENOMEDIUM ENOENT
+#endif
+#define ERRNO_TRAYEMPTY(errno) \
+ ((errno == EIO) || (errno == ENOENT) || \
+ (errno == EINVAL) || (errno == ENOMEDIUM))
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive, char *mnttype, struct stat *stbuf)
+{
+ int is_cd, cdfd;
+ struct cdrom_subchnl info;
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, stbuf) < 0 ) {
+ return(-1);
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ is_cd = 0;
+ if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
+ cdfd = open(drive, (O_RDONLY|O_NONBLOCK), 0);
+ if ( cdfd >= 0 ) {
+ info.cdsc_format = CDROM_MSF;
+ /* Under Linux, EIO occurs when a disk is not present.
+ */
+ if ( (ioctl(cdfd, CDROMSUBCHNL, &info) == 0) ||
+ ERRNO_TRAYEMPTY(errno) ) {
+ is_cd = 1;
+ }
+ close(cdfd);
+ }
+#ifdef USE_MNTENT
+ /* Even if we can't read it, it might be mounted */
+ else if ( mnttype && (SDL_strcmp(mnttype, MNTTYPE_CDROM) == 0) ) {
+ is_cd = 1;
+ }
+#endif
+ }
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Check to make sure it's not already in our list.
+ This can happen when we see a drive via symbolic link.
+ */
+ for ( i=0; i<SDL_numcds; ++i ) {
+ if ( stbuf->st_rdev == SDL_cdmode[i] ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
+#endif
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+#ifdef USE_MNTENT
+static void CheckMounts(const char *mtab)
+{
+ FILE *mntfp;
+ struct mntent *mntent;
+ struct stat stbuf;
+
+ mntfp = setmntent(mtab, "r");
+ if ( mntfp != NULL ) {
+ char *tmp;
+ char *mnt_type;
+ size_t mnt_type_len;
+ char *mnt_dev;
+ size_t mnt_dev_len;
+
+ while ( (mntent=getmntent(mntfp)) != NULL ) {
+ mnt_type_len = SDL_strlen(mntent->mnt_type) + 1;
+ mnt_type = SDL_stack_alloc(char, mnt_type_len);
+ if (mnt_type == NULL)
+ continue; /* maybe you'll get lucky next time. */
+
+ mnt_dev_len = SDL_strlen(mntent->mnt_fsname) + 1;
+ mnt_dev = SDL_stack_alloc(char, mnt_dev_len);
+ if (mnt_dev == NULL) {
+ SDL_stack_free(mnt_type);
+ continue;
+ }
+
+ SDL_strlcpy(mnt_type, mntent->mnt_type, mnt_type_len);
+ SDL_strlcpy(mnt_dev, mntent->mnt_fsname, mnt_dev_len);
+
+ /* Handle "supermount" filesystem mounts */
+ if ( SDL_strcmp(mnt_type, MNTTYPE_SUPER) == 0 ) {
+ tmp = SDL_strstr(mntent->mnt_opts, "fs=");
+ if ( tmp ) {
+ SDL_stack_free(mnt_type);
+ mnt_type = SDL_strdup(tmp + SDL_strlen("fs="));
+ if ( mnt_type ) {
+ tmp = SDL_strchr(mnt_type, ',');
+ if ( tmp ) {
+ *tmp = '\0';
+ }
+ }
+ }
+ tmp = SDL_strstr(mntent->mnt_opts, "dev=");
+ if ( tmp ) {
+ SDL_stack_free(mnt_dev);
+ mnt_dev = SDL_strdup(tmp + SDL_strlen("dev="));
+ if ( mnt_dev ) {
+ tmp = SDL_strchr(mnt_dev, ',');
+ if ( tmp ) {
+ *tmp = '\0';
+ }
+ }
+ }
+ }
+ if ( SDL_strcmp(mnt_type, MNTTYPE_CDROM) == 0 ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Checking mount path from %s: %s mounted on %s of %s\n",
+ mtab, mnt_dev, mntent->mnt_dir, mnt_type);
+#endif
+ if (CheckDrive(mnt_dev, mnt_type, &stbuf) > 0) {
+ AddDrive(mnt_dev, &stbuf);
+ }
+ }
+ SDL_stack_free(mnt_dev);
+ SDL_stack_free(mnt_type);
+ }
+ endmntent(mntfp);
+ }
+}
+#endif /* USE_MNTENT */
+
+int SDL_SYS_CDInit(void)
+{
+ /* checklist: /dev/cdrom, /dev/hd?, /dev/scd? /dev/sr? */
+ static char *checklist[] = {
+ "cdrom", "?a hd?", "?0 scd?", "?0 sr?", NULL
+ };
+ char *SDLcdrom;
+ int i, j, exists;
+ char drive[32];
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom);
+#endif
+ if ( CheckDrive(SDLcdrom, NULL, &stbuf) > 0 ) {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+
+#ifdef USE_MNTENT
+ /* Check /dev/cdrom first :-) */
+ if (CheckDrive("/dev/cdrom", NULL, &stbuf) > 0) {
+ AddDrive("/dev/cdrom", &stbuf);
+ }
+
+ /* Now check the currently mounted CD drives */
+ CheckMounts(_PATH_MOUNTED);
+
+ /* Finally check possible mountable drives in /etc/fstab */
+ CheckMounts(_PATH_MNTTAB);
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+#endif /* USE_MNTENT */
+
+ /* Scan the system for CD-ROM drives.
+ Not always 100% reliable, so use the USE_MNTENT code above first.
+ */
+ for ( i=0; checklist[i]; ++i ) {
+ if ( checklist[i][0] == '?' ) {
+ char *insert;
+ exists = 1;
+ for ( j=checklist[i][1]; exists; ++j ) {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
+ insert = SDL_strchr(drive, '?');
+ if ( insert != NULL ) {
+ *insert = j;
+ }
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive);
+#endif
+ switch (CheckDrive(drive, NULL, &stbuf)) {
+ /* Drive exists and is a CD-ROM */
+ case 1:
+ AddDrive(drive, &stbuf);
+ break;
+ /* Drive exists, but isn't a CD-ROM */
+ case 0:
+ break;
+ /* Drive doesn't exist */
+ case -1:
+ exists = 0;
+ break;
+ }
+ }
+ } else {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive);
+#endif
+ if ( CheckDrive(drive, NULL, &stbuf) > 0 ) {
+ AddDrive(drive, &stbuf);
+ }
+ }
+ }
+ return(0);
+}
+
+/* General ioctl() CD-ROM command function */
+static int SDL_SYS_CDioctl(int id, int command, void *arg)
+{
+ int retval;
+
+ retval = ioctl(id, command, arg);
+ if ( retval < 0 ) {
+ SDL_SetError("ioctl() error: %s", strerror(errno));
+ }
+ return(retval);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(open(SDL_cdlist[drive], (O_RDONLY|O_NONBLOCK), 0));
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ struct cdrom_tochdr toc;
+ int i, okay;
+ struct cdrom_tocentry entry;
+
+ okay = 0;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc) == 0 ) {
+ cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+ /* Read all the track TOC entries */
+ for ( i=0; i<=cdrom->numtracks; ++i ) {
+ if ( i == cdrom->numtracks ) {
+ cdrom->track[i].id = CDROM_LEADOUT;
+ } else {
+ cdrom->track[i].id = toc.cdth_trk0+i;
+ }
+ entry.cdte_track = cdrom->track[i].id;
+ entry.cdte_format = CDROM_MSF;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY,
+ &entry) < 0 ) {
+ break;
+ } else {
+ if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) {
+ cdrom->track[i].type = SDL_DATA_TRACK;
+ } else {
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+ }
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ entry.cdte_addr.msf.minute,
+ entry.cdte_addr.msf.second,
+ entry.cdte_addr.msf.frame);
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length =
+ cdrom->track[i].offset-
+ cdrom->track[i-1].offset;
+ }
+ }
+ }
+ if ( i == (cdrom->numtracks+1) ) {
+ okay = 1;
+ }
+ }
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ struct cdrom_tochdr toc;
+ struct cdrom_subchnl info;
+
+ info.cdsc_format = CDROM_MSF;
+ if ( ioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) {
+ if ( ERRNO_TRAYEMPTY(errno) ) {
+ status = CD_TRAYEMPTY;
+ } else {
+ status = CD_ERROR;
+ }
+ } else {
+ switch (info.cdsc_audiostatus) {
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_NO_STATUS:
+ /* Try to determine if there's a CD available */
+ if (ioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0)
+ status = CD_STOPPED;
+ else
+ status = CD_TRAYEMPTY;
+ break;
+ case CDROM_AUDIO_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case CDROM_AUDIO_PLAY:
+ status = CD_PLAYING;
+ break;
+ case CDROM_AUDIO_PAUSED:
+ /* Workaround buggy CD-ROM drive */
+ if ( info.cdsc_trk == CDROM_LEADOUT ) {
+ status = CD_STOPPED;
+ } else {
+ status = CD_PAUSED;
+ }
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if ( position ) {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
+ *position = MSF_TO_FRAMES(
+ info.cdsc_absaddr.msf.minute,
+ info.cdsc_absaddr.msf.second,
+ info.cdsc_absaddr.msf.frame);
+ } else {
+ *position = 0;
+ }
+ }
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ struct cdrom_msf playtime;
+
+ FRAMES_TO_MSF(start,
+ &playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0);
+ FRAMES_TO_MSF(start+length,
+ &playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1);
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
+ playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
+ playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
+#endif
+ return(SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime));
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_LINUX */
diff --git a/src/sdl_cdrom/SDL_syscdrom_mint.c b/src/sdl_cdrom/SDL_syscdrom_mint.c
new file mode 100644
index 000000000..48496d61f
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_mint.c
@@ -0,0 +1,324 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MINT 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MINT)
+
+#include "SDL_config.h"
+#include "SDL_stdinc.h"
+
+/*#ifdef SDL_CDROM_MINT*/
+
+/*
+ Atari MetaDOS CD-ROM functions
+
+ Patrice Mandin
+*/
+
+#include <errno.h>
+
+#include <mint/cdromio.h>
+#include <mint/metados.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* Some ioctl() errno values which occur when the tray is empty */
+#ifndef ENOMEDIUM
+#define ENOMEDIUM ENOENT
+#endif
+#define ERRNO_TRAYEMPTY(errno) \
+ ((errno == EIO) || (errno == ENOENT) || \
+ (errno == EINVAL) || (errno == ENOMEDIUM))
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 32
+
+typedef struct {
+ char device[3]; /* Physical device letter + ':' + '\0' */
+ metaopen_t metaopen; /* Infos on opened drive */
+} metados_drive_t;
+
+static metados_drive_t metados_drives[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+static int SDL_SYS_CDioctl(int id, int command, void *arg);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+
+int SDL_SYS_CDInit(void)
+{
+ metainit_t metainit={0,0,0,0};
+ metaopen_t metaopen;
+ int i, handle;
+ struct cdrom_subchnl info;
+
+ SDL_numcds = 0;
+ SDL_memset(metados_drives, 0, sizeof(metados_drives));
+
+ Metainit(&metainit);
+ if (metainit.version == NULL) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "MetaDOS not installed\n");
+#endif
+ return 0;
+ }
+
+ if (metainit.drives_map == 0) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "No MetaDOS devices present\n");
+#endif
+ return 0;
+ }
+
+ for (i='A'; i<='Z'; i++) {
+ metados_drives[SDL_numcds].device[0] = 0;
+ metados_drives[SDL_numcds].device[1] = ':';
+ metados_drives[SDL_numcds].device[2] = 0;
+
+ if (metainit.drives_map & (1<<(i-'A'))) {
+ handle = Metaopen(i, &metaopen);
+ if (handle == 0) {
+
+ info.cdsc_format = CDROM_MSF;
+ if ( (Metaioctl(i, METADOS_IOCTL_MAGIC, CDROMSUBCHNL, &info) == 0) || ERRNO_TRAYEMPTY(errno) ) {
+ metados_drives[SDL_numcds].device[0] = i;
+ ++SDL_numcds;
+ }
+
+ Metaclose(i);
+ }
+ }
+ }
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+
+ return 0;
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ SDL_numcds = 0;
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(metados_drives[drive].device);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ int handle;
+
+ handle = Metaopen(metados_drives[drive].device[0], &(metados_drives[drive].metaopen));
+ if (handle == 0) {
+ return drive;
+ }
+
+ return -1;
+}
+
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ Metaclose(metados_drives[cdrom->id].device[0]);
+}
+
+static int SDL_SYS_CDioctl(int id, int command, void *arg)
+{
+ int retval;
+
+ retval = Metaioctl(metados_drives[id].device[0], METADOS_IOCTL_MAGIC, command, arg);
+ if ( retval < 0 ) {
+ SDL_SetError("ioctl() error: %s", strerror(errno));
+ }
+ return(retval);
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ int i,okay;
+ struct cdrom_tochdr toc;
+ struct cdrom_tocentry entry;
+
+ /* Use standard ioctl() */
+ if (SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc)<0) {
+ return -1;
+ }
+
+ cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+
+ /* Read all the track TOC entries */
+ okay=1;
+ for ( i=0; i<=cdrom->numtracks; ++i ) {
+ if ( i == cdrom->numtracks ) {
+ cdrom->track[i].id = CDROM_LEADOUT;
+ } else {
+ cdrom->track[i].id = toc.cdth_trk0+i;
+ }
+ entry.cdte_track = cdrom->track[i].id;
+ entry.cdte_format = CDROM_MSF;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY, &entry) < 0 ) {
+ okay=0;
+ break;
+ } else {
+ if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) {
+ cdrom->track[i].type = SDL_DATA_TRACK;
+ } else {
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+ }
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ entry.cdte_addr.msf.minute,
+ entry.cdte_addr.msf.second,
+ entry.cdte_addr.msf.frame);
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
+ }
+ }
+ }
+
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ struct cdrom_tochdr toc;
+ struct cdrom_subchnl info;
+
+ info.cdsc_format = CDROM_MSF;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) {
+ if ( ERRNO_TRAYEMPTY(errno) ) {
+ status = CD_TRAYEMPTY;
+ } else {
+ status = CD_ERROR;
+ }
+ } else {
+ switch (info.cdsc_audiostatus) {
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_NO_STATUS:
+ /* Try to determine if there's a CD available */
+ if (SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0) {
+ status = CD_STOPPED;
+ } else {
+ status = CD_TRAYEMPTY;
+ }
+ break;
+ case CDROM_AUDIO_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case CDROM_AUDIO_PLAY:
+ status = CD_PLAYING;
+ break;
+ case CDROM_AUDIO_PAUSED:
+ /* Workaround buggy CD-ROM drive */
+ if ( info.cdsc_trk == CDROM_LEADOUT ) {
+ status = CD_STOPPED;
+ } else {
+ status = CD_PAUSED;
+ }
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if ( position ) {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
+ *position = MSF_TO_FRAMES(
+ info.cdsc_absaddr.msf.minute,
+ info.cdsc_absaddr.msf.second,
+ info.cdsc_absaddr.msf.frame);
+ } else {
+ *position = 0;
+ }
+ }
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ struct cdrom_msf playtime;
+
+ FRAMES_TO_MSF(start,
+ &playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0);
+ FRAMES_TO_MSF(start+length,
+ &playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1);
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
+ playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
+ playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
+#endif
+
+ return SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime);
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0);
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0);
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0);
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0);
+}
+
+#endif /* SDL_CDROM_MINT */
diff --git a/src/sdl_cdrom/SDL_syscdrom_openbsd.c b/src/sdl_cdrom/SDL_syscdrom_openbsd.c
new file mode 100644
index 000000000..44f36813e
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_openbsd.c
@@ -0,0 +1,421 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_OPENBSD 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_OPENBSD)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_OPENBSD*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+/* Some ioctl() errno values which occur when the tray is empty */
+#define ERRNO_TRAYEMPTY(errno) \
+ ((errno == EIO) || (errno == ENOENT) || (errno == EINVAL) || \
+ (errno == ENODEV))
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive, struct stat *stbuf)
+{
+ int is_cd, cdfd;
+ struct ioc_read_subchannel info;
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, stbuf) < 0 ) {
+ return(-1);
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ is_cd = 0;
+ if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
+ cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
+ if ( cdfd >= 0 ) {
+ info.address_format = CD_MSF_FORMAT;
+ info.data_format = CD_CURRENT_POSITION;
+ info.data_len = 0;
+ info.data = NULL;
+ /* Under Linux, EIO occurs when a disk is not present.
+ This isn't 100% reliable, so we use the USE_MNTENT
+ code above instead.
+ */
+ if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) ||
+ ERRNO_TRAYEMPTY(errno) ) {
+ is_cd = 1;
+ }
+ close(cdfd);
+ }
+ else if (ERRNO_TRAYEMPTY(errno))
+ is_cd = 1;
+ }
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Check to make sure it's not already in our list.
+ This can happen when we see a drive via symbolic link.
+ */
+ for ( i=0; i<SDL_numcds; ++i ) {
+ if ( stbuf->st_rdev == SDL_cdmode[i] ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
+#endif
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+int SDL_SYS_CDInit(void)
+{
+ static char *checklist[] = {
+#if defined(__OPENBSD__)
+ "?0 cd?c", "cdrom", NULL
+#elif defined(__NETBSD__)
+ "?0 cd?d", "?0 cd?c", "cdrom", NULL
+#else
+ "?0 cd?c", "?0 acd?c", "cdrom", NULL
+#endif
+ };
+ char *SDLcdrom;
+ int i, j, exists;
+ char drive[32];
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+ if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+
+ /* Scan the system for CD-ROM drives */
+ for ( i=0; checklist[i]; ++i ) {
+ if ( checklist[i][0] == '?' ) {
+ char *insert;
+ exists = 1;
+ for ( j=checklist[i][1]; exists; ++j ) {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
+ insert = SDL_strchr(drive, '?');
+ if ( insert != NULL ) {
+ *insert = j;
+ }
+ switch (CheckDrive(drive, &stbuf)) {
+ /* Drive exists and is a CD-ROM */
+ case 1:
+ AddDrive(drive, &stbuf);
+ break;
+ /* Drive exists, but isn't a CD-ROM */
+ case 0:
+ break;
+ /* Drive doesn't exist */
+ case -1:
+ exists = 0;
+ break;
+ }
+ }
+ } else {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
+ if ( CheckDrive(drive, &stbuf) > 0 ) {
+ AddDrive(drive, &stbuf);
+ }
+ }
+ }
+ return(0);
+}
+
+/* General ioctl() CD-ROM command function */
+static int SDL_SYS_CDioctl(int id, int command, void *arg)
+{
+ int retval;
+
+ retval = ioctl(id, command, arg);
+ if ( retval < 0 ) {
+ SDL_SetError("ioctl() error: %s", strerror(errno));
+ }
+ return(retval);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0));
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ struct ioc_toc_header toc;
+ int i, okay;
+ struct ioc_read_toc_entry entry;
+ struct cd_toc_entry data;
+
+ okay = 0;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) {
+ cdrom->numtracks = toc.ending_track-toc.starting_track+1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+ /* Read all the track TOC entries */
+ for ( i=0; i<=cdrom->numtracks; ++i ) {
+ if ( i == cdrom->numtracks ) {
+ cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */
+ } else {
+ cdrom->track[i].id = toc.starting_track+i;
+ }
+ entry.starting_track = cdrom->track[i].id;
+ entry.address_format = CD_MSF_FORMAT;
+ entry.data_len = sizeof(data);
+ entry.data = &data;
+ if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS,
+ &entry) < 0 ) {
+ break;
+ } else {
+ cdrom->track[i].type = data.control;
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ data.addr.msf.minute,
+ data.addr.msf.second,
+ data.addr.msf.frame);
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length =
+ cdrom->track[i].offset-
+ cdrom->track[i-1].offset;
+ }
+ }
+ }
+ if ( i == (cdrom->numtracks+1) ) {
+ okay = 1;
+ }
+ }
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ struct ioc_toc_header toc;
+ struct ioc_read_subchannel info;
+ struct cd_sub_channel_info data;
+
+ info.address_format = CD_MSF_FORMAT;
+ info.data_format = CD_CURRENT_POSITION;
+ info.track = 0;
+ info.data_len = sizeof(data);
+ info.data = &data;
+ if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) {
+ if ( ERRNO_TRAYEMPTY(errno) ) {
+ status = CD_TRAYEMPTY;
+ } else {
+ status = CD_ERROR;
+ }
+ } else {
+ switch (data.header.audio_status) {
+ case CD_AS_AUDIO_INVALID:
+ case CD_AS_NO_STATUS:
+ /* Try to determine if there's a CD available */
+ if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0)
+ status = CD_STOPPED;
+ else
+ status = CD_TRAYEMPTY;
+ break;
+ case CD_AS_PLAY_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case CD_AS_PLAY_IN_PROGRESS:
+ status = CD_PLAYING;
+ break;
+ case CD_AS_PLAY_PAUSED:
+ status = CD_PAUSED;
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if ( position ) {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
+ *position = MSF_TO_FRAMES(
+ data.what.position.absaddr.msf.minute,
+ data.what.position.absaddr.msf.second,
+ data.what.position.absaddr.msf.frame);
+ } else {
+ *position = 0;
+ }
+ }
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ struct ioc_play_msf playtime;
+
+ FRAMES_TO_MSF(start,
+ &playtime.start_m, &playtime.start_s, &playtime.start_f);
+ FRAMES_TO_MSF(start+length,
+ &playtime.end_m, &playtime.end_s, &playtime.end_f);
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
+ playtime.start_m, playtime.start_s, playtime.start_f,
+ playtime.end_m, playtime.end_s, playtime.end_f);
+#endif
+ ioctl(cdrom->id, CDIOCSTART, 0);
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime));
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ SDL_SYS_CDioctl(cdrom->id, CDIOCALLOW, 0);
+ return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_OPENBSD */
diff --git a/src/sdl_cdrom/SDL_syscdrom_os2.c b/src/sdl_cdrom/SDL_syscdrom_os2.c
new file mode 100644
index 000000000..aa69ef837
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_os2.c
@@ -0,0 +1,398 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_OS2 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_OS2)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_OS2*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#define INCL_MCIOS2
+#include <os2.h>
+#include <os2me.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* Size of MCI result buffer (in bytes) */
+#define MCI_CMDRETBUFSIZE 128
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+//static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+/* MCI Timing Functions */
+#define MCI_MMTIMEPERSECOND 3000
+#define FRAMESFROMMM(mmtime) (((mmtime)*CD_FPS)/MCI_MMTIMEPERSECOND)
+
+
+/* Ready for MCI CDAudio Devices */
+int SDL_SYS_CDInit(void)
+{
+int i; /* generig counter */
+MCI_SYSINFO_PARMS msp; /* Structure to MCI SysInfo parameters */
+CHAR SysInfoRet[MCI_CMDRETBUFSIZE]; /* Buffer for MCI Command result */
+
+/* Fill in our driver capabilities */
+SDL_CDcaps.Name = SDL_SYS_CDName;
+SDL_CDcaps.Open = SDL_SYS_CDOpen;
+SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+SDL_CDcaps.Status = SDL_SYS_CDStatus;
+SDL_CDcaps.Play = SDL_SYS_CDPlay;
+SDL_CDcaps.Pause = SDL_SYS_CDPause;
+SDL_CDcaps.Resume = SDL_SYS_CDResume;
+SDL_CDcaps.Stop = SDL_SYS_CDStop;
+SDL_CDcaps.Eject = SDL_SYS_CDEject;
+SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+/* Get the number of CD ROMs in the System */
+/* Clean SysInfo structure */
+SDL_memset(&msp, 0x00, sizeof(MCI_SYSINFO_PARMS));
+/* Prepare structure to Ask Numer of Audio CDs */
+msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO; /* CD Audio Type */
+msp.pszReturn = (PSZ)&SysInfoRet; /* Return Structure */
+msp.ulRetSize = MCI_CMDRETBUFSIZE; /* Size of ret struct */
+if (LOUSHORT(mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_QUANTITY | MCI_WAIT, (PVOID)&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
+SDL_numcds = atoi(SysInfoRet);
+if (SDL_numcds > MAX_DRIVES) SDL_numcds = MAX_DRIVES; /* Limit maximum CD number */
+
+/* Get and Add their system name to the SDL_cdlist */
+msp.pszReturn = (PSZ)&SysInfoRet; /* Return Structure */
+msp.ulRetSize = MCI_CMDRETBUFSIZE; /* Size of ret struct */
+msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO; /* CD Audio Type */
+for (i=0; i<SDL_numcds; i++)
+ {
+ msp.ulNumber = i+1;
+ mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_WAIT,&msp, 0);
+ SDL_cdlist[i] = SDL_strdup(SysInfoRet);
+ if ( SDL_cdlist[i] == NULL )
+ {
+ SDL_OutOfMemory();
+ return(-1);
+ }
+ }
+return(0);
+}
+
+/* Return CDAudio System Dependent Device Name - Ready for MCI*/
+static const char *SDL_SYS_CDName(int drive)
+{
+return(SDL_cdlist[drive]);
+}
+
+/* Open CDAudio Device - Ready for MCI */
+static int SDL_SYS_CDOpen(int drive)
+{
+MCI_OPEN_PARMS mop;
+MCI_SET_PARMS msp;
+MCI_GENERIC_PARMS mgp;
+
+/* Open the device */
+mop.hwndCallback = (HWND)NULL; // None
+mop.usDeviceID = (USHORT)NULL; // Will be returned.
+mop.pszDeviceType = (PSZ)SDL_cdlist[drive]; // CDAudio Device
+if (LOUSHORT(mciSendCommand(0,MCI_OPEN,MCI_WAIT,&mop, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
+/* Set time format */
+msp.hwndCallback = (HWND)NULL; // None
+msp.ulTimeFormat = MCI_FORMAT_MSF; // Minute : Second : Frame structure
+msp.ulSpeedFormat = (ULONG)NULL; // No change
+msp.ulAudio = (ULONG)NULL; // No Channel
+msp.ulLevel = (ULONG)NULL; // No Volume
+msp.ulOver = (ULONG)NULL; // No Delay
+msp.ulItem = (ULONG)NULL; // No item
+msp.ulValue = (ULONG)NULL; // No value for item flag
+if (LOUSHORT(mciSendCommand(mop.usDeviceID,MCI_SET,MCI_WAIT | MCI_SET_TIME_FORMAT,&msp, 0)) == MCIERR_SUCCESS) return (mop.usDeviceID);
+/* Error setting time format? - Close opened device */
+mgp.hwndCallback = (HWND)NULL; // None
+mciSendCommand(mop.usDeviceID,MCI_CLOSE,MCI_WAIT,&mgp, 0);
+return(CD_ERROR);
+}
+
+/* Get CD Table Of Contents - Ready for MCI */
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+MCI_TOC_PARMS mtp;
+MCI_STATUS_PARMS msp;
+MCI_TOC_REC * mtr;
+INT i;
+
+/* Correction because MCI cannot read TOC while CD is playing (it'll stop!) */
+if (cdrom->status == CD_PLAYING || cdrom->status == CD_PAUSED) return 0;
+
+/* Get Number of Tracks */
+msp.hwndCallback = (HWND)NULL; /* None */
+msp.ulReturn = (ULONG)NULL; /* We want this information */
+msp.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
+msp.ulValue = (ULONG)NULL; /* No additional information */
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
+cdrom->numtracks = msp.ulReturn;
+if ( cdrom->numtracks > SDL_MAX_TRACKS )
+ {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+/* Alocate space for TOC data */
+mtr = (MCI_TOC_REC *)SDL_malloc(cdrom->numtracks*sizeof(MCI_TOC_REC));
+if ( mtr == NULL )
+ {
+ SDL_OutOfMemory();
+ return(-1);
+ }
+/* Get TOC from CD */
+mtp.pBuf = mtr;
+mtp.ulBufSize = cdrom->numtracks*sizeof(MCI_TOC_REC);
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_GETTOC,MCI_WAIT,&mtp, 0)) != MCIERR_SUCCESS)
+ {
+ SDL_OutOfMemory();
+ SDL_free(mtr);
+ return(CD_ERROR);
+ }
+/* Fill SDL Tracks Structure */
+for (i=0; i<cdrom->numtracks; i++)
+ {
+ /* Set Track ID */
+ cdrom->track[i].id = (mtr+i)->TrackNum;
+ /* Set Track Type */
+ msp.hwndCallback = (HWND)NULL; /* None */
+ msp.ulReturn = (ULONG)NULL; /* We want this information */
+ msp.ulItem = MCI_CD_STATUS_TRACK_TYPE;
+ msp.ulValue = (ULONG)((mtr+i)->TrackNum); /* Track Number? */
+ if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_TRACK | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS)
+ {
+ SDL_free(mtr);
+ return (CD_ERROR);
+ }
+ if (msp.ulReturn==MCI_CD_TRACK_AUDIO) cdrom->track[i].type = SDL_AUDIO_TRACK;
+ else cdrom->track[i].type = SDL_DATA_TRACK;
+ /* Set Track Length - values from MCI are in MMTIMEs - 3000 MMTIME = 1 second */
+ cdrom->track[i].length = FRAMESFROMMM((mtr+i)->ulEndAddr - (mtr+i)->ulStartAddr);
+ /* Set Track Offset */
+ cdrom->track[i].offset = FRAMESFROMMM((mtr+i)->ulStartAddr);
+ }
+SDL_free(mtr);
+return(0);
+}
+
+
+/* Get CD-ROM status - Ready for MCI */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+CDstatus status;
+MCI_STATUS_PARMS msp;
+
+/* Get Status from MCI */
+msp.hwndCallback = (HWND)NULL; /* None */
+msp.ulReturn = (ULONG)NULL; /* We want this information */
+msp.ulItem = MCI_STATUS_MODE;
+msp.ulValue = (ULONG)NULL; /* No additional information */
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) status = CD_ERROR;
+else
+ {
+ switch(msp.ulReturn)
+ {
+ case MCI_MODE_NOT_READY:
+ status = CD_TRAYEMPTY;
+ break;
+ case MCI_MODE_PAUSE:
+ status = CD_PAUSED;
+ break;
+ case MCI_MODE_PLAY:
+ status = CD_PLAYING;
+ break;
+ case MCI_MODE_STOP:
+ status = CD_STOPPED;
+ break;
+ /* These cases should not occour */
+ case MCI_MODE_RECORD:
+ case MCI_MODE_SEEK:
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+
+/* Determine position */
+if (position != NULL) /* The SDL $&$&%# CDROM call sends NULL pointer here! */
+ {
+ if ((status == CD_PLAYING) || (status == CD_PAUSED))
+ {
+ /* Get Position */
+ msp.hwndCallback = (HWND)NULL; /* None */
+ msp.ulReturn = (ULONG)NULL; /* We want this information */
+ msp.ulItem = MCI_STATUS_POSITION;
+ msp.ulValue = (ULONG)NULL; /* No additiona info */
+ if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return (CD_ERROR);
+ /* Convert from MSF (format selected in the Open process) to Frames (format that will be returned) */
+ *position = MSF_TO_FRAMES(MSF_MINUTE(msp.ulReturn),MSF_SECOND(msp.ulReturn),MSF_FRAME(msp.ulReturn));
+ }
+ else *position = 0;
+ }
+return(status);
+}
+
+/* Start play - Ready for MCI */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+MCI_GENERIC_PARMS mgp;
+MCI_STATUS_PARMS msp;
+MCI_PLAY_PARMS mpp;
+ULONG min,sec,frm;
+
+/* Start MSF */
+FRAMES_TO_MSF(start, &min, &sec, &frm);
+MSF_MINUTE(mpp.ulFrom) = min;
+MSF_SECOND(mpp.ulFrom) = sec;
+MSF_FRAME(mpp.ulFrom) = frm;
+/* End MSF */
+FRAMES_TO_MSF(start+length, &min, &sec, &frm);
+MSF_MINUTE(mpp.ulTo) = min;
+MSF_SECOND(mpp.ulTo) = sec;
+MSF_FRAME(mpp.ulTo) = frm;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
+ playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
+ playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
+#endif
+/* Verifies if it is paused first... and if it is, unpause before stopping it. */
+msp.hwndCallback = (HWND)NULL; /* None */
+msp.ulReturn = (ULONG)NULL; /* We want this information */
+msp.ulItem = MCI_STATUS_MODE;
+msp.ulValue = (ULONG)NULL; /* No additional information */
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS)
+ {
+ if (msp.ulReturn == MCI_MODE_PAUSE)
+ {
+ mgp.hwndCallback = (HWND)NULL; // None
+ mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0);
+ }
+ }
+/* Now play it. */
+mpp.hwndCallback = (HWND)NULL; // We do not want the info. temp
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PLAY,MCI_FROM | MCI_TO,&mpp, 0)) == MCIERR_SUCCESS) return 0;
+return (CD_ERROR);
+}
+
+/* Pause play - Ready for MCI */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+MCI_GENERIC_PARMS mgp;
+
+mgp.hwndCallback = (HWND)NULL; // None
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PAUSE,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
+return(CD_ERROR);
+}
+
+/* Resume play - Ready for MCI */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+MCI_GENERIC_PARMS mgp;
+
+mgp.hwndCallback = (HWND)NULL; // None
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_RESUME,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
+return(CD_ERROR);
+}
+
+/* Stop play - Ready for MCI */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+MCI_GENERIC_PARMS mgp;
+MCI_STATUS_PARMS msp;
+
+/* Verifies if it is paused first... and if it is, unpause before stopping it. */
+msp.hwndCallback = (HWND)NULL; /* None */
+msp.ulReturn = (ULONG)NULL; /* We want this information */
+msp.ulItem = MCI_STATUS_MODE;
+msp.ulValue = (ULONG)NULL; /* No additional information */
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS)
+ {
+ if (msp.ulReturn == MCI_MODE_PAUSE)
+ {
+ mgp.hwndCallback = (HWND)NULL; // None
+ mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0);
+ }
+ }
+/* Now stops the media */
+mgp.hwndCallback = (HWND)NULL; // None
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STOP,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
+return(CD_ERROR);
+}
+
+/* Eject the CD-ROM - Ready for MCI */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+MCI_SET_PARMS msp;
+
+msp.hwndCallback = (HWND)NULL; // None
+msp.ulTimeFormat = (ULONG)NULL; // No change
+msp.ulSpeedFormat = (ULONG)NULL; // No change
+msp.ulAudio = (ULONG)NULL; // No Channel
+msp.ulLevel = (ULONG)NULL; // No Volume
+msp.ulOver = (ULONG)NULL; // No Delay
+msp.ulItem = (ULONG)NULL; // No item
+msp.ulValue = (ULONG)NULL; // No value for item flag
+if (LOUSHORT(mciSendCommand(cdrom->id,MCI_SET,MCI_WAIT | MCI_SET_DOOR_OPEN,&msp, 0)) == MCIERR_SUCCESS) return 0;
+return(CD_ERROR);
+}
+
+/* Close the CD-ROM handle - Ready for MCI */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+MCI_GENERIC_PARMS mgp;
+
+mgp.hwndCallback = (HWND)NULL; // None
+mciSendCommand(cdrom->id,MCI_CLOSE,MCI_WAIT,&mgp, 0);
+}
+
+/* Finalize CDROM Subsystem - Ready for MCI */
+void SDL_SYS_CDQuit(void)
+{
+int i;
+
+if ( SDL_numcds > 0 )
+ {
+ for ( i=0; i<SDL_numcds; ++i )
+ {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_OS2 */
diff --git a/src/sdl_cdrom/SDL_syscdrom_osf.c b/src/sdl_cdrom/SDL_syscdrom_osf.c
new file mode 100644
index 000000000..fb281f9ad
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_osf.c
@@ -0,0 +1,449 @@
+/*
+ Tru64 audio module for SDL (Simple DirectMedia Layer)
+ Copyright (C) 2003
+
+ 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
+
+
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_OSF 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_OSF)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_OSF*/
+
+/* Functions for system-level CD-ROM audio control */
+
+/* #define DEBUG_CDROM 1 */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <io/cam/cdrom.h>
+#include <io/cam/rzdisk.h>
+#include <io/common/devgetinfo.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+/* Check a drive to see if it is a CD-ROM */
+/* Caution!! Not tested. */
+static int CheckDrive(char *drive, struct stat *stbuf)
+{
+ int cdfd, is_cd = 0;
+ struct mode_sel_sns_params msp;
+ struct inquiry_info inq;
+
+#ifdef DEBUG_CDROM
+ char *devtype[] = {"Disk", "Tape", "Printer", "Processor", "WORM",
+ "CD-ROM", "Scanner", "Optical", "Changer", "Comm", "Unknown"};
+#endif
+
+ bzero(&msp, sizeof(msp));
+ bzero(&inq, sizeof(inq));
+
+ /* If it doesn't exist, return -1 */
+ if ( stat(drive, stbuf) < 0 ) {
+ return(-1);
+ }
+
+ if ( (cdfd = open(drive, (O_RDWR|O_NDELAY), 0)) >= 0 ) {
+ msp.msp_addr = (caddr_t) &inq;
+ msp.msp_pgcode = 0;
+ msp.msp_pgctrl = 0;
+ msp.msp_length = sizeof(inq);
+ msp.msp_setps = 0;
+
+ if ( ioctl(cdfd, SCSI_GET_INQUIRY_DATA, &msp) )
+ return (0);
+
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Device Type: %s\n", devtype[inq.perfdt]);
+ fprintf(stderr, "Vendor: %.8s\n", inq.vndrid);
+ fprintf(stderr, "Product: %.8s\n", inq.prodid);
+ fprintf(stderr, "Revision: %.8s\n", inq.revlvl);
+#endif
+ if ( inq.perfdt == DTYPE_RODIRECT )
+ is_cd = 1;
+ }
+
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Check to make sure it's not already in our list.
+ * This can happen when we see a drive via symbolic link.
+ *
+ */
+ for ( i=0; i<SDL_numcds; ++i ) {
+ if ( stbuf->st_rdev == SDL_cdmode[i] ) {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
+#endif
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+int SDL_SYS_CDInit(void)
+{
+ /* checklist:
+ *
+ * Tru64 5.X (/dev/rdisk/cdrom?c)
+ * dir: /dev/rdisk, name: cdrom
+ *
+ * Digital UNIX 4.0X (/dev/rrz?c)
+ * dir: /dev, name: rrz
+ *
+ */
+ struct {
+ char *dir;
+ char *name;
+ } checklist[] = {
+ {"/dev/rdisk", "cdrom"},
+ {"/dev", "rrz"},
+ {NULL, NULL}};
+ char drive[32];
+ char *SDLcdrom;
+ int i, j, exists;
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL ) {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if ( cdpath != NULL ) {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if ( delim ) {
+ *delim++ = '\0';
+ }
+ if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if ( delim ) {
+ SDLcdrom = delim;
+ } else {
+ SDLcdrom = NULL;
+ }
+ } while ( SDLcdrom );
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if ( SDL_numcds > 0 ) {
+ return(0);
+ }
+ }
+ /* Scan the system for CD-ROM drives */
+ for ( i = 0; checklist[i].dir; ++i) {
+ DIR *devdir;
+ struct dirent *devent;
+ int name_len;
+
+ devdir = opendir(checklist[i].dir);
+ if (devdir) {
+ name_len = SDL_strlen(checklist[i].name);
+ while (devent = readdir(devdir))
+ if (SDL_memcmp(checklist[i].name, devent->d_name, name_len) == 0)
+ if (devent->d_name[devent->d_namlen-1] == 'c') {
+ SDL_snprintf(drive, SDL_arraysize(drive), "%s/%s", checklist[i].dir, devent->d_name);
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Try to add drive: %s\n", drive);
+#endif
+ if ( CheckDrive(drive, &stbuf) > 0 )
+ AddDrive(drive, &stbuf);
+ }
+ closedir(devdir);
+ } else {
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "cannot open dir: %s\n", checklist[i].dir);
+#endif
+ }
+ }
+ return (0);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ /* O_RDWR: To use ioctl(fd, SCSI_STOP_UNIT) */
+ return(open(SDL_cdlist[drive], (O_RDWR|O_NDELAY), 0));
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ struct cd_toc toc;
+ struct cd_toc_header hdr;
+ struct cd_toc_entry *cdte;
+ int i;
+ int okay = 0;
+ if ( ioctl(cdrom->id, CDROM_TOC_HEADER, &hdr) ) {
+ fprintf(stderr,"ioctl error CDROM_TOC_HEADER\n");
+ return -1;
+ }
+ cdrom->numtracks = hdr.th_ending_track - hdr.th_starting_track + 1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+#ifdef DEBUG_CDROM
+ fprintf(stderr,"hdr.th_data_len1 = %d\n", hdr.th_data_len1);
+ fprintf(stderr,"hdr.th_data_len0 = %d\n", hdr.th_data_len0);
+ fprintf(stderr,"hdr.th_starting_track = %d\n", hdr.th_starting_track);
+ fprintf(stderr,"hdr.th_ending_track = %d\n", hdr.th_ending_track);
+ fprintf(stderr,"cdrom->numtracks = %d\n", cdrom->numtracks);
+#endif
+ toc.toc_address_format = CDROM_LBA_FORMAT;
+ toc.toc_starting_track = 0;
+ toc.toc_alloc_length = (hdr.th_data_len1 << 8) +
+ hdr.th_data_len0 + sizeof(hdr);
+ if ( (toc.toc_buffer = alloca(toc.toc_alloc_length)) == NULL) {
+ fprintf(stderr,"cannot allocate toc.toc_buffer\n");
+ return -1;
+ }
+
+ bzero (toc.toc_buffer, toc.toc_alloc_length);
+ if (ioctl(cdrom->id, CDROM_TOC_ENTRYS, &toc)) {
+ fprintf(stderr,"ioctl error CDROM_TOC_ENTRYS\n");
+ return -1;
+ }
+
+ cdte =(struct cd_toc_entry *) ((char *) toc.toc_buffer + sizeof(hdr));
+ for (i=0; i <= cdrom->numtracks; ++i) {
+ if (i == cdrom->numtracks ) {
+ cdrom->track[i].id = 0xAA;;
+ } else {
+ cdrom->track[i].id = hdr.th_starting_track + i;
+ }
+
+ cdrom->track[i].type =
+ cdte[i].te_control & CDROM_DATA_TRACK;
+ cdrom->track[i].offset =
+ cdte[i].te_absaddr.lba.addr3 << 24 |
+ cdte[i].te_absaddr.lba.addr2 << 16 |
+ cdte[i].te_absaddr.lba.addr1 << 8 |
+ cdte[i].te_absaddr.lba.addr0;
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i - 1].length =
+ cdrom->track[i].offset -
+ cdrom->track[i - 1].offset;
+ }
+ }
+#ifdef DEBUG_CDROM
+ for (i = 0; i <= cdrom->numtracks; i++) {
+ fprintf(stderr,"toc_entry[%d].te_track_number = %d\n",
+ i,cdte[i].te_track_number);
+ fprintf(stderr,"cdrom->track[%d].id = %d\n", i,cdrom->track[i].id);
+ fprintf(stderr,"cdrom->track[%d].type = %x\n", i,cdrom->track[i].type);
+ fprintf(stderr,"cdrom->track[%d].offset = %d\n", i,cdrom->track[i].offset);
+ fprintf(stderr,"cdrom->track[%d].length = %d\n", i,cdrom->track[i].length);
+ }
+#endif
+ if ( i == (cdrom->numtracks+1) ) {
+ okay = 1;
+ }
+
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ struct cd_sub_channel sc;
+ struct cd_subc_channel_data scd;
+
+ sc.sch_address_format = CDROM_LBA_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;
+ if ( ioctl(cdrom->id, CDROM_READ_SUBCHANNEL, &sc) ) {
+ status = CD_ERROR;
+ fprintf(stderr,"ioctl error CDROM_READ_SUBCHANNEL \n");
+ } else {
+ switch (scd.scd_header.sh_audio_status) {
+ case AS_AUDIO_INVALID:
+ status = CD_STOPPED;
+ break;
+ case AS_PLAY_IN_PROGRESS:
+ status = CD_PLAYING;
+ break;
+ case AS_PLAY_PAUSED:
+ status = CD_PAUSED;
+ break;
+ case AS_PLAY_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case AS_PLAY_ERROR:
+ status = CD_ERROR;
+ break;
+ case AS_NO_STATUS:
+ status = CD_STOPPED;
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+#ifdef DEBUG_CDROM
+ fprintf(stderr,"scd.scd_header.sh_audio_status = %x\n",
+ scd.scd_header.sh_audio_status);
+#endif
+ }
+ if (position) {
+ if (status == CD_PLAYING || (status == CD_PAUSED) ) {
+ *position =
+ scd.scd_position_data.scp_absaddr.lba.addr3 << 24 |
+ scd.scd_position_data.scp_absaddr.lba.addr2 << 16 |
+ scd.scd_position_data.scp_absaddr.lba.addr1 << 8 |
+ scd.scd_position_data.scp_absaddr.lba.addr0;
+ } else {
+ *position = 0;
+ }
+ }
+
+ return status;
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+/*
+ * Play MSF
+ */
+ struct cd_play_audio_msf msf;
+ int end;
+
+ bzero(&msf, sizeof(msf));
+ end = start +length;
+ FRAMES_TO_MSF(start + 150, /* LBA = 4500*M + 75*S + F - 150 */
+ &msf.msf_starting_M_unit,
+ &msf.msf_starting_S_unit,
+ &msf.msf_starting_F_unit);
+ FRAMES_TO_MSF(end + 150, /* LBA = 4500*M + 75*S + F - 150 */
+ &msf.msf_ending_M_unit,
+ &msf.msf_ending_S_unit,
+ &msf.msf_ending_F_unit);
+
+ return(ioctl(cdrom->id, CDROM_PLAY_AUDIO_MSF, &msf));
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ return(ioctl(cdrom->id, CDROM_PAUSE_PLAY));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ return(ioctl(cdrom->id, CDROM_RESUME_PLAY));
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return(ioctl(cdrom->id, SCSI_STOP_UNIT));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return(ioctl(cdrom->id, CDROM_EJECT_CADDY));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_OSF */
diff --git a/src/sdl_cdrom/SDL_syscdrom_qnx.c b/src/sdl_cdrom/SDL_syscdrom_qnx.c
new file mode 100644
index 000000000..c656a7809
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_qnx.c
@@ -0,0 +1,556 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_QNX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_QNX)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_QNX*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/cdrom.h>
+#include <sys/dcmd_cam.h>
+
+#include "SDL_timer.h"
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 16
+
+#define QNX_CD_OPENMODE O_RDONLY | O_EXCL
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static dev_t SDL_cdmode[MAX_DRIVES];
+static int SDL_cdopen[MAX_DRIVES];
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+/* Check a drive to see if it is a CD-ROM */
+static int CheckDrive(char *drive, struct stat *stbuf)
+{
+ int is_cd, cdfd;
+ cam_devinfo_t dinfo;
+ int devctlret=0;
+
+ int atapi;
+ int removable;
+ int cdb10;
+
+ /* If it doesn't exist, return -1 */
+ if (stat(drive, stbuf) < 0)
+ {
+ return(-1);
+ }
+
+ /* If it does exist, verify that it's an available CD-ROM */
+ is_cd = 0;
+
+ if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode))
+ {
+ cdfd = open(drive, QNX_CD_OPENMODE);
+ if ( cdfd >= 0 )
+ {
+ devctlret=devctl(cdfd, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
+
+ if (devctlret==EOK)
+ {
+ atapi=dinfo.flags & DEV_ATAPI;
+ removable=dinfo.flags & DEV_REMOVABLE;
+ cdb10=dinfo.flags & DEV_CDB_10; /* I'm not sure about that flag */
+
+ /* in the near future need to add more checks for splitting cdroms from other devices */
+ if ((atapi)&&(removable))
+ {
+ is_cd = 1;
+ }
+ }
+
+ close(cdfd);
+ }
+ }
+ return(is_cd);
+}
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive, struct stat *stbuf)
+{
+ int i;
+
+ if (SDL_numcds < MAX_DRIVES)
+ {
+ /* Check to make sure it's not already in our list.
+ This can happen when we see a drive via symbolic link. */
+
+ for (i=0; i<SDL_numcds; ++i)
+ {
+ if (stbuf->st_rdev == SDL_cdmode[i])
+ {
+ return;
+ }
+ }
+
+ /* Add this drive to our list */
+
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if (SDL_cdlist[i] == NULL)
+ {
+ SDL_OutOfMemory();
+ return;
+ }
+ SDL_cdmode[i] = stbuf->st_rdev;
+ ++SDL_numcds;
+ }
+}
+
+int SDL_SYS_CDInit(void)
+{
+ /* checklist: /dev/cdrom, /dev/cd?, /dev/scd? */
+ static char *checklist[]={"cdrom", "?0 cd?", "?1 cd?", "?0 scd?", NULL};
+
+ char *SDLcdrom;
+ int i, j, exists;
+ char drive[32];
+ struct stat stbuf;
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* clearing device open status */
+ for (i=0; i<MAX_DRIVES; i++)
+ {
+ SDL_cdopen[i]=0;
+ }
+
+ /* Look in the environment for our CD-ROM drive list */
+ SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
+ if ( SDLcdrom != NULL )
+ {
+ char *cdpath, *delim;
+ size_t len = SDL_strlen(SDLcdrom)+1;
+ cdpath = SDL_stack_alloc(char, len);
+ if (cdpath != NULL)
+ {
+ SDL_strlcpy(cdpath, SDLcdrom, len);
+ SDLcdrom = cdpath;
+ do {
+ delim = SDL_strchr(SDLcdrom, ':');
+ if (delim)
+ {
+ *delim++ = '\0';
+ }
+ if (CheckDrive(SDLcdrom, &stbuf) > 0)
+ {
+ AddDrive(SDLcdrom, &stbuf);
+ }
+ if (delim)
+ {
+ SDLcdrom = delim;
+ }
+ else
+ {
+ SDLcdrom = NULL;
+ }
+ } while (SDLcdrom);
+ SDL_stack_free(cdpath);
+ }
+
+ /* If we found our drives, there's nothing left to do */
+ if (SDL_numcds > 0)
+ {
+ return(0);
+ }
+ }
+
+ /* Scan the system for CD-ROM drives */
+ for ( i=0; checklist[i]; ++i )
+ {
+ if (checklist[i][0] == '?')
+ {
+ char* insert;
+ exists = 1;
+
+ for ( j=checklist[i][1]; exists; ++j )
+ {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
+ insert = SDL_strchr(drive, '?');
+ if (insert != NULL)
+ {
+ *insert = j;
+ }
+ switch (CheckDrive(drive, &stbuf))
+ {
+ /* Drive exists and is a CD-ROM */
+ case 1:
+ AddDrive(drive, &stbuf);
+ break;
+ /* Drive exists, but isn't a CD-ROM */
+ case 0:
+ break;
+ /* Drive doesn't exist */
+ case -1:
+ exists = 0;
+ break;
+ }
+ }
+ }
+ else
+ {
+ SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
+ if (CheckDrive(drive, &stbuf) > 0)
+ {
+ AddDrive(drive, &stbuf);
+ }
+ }
+ }
+ return(0);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ int handle;
+
+ handle=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
+
+ if (handle>0)
+ {
+ SDL_cdopen[drive]=handle;
+ }
+
+ return (handle);
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ cdrom_read_toc_t toc;
+ int i, okay;
+
+ okay = 0;
+ if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL) == 0)
+ {
+ cdrom->numtracks = toc.last_track - toc.first_track + 1;
+ if (cdrom->numtracks > SDL_MAX_TRACKS)
+ {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+ /* Read all the track TOC entries */
+ for (i=0; i<=cdrom->numtracks; ++i)
+ {
+ if (i == cdrom->numtracks)
+ {
+ cdrom->track[i].id = CDROM_LEADOUT;
+ }
+ else
+ {
+ cdrom->track[i].id = toc.first_track+i;
+ }
+
+ cdrom->track[i].type = toc.toc_entry[i].control_adr & 0x0F;
+ cdrom->track[i].offset = toc.toc_entry[i].addr.lba;
+ cdrom->track[i].length = 0;
+
+ if (i > 0)
+ {
+ cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
+ }
+ }
+ if (i == (cdrom->numtracks+1))
+ {
+ okay = 1;
+ }
+ }
+ return (okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+
+ cdrom_read_toc_t toc;
+ cdrom_subch_data_t info;
+ cam_devinfo_t dinfo;
+
+ int devctlret=0;
+ int drive=-1;
+ int i;
+ int eagaincnt=0;
+
+ /* check media presence before read subchannel call, some cdroms can lockups */
+ /* if no media, while calling read subchannel functions. */
+ devctlret=devctl(cdrom->id, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
+
+ if (devctlret==EOK)
+ {
+ if ((dinfo.flags & DEV_NO_MEDIA)!=0)
+ {
+ status = CD_TRAYEMPTY;
+ if (position)
+ {
+ *position = 0;
+ }
+ return (status);
+ }
+ }
+
+ /* if media exists, then do other stuff */
+
+ SDL_memset(&info, 0x00, sizeof(info));
+ info.subch_command.data_format = CDROM_SUBCH_CURRENT_POSITION;
+
+ do {
+ devctlret=devctl(cdrom->id, DCMD_CAM_CDROMSUBCHNL, &info, sizeof(info), NULL);
+ if (devctlret==EIO)
+ {
+ /* big workaround for media change, handle is unusable after that,
+ that bug was found in QNX 6.2, 6.2.1 is not released yet. */
+
+ for (i=0; i<MAX_DRIVES; i++)
+ {
+ if (SDL_cdopen[i]==cdrom->id)
+ {
+ drive=i;
+ break;
+ }
+ }
+ if (drive==-1)
+ {
+ /* that cannot happen, but ... */
+ break;
+ }
+ close(cdrom->id);
+ cdrom->id=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
+ devctlret=EAGAIN;
+ }
+ if (devctlret==EAGAIN)
+ {
+ eagaincnt++;
+ }
+ if (eagaincnt==2)
+ {
+ /* workaround for broken cdroms, which can return always EAGAIN when its not ready, */
+ /* that mean errornous media or just no media avail */
+ devctlret=ENXIO;
+ break;
+ }
+ } while ((devctlret==EAGAIN)||(devctlret==ESTALE));
+
+ if (devctlret != 0)
+ {
+ if (devctlret==ENXIO)
+ {
+ status = CD_TRAYEMPTY;
+ }
+ else
+ {
+ status = CD_ERROR;
+ }
+ }
+ else
+ {
+ switch (info.current_position.header.audio_status)
+ {
+ case CDROM_AUDIO_INVALID:
+ case CDROM_AUDIO_NO_STATUS:
+ /* Try to determine if there's a CD available */
+ if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL)==0)
+ status = CD_STOPPED;
+ else
+ status = CD_TRAYEMPTY;
+ break;
+ case CDROM_AUDIO_COMPLETED:
+ status = CD_STOPPED;
+ break;
+ case CDROM_AUDIO_PLAY:
+ status = CD_PLAYING;
+ break;
+ case CDROM_AUDIO_PAUSED:
+ /* Workaround buggy CD-ROM drive */
+ if (info.current_position.data_format == CDROM_LEADOUT)
+ {
+ status = CD_STOPPED;
+ }
+ else
+ {
+ status = CD_PAUSED;
+ }
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+
+ if (position)
+ {
+ if (status==CD_PLAYING || (status==CD_PAUSED))
+ {
+ *position = MSF_TO_FRAMES(info.current_position.addr.msf.minute,
+ info.current_position.addr.msf.second,
+ info.current_position.addr.msf.frame);
+ }
+ else
+ {
+ *position = 0;
+ }
+ }
+
+ return (status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ cdrom_playmsf_t playtime;
+
+ FRAMES_TO_MSF(start, &playtime.start_minute, &playtime.start_second, &playtime.start_frame);
+ FRAMES_TO_MSF(start+length, &playtime.end_minute, &playtime.end_second, &playtime.end_frame);
+
+ if (devctl(cdrom->id, DCMD_CAM_CDROMPLAYMSF, &playtime, sizeof(playtime), NULL) != 0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ if (devctl(cdrom->id, DCMD_CAM_CDROMPAUSE, NULL, 0, NULL)!=0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ if (devctl(cdrom->id, DCMD_CAM_CDROMRESUME, NULL, 0, NULL)!=0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ if (devctl(cdrom->id, DCMD_CAM_CDROMSTOP, NULL, 0, NULL)!=0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ if (devctl(cdrom->id, DCMD_CAM_EJECT_MEDIA, NULL, 0, NULL)!=0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ int i;
+
+ for (i=0; i<MAX_DRIVES; i++)
+ {
+ if (SDL_cdopen[i]==cdrom->id)
+ {
+ SDL_cdopen[i]=0;
+ break;
+ }
+ }
+
+ close(cdrom->id);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if (SDL_numcds > 0)
+ {
+ for (i=0; i<SDL_numcds; ++i)
+ {
+ SDL_free(SDL_cdlist[i]);
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_QNX */
diff --git a/src/sdl_cdrom/SDL_syscdrom_win32.c b/src/sdl_cdrom/SDL_syscdrom_win32.c
new file mode 100644
index 000000000..67c7c6f1e
--- /dev/null
+++ b/src/sdl_cdrom/SDL_syscdrom_win32.c
@@ -0,0 +1,391 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_WIN32 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_WIN32)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_WIN32*/
+
+/* Functions for system-level CD-ROM audio control */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+/* This really broken?? */
+#define BROKEN_MCI_PAUSE /* Pausing actually stops play -- Doh! */
+
+/* The maximum number of CD-ROM drives we'll detect (Don't change!) */
+#define MAX_DRIVES 26
+
+/* A list of available CD-ROM drives */
+static char *SDL_cdlist[MAX_DRIVES];
+static MCIDEVICEID SDL_mciID[MAX_DRIVES];
+#ifdef BROKEN_MCI_PAUSE
+static int SDL_paused[MAX_DRIVES];
+#endif
+static int SDL_CD_end_position;
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+
+/* Add a CD-ROM drive to our list of valid drives */
+static void AddDrive(char *drive)
+{
+ int i;
+
+ if ( SDL_numcds < MAX_DRIVES ) {
+ /* Add this drive to our list */
+ i = SDL_numcds;
+ SDL_cdlist[i] = SDL_strdup(drive);
+ if ( SDL_cdlist[i] == NULL ) {
+ SDL_OutOfMemory();
+ return;
+ }
+ ++SDL_numcds;
+#ifdef CDROM_DEBUG
+ fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
+#endif
+ }
+}
+
+int SDL_SYS_CDInit(void)
+{
+ /* checklist: Drive 'A' - 'Z' */
+ int i;
+ char drive[4];
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Scan the system for CD-ROM drives */
+ for ( i='A'; i<='Z'; ++i ) {
+ SDL_snprintf(drive, SDL_arraysize(drive), "%c:\\", i);
+ if ( GetDriveType(drive) == DRIVE_CDROM ) {
+ AddDrive(drive);
+ }
+ }
+ SDL_memset(SDL_mciID, 0, sizeof(SDL_mciID));
+ return(0);
+}
+
+/* General ioctl() CD-ROM command function */
+static int SDL_SYS_CDioctl(int id, UINT msg, DWORD flags, void *arg)
+{
+ MCIERROR mci_error;
+
+ mci_error = mciSendCommand(SDL_mciID[id], msg, flags, (DWORD_PTR)arg);
+ if ( mci_error ) {
+ char error[256];
+
+ mciGetErrorString(mci_error, error, 256);
+ SDL_SetError("mciSendCommand() error: %s", error);
+ }
+ return(!mci_error ? 0 : -1);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive]);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ MCI_OPEN_PARMS mci_open;
+ MCI_SET_PARMS mci_set;
+ char device[3];
+ DWORD flags;
+
+ /* Open the requested device */
+ mci_open.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
+ device[0] = *SDL_cdlist[drive];
+ device[1] = ':';
+ device[2] = '\0';
+ mci_open.lpstrElementName = device;
+ flags =
+ (MCI_OPEN_TYPE|MCI_OPEN_SHAREABLE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT);
+ if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) {
+ flags &= ~MCI_OPEN_SHAREABLE;
+ if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) {
+ return(-1);
+ }
+ }
+ SDL_mciID[drive] = mci_open.wDeviceID;
+
+ /* Set the minute-second-frame time format */
+ mci_set.dwTimeFormat = MCI_FORMAT_MSF;
+ SDL_SYS_CDioctl(drive, MCI_SET, MCI_SET_TIME_FORMAT, &mci_set);
+
+#ifdef BROKEN_MCI_PAUSE
+ SDL_paused[drive] = 0;
+#endif
+ return(drive);
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ MCI_STATUS_PARMS mci_status;
+ int i, okay;
+ DWORD flags;
+
+ okay = 0;
+ mci_status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
+ flags = MCI_STATUS_ITEM | MCI_WAIT;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) {
+ cdrom->numtracks = mci_status.dwReturn;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ }
+ /* Read all the track TOC entries */
+ flags = MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT;
+ for ( i=0; i<cdrom->numtracks; ++i ) {
+ cdrom->track[i].id = i+1;
+ mci_status.dwTrack = cdrom->track[i].id;
+#ifdef MCI_CDA_STATUS_TYPE_TRACK
+ mci_status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
+ &mci_status) < 0 ) {
+ break;
+ }
+ if ( mci_status.dwReturn == MCI_CDA_TRACK_AUDIO ) {
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+ } else {
+ cdrom->track[i].type = SDL_DATA_TRACK;
+ }
+#else
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+#endif
+ mci_status.dwItem = MCI_STATUS_POSITION;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
+ &mci_status) < 0 ) {
+ break;
+ }
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ MCI_MSF_MINUTE(mci_status.dwReturn),
+ MCI_MSF_SECOND(mci_status.dwReturn),
+ MCI_MSF_FRAME(mci_status.dwReturn));
+ cdrom->track[i].length = 0;
+ if ( i > 0 ) {
+ cdrom->track[i-1].length =
+ cdrom->track[i].offset-
+ cdrom->track[i-1].offset;
+ }
+ }
+ if ( i == cdrom->numtracks ) {
+ mci_status.dwTrack = cdrom->track[i - 1].id;
+ mci_status.dwItem = MCI_STATUS_LENGTH;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
+ &mci_status) == 0 ) {
+ cdrom->track[i - 1].length = MSF_TO_FRAMES(
+ MCI_MSF_MINUTE(mci_status.dwReturn),
+ MCI_MSF_SECOND(mci_status.dwReturn),
+ MCI_MSF_FRAME(mci_status.dwReturn));
+ /* compute lead-out offset */
+ cdrom->track[i].offset = cdrom->track[i - 1].offset +
+ cdrom->track[i - 1].length;
+ cdrom->track[i].length = 0;
+ okay = 1;
+ }
+ }
+ }
+ return(okay ? 0 : -1);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDstatus status;
+ MCI_STATUS_PARMS mci_status;
+ DWORD flags;
+
+ flags = MCI_STATUS_ITEM | MCI_WAIT;
+ mci_status.dwItem = MCI_STATUS_MODE;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) < 0 ) {
+ status = CD_ERROR;
+ } else {
+ switch (mci_status.dwReturn) {
+ case MCI_MODE_NOT_READY:
+ case MCI_MODE_OPEN:
+ status = CD_TRAYEMPTY;
+ break;
+ case MCI_MODE_STOP:
+#ifdef BROKEN_MCI_PAUSE
+ if ( SDL_paused[cdrom->id] ) {
+ status = CD_PAUSED;
+ } else {
+ status = CD_STOPPED;
+ }
+#else
+ status = CD_STOPPED;
+#endif /* BROKEN_MCI_PAUSE */
+ break;
+ case MCI_MODE_PLAY:
+#ifdef BROKEN_MCI_PAUSE
+ if ( SDL_paused[cdrom->id] ) {
+ status = CD_PAUSED;
+ } else {
+ status = CD_PLAYING;
+ }
+#else
+ status = CD_PLAYING;
+#endif /* BROKEN_MCI_PAUSE */
+ break;
+ case MCI_MODE_PAUSE:
+ status = CD_PAUSED;
+ break;
+ default:
+ status = CD_ERROR;
+ break;
+ }
+ }
+ if ( position ) {
+ if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
+ mci_status.dwItem = MCI_STATUS_POSITION;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
+ &mci_status) == 0 ) {
+ *position = MSF_TO_FRAMES(
+ MCI_MSF_MINUTE(mci_status.dwReturn),
+ MCI_MSF_SECOND(mci_status.dwReturn),
+ MCI_MSF_FRAME(mci_status.dwReturn));
+ } else {
+ *position = 0;
+ }
+ } else {
+ *position = 0;
+ }
+ }
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ MCI_PLAY_PARMS mci_play;
+ int m, s, f;
+ DWORD flags;
+
+ flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
+ mci_play.dwCallback = 0;
+ FRAMES_TO_MSF(start, &m, &s, &f);
+ mci_play.dwFrom = MCI_MAKE_MSF(m, s, f);
+ FRAMES_TO_MSF(start+length, &m, &s, &f);
+ mci_play.dwTo = MCI_MAKE_MSF(m, s, f);
+ SDL_CD_end_position = mci_play.dwTo;
+ return(SDL_SYS_CDioctl(cdrom->id, MCI_PLAY, flags, &mci_play));
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+#ifdef BROKEN_MCI_PAUSE
+ SDL_paused[cdrom->id] = 1;
+#endif
+ return(SDL_SYS_CDioctl(cdrom->id, MCI_PAUSE, MCI_WAIT, NULL));
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+#ifdef BROKEN_MCI_PAUSE
+ MCI_STATUS_PARMS mci_status;
+ int okay;
+ int flags;
+
+ okay = 0;
+ /* Play from the current play position to the end position set earlier */
+ flags = MCI_STATUS_ITEM | MCI_WAIT;
+ mci_status.dwItem = MCI_STATUS_POSITION;
+ if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) {
+ MCI_PLAY_PARMS mci_play;
+
+ flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
+ mci_play.dwCallback = 0;
+ mci_play.dwFrom = mci_status.dwReturn;
+ mci_play.dwTo = SDL_CD_end_position;
+ if (SDL_SYS_CDioctl(cdrom->id,MCI_PLAY,flags,&mci_play) == 0) {
+ okay = 1;
+ SDL_paused[cdrom->id] = 0;
+ }
+ }
+ return(okay ? 0 : -1);
+#else
+ return(SDL_SYS_CDioctl(cdrom->id, MCI_RESUME, MCI_WAIT, NULL));
+#endif /* BROKEN_MCI_PAUSE */
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, MCI_STOP, MCI_WAIT, NULL));
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ return(SDL_SYS_CDioctl(cdrom->id, MCI_SET, MCI_SET_DOOR_OPEN, NULL));
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ SDL_SYS_CDioctl(cdrom->id, MCI_CLOSE, MCI_WAIT, NULL);
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ int i;
+
+ if ( SDL_numcds > 0 ) {
+ for ( i=0; i<SDL_numcds; ++i ) {
+ SDL_free(SDL_cdlist[i]);
+ SDL_cdlist[i] = NULL;
+ }
+ SDL_numcds = 0;
+ }
+}
+
+#endif /* SDL_CDROM_WIN32 */
diff --git a/src/sdl_cdrom/compat_SDL_cdrom.c b/src/sdl_cdrom/compat_SDL_cdrom.c
new file mode 100644
index 000000000..97f76597f
--- /dev/null
+++ b/src/sdl_cdrom/compat_SDL_cdrom.c
@@ -0,0 +1,347 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+/*#include "SDL_config.h"*/
+
+/* This is the CD-audio control API for Simple DirectMedia Layer */
+
+#include "config.h"
+
+#ifdef C_COMPAT_SDL_CDROM_PLATFORM
+
+#include "compat_SDL_cdrom.h"
+#include "SDL_syscdrom.h"
+
+#if !defined(__MACOS__)
+#define CLIP_FRAMES 10 /* Some CD-ROMs won't go all the way */
+#endif
+
+static int SDL_cdinitted = 0;
+static SDL_CD *default_cdrom;
+
+/* The system level CD-ROM control functions */
+struct CDcaps SDL_CDcaps = {
+ NULL, /* Name */
+ NULL, /* Open */
+ NULL, /* GetTOC */
+ NULL, /* Status */
+ NULL, /* Play */
+ NULL, /* Pause */
+ NULL, /* Resume */
+ NULL, /* Stop */
+ NULL, /* Eject */
+ NULL, /* Close */
+};
+int SDL_numcds;
+
+int Compat_SDL_CDROMInit(void)
+{
+ int retval;
+
+ SDL_numcds = 0;
+ retval = SDL_SYS_CDInit();
+ if ( retval == 0 ) {
+ SDL_cdinitted = 1;
+ }
+ default_cdrom = NULL;
+ return(retval);
+}
+
+/* Check to see if the CD-ROM subsystem has been initialized */
+static int CheckInit(int check_cdrom, SDL_CD **cdrom)
+{
+ int okay;
+
+ okay = SDL_cdinitted;
+ if ( check_cdrom && (*cdrom == NULL) ) {
+ *cdrom = default_cdrom;
+ if ( *cdrom == NULL ) {
+ SDL_SetError("CD-ROM not opened");
+ okay = 0;
+ }
+ }
+ if ( ! SDL_cdinitted ) {
+ SDL_SetError("CD-ROM subsystem not initialized");
+ }
+ return(okay);
+}
+
+int SDL_CDNumDrives(void)
+{
+ if ( ! CheckInit(0, NULL) ) {
+ return(-1);
+ }
+ return(SDL_numcds);
+}
+
+const char *SDL_CDName(int drive)
+{
+ if ( ! CheckInit(0, NULL) ) {
+ return(NULL);
+ }
+ if ( drive >= SDL_numcds ) {
+ SDL_SetError("Invalid CD-ROM drive index");
+ return(NULL);
+ }
+ if ( SDL_CDcaps.Name ) {
+ return(SDL_CDcaps.Name(drive));
+ } else {
+ return("");
+ }
+}
+
+SDL_CD *SDL_CDOpen(int drive)
+{
+ struct SDL_CD *cdrom;
+
+ if ( ! CheckInit(0, NULL) ) {
+ return(NULL);
+ }
+ if ( drive >= SDL_numcds ) {
+ SDL_SetError("Invalid CD-ROM drive index");
+ return(NULL);
+ }
+ cdrom = (SDL_CD *)SDL_malloc(sizeof(*cdrom));
+ if ( cdrom == NULL ) {
+ SDL_OutOfMemory();
+ return(NULL);
+ }
+ SDL_memset(cdrom, 0, sizeof(*cdrom));
+ cdrom->id = SDL_CDcaps.Open(drive);
+ if ( cdrom->id < 0 ) {
+ SDL_free(cdrom);
+ return(NULL);
+ }
+ default_cdrom = cdrom;
+ return(cdrom);
+}
+
+CDstatus SDL_CDStatus(SDL_CD *cdrom)
+{
+ CDstatus status;
+ int i;
+ Uint32 position;
+
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+
+ /* Get the current status of the drive */
+ cdrom->numtracks = 0;
+ cdrom->cur_track = 0;
+ cdrom->cur_frame = 0;
+ status = SDL_CDcaps.Status(cdrom, &i);
+ position = (Uint32)i;
+ cdrom->status = status;
+
+ /* Get the table of contents, if there's a CD available */
+ if ( CD_INDRIVE(status) ) {
+ if ( SDL_CDcaps.GetTOC(cdrom) < 0 ) {
+ status = CD_ERROR;
+ }
+ /* If the drive is playing, get current play position */
+ if ( (status == CD_PLAYING) || (status == CD_PAUSED) ) {
+ for ( i=1; cdrom->track[i].offset <= position; ++i ) {
+ /* Keep looking */;
+ }
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Current position: %d, track = %d (offset is %d)\n",
+ position, i-1, cdrom->track[i-1].offset);
+#endif
+ cdrom->cur_track = i-1;
+ position -= cdrom->track[cdrom->cur_track].offset;
+ cdrom->cur_frame = position;
+ }
+ }
+ return(status);
+}
+
+int SDL_CDPlayTracks(SDL_CD *cdrom,
+ int strack, int sframe, int ntracks, int nframes)
+{
+ int etrack, eframe;
+ int start, length;
+
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+
+ /* Determine the starting and ending tracks */
+ if ( (strack < 0) || (strack >= cdrom->numtracks) ) {
+ SDL_SetError("Invalid starting track");
+ return(CD_ERROR);
+ }
+ if ( ! ntracks && ! nframes ) {
+ etrack = cdrom->numtracks;
+ eframe = 0;
+ } else {
+ etrack = strack+ntracks;
+ if ( etrack == strack ) {
+ eframe = sframe + nframes;
+ } else {
+ eframe = nframes;
+ }
+ }
+ if ( etrack > cdrom->numtracks ) {
+ SDL_SetError("Invalid play length");
+ return(CD_ERROR);
+ }
+
+ /* Skip data tracks and verify frame offsets */
+ while ( (strack <= etrack) &&
+ (cdrom->track[strack].type == SDL_DATA_TRACK) ) {
+ ++strack;
+ }
+ if ( sframe >= (int)cdrom->track[strack].length ) {
+ SDL_SetError("Invalid starting frame for track %d", strack);
+ return(CD_ERROR);
+ }
+ while ( (etrack > strack) &&
+ (cdrom->track[etrack-1].type == SDL_DATA_TRACK) ) {
+ --etrack;
+ }
+ if ( eframe > (int)cdrom->track[etrack].length ) {
+ SDL_SetError("Invalid ending frame for track %d", etrack);
+ return(CD_ERROR);
+ }
+
+ /* Determine start frame and play length */
+ start = (cdrom->track[strack].offset+sframe);
+ length = (cdrom->track[etrack].offset+eframe)-start;
+#ifdef CLIP_FRAMES
+ /* I've never seen this necessary, but xmcd does it.. */
+ length -= CLIP_FRAMES; /* CLIP_FRAMES == 10 */
+#endif
+ if ( length < 0 ) {
+ return(0);
+ }
+
+ /* Play! */
+#ifdef DEBUG_CDROM
+ fprintf(stderr, "Playing %d frames at offset %d\n", length, start);
+#endif
+ return(SDL_CDcaps.Play(cdrom, start, length));
+}
+
+int SDL_CDPlay(SDL_CD *cdrom, int sframe, int length)
+{
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+
+ return(SDL_CDcaps.Play(cdrom, sframe, length));
+}
+
+int SDL_CDPause(SDL_CD *cdrom)
+{
+ CDstatus status;
+ int retval;
+
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+
+ status = SDL_CDcaps.Status(cdrom, NULL);
+ switch (status) {
+ case CD_PLAYING:
+ retval = SDL_CDcaps.Pause(cdrom);
+ break;
+ default:
+ retval = 0;
+ break;
+ }
+ return(retval);
+}
+
+int SDL_CDResume(SDL_CD *cdrom)
+{
+ CDstatus status;
+ int retval;
+
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+
+ status = SDL_CDcaps.Status(cdrom, NULL);
+ switch (status) {
+ case CD_PAUSED:
+ retval = SDL_CDcaps.Resume(cdrom);
+ default:
+ retval = 0;
+ break;
+ }
+ return(retval);
+}
+
+int SDL_CDStop(SDL_CD *cdrom)
+{
+ CDstatus status;
+ int retval;
+
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+
+ status = SDL_CDcaps.Status(cdrom, NULL);
+ switch (status) {
+ case CD_PLAYING:
+ case CD_PAUSED:
+ retval = SDL_CDcaps.Stop(cdrom);
+ default:
+ retval = 0;
+ break;
+ }
+ return(retval);
+}
+
+int SDL_CDEject(SDL_CD *cdrom)
+{
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return(CD_ERROR);
+ }
+ return(SDL_CDcaps.Eject(cdrom));
+}
+
+void SDL_CDClose(SDL_CD *cdrom)
+{
+ /* Check if the CD-ROM subsystem has been initialized */
+ if ( ! CheckInit(1, &cdrom) ) {
+ return;
+ }
+ SDL_CDcaps.Close(cdrom);
+ SDL_free(cdrom);
+ default_cdrom = NULL;
+}
+
+void Compat_SDL_CDROMQuit(void)
+{
+ SDL_SYS_CDQuit();
+ SDL_cdinitted = 0;
+}
+
+#endif /* SDL_CDROM */
diff --git a/src/sdl_cdrom/compat_SDL_cdrom.h b/src/sdl_cdrom/compat_SDL_cdrom.h
new file mode 100644
index 000000000..fa78410fe
--- /dev/null
+++ b/src/sdl_cdrom/compat_SDL_cdrom.h
@@ -0,0 +1,206 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+
+/**
+ * @file SDL_cdrom.h
+ * This is the CD-audio control API for Simple DirectMedia Layer
+ */
+
+#ifndef _SDL_cdrom_h
+#define _SDL_cdrom_h
+
+#include "SDL_stdinc.h"
+#include "SDL_error.h"
+
+/*#include "begin_code.h"*/
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file SDL_cdrom.h
+ * In order to use these functions (original SDL 1.2 revision), SDL_Init()
+ * must have been called with the SDL_INIT_CDROM flag. This causes SDL to
+ * scan the system for CD-ROM drives, and load appropriate drivers.
+ */
+
+/** The maximum number of CD-ROM tracks on a disk */
+#define SDL_MAX_TRACKS 99
+
+/** @name Track Types
+ * The types of CD-ROM track possible
+ */
+/*@{*/
+#define SDL_AUDIO_TRACK 0x00
+#define SDL_DATA_TRACK 0x04
+/*@}*/
+
+/** The possible states which a CD-ROM drive can be in. */
+typedef enum {
+ CD_TRAYEMPTY,
+ CD_STOPPED,
+ CD_PLAYING,
+ CD_PAUSED,
+ CD_ERROR = -1
+} CDstatus;
+
+/** Given a status, returns true if there's a disk in the drive */
+#define CD_INDRIVE(status) ((int)(status) > 0)
+
+typedef struct SDL_CDtrack {
+ Uint8 id; /**< Track number */
+ Uint8 type; /**< Data or audio track */
+ Uint16 unused;
+ Uint32 length; /**< Length, in frames, of this track */
+ Uint32 offset; /**< Offset, in frames, from start of disk */
+} SDL_CDtrack;
+
+/** This structure is only current as of the last call to SDL_CDStatus() */
+typedef struct SDL_CD {
+ int id; /**< Private drive identifier */
+ CDstatus status; /**< Current drive status */
+
+ /** The rest of this structure is only valid if there's a CD in drive */
+ /*@{*/
+ int numtracks; /**< Number of tracks on disk */
+ int cur_track; /**< Current track position */
+ int cur_frame; /**< Current frame offset within current track */
+ SDL_CDtrack track[SDL_MAX_TRACKS+1];
+ /*@}*/
+} SDL_CD;
+
+/** @name Frames / MSF Conversion Functions
+ * Conversion functions from frames to Minute/Second/Frames and vice versa
+ */
+/*@{*/
+#define CD_FPS 75
+#define FRAMES_TO_MSF(f, M,S,F) { \
+ int value = f; \
+ *(F) = value%CD_FPS; \
+ value /= CD_FPS; \
+ *(S) = value%60; \
+ value /= 60; \
+ *(M) = value; \
+}
+#define MSF_TO_FRAMES(M, S, F) ((M)*60*CD_FPS+(S)*CD_FPS+(F))
+/*@}*/
+
+/* Compatibility functions (used separately from the SDL_Init+Quit rountines) */
+extern int Compat_SDL_CDROMInit(void);
+extern void Compat_SDL_CDROMQuit(void);
+
+/* CD-audio API functions: */
+
+/**
+ * Returns the number of CD-ROM drives on the system, or -1 if
+ * SDL_Init() has not been called with the SDL_INIT_CDROM flag.
+ */
+extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDNumDrives(void);
+
+/**
+ * Returns a human-readable, system-dependent identifier for the CD-ROM.
+ * Example:
+ * - "/dev/cdrom"
+ * - "E:"
+ * - "/dev/disk/ide/1/master"
+ */
+extern /*DECLSPEC*/ const char * /*SDLCALL*/ SDL_CDName(int drive);
+
+/**
+ * Opens a CD-ROM drive for access. It returns a drive handle on success,
+ * or NULL if the drive was invalid or busy. This newly opened CD-ROM
+ * becomes the default CD used when other CD functions are passed a NULL
+ * CD-ROM handle.
+ * Drives are numbered starting with 0. Drive 0 is the system default CD-ROM.
+ */
+extern /*DECLSPEC*/ SDL_CD * /*SDLCALL*/ SDL_CDOpen(int drive);
+
+/**
+ * This function returns the current status of the given drive.
+ * If the drive has a CD in it, the table of contents of the CD and current
+ * play position of the CD will be stored in the SDL_CD structure.
+ */
+extern /*DECLSPEC*/ CDstatus /*SDLCALL*/ SDL_CDStatus(SDL_CD *cdrom);
+
+/**
+ * Play the given CD starting at 'start_track' and 'start_frame' for 'ntracks'
+ * tracks and 'nframes' frames. If both 'ntrack' and 'nframe' are 0, play
+ * until the end of the CD. This function will skip data tracks.
+ * This function should only be called after calling SDL_CDStatus() to
+ * get track information about the CD.
+ * For example:
+ * @code
+ * // Play entire CD:
+ * if ( CD_INDRIVE(SDL_CDStatus(cdrom)) )
+ * SDL_CDPlayTracks(cdrom, 0, 0, 0, 0);
+ * // Play last track:
+ * if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) {
+ * SDL_CDPlayTracks(cdrom, cdrom->numtracks-1, 0, 0, 0);
+ * }
+ * // Play first and second track and 10 seconds of third track:
+ * if ( CD_INDRIVE(SDL_CDStatus(cdrom)) )
+ * SDL_CDPlayTracks(cdrom, 0, 0, 2, 10);
+ * @endcode
+ *
+ * @return This function returns 0, or -1 if there was an error.
+ */
+extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDPlayTracks(SDL_CD *cdrom,
+ int start_track, int start_frame, int ntracks, int nframes);
+
+/**
+ * Play the given CD starting at 'start' frame for 'length' frames.
+ * @return It returns 0, or -1 if there was an error.
+ */
+extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDPlay(SDL_CD *cdrom, int start, int length);
+
+/** Pause play
+ * @return returns 0, or -1 on error
+ */
+extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDPause(SDL_CD *cdrom);
+
+/** Resume play
+ * @return returns 0, or -1 on error
+ */
+extern DECLSPEC int /*SDLCALL*/ SDL_CDResume(SDL_CD *cdrom);
+
+/** Stop play
+ * @return returns 0, or -1 on error
+ */
+extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDStop(SDL_CD *cdrom);
+
+/** Eject CD-ROM
+ * @return returns 0, or -1 on error
+ */
+extern /*DECLSPEC*/ int /*SDLCALL*/ SDL_CDEject(SDL_CD *cdrom);
+
+/** Closes the handle for the CD-ROM drive */
+extern /*DECLSPEC*/ void /*SDLCALL*/ SDL_CDClose(SDL_CD *cdrom);
+
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+/*#include "close_code.h"*/
+
+#endif /* _SDL_video_h */
diff --git a/src/sdl_cdrom/macos/Makefile.am b/src/sdl_cdrom/macos/Makefile.am
new file mode 100644
index 000000000..1d1fe5dd8
--- /dev/null
+++ b/src/sdl_cdrom/macos/Makefile.am
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libcsdlcdrommacos.a
+EXTRA_DIST = DL_syscdrom_c.h
+libcsdlcdrommacos_a_SOURCES = SDL_syscdrom.c
diff --git a/src/sdl_cdrom/macos/SDL_syscdrom.c b/src/sdl_cdrom/macos/SDL_syscdrom.c
new file mode 100644
index 000000000..229d0648c
--- /dev/null
+++ b/src/sdl_cdrom/macos/SDL_syscdrom.c
@@ -0,0 +1,530 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MACOS 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOS)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_MACOS*/
+
+/* MacOS functions for system-level CD-ROM audio control */
+
+#include <Devices.h>
+#include <Files.h>
+#include <LowMem.h> /* Use entry table macros, not functions in InterfaceLib */
+
+#include "../compat_SDL_cdrom.h"
+#include "../SDL_syscdrom.h"
+#include "SDL_syscdrom_c.h"
+
+/* Added by Matt Slot */
+#if !defined(LMGetUnitTableEntryCount)
+ #define LMGetUnitTableEntryCount() *(short *)0x01D2
+#endif
+
+/* The maximum number of CD-ROM drives we'll detect */
+#define MAX_DRIVES 26
+
+/* A list of available CD-ROM drives */
+static long SDL_cdversion = 0;
+static struct {
+ short dRefNum;
+ short driveNum;
+ long frames;
+ char name[256];
+ Boolean hasAudio;
+ } SDL_cdlist[MAX_DRIVES];
+static StringPtr gDriverName = "\p.AppleCD";
+
+/* The system-dependent CD control functions */
+static const char *SDL_SYS_CDName(int drive);
+static int SDL_SYS_CDOpen(int drive);
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause(SDL_CD *cdrom);
+static int SDL_SYS_CDResume(SDL_CD *cdrom);
+static int SDL_SYS_CDStop(SDL_CD *cdrom);
+static int SDL_SYS_CDEject(SDL_CD *cdrom);
+static void SDL_SYS_CDClose(SDL_CD *cdrom);
+
+static short SDL_SYS_ShortToBCD(short value)
+{
+ return((value % 10) + (value / 10) * 0x10); /* Convert value to BCD */
+}
+
+static short SDL_SYS_BCDToShort(short value)
+{
+ return((value % 0x10) + (value / 0x10) * 10); /* Convert value from BCD */
+}
+
+int SDL_SYS_CDInit(void)
+{
+ SInt16 dRefNum = 0;
+ SInt16 first, last;
+
+ SDL_numcds = 0;
+
+ /* Check that the software is available */
+ if (Gestalt(kGestaltAudioCDSelector, &SDL_cdversion) ||
+ !SDL_cdversion) return(0);
+
+ /* Fill in our driver capabilities */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /* Walk the list, count each AudioCD driver, and save the refnums */
+ first = -1;
+ last = 0 - LMGetUnitTableEntryCount();
+ for(dRefNum = first; dRefNum >= last; dRefNum--) {
+ Str255 driverName;
+ StringPtr namePtr;
+ DCtlHandle deviceEntry;
+
+ deviceEntry = GetDCtlEntry(dRefNum);
+ if (! deviceEntry) continue;
+
+ /* Is this an .AppleCD ? */
+ namePtr = (*deviceEntry)->dCtlFlags & (1L << dRAMBased) ?
+ ((StringPtr) ((DCtlPtr) deviceEntry)->dCtlDriver + 18) :
+ ((StringPtr) (*deviceEntry)->dCtlDriver + 18);
+ BlockMoveData(namePtr, driverName, namePtr[0]+1);
+ if (driverName[0] > gDriverName[0]) driverName[0] = gDriverName[0];
+ if (! EqualString(driverName, gDriverName, false, false)) continue;
+
+ /* Record the basic info for each drive */
+ SDL_cdlist[SDL_numcds].dRefNum = dRefNum;
+ BlockMoveData(namePtr + 1, SDL_cdlist[SDL_numcds].name, namePtr[0]);
+ SDL_cdlist[SDL_numcds].name[namePtr[0]] = 0;
+ SDL_cdlist[SDL_numcds].hasAudio = false;
+ SDL_numcds++;
+ }
+ return(0);
+}
+
+static const char *SDL_SYS_CDName(int drive)
+{
+ return(SDL_cdlist[drive].name);
+}
+
+static int get_drivenum(int drive)
+{
+ QHdr *driveQ = GetDrvQHdr();
+ DrvQEl *driveElem;
+
+ /* Update the drive number */
+ SDL_cdlist[drive].driveNum = 0;
+ if ( driveQ->qTail ) {
+ driveQ->qTail->qLink = 0;
+ }
+ for ( driveElem=(DrvQEl *)driveQ->qHead; driveElem;
+ driveElem = (DrvQEl *)driveElem->qLink ) {
+ if ( driveElem->dQRefNum == SDL_cdlist[drive].dRefNum ) {
+ SDL_cdlist[drive].driveNum = driveElem->dQDrive;
+ break;
+ }
+ }
+ return(SDL_cdlist[drive].driveNum);
+}
+
+static int SDL_SYS_CDOpen(int drive)
+{
+ return(drive);
+}
+
+static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
+{
+ CDCntrlParam cdpb;
+ CDTrackData tracks[SDL_MAX_TRACKS];
+ long i, leadout;
+
+ /* Get the number of tracks on the CD by examining the TOC */
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kReadTOC;
+ cdpb.csParam.words[0] = kGetTrackRange;
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+
+ cdrom->numtracks =
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) -
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS )
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ cdrom->status = CD_STOPPED;
+ cdrom->cur_track = 0; /* Apparently these are set elsewhere */
+ cdrom->cur_frame = 0; /* Apparently these are set elsewhere */
+
+
+ /* Get the lead out area of the CD by examining the TOC */
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kReadTOC;
+ cdpb.csParam.words[0] = kGetLeadOutArea;
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+
+ leadout = MSF_TO_FRAMES(
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]),
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]),
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[2]));
+
+ /* Get an array of track locations by examining the TOC */
+ SDL_memset(tracks, 0, sizeof(tracks));
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kReadTOC;
+ cdpb.csParam.words[0] = kGetTrackEntries; /* Type of Query */
+ * ((long *) (cdpb.csParam.words+1)) = (long) tracks;
+ cdpb.csParam.words[3] = cdrom->numtracks * sizeof(tracks[0]);
+ * ((char *) (cdpb.csParam.words+4)) = 1; /* First track */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+
+ /* Read all the track TOC entries */
+ SDL_cdlist[cdrom->id].hasAudio = false;
+ for ( i=0; i<cdrom->numtracks; ++i )
+ {
+ cdrom->track[i].id = i+1;
+ if (tracks[i].entry.control & kDataTrackMask)
+ cdrom->track[i].type = SDL_DATA_TRACK;
+ else
+ {
+ cdrom->track[i].type = SDL_AUDIO_TRACK;
+ SDL_cdlist[SDL_numcds].hasAudio = true;
+ }
+
+ cdrom->track[i].offset = MSF_TO_FRAMES(
+ SDL_SYS_BCDToShort(tracks[i].entry.min),
+ SDL_SYS_BCDToShort(tracks[i].entry.min),
+ SDL_SYS_BCDToShort(tracks[i].entry.frame));
+ cdrom->track[i].length = MSF_TO_FRAMES(
+ SDL_SYS_BCDToShort(tracks[i+1].entry.min),
+ SDL_SYS_BCDToShort(tracks[i+1].entry.min),
+ SDL_SYS_BCDToShort(tracks[i+1].entry.frame)) -
+ cdrom->track[i].offset;
+ }
+
+ /* Apparently SDL wants a fake last entry */
+ cdrom->track[i].offset = leadout;
+ cdrom->track[i].length = 0;
+
+ return(0);
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
+{
+ CDCntrlParam cdpb;
+ CDstatus status = CD_ERROR;
+ Boolean spinning = false;
+
+ if (position) *position = 0;
+
+ /* Get the number of tracks on the CD by examining the TOC */
+ if ( ! get_drivenum(cdrom->id) ) {
+ return(CD_TRAYEMPTY);
+ }
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kReadTOC;
+ cdpb.csParam.words[0] = kGetTrackRange;
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(CD_ERROR);
+ }
+
+ cdrom->numtracks =
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) -
+ SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1;
+ if ( cdrom->numtracks > SDL_MAX_TRACKS )
+ cdrom->numtracks = SDL_MAX_TRACKS;
+ cdrom->cur_track = 0; /* Apparently these are set elsewhere */
+ cdrom->cur_frame = 0; /* Apparently these are set elsewhere */
+
+
+ if (1 || SDL_cdlist[cdrom->id].hasAudio) {
+ /* Get the current playback status */
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioStatus;
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+
+ switch(cdpb.csParam.cd.status) {
+ case kStatusPlaying:
+ status = CD_PLAYING;
+ spinning = true;
+ break;
+ case kStatusPaused:
+ status = CD_PAUSED;
+ spinning = true;
+ break;
+ case kStatusMuted:
+ status = CD_PLAYING; /* What should I do here? */
+ spinning = true;
+ break;
+ case kStatusDone:
+ status = CD_STOPPED;
+ spinning = true;
+ break;
+ case kStatusStopped:
+ status = CD_STOPPED;
+ spinning = false;
+ break;
+ case kStatusError:
+ default:
+ status = CD_ERROR;
+ spinning = false;
+ break;
+ }
+
+ if (spinning && position) *position = MSF_TO_FRAMES(
+ SDL_SYS_BCDToShort(cdpb.csParam.cd.minute),
+ SDL_SYS_BCDToShort(cdpb.csParam.cd.second),
+ SDL_SYS_BCDToShort(cdpb.csParam.cd.frame));
+ }
+ else
+ status = CD_ERROR; /* What should I do here? */
+
+ return(status);
+}
+
+/* Start play */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ CDCntrlParam cdpb;
+
+ /* Pause the current audio playback to avoid audible artifacts */
+ if ( SDL_SYS_CDPause(cdrom) < 0 ) {
+ return(-1);
+ }
+
+ /* Specify the AudioCD playback mode */
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kSetPlayMode;
+ cdpb.csParam.bytes[0] = false; /* Repeat? */
+ cdpb.csParam.bytes[1] = kPlayModeSequential; /* Play mode */
+ /* \A5\A5\A5\CATreat as soft error, NEC Drive doesnt support this call \A5\A5\A5 */
+ PBControlSync((ParmBlkPtr) &cdpb);
+
+#if 1
+ /* Specify the end of audio playback */
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioStop;
+ cdpb.csParam.words[0] = kBlockPosition; /* Position Mode */
+ *(long *) (cdpb.csParam.words + 1) = start+length-1; /* Search Address */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+
+ /* Specify the start of audio playback, and start it */
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioPlay;
+ cdpb.csParam.words[0] = kBlockPosition; /* Position Mode */
+ *(long *) (cdpb.csParam.words + 1) = start+1; /* Search Address */
+ cdpb.csParam.words[3] = false; /* Stop address? */
+ cdpb.csParam.words[4] = kStereoPlayMode; /* Audio Play Mode */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+#else
+ /* Specify the end of audio playback */
+ FRAMES_TO_MSF(start+length, &m, &s, &f);
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioStop;
+ cdpb.csParam.words[0] = kTrackPosition; /* Position Mode */
+ cdpb.csParam.words[1] = 0; /* Search Address (hiword)*/
+ cdpb.csParam.words[2] = /* Search Address (loword)*/
+ SDL_SYS_ShortToBCD(cdrom->numtracks);
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+
+ /* Specify the start of audio playback, and start it */
+ FRAMES_TO_MSF(start, &m, &s, &f);
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioPlay;
+ cdpb.csParam.words[0] = kTrackPosition; /* Position Mode */
+ cdpb.csParam.words[1] = 0; /* Search Address (hiword)*/
+ cdpb.csParam.words[2] = SDL_SYS_ShortToBCD(1); /* Search Address (loword)*/
+ cdpb.csParam.words[3] = false; /* Stop address? */
+ cdpb.csParam.words[4] = kStereoPlayMode; /* Audio Play Mode */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+#endif
+
+ return(0);
+}
+
+/* Pause play */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ CDCntrlParam cdpb;
+
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioPause;
+ cdpb.csParam.words[0] = 0; /* Pause/Continue Flag (hiword) */
+ cdpb.csParam.words[1] = 1; /* Pause/Continue Flag (loword) */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+ return(0);
+}
+
+/* Resume play */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ CDCntrlParam cdpb;
+
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioPause;
+ cdpb.csParam.words[0] = 0; /* Pause/Continue Flag (hiword) */
+ cdpb.csParam.words[1] = 0; /* Pause/Continue Flag (loword) */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+ return(0);
+}
+
+/* Stop play */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ CDCntrlParam cdpb;
+
+ SDL_memset(&cdpb, 0, sizeof(cdpb));
+ cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
+ cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cdpb.csCode = kAudioStop;
+ cdpb.csParam.words[0] = 0; /* Position Mode */
+ cdpb.csParam.words[1] = 0; /* Search Address (hiword) */
+ cdpb.csParam.words[2] = 0; /* Search Address (loword) */
+ if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+ return(0);
+}
+
+/* Eject the CD-ROM */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ Boolean disk = false;
+ QHdr *driveQ = GetDrvQHdr();
+ DrvQEl *driveElem;
+ HParamBlockRec hpb;
+ ParamBlockRec cpb;
+
+ for ( driveElem = (DrvQEl *) driveQ->qHead; driveElem; driveElem =
+ (driveElem) ? ((DrvQEl *) driveElem->qLink) :
+ ((DrvQEl *) driveQ->qHead) ) {
+ if ( driveQ->qTail ) {
+ driveQ->qTail->qLink = 0;
+ }
+ if ( driveElem->dQRefNum != SDL_cdlist[cdrom->id].dRefNum ) {
+ continue;
+ }
+
+ /* Does drive contain mounted volume? If not, skip */
+ SDL_memset(&hpb, 0, sizeof(hpb));
+ hpb.volumeParam.ioVRefNum = driveElem->dQDrive;
+ if ( PBHGetVInfoSync(&hpb) != noErr ) {
+ continue;
+ }
+ if ( (UnmountVol(0, driveElem->dQDrive) == noErr) &&
+ (Eject(0, driveElem->dQDrive) == noErr) ) {
+ driveElem = 0; /* Clear pointer to reset our loop */
+ disk = true;
+ }
+ }
+
+ /* If no disk is present, just eject the tray */
+ if (! disk) {
+ SDL_memset(&cpb, 0, sizeof(cpb));
+ cpb.cntrlParam.ioVRefNum = 0; /* No Drive */
+ cpb.cntrlParam.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
+ cpb.cntrlParam.csCode = kEjectTheDisc;
+ if ( PBControlSync((ParmBlkPtr)&cpb) != noErr ) {
+ SDL_SetError("PBControlSync() failed");
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+/* Close the CD-ROM handle */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ return;
+}
+
+void SDL_SYS_CDQuit(void)
+{
+ while(SDL_numcds--)
+ SDL_memset(SDL_cdlist + SDL_numcds, 0, sizeof(SDL_cdlist[0]));
+}
+
+#endif /* SDL_CDROM_MACOS */
diff --git a/src/sdl_cdrom/macos/SDL_syscdrom_c.h b/src/sdl_cdrom/macos/SDL_syscdrom_c.h
new file mode 100644
index 000000000..e715a256d
--- /dev/null
+++ b/src/sdl_cdrom/macos/SDL_syscdrom_c.h
@@ -0,0 +1,140 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* This is the MacOS specific header for the SDL CD-ROM API
+ Contributed by Matt Slot
+ */
+
+/* AppleCD Control calls */
+#define kVerifyTheDisc 5 /* Returns noErr if there is disc inserted */
+#define kEjectTheDisc 7 /* Eject disc from drive */
+#define kUserEject 80 /* Enable/disable the CD-ROM eject button */
+#define kReadTOC 100 /* Extract various TOC information from the disc */
+#define kReadQ 101 /* Extract Q subcode info for the current track */
+#define kAudioTrackSearch 103 /* Start playback from the indicated position */
+#define kAudioPlay 104 /* Start playback from the indicated position */
+#define kAudioPause 105 /* Pause/continue the playback */
+#define kAudioStop 106 /* Stop playback at the indicated position */
+#define kAudioStatus 107 /* Return audio play status */
+#define kAudioControl 109 /* Set the output volume for the audio channels */
+#define kReadAudioVolume 112 /* Get the output volume for the audio channels */
+#define kSetTrackList 122 /* Set the track program for the audio CD to play */
+#define kGetTrackList 123 /* Get the track program the audio CD is playing */
+#define kGetTrackIndex 124 /* Get the track index the audio CD is playing */
+#define kSetPlayMode 125 /* Set the audio tracks play mode */
+#define kGetPlayMode 126 /* Get the audio tracks play mode */
+
+/* AppleCD Status calls */
+#define kGetDriveType 96 /* Get the type of the physical CD-ROM drive */
+#define kWhoIsThere 97 /* Get a bitmap of SCSI IDs the driver controls */
+#define kGetBlockSize 98 /* Get current block size of the CD-ROM drive */
+
+/* AppleCD other constants */
+#define kBlockPosition 0 /* Position at the specified logical block number */
+#define kAbsMSFPosition 1 /* Position at the specified Min/Sec/Frame (in BCD) */
+#define kTrackPosition 2 /* Position at the specified track number (in BCD) */
+#define kIndexPosition 3 /* Position at the nth track in program (in BCD) */
+
+#define kMutedPlayMode 0 /* Play the audio track with no output */
+#define kStereoPlayMode 9 /* Play the audio track in normal stereo */
+
+#define kControlFieldMask 0x0D /* Bits 3,2,0 in the nibble */
+#define kDataTrackMask 0x04 /* Indicates Data Track */
+
+#define kGetTrackRange 1 /* Query TOC for track numbers */
+#define kGetLeadOutArea 2 /* Query TOC for "Lead Out" end of audio data */
+#define kGetTrackEntries 3 /* Query TOC for track starts and data types */
+
+#define kStatusPlaying 0 /* Audio Play operation in progress */
+#define kStatusPaused 1 /* CD-ROM device in Hold Track ("Pause") state */
+#define kStatusMuted 2 /* MUTING-ON operation in progress */
+#define kStatusDone 3 /* Audio Play completed */
+#define kStatusError 4 /* Error occurred during audio play operation */
+#define kStatusStopped 5 /* Audio play operation not requested */
+
+#define kPlayModeSequential 0 /* Play tracks in order */
+#define kPlayModeShuffled 1 /* Play tracks randomly */
+#define kPlayModeProgrammed 2 /* Use custom playlist */
+
+/* AppleCD Gestalt selectors */
+#define kGestaltAudioCDSelector 'aucd'
+#define kDriverVersion52 0x00000520
+#define kDriverVersion51 0x00000510
+#define kDriverVersion50 0x00000500
+
+/* Drive type constants */
+#define kDriveAppleCD_SC 1
+#define kDriveAppleCD_SCPlus_or_150 2
+#define kDriveAppleCD_300_or_300Plus 3
+
+/* Misc constants */
+#define kFirstSCSIDevice -33
+#define kLastSCSIDevice -40
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#endif
+
+/* AppleCD driver parameter block */
+typedef struct CDCntrlParam {
+ QElemPtr qLink;
+ short qType;
+ short ioTrap;
+ Ptr ioCmdAddr;
+ IOCompletionUPP ioCompletion;
+ OSErr ioResult;
+ StringPtr ioNamePtr;
+ short ioVRefNum;
+ short ioCRefNum;
+ short csCode;
+
+ union {
+ long longs[6];
+ short words[11];
+ unsigned char bytes[22];
+ struct {
+ unsigned char status;
+ unsigned char play;
+ unsigned char control;
+ unsigned char minute;
+ unsigned char second;
+ unsigned char frame;
+ } cd;
+ } csParam;
+
+ } CDCntrlParam, *CDCntrlParamPtr;
+
+typedef union CDTrackData {
+ long value; /* Treat as a longword value */
+ struct {
+ unsigned char reserved : 4; /* Unused by AppleCD driver */
+ unsigned char control : 4; /* Track flags (data track?) */
+ unsigned char min; /* Start of track (BCD) */
+ unsigned char sec; /* Start of track (BCD) */
+ unsigned char frame; /* Start of track (BCD) */
+ } entry; /* Broken into fields */
+ } CDTrackData, *CDTrackPtr;
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#endif
diff --git a/src/sdl_cdrom/macosx/AudioFilePlayer.c b/src/sdl_cdrom/macosx/AudioFilePlayer.c
new file mode 100644
index 000000000..d702053c5
--- /dev/null
+++ b/src/sdl_cdrom/macosx/AudioFilePlayer.c
@@ -0,0 +1,366 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+
+ This file based on Apple sample code. We haven't changed the file name,
+ so if you want to see the original search for it on apple.com/developer
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX)
+
+#include "SDL_config.h"
+#include "SDL_endian.h"
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ AudioFilePlayer.cpp
+*/
+#include "AudioFilePlayer.h"
+
+/*
+void ThrowResult (OSStatus result, const char* str)
+{
+ SDL_SetError ("Error: %s %d", str, result);
+ throw result;
+}
+*/
+
+#if DEBUG
+static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
+{
+ if (!inDesc) {
+ printf ("Can't print a NULL desc!\n");
+ return;
+ }
+
+ printf ("- - - - - - - - - - - - - - - - - - - -\n");
+ printf (" Sample Rate:%f\n", inDesc->mSampleRate);
+ printf (" Format ID:%s\n", (char*)&inDesc->mFormatID);
+ printf (" Format Flags:%lX\n", inDesc->mFormatFlags);
+ printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
+ printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
+ printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
+ printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
+ printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
+ printf ("- - - - - - - - - - - - - - - - - - - -\n");
+}
+#endif
+
+
+static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit *inDestUnit)
+{
+ /*if (afp->mConnected) throw static_cast<OSStatus>(-1);*/ /* can't set dest if already engaged */
+ if (afp->mConnected)
+ return 0 ;
+
+ SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
+
+ OSStatus result = noErr;
+
+
+ /* we can "down" cast a component instance to a component */
+ ComponentDescription desc;
+ result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
+ if (result) return 0; /*THROW_RESULT("GetComponentInfo")*/
+
+ /* we're going to use this to know which convert routine to call
+ a v1 audio unit will have a type of 'aunt'
+ a v2 audio unit will have one of several different types. */
+ if (desc.componentType != kAudioUnitType_Output) {
+ result = badComponentInstance;
+ /*THROW_RESULT("BAD COMPONENT")*/
+ if (result) return 0;
+ }
+
+ /* Set the input format of the audio unit. */
+ result = AudioUnitSetProperty (*inDestUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 0,
+ &afp->mFileDescription,
+ sizeof (afp->mFileDescription));
+ /*THROW_RESULT("AudioUnitSetProperty")*/
+ if (result) return 0;
+ return 1;
+}
+
+static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
+{
+ afp->mNotifier = inNotifier;
+ afp->mRefCon = inRefCon;
+}
+
+static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
+{
+ return afp->mConnected;
+}
+
+static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
+{
+ return afp->mPlayUnit;
+}
+
+static void AudioFilePlayer_Print(AudioFilePlayer *afp)
+{
+#if DEBUG
+ printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
+ printf ("- - - - - - - - - - - - - - \n");
+#endif
+}
+
+static void AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
+{
+ SInt64 position = frame * 2352;
+
+ afp->mStartFrame = frame;
+ afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
+}
+
+
+static int AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
+{
+ return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
+}
+
+static void AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
+{
+ SInt64 position = frame * 2352;
+
+ afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
+}
+
+void delete_AudioFilePlayer(AudioFilePlayer *afp)
+{
+ if (afp != NULL)
+ {
+ afp->Disconnect(afp);
+
+ if (afp->mAudioFileManager) {
+ delete_AudioFileManager(afp->mAudioFileManager);
+ afp->mAudioFileManager = 0;
+ }
+
+ if (afp->mForkRefNum) {
+ FSCloseFork (afp->mForkRefNum);
+ afp->mForkRefNum = 0;
+ }
+ SDL_free(afp);
+ }
+}
+
+static int AudioFilePlayer_Connect(AudioFilePlayer *afp)
+{
+#if DEBUG
+ printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
+#endif
+ if (!afp->mConnected)
+ {
+ if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
+ return 0;
+
+ /* set the render callback for the file data to be supplied to the sound converter AU */
+ afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
+ afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
+
+ OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input,
+ 0,
+ &afp->mInputCallback,
+ sizeof(afp->mInputCallback));
+ if (result) return 0; /*THROW_RESULT("AudioUnitSetProperty")*/
+ afp->mConnected = 1;
+ }
+
+ return 1;
+}
+
+/* warning noted, now please go away ;-) */
+/* #warning This should redirect the calling of notification code to some other thread */
+static void AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
+{
+ if (afp->mNotifier) {
+ (*afp->mNotifier) (afp->mRefCon, inStatus);
+ } else {
+ SDL_SetError ("Notification posted with no notifier in place");
+
+ if (inStatus == kAudioFilePlay_FileIsFinished)
+ afp->Disconnect(afp);
+ else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
+ afp->Disconnect(afp);
+ }
+}
+
+static void AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
+{
+#if DEBUG
+ printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
+#endif
+ if (afp->mConnected)
+ {
+ afp->mConnected = 0;
+
+ afp->mInputCallback.inputProc = 0;
+ afp->mInputCallback.inputProcRefCon = 0;
+ OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input,
+ 0,
+ &afp->mInputCallback,
+ sizeof(afp->mInputCallback));
+ if (result)
+ SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
+
+ afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
+ }
+}
+
+typedef struct {
+ UInt32 offset;
+ UInt32 blockSize;
+} SSNDData;
+
+static int AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
+{
+ ContainerChunk chunkHeader;
+ ChunkHeader chunk;
+ SSNDData ssndData;
+
+ OSErr result;
+ HFSUniStr255 dfName;
+ ByteCount actual;
+ SInt64 offset;
+
+ /* Open the data fork of the input file */
+ result = FSGetDataForkName(&dfName);
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")*/
+
+ result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")*/
+
+ /* Read the file header, and check if it's indeed an AIFC file */
+ result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
+
+ if (SDL_SwapBE32(chunkHeader.ckID) != 'FORM') {
+ result = -1;
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");*/
+ }
+
+ if (SDL_SwapBE32(chunkHeader.formType) != 'AIFC') {
+ result = -1;
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");*/
+ }
+
+ /* Search for the SSND chunk. We ignore all compression etc. information
+ in other chunks. Of course that is kind of evil, but for now we are lazy
+ and rely on the cdfs to always give us the same fixed format.
+ TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
+ */
+ offset = 0;
+ do {
+ result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
+
+ chunk.ckID = SDL_SwapBE32(chunk.ckID);
+ chunk.ckSize = SDL_SwapBE32(chunk.ckSize);
+
+ /* Skip the chunk data */
+ offset = chunk.ckSize;
+ } while (chunk.ckID != 'SSND');
+
+ /* Read the header of the SSND chunk. After this, we are positioned right
+ at the start of the audio data. */
+ result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
+
+ ssndData.offset = SDL_SwapBE32(ssndData.offset);
+
+ result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
+ if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")*/
+
+ /* Data size */
+ *outFileDataSize = chunk.ckSize - ssndData.offset - 8;
+
+ /* File format */
+ afp->mFileDescription.mSampleRate = 44100;
+ afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
+ afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
+ afp->mFileDescription.mBytesPerPacket = 4;
+ afp->mFileDescription.mFramesPerPacket = 1;
+ afp->mFileDescription.mBytesPerFrame = 4;
+ afp->mFileDescription.mChannelsPerFrame = 2;
+ afp->mFileDescription.mBitsPerChannel = 16;
+
+ return 1;
+}
+
+AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
+{
+ SInt64 fileDataSize = 0;
+
+ AudioFilePlayer *afp = (AudioFilePlayer *) SDL_malloc(sizeof (AudioFilePlayer));
+ if (afp == NULL)
+ return NULL;
+ SDL_memset(afp, '\0', sizeof (*afp));
+
+ #define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
+ SET_AUDIOFILEPLAYER_METHOD(SetDestination);
+ SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
+ SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
+ SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
+ SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
+ SET_AUDIOFILEPLAYER_METHOD(Connect);
+ SET_AUDIOFILEPLAYER_METHOD(Disconnect);
+ SET_AUDIOFILEPLAYER_METHOD(DoNotification);
+ SET_AUDIOFILEPLAYER_METHOD(IsConnected);
+ SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
+ SET_AUDIOFILEPLAYER_METHOD(Print);
+ SET_AUDIOFILEPLAYER_METHOD(OpenFile);
+ #undef SET_AUDIOFILEPLAYER_METHOD
+
+ if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
+ {
+ SDL_free(afp);
+ return NULL;
+ }
+
+ /* we want about 4 seconds worth of data for the buffer */
+ int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
+
+#if DEBUG
+ printf("File format:\n");
+ PrintStreamDesc (&afp->mFileDescription);
+#endif
+
+ afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
+ fileDataSize,
+ bytesPerSecond);
+ if (afp->mAudioFileManager == NULL)
+ {
+ delete_AudioFilePlayer(afp);
+ return NULL;
+ }
+
+ return afp;
+}
+
+#endif /* SDL_CDROM_MACOSX */
diff --git a/src/sdl_cdrom/macosx/AudioFilePlayer.h b/src/sdl_cdrom/macosx/AudioFilePlayer.h
new file mode 100644
index 000000000..886d017a5
--- /dev/null
+++ b/src/sdl_cdrom/macosx/AudioFilePlayer.h
@@ -0,0 +1,178 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+
+ This file based on Apple sample code. We haven't changed the file name,
+ so if you want to see the original search for it on apple.com/developer
+*/
+#include "SDL_config.h"
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ AudioFilePlayer.h
+*/
+#ifndef __AudioFilePlayer_H__
+#define __AudioFilePlayer_H__
+
+#include <CoreServices/CoreServices.h>
+
+#include <AudioUnit/AudioUnit.h>
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050
+#include <AudioUnit/AUNTComponent.h>
+#endif
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1050)
+typedef SInt16 FSIORefNum;
+#endif
+
+#include "SDL_error.h"
+
+const char* AudioFilePlayerErrorStr (OSStatus error);
+
+/*
+void ThrowResult (OSStatus result, const char *str);
+
+#define THROW_RESULT(str) \
+ if (result) { \
+ ThrowResult (result, str); \
+ }
+*/
+
+typedef void (*AudioFilePlayNotifier)(void *inRefCon,
+ OSStatus inStatus);
+
+enum {
+ kAudioFilePlayErr_FilePlayUnderrun = -10000,
+ kAudioFilePlay_FileIsFinished = -10001,
+ kAudioFilePlay_PlayerIsUninitialized = -10002
+};
+
+
+struct S_AudioFileManager;
+
+#pragma mark __________ AudioFilePlayer
+typedef struct S_AudioFilePlayer
+{
+/*public:*/
+ int (*SetDestination)(struct S_AudioFilePlayer *afp, AudioUnit *inDestUnit);
+ void (*SetNotifier)(struct S_AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon);
+ void (*SetStartFrame)(struct S_AudioFilePlayer *afp, int frame); /* seek in the file */
+ int (*GetCurrentFrame)(struct S_AudioFilePlayer *afp); /* get the current frame position */
+ void (*SetStopFrame)(struct S_AudioFilePlayer *afp, int frame); /* set limit in the file */
+ int (*Connect)(struct S_AudioFilePlayer *afp);
+ void (*Disconnect)(struct S_AudioFilePlayer *afp);
+ void (*DoNotification)(struct S_AudioFilePlayer *afp, OSStatus inError);
+ int (*IsConnected)(struct S_AudioFilePlayer *afp);
+ AudioUnit (*GetDestUnit)(struct S_AudioFilePlayer *afp);
+ void (*Print)(struct S_AudioFilePlayer *afp);
+
+/*private:*/
+ AudioUnit mPlayUnit;
+ FSIORefNum mForkRefNum;
+
+ AURenderCallbackStruct mInputCallback;
+
+ AudioStreamBasicDescription mFileDescription;
+
+ int mConnected;
+
+ struct S_AudioFileManager* mAudioFileManager;
+
+ AudioFilePlayNotifier mNotifier;
+ void* mRefCon;
+
+ int mStartFrame;
+
+#pragma mark __________ Private_Methods
+
+ int (*OpenFile)(struct S_AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileSize);
+} AudioFilePlayer;
+
+
+AudioFilePlayer *new_AudioFilePlayer(const FSRef *inFileRef);
+void delete_AudioFilePlayer(AudioFilePlayer *afp);
+
+
+
+#pragma mark __________ AudioFileManager
+typedef struct S_AudioFileManager
+{
+/*public:*/
+ /* this method should NOT be called by an object of this class
+ as it is called by the parent's Disconnect() method */
+ void (*Disconnect)(struct S_AudioFileManager *afm);
+ int (*DoConnect)(struct S_AudioFileManager *afm);
+ OSStatus (*Read)(struct S_AudioFileManager *afm, char *buffer, ByteCount *len);
+ const char* (*GetFileBuffer)(struct S_AudioFileManager *afm);
+ const AudioFilePlayer *(*GetParent)(struct S_AudioFileManager *afm);
+ void (*SetPosition)(struct S_AudioFileManager *afm, SInt64 pos); /* seek/rewind in the file */
+ int (*GetByteCounter)(struct S_AudioFileManager *afm); /* return actual bytes streamed to audio hardware */
+ void (*SetEndOfFile)(struct S_AudioFileManager *afm, SInt64 pos); /* set the "EOF" (will behave just like it reached eof) */
+
+/*protected:*/
+ AudioFilePlayer* mParent;
+ SInt16 mForkRefNum;
+ SInt64 mAudioDataOffset;
+
+ char* mFileBuffer;
+
+ int mByteCounter;
+
+ int mReadFromFirstBuffer;
+ int mLockUnsuccessful;
+ int mIsEngaged;
+
+ int mNumTimesAskedSinceFinished;
+
+
+ void* mTmpBuffer;
+ UInt32 mBufferSize;
+ UInt32 mBufferOffset;
+/*public:*/
+ UInt32 mChunkSize;
+ SInt64 mFileLength;
+ SInt64 mReadFilePosition;
+ int mWriteToFirstBuffer;
+ int mFinishedReadingData;
+
+/*protected:*/
+ OSStatus (*Render)(struct S_AudioFileManager *afm, AudioBufferList *ioData);
+ OSStatus (*GetFileData)(struct S_AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize);
+ void (*AfterRender)(struct S_AudioFileManager *afm);
+
+/*public:*/
+ /*static*/
+ OSStatus (*FileInputProc)(void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData);
+} AudioFileManager;
+
+
+AudioFileManager *new_AudioFileManager (AudioFilePlayer *inParent,
+ SInt16 inForkRefNum,
+ SInt64 inFileLength,
+ UInt32 inChunkSize);
+
+void delete_AudioFileManager(AudioFileManager *afm);
+
+#endif
+
diff --git a/src/sdl_cdrom/macosx/AudioFileReaderThread.c b/src/sdl_cdrom/macosx/AudioFileReaderThread.c
new file mode 100644
index 000000000..1b48d6404
--- /dev/null
+++ b/src/sdl_cdrom/macosx/AudioFileReaderThread.c
@@ -0,0 +1,616 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+
+ This file based on Apple sample code. We haven't changed the file name,
+ so if you want to see the original search for it on apple.com/developer
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX)
+
+#include "SDL_config.h"
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ AudioFileManager.cpp
+*/
+#include "AudioFilePlayer.h"
+#include <mach/mach.h> /* used for setting policy of thread */
+#include "SDLOSXCAGuard.h"
+#include <pthread.h>
+
+/*#include <list>*/
+
+/*typedef void *FileData;*/
+typedef struct S_FileData
+{
+ AudioFileManager *obj;
+ struct S_FileData *next;
+} FileData;
+
+
+typedef struct S_FileReaderThread {
+/*public:*/
+ SDLOSXCAGuard* (*GetGuard)(struct S_FileReaderThread *frt);
+ void (*AddReader)(struct S_FileReaderThread *frt);
+ void (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
+ int (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
+
+ int mThreadShouldDie;
+
+/*private:*/
+ /*typedef std::list<AudioFileManager*> FileData;*/
+
+ SDLOSXCAGuard *mGuard;
+ UInt32 mThreadPriority;
+
+ int mNumReaders;
+ FileData *mFileData;
+
+
+ void (*ReadNextChunk)(struct S_FileReaderThread *frt);
+ int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt);
+ /*static*/
+ UInt32 (*GetThreadBasePriority)(pthread_t inThread);
+ /*static*/
+ void* (*DiskReaderEntry)(void *inRefCon);
+} FileReaderThread;
+
+
+static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
+{
+ return frt->mGuard;
+}
+
+/* returns 1 if succeeded */
+static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem)
+{
+ int didLock = 0;
+ int succeeded = 0;
+ if (frt->mGuard->Try(frt->mGuard, &didLock))
+ {
+ /*frt->mFileData.push_back (inItem);*/
+ /* !!! FIXME: this could be faster with a "tail" member. --ryan. */
+ FileData *i = frt->mFileData;
+ FileData *prev = NULL;
+
+ FileData *newfd = (FileData *) SDL_malloc(sizeof (FileData));
+ newfd->obj = inItem;
+ newfd->next = NULL;
+
+ while (i != NULL) { prev = i; i = i->next; }
+ if (prev == NULL)
+ frt->mFileData = newfd;
+ else
+ prev->next = newfd;
+
+ frt->mGuard->Notify(frt->mGuard);
+ succeeded = 1;
+
+ if (didLock)
+ frt->mGuard->Unlock(frt->mGuard);
+ }
+
+ return succeeded;
+}
+
+static void FileReaderThread_AddReader(FileReaderThread *frt)
+{
+ if (frt->mNumReaders == 0)
+ {
+ frt->mThreadShouldDie = 0;
+ frt->StartFixedPriorityThread (frt);
+ }
+ frt->mNumReaders++;
+}
+
+static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem)
+{
+ if (frt->mNumReaders > 0)
+ {
+ int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
+
+ /*frt->mFileData.remove (inItem);*/
+ FileData *i = frt->mFileData;
+ FileData *prev = NULL;
+ while (i != NULL)
+ {
+ FileData *next = i->next;
+ if (i->obj != inItem)
+ prev = i;
+ else
+ {
+ if (prev == NULL)
+ frt->mFileData = next;
+ else
+ prev->next = next;
+ SDL_free(i);
+ }
+ i = next;
+ }
+
+ if (--frt->mNumReaders == 0) {
+ frt->mThreadShouldDie = 1;
+ frt->mGuard->Notify(frt->mGuard); /* wake up thread so it will quit */
+ frt->mGuard->Wait(frt->mGuard); /* wait for thread to die */
+ }
+
+ if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
+ }
+}
+
+static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt)
+{
+ pthread_attr_t theThreadAttrs;
+ pthread_t pThread;
+
+ OSStatus result = pthread_attr_init(&theThreadAttrs);
+ if (result) return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")*/
+
+ result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
+ if (result) return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")*/
+
+ result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
+ if (result) return 0; /*THROW_RESULT("pthread_create - Create and start the thread.")*/
+
+ pthread_attr_destroy(&theThreadAttrs);
+
+ /* we've now created the thread and started it
+ we'll now set the priority of the thread to the nominated priority
+ and we'll also make the thread fixed */
+ thread_extended_policy_data_t theFixedPolicy;
+ thread_precedence_policy_data_t thePrecedencePolicy;
+ SInt32 relativePriority;
+
+ /* make thread fixed */
+ theFixedPolicy.timeshare = 0; /* set to 1 for a non-fixed thread */
+ result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
+ if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")*/
+ /* set priority */
+ /* precedency policy's "importance" value is relative to spawning thread's priority */
+ relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
+
+ thePrecedencePolicy.importance = relativePriority;
+ result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
+ if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.")*/
+
+ return 1;
+}
+
+static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread)
+{
+ thread_basic_info_data_t threadInfo;
+ policy_info_data_t thePolicyInfo;
+ unsigned int count;
+
+ /* get basic info */
+ count = THREAD_BASIC_INFO_COUNT;
+ thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
+
+ switch (threadInfo.policy) {
+ case POLICY_TIMESHARE:
+ count = POLICY_TIMESHARE_INFO_COUNT;
+ thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
+ return thePolicyInfo.ts.base_priority;
+ break;
+
+ case POLICY_FIFO:
+ count = POLICY_FIFO_INFO_COUNT;
+ thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
+ if (thePolicyInfo.fifo.depressed) {
+ return thePolicyInfo.fifo.depress_priority;
+ } else {
+ return thePolicyInfo.fifo.base_priority;
+ }
+ break;
+
+ case POLICY_RR:
+ count = POLICY_RR_INFO_COUNT;
+ thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
+ if (thePolicyInfo.rr.depressed) {
+ return thePolicyInfo.rr.depress_priority;
+ } else {
+ return thePolicyInfo.rr.base_priority;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static void *FileReaderThread_DiskReaderEntry (void *inRefCon)
+{
+ FileReaderThread *frt = (FileReaderThread *)inRefCon;
+ frt->ReadNextChunk(frt);
+ #if DEBUG
+ printf ("finished with reading file\n");
+ #endif
+
+ return 0;
+}
+
+static void FileReaderThread_ReadNextChunk (FileReaderThread *frt)
+{
+ OSStatus result;
+ ByteCount dataChunkSize;
+ AudioFileManager* theItem = 0;
+
+ for (;;)
+ {
+ { /* this is a scoped based lock */
+ int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
+
+ if (frt->mThreadShouldDie) {
+ frt->mGuard->Notify(frt->mGuard);
+ if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
+ return;
+ }
+
+ /*if (frt->mFileData.empty())*/
+ if (frt->mFileData == NULL)
+ {
+ frt->mGuard->Wait(frt->mGuard);
+ }
+
+ /* kill thread */
+ if (frt->mThreadShouldDie) {
+
+ frt->mGuard->Notify(frt->mGuard);
+ if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
+ return;
+ }
+
+ /*theItem = frt->mFileData.front();*/
+ /*frt->mFileData.pop_front();*/
+ theItem = NULL;
+ if (frt->mFileData != NULL)
+ {
+ FileData *next = frt->mFileData->next;
+ theItem = frt->mFileData->obj;
+ SDL_free(frt->mFileData);
+ frt->mFileData = next;
+ }
+
+ if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
+ }
+
+ if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
+ dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
+ else
+ dataChunkSize = theItem->mChunkSize;
+
+ /* this is the exit condition for the thread */
+ if (dataChunkSize <= 0) {
+ theItem->mFinishedReadingData = 1;
+ continue;
+ }
+ /* construct pointer */
+ char* writePtr = (char *) (theItem->GetFileBuffer(theItem) +
+ (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
+
+ /* read data */
+ result = theItem->Read(theItem, writePtr, &dataChunkSize);
+ if (result != noErr && result != eofErr) {
+ AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem);
+ afp->DoNotification(afp, result);
+ continue;
+ }
+
+ if (dataChunkSize != theItem->mChunkSize)
+ {
+ writePtr += dataChunkSize;
+
+ /* can't exit yet.. we still have to pass the partial buffer back */
+ SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
+ }
+
+ theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
+
+ if (result == eofErr)
+ theItem->mReadFilePosition = theItem->mFileLength;
+ else
+ theItem->mReadFilePosition += dataChunkSize; /* increment count */
+ }
+}
+
+void delete_FileReaderThread(FileReaderThread *frt)
+{
+ if (frt != NULL)
+ {
+ delete_SDLOSXCAGuard(frt->mGuard);
+ SDL_free(frt);
+ }
+}
+
+FileReaderThread *new_FileReaderThread ()
+{
+ FileReaderThread *frt = (FileReaderThread *) SDL_malloc(sizeof (FileReaderThread));
+ if (frt == NULL)
+ return NULL;
+ SDL_memset(frt, '\0', sizeof (*frt));
+
+ frt->mGuard = new_SDLOSXCAGuard();
+ if (frt->mGuard == NULL)
+ {
+ SDL_free(frt);
+ return NULL;
+ }
+
+ #define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
+ SET_FILEREADERTHREAD_METHOD(GetGuard);
+ SET_FILEREADERTHREAD_METHOD(AddReader);
+ SET_FILEREADERTHREAD_METHOD(RemoveReader);
+ SET_FILEREADERTHREAD_METHOD(TryNextRead);
+ SET_FILEREADERTHREAD_METHOD(ReadNextChunk);
+ SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread);
+ SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority);
+ SET_FILEREADERTHREAD_METHOD(DiskReaderEntry);
+ #undef SET_FILEREADERTHREAD_METHOD
+
+ frt->mThreadPriority = 62;
+ return frt;
+}
+
+
+static FileReaderThread *sReaderThread;
+
+
+static int AudioFileManager_DoConnect (AudioFileManager *afm)
+{
+ if (!afm->mIsEngaged)
+ {
+ OSStatus result;
+
+ /*afm->mReadFilePosition = 0;*/
+ afm->mFinishedReadingData = 0;
+
+ afm->mNumTimesAskedSinceFinished = 0;
+ afm->mLockUnsuccessful = 0;
+
+ ByteCount dataChunkSize;
+
+ if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
+ dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
+ else
+ dataChunkSize = afm->mChunkSize;
+
+ result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
+ if (result) return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read")*/
+
+ afm->mReadFilePosition += dataChunkSize;
+
+ afm->mWriteToFirstBuffer = 0;
+ afm->mReadFromFirstBuffer = 1;
+
+ sReaderThread->AddReader(sReaderThread);
+
+ afm->mIsEngaged = 1;
+ }
+ /*
+ else
+ throw static_cast<OSStatus>(-1); */ /* thread has already been started */
+
+ return 1;
+}
+
+static void AudioFileManager_Disconnect (AudioFileManager *afm)
+{
+ if (afm->mIsEngaged)
+ {
+ sReaderThread->RemoveReader (sReaderThread, afm);
+ afm->mIsEngaged = 0;
+ }
+}
+
+static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, ByteCount *len)
+{
+ return FSReadFork (afm->mForkRefNum,
+ fsFromStart,
+ afm->mReadFilePosition + afm->mAudioDataOffset,
+ *len,
+ buffer,
+ len);
+}
+
+static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize)
+{
+ if (afm->mFinishedReadingData)
+ {
+ ++afm->mNumTimesAskedSinceFinished;
+ *inOutDataSize = 0;
+ *inOutData = 0;
+ return noErr;
+ }
+
+ if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
+ #if DEBUG
+ printf ("* * * * * * * Can't keep up with reading file\n");
+ #endif
+
+ afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun);
+ *inOutDataSize = 0;
+ *inOutData = 0;
+ } else {
+ *inOutDataSize = afm->mChunkSize;
+ *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize);
+ }
+
+ afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
+
+ afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
+
+ return noErr;
+}
+
+static void AudioFileManager_AfterRender (AudioFileManager *afm)
+{
+ if (afm->mNumTimesAskedSinceFinished > 0)
+ {
+ int didLock = 0;
+ SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
+ if (guard->Try(guard, &didLock)) {
+ afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished);
+ if (didLock)
+ guard->Unlock(guard);
+ }
+ }
+
+ if (afm->mLockUnsuccessful)
+ afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
+}
+
+static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos)
+{
+ if (pos < 0 || pos >= afm->mFileLength) {
+ SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
+ (unsigned int)pos, (unsigned int)afm->mFileLength);
+ pos = 0;
+ }
+
+ afm->mReadFilePosition = pos;
+}
+
+static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos)
+{
+ if (pos <= 0 || pos > afm->mFileLength) {
+ SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
+ pos = afm->mFileLength;
+ }
+
+ afm->mFileLength = pos;
+}
+
+static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
+{
+ return afm->mFileBuffer;
+}
+
+const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
+{
+ return afm->mParent;
+}
+
+static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
+{
+ return afm->mByteCounter;
+}
+
+static OSStatus AudioFileManager_FileInputProc (void *inRefCon,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList *ioData)
+{
+ AudioFileManager* afm = (AudioFileManager*)inRefCon;
+ return afm->Render(afm, ioData);
+}
+
+static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBufferList *ioData)
+{
+ OSStatus result = noErr;
+ AudioBuffer *abuf;
+ UInt32 i;
+
+ for (i = 0; i < ioData->mNumberBuffers; i++) {
+ abuf = &ioData->mBuffers[i];
+ if (afm->mBufferOffset >= afm->mBufferSize) {
+ result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize);
+ if (result) {
+ SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
+ afm->mParent->DoNotification(afm->mParent, result);
+ return result;
+ }
+
+ afm->mBufferOffset = 0;
+ }
+
+ if (abuf->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
+ abuf->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
+ abuf->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset;
+ afm->mBufferOffset += abuf->mDataByteSize;
+
+ afm->mByteCounter += abuf->mDataByteSize;
+ afm->AfterRender(afm);
+ }
+ return result;
+}
+
+
+void delete_AudioFileManager (AudioFileManager *afm)
+{
+ if (afm != NULL) {
+ if (afm->mFileBuffer) {
+ free(afm->mFileBuffer);
+ }
+
+ SDL_free(afm);
+ }
+}
+
+
+AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent,
+ SInt16 inForkRefNum,
+ SInt64 inFileLength,
+ UInt32 inChunkSize)
+{
+ AudioFileManager *afm;
+
+ if (sReaderThread == NULL)
+ {
+ sReaderThread = new_FileReaderThread();
+ if (sReaderThread == NULL)
+ return NULL;
+ }
+
+ afm = (AudioFileManager *) SDL_malloc(sizeof (AudioFileManager));
+ if (afm == NULL)
+ return NULL;
+ SDL_memset(afm, '\0', sizeof (*afm));
+
+ #define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
+ SET_AUDIOFILEMANAGER_METHOD(Disconnect);
+ SET_AUDIOFILEMANAGER_METHOD(DoConnect);
+ SET_AUDIOFILEMANAGER_METHOD(Read);
+ SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer);
+ SET_AUDIOFILEMANAGER_METHOD(GetParent);
+ SET_AUDIOFILEMANAGER_METHOD(SetPosition);
+ SET_AUDIOFILEMANAGER_METHOD(GetByteCounter);
+ SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile);
+ SET_AUDIOFILEMANAGER_METHOD(Render);
+ SET_AUDIOFILEMANAGER_METHOD(GetFileData);
+ SET_AUDIOFILEMANAGER_METHOD(AfterRender);
+ SET_AUDIOFILEMANAGER_METHOD(FileInputProc);
+ #undef SET_AUDIOFILEMANAGER_METHOD
+
+ afm->mParent = inParent;
+ afm->mForkRefNum = inForkRefNum;
+ afm->mBufferSize = inChunkSize;
+ afm->mBufferOffset = inChunkSize;
+ afm->mChunkSize = inChunkSize;
+ afm->mFileLength = inFileLength;
+ afm->mFileBuffer = (char*) SDL_malloc(afm->mChunkSize * 2);
+ FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
+ assert (afm->mFileBuffer != NULL);
+ return afm;
+}
+
+#endif /* SDL_CDROM_MACOSX */
diff --git a/src/sdl_cdrom/macosx/CDPlayer.c b/src/sdl_cdrom/macosx/CDPlayer.c
new file mode 100644
index 000000000..f86973c4e
--- /dev/null
+++ b/src/sdl_cdrom/macosx/CDPlayer.c
@@ -0,0 +1,643 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX)
+
+#include "SDL_config.h"
+
+#include "CDPlayer.h"
+#include "AudioFilePlayer.h"
+#include "SDLOSXCAGuard.h"
+
+/* we're exporting these functions into C land for SDL_syscdrom.c */
+/*extern "C" {*/
+
+/*///////////////////////////////////////////////////////////////////////////
+ Constants
+ //////////////////////////////////////////////////////////////////////////*/
+
+#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H') /* 'JH'; this avoids compiler warning */
+
+/* XML PList keys */
+#define kRawTOCDataString "Format 0x02 TOC Data"
+#define kSessionsString "Sessions"
+#define kSessionTypeString "Session Type"
+#define kTrackArrayString "Track Array"
+#define kFirstTrackInSessionString "First Track"
+#define kLastTrackInSessionString "Last Track"
+#define kLeadoutBlockString "Leadout Block"
+#define kDataKeyString "Data"
+#define kPointKeyString "Point"
+#define kSessionNumberKeyString "Session Number"
+#define kStartBlockKeyString "Start Block"
+
+/*///////////////////////////////////////////////////////////////////////////
+ Globals
+ //////////////////////////////////////////////////////////////////////////*/
+
+#pragma mark -- Globals --
+
+static int playBackWasInit = 0;
+static AudioUnit theUnit;
+static AudioFilePlayer* thePlayer = NULL;
+static CDPlayerCompletionProc completionProc = NULL;
+static SDL_mutex *apiMutex = NULL;
+static SDL_sem *callbackSem;
+static SDL_CD* theCDROM;
+
+/*///////////////////////////////////////////////////////////////////////////
+ Prototypes
+ //////////////////////////////////////////////////////////////////////////*/
+
+#pragma mark -- Prototypes --
+
+static OSStatus CheckInit ();
+
+static void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus);
+
+static int RunCallBackThread (void* inRefCon);
+
+
+#pragma mark -- Public Functions --
+
+void Lock ()
+{
+ if (!apiMutex) {
+ apiMutex = SDL_CreateMutex();
+ }
+ SDL_mutexP(apiMutex);
+}
+
+void Unlock ()
+{
+ SDL_mutexV(apiMutex);
+}
+
+int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes)
+{
+ int volumeIndex;
+ int cdVolumeCount = 0;
+ OSStatus result = noErr;
+
+ for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
+ {
+ FSVolumeRefNum actualVolume;
+ FSVolumeInfo volumeInfo;
+
+ memset (&volumeInfo, 0, sizeof(volumeInfo));
+
+ result = FSGetVolumeInfo (kFSInvalidVolumeRefNum,
+ volumeIndex,
+ &actualVolume,
+ kFSVolInfoFSInfo,
+ &volumeInfo,
+ NULL,
+ NULL);
+
+ if (result == noErr)
+ {
+ if (volumeInfo.filesystemID == kAudioCDFilesystemID) /* It's an audio CD */
+ {
+ if (volumes != NULL && cdVolumeCount < numVolumes)
+ volumes[cdVolumeCount] = actualVolume;
+
+ cdVolumeCount++;
+ }
+ }
+ else
+ {
+ /* I'm commenting this out because it seems to be harmless */
+ /*SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);*/
+ }
+ }
+
+ return cdVolumeCount;
+}
+
+int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD)
+{
+ HFSUniStr255 dataForkName;
+ OSStatus theErr;
+ FSIORefNum forkRefNum;
+ SInt64 forkSize;
+ Ptr forkData = 0;
+ ByteCount actualRead;
+ CFDataRef dataRef = 0;
+ CFPropertyListRef propertyListRef = 0;
+ FSRefParam fsRefPB;
+ FSRef tocPlistFSRef;
+ FSRef rootRef;
+ const char* error = "Unspecified Error";
+ const UniChar uniName[] = { '.','T','O','C','.','p','l','i','s','t' };
+
+ theErr = FSGetVolumeInfo(theVolume, 0, 0, kFSVolInfoNone, 0, 0, &rootRef);
+ if(theErr != noErr) {
+ error = "FSGetVolumeInfo";
+ goto bail;
+ }
+
+ SDL_memset(&fsRefPB, '\0', sizeof (fsRefPB));
+
+ /* get stuff from .TOC.plist */
+ fsRefPB.ref = &rootRef;
+ fsRefPB.newRef = &tocPlistFSRef;
+ fsRefPB.nameLength = sizeof (uniName) / sizeof (uniName[0]);
+ fsRefPB.name = uniName;
+ fsRefPB.textEncodingHint = kTextEncodingUnknown;
+
+ theErr = PBMakeFSRefUnicodeSync (&fsRefPB);
+ if(theErr != noErr) {
+ error = "PBMakeFSRefUnicodeSync";
+ goto bail;
+ }
+
+ /* Load and parse the TOC XML data */
+
+ theErr = FSGetDataForkName (&dataForkName);
+ if (theErr != noErr) {
+ error = "FSGetDataForkName";
+ goto bail;
+ }
+
+ theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum);
+ if (theErr != noErr) {
+ error = "FSOpenFork";
+ goto bail;
+ }
+
+ theErr = FSGetForkSize (forkRefNum, &forkSize);
+ if (theErr != noErr) {
+ error = "FSGetForkSize";
+ goto bail;
+ }
+
+ /* Allocate some memory for the XML data */
+ forkData = NewPtr (forkSize);
+ if(forkData == NULL) {
+ error = "NewPtr";
+ goto bail;
+ }
+
+ theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead);
+ if(theErr != noErr) {
+ error = "FSReadFork";
+ goto bail;
+ }
+
+ dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize);
+ if(dataRef == 0) {
+ error = "CFDataCreate";
+ goto bail;
+ }
+
+ propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault,
+ dataRef,
+ kCFPropertyListImmutable,
+ NULL);
+ if (propertyListRef == NULL) {
+ error = "CFPropertyListCreateFromXMLData";
+ goto bail;
+ }
+
+ /* Now we got the Property List in memory. Parse it. */
+
+ /* First, make sure the root item is a CFDictionary. If not, release and bail. */
+ if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID())
+ {
+ CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef;
+
+ CFDataRef theRawTOCDataRef;
+ CFArrayRef theSessionArrayRef;
+ CFIndex numSessions;
+ CFIndex index;
+
+ /* This is how we get the Raw TOC Data */
+ theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString));
+
+ /* Get the session array info. */
+ theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString));
+
+ /* Find out how many sessions there are. */
+ numSessions = CFArrayGetCount (theSessionArrayRef);
+
+ /* Initialize the total number of tracks to 0 */
+ theCD->numtracks = 0;
+
+ /* Iterate over all sessions, collecting the track data */
+ for(index = 0; index < numSessions; index++)
+ {
+ CFDictionaryRef theSessionDict;
+ CFNumberRef leadoutBlock;
+ CFArrayRef trackArray;
+ CFIndex numTracks;
+ CFIndex trackIndex;
+ UInt32 value = 0;
+
+ theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index);
+ leadoutBlock = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString));
+
+ trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString));
+
+ numTracks = CFArrayGetCount (trackArray);
+
+ for(trackIndex = 0; trackIndex < numTracks; trackIndex++) {
+
+ CFDictionaryRef theTrackDict;
+ CFNumberRef trackNumber;
+ CFNumberRef sessionNumber;
+ CFNumberRef startBlock;
+ CFBooleanRef isDataTrack;
+ UInt32 value;
+
+ theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex);
+
+ trackNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString));
+ sessionNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString));
+ startBlock = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString));
+ isDataTrack = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString));
+
+ /* Fill in the SDL_CD struct */
+ int idx = theCD->numtracks++;
+
+ CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value);
+ theCD->track[idx].id = value;
+
+ CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value);
+ theCD->track[idx].offset = value;
+
+ theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK;
+
+ /* Since the track lengths are not stored in .TOC.plist we compute them. */
+ if (trackIndex > 0) {
+ theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset;
+ }
+ }
+
+ /* Compute the length of the last track */
+ CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value);
+
+ theCD->track[theCD->numtracks-1].length =
+ value - theCD->track[theCD->numtracks-1].offset;
+
+ /* Set offset to leadout track */
+ theCD->track[theCD->numtracks].offset = value;
+ }
+
+ }
+
+ theErr = 0;
+ goto cleanup;
+bail:
+ SDL_SetError ("ReadTOCData: %s returned %d", error, theErr);
+ theErr = -1;
+cleanup:
+
+ if (propertyListRef != NULL)
+ CFRelease(propertyListRef);
+ if (dataRef != NULL)
+ CFRelease(dataRef);
+ if (forkData != NULL)
+ DisposePtr(forkData);
+
+ FSCloseFork (forkRefNum);
+
+ return theErr;
+}
+
+int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks)
+{
+ OSStatus result = -1;
+ FSIterator iterator;
+ ItemCount actualObjects;
+ FSRef rootDirectory;
+ FSRef ref;
+ HFSUniStr255 nameStr;
+
+ result = FSGetVolumeInfo (theVolume,
+ 0,
+ NULL,
+ kFSVolInfoFSInfo,
+ NULL,
+ NULL,
+ &rootDirectory);
+
+ if (result != noErr) {
+ SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result);
+ return result;
+ }
+
+ result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator);
+ if (result == noErr) {
+ do
+ {
+ result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects,
+ NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr);
+ if (result == noErr) {
+
+ CFStringRef name;
+ name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length);
+
+ /* Look for .aiff extension */
+ if (CFStringHasSuffix (name, CFSTR(".aiff")) ||
+ CFStringHasSuffix (name, CFSTR(".cdda"))) {
+
+ /* Extract the track id from the filename */
+ int trackID = 0, i = 0;
+ while (i < nameStr.length && !isdigit(nameStr.unicode[i])) {
+ ++i;
+ }
+ while (i < nameStr.length && isdigit(nameStr.unicode[i])) {
+ trackID = 10 * trackID +(nameStr.unicode[i] - '0');
+ ++i;
+ }
+
+ #if DEBUG_CDROM
+ printf("Found AIFF for track %d: '%s'\n", trackID,
+ CFStringGetCStringPtr (name, CFStringGetSystemEncoding()));
+ #endif
+
+ /* Track ID's start at 1, but we want to start at 0 */
+ trackID--;
+
+ assert(0 <= trackID && trackID <= SDL_MAX_TRACKS);
+
+ if (trackID < numTracks)
+ memcpy (&trackFiles[trackID], &ref, sizeof(FSRef));
+ }
+ CFRelease (name);
+ }
+ } while(noErr == result);
+ FSCloseIterator (iterator);
+ }
+
+ return 0;
+}
+
+int LoadFile (const FSRef *ref, int startFrame, int stopFrame)
+{
+ int error = -1;
+
+ if (CheckInit () < 0)
+ goto bail;
+
+ /* release any currently playing file */
+ if (ReleaseFile () < 0)
+ goto bail;
+
+ #if DEBUG_CDROM
+ printf ("LoadFile: %d %d\n", startFrame, stopFrame);
+ #endif
+
+ /*try {*/
+
+ /* create a new player, and attach to the audio unit */
+
+ thePlayer = new_AudioFilePlayer(ref);
+ if (thePlayer == NULL) {
+ SDL_SetError ("LoadFile: Could not create player");
+ return -3; /*throw (-3);*/
+ }
+
+ if (!thePlayer->SetDestination(thePlayer, &theUnit))
+ goto bail;
+
+ if (startFrame >= 0)
+ thePlayer->SetStartFrame (thePlayer, startFrame);
+
+ if (stopFrame >= 0 && stopFrame > startFrame)
+ thePlayer->SetStopFrame (thePlayer, stopFrame);
+
+ /* we set the notifier later */
+ /*thePlayer->SetNotifier(thePlayer, FilePlayNotificationHandler, NULL);*/
+
+ if (!thePlayer->Connect(thePlayer))
+ goto bail;
+
+ #if DEBUG_CDROM
+ thePlayer->Print(thePlayer);
+ fflush (stdout);
+ #endif
+ /*}
+ catch (...)
+ {
+ goto bail;
+ }*/
+
+ error = 0;
+
+ bail:
+ return error;
+}
+
+int ReleaseFile ()
+{
+ int error = -1;
+
+ /* (Don't see any way that the original C++ code could throw here.) --ryan. */
+ /*try {*/
+ if (thePlayer != NULL) {
+
+ thePlayer->Disconnect(thePlayer);
+
+ delete_AudioFilePlayer(thePlayer);
+
+ thePlayer = NULL;
+ }
+ /*}
+ catch (...)
+ {
+ goto bail;
+ }*/
+
+ error = 0;
+
+/* bail: */
+ return error;
+}
+
+int PlayFile ()
+{
+ OSStatus result = -1;
+
+ if (CheckInit () < 0)
+ goto bail;
+
+ /*try {*/
+
+ // start processing of the audio unit
+ result = AudioOutputUnitStart (theUnit);
+ if (result) goto bail; //THROW_RESULT("PlayFile: AudioOutputUnitStart")
+
+ /*}
+ catch (...)
+ {
+ goto bail;
+ }*/
+
+ result = 0;
+
+bail:
+ return result;
+}
+
+int PauseFile ()
+{
+ OSStatus result = -1;
+
+ if (CheckInit () < 0)
+ goto bail;
+
+ /*try {*/
+
+ /* stop processing the audio unit */
+ result = AudioOutputUnitStop (theUnit);
+ if (result) goto bail; /*THROW_RESULT("PauseFile: AudioOutputUnitStop")*/
+ /*}
+ catch (...)
+ {
+ goto bail;
+ }*/
+
+ result = 0;
+bail:
+ return result;
+}
+
+void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
+{
+ assert(thePlayer != NULL);
+
+ theCDROM = cdrom;
+ completionProc = proc;
+ thePlayer->SetNotifier (thePlayer, FilePlayNotificationHandler, cdrom);
+}
+
+int GetCurrentFrame ()
+{
+ int frame;
+
+ if (thePlayer == NULL)
+ frame = 0;
+ else
+ frame = thePlayer->GetCurrentFrame (thePlayer);
+
+ return frame;
+}
+
+
+#pragma mark -- Private Functions --
+
+static OSStatus CheckInit ()
+{
+ if (playBackWasInit)
+ return 0;
+
+ OSStatus result = noErr;
+
+ /* Create the callback semaphore */
+ callbackSem = SDL_CreateSemaphore(0);
+
+ /* Start callback thread */
+ SDL_CreateThread(RunCallBackThread, NULL);
+
+ { /*try {*/
+ ComponentDescription desc;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ Component comp = FindNextComponent (NULL, &desc);
+ if (comp == NULL) {
+ SDL_SetError ("CheckInit: FindNextComponent returned NULL");
+ if (result) return -1; //throw(internalComponentErr);
+ }
+
+ result = OpenAComponent (comp, &theUnit);
+ if (result) return -1; //THROW_RESULT("CheckInit: OpenAComponent")
+
+ // you need to initialize the output unit before you set it as a destination
+ result = AudioUnitInitialize (theUnit);
+ if (result) return -1; //THROW_RESULT("CheckInit: AudioUnitInitialize")
+
+
+ playBackWasInit = true;
+ }
+ /*catch (...)
+ {
+ return -1;
+ }*/
+
+ return 0;
+}
+
+static void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus)
+{
+ if (inStatus == kAudioFilePlay_FileIsFinished) {
+
+ /* notify non-CA thread to perform the callback */
+ SDL_SemPost(callbackSem);
+
+ } else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) {
+
+ SDL_SetError ("CDPlayer Notification: buffer underrun");
+ } else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) {
+
+ SDL_SetError ("CDPlayer Notification: player is uninitialized");
+ } else {
+
+ SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus);
+ }
+}
+
+static int RunCallBackThread (void *param)
+{
+ for (;;) {
+
+ SDL_SemWait(callbackSem);
+
+ if (completionProc && theCDROM) {
+ #if DEBUG_CDROM
+ printf ("callback!\n");
+ #endif
+ (*completionProc)(theCDROM);
+ } else {
+ #if DEBUG_CDROM
+ printf ("callback?\n");
+ #endif
+ }
+ }
+
+ #if DEBUG_CDROM
+ printf ("thread dying now...\n");
+ #endif
+
+ return 0;
+}
+
+#endif /* SDL_CDROM_MACOSX */
+
+/*}; // extern "C" */
diff --git a/src/sdl_cdrom/macosx/CDPlayer.h b/src/sdl_cdrom/macosx/CDPlayer.h
new file mode 100644
index 000000000..be1ac1826
--- /dev/null
+++ b/src/sdl_cdrom/macosx/CDPlayer.h
@@ -0,0 +1,69 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+#ifndef __CDPlayer__H__
+#define __CDPlayer__H__ 1
+
+#include <string.h>
+
+#include <Carbon/Carbon.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <AudioUnit/AudioUnit.h>
+
+#include "SDL.h"
+#include "SDL_thread.h"
+#include "SDL_mutex.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*CDPlayerCompletionProc)(SDL_CD *cdrom) ;
+
+void Lock ();
+
+void Unlock();
+
+int LoadFile (const FSRef *ref, int startFrame, int endFrame); /* pass -1 to do nothing */
+
+int ReleaseFile ();
+
+int PlayFile ();
+
+int PauseFile ();
+
+void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom);
+
+int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD);
+
+int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks);
+
+int DetectAudioCDVolumes (FSVolumeRefNum *volumes, int numVolumes);
+
+int GetCurrentFrame ();
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* __CD_Player__H__ */
diff --git a/src/sdl_cdrom/macosx/Makefile.am b/src/sdl_cdrom/macosx/Makefile.am
new file mode 100644
index 000000000..f92f83c52
--- /dev/null
+++ b/src/sdl_cdrom/macosx/Makefile.am
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LIBRARIES = libcsdlcdrommacosx.a
+EXTRA_DIST = AudioFilePlayer.h CDPlayer.h SDLOSXCAGuard.h SDL_syscdrom_c.h
+libcsdlcdrommacosx_a_SOURCES = AudioFilePlayer.c AudioFileReaderThread.c CDPlayer.c SDLOSXCAGuard.c SDL_syscdrom.c
diff --git a/src/sdl_cdrom/macosx/SDLOSXCAGuard.c b/src/sdl_cdrom/macosx/SDLOSXCAGuard.c
new file mode 100644
index 000000000..df120b7a6
--- /dev/null
+++ b/src/sdl_cdrom/macosx/SDLOSXCAGuard.c
@@ -0,0 +1,205 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX)
+
+#include "SDL_config.h"
+
+/*
+ Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
+
+ Copyright: \A9 Copyright 2002 Apple Computer, Inc. All rights reserved.
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under Apple\D5s
+ copyrights in this original Apple software (the "Apple Software"), to use,
+ reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions of
+ the Apple Software. Neither the name, trademarks, service marks or logos of
+ Apple Computer, Inc. may be used to endorse or promote products derived from the
+ Apple Software without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or implied,
+ are granted by Apple herein, including but not limited to any patent rights that
+ may be infringed by your derivative works or by other works in which the Apple
+ Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*=============================================================================
+ CAGuard.cp
+
+=============================================================================*/
+
+/*=============================================================================
+ Includes
+ =============================================================================*/
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+*/
+#include "SDL_stdinc.h"
+
+/*#define NDEBUG 1*/
+/*
+#include <assert.h>
+*/
+#define assert(X)
+
+
+#include "SDLOSXCAGuard.h"
+
+/*#warning Need a try-based Locker too*/
+/*=============================================================================
+ SDLOSXCAGuard
+ =============================================================================*/
+
+static int SDLOSXCAGuard_Lock(SDLOSXCAGuard *cag)
+{
+ int theAnswer = 0;
+
+ if(pthread_self() != cag->mOwner)
+ {
+ OSStatus theError = pthread_mutex_lock(&cag->mMutex);
+ (void)theError;
+ assert(theError == 0);
+ cag->mOwner = pthread_self();
+ theAnswer = 1;
+ }
+
+ return theAnswer;
+}
+
+static void SDLOSXCAGuard_Unlock(SDLOSXCAGuard *cag)
+{
+ OSStatus theError;
+ assert(pthread_self() == cag->mOwner);
+
+ cag->mOwner = 0;
+ theError = pthread_mutex_unlock(&cag->mMutex);
+ (void)theError;
+ assert(theError == 0);
+}
+
+static int SDLOSXCAGuard_Try (SDLOSXCAGuard *cag, int *outWasLocked)
+{
+ int theAnswer = 0;
+ *outWasLocked = 0;
+
+ if (pthread_self() == cag->mOwner) {
+ theAnswer = 1;
+ *outWasLocked = 0;
+ } else {
+ OSStatus theError = pthread_mutex_trylock(&cag->mMutex);
+ if (theError == 0) {
+ cag->mOwner = pthread_self();
+ theAnswer = 1;
+ *outWasLocked = 1;
+ }
+ }
+
+ return theAnswer;
+}
+
+static void SDLOSXCAGuard_Wait(SDLOSXCAGuard *cag)
+{
+ OSStatus theError;
+ assert(pthread_self() == cag->mOwner);
+
+ cag->mOwner = 0;
+
+ theError = pthread_cond_wait(&cag->mCondVar, &cag->mMutex);
+ (void)theError;
+ assert(theError == 0);
+ cag->mOwner = pthread_self();
+}
+
+static void SDLOSXCAGuard_Notify(SDLOSXCAGuard *cag)
+{
+ OSStatus theError = pthread_cond_signal(&cag->mCondVar);
+ (void)theError;
+ assert(theError == 0);
+}
+
+
+SDLOSXCAGuard *new_SDLOSXCAGuard(void)
+{
+ OSStatus theError;
+ SDLOSXCAGuard *cag = (SDLOSXCAGuard *) SDL_malloc(sizeof (SDLOSXCAGuard));
+ if (cag == NULL)
+ return NULL;
+ SDL_memset(cag, '\0', sizeof (*cag));
+
+ #define SET_SDLOSXCAGUARD_METHOD(m) cag->m = SDLOSXCAGuard_##m
+ SET_SDLOSXCAGUARD_METHOD(Lock);
+ SET_SDLOSXCAGUARD_METHOD(Unlock);
+ SET_SDLOSXCAGUARD_METHOD(Try);
+ SET_SDLOSXCAGUARD_METHOD(Wait);
+ SET_SDLOSXCAGUARD_METHOD(Notify);
+ #undef SET_SDLOSXCAGUARD_METHOD
+
+ theError = pthread_mutex_init(&cag->mMutex, NULL);
+ (void)theError;
+ assert(theError == 0);
+
+ theError = pthread_cond_init(&cag->mCondVar, NULL);
+ (void)theError;
+ assert(theError == 0);
+
+ cag->mOwner = 0;
+ return cag;
+}
+
+void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag)
+{
+ if (cag != NULL)
+ {
+ pthread_mutex_destroy(&cag->mMutex);
+ pthread_cond_destroy(&cag->mCondVar);
+ SDL_free(cag);
+ }
+}
+
+#endif /* SDL_CDROM_MACOSX */
diff --git a/src/sdl_cdrom/macosx/SDLOSXCAGuard.h b/src/sdl_cdrom/macosx/SDLOSXCAGuard.h
new file mode 100644
index 000000000..08604ed09
--- /dev/null
+++ b/src/sdl_cdrom/macosx/SDLOSXCAGuard.h
@@ -0,0 +1,116 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/*
+ Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
+
+
+ Copyright: \A9 Copyright 2002 Apple Computer, Inc. All rights reserved.
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under Apple\D5s
+ copyrights in this original Apple software (the "Apple Software"), to use,
+ reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions of
+ the Apple Software. Neither the name, trademarks, service marks or logos of
+ Apple Computer, Inc. may be used to endorse or promote products derived from the
+ Apple Software without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or implied,
+ are granted by Apple herein, including but not limited to any patent rights that
+ may be infringed by your derivative works or by other works in which the Apple
+ Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*=============================================================================
+ CAGuard.h
+
+=============================================================================*/
+#if !defined(__CAGuard_h__)
+#define __CAGuard_h__
+
+/*=============================================================================
+ Includes
+ =============================================================================*/
+
+#include <CoreAudio/CoreAudioTypes.h>
+#include <pthread.h>
+
+
+/*=============================================================================
+ CAGuard
+
+ This is your typical mutex with signalling implemented via pthreads.
+ Lock() will return true if and only if the guard is locked on that call.
+ A thread that already has the guard will receive 'false' if it locks it
+ again. Use of the stack-based CAGuard::Locker class is highly recommended
+ to properly manage the recursive nesting. The Wait calls with timeouts
+ will return true if and only if the timeout period expired. They will
+ return false if they receive notification any other way.
+ =============================================================================*/
+
+typedef struct S_SDLOSXCAGuard
+{
+
+/* Construction/Destruction */
+/*public:*/
+/* Actions */
+/*public:*/
+ int (*Lock)(struct S_SDLOSXCAGuard *cag);
+ void (*Unlock)(struct S_SDLOSXCAGuard *cag);
+ int (*Try)(struct S_SDLOSXCAGuard *cag, int *outWasLocked); /* returns true if lock is free, false if not */
+ void (*Wait)(struct S_SDLOSXCAGuard *cag);
+ void (*Notify)(struct S_SDLOSXCAGuard *cag);
+
+/* Implementation */
+/*protected:*/
+ pthread_mutex_t mMutex;
+ pthread_cond_t mCondVar;
+ pthread_t mOwner;
+} SDLOSXCAGuard;
+
+SDLOSXCAGuard *new_SDLOSXCAGuard(void);
+void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag);
+
+#endif
+
diff --git a/src/sdl_cdrom/macosx/SDL_syscdrom.c b/src/sdl_cdrom/macosx/SDL_syscdrom.c
new file mode 100644
index 000000000..43023304f
--- /dev/null
+++ b/src/sdl_cdrom/macosx/SDL_syscdrom.c
@@ -0,0 +1,519 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "config.h"
+#define COMPAT_SDL_CDROM_PLATFORM_MACOSX 1
+
+#if (C_COMPAT_SDL_CDROM_PLATFORM == COMPAT_SDL_CDROM_PLATFORM_MACOSX)
+
+#include "SDL_config.h"
+
+/*#ifdef SDL_CDROM_MACOSX*/
+
+#include "SDL_syscdrom_c.h"
+
+#pragma mark -- Globals --
+
+static FSRef** tracks;
+static FSVolumeRefNum* volumes;
+static CDstatus status;
+static int nextTrackFrame;
+static int nextTrackFramesRemaining;
+static int fakeCD;
+static int currentTrack;
+static int didReadTOC;
+static int cacheTOCNumTracks;
+static int currentDrive; /* Only allow 1 drive in use at a time */
+
+#pragma mark -- Prototypes --
+
+static const char *SDL_SYS_CDName (int drive);
+static int SDL_SYS_CDOpen (int drive);
+static int SDL_SYS_CDGetTOC (SDL_CD *cdrom);
+static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position);
+static int SDL_SYS_CDPlay (SDL_CD *cdrom, int start, int length);
+static int SDL_SYS_CDPause (SDL_CD *cdrom);
+static int SDL_SYS_CDResume (SDL_CD *cdrom);
+static int SDL_SYS_CDStop (SDL_CD *cdrom);
+static int SDL_SYS_CDEject (SDL_CD *cdrom);
+static void SDL_SYS_CDClose (SDL_CD *cdrom);
+
+#pragma mark -- Helper Functions --
+
+/* Read a list of tracks from the volume */
+static int LoadTracks (SDL_CD *cdrom)
+{
+ /* Check if tracks are already loaded */
+ if ( tracks[cdrom->id] != NULL )
+ return 0;
+
+ /* Allocate memory for tracks */
+ tracks[cdrom->id] = (FSRef*) SDL_calloc (1, sizeof(**tracks) * cdrom->numtracks);
+ if (tracks[cdrom->id] == NULL) {
+ SDL_OutOfMemory ();
+ return -1;
+ }
+
+ /* Load tracks */
+ if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Find a file for a given start frame and length */
+static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length, int *outStartFrame, int *outStopFrame)
+{
+ int i;
+
+ for (i = 0; i < cdrom->numtracks; i++) {
+
+ if (cdrom->track[i].offset <= start &&
+ start < (cdrom->track[i].offset + cdrom->track[i].length))
+ break;
+ }
+
+ if (i == cdrom->numtracks)
+ return NULL;
+
+ currentTrack = i;
+
+ *outStartFrame = start - cdrom->track[i].offset;
+
+ if ((*outStartFrame + length) < cdrom->track[i].length) {
+ *outStopFrame = *outStartFrame + length;
+ length = 0;
+ nextTrackFrame = -1;
+ nextTrackFramesRemaining = -1;
+ }
+ else {
+ *outStopFrame = -1;
+ length -= cdrom->track[i].length - *outStartFrame;
+ nextTrackFrame = cdrom->track[i+1].offset;
+ nextTrackFramesRemaining = length;
+ }
+
+ return &tracks[cdrom->id][i];
+}
+
+/* Setup another file for playback, or stop playback (called from another thread) */
+static void CompletionProc (SDL_CD *cdrom)
+{
+
+ Lock ();
+
+ if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) {
+
+ /* Load the next file to play */
+ int startFrame, stopFrame;
+ FSRef *file;
+
+ PauseFile ();
+ ReleaseFile ();
+
+ file = GetFileForOffset (cdrom, nextTrackFrame,
+ nextTrackFramesRemaining, &startFrame, &stopFrame);
+
+ if (file == NULL) {
+ status = CD_STOPPED;
+ Unlock ();
+ return;
+ }
+
+ LoadFile (file, startFrame, stopFrame);
+
+ SetCompletionProc (CompletionProc, cdrom);
+
+ PlayFile ();
+ }
+ else {
+
+ /* Release the current file */
+ PauseFile ();
+ ReleaseFile ();
+ status = CD_STOPPED;
+ }
+
+ Unlock ();
+}
+
+
+#pragma mark -- Driver Functions --
+
+/* Initialize */
+int SDL_SYS_CDInit (void)
+{
+ /* Initialize globals */
+ volumes = NULL;
+ tracks = NULL;
+ status = CD_STOPPED;
+ nextTrackFrame = -1;
+ nextTrackFramesRemaining = -1;
+ fakeCD = SDL_FALSE;
+ currentTrack = -1;
+ didReadTOC = SDL_FALSE;
+ cacheTOCNumTracks = -1;
+ currentDrive = -1;
+
+ /* Fill in function pointers */
+ SDL_CDcaps.Name = SDL_SYS_CDName;
+ SDL_CDcaps.Open = SDL_SYS_CDOpen;
+ SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
+ SDL_CDcaps.Status = SDL_SYS_CDStatus;
+ SDL_CDcaps.Play = SDL_SYS_CDPlay;
+ SDL_CDcaps.Pause = SDL_SYS_CDPause;
+ SDL_CDcaps.Resume = SDL_SYS_CDResume;
+ SDL_CDcaps.Stop = SDL_SYS_CDStop;
+ SDL_CDcaps.Eject = SDL_SYS_CDEject;
+ SDL_CDcaps.Close = SDL_SYS_CDClose;
+
+ /*
+ Read the list of "drives"
+
+ This is currently a hack that infers drives from
+ mounted audio CD volumes, rather than
+ actual CD-ROM devices - which means it may not
+ act as expected sometimes.
+ */
+
+ /* Find out how many cd volumes are mounted */
+ SDL_numcds = DetectAudioCDVolumes (NULL, 0);
+
+ /*
+ If there are no volumes, fake a cd device
+ so tray empty can be reported.
+ */
+ if (SDL_numcds == 0) {
+
+ fakeCD = SDL_TRUE;
+ SDL_numcds = 1;
+ status = CD_TRAYEMPTY;
+
+ return 0;
+ }
+
+ /* Allocate space for volumes */
+ volumes = (FSVolumeRefNum*) SDL_calloc (1, sizeof(*volumes) * SDL_numcds);
+ if (volumes == NULL) {
+ SDL_OutOfMemory ();
+ return -1;
+ }
+
+ /* Allocate space for tracks */
+ tracks = (FSRef**) SDL_calloc (1, sizeof(*tracks) * (SDL_numcds + 1));
+ if (tracks == NULL) {
+ SDL_OutOfMemory ();
+ return -1;
+ }
+
+ /* Mark the end of the tracks array */
+ tracks[ SDL_numcds ] = (FSRef*)-1;
+
+ /*
+ Redetect, now save all volumes for later
+ Update SDL_numcds just in case it changed
+ */
+ {
+ int numVolumes = SDL_numcds;
+
+ SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes);
+
+ /* If more cds suddenly show up, ignore them */
+ if (SDL_numcds > numVolumes) {
+ SDL_SetError ("Some CD's were added but they will be ignored");
+ SDL_numcds = numVolumes;
+ }
+ }
+
+ return 0;
+}
+
+/* Shutdown and cleanup */
+void SDL_SYS_CDQuit(void)
+{
+ ReleaseFile();
+
+ if (volumes != NULL)
+ free (volumes);
+
+ if (tracks != NULL) {
+
+ FSRef **ptr;
+ for (ptr = tracks; *ptr != (FSRef*)-1; ptr++)
+ if (*ptr != NULL)
+ free (*ptr);
+
+ free (tracks);
+ }
+}
+
+/* Get the Unix disk name of the volume */
+static const char *SDL_SYS_CDName (int drive)
+{
+ /*
+ * !!! FIXME: PBHGetVolParmsSync() is gone in 10.6,
+ * !!! FIXME: replaced with FSGetVolumeParms(), which
+ * !!! FIXME: isn't available before 10.5. :/
+ */
+ return "Mac OS X CD-ROM Device";
+
+#if 0
+ OSStatus err = noErr;
+ HParamBlockRec pb;
+ GetVolParmsInfoBuffer volParmsInfo;
+
+ if (fakeCD)
+ return "Fake CD-ROM Device";
+
+ pb.ioParam.ioNamePtr = NULL;
+ pb.ioParam.ioVRefNum = volumes[drive];
+ pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
+ pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
+ err = PBHGetVolParmsSync(&pb);
+
+ if (err != noErr) {
+ SDL_SetError ("PBHGetVolParmsSync returned %d", err);
+ return NULL;
+ }
+
+ return volParmsInfo.vMDeviceID;
+#endif
+}
+
+/* Open the "device" */
+static int SDL_SYS_CDOpen (int drive)
+{
+ /* Only allow 1 device to be open */
+ if (currentDrive >= 0) {
+ SDL_SetError ("Only one cdrom is supported");
+ return -1;
+ }
+ else
+ currentDrive = drive;
+
+ return drive;
+}
+
+/* Get the table of contents */
+static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
+{
+ if (fakeCD) {
+ SDL_SetError (kErrorFakeDevice);
+ return -1;
+ }
+
+ if (didReadTOC) {
+ cdrom->numtracks = cacheTOCNumTracks;
+ return 0;
+ }
+
+
+ ReadTOCData (volumes[cdrom->id], cdrom);
+ didReadTOC = SDL_TRUE;
+ cacheTOCNumTracks = cdrom->numtracks;
+
+ return 0;
+}
+
+/* Get CD-ROM status */
+static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
+{
+ if (position) {
+ int trackFrame;
+
+ Lock ();
+ trackFrame = GetCurrentFrame ();
+ Unlock ();
+
+ *position = cdrom->track[currentTrack].offset + trackFrame;
+ }
+
+ return status;
+}
+
+/* Start playback */
+static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
+{
+ int startFrame, stopFrame;
+ FSRef *ref;
+
+ if (fakeCD) {
+ SDL_SetError (kErrorFakeDevice);
+ return -1;
+ }
+
+ Lock();
+
+ if (LoadTracks (cdrom) < 0)
+ return -2;
+
+ if (PauseFile () < 0)
+ return -3;
+
+ if (ReleaseFile () < 0)
+ return -4;
+
+ ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
+ if (ref == NULL) {
+ SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
+ return -5;
+ }
+
+ if (LoadFile (ref, startFrame, stopFrame) < 0)
+ return -6;
+
+ SetCompletionProc (CompletionProc, cdrom);
+
+ if (PlayFile () < 0)
+ return -7;
+
+ status = CD_PLAYING;
+
+ Unlock();
+
+ return 0;
+}
+
+/* Pause playback */
+static int SDL_SYS_CDPause(SDL_CD *cdrom)
+{
+ if (fakeCD) {
+ SDL_SetError (kErrorFakeDevice);
+ return -1;
+ }
+
+ Lock ();
+
+ if (PauseFile () < 0) {
+ Unlock ();
+ return -2;
+ }
+
+ status = CD_PAUSED;
+
+ Unlock ();
+
+ return 0;
+}
+
+/* Resume playback */
+static int SDL_SYS_CDResume(SDL_CD *cdrom)
+{
+ if (fakeCD) {
+ SDL_SetError (kErrorFakeDevice);
+ return -1;
+ }
+
+ Lock ();
+
+ if (PlayFile () < 0) {
+ Unlock ();
+ return -2;
+ }
+
+ status = CD_PLAYING;
+
+ Unlock ();
+
+ return 0;
+}
+
+/* Stop playback */
+static int SDL_SYS_CDStop(SDL_CD *cdrom)
+{
+ if (fakeCD) {
+ SDL_SetError (kErrorFakeDevice);
+ return -1;
+ }
+
+ Lock ();
+
+ if (PauseFile () < 0) {
+ Unlock ();
+ return -2;
+ }
+
+ if (ReleaseFile () < 0) {
+ Unlock ();
+ return -3;
+ }
+
+ status = CD_STOPPED;
+
+ Unlock ();
+
+ return 0;
+}
+
+/* Eject the CD-ROM (Unmount the volume) */
+static int SDL_SYS_CDEject(SDL_CD *cdrom)
+{
+ OSStatus err;
+ pid_t dissenter;
+
+ if (fakeCD) {
+ SDL_SetError (kErrorFakeDevice);
+ return -1;
+ }
+
+ Lock ();
+
+ if (PauseFile () < 0) {
+ Unlock ();
+ return -2;
+ }
+
+ if (ReleaseFile () < 0) {
+ Unlock ();
+ return -3;
+ }
+
+ status = CD_STOPPED;
+
+ /* Eject the volume */
+ err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter);
+
+ if (err != noErr) {
+ Unlock ();
+ SDL_SetError ("PBUnmountVol returned %d", err);
+ return -4;
+ }
+
+ status = CD_TRAYEMPTY;
+
+ /* Invalidate volume and track info */
+ volumes[cdrom->id] = 0;
+ free (tracks[cdrom->id]);
+ tracks[cdrom->id] = NULL;
+
+ Unlock ();
+
+ return 0;
+}
+
+/* Close the CD-ROM */
+static void SDL_SYS_CDClose(SDL_CD *cdrom)
+{
+ currentDrive = -1;
+ return;
+}
+
+#endif /* SDL_CDROM_MACOSX */
diff --git a/src/sdl_cdrom/macosx/SDL_syscdrom_c.h b/src/sdl_cdrom/macosx/SDL_syscdrom_c.h
new file mode 100644
index 000000000..ae3d9144f
--- /dev/null
+++ b/src/sdl_cdrom/macosx/SDL_syscdrom_c.h
@@ -0,0 +1,136 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ 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
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* This is the Mac OS X / CoreAudio specific header for the SDL CD-ROM API
+ Contributed by Darrell Walisser and Max Horn
+ */
+
+/***********************************************************************************
+ Implementation Notes
+ *********************
+
+ This code has several limitations currently (all of which are proabaly fixable):
+
+ 1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is
+ not necessarily the first CD-ROM device on the system. (Somewhat easy to fix
+ by useing the device name from the volume id's to reorder the volumes)
+
+ 2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix,
+ due to extensive code restructuring)
+
+ 3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in
+ 1-second intervals (because the audio is buffered in 1-second chunks) If
+ the audio data is less than 1 second, the remainder is filled with silence.
+
+ If you need to play sequences back-to-back that are less that 1 second long,
+ use the frame position to determine when to play the next sequence, instead
+ of SDL_CDStatus.
+
+ This may be possible to fix with a clever usage of the AudioUnit API.
+
+ 4. When new volumes are inserted, our volume information is not updated. The only way
+ to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this,
+ one would probably have to fix point 1 above first, then figure out how to register
+ for a notification when new media is mounted in order to perform an automatic
+ rescan for cdfs volumes.
+
+
+
+ So, here comes a description of how this all works.
+
+ < Initializing >
+
+ To get things rolling, we have to locate mounted volumes that contain
+ audio (since nearly all Macs don't have analog audio-in on the sound card).
+ That's easy, since these volumes have a flag that indicates this special
+ filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code.
+
+ Next, we parse the invisible .TOC.plist in the root of the volume, which gets us
+ the track information (number, offset, length, leadout, etc). See ReadTOCData() in
+ CDPlayer.cpp for the skinny on this.
+
+
+ < The Playback Loop >
+
+ Now come the tricky parts. Let's start with basic audio playback. When a frame
+ range to play is requested, we must first find the .aiff files on the volume,
+ hopefully in the right order. Since these files all begin with a number "1 Audio Track",
+ etc, this is used to determine the correct track order.
+
+ Once all files are determined, we have to find what file corresponds to the start
+ and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the
+ cdrom's track list. At this point, we also save the offset to the next track and frames
+ remaining, if we're going to have to play another file after the first one. See
+ GetFileForOffset() for this code.
+
+ At this point we have all info needed to start playback, so we hand off to the LoadFile()
+ function, which proceeds to do its magic and plays back the file.
+
+ When the file is finished playing, CompletionProc() is invoked, at which time we can
+ play the next file if the previously saved next track and frames remaining
+ indicates that we should.
+
+
+ < Magic >
+
+ OK, so it's not really magic, but since I don't fully understand all the hidden details it
+ seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These
+ appear to be an extension of CoreAudio for creating modular playback and f/x entities.
+ The important thing is that CPU usage is very low and reliability is very high. You'd
+ be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks.
+
+ One part of this magic is that it uses multiple threads, which carries the usual potential
+ for disaster if not handled carefully. Playback currently requires 4 additional threads:
+ 1. The coreaudio runloop thread
+ 2. The coreaudio device i/o thread
+ 3. The file streaming thread
+ 4. The notification/callback thread
+
+ The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation
+ is (even the SDL sound implementation creates theses suckers). The last two are are created
+ by us.
+
+ The file is streamed from disk using a threaded double-buffer approach.
+ This way, the high latency operation of reading from disk can be performed without interrupting
+ the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the
+ buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets
+ to the sound card.
+
+ The device thread posts a notification when the file streaming thread is out of data. This
+ notification must be handled in a separate thread to avoid potential deadlock in the
+ device thread. That's where the notification thread comes in. This thread is signaled
+ whenever a notification needs to be processed, so another file can be played back if need be.
+
+ The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread
+ and main thread (or another other thread using the SDL CD api) can potentially call it at the same time.
+
+************************************************************************************/
+
+
+#include "../compat_SDL_cdrom.h"
+#include "../SDL_syscdrom.h"
+
+#include "CDPlayer.h"
+
+#define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes."
+