Better background game loader

Pass LoadProgress object to report load progress and cancel
Better UI
Cancel with exception
Fix issues when stopping/exiting in !threaded
dx9: call gui_term on shutdown
This commit is contained in:
Flyinghead 2021-10-02 11:30:40 +02:00
parent c1bcf91dd6
commit 5bcfa35737
25 changed files with 192 additions and 182 deletions

View File

@ -37,9 +37,9 @@
#include "network/ggpo.h" #include "network/ggpo.h"
#include "hw/mem/mem_watch.h" #include "hw/mem/mem_watch.h"
#include "network/net_handshake.h" #include "network/net_handshake.h"
#include "rend/gui.h"
#include <chrono> #include <chrono>
std::atomic<bool> loading_canceled;
settings_t settings; settings_t settings;
static void loadSpecialSettings() static void loadSpecialSettings()
@ -396,7 +396,7 @@ static int getGamePlatform(const char *path)
return DC_PLATFORM_DREAMCAST; return DC_PLATFORM_DREAMCAST;
} }
void Emulator::loadGame(const char *path) void Emulator::loadGame(const char *path, LoadProgress *progress)
{ {
init(); init();
try { try {
@ -458,9 +458,7 @@ void Emulator::loadGame(const char *path)
else if (settings.platform.system == DC_PLATFORM_NAOMI || settings.platform.system == DC_PLATFORM_ATOMISWAVE) else if (settings.platform.system == DC_PLATFORM_NAOMI || settings.platform.system == DC_PLATFORM_ATOMISWAVE)
{ {
LoadRomFiles(); LoadRomFiles();
naomi_cart_LoadRom(path); naomi_cart_LoadRom(path, progress);
if (loading_canceled)
return;
loadGameSpecificSettings(); loadGameSpecificSettings();
// Reload the BIOS in case a game-specific region is set // Reload the BIOS in case a game-specific region is set
naomi_cart_LoadBios(path); naomi_cart_LoadBios(path);
@ -641,6 +639,7 @@ bool dc_loadstate(const void **data, u32 size)
void Emulator::setNetworkState(bool online) void Emulator::setNetworkState(bool online)
{ {
if (settings.online != online)
DEBUG_LOG(NETWORK, "Network state %d", online); DEBUG_LOG(NETWORK, "Network state %d", online);
settings.online = online; settings.online = online;
settings.input.fastForwardMode &= !online; settings.input.fastForwardMode &= !online;

View File

@ -25,25 +25,19 @@
#include <vector> #include <vector>
#include <future> #include <future>
#include <string> #include <string>
#include <memory>
void loadGameSpecificSettings(); void loadGameSpecificSettings();
void SaveSettings(); void SaveSettings();
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_term(); void flycast_term();
void dc_exit(); void dc_exit();
void dc_savestate(int index = 0); void dc_savestate(int index = 0);
void dc_loadstate(int index = 0); void dc_loadstate(int index = 0);
bool dc_loadstate(const void **data, unsigned size); bool dc_loadstate(const void **data, unsigned size);
void dc_load_game(const std::string& path);
bool dc_is_load_done();
void dc_cancel_load();
void dc_get_load_status();
enum class Event { enum class Event {
Start, Start,
Pause, Pause,
@ -80,6 +74,19 @@ private:
std::map<Event, std::vector<Callback>> callbacks; std::map<Event, std::vector<Callback>> callbacks;
}; };
struct LoadProgress
{
void reset()
{
cancelled = false;
label = nullptr;
progress = 0.f;
}
std::atomic<bool> cancelled;
std::atomic<const char *> label;
std::atomic<float> progress;
};
class Emulator class Emulator
{ {
public: public:
@ -96,7 +103,7 @@ public:
* May throw if the game cannot be loaded. * May throw if the game cannot be loaded.
* If a game is already loaded, or the emulator is in the error state, unloadGame() must be called first. * If a game is already loaded, or the emulator is in the error state, unloadGame() must be called first.
*/ */
void loadGame(const char *path); void loadGame(const char *path, LoadProgress *progress = nullptr);
/** /**
* Reset the emulator in order to load another game. After calling this method, only loadGame() and term() can be called. * Reset the emulator in order to load another game. After calling this method, only loadGame() and term() can be called.
* Does nothing if no game is loaded. * Does nothing if no game is loaded.

View File

@ -333,7 +333,7 @@ u16 AWCartridge::decrypt(u16 cipherText, u32 address, const u8 key)
} }
void AWCartridge::Init() void AWCartridge::Init(LoadProgress *progress)
{ {
mpr_offset = decrypt16(0x58/2) | (decrypt16(0x5a/2) << 16); mpr_offset = decrypt16(0x58/2) | (decrypt16(0x5a/2) << 16);
INFO_LOG(NAOMI, "AWCartridge::SetKey rombd_key %02x mpr_offset %08x", rombd_key, mpr_offset); INFO_LOG(NAOMI, "AWCartridge::SetKey rombd_key %02x mpr_offset %08x", rombd_key, mpr_offset);

View File

@ -19,7 +19,7 @@ class AWCartridge: public Cartridge
public: public:
AWCartridge(u32 size) : Cartridge(size) {} AWCartridge(u32 size) : Cartridge(size) {}
void Init() override; void Init(LoadProgress *progress = nullptr) override;
u32 ReadMem(u32 address, u32 size) override; u32 ReadMem(u32 address, u32 size) override;
void WriteMem(u32 address, u32 data, u32 size) override; void WriteMem(u32 address, u32 data, u32 size) override;

View File

@ -13,7 +13,6 @@
#include "gdcartridge.h" #include "gdcartridge.h"
#include "stdclass.h" #include "stdclass.h"
#include "emulator.h" #include "emulator.h"
#include "rend/gui.h"
/* /*
@ -436,12 +435,12 @@ void GDCartridge::find_file(const char *name, const u8 *dir_sector, u32 &file_st
} }
} }
void GDCartridge::read_gdrom(Disc *gdrom, u32 sector, u8* dst, u32 count) void GDCartridge::read_gdrom(Disc *gdrom, u32 sector, u8* dst, u32 count, LoadProgress *progress)
{ {
gdrom->ReadSectors(sector + 150, count, dst, 2048); gdrom->ReadSectors(sector + 150, count, dst, 2048, progress);
} }
void GDCartridge::device_start() void GDCartridge::device_start(LoadProgress *progress)
{ {
if (dimm_data != NULL) if (dimm_data != NULL)
{ {
@ -494,15 +493,15 @@ void GDCartridge::device_start()
u8 buffer[2048]; u8 buffer[2048];
std::string gdrom_path = get_game_basename() + "/" + gdrom_name; std::string gdrom_path = get_game_basename() + "/" + gdrom_name;
Disc *gdrom = OpenDisc(gdrom_path + ".chd"); std::unique_ptr<Disc> gdrom = std::unique_ptr<Disc>(OpenDisc(gdrom_path + ".chd"));
if (gdrom == nullptr) if (gdrom == nullptr)
gdrom = OpenDisc(gdrom_path + ".gdi"); gdrom = std::unique_ptr<Disc>(OpenDisc(gdrom_path + ".gdi"));
if (gdrom_parent_name != nullptr && gdrom == nullptr) if (gdrom_parent_name != nullptr && gdrom == nullptr)
{ {
std::string gdrom_parent_path = get_game_dir() + "/" + gdrom_parent_name + "/" + gdrom_name; std::string gdrom_parent_path = get_game_dir() + "/" + gdrom_parent_name + "/" + gdrom_name;
gdrom = OpenDisc(gdrom_parent_path + ".chd"); gdrom = std::unique_ptr<Disc>(OpenDisc(gdrom_parent_path + ".chd"));
if (gdrom == nullptr) if (gdrom == nullptr)
gdrom = OpenDisc(gdrom_parent_path + ".gdi"); gdrom = std::unique_ptr<Disc>(OpenDisc(gdrom_parent_path + ".gdi"));
} }
if (gdrom == nullptr) if (gdrom == nullptr)
throw NaomiCartException("Naomi GDROM: Cannot open " + gdrom_path + ".chd or " + gdrom_path + ".gdi"); throw NaomiCartException("Naomi GDROM: Cannot open " + gdrom_path + ".chd or " + gdrom_path + ".gdi");
@ -510,13 +509,13 @@ void GDCartridge::device_start()
// primary volume descriptor // primary volume descriptor
// read frame 0xb06e (frame=sector+150) // read frame 0xb06e (frame=sector+150)
// dimm board firmware starts straight from this frame // dimm board firmware starts straight from this frame
read_gdrom(gdrom, (netpic ? 0 : 45000) + 16, buffer); read_gdrom(gdrom.get(), (netpic ? 0 : 45000) + 16, buffer);
u32 path_table = ((buffer[0x8c+0] << 0) | u32 path_table = ((buffer[0x8c+0] << 0) |
(buffer[0x8c+1] << 8) | (buffer[0x8c+1] << 8) |
(buffer[0x8c+2] << 16) | (buffer[0x8c+2] << 16) |
(buffer[0x8c+3] << 24)); (buffer[0x8c+3] << 24));
// path table // path table
read_gdrom(gdrom, path_table, buffer); read_gdrom(gdrom.get(), path_table, buffer);
// directory // directory
u8 dir_sector[2048]; u8 dir_sector[2048];
@ -529,12 +528,12 @@ void GDCartridge::device_start()
(buffer[0x2 + 2] << 16) | (buffer[0x2 + 2] << 16) |
(buffer[0x2 + 3] << 24)); (buffer[0x2 + 3] << 24));
read_gdrom(gdrom, dir, dir_sector); read_gdrom(gdrom.get(), dir, dir_sector);
find_file(name, dir_sector, file_start, file_size); find_file(name, dir_sector, file_start, file_size);
if (file_start && (file_size == 0x100)) { if (file_start && (file_size == 0x100)) {
// read file // read file
read_gdrom(gdrom, file_start, buffer); read_gdrom(gdrom.get(), file_start, buffer);
// get "rom" file name // get "rom" file name
memset(name, '\0', 128); memset(name, '\0', 128);
memcpy(name, buffer + 0xc0, FILENAME_LENGTH - 1); memcpy(name, buffer + 0xc0, FILENAME_LENGTH - 1);
@ -550,7 +549,7 @@ void GDCartridge::device_start()
(buffer[i + 4] << 16) | (buffer[i + 4] << 16) |
(buffer[i + 5] << 24)); (buffer[i + 5] << 24));
memcpy(name, "ROM.BIN", 7); memcpy(name, "ROM.BIN", 7);
read_gdrom(gdrom, dir, dir_sector); read_gdrom(gdrom.get(), dir, dir_sector);
break; break;
} }
i += buffer[i] + 8 + (buffer[i] & 1); i += buffer[i] + 8 + (buffer[i] & 1);
@ -570,31 +569,25 @@ void GDCartridge::device_start()
// read encrypted data into dimm_data // read encrypted data into dimm_data
u32 sectors = file_rounded_size / 2048; u32 sectors = file_rounded_size / 2048;
read_gdrom(gdrom, file_start, dimm_data, sectors); read_gdrom(gdrom.get(), file_start, dimm_data, sectors, progress);
// decrypt loaded data // decrypt loaded data
u32 des_subkeys[32]; u32 des_subkeys[32];
des_generate_subkeys(rev64(key), des_subkeys); des_generate_subkeys(rev64(key), des_subkeys);
u32 progress = ~0;
for (u32 i = 0; i < file_rounded_size; i += 8) for (u32 i = 0; i < file_rounded_size; i += 8)
{ {
const u32 new_progress = (u32)(((u64)i + 8) * 100 / file_rounded_size); if (progress != nullptr)
if (progress != new_progress)
{ {
if (loading_canceled) if (progress->cancelled)
break; throw LoadCancelledException();
progress = new_progress; progress->label = "Decrypting...";
char status_str[16]; progress->progress = (float)(i + 8) / file_rounded_size;
sprintf(status_str, "Decrypting %d%%", progress);
gui_display_notification(status_str, 2000);
} }
*(u64 *)(dimm_data + i) = des_encrypt_decrypt<true>(*(u64 *)(dimm_data + i), des_subkeys); *(u64 *)(dimm_data + i) = des_encrypt_decrypt<true>(*(u64 *)(dimm_data + i), des_subkeys);
} }
} }
delete gdrom;
if (!dimm_data) if (!dimm_data)
throw NaomiCartException("Naomi GDROM: Could not find the file to decrypt."); throw NaomiCartException("Naomi GDROM: Could not find the file to decrypt.");
} }

View File

@ -25,9 +25,9 @@ public:
{ {
free(dimm_data); free(dimm_data);
} }
void Init() override void Init(LoadProgress *progress = nullptr) override
{ {
device_start(); device_start(progress);
device_reset(); device_reset();
} }
void* GetDmaPtr(u32 &size) override; void* GetDmaPtr(u32 &size) override;
@ -61,7 +61,7 @@ private:
static const u32 DES_MASK_TABLE[]; static const u32 DES_MASK_TABLE[];
static const u8 DES_ROTATE_TABLE[16]; static const u8 DES_ROTATE_TABLE[16];
void device_start(); void device_start(LoadProgress *progress);
void device_reset(); void device_reset();
void find_file(const char *name, const u8 *dir_sector, u32 &file_start, u32 &file_size); void find_file(const char *name, const u8 *dir_sector, u32 &file_start, u32 &file_size);
@ -70,7 +70,7 @@ private:
template<bool decrypt> template<bool decrypt>
u64 des_encrypt_decrypt(u64 src, const u32 *des_subkeys); u64 des_encrypt_decrypt(u64 src, const u32 *des_subkeys);
u64 rev64(u64 src); u64 rev64(u64 src);
void read_gdrom(Disc *gdrom, u32 sector, u8* dst, u32 count = 1); void read_gdrom(Disc *gdrom, u32 sector, u8* dst, u32 count = 1, LoadProgress *progress = nullptr);
}; };
#endif /* CORE_HW_NAOMI_GDCARTRIDGE_H_ */ #endif /* CORE_HW_NAOMI_GDCARTRIDGE_H_ */

View File

@ -20,7 +20,7 @@ public:
M4Cartridge(u32 size) : NaomiCartridge(size) { } M4Cartridge(u32 size) : NaomiCartridge(size) { }
~M4Cartridge() override; ~M4Cartridge() override;
void Init() override void Init(LoadProgress *progress = nullptr) override
{ {
device_start(); device_start();
device_reset(); device_reset();

View File

@ -34,7 +34,6 @@
#include "archive/archive.h" #include "archive/archive.h"
#include "stdclass.h" #include "stdclass.h"
#include "emulator.h" #include "emulator.h"
#include "rend/gui.h"
#include "cfg/option.h" #include "cfg/option.h"
#include "oslib/oslib.h" #include "oslib/oslib.h"
@ -197,7 +196,7 @@ void naomi_cart_LoadBios(const char *filename)
bios_loaded = true; bios_loaded = true;
} }
static void naomi_cart_LoadZip(const char *filename) static void naomi_cart_LoadZip(const char *filename, LoadProgress *progress)
{ {
Game *game = FindGame(filename); Game *game = FindGame(filename);
if (game == NULL) if (game == NULL)
@ -257,12 +256,22 @@ static void naomi_cart_LoadZip(const char *filename)
CurrentCartridge->SetKey(game->key); CurrentCartridge->SetKey(game->key);
NaomiGameInputs = game->inputs; NaomiGameInputs = game->inputs;
for (int romid = 0; game->blobs[romid].filename != NULL && !loading_canceled; romid++) int romCount = 0;
while (game->blobs[romCount].filename != nullptr)
romCount++;
for (int romid = 0; romid < romCount; romid++)
{ {
if (progress != nullptr)
{
if (progress->cancelled)
throw LoadCancelledException();
if (game->cart_type != GD) if (game->cart_type != GD)
{ {
std::string progress = "ROM " + std::to_string(romid + 1); static std::string label;
gui_display_notification(progress.c_str(), 1000); label = "ROM " + std::to_string(romid + 1);
progress->label = label.c_str();
progress->progress = (float)(romid + 1) / romCount;
}
} }
u32 len = game->blobs[romid].length; u32 len = game->blobs[romid].length;
@ -350,31 +359,26 @@ static void naomi_cart_LoadZip(const char *filename)
} }
} }
} }
if (loading_canceled)
return;
if (naomi_default_eeprom == NULL && game->eeprom_dump != NULL) if (naomi_default_eeprom == NULL && game->eeprom_dump != NULL)
naomi_default_eeprom = game->eeprom_dump; naomi_default_eeprom = game->eeprom_dump;
if (game->rotation_flag == ROT270) if (game->rotation_flag == ROT270)
config::Rotate90.override(true); config::Rotate90.override(true);
CurrentCartridge->Init(); CurrentCartridge->Init(progress);
if (loading_canceled)
return;
strcpy(naomi_game_id, CurrentCartridge->GetGameId().c_str()); strcpy(naomi_game_id, CurrentCartridge->GetGameId().c_str());
if (naomi_game_id[0] == '\0') if (naomi_game_id[0] == '\0')
strcpy(naomi_game_id, game->name); strcpy(naomi_game_id, game->name);
NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s]", naomi_game_id); NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s]", naomi_game_id);
} catch (const FlycastException& ex) { } catch (...) {
delete CurrentCartridge; delete CurrentCartridge;
CurrentCartridge = NULL; CurrentCartridge = NULL;
throw;
throw ex;
} }
} }
void naomi_cart_LoadRom(const char* file) void naomi_cart_LoadRom(const char* file, LoadProgress *progress)
{ {
naomi_cart_Close(); naomi_cart_Close();
@ -382,7 +386,7 @@ void naomi_cart_LoadRom(const char* file)
if (extension == "zip" || extension == "7z") if (extension == "zip" || extension == "7z")
{ {
naomi_cart_LoadZip(file); naomi_cart_LoadZip(file, progress);
return; return;
} }
@ -609,6 +613,11 @@ std::string Cartridge::GetGameId() {
} }
while (!game_id.empty() && game_id.back() == ' ') while (!game_id.empty() && game_id.back() == ' ')
game_id.pop_back(); game_id.pop_back();
if (RomSize < 0x138)
printf("GAME EEPROM ID: (ROM too small)\n");
else
printf("GAME EEPROM ID: %c%c%c%c\n", RomPtr[0x134], RomPtr[0x135], RomPtr[0x136], RomPtr[0x137]);
return game_id; return game_id;
} }
@ -918,6 +927,10 @@ std::string M2Cartridge::GetGameId()
game_id = std::string((char *)RomPtr + 0x800030, 0x20); game_id = std::string((char *)RomPtr + 0x800030, 0x20);
while (!game_id.empty() && game_id.back() == ' ') while (!game_id.empty() && game_id.back() == ' ')
game_id.pop_back(); game_id.pop_back();
if (RomSize < 0x800138)
printf("m2 GAME EEPROM ID: (ROM too small)\n");
else
printf("m2 GAME EEPROM ID: %c%c%c%c\n", RomPtr[0x800134], RomPtr[0x800135], RomPtr[0x800136], RomPtr[0x800137]);
} }
return game_id; return game_id;
} }

