diff --git a/core/emulator.cpp b/core/emulator.cpp index da39cac90..1cf948f9e 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -37,9 +37,9 @@ #include "network/ggpo.h" #include "hw/mem/mem_watch.h" #include "network/net_handshake.h" +#include "rend/gui.h" #include -std::atomic loading_canceled; settings_t settings; static void loadSpecialSettings() @@ -396,7 +396,7 @@ static int getGamePlatform(const char *path) return DC_PLATFORM_DREAMCAST; } -void Emulator::loadGame(const char *path) +void Emulator::loadGame(const char *path, LoadProgress *progress) { init(); 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) { LoadRomFiles(); - naomi_cart_LoadRom(path); - if (loading_canceled) - return; + naomi_cart_LoadRom(path, progress); loadGameSpecificSettings(); // Reload the BIOS in case a game-specific region is set naomi_cart_LoadBios(path); @@ -641,7 +639,8 @@ bool dc_loadstate(const void **data, u32 size) void Emulator::setNetworkState(bool online) { - DEBUG_LOG(NETWORK, "Network state %d", online); + if (settings.online != online) + DEBUG_LOG(NETWORK, "Network state %d", online); settings.online = online; settings.input.fastForwardMode &= !online; } diff --git a/core/emulator.h b/core/emulator.h index 826157708..3f9a8cafa 100644 --- a/core/emulator.h +++ b/core/emulator.h @@ -25,25 +25,19 @@ #include #include #include +#include void loadGameSpecificSettings(); void SaveSettings(); -extern std::atomic loading_canceled; - int flycast_init(int argc, char* argv[]); void dc_reset(bool hard); -void dc_term(); +void flycast_term(); void dc_exit(); void dc_savestate(int index = 0); void dc_loadstate(int index = 0); 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 { Start, Pause, @@ -80,6 +74,19 @@ private: std::map> callbacks; }; +struct LoadProgress +{ + void reset() + { + cancelled = false; + label = nullptr; + progress = 0.f; + } + std::atomic cancelled; + std::atomic label; + std::atomic progress; +}; + class Emulator { public: @@ -96,7 +103,7 @@ public: * 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. */ - 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. * Does nothing if no game is loaded. diff --git a/core/hw/naomi/awcartridge.cpp b/core/hw/naomi/awcartridge.cpp index 9ffe21a83..0b4bf0ae2 100644 --- a/core/hw/naomi/awcartridge.cpp +++ b/core/hw/naomi/awcartridge.cpp @@ -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); INFO_LOG(NAOMI, "AWCartridge::SetKey rombd_key %02x mpr_offset %08x", rombd_key, mpr_offset); diff --git a/core/hw/naomi/awcartridge.h b/core/hw/naomi/awcartridge.h index 2311ec277..c5d0e0106 100644 --- a/core/hw/naomi/awcartridge.h +++ b/core/hw/naomi/awcartridge.h @@ -19,7 +19,7 @@ class AWCartridge: public Cartridge public: AWCartridge(u32 size) : Cartridge(size) {} - void Init() override; + void Init(LoadProgress *progress = nullptr) override; u32 ReadMem(u32 address, u32 size) override; void WriteMem(u32 address, u32 data, u32 size) override; diff --git a/core/hw/naomi/gdcartridge.cpp b/core/hw/naomi/gdcartridge.cpp index 0e1ec779d..ba2a8ffed 100644 --- a/core/hw/naomi/gdcartridge.cpp +++ b/core/hw/naomi/gdcartridge.cpp @@ -13,7 +13,6 @@ #include "gdcartridge.h" #include "stdclass.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) { @@ -494,15 +493,15 @@ void GDCartridge::device_start() u8 buffer[2048]; std::string gdrom_path = get_game_basename() + "/" + gdrom_name; - Disc *gdrom = OpenDisc(gdrom_path + ".chd"); + std::unique_ptr gdrom = std::unique_ptr(OpenDisc(gdrom_path + ".chd")); if (gdrom == nullptr) - gdrom = OpenDisc(gdrom_path + ".gdi"); + gdrom = std::unique_ptr(OpenDisc(gdrom_path + ".gdi")); if (gdrom_parent_name != nullptr && gdrom == nullptr) { std::string gdrom_parent_path = get_game_dir() + "/" + gdrom_parent_name + "/" + gdrom_name; - gdrom = OpenDisc(gdrom_parent_path + ".chd"); + gdrom = std::unique_ptr(OpenDisc(gdrom_parent_path + ".chd")); if (gdrom == nullptr) - gdrom = OpenDisc(gdrom_parent_path + ".gdi"); + gdrom = std::unique_ptr(OpenDisc(gdrom_parent_path + ".gdi")); } if (gdrom == nullptr) throw NaomiCartException("Naomi GDROM: Cannot open " + gdrom_path + ".chd or " + gdrom_path + ".gdi"); @@ -510,13 +509,13 @@ void GDCartridge::device_start() // primary volume descriptor // read frame 0xb06e (frame=sector+150) // 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) | (buffer[0x8c+1] << 8) | (buffer[0x8c+2] << 16) | (buffer[0x8c+3] << 24)); // path table - read_gdrom(gdrom, path_table, buffer); + read_gdrom(gdrom.get(), path_table, buffer); // directory u8 dir_sector[2048]; @@ -529,12 +528,12 @@ void GDCartridge::device_start() (buffer[0x2 + 2] << 16) | (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); if (file_start && (file_size == 0x100)) { // read file - read_gdrom(gdrom, file_start, buffer); + read_gdrom(gdrom.get(), file_start, buffer); // get "rom" file name memset(name, '\0', 128); memcpy(name, buffer + 0xc0, FILENAME_LENGTH - 1); @@ -550,7 +549,7 @@ void GDCartridge::device_start() (buffer[i + 4] << 16) | (buffer[i + 5] << 24)); memcpy(name, "ROM.BIN", 7); - read_gdrom(gdrom, dir, dir_sector); + read_gdrom(gdrom.get(), dir, dir_sector); break; } i += buffer[i] + 8 + (buffer[i] & 1); @@ -570,31 +569,25 @@ void GDCartridge::device_start() // read encrypted data into dimm_data 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 u32 des_subkeys[32]; des_generate_subkeys(rev64(key), des_subkeys); - u32 progress = ~0; for (u32 i = 0; i < file_rounded_size; i += 8) { - const u32 new_progress = (u32)(((u64)i + 8) * 100 / file_rounded_size); - if (progress != new_progress) + if (progress != nullptr) { - if (loading_canceled) - break; - progress = new_progress; - char status_str[16]; - sprintf(status_str, "Decrypting %d%%", progress); - gui_display_notification(status_str, 2000); + if (progress->cancelled) + throw LoadCancelledException(); + progress->label = "Decrypting..."; + progress->progress = (float)(i + 8) / file_rounded_size; } *(u64 *)(dimm_data + i) = des_encrypt_decrypt(*(u64 *)(dimm_data + i), des_subkeys); } } - delete gdrom; - if (!dimm_data) throw NaomiCartException("Naomi GDROM: Could not find the file to decrypt."); } diff --git a/core/hw/naomi/gdcartridge.h b/core/hw/naomi/gdcartridge.h index 56a2b1f21..843884032 100644 --- a/core/hw/naomi/gdcartridge.h +++ b/core/hw/naomi/gdcartridge.h @@ -25,9 +25,9 @@ public: { free(dimm_data); } - void Init() override + void Init(LoadProgress *progress = nullptr) override { - device_start(); + device_start(progress); device_reset(); } void* GetDmaPtr(u32 &size) override; @@ -61,7 +61,7 @@ private: static const u32 DES_MASK_TABLE[]; static const u8 DES_ROTATE_TABLE[16]; - void device_start(); + void device_start(LoadProgress *progress); void device_reset(); void find_file(const char *name, const u8 *dir_sector, u32 &file_start, u32 &file_size); @@ -70,7 +70,7 @@ private: template u64 des_encrypt_decrypt(u64 src, const u32 *des_subkeys); 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_ */ diff --git a/core/hw/naomi/m4cartridge.h b/core/hw/naomi/m4cartridge.h index 40e589b75..aa93c0b7e 100644 --- a/core/hw/naomi/m4cartridge.h +++ b/core/hw/naomi/m4cartridge.h @@ -20,7 +20,7 @@ public: M4Cartridge(u32 size) : NaomiCartridge(size) { } ~M4Cartridge() override; - void Init() override + void Init(LoadProgress *progress = nullptr) override { device_start(); device_reset(); diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index 0f5219fff..6285e6909 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -34,7 +34,6 @@ #include "archive/archive.h" #include "stdclass.h" #include "emulator.h" -#include "rend/gui.h" #include "cfg/option.h" #include "oslib/oslib.h" @@ -197,7 +196,7 @@ void naomi_cart_LoadBios(const char *filename) 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); if (game == NULL) @@ -257,12 +256,22 @@ static void naomi_cart_LoadZip(const char *filename) CurrentCartridge->SetKey(game->key); 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 (game->cart_type != GD) + if (progress != nullptr) { - std::string progress = "ROM " + std::to_string(romid + 1); - gui_display_notification(progress.c_str(), 1000); + if (progress->cancelled) + throw LoadCancelledException(); + if (game->cart_type != GD) + { + static std::string label; + label = "ROM " + std::to_string(romid + 1); + progress->label = label.c_str(); + progress->progress = (float)(romid + 1) / romCount; + } } 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) naomi_default_eeprom = game->eeprom_dump; if (game->rotation_flag == ROT270) config::Rotate90.override(true); - CurrentCartridge->Init(); - if (loading_canceled) - return; + CurrentCartridge->Init(progress); strcpy(naomi_game_id, CurrentCartridge->GetGameId().c_str()); if (naomi_game_id[0] == '\0') strcpy(naomi_game_id, game->name); NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s]", naomi_game_id); - } catch (const FlycastException& ex) { + } catch (...) { delete CurrentCartridge; CurrentCartridge = NULL; - - throw ex; + throw; } } -void naomi_cart_LoadRom(const char* file) +void naomi_cart_LoadRom(const char* file, LoadProgress *progress) { naomi_cart_Close(); @@ -382,7 +386,7 @@ void naomi_cart_LoadRom(const char* file) if (extension == "zip" || extension == "7z") { - naomi_cart_LoadZip(file); + naomi_cart_LoadZip(file, progress); return; } @@ -609,6 +613,11 @@ std::string Cartridge::GetGameId() { } while (!game_id.empty() && game_id.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; } @@ -918,6 +927,10 @@ std::string M2Cartridge::GetGameId() game_id = std::string((char *)RomPtr + 0x800030, 0x20); while (!game_id.empty() && game_id.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; } diff --git a/core/hw/naomi/naomi_cart.h b/core/hw/naomi/naomi_cart.h index 4c18376fc..547720b4c 100644 --- a/core/hw/naomi/naomi_cart.h +++ b/core/hw/naomi/naomi_cart.h @@ -3,6 +3,7 @@ #include #include #include "types.h" +#include "emulator.h" class Cartridge { @@ -10,7 +11,7 @@ public: Cartridge(u32 size); virtual ~Cartridge(); - virtual void Init() {} + virtual void Init(LoadProgress *progress = nullptr) {} virtual u32 ReadMem(u32 address, 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) {} }; -void naomi_cart_LoadRom(const char* file); +void naomi_cart_LoadRom(const char* file, LoadProgress *progress); void naomi_cart_Close(); int naomi_cart_GetPlatform(const char *path); void naomi_cart_LoadBios(const char *filename); diff --git a/core/imgread/common.cpp b/core/imgread/common.cpp index 15dc08f08..152cc87d3 100644 --- a/core/imgread/common.cpp +++ b/core/imgread/common.cpp @@ -275,28 +275,20 @@ DiscType GuessDiscType(bool m1, bool m2, bool da) 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]; SectorFormat secfmt; SubcodeFormat subfmt; - u32 progress = ~0; for (u32 i = 1; i <= count; i++) { - if (count >= 1000) + if (progress != nullptr) { - if (loading_canceled) - break; - // Progress report when loading naomi gd-rom games - const u32 new_progress = i * 100 / count; - if (progress != new_progress) - { - progress = new_progress; - char status_str[16]; - sprintf(status_str, "%d%%", progress); - gui_display_notification(status_str, 2000); - } + if (progress->cancelled) + throw LoadCancelledException(); + progress->label = "Loading..."; + progress->progress = (float)i / count; } if (ReadSector(FAD,temp,&secfmt,q_subchannel,&subfmt)) { diff --git a/core/imgread/common.h b/core/imgread/common.h index 1fb49ebc5..7b1d19e7c 100644 --- a/core/imgread/common.h +++ b/core/imgread/common.h @@ -4,7 +4,6 @@ #include "emulator.h" #include "hw/gdrom/gdrom_if.h" -#include "rend/gui.h" /* Mode2 Subheader: @@ -119,7 +118,7 @@ struct Disc 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() { diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 25af39d79..12576b18e 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -391,7 +391,7 @@ int main(int argc, char* argv[]) mainui_loop(); - emu.term(); + flycast_term(); os_UninstallFaultHandler(); diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 9598a5808..d476212df 100644 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -1,7 +1,5 @@ #ifndef LIBRETRO #include "types.h" -#include - #include "emulator.h" #include "hw/mem/_vmem.h" #include "cfg/cfg.h" @@ -9,14 +7,11 @@ #include "log/LogManager.h" #include "rend/gui.h" #include "oslib/oslib.h" -#include "hw/sh4/sh4_if.h" #include "debug/gdb_server.h" #include "archive/rzip.h" #include "rend/mainui.h" #include "input/gamepad_device.h" -static std::future loadingDone; - int flycast_init(int argc, char* argv[]) { #if defined(TEST_AUTOMATION) @@ -58,7 +53,7 @@ int flycast_init(int argc, char* argv[]) void dc_exit() { - emu.term(); + emu.stop(); mainui_stop(); } @@ -73,11 +68,10 @@ void SaveSettings() #endif } -void dc_term() +void flycast_term() { - dc_cancel_load(); + gui_cancel_load(); emu.term(); - SaveSettings(); } 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) ; } -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 - diff --git a/core/rend/dx9/dxcontext.cpp b/core/rend/dx9/dxcontext.cpp index 18a23f990..00d51f59c 100644 --- a/core/rend/dx9/dxcontext.cpp +++ b/core/rend/dx9/dxcontext.cpp @@ -63,6 +63,7 @@ bool DXContext::Init(bool keepCurrentWindow) void DXContext::Term() { overlay.term(); + gui_term(); ImGui_ImplDX9_Shutdown(); pDevice.reset(); pD3D.reset(); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 3f159c095..741e8071f 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -69,6 +69,7 @@ static void term_vmus(); static void displayCrosshairs(); GameScanner scanner; +static BackgroundGameLoader gameLoader; static void emuEventCallback(Event event) { @@ -409,7 +410,7 @@ void gui_open_settings() } else if (gui_state == GuiState::Loading) { - dc_cancel_load(); + gameLoader.cancel(); gui_state = GuiState::Main; } else if (gui_state == GuiState::Commands) @@ -427,7 +428,7 @@ void gui_start_game(const std::string& path) scanner.stop(); gui_state = GuiState::Loading; - dc_load_game(path); + gameLoader.load(path); } void gui_stop_game(const std::string& message) @@ -2076,10 +2077,7 @@ static void gui_display_content() ImGui::PushID("bios"); if (ImGui::Selectable("Dreamcast BIOS")) - { - gui_state = GuiState::Closed; gui_start_game(""); - } ImGui::PopID(); { @@ -2113,7 +2111,6 @@ static void gui_display_content() { std::string gamePath(game.path); scanner.get_mutex().unlock(); - gui_state = GuiState::Closed; gui_start_game(gamePath); scanner.get_mutex().lock(); ImGui::PopID(); @@ -2197,7 +2194,7 @@ static void gui_network_start() if (networkStatus.get()) { gui_state = GuiState::Closed; - ImGui::Text("STARTING..."); + ImGui::Text("Starting..."); } else { @@ -2213,7 +2210,7 @@ static void gui_network_start() } else { - ImGui::Text("STARTING NETWORK..."); + ImGui::Text("Starting Network..."); if (NetworkHandshake::instance->canStartNow()) 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::AlignTextToFramePadding(); ImGui::SetCursorPosX(20.f * scaling); - if (dc_is_load_done()) - { - try { - dc_get_load_status(); + try { + if (gameLoader.ready()) + { if (NetworkHandshake::instance != nullptr) { networkStatus = NetworkHandshake::instance->start(); @@ -2262,32 +2258,36 @@ static void gui_display_loadscreen() else { gui_state = GuiState::Closed; - ImGui::Text("STARTING..."); + ImGui::Text("Starting..."); } - } catch (const FlycastException& ex) { - ERROR_LOG(BOOT, "%s", ex.what()); - error_msg = ex.what(); -#ifdef TEST_AUTOMATION - die("Game load failed"); -#endif - emu.unloadGame(); - 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))) + else { - dc_cancel_load(); - gui_state = GuiState::Main; + 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) { + ERROR_LOG(BOOT, "%s", ex.what()); + error_msg = ex.what(); +#ifdef TEST_AUTOMATION + die("Game load failed"); +#endif + emu.unloadGame(); + gui_state = GuiState::Main; } ImGui::PopStyleVar(); @@ -2426,6 +2426,11 @@ void gui_open_onboarding() gui_state = GuiState::Onboarding; } +void gui_cancel_load() +{ + gameLoader.cancel(); +} + void gui_term() { if (inited) @@ -2436,6 +2441,8 @@ void gui_term() ImGui_ImplOpenGL3_Shutdown(); ImGui::DestroyContext(); EventManager::unlisten(Event::Resume, emuEventCallback); + EventManager::unlisten(Event::Start, emuEventCallback); + EventManager::unlisten(Event::Terminate, emuEventCallback); } } diff --git a/core/rend/gui.h b/core/rend/gui.h index 0412e6ff7..c468ee33f 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -27,6 +27,7 @@ void gui_display_notification(const char *msg, int duration); void gui_display_osd(); void gui_open_onboarding(); void gui_term(); +void gui_cancel_load(); void gui_refresh_files(); void gui_cheats(); void gui_keyboard_input(u16 wc); diff --git a/core/rend/gui_util.h b/core/rend/gui_util.h index afe5e1078..586fe2ae7 100644 --- a/core/rend/gui_util.h +++ b/core/rend/gui_util.h @@ -27,6 +27,7 @@ #include "vulkan/vulkan_context.h" #include "dx9/dxcontext.h" #include "gui.h" +#include "emulator.h" 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 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 future; +}; diff --git a/core/types.h b/core/types.h index b04d35ceb..50d83e63b 100644 --- a/core/types.h +++ b/core/types.h @@ -453,6 +453,12 @@ public: FlycastException(const std::string& reason) : std::runtime_error(reason) {} }; +class LoadCancelledException : public FlycastException +{ +public: + LoadCancelledException() : FlycastException("") {} +}; + enum serialize_version_enum { V1, V2, diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 84bf70a2f..c25155e29 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -758,7 +758,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi mainui_loop(); - emu.term(); + flycast_term(); os_UninstallFaultHandler(); diff --git a/shell/apple/emulator-ios/emulator/AppDelegate.mm b/shell/apple/emulator-ios/emulator/AppDelegate.mm index 754effa4e..780128b67 100644 --- a/shell/apple/emulator-ios/emulator/AppDelegate.mm +++ b/shell/apple/emulator-ios/emulator/AppDelegate.mm @@ -88,7 +88,7 @@ static bool emulatorRunning; - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. - dc_term(); + flycast_term(); LogManager::Shutdown(); } diff --git a/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift b/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift index 1ffb583e4..d33ca7590 100644 --- a/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift +++ b/shell/apple/emulator-osx/emulator-osx/AppDelegate.swift @@ -24,7 +24,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func applicationWillTerminate(_ aNotification: Notification) { - emu_dc_term() + emu_flycast_term() } func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { diff --git a/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift b/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift index fd6e2ff3c..852551c4c 100644 --- a/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift +++ b/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift @@ -71,7 +71,7 @@ class EmuGLView: NSOpenGLView, NSWindowDelegate { let rect = convertToBacking(frame) emu_gles_init(Int32(rect.width), Int32(rect.height)) - if (emu_reicast_init() != 0) { + if (emu_flycast_init() != 0) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "Flycast initialization failed" diff --git a/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h b/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h index 1e5c98233..f72b8e4e1 100644 --- a/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h +++ b/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h @@ -14,15 +14,14 @@ extern "C" { #endif -void emu_dc_exit(); -void emu_dc_term(); +void emu_flycast_term(); void emu_gui_open_settings(); bool emu_renderer_enabled(); bool emu_fast_forward(); bool emu_vsync_enabled(); bool emu_single_frame(int w, int h); 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_character_input(const char *characters); void emu_mouse_buttons(int button, bool pressed); diff --git a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm index d1a62f475..2c0249cc7 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm +++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm @@ -90,16 +90,9 @@ void os_SetupInput() void common_linux_setup(); -void emu_dc_exit() +void emu_flycast_term() { - dc_exit(); -} - -void emu_dc_term() -{ - if (emu.running()) - dc_exit(); - dc_term(); + flycast_term(); LogManager::Shutdown(); } @@ -238,7 +231,7 @@ void emu_gles_init(int width, int height) mainui_enabled = true; } -int emu_reicast_init() +int emu_flycast_init() { LogManager::Init(); common_linux_setup(); diff --git a/shell/libretro/libretro.cpp b/shell/libretro/libretro.cpp index af23c215c..9d48baddc 100644 --- a/shell/libretro/libretro.cpp +++ b/shell/libretro/libretro.cpp @@ -125,7 +125,6 @@ static double vib_delta[4]; unsigned per_content_vmus = 0; static bool first_run = true; -static bool mute_messages; static bool rotate_screen; static bool rotate_game; static int framebufferWidth; @@ -151,6 +150,7 @@ static void refresh_devices(bool first_startup); static void init_disk_control_interface(); static bool read_m3u(const char *file); void UpdateInputState(); +void gui_display_notification(const char *msg, int duration); static std::string game_data; static char g_base_name[128]; @@ -867,16 +867,13 @@ void retro_run() static bool loadGame() { - mute_messages = true; try { emu.loadGame(game_data.c_str()); } catch (const FlycastException& e) { ERROR_LOG(BOOT, "%s", e.what()); - mute_messages = false; gui_display_notification(e.what(), 5000); return false; } - mute_messages = false; return true; } @@ -2923,8 +2920,6 @@ static bool read_m3u(const char *file) void gui_display_notification(const char *msg, int duration) { - if (mute_messages) - return; retro_message retromsg; retromsg.msg = msg; retromsg.frames = duration / 17;