diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml
index 580bbe90e..7beecc583 100644
--- a/.github/workflows/bsd.yml
+++ b/.github/workflows/bsd.yml
@@ -14,13 +14,13 @@ jobs:
include:
- operating_system: freebsd
version: '14.2'
- pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2
+ pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
- operating_system: netbsd
version: '10.0'
- pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 && export PATH=/usr/pkg/gcc12/bin:$PATH
+ pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 libcdio && export PATH=/usr/pkg/gcc12/bin:$PATH
- operating_system: openbsd
version: '7.6'
- pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2
+ pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
exclude:
- architecture: arm64
@@ -44,5 +44,5 @@ jobs:
environment_variables: CCACHE_DIR
run: |
${{ matrix.pkginstall }}
- cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja
+ cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build build --config Release
diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml
index 3954dee58..79ec624ff 100644
--- a/.github/workflows/c-cpp.yml
+++ b/.github/workflows/c-cpp.yml
@@ -20,11 +20,11 @@ jobs:
- {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release}
- {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo}
- {name: apple-ios, os: macos-latest, shell: sh, cmakeArgs: -DCMAKE_SYSTEM_NAME=iOS -G Xcode, destDir: ios, buildType: Release}
- - {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: linux, buildType: RelWithDebInfo}
+ - {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo}
- {name: x86_64-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release}
- - {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: win, buildType: RelWithDebInfo}
- - {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
- - {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -G Ninja, buildType: Release}
+ - {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo}
+ - {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
+ - {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -G Ninja, buildType: Release}
steps:
- name: Set up build environment (macOS)
@@ -45,7 +45,7 @@ jobs:
run: |
sudo add-apt-repository ppa:christianrauch/libdecoration
sudo apt-get update
- sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev
+ sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev libcdio-dev
sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
if: runner.os == 'Linux'
@@ -53,7 +53,7 @@ jobs:
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
- install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain
+ install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain mingw-w64-x86_64-libcdio
if: matrix.config.shell == 'msys2 {0}'
- name: Set up build environment (Windows, Visual Studio)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d2f50b97e..b3cc580a8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -9,12 +9,12 @@
variables:
GIT_SUBMODULE_STRATEGY: recursive
CORENAME: flycast
- CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
+ CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release
.core-defs-linux:
extends: .core-defs
variables:
- CORE_ARGS: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release
+ CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release
.core-defs-osx-x64:
extends: .core-defs
@@ -36,7 +36,7 @@
.core-defs-android:
extends: .core-defs
script:
- - cmake $CORE_ARGS -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
+ - cmake -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
- cmake --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC
- mv $BUILD_DIR/${CORENAME}_libretro.so $LIBNAME
- if [ $STRIP_CORE_LIB -eq 1 ]; then $NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip $LIBNAME; fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7114589fb..89fc30afb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -76,6 +76,7 @@ option(ENABLE_GDB_SERVER "Build with GDB debugging support" OFF)
option(ENABLE_DC_PROFILER "Build with support for target machine (SH4) profiler" OFF)
option(ENABLE_FC_PROFILER "Build with support for host app (Flycast) profiler" OFF)
option(USE_DISCORD "Use Discord Presence API" OFF)
+option(USE_LIBCDIO "Use libcdio for CDROM access" OFF)
if(IOS AND NOT LIBRETRO)
set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE)
@@ -672,6 +673,23 @@ if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH)
target_link_libraries(${PROJECT_NAME} PRIVATE libzip::zip)
endif()
+if(USE_LIBCDIO)
+ if(PKG_CONFIG_FOUND)
+ pkg_check_modules(CDIO IMPORTED_TARGET libcdio)
+ if(CDIO_FOUND)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LIBCDIO)
+ target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::CDIO)
+ endif()
+ endif()
+ if(NOT CDIO_FOUND)
+ find_package(libcdio)
+ if(TARGET libcdio::libcdio)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LIBCDIO)
+ target_link_libraries(${PROJECT_NAME} PRIVATE libcdio::libcdio)
+ endif()
+ endif()
+endif()
+
if(WIN32)
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent)
endif()
@@ -1062,13 +1080,13 @@ endif()
target_sources(${PROJECT_NAME} PRIVATE
core/imgread/cdi.cpp
+ core/imgread/cdio.cpp
core/imgread/chd.cpp
core/imgread/common.cpp
core/imgread/common.h
core/imgread/cue.cpp
core/imgread/gdi.cpp
core/imgread/ImgReader.cpp
- core/imgread/ioctl.cpp
core/imgread/iso9660.h
core/imgread/isofs.cpp
core/imgread/isofs.h)
diff --git a/core/imgread/cdio.cpp b/core/imgread/cdio.cpp
new file mode 100755
index 000000000..4d4f71b4b
--- /dev/null
+++ b/core/imgread/cdio.cpp
@@ -0,0 +1,234 @@
+/*
+ Copyright 2024 flyinghead
+
+ This file is part of Flycast.
+
+ Flycast is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ Flycast is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Flycast. If not, see .
+ */
+#include "build.h"
+#ifdef USE_LIBCDIO
+#include "types.h"
+#include "imgread/common.h"
+#include
+#include
+#include
+#include
+
+namespace hostfs
+{
+
+const std::vector& getCdromDrives()
+{
+ static std::vector cdromDevices;
+ static bool devicesFetched;
+
+ if (devicesFetched)
+ return cdromDevices;
+ devicesFetched = true;
+ // Set a custom log handler
+ cdio_log_set_handler([](cdio_log_level_t level, const char message[]) {
+ switch (level)
+ {
+ case CDIO_LOG_DEBUG:
+ DEBUG_LOG(GDROM, "%s", message);
+ break;
+ case CDIO_LOG_INFO:
+ INFO_LOG(GDROM, "%s", message);
+ break;
+ case CDIO_LOG_WARN:
+ WARN_LOG(GDROM, "%s", message);
+ break;
+ case CDIO_LOG_ERROR:
+ case CDIO_LOG_ASSERT:
+ ERROR_LOG(GDROM, "%s", message);
+ break;
+ }
+ });
+ // Get the list of all hardware devices
+ char **list = cdio_get_devices(DRIVER_DEVICE);
+ if (list != nullptr)
+ {
+ for (char **dev = &list[0]; *dev != nullptr; dev++)
+ cdromDevices.push_back(*dev);
+ cdio_free_device_list(list);
+ }
+
+ return cdromDevices;
+}
+
+}
+
+struct CdioDrive;
+
+struct CdioTrack : public TrackFile
+{
+ CdioTrack(CdioDrive& disk, bool audio)
+ : disk(disk), audio(audio) {}
+ bool Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type);
+
+ CdioDrive& disk;
+ bool audio;
+};
+
+struct CdioDrive : public Disc
+{
+ bool open(const char *path)
+ {
+ const std::vector& devices = hostfs::getCdromDrives();
+ if (!devices.empty())
+ {
+ // If the list isn't empty, check that an entry exists for the current path
+ bool found = false;
+ for (const std::string& dev : devices)
+ if (dev == path) {
+ found = true;
+ break;
+ }
+ if (!found)
+ return false;
+ }
+ pCdio = cdio_open(path, DRIVER_DEVICE);
+ if (pCdio == nullptr) {
+ WARN_LOG(GDROM, "Can't open CD device %s", path);
+ return false;
+ }
+ track_t firstTrk = cdio_get_first_track_num(pCdio);
+ track_t lastTrk = cdio_get_last_track_num(pCdio);
+ if (firstTrk == CDIO_INVALID_TRACK || lastTrk == CDIO_INVALID_TRACK)
+ {
+ WARN_LOG(GDROM, "Can't find first and/or last track");
+ close();
+ return false;
+ }
+
+ Session session;
+ session.StartFAD = 150;
+ session.FirstTrack = firstTrk;
+ sessions.push_back(session);
+ type = CdDA; // TODO more CD types
+
+ for (int i = firstTrk; i <= lastTrk; i++)
+ {
+ lba_t lba = cdio_get_track_lba(pCdio, i);
+ if (lba == CDIO_INVALID_LBA)
+ {
+ WARN_LOG(GDROM, "Can't find track %d", i);
+ close();
+ return false;
+ }
+ track_format_t format = cdio_get_track_format(pCdio, i);
+ bool copy = true;
+ if (format == TRACK_FORMAT_AUDIO) {
+ track_flag_t copyFlag = cdio_get_track_copy_permit(pCdio, i);
+ copy = copyFlag == CDIO_TRACK_FLAG_TRUE;
+ }
+ else if (!tracks.empty() && !tracks.back().isDataTrack())
+ {
+ // session 1 lead-out & session 2 lead-in and pre-gap
+ tracks.back().EndFAD -= 11400;
+
+ type = CdRom_XA;
+ Session session;
+ session.StartFAD = lba;
+ session.FirstTrack = i;
+ sessions.push_back(session);
+ }
+ Track t;
+ t.ADR = 1; // FIXME correct?
+ t.CTRL = format == TRACK_FORMAT_AUDIO ? 0 : CDIO_CDROM_DATA_TRACK;
+ t.StartFAD = lba;
+ lsn_t last = cdio_get_track_last_lsn(pCdio, i);
+ if (last == CDIO_INVALID_LSN)
+ WARN_LOG(GDROM, "Can't get last lsn of track %d", i);
+ else
+ t.EndFAD = cdio_lsn_to_lba(last);
+ if (i == firstTrk)
+ sessions.front().StartFAD = t.StartFAD;
+ INFO_LOG(GDROM, "Track #%d: start %d end %d format %d copy %d", i, t.StartFAD, t.EndFAD, format, copy);
+ t.file = new CdioTrack(*this, format == TRACK_FORMAT_AUDIO);
+ tracks.push_back(t);
+ }
+ lba_t leadout = cdio_get_track_lba(pCdio, CDIO_CDROM_LEADOUT_TRACK);
+ if (leadout == CDIO_INVALID_LBA)
+ {
+ WARN_LOG(GDROM, "Can't find leadout track");
+ close();
+ return false;
+ }
+ LeadOut.StartFAD = leadout;
+ LeadOut.ADR = 1;
+ LeadOut.CTRL = CDIO_CDROM_DATA_TRACK;
+
+ return true;
+ }
+
+ void close()
+ {
+ if (pCdio != nullptr) {
+ cdio_destroy(pCdio);
+ pCdio = nullptr;
+ }
+ }
+ ~CdioDrive() {
+ close();
+ }
+
+ CdIo_t *pCdio = nullptr;
+};
+
+bool CdioTrack::Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type)
+{
+ lsn_t lsn = cdio_lba_to_lsn(FAD);
+ if (audio)
+ {
+ *sector_type = SECFMT_2352;
+ if (cdio_read_audio_sector(disk.pCdio, dst, lsn) != DRIVER_OP_SUCCESS) {
+ WARN_LOG(GDROM, "Read audio fad %d failed", FAD);
+ return false;
+ }
+ }
+ else
+ {
+ *sector_type = SECFMT_2048_MODE2_FORM1;
+ if (cdio_read_mode2_sector(disk.pCdio, dst, lsn, false) != DRIVER_OP_SUCCESS)
+ {
+ if (cdio_read_mode1_sector(disk.pCdio, dst, lsn, false) != DRIVER_OP_SUCCESS) {
+ WARN_LOG(GDROM, "Read data fad %d failed", FAD);
+ return false;
+ }
+ *sector_type = SECFMT_2048_MODE1;
+ }
+ }
+
+ return true;
+}
+
+Disc *cdio_parse(const char *file, std::vector *digest)
+{
+ INFO_LOG(GDROM, "Opening CDIO device %s", file);
+ CdioDrive *disk = new CdioDrive();
+
+ if (disk->open(file))
+ {
+ if (digest != nullptr)
+ digest->clear();
+ return disk;
+ }
+ else {
+ delete disk;
+ return nullptr;
+ }
+}
+
+#endif // USE_LIBCDIO
diff --git a/core/imgread/common.cpp b/core/imgread/common.cpp
index 6e7199241..e40c24d5f 100644
--- a/core/imgread/common.cpp
+++ b/core/imgread/common.cpp
@@ -9,7 +9,7 @@ Disc* chd_parse(const char* file, std::vector *digest);
Disc* gdi_parse(const char* file, std::vector *digest);
Disc* cdi_parse(const char* file, std::vector *digest);
Disc* cue_parse(const char* file, std::vector *digest);
-Disc* ioctl_parse(const char* file, std::vector *digest);
+Disc *cdio_parse(const char *file, std::vector *digest);
static u32 NullDriveDiscType;
Disc* disc;
@@ -21,8 +21,8 @@ constexpr Disc* (*drivers[])(const char* path, std::vector *digest)
gdi_parse,
cdi_parse,
cue_parse,
-#if defined(_WIN32) && !defined(TARGET_UWP)
- ioctl_parse,
+#ifdef USE_LIBCDIO
+ cdio_parse,
#endif
};
@@ -278,7 +278,7 @@ bool Disc::readSector(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode,
void Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, LoadProgress *progress)
{
- u8 temp[2448];
+ u8 temp[2352];
SectorFormat secfmt;
SubcodeFormat subfmt;
diff --git a/core/imgread/ioctl.cpp b/core/imgread/ioctl.cpp
deleted file mode 100644
index 6b879e0ef..000000000
--- a/core/imgread/ioctl.cpp
+++ /dev/null
@@ -1,360 +0,0 @@
-#include "build.h"
-#if defined(_WIN32) && !defined(TARGET_UWP)
-#include "types.h"
-#include "common.h"
-
-#include
-#include
-
-#include
-#include
-
-#ifdef _MSC_VER
-#define _NTSCSI_USER_MODE_
-#include
-#undef _NTSCSI_USER_MODE_
-#else
-#define CD_RAW_READ_SUBCODE_SIZE ( 96)
-
-#pragma pack(push, cdb, 1)
-typedef union _CDB {
- struct _READ_CD {
- UCHAR OperationCode; // 0xBE - SCSIOP_READ_CD
- UCHAR RelativeAddress : 1;
- UCHAR Reserved0 : 1;
- UCHAR ExpectedSectorType : 3;
- UCHAR Lun : 3;
- UCHAR StartingLBA[4];
- UCHAR TransferBlocks[3];
- UCHAR Reserved2 : 1;
- UCHAR ErrorFlags : 2;
- UCHAR IncludeEDC : 1;
- UCHAR IncludeUserData : 1;
- UCHAR HeaderCode : 2;
- UCHAR IncludeSyncData : 1;
- UCHAR SubChannelSelection : 3;
- UCHAR Reserved3 : 5;
- UCHAR Control;
- } READ_CD;
-} CDB, *PCDB;
-#pragma pack(pop, cdb)
-
-#define READ_TOC_FORMAT_FULL_TOC 0x02
-
-#define SCSIOP_READ 0x28
-#define SCSIOP_READ_CD 0xBE
-#endif
-
-#define RAW_SECTOR_SIZE 2352
-#define CD_SECTOR_SIZE 2048
-#define SECTORS_AT_READ 20
-#define CD_BLOCKS_PER_SECOND 75
-
-struct spti_s
-{
- SCSI_PASS_THROUGH_DIRECT sptd;
- DWORD alignmentDummy;
- BYTE senseBuf[0x12];
-};
-
-ULONG msf2fad(const UCHAR Addr[4])
-{
- ULONG Sectors = ( Addr[0] * (CD_BLOCKS_PER_SECOND*60) ) + ( Addr[1]*CD_BLOCKS_PER_SECOND) + Addr[2];
- return Sectors;
-}
-
-
-// Msf: Hours, Minutes, Seconds, Frames
-ULONG AddressToSectors( UCHAR Addr[4] );
-
-
-bool spti_SendCommand(HANDLE hand,spti_s& s,SCSI_ADDRESS& ioctl_addr)
-{
- s.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
- s.sptd.PathId = ioctl_addr.PathId;
- s.sptd.TargetId = ioctl_addr.TargetId;
- s.sptd.Lun = ioctl_addr.Lun;
- s.sptd.TimeOutValue = 30;
- //s.sptd.CdbLength = 0x0A;
- s.sptd.SenseInfoLength = 0x12;
- s.sptd.SenseInfoOffset = offsetof(spti_s, senseBuf);
-// s.sptd.DataIn = SCSI_IOCTL_DATA_IN;
-// s.sptd.DataTransferLength = 0x800;
-// s.sptd.DataBuffer = pdata;
-
- DWORD bytesReturnedIO = 0;
- if(!DeviceIoControl(hand, IOCTL_SCSI_PASS_THROUGH_DIRECT, &s, sizeof(s), &s, sizeof(s), &bytesReturnedIO, NULL))
- return false;
-
- if(s.sptd.ScsiStatus)
- return false;
- return true;
-}
-
-bool spti_Read10(HANDLE hand,void * pdata,u32 sector,SCSI_ADDRESS& ioctl_addr)
-{
- spti_s s;
- memset(&s,0,sizeof(spti_s));
-
- s.sptd.Cdb[0] = SCSIOP_READ;
- s.sptd.Cdb[1] = (ioctl_addr.Lun&7) << 5;// | DPO ; DPO = 8
-
- s.sptd.Cdb[2] = (BYTE)(sector >> 0x18 & 0xFF); // MSB
- s.sptd.Cdb[3] = (BYTE)(sector >> 0x10 & 0xFF);
- s.sptd.Cdb[4] = (BYTE)(sector >> 0x08 & 0xFF);
- s.sptd.Cdb[5] = (BYTE)(sector >> 0x00 & 0xFF); // LSB
-
- s.sptd.Cdb[7] = 0;
- s.sptd.Cdb[8] = 1;
-
- s.sptd.CdbLength = 0x0A;
- s.sptd.DataIn = SCSI_IOCTL_DATA_IN;
- s.sptd.DataTransferLength = 0x800;
- s.sptd.DataBuffer = pdata;
-
- return spti_SendCommand(hand,s,ioctl_addr);
-}
-bool spti_ReadCD(HANDLE hand,void * pdata,u32 sector,SCSI_ADDRESS& ioctl_addr)
-{
- spti_s s;
- memset(&s,0,sizeof(spti_s));
- CDB& r = *(PCDB)s.sptd.Cdb;
-
- r.READ_CD.OperationCode = SCSIOP_READ_CD;
-
- r.READ_CD.StartingLBA[0] = (BYTE)(sector >> 0x18 & 0xFF);
- r.READ_CD.StartingLBA[1] = (BYTE)(sector >> 0x10 & 0xFF);
- r.READ_CD.StartingLBA[2] = (BYTE)(sector >> 0x08 & 0xFF);
- r.READ_CD.StartingLBA[3] = (BYTE)(sector >> 0x00 & 0xFF);
-
- // 1 sector
- r.READ_CD.TransferBlocks[0] = 0;
- r.READ_CD.TransferBlocks[1] = 0;
- r.READ_CD.TransferBlocks[2] = 1;
-
- // 0xF8
- r.READ_CD.IncludeSyncData = 1;
- r.READ_CD.HeaderCode = 3;
- r.READ_CD.IncludeUserData = 1;
- r.READ_CD.IncludeEDC = 1;
-
- r.READ_CD.SubChannelSelection = 1;
-
- s.sptd.CdbLength = 12;
- s.sptd.DataIn = SCSI_IOCTL_DATA_IN;
- s.sptd.DataTransferLength = 2448;
- s.sptd.DataBuffer = pdata;
- return spti_SendCommand(hand,s,ioctl_addr);
-}
-
-struct PhysicalDrive;
-struct PhysicalTrack:TrackFile
-{
- PhysicalDrive* disc;
- PhysicalTrack(PhysicalDrive* disc) { this->disc=disc; }
-
- bool Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type) override;
-};
-
-struct PhysicalDrive:Disc
-{
- HANDLE drive;
- SCSI_ADDRESS scsi_addr;
- bool use_scsi;
-
- PhysicalDrive()
- {
- drive=INVALID_HANDLE_VALUE;
- memset(&scsi_addr,0,sizeof(scsi_addr));
- use_scsi=false;
- }
-
- bool Build(char* path)
- {
- drive = CreateFile( path, GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL, OPEN_EXISTING, 0, NULL);
-
- if ( INVALID_HANDLE_VALUE == drive )
- return false; //failed to open
-
- printf(" Opened device %s, reading TOC ...",path);
- // Get track-table and parse it
- CDROM_READ_TOC_EX tocrq={0};
-
- tocrq.Format = READ_TOC_FORMAT_FULL_TOC;
- tocrq.Msf=1;
- tocrq.SessionTrack=1;
- u8 buff[2048];
- CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)buff;
-
- ULONG BytesRead;
- memset(buff,0,sizeof(buff));
- int code = DeviceIoControl(drive,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &BytesRead, NULL);
-
-// CDROM_TOC toc;
- int currs=-1;
- if (0==code)
- {
- printf(" failed\n");
- //failed to read toc
- CloseHandle(drive);
- return false;
- }
- else
- {
- printf(" done !\n");
-
- type=CdRom_XA;
-
- BytesRead-=sizeof(CDROM_TOC_FULL_TOC_DATA);
- BytesRead/=sizeof(ftd->Descriptors[0]);
-
- for (u32 i=0;iDescriptors[i].Point==0xA2)
- {
- this->EndFAD=msf2fad(ftd->Descriptors[i].Msf);
- continue;
- }
- if (ftd->Descriptors[i].Point>=1 && ftd->Descriptors[i].Point<=0x63 &&
- ftd->Descriptors[i].Adr==1)
- {
- u32 trackn=ftd->Descriptors[i].Point-1;
- verify(trackn==tracks.size());
- Track t;
-
- t.ADR=ftd->Descriptors[i].Adr;
- t.CTRL=ftd->Descriptors[i].Control;
- t.StartFAD=msf2fad(ftd->Descriptors[i].Msf);
- t.file = new PhysicalTrack(this);
-
- tracks.push_back(t);
-
- if (currs!=ftd->Descriptors[i].SessionNumber)
- {
- currs=ftd->Descriptors[i].SessionNumber;
- verify(sessions.size()==(currs-1));
- Session s;
- s.FirstTrack=trackn+1;
- s.StartFAD=t.StartFAD;
-
- sessions.push_back(s);
- }
- }
- }
- LeadOut.StartFAD = EndFAD;
- LeadOut.ADR = 1;
- LeadOut.CTRL = 4;
- }
-
- DWORD bytesReturnedIO = 0;
- BOOL resultIO = DeviceIoControl(drive, IOCTL_SCSI_GET_ADDRESS, NULL, 0, &scsi_addr, sizeof(scsi_addr), &bytesReturnedIO, NULL);
- //done !
- if (resultIO)
- use_scsi=true;
- else
- use_scsi=false;
-
- return true;
- }
-};
-
-bool PhysicalTrack::Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type)
-{
- static u8 temp[2500];
-
- u32 LBA=FAD-150;
-
- if (disc->use_scsi)
- {
- if (!spti_ReadCD(disc->drive, temp,LBA,disc->scsi_addr))
- {
- if (spti_Read10(disc->drive, dst,LBA,disc->scsi_addr))
- {
- //sector read success, just user data
- *sector_type=SECFMT_2048_MODE2_FORM1; //m2f1 seems more common ? is there some way to detect it properly here?
- return true;
- }
- }
- else
- {
- //sector read success, with subcode
- memcpy(dst, temp, 2352);
- memcpy(subcode, temp + 2352, CD_RAW_READ_SUBCODE_SIZE);
-
- *sector_type=SECFMT_2352;
- *subcode_type=SUBFMT_96;
- return true;
- }
- }
-
- //hmm, spti failed/cannot be used
-
-
- //try IOCTL_CDROM_RAW_READ
-
-
- static __RAW_READ_INFO Info;
-
- Info.SectorCount=1;
- Info.DiskOffset.QuadPart = LBA * CD_SECTOR_SIZE; //CD_SECTOR_SIZE, even though we read RAW sectors. Its how winapi works.
- ULONG Dummy;
-
- //try all 3 track modes, starting from the one that succeeded last time (Info is static) to save time !
- for (int tr=0;tr<3;tr++)
- {
- if ( 0 == DeviceIoControl( disc->drive, IOCTL_CDROM_RAW_READ, &Info, sizeof(Info), dst, RAW_SECTOR_SIZE, &Dummy, NULL ) )
- {
- Info.TrackMode=(TRACK_MODE_TYPE)((Info.TrackMode+1)%3); //try next mode
- }
- else
- {
- //sector read success
- *sector_type=SECFMT_2352;
- return true;
- }
- }
-
- //finally, try ReadFile
- if (SetFilePointer(disc->drive,LBA*2048,0,FILE_BEGIN)!=INVALID_SET_FILE_POINTER)
- {
- DWORD BytesRead;
- if (FALSE!=ReadFile(disc->drive,dst,2048,&BytesRead,0) && BytesRead==2048)
- {
- //sector read success, just user data
- *sector_type=SECFMT_2048_MODE2_FORM1; //m2f1 seems more common ? is there some way to detect it properly here?
- return true;
- }
- }
-
- printf("IOCTL: Totally failed to read sector @LBA %d\n", LBA);
- return false;
-}
-
-
-Disc* ioctl_parse(const char* file, std::vector *digest)
-{
-
- if (strlen(file)==3 && GetDriveType(file)==DRIVE_CDROM)
- {
- printf("Opening device %s ...",file);
- char fn[]={ '\\', '\\', '.', '\\', file[0],':', '\0' };
- PhysicalDrive* rv = new PhysicalDrive();
-
- if (rv->Build(fn))
- {
- if (digest != nullptr)
- digest->clear();
- return rv;
- }
- else
- {
- delete rv;
- return 0;
- }
- }
- else
- {
- return 0;
- }
-}
-#endif
diff --git a/core/oslib/oslib.cpp b/core/oslib/oslib.cpp
index cb2c3aaa9..4595ed42d 100644
--- a/core/oslib/oslib.cpp
+++ b/core/oslib/oslib.cpp
@@ -315,6 +315,13 @@ void saveScreenshot(const std::string& name, const std::vector& data)
#endif
+#ifndef USE_LIBCDIO
+const std::vector& getCdromDrives() {
+ static std::vector empty;
+ return empty;
+}
+#endif
+
} // namespace hostfs
void os_CreateWindow()
diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h
index 7074c8346..e55fffea3 100644
--- a/core/oslib/oslib.h
+++ b/core/oslib/oslib.h
@@ -63,6 +63,7 @@ namespace hostfs
std::string getShaderCachePath(const std::string& filename);
void saveScreenshot(const std::string& name, const std::vector& data);
+ const std::vector& getCdromDrives();
#ifdef __ANDROID__
void importHomeDirectory();
void exportHomeDirectory();
diff --git a/core/oslib/storage.cpp b/core/oslib/storage.cpp
index 97d21f1ce..dbb1546d9 100644
--- a/core/oslib/storage.cpp
+++ b/core/oslib/storage.cpp
@@ -194,24 +194,43 @@ public:
info.size = st.st_size;
info.updateTime = st.st_mtime;
#else // _WIN32
+ std::string lpath(path);
+ if (path.length() == 2 && isalpha(path[0]) && path[1] == ':')
+ /* D: -> \\.\D:\ */
+ lpath = "\\\\.\\" + path + "\\";
+ else if (path.substr(0, 4) == "\\\\.\\" && path.length() == 6)
+ /* \\.\D: -> \\.\D:\ */
+ lpath += "\\";
nowide::wstackstring wname;
- if (wname.convert(path.c_str()))
+ if (wname.convert(lpath.c_str()))
{
- WIN32_FILE_ATTRIBUTE_DATA fileAttribs;
- if (GetFileAttributesExW(wname.get(), GetFileExInfoStandard, &fileAttribs))
+ if (lpath.substr(0, 4) == "\\\\.\\")
{
- info.isDirectory = (fileAttribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- info.size = fileAttribs.nFileSizeLow + ((u64)fileAttribs.nFileSizeHigh << 32);
- u64 t = ((u64)fileAttribs.ftLastWriteTime.dwHighDateTime << 32) | fileAttribs.ftLastWriteTime.dwLowDateTime;
- info.updateTime = t / 10000000 - 11644473600LL; // 100-nano to secs minus (unix epoch - windows epoch)
+ // Win32 device namespace
+ UINT type = GetDriveTypeW(wname.get());
+ if (type != DRIVE_CDROM)
+ throw StorageException("Invalid device " + lpath.substr(4, 2));
+ info.isDirectory = false;
+ info.isWritable = false;
}
else
{
- const int error = GetLastError();
- if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND)
- INFO_LOG(COMMON, "Cannot get attributes of '%s' error 0x%x", path.c_str(), error);
- _set_errno(error);
- throw StorageException("Cannot get attributes of " + path);
+ WIN32_FILE_ATTRIBUTE_DATA fileAttribs;
+ if (GetFileAttributesExW(wname.get(), GetFileExInfoStandard, &fileAttribs))
+ {
+ info.isDirectory = (fileAttribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ info.size = fileAttribs.nFileSizeLow + ((u64)fileAttribs.nFileSizeHigh << 32);
+ u64 t = ((u64)fileAttribs.ftLastWriteTime.dwHighDateTime << 32) | fileAttribs.ftLastWriteTime.dwLowDateTime;
+ info.updateTime = t / 10000000 - 11644473600LL; // 100-nano to secs minus (unix epoch - windows epoch)
+ }
+ else
+ {
+ const int error = GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND)
+ INFO_LOG(COMMON, "Cannot get attributes of '%s' error 0x%x", lpath.c_str(), error);
+ _set_errno(error);
+ throw StorageException("Cannot get attributes of " + lpath);
+ }
}
}
else
diff --git a/core/ui/game_scanner.cpp b/core/ui/game_scanner.cpp
index 0dba94738..d56dd179e 100644
--- a/core/ui/game_scanner.cpp
+++ b/core/ui/game_scanner.cpp
@@ -152,9 +152,22 @@ void GameScanner::fetch_game_list()
}
std::string dcbios = hostfs::findFlash("dc_", "%bios.bin;%boot.bin");
{
+ const std::vector& cdromDrives = hostfs::getCdromDrives();
LockGuard _(mutex);
+ // CD-ROM devices
+ for (auto it = cdromDrives.rbegin(); it != cdromDrives.rend(); ++it)
+ {
+ std::string name;
+ if (it->substr(0, 4) == "\\\\.\\")
+ name = it->substr(4);
+ else
+ name = *it;
+ game_list.insert(game_list.begin(), { name, *it, name, "", true });
+ }
+ // Dreamcast BIOS
if (!dcbios.empty())
game_list.insert(game_list.begin(), { "Dreamcast BIOS" });
+ // Arcade games
game_list.insert(game_list.end(), arcade_game_list.begin(), arcade_game_list.end());
}
if (running)
diff --git a/core/ui/game_scanner.h b/core/ui/game_scanner.h
index f6f5901d8..e92b17ddb 100644
--- a/core/ui/game_scanner.h
+++ b/core/ui/game_scanner.h
@@ -26,11 +26,13 @@
#include
#include
-struct GameMedia {
+struct GameMedia
+{
std::string name; // Display name
std::string path; // Full path to rom. May be an encoded uri
std::string fileName; // Last component of the path, decoded
std::string gameName; // for arcade games only, description from the rom list
+ bool device = false; // Corresponds to a physical cdrom device
};
class GameScanner
diff --git a/core/ui/gui.cpp b/core/ui/gui.cpp
index e826a6b77..286dc66fa 100644
--- a/core/ui/gui.cpp
+++ b/core/ui/gui.cpp
@@ -3275,7 +3275,7 @@ static void gui_display_content()
if (gui_state == GuiState::SelectDisk)
{
std::string extension = get_file_extension(game.path);
- if (extension != "gdi" && extension != "chd"
+ if (!game.device && extension != "gdi" && extension != "chd"
&& extension != "cdi" && extension != "cue")
// Only dreamcast disks
continue;
@@ -3285,7 +3285,7 @@ static void gui_display_content()
}
std::string gameName = game.name;
GameBoxart art;
- if (config::BoxartDisplayMode)
+ if (config::BoxartDisplayMode && !game.device)
{
art = boxart.getBoxartAndLoad(game);
gameName = art.name;
diff --git a/shell/linux/make-appimage.sh b/shell/linux/make-appimage.sh
index 67f594dc8..e8dddbfdd 100755
--- a/shell/linux/make-appimage.sh
+++ b/shell/linux/make-appimage.sh
@@ -72,6 +72,7 @@ SHLIBS=(
libsqlite3.so.0
libcrypt.so.1
libbsd.so.0
+ libcdio.so.18
)
if [ ! -f appimagetool-x86_64.AppImage ]; then