Use exception when load content fails. ui: graceful stop on fatal error
libretro: catch/detect fatal errors and shutdown
This commit is contained in:
parent
2dd7717128
commit
951e70328f
|
@ -13,7 +13,7 @@ TA_context* read_frame(const char* file, u8* vram_ref = NULL);
|
||||||
void rend_set_fb_scale(float x,float y);
|
void rend_set_fb_scale(float x,float y);
|
||||||
|
|
||||||
#ifdef TARGET_DISPFRAME
|
#ifdef TARGET_DISPFRAME
|
||||||
void *dc_run(void*)
|
void dc_run()
|
||||||
{
|
{
|
||||||
struct sigaction act, segv_oact;
|
struct sigaction act, segv_oact;
|
||||||
memset(&act, 0, sizeof(act));
|
memset(&act, 0, sizeof(act));
|
||||||
|
@ -58,6 +58,5 @@ void *dc_run(void*)
|
||||||
|
|
||||||
os_DoEvents();
|
os_DoEvents();
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -42,8 +42,10 @@ extern int screen_width, screen_height;
|
||||||
std::atomic<bool> loading_canceled;
|
std::atomic<bool> loading_canceled;
|
||||||
settings_t settings;
|
settings_t settings;
|
||||||
|
|
||||||
static cThread emuThread(&dc_run, nullptr);
|
static void *dc_run_thread(void *);
|
||||||
|
static cThread emuThread(&dc_run_thread, nullptr);
|
||||||
static bool initDone;
|
static bool initDone;
|
||||||
|
static std::string lastError;
|
||||||
|
|
||||||
static s32 devicesInit()
|
static s32 devicesInit()
|
||||||
{
|
{
|
||||||
|
@ -521,11 +523,27 @@ bool dc_is_running()
|
||||||
return sh4_cpu.IsCpuRunning();
|
return sh4_cpu.IsCpuRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TARGET_DISPFRAME
|
static void *dc_run_thread(void*)
|
||||||
void* dc_run(void*)
|
|
||||||
{
|
{
|
||||||
InitAudio();
|
InitAudio();
|
||||||
|
|
||||||
|
try {
|
||||||
|
dc_run();
|
||||||
|
} catch (const FlycastException& e) {
|
||||||
|
ERROR_LOG(COMMON, "%s", e.what());
|
||||||
|
sh4_cpu.Stop();
|
||||||
|
lastError = e.what();
|
||||||
|
}
|
||||||
|
|
||||||
|
TermAudio();
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef TARGET_DISPFRAME
|
||||||
|
|
||||||
|
void dc_run()
|
||||||
|
{
|
||||||
#if FEAT_SHREC != DYNAREC_NONE
|
#if FEAT_SHREC != DYNAREC_NONE
|
||||||
if (config::DynarecEnabled)
|
if (config::DynarecEnabled)
|
||||||
{
|
{
|
||||||
|
@ -557,10 +575,6 @@ void* dc_run(void*)
|
||||||
}
|
}
|
||||||
} while (resetRequested);
|
} while (resetRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
TermAudio();
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -583,8 +597,6 @@ void dc_term_emulator()
|
||||||
dc_term_game();
|
dc_term_game();
|
||||||
debugger::term();
|
debugger::term();
|
||||||
sh4_cpu.Term();
|
sh4_cpu.Term();
|
||||||
if (settings.platform.system != DC_PLATFORM_DREAMCAST)
|
|
||||||
naomi_cart_Close();
|
|
||||||
custom_texture.Terminate(); // lr: avoid deadlock on exit (win32)
|
custom_texture.Terminate(); // lr: avoid deadlock on exit (win32)
|
||||||
devicesTerm();
|
devicesTerm();
|
||||||
mem_Term();
|
mem_Term();
|
||||||
|
@ -711,6 +723,13 @@ bool dc_loadstate(const void **data, u32 size)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string dc_get_last_error()
|
||||||
|
{
|
||||||
|
std::string error(lastError);
|
||||||
|
lastError.clear();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
EventManager EventManager::Instance;
|
EventManager EventManager::Instance;
|
||||||
|
|
||||||
void EventManager::registerEvent(Event event, Callback callback)
|
void EventManager::registerEvent(Event event, Callback callback)
|
||||||
|
|
|
@ -31,7 +31,7 @@ extern std::atomic<bool> loading_canceled;
|
||||||
int flycast_init(int argc, char* argv[]);
|
int flycast_init(int argc, char* argv[]);
|
||||||
void dc_reset(bool hard);
|
void dc_reset(bool hard);
|
||||||
void dc_init();
|
void dc_init();
|
||||||
void* dc_run(void*);
|
void dc_run();
|
||||||
void dc_term();
|
void dc_term();
|
||||||
void dc_stop();
|
void dc_stop();
|
||||||
void dc_term_game();
|
void dc_term_game();
|
||||||
|
@ -50,6 +50,7 @@ void dc_cancel_load();
|
||||||
void dc_get_load_status();
|
void dc_get_load_status();
|
||||||
bool dc_is_running();
|
bool dc_is_running();
|
||||||
void dc_resize_renderer();
|
void dc_resize_renderer();
|
||||||
|
std::string dc_get_last_error();
|
||||||
|
|
||||||
enum class Event {
|
enum class Event {
|
||||||
Start,
|
Start,
|
||||||
|
|
|
@ -1,25 +1,24 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "stdclass.h"
|
||||||
|
|
||||||
#include "deps/chdpsr/cdipsr.h"
|
#include "deps/chdpsr/cdipsr.h"
|
||||||
|
|
||||||
Disc* cdi_parse(const char* file)
|
Disc* cdi_parse(const char* file)
|
||||||
{
|
{
|
||||||
// Only try to open .cdi files
|
if (get_file_extension(file) != "cdi")
|
||||||
size_t len = strlen(file);
|
|
||||||
if (len > 4 && stricmp( &file[len - 4], ".cdi"))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
FILE *fsource = nowide::fopen(file, "rb");
|
FILE *fsource = nowide::fopen(file, "rb");
|
||||||
|
|
||||||
if (!fsource)
|
if (fsource == nullptr)
|
||||||
return nullptr;
|
throw FlycastException(std::string("Cannot open CDI file ") + file);
|
||||||
|
|
||||||
image_s image = { 0 };
|
image_s image = { 0 };
|
||||||
track_s track = { 0 };
|
track_s track = { 0 };
|
||||||
if (!CDI_init(fsource, &image, file))
|
if (!CDI_init(fsource, &image, file))
|
||||||
{
|
{
|
||||||
std::fclose(fsource);
|
std::fclose(fsource);
|
||||||
return nullptr;
|
throw FlycastException(std::string("Invalid CDI file ") + file);
|
||||||
}
|
}
|
||||||
|
|
||||||
CDI_get_sessions(fsource,&image);
|
CDI_get_sessions(fsource,&image);
|
||||||
|
@ -41,8 +40,6 @@ Disc* cdi_parse(const char* file)
|
||||||
|
|
||||||
image.header_position = std::ftell(fsource);
|
image.header_position = std::ftell(fsource);
|
||||||
|
|
||||||
//printf("\nSession %d has %d track(s)\n",image.global_current_session,image.tracks);
|
|
||||||
|
|
||||||
if (image.tracks == 0)
|
if (image.tracks == 0)
|
||||||
INFO_LOG(GDROM, "Open session");
|
INFO_LOG(GDROM, "Open session");
|
||||||
else
|
else
|
||||||
|
@ -96,8 +93,6 @@ Disc* cdi_parse(const char* file)
|
||||||
if (track.mode==0)
|
if (track.mode==0)
|
||||||
CD_DA=true;
|
CD_DA=true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
t.ADDR=1;//hmm is that ok ?
|
t.ADDR=1;//hmm is that ok ?
|
||||||
|
|
||||||
t.CTRL=track.mode==0?0:4;
|
t.CTRL=track.mode==0?0:4;
|
||||||
|
@ -107,55 +102,30 @@ Disc* cdi_parse(const char* file)
|
||||||
|
|
||||||
rv->tracks.push_back(t);
|
rv->tracks.push_back(t);
|
||||||
|
|
||||||
//printf("\n");
|
|
||||||
|
|
||||||
// if (track.pregap_length != 150) printf("Warning! This track seems to have a non-standard pregap...\n");
|
|
||||||
|
|
||||||
if (track.length < 0)
|
if (track.length < 0)
|
||||||
WARN_LOG(GDROM, "Negative track size found. You must extract image with /pregap option");
|
WARN_LOG(GDROM, "Negative track size found. You must extract image with /pregap option");
|
||||||
|
|
||||||
//if (!opts.showinfo)
|
std::fseek(fsource, track.position, SEEK_SET);
|
||||||
{
|
|
||||||
if (track.total_length < track.length + track.pregap_length)
|
if (track.total_length < track.length + track.pregap_length)
|
||||||
{
|
{
|
||||||
WARN_LOG(GDROM, "This track seems truncated. Skipping...");
|
WARN_LOG(GDROM, "This track seems truncated. Skipping...");
|
||||||
std::fseek(fsource, track.position, SEEK_SET);
|
// FIXME that can't be right
|
||||||
std::fseek(fsource, track.total_length, SEEK_CUR);
|
std::fseek(fsource, track.total_length, SEEK_CUR);
|
||||||
track.position = std::ftell(fsource);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
//printf("Track position: %lu\n",track.position + track.pregap_length * track.sector_size);
|
|
||||||
std::fseek(fsource, track.position, SEEK_SET);
|
|
||||||
// fseek(fsource, track->pregap_length * track->sector_size, SEEK_CUR);
|
|
||||||
// fseek(fsource, track->length * track->sector_size, SEEK_CUR);
|
|
||||||
std::fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
|
std::fseek(fsource, track.total_length * track.sector_size, SEEK_CUR);
|
||||||
|
|
||||||
//savetrack(fsource, &image, &track, &opts, &flags);
|
|
||||||
track.position = std::ftell(fsource);
|
|
||||||
|
|
||||||
rv->EndFAD=track.start_lba +track.total_length;
|
rv->EndFAD=track.start_lba +track.total_length;
|
||||||
// Generate cuesheet entries
|
|
||||||
|
|
||||||
//if (flags.create_cuesheet && !(track.mode == 2 && flags.do_convert)) // Do not generate input if converted (obsolete)
|
|
||||||
// savecuesheet(fcuesheet, &image, &track, &opts, &flags);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
track.position = std::ftell(fsource);
|
||||||
|
|
||||||
std::fseek(fsource, image.header_position, SEEK_SET);
|
std::fseek(fsource, image.header_position, SEEK_SET);
|
||||||
|
|
||||||
|
|
||||||
// Close loops
|
|
||||||
|
|
||||||
image.remaining_tracks--;
|
image.remaining_tracks--;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (flags.create_cuesheet) fclose(fcuesheet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CDI_skip_next_session (fsource, &image);
|
CDI_skip_next_session(fsource, &image);
|
||||||
|
|
||||||
image.remaining_sessions--;
|
image.remaining_sessions--;
|
||||||
}
|
}
|
||||||
|
@ -167,8 +137,6 @@ Disc* cdi_parse(const char* file)
|
||||||
rv->LeadOut.ADDR=0;
|
rv->LeadOut.ADDR=0;
|
||||||
rv->LeadOut.CTRL=0;
|
rv->LeadOut.CTRL=0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "stdclass.h"
|
||||||
|
|
||||||
#include <libchdr/chd.h>
|
#include <libchdr/chd.h>
|
||||||
|
|
||||||
/* tracks are padded to a multiple of this many frames */
|
/* tracks are padded to a multiple of this many frames */
|
||||||
const uint32_t CD_TRACK_PADDING = 4;
|
constexpr uint32_t CD_TRACK_PADDING = 4;
|
||||||
|
|
||||||
struct CHDDisc : Disc
|
struct CHDDisc : Disc
|
||||||
{
|
{
|
||||||
|
@ -15,7 +16,7 @@ struct CHDDisc : Disc
|
||||||
u32 hunkbytes = 0;
|
u32 hunkbytes = 0;
|
||||||
u32 sph = 0;
|
u32 sph = 0;
|
||||||
|
|
||||||
bool TryOpen(const char* file);
|
void tryOpen(const char* file);
|
||||||
|
|
||||||
~CHDDisc() override
|
~CHDDisc() override
|
||||||
{
|
{
|
||||||
|
@ -38,20 +39,21 @@ struct CHDTrack : TrackFile
|
||||||
|
|
||||||
CHDTrack(CHDDisc* disc, u32 StartFAD, s32 Offset, u32 fmt, bool swap_bytes)
|
CHDTrack(CHDDisc* disc, u32 StartFAD, s32 Offset, u32 fmt, bool swap_bytes)
|
||||||
{
|
{
|
||||||
this->disc=disc;
|
this->disc = disc;
|
||||||
this->StartFAD=StartFAD;
|
this->StartFAD = StartFAD;
|
||||||
this->Offset = Offset;
|
this->Offset = Offset;
|
||||||
this->fmt=fmt;
|
this->fmt = fmt;
|
||||||
this->swap_bytes = swap_bytes;
|
this->swap_bytes = swap_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Read(u32 FAD, u8* dst, SectorFormat* sector_type, u8* subcode, SubcodeFormat* subcode_type) override
|
bool Read(u32 FAD, u8* dst, SectorFormat* sector_type, u8* subcode, SubcodeFormat* subcode_type) override
|
||||||
{
|
{
|
||||||
u32 fad_offs = FAD + Offset;
|
u32 fad_offs = FAD + Offset;
|
||||||
u32 hunk=(fad_offs)/disc->sph;
|
u32 hunk=(fad_offs)/disc->sph;
|
||||||
if (disc->old_hunk!=hunk)
|
if (disc->old_hunk!=hunk)
|
||||||
{
|
{
|
||||||
chd_read(disc->chd,hunk,disc->hunk_mem); //CHDERR_NONE
|
if (chd_read(disc->chd,hunk,disc->hunk_mem) != CHDERR_NONE)
|
||||||
|
return false;
|
||||||
disc->old_hunk = hunk;
|
disc->old_hunk = hunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,24 +75,21 @@ struct CHDTrack : TrackFile
|
||||||
//While space is reserved for it, the images contain no actual subcodes
|
//While space is reserved for it, the images contain no actual subcodes
|
||||||
//memcpy(subcode,disc->hunk_mem+hunk_ofs*(2352+96)+2352,96);
|
//memcpy(subcode,disc->hunk_mem+hunk_ofs*(2352+96)+2352,96);
|
||||||
*subcode_type=SUBFMT_NONE;
|
*subcode_type=SUBFMT_NONE;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CHDDisc::TryOpen(const char* file)
|
void CHDDisc::tryOpen(const char* file)
|
||||||
{
|
{
|
||||||
fp = nowide::fopen(file, "rb");
|
fp = nowide::fopen(file, "rb");
|
||||||
if (fp == nullptr)
|
if (fp == nullptr)
|
||||||
{
|
throw FlycastException(std::string("Cannot open CHD file ") + file);
|
||||||
INFO_LOG(GDROM, "chd: fopen failed for file %s: %d", file, errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
chd_error err = chd_open_file(fp, CHD_OPEN_READ, 0, &chd);
|
chd_error err = chd_open_file(fp, CHD_OPEN_READ, 0, &chd);
|
||||||
|
|
||||||
if (err != CHDERR_NONE)
|
if (err != CHDERR_NONE)
|
||||||
{
|
throw FlycastException(std::string("Invalid CHD file ") + file);
|
||||||
INFO_LOG(GDROM, "chd: chd_open_file failed for file %s: %d", file, err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO_LOG(GDROM, "chd: parsing file %s", file);
|
INFO_LOG(GDROM, "chd: parsing file %s", file);
|
||||||
|
|
||||||
|
@ -102,11 +101,8 @@ bool CHDDisc::TryOpen(const char* file)
|
||||||
|
|
||||||
sph = hunkbytes/(2352+96);
|
sph = hunkbytes/(2352+96);
|
||||||
|
|
||||||
if (hunkbytes%(2352+96)!=0)
|
if (hunkbytes % (2352 + 96) != 0)
|
||||||
{
|
throw FlycastException(std::string("Invalid hunkbytes for CHD file ") + file);
|
||||||
INFO_LOG(GDROM, "chd: hunkbytes is invalid, %d\n",hunkbytes);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 tag;
|
u32 tag;
|
||||||
u8 flags;
|
u8 flags;
|
||||||
|
@ -156,15 +152,13 @@ bool CHDDisc::TryOpen(const char* file)
|
||||||
|| pregap != 0
|
|| pregap != 0
|
||||||
|| postgap != 0)
|
|| postgap != 0)
|
||||||
{
|
{
|
||||||
INFO_LOG(GDROM, "chd: track type %s is not supported", type);
|
throw FlycastException((std::string("chd: track type ") + type) + " is not supported");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
DEBUG_LOG(GDROM, "%s", temp);
|
DEBUG_LOG(GDROM, "%s", temp);
|
||||||
Track t;
|
Track t;
|
||||||
t.StartFAD = total_frames;
|
t.StartFAD = total_frames;
|
||||||
total_frames += frames;
|
total_frames += frames;
|
||||||
t.EndFAD = total_frames - 1;
|
t.EndFAD = total_frames - 1;
|
||||||
t.ADDR = 0;
|
|
||||||
t.CTRL = strcmp(type,"AUDIO") == 0 ? 0 : 4;
|
t.CTRL = strcmp(type,"AUDIO") == 0 ? 0 : 4;
|
||||||
|
|
||||||
t.file = new CHDTrack(this, t.StartFAD, Offset - t.StartFAD, strcmp(type, "MODE1") ? 2352 : 2048,
|
t.file = new CHDTrack(this, t.StartFAD, Offset - t.StartFAD, strcmp(type, "MODE1") ? 2352 : 2048,
|
||||||
|
@ -182,25 +176,21 @@ bool CHDDisc::TryOpen(const char* file)
|
||||||
WARN_LOG(GDROM, "WARNING: chd: Total frames is wrong: %u frames (549300 expected) in %zu tracks", total_frames, tracks.size());
|
WARN_LOG(GDROM, "WARNING: chd: Total frames is wrong: %u frames (549300 expected) in %zu tracks", total_frames, tracks.size());
|
||||||
|
|
||||||
FillGDSession();
|
FillGDSession();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Disc* chd_parse(const char* file)
|
Disc* chd_parse(const char* file)
|
||||||
{
|
{
|
||||||
// Only try to open .chd files
|
if (get_file_extension(file) != "chd")
|
||||||
size_t len = strlen(file);
|
|
||||||
if (len > 4 && stricmp( &file[len - 4], ".chd"))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
CHDDisc* rv = new CHDDisc();
|
CHDDisc* rv = new CHDDisc();
|
||||||
|
|
||||||
if (rv->TryOpen(file))
|
try {
|
||||||
|
rv->tryOpen(file);
|
||||||
return rv;
|
return rv;
|
||||||
else
|
} catch (...) {
|
||||||
{
|
|
||||||
delete rv;
|
delete rv;
|
||||||
return 0;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,37 +87,29 @@ struct Session
|
||||||
|
|
||||||
struct TrackFile
|
struct TrackFile
|
||||||
{
|
{
|
||||||
virtual void Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type)=0;
|
virtual bool Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type) = 0;
|
||||||
virtual ~TrackFile() = default;;
|
virtual ~TrackFile() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Track
|
struct Track
|
||||||
{
|
{
|
||||||
TrackFile* file; //handler for actual IO
|
TrackFile* file = nullptr; // handler for actual IO
|
||||||
u32 StartFAD; //Start FAD
|
u32 StartFAD = 0; // Start FAD
|
||||||
u32 EndFAD; //End FAD
|
u32 EndFAD = 0; // End FAD
|
||||||
u8 CTRL;
|
u8 CTRL = 0;
|
||||||
u8 ADDR;
|
u8 ADDR = 0;
|
||||||
|
|
||||||
Track()
|
bool Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type)
|
||||||
{
|
{
|
||||||
file = 0;
|
if (FAD >= StartFAD && (FAD <= EndFAD || EndFAD == 0) && file != nullptr)
|
||||||
StartFAD = 0;
|
return file->Read(FAD, dst, sector_type, subcode, subcode_type);
|
||||||
EndFAD = 0;
|
|
||||||
CTRL = 0;
|
|
||||||
ADDR = 0;
|
|
||||||
}
|
|
||||||
bool Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type)
|
|
||||||
{
|
|
||||||
if (FAD>=StartFAD && (FAD<=EndFAD || EndFAD==0) && file)
|
|
||||||
{
|
|
||||||
file->Read(FAD,dst,sector_type,subcode,subcode_type);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void Destroy() { delete file; file=0; }
|
void Destroy() {
|
||||||
|
delete file;
|
||||||
|
file = nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Disc
|
struct Disc
|
||||||
|
@ -196,7 +188,7 @@ struct Disc
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
INFO_LOG(GDROM, "Sector Read miss FAD: %d", FAD);
|
WARN_LOG(GDROM, "Sector Read miss FAD: %d", FAD);
|
||||||
}
|
}
|
||||||
dst+=fmt;
|
dst+=fmt;
|
||||||
FAD++;
|
FAD++;
|
||||||
|
@ -261,18 +253,16 @@ struct RawTrackFile : TrackFile
|
||||||
FILE *file;
|
FILE *file;
|
||||||
s32 offset;
|
s32 offset;
|
||||||
u32 fmt;
|
u32 fmt;
|
||||||
bool cleanup;
|
|
||||||
|
|
||||||
RawTrackFile(FILE *file, u32 file_offs, u32 first_fad, u32 secfmt)
|
RawTrackFile(FILE *file, u32 file_offs, u32 first_fad, u32 secfmt)
|
||||||
{
|
{
|
||||||
verify(file!=0);
|
verify(file != nullptr);
|
||||||
this->file=file;
|
this->file = file;
|
||||||
this->offset=file_offs-first_fad*secfmt;
|
this->offset = file_offs - first_fad * secfmt;
|
||||||
this->fmt=secfmt;
|
this->fmt = secfmt;
|
||||||
this->cleanup=true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type) override
|
bool Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type) override
|
||||||
{
|
{
|
||||||
//for now hackish
|
//for now hackish
|
||||||
if (fmt==2352)
|
if (fmt==2352)
|
||||||
|
@ -290,11 +280,15 @@ struct RawTrackFile : TrackFile
|
||||||
|
|
||||||
std::fseek(file, offset + FAD * fmt, SEEK_SET);
|
std::fseek(file, offset + FAD * fmt, SEEK_SET);
|
||||||
if (std::fread(dst, 1, fmt, file) != fmt)
|
if (std::fread(dst, 1, fmt, file) != fmt)
|
||||||
|
{
|
||||||
WARN_LOG(GDROM, "Failed or truncated GD-Rom read");
|
WARN_LOG(GDROM, "Failed or truncated GD-Rom read");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
~RawTrackFile() override
|
~RawTrackFile() override
|
||||||
{
|
{
|
||||||
if (cleanup && file)
|
|
||||||
std::fclose(file);
|
std::fclose(file);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "stdclass.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
extern std::string OS_dirname(std::string file);
|
extern std::string OS_dirname(std::string file);
|
||||||
|
@ -46,15 +47,13 @@ static u32 getSectorSize(const std::string& type) {
|
||||||
|
|
||||||
Disc* cue_parse(const char* file)
|
Disc* cue_parse(const char* file)
|
||||||
{
|
{
|
||||||
// Only try to open .cue files
|
if (get_file_extension(file) != "cue")
|
||||||
size_t len = strlen(file);
|
|
||||||
if (len > 4 && stricmp( &file[len - 4], ".cue"))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
FILE *fsource = nowide::fopen(file, "rb");
|
FILE *fsource = nowide::fopen(file, "rb");
|
||||||
|
|
||||||
if (fsource == nullptr)
|
if (fsource == nullptr)
|
||||||
return nullptr;
|
throw FlycastException(std::string("Cannot open CUE file ") + file);
|
||||||
|
|
||||||
size_t cue_len = flycast::fsize(fsource);
|
size_t cue_len = flycast::fsize(fsource);
|
||||||
|
|
||||||
|
@ -62,9 +61,8 @@ Disc* cue_parse(const char* file)
|
||||||
|
|
||||||
if (cue_len >= sizeof(cue_data))
|
if (cue_len >= sizeof(cue_data))
|
||||||
{
|
{
|
||||||
WARN_LOG(GDROM, "CUE parse error: CUE file too big");
|
|
||||||
std::fclose(fsource);
|
std::fclose(fsource);
|
||||||
return nullptr;
|
throw FlycastException("CUE parse error: CUE file too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::fread(cue_data, 1, cue_len, fsource) != cue_len)
|
if (std::fread(cue_data, 1, cue_len, fsource) != cue_len)
|
||||||
|
@ -163,24 +161,21 @@ Disc* cue_parse(const char* file)
|
||||||
if (index_num == 1)
|
if (index_num == 1)
|
||||||
{
|
{
|
||||||
Track t;
|
Track t;
|
||||||
t.ADDR = 0;
|
|
||||||
t.StartFAD = current_fad;
|
t.StartFAD = current_fad;
|
||||||
t.CTRL = (track_type == "AUDIO" || track_type == "CDG") ? 0 : 4;
|
t.CTRL = (track_type == "AUDIO" || track_type == "CDG") ? 0 : 4;
|
||||||
std::string path = basepath + normalize_path_separator(track_filename);
|
std::string path = basepath + normalize_path_separator(track_filename);
|
||||||
FILE *track_file = nowide::fopen(path.c_str(), "rb");
|
FILE *track_file = nowide::fopen(path.c_str(), "rb");
|
||||||
if (track_file == nullptr)
|
if (track_file == nullptr)
|
||||||
{
|
{
|
||||||
WARN_LOG(GDROM, "CUE file: cannot open track %d: %s", track_number, path.c_str());
|
|
||||||
delete disc;
|
delete disc;
|
||||||
return nullptr;
|
throw FlycastException("CUE file: cannot open track " + path);
|
||||||
}
|
}
|
||||||
u32 sector_size = getSectorSize(track_type);
|
u32 sector_size = getSectorSize(track_type);
|
||||||
if (sector_size == 0)
|
if (sector_size == 0)
|
||||||
{
|
{
|
||||||
WARN_LOG(GDROM, "CUE file: track %d has unknown sector type: %s", track_number, track_type.c_str());
|
|
||||||
std::fclose(track_file);
|
std::fclose(track_file);
|
||||||
delete disc;
|
delete disc;
|
||||||
return nullptr;
|
throw FlycastException("CUE file: track has unknown sector type: " + track_type);
|
||||||
}
|
}
|
||||||
if (flycast::fsize(track_file) % sector_size != 0)
|
if (flycast::fsize(track_file) % sector_size != 0)
|
||||||
WARN_LOG(GDROM, "Warning: Size of track %s is not multiple of sector size %d", track_filename.c_str(), sector_size);
|
WARN_LOG(GDROM, "Warning: Size of track %s is not multiple of sector size %d", track_filename.c_str(), sector_size);
|
||||||
|
@ -199,9 +194,8 @@ Disc* cue_parse(const char* file)
|
||||||
}
|
}
|
||||||
if (disc->tracks.empty())
|
if (disc->tracks.empty())
|
||||||
{
|
{
|
||||||
WARN_LOG(GDROM, "CUE parse error: failed to parse or invalid file with 0 tracks");
|
|
||||||
delete disc;
|
delete disc;
|
||||||
return nullptr;
|
throw FlycastException("CUE parse error: failed to parse or invalid file with 0 tracks");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_number == 0)
|
if (session_number == 0)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "stdclass.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -62,8 +63,8 @@ namespace {
|
||||||
Disc* load_gdi(const char* file)
|
Disc* load_gdi(const char* file)
|
||||||
{
|
{
|
||||||
FILE *t = nowide::fopen(file, "rb");
|
FILE *t = nowide::fopen(file, "rb");
|
||||||
if (!t)
|
if (t == nullptr)
|
||||||
return nullptr;
|
throw FlycastException(std::string("Cannot open GDI file ") + file);
|
||||||
|
|
||||||
size_t gdi_len = flycast::fsize(t);
|
size_t gdi_len = flycast::fsize(t);
|
||||||
|
|
||||||
|
@ -71,9 +72,8 @@ Disc* load_gdi(const char* file)
|
||||||
|
|
||||||
if (gdi_len >= sizeof(gdi_data))
|
if (gdi_len >= sizeof(gdi_data))
|
||||||
{
|
{
|
||||||
WARN_LOG(GDROM, "GDI: file too big");
|
|
||||||
std::fclose(t);
|
std::fclose(t);
|
||||||
return nullptr;
|
throw FlycastException("GDI file too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::fread(gdi_data, 1, gdi_len, t) != gdi_len)
|
if (std::fread(gdi_data, 1, gdi_len, t) != gdi_len)
|
||||||
|
@ -85,10 +85,8 @@ Disc* load_gdi(const char* file)
|
||||||
u32 iso_tc = 0;
|
u32 iso_tc = 0;
|
||||||
gdi >> iso_tc;
|
gdi >> iso_tc;
|
||||||
if (iso_tc == 0)
|
if (iso_tc == 0)
|
||||||
{
|
throw FlycastException("GDI: empty or invalid GDI file");
|
||||||
WARN_LOG(GDROM, "GDI: empty or invalid GDI file");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
INFO_LOG(GDROM, "GDI : %d tracks", iso_tc);
|
INFO_LOG(GDROM, "GDI : %d tracks", iso_tc);
|
||||||
|
|
||||||
std::string basepath = OS_dirname(file);
|
std::string basepath = OS_dirname(file);
|
||||||
|
@ -134,16 +132,19 @@ Disc* load_gdi(const char* file)
|
||||||
DEBUG_LOG(GDROM, "file[%d] \"%s\": FAD:%d, CTRL:%d, SSIZE:%d, OFFSET:%d", TRACK, track_filename.c_str(), FADS, CTRL, SSIZE, OFFSET);
|
DEBUG_LOG(GDROM, "file[%d] \"%s\": FAD:%d, CTRL:%d, SSIZE:%d, OFFSET:%d", TRACK, track_filename.c_str(), FADS, CTRL, SSIZE, OFFSET);
|
||||||
|
|
||||||
Track t;
|
Track t;
|
||||||
t.ADDR=0;
|
t.StartFAD = FADS + 150;
|
||||||
t.StartFAD=FADS+150;
|
|
||||||
t.EndFAD=0; //fill it in
|
|
||||||
t.file=0;
|
|
||||||
t.CTRL = CTRL;
|
t.CTRL = CTRL;
|
||||||
|
|
||||||
if (SSIZE!=0)
|
if (SSIZE!=0)
|
||||||
{
|
{
|
||||||
std::string path = basepath + normalize_path_separator(track_filename);
|
std::string path = basepath + normalize_path_separator(track_filename);
|
||||||
t.file = new RawTrackFile(nowide::fopen(path.c_str(), "rb"), OFFSET, t.StartFAD,SSIZE);
|
FILE *file = nowide::fopen(path.c_str(), "rb");
|
||||||
|
if (file == nullptr)
|
||||||
|
{
|
||||||
|
delete disc;
|
||||||
|
throw FlycastException("GDI file: Cannot open track " + path);
|
||||||
|
}
|
||||||
|
t.file = new RawTrackFile(file, OFFSET, t.StartFAD, SSIZE);
|
||||||
}
|
}
|
||||||
if (!disc->tracks.empty())
|
if (!disc->tracks.empty())
|
||||||
disc->tracks.back().EndFAD = t.StartFAD - 1;
|
disc->tracks.back().EndFAD = t.StartFAD - 1;
|
||||||
|
@ -158,13 +159,8 @@ Disc* load_gdi(const char* file)
|
||||||
|
|
||||||
Disc* gdi_parse(const char* file)
|
Disc* gdi_parse(const char* file)
|
||||||
{
|
{
|
||||||
size_t len=strlen(file);
|
if (get_file_extension(file) != "gdi")
|
||||||
if (len>4)
|
return nullptr;
|
||||||
{
|
|
||||||
if (stricmp( &file[len-4],".gdi")==0)
|
|
||||||
{
|
|
||||||
return load_gdi(file);
|
return load_gdi(file);
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -394,6 +394,7 @@ static void reios_sys_misc()
|
||||||
|
|
||||||
case 1: // Exit to BIOS menu
|
case 1: // Exit to BIOS menu
|
||||||
WARN_LOG(REIOS, "SYS_MISC 1");
|
WARN_LOG(REIOS, "SYS_MISC 1");
|
||||||
|
throw FlycastException("Reboot to BIOS");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // check disk
|
case 2: // check disk
|
||||||
|
@ -645,9 +646,8 @@ static void reios_boot()
|
||||||
std::string extension = get_file_extension(settings.imgread.ImagePath);
|
std::string extension = get_file_extension(settings.imgread.ImagePath);
|
||||||
if (extension == "elf")
|
if (extension == "elf")
|
||||||
{
|
{
|
||||||
if (!reios_loadElf(settings.imgread.ImagePath)) {
|
if (!reios_loadElf(settings.imgread.ImagePath))
|
||||||
msgboxf("Failed to open %s", MBX_ICONERROR, settings.imgread.ImagePath);
|
throw FlycastException(std::string("Failed to open ELF ") + settings.imgread.ImagePath);
|
||||||
}
|
|
||||||
reios_setup_state(0x8C010000);
|
reios_setup_state(0x8C010000);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -656,7 +656,7 @@ static void reios_boot()
|
||||||
char bootfile[sizeof(ip_meta.boot_filename) + 1] = {0};
|
char bootfile[sizeof(ip_meta.boot_filename) + 1] = {0};
|
||||||
memcpy(bootfile, ip_meta.boot_filename, sizeof(ip_meta.boot_filename));
|
memcpy(bootfile, ip_meta.boot_filename, sizeof(ip_meta.boot_filename));
|
||||||
if (bootfile[0] == '\0' || !reios_locate_bootfile(bootfile))
|
if (bootfile[0] == '\0' || !reios_locate_bootfile(bootfile))
|
||||||
msgboxf("Failed to locate bootfile %s", MBX_ICONERROR, bootfile);
|
throw FlycastException(std::string("Failed to locate bootfile ") + bootfile);
|
||||||
reios_setup_state(0xac008300);
|
reios_setup_state(0xac008300);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -668,14 +668,14 @@ static void reios_boot()
|
||||||
}
|
}
|
||||||
u32 data_size = 4;
|
u32 data_size = 4;
|
||||||
u32* sz = (u32*)CurrentCartridge->GetPtr(0x368, data_size);
|
u32* sz = (u32*)CurrentCartridge->GetPtr(0x368, data_size);
|
||||||
if (!sz || data_size != 4) {
|
if (sz == nullptr || data_size != 4)
|
||||||
msgboxf("Naomi boot failure", MBX_ICONERROR);
|
throw FlycastException("Naomi boot failure");
|
||||||
}
|
|
||||||
|
|
||||||
const u32 size = *sz;
|
const u32 size = *sz;
|
||||||
|
|
||||||
data_size = 1;
|
data_size = 1;
|
||||||
verify(size < RAM_SIZE && CurrentCartridge->GetPtr(size - 1, data_size) && "Invalid cart size");
|
if (size > RAM_SIZE || CurrentCartridge->GetPtr(size - 1, data_size) == nullptr)
|
||||||
|
throw FlycastException("Invalid cart size");
|
||||||
|
|
||||||
data_size = size;
|
data_size = size;
|
||||||
WriteMemBlock_nommu_ptr(0x0c020000, (u32*)CurrentCartridge->GetPtr(0, data_size), size);
|
WriteMemBlock_nommu_ptr(0x0c020000, (u32*)CurrentCartridge->GetPtr(0, data_size), size);
|
||||||
|
|
|
@ -432,6 +432,26 @@ static void gui_start_game(const std::string& path)
|
||||||
dc_load_game(path.empty() ? NULL : path_copy.c_str());
|
dc_load_game(path.empty() ? NULL : path_copy.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void gui_stop_game(const std::string& message)
|
||||||
|
{
|
||||||
|
if (!commandLineStart)
|
||||||
|
{
|
||||||
|
// Exit to main menu
|
||||||
|
dc_term_game();
|
||||||
|
gui_state = GuiState::Main;
|
||||||
|
game_started = false;
|
||||||
|
settings.imgread.ImagePath[0] = '\0';
|
||||||
|
reset_vmus();
|
||||||
|
if (!message.empty())
|
||||||
|
error_msg = "Flycast has stopped.\n\n" + message;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Exit emulator
|
||||||
|
dc_exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void gui_display_commands()
|
static void gui_display_commands()
|
||||||
{
|
{
|
||||||
if (dc_is_running())
|
if (dc_is_running())
|
||||||
|
@ -515,20 +535,7 @@ static void gui_display_commands()
|
||||||
if (ImGui::Button("Exit", ImVec2(300 * scaling + ImGui::GetStyle().ColumnsMinSpacing + ImGui::GetStyle().FramePadding.x * 2 - 1,
|
if (ImGui::Button("Exit", ImVec2(300 * scaling + ImGui::GetStyle().ColumnsMinSpacing + ImGui::GetStyle().FramePadding.x * 2 - 1,
|
||||||
50 * scaling)))
|
50 * scaling)))
|
||||||
{
|
{
|
||||||
if (!commandLineStart)
|
gui_stop_game();
|
||||||
{
|
|
||||||
// Exit to main menu
|
|
||||||
dc_term_game();
|
|
||||||
gui_state = GuiState::Main;
|
|
||||||
game_started = false;
|
|
||||||
settings.imgread.ImagePath[0] = '\0';
|
|
||||||
reset_vmus();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Exit emulator
|
|
||||||
dc_exit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
@ -909,6 +916,9 @@ static void error_popup()
|
||||||
{
|
{
|
||||||
if (!error_msg.empty())
|
if (!error_msg.empty())
|
||||||
{
|
{
|
||||||
|
ImVec2 padding = ImVec2(20 * scaling, 20 * scaling);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, padding);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, padding);
|
||||||
ImGui::OpenPopup("Error");
|
ImGui::OpenPopup("Error");
|
||||||
if (ImGui::BeginPopupModal("Error", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
|
if (ImGui::BeginPopupModal("Error", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
|
||||||
{
|
{
|
||||||
|
@ -924,8 +934,11 @@ static void error_popup()
|
||||||
}
|
}
|
||||||
ImGui::SetItemDefaultFocus();
|
ImGui::SetItemDefaultFocus();
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1918,8 +1931,12 @@ static void gui_display_content()
|
||||||
if (gui_state == GuiState::SelectDisk)
|
if (gui_state == GuiState::SelectDisk)
|
||||||
{
|
{
|
||||||
strcpy(settings.imgread.ImagePath, game.path.c_str());
|
strcpy(settings.imgread.ImagePath, game.path.c_str());
|
||||||
|
try {
|
||||||
DiscSwap();
|
DiscSwap();
|
||||||
gui_state = GuiState::Closed;
|
gui_state = GuiState::Closed;
|
||||||
|
} catch (const FlycastException& e) {
|
||||||
|
error_msg = e.what();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@ void gui_set_mouse_position(int x, int y);
|
||||||
void gui_set_mouse_button(int button, bool pressed);
|
void gui_set_mouse_button(int button, bool pressed);
|
||||||
void gui_set_mouse_wheel(float delta);
|
void gui_set_mouse_wheel(float delta);
|
||||||
void gui_set_insets(int left, int right, int top, int bottom);
|
void gui_set_insets(int left, int right, int top, int bottom);
|
||||||
|
void gui_stop_game(const std::string& message = "");
|
||||||
|
|
||||||
extern int screen_dpi;
|
extern int screen_dpi;
|
||||||
extern float scaling;
|
extern float scaling;
|
||||||
|
|
|
@ -49,6 +49,11 @@ bool mainui_rend_frame()
|
||||||
if (!rend_single_frame(mainui_enabled))
|
if (!rend_single_frame(mainui_enabled))
|
||||||
{
|
{
|
||||||
UpdateInputState();
|
UpdateInputState();
|
||||||
|
if (!dc_is_running())
|
||||||
|
{
|
||||||
|
dc_stop();
|
||||||
|
gui_stop_game(dc_get_last_error());
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -845,12 +845,26 @@ void retro_run()
|
||||||
// Render
|
// Render
|
||||||
is_dupe = true;
|
is_dupe = true;
|
||||||
for (int i = 0; i < 5 && is_dupe; i++)
|
for (int i = 0; i < 5 && is_dupe; i++)
|
||||||
|
{
|
||||||
is_dupe = !rend_single_frame(true);
|
is_dupe = !rend_single_frame(true);
|
||||||
|
if (!dc_is_running()) {
|
||||||
|
std::string error = dc_get_last_error();
|
||||||
|
if (!error.empty())
|
||||||
|
gui_display_notification(error.c_str(), 5000);
|
||||||
|
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
startTime = sh4_sched_now64();
|
startTime = sh4_sched_now64();
|
||||||
dc_run(nullptr);
|
try {
|
||||||
|
dc_run();
|
||||||
|
} catch (const FlycastException& e) {
|
||||||
|
ERROR_LOG(COMMON, "%s", e.what());
|
||||||
|
gui_display_notification(e.what(), 5000);
|
||||||
|
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config::RendererType.isOpenGL())
|
if (config::RendererType.isOpenGL())
|
||||||
|
@ -870,7 +884,7 @@ static bool loadGame(const char *path)
|
||||||
} catch (const FlycastException& e) {
|
} catch (const FlycastException& e) {
|
||||||
ERROR_LOG(BOOT, "%s", e.what());
|
ERROR_LOG(BOOT, "%s", e.what());
|
||||||
mute_messages = false;
|
mute_messages = false;
|
||||||
gui_display_notification(e.what(), 2000);
|
gui_display_notification(e.what(), 5000);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mute_messages = false;
|
mute_messages = false;
|
||||||
|
@ -2751,7 +2765,12 @@ static bool retro_set_eject_state(bool ejected)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
return DiscSwap();
|
return DiscSwap();
|
||||||
|
} catch (const FlycastException& e) {
|
||||||
|
ERROR_LOG(GDROM, "%s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2779,8 +2798,13 @@ static bool retro_set_image_index(unsigned index)
|
||||||
|
|
||||||
if (disc_tray_open)
|
if (disc_tray_open)
|
||||||
return true;
|
return true;
|
||||||
else
|
|
||||||
|
try {
|
||||||
return DiscSwap();
|
return DiscSwap();
|
||||||
|
} catch (const FlycastException& e) {
|
||||||
|
ERROR_LOG(GDROM, "%s", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned retro_get_num_images()
|
static unsigned retro_get_num_images()
|
||||||
|
|
Loading…
Reference in New Issue