View File

@ -3,6 +3,7 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include "types.h" #include "types.h"
#include "emulator.h"
class Cartridge class Cartridge
{ {
@ -10,7 +11,7 @@ public:
Cartridge(u32 size); Cartridge(u32 size);
virtual ~Cartridge(); virtual ~Cartridge();
virtual void Init() {} virtual void Init(LoadProgress *progress = nullptr) {}
virtual u32 ReadMem(u32 address, u32 size) = 0; virtual u32 ReadMem(u32 address, u32 size) = 0;
virtual void WriteMem(u32 address, u32 data, u32 size) = 0; virtual void WriteMem(u32 address, u32 data, u32 size) = 0;
@ -83,7 +84,7 @@ public:
NaomiCartException(const std::string& reason) : FlycastException(reason) {} NaomiCartException(const std::string& reason) : FlycastException(reason) {}
}; };
void naomi_cart_LoadRom(const char* file); void naomi_cart_LoadRom(const char* file, LoadProgress *progress);
void naomi_cart_Close(); void naomi_cart_Close();
int naomi_cart_GetPlatform(const char *path); int naomi_cart_GetPlatform(const char *path);
void naomi_cart_LoadBios(const char *filename); void naomi_cart_LoadBios(const char *filename);

View File

@ -275,28 +275,20 @@ DiscType GuessDiscType(bool m1, bool m2, bool da)
return CdRom; return CdRom;
} }
void Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt) void Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, LoadProgress *progress)
{ {
u8 temp[2448]; u8 temp[2448];
SectorFormat secfmt; SectorFormat secfmt;
SubcodeFormat subfmt; SubcodeFormat subfmt;
u32 progress = ~0;
for (u32 i = 1; i <= count; i++) for (u32 i = 1; i <= count; i++)
{ {
if (count >= 1000) if (progress != nullptr)
{ {
if (loading_canceled) if (progress->cancelled)
break; throw LoadCancelledException();
// Progress report when loading naomi gd-rom games progress->label = "Loading...";
const u32 new_progress = i * 100 / count; progress->progress = (float)i / count;
if (progress != new_progress)
{
progress = new_progress;
char status_str[16];
sprintf(status_str, "%d%%", progress);
gui_display_notification(status_str, 2000);
}
} }
if (ReadSector(FAD,temp,&secfmt,q_subchannel,&subfmt)) if (ReadSector(FAD,temp,&secfmt,q_subchannel,&subfmt))
{ {

View File

@ -4,7 +4,6 @@
#include "emulator.h" #include "emulator.h"
#include "hw/gdrom/gdrom_if.h" #include "hw/gdrom/gdrom_if.h"
#include "rend/gui.h"
/* /*
Mode2 Subheader: Mode2 Subheader:
@ -119,7 +118,7 @@ struct Disc
return false; return false;
} }
void ReadSectors(u32 FAD,u32 count,u8* dst,u32 fmt); void ReadSectors(u32 FAD, u32 count, u8 *dst, u32 fmt, LoadProgress *progress = nullptr);
virtual ~Disc() virtual ~Disc()
{ {

View File

@ -391,7 +391,7 @@ int main(int argc, char* argv[])
mainui_loop(); mainui_loop();
emu.term(); flycast_term();
os_UninstallFaultHandler(); os_UninstallFaultHandler();

View File

@ -1,7 +1,5 @@
#ifndef LIBRETRO #ifndef LIBRETRO
#include "types.h" #include "types.h"
#include <future>
#include "emulator.h" #include "emulator.h"
#include "hw/mem/_vmem.h" #include "hw/mem/_vmem.h"
#include "cfg/cfg.h" #include "cfg/cfg.h"
@ -9,14 +7,11 @@
#include "log/LogManager.h" #include "log/LogManager.h"
#include "rend/gui.h" #include "rend/gui.h"
#include "oslib/oslib.h" #include "oslib/oslib.h"
#include "hw/sh4/sh4_if.h"
#include "debug/gdb_server.h" #include "debug/gdb_server.h"
#include "archive/rzip.h" #include "archive/rzip.h"
#include "rend/mainui.h" #include "rend/mainui.h"
#include "input/gamepad_device.h" #include "input/gamepad_device.h"
static std::future<void> loadingDone;
int flycast_init(int argc, char* argv[]) int flycast_init(int argc, char* argv[])
{ {
#if defined(TEST_AUTOMATION) #if defined(TEST_AUTOMATION)
@ -58,7 +53,7 @@ int flycast_init(int argc, char* argv[])
void dc_exit() void dc_exit()
{ {
emu.term(); emu.stop();
mainui_stop(); mainui_stop();
} }
@ -73,11 +68,10 @@ void SaveSettings()
#endif #endif
} }
void dc_term() void flycast_term()
{ {
dc_cancel_load(); gui_cancel_load();
emu.term(); emu.term();
SaveSettings();
} }
void dc_savestate(int index) void dc_savestate(int index)
@ -215,38 +209,4 @@ void dc_loadstate(int index)
INFO_LOG(SAVESTATE, "Loaded state from %s size %d", filename.c_str(), total_size) ; INFO_LOG(SAVESTATE, "Loaded state from %s size %d", filename.c_str(), total_size) ;
} }
void dc_load_game(const std::string& path)
{
loading_canceled = false;
loadingDone = std::async(std::launch::async, [path] {
emu.loadGame(path.c_str());
});
}
bool dc_is_load_done()
{
if (!loadingDone.valid())
return true;
if (loadingDone.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
return true;
return false;
}
void dc_cancel_load()
{
if (loadingDone.valid())
{
loading_canceled = true;
loadingDone.get();
}
settings.content.path.clear();
}
void dc_get_load_status()
{
if (loadingDone.valid())
loadingDone.get();
}
#endif #endif

View File

@ -63,6 +63,7 @@ bool DXContext::Init(bool keepCurrentWindow)
void DXContext::Term() void DXContext::Term()
{ {
overlay.term(); overlay.term();
gui_term();
ImGui_ImplDX9_Shutdown(); ImGui_ImplDX9_Shutdown();
pDevice.reset(); pDevice.reset();
pD3D.reset(); pD3D.reset();

View File

@ -69,6 +69,7 @@ static void term_vmus();
static void displayCrosshairs(); static void displayCrosshairs();
GameScanner scanner; GameScanner scanner;
static BackgroundGameLoader gameLoader;
static void emuEventCallback(Event event) static void emuEventCallback(Event event)
{ {
@ -409,7 +410,7 @@ void gui_open_settings()
} }
else if (gui_state == GuiState::Loading) else if (gui_state == GuiState::Loading)
{ {
dc_cancel_load(); gameLoader.cancel();
gui_state = GuiState::Main; gui_state = GuiState::Main;
} }
else if (gui_state == GuiState::Commands) else if (gui_state == GuiState::Commands)
@ -427,7 +428,7 @@ void gui_start_game(const std::string& path)
scanner.stop(); scanner.stop();
gui_state = GuiState::Loading; gui_state = GuiState::Loading;
dc_load_game(path); gameLoader.load(path);
} }
void gui_stop_game(const std::string& message) void gui_stop_game(const std::string& message)
@ -2076,10 +2077,7 @@ static void gui_display_content()
ImGui::PushID("bios"); ImGui::PushID("bios");
if (ImGui::Selectable("Dreamcast BIOS")) if (ImGui::Selectable("Dreamcast BIOS"))
{
gui_state = GuiState::Closed;
gui_start_game(""); gui_start_game("");
}
ImGui::PopID(); ImGui::PopID();
{ {
@ -2113,7 +2111,6 @@ static void gui_display_content()
{ {
std::string gamePath(game.path); std::string gamePath(game.path);
scanner.get_mutex().unlock(); scanner.get_mutex().unlock();
gui_state = GuiState::Closed;
gui_start_game(gamePath); gui_start_game(gamePath);
scanner.get_mutex().lock(); scanner.get_mutex().lock();
ImGui::PopID(); ImGui::PopID();
@ -2197,7 +2194,7 @@ static void gui_network_start()
if (networkStatus.get()) if (networkStatus.get())
{ {
gui_state = GuiState::Closed; gui_state = GuiState::Closed;
ImGui::Text("STARTING..."); ImGui::Text("Starting...");
} }
else else
{ {
@ -2213,7 +2210,7 @@ static void gui_network_start()
} }
else else
{ {
ImGui::Text("STARTING NETWORK..."); ImGui::Text("Starting Network...");
if (NetworkHandshake::instance->canStartNow()) if (NetworkHandshake::instance->canStartNow())
ImGui::Text("Press Start to start the game now."); ImGui::Text("Press Start to start the game now.");
} }
@ -2250,10 +2247,9 @@ static void gui_display_loadscreen()
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20 * scaling, 10 * scaling)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20 * scaling, 10 * scaling));
ImGui::AlignTextToFramePadding(); ImGui::AlignTextToFramePadding();
ImGui::SetCursorPosX(20.f * scaling); ImGui::SetCursorPosX(20.f * scaling);
if (dc_is_load_done())
{
try { try {
dc_get_load_status(); if (gameLoader.ready())
{
if (NetworkHandshake::instance != nullptr) if (NetworkHandshake::instance != nullptr)
{ {
networkStatus = NetworkHandshake::instance->start(); networkStatus = NetworkHandshake::instance->start();
@ -2262,7 +2258,27 @@ static void gui_display_loadscreen()
else else
{ {
gui_state = GuiState::Closed; gui_state = GuiState::Closed;
ImGui::Text("STARTING..."); ImGui::Text("Starting...");
}
}
else
{
const char *label = gameLoader.getProgress().label;
if (label == nullptr)
label = "Loading...";
ImGui::Text("%s", label);
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.557f, 0.268f, 0.965f, 1.f));
ImGui::ProgressBar(gameLoader.getProgress().progress, ImVec2(-1, 20.f * scaling), "");
ImGui::PopStyleColor();
float currentwidth = ImGui::GetContentRegionAvail().x;
ImGui::SetCursorPosX((currentwidth - 100.f * scaling) / 2.f + ImGui::GetStyle().WindowPadding.x);
ImGui::SetCursorPosY(126.f * scaling);
if (ImGui::Button("Cancel", ImVec2(100.f * scaling, 0.f)))
{
gameLoader.cancel();
gui_state = GuiState::Main;
}
} }
} catch (const FlycastException& ex) { } catch (const FlycastException& ex) {
ERROR_LOG(BOOT, "%s", ex.what()); ERROR_LOG(BOOT, "%s", ex.what());
@ -2273,22 +2289,6 @@ static void gui_display_loadscreen()
emu.unloadGame(); emu.unloadGame();
gui_state = GuiState::Main; gui_state = GuiState::Main;
} }
}
else
{
ImGui::Text("LOADING... ");
ImGui::SameLine();
ImGui::Text("%s", get_notification().c_str());
float currentwidth = ImGui::GetContentRegionAvail().x;
ImGui::SetCursorPosX((currentwidth - 100.f * scaling) / 2.f + ImGui::GetStyle().WindowPadding.x);
ImGui::SetCursorPosY(126.f * scaling);
if (ImGui::Button("Cancel", ImVec2(100.f * scaling, 0.f)))
{
dc_cancel_load();
gui_state = GuiState::Main;
}
}
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::End(); ImGui::End();
@ -2426,6 +2426,11 @@ void gui_open_onboarding()
gui_state = GuiState::Onboarding; gui_state = GuiState::Onboarding;
} }
void gui_cancel_load()
{
gameLoader.cancel();
}
void gui_term() void gui_term()
{ {
if (inited) if (inited)
@ -2436,6 +2441,8 @@ void gui_term()
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
EventManager::unlisten(Event::Resume, emuEventCallback); EventManager::unlisten(Event::Resume, emuEventCallback);
EventManager::unlisten(Event::Start, emuEventCallback);
EventManager::unlisten(Event::Terminate, emuEventCallback);
} }
} }

View File

@ -27,6 +27,7 @@ void gui_display_notification(const char *msg, int duration);
void gui_display_osd(); void gui_display_osd();
void gui_open_onboarding(); void gui_open_onboarding();
void gui_term(); void gui_term();
void gui_cancel_load();
void gui_refresh_files(); void gui_refresh_files();
void gui_cheats(); void gui_cheats();
void gui_keyboard_input(u16 wc); void gui_keyboard_input(u16 wc);

View File

@ -27,6 +27,7 @@
#include "vulkan/vulkan_context.h" #include "vulkan/vulkan_context.h"
#include "dx9/dxcontext.h" #include "dx9/dxcontext.h"
#include "gui.h" #include "gui.h"
#include "emulator.h"
typedef void (*StringCallback)(bool cancelled, std::string selection); typedef void (*StringCallback)(bool cancelled, std::string selection);
@ -82,3 +83,46 @@ static inline bool operator!=(const ImVec2& l, const ImVec2& r)
void fullScreenWindow(bool modal); void fullScreenWindow(bool modal);
void windowDragScroll(); void windowDragScroll();
class BackgroundGameLoader
{
public:
void load(const std::string& path)
{
progress.reset();
future = std::async(std::launch::async, [this, path] {
emu.loadGame(path.c_str(), &progress);
});
}
void cancel()
{
progress.cancelled = true;
if (future.valid())
try {
future.get();
} catch (const FlycastException& e) {
}
emu.unloadGame();
}
bool ready()
{
if (!future.valid())
return true;
if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
future.get();
return true;
}
return false;
}
const LoadProgress& getProgress() const {
return progress;
}
private:
LoadProgress progress;
std::future<void> future;
};

View File

@ -453,6 +453,12 @@ public:
FlycastException(const std::string& reason) : std::runtime_error(reason) {} FlycastException(const std::string& reason) : std::runtime_error(reason) {}
}; };
class LoadCancelledException : public FlycastException
{
public:
LoadCancelledException() : FlycastException("") {}
};
enum serialize_version_enum { enum serialize_version_enum {
V1, V1,
V2, V2,

View File

@ -758,7 +758,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
mainui_loop(); mainui_loop();
emu.term(); flycast_term();
os_UninstallFaultHandler(); os_UninstallFaultHandler();

View File

@ -88,7 +88,7 @@ static bool emulatorRunning;
- (void)applicationWillTerminate:(UIApplication *)application - (void)applicationWillTerminate:(UIApplication *)application
{ {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
dc_term(); flycast_term();
LogManager::Shutdown(); LogManager::Shutdown();
} }

View File

@ -24,7 +24,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
func applicationWillTerminate(_ aNotification: Notification) { func applicationWillTerminate(_ aNotification: Notification) {
emu_dc_term() emu_flycast_term()
} }
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {

View File

@ -71,7 +71,7 @@ class EmuGLView: NSOpenGLView, NSWindowDelegate {
let rect = convertToBacking(frame) let rect = convertToBacking(frame)
emu_gles_init(Int32(rect.width), Int32(rect.height)) emu_gles_init(Int32(rect.width), Int32(rect.height))
if (emu_reicast_init() != 0) { if (emu_flycast_init() != 0) {
let alert = NSAlert() let alert = NSAlert()
alert.alertStyle = .critical alert.alertStyle = .critical
alert.messageText = "Flycast initialization failed" alert.messageText = "Flycast initialization failed"

View File

@ -14,15 +14,14 @@
extern "C" { extern "C" {
#endif #endif
void emu_dc_exit(); void emu_flycast_term();
void emu_dc_term();
void emu_gui_open_settings(); void emu_gui_open_settings();
bool emu_renderer_enabled(); bool emu_renderer_enabled();
bool emu_fast_forward(); bool emu_fast_forward();
bool emu_vsync_enabled(); bool emu_vsync_enabled();
bool emu_single_frame(int w, int h); bool emu_single_frame(int w, int h);
void emu_gles_init(int width, int height); void emu_gles_init(int width, int height);
int emu_reicast_init(); int emu_flycast_init();
void emu_key_input(UInt16 keyCode, bool pressed, UInt32 modifierFlags); void emu_key_input(UInt16 keyCode, bool pressed, UInt32 modifierFlags);
void emu_character_input(const char *characters); void emu_character_input(const char *characters);
void emu_mouse_buttons(int button, bool pressed); void emu_mouse_buttons(int button, bool pressed);

View File

@ -90,16 +90,9 @@ void os_SetupInput()
void common_linux_setup(); void common_linux_setup();
void emu_dc_exit() void emu_flycast_term()
{ {
dc_exit(); flycast_term();
}
void emu_dc_term()
{
if (emu.running())
dc_exit();
dc_term();
LogManager::Shutdown(); LogManager::Shutdown();
} }
@ -238,7 +231,7 @@ void emu_gles_init(int width, int height)
mainui_enabled = true; mainui_enabled = true;
} }
int emu_reicast_init() int emu_flycast_init()
{ {
LogManager::Init(); LogManager::Init();
common_linux_setup(); common_linux_setup();

View File

@ -125,7 +125,6 @@ static double vib_delta[4];
unsigned per_content_vmus = 0; unsigned per_content_vmus = 0;
static bool first_run = true; static bool first_run = true;
static bool mute_messages;
static bool rotate_screen; static bool rotate_screen;
static bool rotate_game; static bool rotate_game;
static int framebufferWidth; static int framebufferWidth;
@ -151,6 +150,7 @@ static void refresh_devices(bool first_startup);
static void init_disk_control_interface(); static void init_disk_control_interface();
static bool read_m3u(const char *file); static bool read_m3u(const char *file);
void UpdateInputState(); void UpdateInputState();
void gui_display_notification(const char *msg, int duration);
static std::string game_data; static std::string game_data;
static char g_base_name[128]; static char g_base_name[128];
@ -867,16 +867,13 @@ void retro_run()
static bool loadGame() static bool loadGame()
{ {
mute_messages = true;
try { try {
emu.loadGame(game_data.c_str()); emu.loadGame(game_data.c_str());
} catch (const FlycastException& e) { } catch (const FlycastException& e) {
ERROR_LOG(BOOT, "%s", e.what()); ERROR_LOG(BOOT, "%s", e.what());
mute_messages = false;
gui_display_notification(e.what(), 5000); gui_display_notification(e.what(), 5000);
return false; return false;
} }
mute_messages = false;
return true; return true;
} }
@ -2923,8 +2920,6 @@ static bool read_m3u(const char *file)
void gui_display_notification(const char *msg, int duration) void gui_display_notification(const char *msg, int duration)
{ {
if (mute_messages)
return;
retro_message retromsg; retro_message retromsg;
retromsg.msg = msg; retromsg.msg = msg;
retromsg.frames = duration / 17; retromsg.frames = duration / 17;