Skip to content

Commit

Permalink
gdrom: add support for hardware CD-ROM devices
Browse files Browse the repository at this point in the history
Use libcdio to read CD/DVD/BD drives (linux, windows, bsd)
Get rid of old ioctl win32 driver.
Add detected CDROM devices to game list.
Issue #1654
  • Loading branch information
flyinghead committed Dec 11, 2024
1 parent f5389bc commit 34961da
Show file tree
Hide file tree
Showing 14 changed files with 328 additions and 393 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/bsd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
12 changes: 6 additions & 6 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -45,15 +45,15 @@ 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'

- name: Set up build environment (Windows, MinGW)
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)
Expand Down
6 changes: 3 additions & 3 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
20 changes: 19 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
Expand Down
234 changes: 234 additions & 0 deletions core/imgread/cdio.cpp
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
*/
#include "build.h"
#ifdef USE_LIBCDIO
#include "types.h"
#include "imgread/common.h"
#include <cstring>
#include <cdio/cdio.h>
#include <cdio/logging.h>
#include <vector>

namespace hostfs
{

const std::vector<std::string>& getCdromDrives()
{
static std::vector<std::string> 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<std::string>& 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<u8> *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
8 changes: 4 additions & 4 deletions core/imgread/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Disc* chd_parse(const char* file, std::vector<u8> *digest);
Disc* gdi_parse(const char* file, std::vector<u8> *digest);
Disc* cdi_parse(const char* file, std::vector<u8> *digest);
Disc* cue_parse(const char* file, std::vector<u8> *digest);
Disc* ioctl_parse(const char* file, std::vector<u8> *digest);
Disc *cdio_parse(const char *file, std::vector<u8> *digest);

static u32 NullDriveDiscType;
Disc* disc;
Expand All @@ -21,8 +21,8 @@ constexpr Disc* (*drivers[])(const char* path, std::vector<u8> *digest)
gdi_parse,
cdi_parse,
cue_parse,
#if defined(_WIN32) && !defined(TARGET_UWP)
ioctl_parse,
#ifdef USE_LIBCDIO
cdio_parse,
#endif
};

Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 34961da

Please sign in to comment.