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:
|
include:
|
||||||
- operating_system: freebsd
|
- operating_system: freebsd
|
||||||
version: '14.2'
|
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
|
- operating_system: netbsd
|
||||||
version: '10.0'
|
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
|
- operating_system: openbsd
|
||||||
version: '7.6'
|
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:
|
exclude:
|
||||||
- architecture: arm64
|
- architecture: arm64
|
||||||
|
|
||||||
|
@ -44,5 +44,5 @@ jobs:
|
||||||
environment_variables: CCACHE_DIR
|
environment_variables: CCACHE_DIR
|
||||||
run: |
|
run: |
|
||||||
${{ matrix.pkginstall }}
|
${{ 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
|
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: 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-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: 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-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: 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 -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
|
- {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 -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:
|
steps:
|
||||||
- name: Set up build environment (macOS)
|
- name: Set up build environment (macOS)
|
||||||
|
@ -45,7 +45,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository ppa:christianrauch/libdecoration
|
sudo add-apt-repository ppa:christianrauch/libdecoration
|
||||||
sudo apt-get update
|
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
|
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'
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ jobs:
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
msystem: MINGW64
|
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}'
|
if: matrix.config.shell == 'msys2 {0}'
|
||||||
|
|
||||||
- name: Set up build environment (Windows, Visual Studio)
|
- name: Set up build environment (Windows, Visual Studio)
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
variables:
|
variables:
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
CORENAME: flycast
|
CORENAME: flycast
|
||||||
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
|
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
.core-defs-linux:
|
.core-defs-linux:
|
||||||
extends: .core-defs
|
extends: .core-defs
|
||||||
variables:
|
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:
|
.core-defs-osx-x64:
|
||||||
extends: .core-defs
|
extends: .core-defs
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
.core-defs-android:
|
.core-defs-android:
|
||||||
extends: .core-defs
|
extends: .core-defs
|
||||||
script:
|
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
|
- cmake --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC
|
||||||
- mv $BUILD_DIR/${CORENAME}_libretro.so $LIBNAME
|
- 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
|
- 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_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(ENABLE_FC_PROFILER "Build with support for host app (Flycast) profiler" OFF)
|
||||||
option(USE_DISCORD "Use Discord Presence API" OFF)
|
option(USE_DISCORD "Use Discord Presence API" OFF)
|
||||||
|
option(USE_LIBCDIO "Use libcdio for CDROM access" OFF)
|
||||||
|
|
||||||
if(IOS AND NOT LIBRETRO)
|
if(IOS AND NOT LIBRETRO)
|
||||||
set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE)
|
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)
|
target_link_libraries(${PROJECT_NAME} PRIVATE libzip::zip)
|
||||||
endif()
|
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)
|
if(WIN32)
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent)
|
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent)
|
||||||
endif()
|
endif()
|
||||||
|
@ -1062,13 +1080,13 @@ endif()
|
||||||
|
|
||||||
target_sources(${PROJECT_NAME} PRIVATE
|
target_sources(${PROJECT_NAME} PRIVATE
|
||||||
core/imgread/cdi.cpp
|
core/imgread/cdi.cpp
|
||||||
|
core/imgread/cdio.cpp
|
||||||
core/imgread/chd.cpp
|
core/imgread/chd.cpp
|
||||||
core/imgread/common.cpp
|
core/imgread/common.cpp
|
||||||
core/imgread/common.h
|
core/imgread/common.h
|
||||||
core/imgread/cue.cpp
|
core/imgread/cue.cpp
|
||||||
core/imgread/gdi.cpp
|
core/imgread/gdi.cpp
|
||||||
core/imgread/ImgReader.cpp
|
core/imgread/ImgReader.cpp
|
||||||
core/imgread/ioctl.cpp
|
|
||||||
core/imgread/iso9660.h
|
core/imgread/iso9660.h
|
||||||
core/imgread/isofs.cpp
|
core/imgread/isofs.cpp
|
||||||
core/imgread/isofs.h)
|
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* gdi_parse(const char* file, std::vector<u8> *digest);
|
||||||
Disc* cdi_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* 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;
|
static u32 NullDriveDiscType;
|
||||||
Disc* disc;
|
Disc* disc;
|
||||||
|
@ -21,8 +21,8 @@ constexpr Disc* (*drivers[])(const char* path, std::vector<u8> *digest)
|
||||||
gdi_parse,
|
gdi_parse,
|
||||||
cdi_parse,
|
cdi_parse,
|
||||||
cue_parse,
|
cue_parse,
|
||||||
#if defined(_WIN32) && !defined(TARGET_UWP)
|
#ifdef USE_LIBCDIO
|
||||||
ioctl_parse,
|
cdio_parse,
|
||||||
#endif
|
#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)
|
void Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, LoadProgress *progress)
|
||||||
{
|
{
|
||||||
u8 temp[2448];
|
u8 temp[2352];
|
||||||
SectorFormat secfmt;
|
SectorFormat secfmt;
|
||||||
SubcodeFormat subfmt;
|
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
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_LIBCDIO
|
||||||
|
const std::vector<std::string>& getCdromDrives() {
|
||||||
|
static std::vector<std::string> empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace hostfs
|
} // namespace hostfs
|
||||||
|
|
||||||
void os_CreateWindow()
|
void os_CreateWindow()
|
||||||
|
|
|
@ -63,6 +63,7 @@ namespace hostfs
|
||||||
std::string getShaderCachePath(const std::string& filename);
|
std::string getShaderCachePath(const std::string& filename);
|
||||||
void saveScreenshot(const std::string& name, const std::vector<u8>& data);
|
void saveScreenshot(const std::string& name, const std::vector<u8>& data);
|
||||||
|
|
||||||
|
const std::vector<std::string>& getCdromDrives();
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
void importHomeDirectory();
|
void importHomeDirectory();
|
||||||
void exportHomeDirectory();
|
void exportHomeDirectory();
|
||||||
|
|
|
@ -194,24 +194,43 @@ public:
|
||||||
info.size = st.st_size;
|
info.size = st.st_size;
|
||||||
info.updateTime = st.st_mtime;
|
info.updateTime = st.st_mtime;
|
||||||
#else // _WIN32
|
#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;
|
nowide::wstackstring wname;
|
||||||
if (wname.convert(path.c_str()))
|
if (wname.convert(lpath.c_str()))
|
||||||
{
|
{
|
||||||
WIN32_FILE_ATTRIBUTE_DATA fileAttribs;
|
if (lpath.substr(0, 4) == "\\\\.\\")
|
||||||
if (GetFileAttributesExW(wname.get(), GetFileExInfoStandard, &fileAttribs))
|
|
||||||
{
|
{
|
||||||
info.isDirectory = (fileAttribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
// Win32 device namespace
|
||||||
info.size = fileAttribs.nFileSizeLow + ((u64)fileAttribs.nFileSizeHigh << 32);
|
UINT type = GetDriveTypeW(wname.get());
|
||||||
u64 t = ((u64)fileAttribs.ftLastWriteTime.dwHighDateTime << 32) | fileAttribs.ftLastWriteTime.dwLowDateTime;
|
if (type != DRIVE_CDROM)
|
||||||
info.updateTime = t / 10000000 - 11644473600LL; // 100-nano to secs minus (unix epoch - windows epoch)
|
throw StorageException("Invalid device " + lpath.substr(4, 2));
|
||||||
|
info.isDirectory = false;
|
||||||
|
info.isWritable = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const int error = GetLastError();
|
WIN32_FILE_ATTRIBUTE_DATA fileAttribs;
|
||||||
if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND)
|
if (GetFileAttributesExW(wname.get(), GetFileExInfoStandard, &fileAttribs))
|
||||||
INFO_LOG(COMMON, "Cannot get attributes of '%s' error 0x%x", path.c_str(), error);
|
{
|
||||||
_set_errno(error);
|
info.isDirectory = (fileAttribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
throw StorageException("Cannot get attributes of " + path);
|
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
|
else
|
||||||
|
|
|
@ -152,9 +152,22 @@ void GameScanner::fetch_game_list()
|
||||||
}
|
}
|
||||||
std::string dcbios = hostfs::findFlash("dc_", "%bios.bin;%boot.bin");
|
std::string dcbios = hostfs::findFlash("dc_", "%bios.bin;%boot.bin");
|
||||||
{
|
{
|
||||||
|
const std::vector<std::string>& cdromDrives = hostfs::getCdromDrives();
|
||||||
LockGuard _(mutex);
|
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())
|
if (!dcbios.empty())
|
||||||
game_list.insert(game_list.begin(), { "Dreamcast BIOS" });
|
game_list.insert(game_list.begin(), { "Dreamcast BIOS" });
|
||||||
|
// Arcade games
|
||||||
game_list.insert(game_list.end(), arcade_game_list.begin(), arcade_game_list.end());
|
game_list.insert(game_list.end(), arcade_game_list.begin(), arcade_game_list.end());
|
||||||
}
|
}
|
||||||
if (running)
|
if (running)
|
||||||
|
|
|
@ -26,11 +26,13 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
struct GameMedia {
|
struct GameMedia
|
||||||
|
{
|
||||||
std::string name; // Display name
|
std::string name; // Display name
|
||||||
std::string path; // Full path to rom. May be an encoded uri
|
std::string path; // Full path to rom. May be an encoded uri
|
||||||
std::string fileName; // Last component of the path, decoded
|
std::string fileName; // Last component of the path, decoded
|
||||||
std::string gameName; // for arcade games only, description from the rom list
|
std::string gameName; // for arcade games only, description from the rom list
|
||||||
|
bool device = false; // Corresponds to a physical cdrom device
|
||||||
};
|
};
|
||||||
|
|
||||||
class GameScanner
|
class GameScanner
|
||||||
|
|
|
@ -3275,7 +3275,7 @@ static void gui_display_content()
|
||||||
if (gui_state == GuiState::SelectDisk)
|
if (gui_state == GuiState::SelectDisk)
|
||||||
{
|
{
|
||||||
std::string extension = get_file_extension(game.path);
|
std::string extension = get_file_extension(game.path);
|
||||||
if (extension != "gdi" && extension != "chd"
|
if (!game.device && extension != "gdi" && extension != "chd"
|
||||||
&& extension != "cdi" && extension != "cue")
|
&& extension != "cdi" && extension != "cue")
|
||||||
// Only dreamcast disks
|
// Only dreamcast disks
|
||||||
continue;
|
continue;
|
||||||
|
@ -3285,7 +3285,7 @@ static void gui_display_content()
|
||||||
}
|
}
|
||||||
std::string gameName = game.name;
|
std::string gameName = game.name;
|
||||||
GameBoxart art;
|
GameBoxart art;
|
||||||
if (config::BoxartDisplayMode)
|
if (config::BoxartDisplayMode && !game.device)
|
||||||
{
|
{
|
||||||
art = boxart.getBoxartAndLoad(game);
|
art = boxart.getBoxartAndLoad(game);
|
||||||
gameName = art.name;
|
gameName = art.name;
|
||||||
|
|
|
@ -72,6 +72,7 @@ SHLIBS=(
|
||||||
libsqlite3.so.0
|
libsqlite3.so.0
|
||||||
libcrypt.so.1
|
libcrypt.so.1
|
||||||
libbsd.so.0
|
libbsd.so.0
|
||||||
|
libcdio.so.18
|
||||||
)
|
)
|
||||||
|
|
||||||
if [ ! -f appimagetool-x86_64.AppImage ]; then
|
if [ ! -f appimagetool-x86_64.AppImage ]; then
|
||||||
|
|
Loading…
Reference in New Issue