diff --git a/CMakeLists.txt b/CMakeLists.txt index f937be8..6f8dc14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(skeldal) find_package(SDL2 REQUIRED) if (MSVC) - add_compile_options(/W4 /EHsc /DNOMINMAX /J) + add_compile_options(/W4 /EHa /DNOMINMAX /J) set(STANDARD_LIBRARIES "") else() add_compile_options(-Wall -Wextra -Werror -Wno-unused-result -Wno-unused-parameter -Wno-unused-value -Wno-extern-c-compat -funsigned-char) diff --git a/game/builder.c b/game/builder.c index 478763a..914bba4 100644 --- a/game/builder.c +++ b/game/builder.c @@ -1375,7 +1375,6 @@ void redraw_scene() ukaz_mysku(); global_anim_counter++; send_message(E_KOUZLO_ANM); - } void refresh_scene(THE_TIMER *t) diff --git a/game/gamesave.c b/game/gamesave.c index 3969522..ef0edfa 100644 --- a/game/gamesave.c +++ b/game/gamesave.c @@ -1849,18 +1849,24 @@ void do_autosave() { DEFAULT_GAME_NAME(""); char prefix[50]; snprintf(prefix,50,"sav.%08lx.",current_campaign); - TSAVEGAME_CB_STATE st; - st.files = create_list(32); - st.prefix = prefix; - st.prefix_len = strlen(prefix); - st.count = 0; - st.skip_autosave = 0; - list_files(gpathtable[SR_SAVES], file_type_just_name|file_type_need_timestamp|file_type_normal, get_all_savegames_callback, &st); - for (size_t i = 0; i < st.count; ++i) { - const char *n = st.files[i]; - if (strstr(n, AUTOSAVE_SUFFIX)) { - remove(build_pathname(2, gpathtable[SR_SAVES],n)); - } + char isdead = 0; + for (int i = 0; i < POCET_POSTAV; ++i) { + isdead = isdead || (postavy[i].used && postavy[i].lives == 0 && postavy[i].inmaphash == current_map_hash); + } + if (!isdead) { + TSAVEGAME_CB_STATE st; + st.files = create_list(32); + st.prefix = prefix; + st.prefix_len = strlen(prefix); + st.count = 0; + st.skip_autosave = 0; + list_files(gpathtable[SR_SAVES], file_type_just_name|file_type_need_timestamp|file_type_normal, get_all_savegames_callback, &st); + for (size_t i = 0; i < st.count; ++i) { + const char *n = st.files[i]; + if (strstr(n, AUTOSAVE_SUFFIX)) { + remove(build_pathname(2, gpathtable[SR_SAVES],n)); + } + } } save_game(get_save_game_slot_id(), game_name,1); } @@ -1907,7 +1913,8 @@ static void save_as_dialog(int pos) { todel = local_strdup(todel); } unwire_proc(); - if (ask_save_dialog(game_name, sizeof(game_name))) { + char r = ask_save_dialog(game_name, sizeof(game_name), todel != NULL); + if (r==1) { prev_game_time_save = cur_time; save_game(get_save_game_slot_id(), game_name,0); if (todel) { @@ -1915,6 +1922,8 @@ static void save_as_dialog(int pos) { } wire_proc(); return; + } else if (r == 2 && todel) { + remove(todel); } wire_save_load(1); } diff --git a/game/globals.h b/game/globals.h index 313e377..2f60ef4 100644 --- a/game/globals.h +++ b/game/globals.h @@ -1413,7 +1413,7 @@ void load_map_description(TMPFILE_RD *f); void free_map_description(); void wire_save_load(char save); void do_save_dialog(); -char ask_save_dialog(char *name_buffer, size_t name_size); +char ask_save_dialog(char *name_buffer, size_t name_size, char allow_remove); void do_autosave(); #define autosave() if (autosave_enabled) do_autosave(); extern char autosave_enabled; diff --git a/game/interfac.c b/game/interfac.c index d8559e0..6d6cfaa 100644 --- a/game/interfac.c +++ b/game/interfac.c @@ -1602,7 +1602,7 @@ THAGGLERESULT smlouvat_dlg(int cena,int puvod,int pocet,int posledni, int money, return res; } -char ask_save_dialog(char *name_buffer, size_t name_size) { +char ask_save_dialog(char *name_buffer, size_t name_size, char allow_remove) { const char *str_label = texty[98]; // if (str_label == 0) str_label ="Ulo\x91it hru jako"; @@ -1613,11 +1613,14 @@ char ask_save_dialog(char *name_buffer, size_t name_size) { define(10,10,30,270,13,0,input_line,name_size-1);property(def_border(5,BAR_COLOR),NULL,NULL,0);set_default(name_buffer); define(20,20,20,60,20,2,button,texty[239]);property(def_border(5,BAR_COLOR),NULL,NULL,BAR_COLOR);on_control_change(terminate_gui); define(30,90,20,60,20,2,button,texty[80]);property(def_border(5,BAR_COLOR),NULL,NULL,BAR_COLOR);on_control_change(terminate_gui); + if (allow_remove) { + define(40,10,20,70,20,3,button,texty[172]);property(def_border(5,BAR_COLOR),NULL,NULL,BAR_COLOR);on_control_change(terminate_gui); + } redraw_window(); goto_control(10); escape(); get_value(0,10,name_buffer); - char ret = o_aktual->id==30; + char ret = o_aktual->id==40?2:o_aktual->id==30?1:0; close_current(); return ret; diff --git a/game/skeldal.c b/game/skeldal.c index 7bda15b..60fd265 100644 --- a/game/skeldal.c +++ b/game/skeldal.c @@ -711,7 +711,7 @@ void done_skeldal(void) close_manager(); close_story_file(); - purge_temps(1); + temp_storage_clear(); stop_mixing(); // deinstall_mouse_handler(); if (texty != NULL) { @@ -805,7 +805,7 @@ void cti_texty(void) //patch stringtable if (!texty[98]) str_replace(&texty,98,"Ulo\x91it hru jako"); if (!texty[99]) str_replace(&texty,99,"CRT Filter (>720p)"); - str_replace(&texty,0,"Byl detekov\xA0n ovlada\x87\nPro aktivaci ovlada\x87""e stiskn\x88te kt\x82rekoliv tla\x87\xA1tko na ovlada\x87i"); + str_replace(&texty,0,"Byl nalezen p\xA9ipojen\x98 ovlada\x87\nPro aktivaci ovlada\x87""e stiskn\x88te kt\x82rekoliv tla\x87\xA1tko na ovlada\x87i"); lang_patch_stringtable(&texty, "ui.csv", ""); } @@ -945,14 +945,13 @@ void show_joystick_info(void) { curcolor = 0; - bar32(0,0,639,479); - set_font(H_FBOLD, NOSHADOW(RGB888(255,255,255))); + set_font(H_FBOLD, RGB888(255,255,255)); const char *prompt = texty[0]; char *buff = (char *)alloca(strlen(prompt)+10); int xs = 0; int ys = 0; zalamovani(prompt,buff,560, &xs, &ys); - int y = 320; + int y = 100; while (*buff) { set_aligned_position(320,y,1,1,buff); outtext(buff); @@ -972,6 +971,18 @@ void show_joystick_info(void) { } +void show_loading_picture(char *filename) + { + const void *p; + int32_t s; + + p=afile(filename,SR_BGRAFIKA,&s); + put_picture(0,0,p); + showview(0,0,0,0); + ablock_free(p); + } + + void init_skeldal(const INI_CONFIG *cfg) { @@ -985,7 +996,8 @@ void init_skeldal(const INI_CONFIG *cfg) char verr = game_display_init(ini_section_open(cfg, "video"), "Skeldal"); if (!verr) { - exit(1); + display_error("Error game_display_init %d", verr); + exit(1); } showview = game_display_update_rect; game_display_set_icon(getWindowIcon(), getWindowIconSize()); @@ -995,6 +1007,7 @@ void init_skeldal(const INI_CONFIG *cfg) atexit(done_skeldal); init_DDL_manager(); + show_loading_picture("LOADING.HI"); if (lang_get_folder()) { texty_knihy = build_pathname(2, lang_get_folder(), "book.txt"); @@ -1742,6 +1755,7 @@ int skeldal_entry_point(const SKELDAL_CONFIG *start_cfg) closemode(); + ini_close(cfg); return 0; diff --git a/platform/error.h b/platform/error.h index 784c51c..5b86ccd 100644 --- a/platform/error.h +++ b/platform/error.h @@ -7,6 +7,8 @@ extern "C" { void send_log_impl(const char *format, ...); +void display_error(const char *format, ...); + #ifdef __cplusplus } diff --git a/platform/legacy_coroutines.cpp b/platform/legacy_coroutines.cpp index 0bc0616..2d76fbd 100644 --- a/platform/legacy_coroutines.cpp +++ b/platform/legacy_coroutines.cpp @@ -1,5 +1,5 @@ #include "legacy_coroutines.h" - +#include "error.h" #include #include @@ -39,7 +39,6 @@ struct MsgQueue { TaskInfo *sender; }; -static std::queue msg_queue; static int recursion_count = 0; @@ -117,6 +116,17 @@ static void broadcast_message(EVENT_MSG *msg) { clean_task_table(); } +static void crash_task_exception() { + try { + throw; + } catch (std::exception &e) { + display_error("Unhandled exception in task %d: %s",q_current_task(), e.what()); + } catch (...) { + display_error("Unhandled exception in task %d: unknown/crash",q_current_task()); + } + abort(); +} + int add_task(int stack,TaskerFunctionName fcname,...) { int id = get_new_task_id(); @@ -127,8 +137,12 @@ int add_task(int stack,TaskerFunctionName fcname,...) { new_task->thr = std::thread([&]{ new_task->resume_flag.wait(false); new_task->resume_flag = false; - fcname(args); - clean_up_current_task(); + try { + fcname(args); + clean_up_current_task(); + } catch (...) { + crash_task_exception(); + } }); switch_to_task(new_task); return id; @@ -148,7 +162,6 @@ char is_running(int id_num) { return !iter->second->exited; } void unsuspend_task(EVENT_MSG *msg) { - msg_queue.push({msg, current_task_inst}); broadcast_message(msg); } void task_sleep(void) { diff --git a/platform/sdl/input.cpp b/platform/sdl/input.cpp index b4dd55e..6f019b4 100644 --- a/platform/sdl/input.cpp +++ b/platform/sdl/input.cpp @@ -55,18 +55,18 @@ void init_joystick(const INI_CONFIG_SECTION *section) { cfg.buttons[10] = SDLContext::JoystickButton::mod_key; cfg.buttons_mod[10] = SDLContext::JoystickButton::mod_key; cfg.buttons[11] = SDLContext::JoystickButton::map; - cfg.buttons_mod[11] = SDLContext::JoystickButton::F6; cfg.buttons[12] = SDLContext::JoystickButton::merge; cfg.buttons[14] = SDLContext::JoystickButton::left; cfg.buttons[13] = SDLContext::JoystickButton::right; cfg.buttons[9] = SDLContext::JoystickButton::backspace; cfg.buttons_mod[5] = SDLContext::JoystickButton::escape; cfg.buttons_mod[9] = SDLContext::JoystickButton::F4; - cfg.buttons_mod[12] = SDLContext::JoystickButton::F7; - cfg.buttons_mod[0] = SDLContext::JoystickButton::F8; + cfg.buttons_mod[14] = SDLContext::JoystickButton::F6; + cfg.buttons_mod[2] = SDLContext::JoystickButton::F7; + cfg.buttons_mod[3] = SDLContext::JoystickButton::F8; cfg.buttons_mod[1] = SDLContext::JoystickButton::F9; - cfg.buttons_mod[2] = SDLContext::JoystickButton::F10; - cfg.buttons_mod[3] = SDLContext::JoystickButton::F11; + cfg.buttons_mod[0] = SDLContext::JoystickButton::F10; + cfg.buttons_mod[11] = SDLContext::JoystickButton::F11; cfg.buttons_mod[14] = SDLContext::JoystickButton::F12; cfg.buttons_mod[13] = SDLContext::JoystickButton::backspace; cfg.enabled = true; diff --git a/platform/sdl/sdl_context.cpp b/platform/sdl/sdl_context.cpp index c241c7a..185cdae 100644 --- a/platform/sdl/sdl_context.cpp +++ b/platform/sdl/sdl_context.cpp @@ -63,6 +63,12 @@ SDLContext::SDLContext() { if (!init_context.inited) throw std::runtime_error("SDL not inited"); } +void handle_sdl_error(const char *msg) { + char buff[512]; + + snprintf(buff, sizeof(buff), "SDL critical error (check video driver): %s %s",msg, SDL_GetError()); + throw std::runtime_error(buff); +} void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** texture, int width, int height, CrtFilterType type) { @@ -78,20 +84,27 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur } // Vytvoř novou texturu ve správné velikosti *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height); + if (!*texture) { + type = CrtFilterType::none; + return; //crt filter failed to create, do not use filter + } SDL_SetTextureBlendMode(*texture, SDL_BLENDMODE_MUL); // Zamkni texturu pro přímý přístup k pixelům void* pixels; int pitch; - SDL_LockTexture(*texture, nullptr, &pixels, &pitch); + if (SDL_LockTexture(*texture, nullptr, &pixels, &pitch)<0) { + SDL_DestroyTexture(*texture); + *texture = nullptr; + type = CrtFilterType::none; + return; + } Uint32* pixelArray = (Uint32*)pixels; if (type == CrtFilterType::scanlines) { - - Uint32 darkPixel = 0xA0A0A0FF; Uint32 transparentPixel = 0xFFFFFFC0; @@ -152,10 +165,19 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur SDL_UnlockTexture(*texture); } +static void crash_sdl_exception() { + try { + throw; + } catch (std::exception &e) { + display_error("Display server - unhandled exception: %s", e.what()); + } catch (...) { + display_error("Display server - unhandled unknown exception (probably crash)"); + } + abort(); +} void SDLContext::init_video(const VideoConfig &config, const char *title) { - char buff[256]; static Uint32 update_request_event = SDL_RegisterEvents(1); static Uint32 refresh_request_event = SDL_RegisterEvents(1); _update_request_event = update_request_event; @@ -187,15 +209,13 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { width, height, SDL_WINDOW_RESIZABLE|(_fullscreen_mode?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); if (!window) { - snprintf(buff, sizeof(buff), "SDL Error create window: %s\n", SDL_GetError()); - throw std::runtime_error(buff); + handle_sdl_error("SDL Error create window"); } _window.reset(window); SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, config.composer); if (!renderer) { - snprintf(buff,sizeof(buff), "Failed to create composer: %s\n", SDL_GetError()); - throw std::runtime_error(buff); + handle_sdl_error("Failed to create composer"); } SDL_RendererInfo rinfo; @@ -212,15 +232,14 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { _renderer.reset(renderer); SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB1555, SDL_TEXTUREACCESS_STREAMING, 640, 480); if (!texture) { - snprintf(buff, sizeof(buff), "Chyba při vytváření textury: %s\n", SDL_GetError()); - throw std::runtime_error(buff); + handle_sdl_error("Failed to create render target"); } + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); _texture.reset(texture); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB1555, SDL_TEXTUREACCESS_STREAMING, 640, 480); if (!texture) { - snprintf(buff, sizeof(buff), "Chyba při vytváření textury: %s\n", SDL_GetError()); - throw std::runtime_error(buff); + handle_sdl_error("Failed to create second render target"); } SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); _texture2.reset(texture); @@ -232,8 +251,16 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { } done = true; done.notify_all(); - SDL_ShowCursor(SDL_DISABLE); - if (!err) event_loop(stp); + + if (!err) { + try { + SDL_ShowCursor(SDL_DISABLE); + event_loop(stp); + } catch (...) { + SDL_ShowCursor(SDL_ENABLE); + crash_sdl_exception(); + } + } _texture.reset(); _texture2.reset(); _renderer.reset(); @@ -648,7 +675,7 @@ void SDLContext::update_screen(bool force_refresh) { SDL_Rect r; pop_item(iter, r); std::string_view data = pop_data(iter, r.w*r.h*2); - SDL_UpdateTexture(_texture.get(), &r, data.data(), r.w*2); + if (SDL_UpdateTexture(_texture.get(), &r, data.data(), r.w*2)<0) handle_sdl_error("Update of render target failed"); } break; case DisplayRequest::show_mouse_cursor: { @@ -656,10 +683,11 @@ void SDLContext::update_screen(bool force_refresh) { pop_item(iter, r); std::string_view data = pop_data(iter, r.w*r.h*2); _mouse.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_ARGB1555,SDL_TEXTUREACCESS_STATIC, r.w, r.h)); + if (!_mouse) handle_sdl_error("Failed to create surface for mouse cursor"); SDL_SetTextureBlendMode(_mouse.get(), SDL_BLENDMODE_BLEND); _mouse_rect.w = r.w; _mouse_rect.h = r.h; - SDL_UpdateTexture(_mouse.get(), NULL, data.data(), r.w*2); + if (SDL_UpdateTexture(_mouse.get(), NULL, data.data(), r.w*2)<0) handle_sdl_error("Update of mouse cursor failed"); } break; case DisplayRequest::hide_mouse_cursor: { @@ -697,8 +725,9 @@ void SDLContext::update_screen(bool force_refresh) { iter = _sprites.insert(iter,{id}); } iter->_txtr.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_ARGB1555, SDL_TEXTUREACCESS_STATIC,r.w, r.h)); + if (!iter->_txtr) handle_sdl_error("Failed to create compositor sprite"); SDL_SetTextureBlendMode(iter->_txtr.get(), SDL_BLENDMODE_BLEND); - SDL_UpdateTexture(iter->_txtr.get(), NULL, data.data(), r.w*2); + if (SDL_UpdateTexture(iter->_txtr.get(), NULL, data.data(), r.w*2)<0) handle_sdl_error("Update of sprite failed"); iter->_rect = r; update_zindex(); } break; @@ -947,13 +976,7 @@ void SDLContext::close_audio() { void SDLContext::set_window_icon(const void *icon_data, size_t icon_size) { SDL_Surface *surface = SDL_LoadBMP_RW(SDL_RWFromConstMem(icon_data, icon_size), 1); - if (surface == 0) { - char buff[256]; - snprintf(buff,sizeof(buff),"Can't load icon: %s", SDL_GetError()); - display_error(buff); - std::ofstream x("test.dat", std::ios::out|std::ios::binary|std::ios::trunc); - x.write(reinterpret_cast(icon_data), icon_size); - } else { + if (surface) { SDL_SetWindowIcon(_window.get(), surface); } } diff --git a/platform/sdl/sound.cpp b/platform/sdl/sound.cpp index d59c495..6c2c562 100644 --- a/platform/sdl/sound.cpp +++ b/platform/sdl/sound.cpp @@ -50,10 +50,20 @@ void game_sound_init_device(const INI_CONFIG_SECTION *audio_section) { } static void SDLCALL mixing_callback(void *userdata, Uint8 * stream, int len) { - float *s = reinterpret_cast(stream); - int samples = len/8; - std::fill(s, s+2*samples, 0.0f); - sound_mixer.mix_to_buffer(s, samples); + static char crashed = 0; + float *s = reinterpret_cast(stream); + int samples = len/8; + std::fill(s, s+2*samples, 0.0f); + if (crashed) return; + try { + sound_mixer.mix_to_buffer(s, samples); + } catch (std::exception &e) { + crashed = 1; + display_error("Unhandled exception in sound mixer: %s", e.what()); + } catch (...) { + crashed = 1; + display_error("Crash in sound mixer: %s"); + } } char start_mixing() { diff --git a/platform/windows/app_start.cpp b/platform/windows/app_start.cpp index 19282c2..8ecc5a9 100644 --- a/platform/windows/app_start.cpp +++ b/platform/windows/app_start.cpp @@ -85,6 +85,10 @@ int main(int argc, char **argv) { cfg.show_error(e.what()); return 1; } + catch (...) { + cfg.show_error("Uknown error or crash"); + return 1; + } return 0;