gdrom: add support for hardware CD-ROM devices
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
This commit is contained in:
parent
f5389bcd0b
commit
34961daaec
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,360 +0,0 @@
|
|||
#include "build.h"
|
||||
#if defined(_WIN32) && !defined(TARGET_UWP)
|
||||
#include "types.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntddcdrm.h>
|
||||
#include <ntddscsi.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _NTSCSI_USER_MODE_
|
||||
#include <scsi.h>
|
||||
#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;i<BytesRead;i++)
|
||||
{
|
||||
if (ftd->Descriptors[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<u8> *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
|
|
@ -315,6 +315,13 @@ void saveScreenshot(const std::string& name, const std::vector<u8>& data)
|
|||
|
||||
#endif
|
||||
|
||||
#ifndef USE_LIBCDIO
|
||||
const std::vector<std::string>& getCdromDrives() {
|
||||
static std::vector<std::string> empty;
|
||||
return empty;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace hostfs
|
||||
|
||||
void os_CreateWindow()
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace hostfs
|
|||
std::string getShaderCachePath(const std::string& filename);
|
||||
void saveScreenshot(const std::string& name, const std::vector<u8>& data);
|
||||
|
||||
const std::vector<std::string>& getCdromDrives();
|
||||
#ifdef __ANDROID__
|
||||
void importHomeDirectory();
|
||||
void exportHomeDirectory();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -152,9 +152,22 @@ void GameScanner::fetch_game_list()
|
|||
}
|
||||
std::string dcbios = hostfs::findFlash("dc_", "%bios.bin;%boot.bin");
|
||||
{
|
||||
const std::vector<std::string>& 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)
|
||||
|
|
|
@ -26,11 +26,13 @@
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -72,6 +72,7 @@ SHLIBS=(
|
|||
libsqlite3.so.0
|
||||
libcrypt.so.1
|
||||
libbsd.so.0
|
||||
libcdio.so.18
|
||||
)
|
||||
|
||||
if [ ! -f appimagetool-x86_64.AppImage ]; then
|
||||
|
|
Loading…
Reference in New Issue