From c2ff6de675e845dc54470c20e058d9482afdcdd9 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Thu, 24 Dec 2020 16:52:29 +0900 Subject: [PATCH] Stop message skip on unseen text This makes System3-sdl2 stop message skip when it sees text that has not been read by the player. Seen / unseen flags are stored in MSGSKIP.DAT file in the save folder. --- src/CMakeLists.txt | 1 + src/android/nact_android.cpp | 4 ++ src/emscripten/nact_emscripten.cpp | 4 ++ src/linux/nact_linux.cpp | 4 ++ src/sys/msgskip.cpp | 81 ++++++++++++++++++++++++++++++ src/sys/msgskip.h | 24 +++++++++ src/sys/nact.cpp | 15 +++--- src/sys/nact.h | 4 +- src/sys/nact_sys1.cpp | 3 +- src/sys/nact_sys2.cpp | 3 +- src/sys/nact_sys3.cpp | 3 +- src/win/nact_win.cpp | 15 ++++-- 12 files changed, 146 insertions(+), 15 deletions(-) create mode 100644 src/sys/msgskip.cpp create mode 100644 src/sys/msgskip.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a98a864..55b4019 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -93,6 +93,7 @@ target_sources(system3 PRIVATE sys/ags_window.cpp sys/dri.cpp sys/mako_midi.cpp + sys/msgskip.cpp sys/nact.cpp sys/nact_crc32.cpp sys/nact_input.cpp diff --git a/src/android/nact_android.cpp b/src/android/nact_android.cpp index d303198..dd7e83f 100644 --- a/src/android/nact_android.cpp +++ b/src/android/nact_android.cpp @@ -47,6 +47,10 @@ void NACT::output_console(const char *format, ...) #endif } +void NACT::set_skip_menu_state(bool enabled, bool checked) +{ +} + bool NACT::handle_platform_event(const SDL_Event& e) { if (e.type == SDL_FINGERDOWN && SDL_GetNumTouchFingers(e.tfinger.touchId) == 3) { diff --git a/src/emscripten/nact_emscripten.cpp b/src/emscripten/nact_emscripten.cpp index d552e09..aafb2bd 100644 --- a/src/emscripten/nact_emscripten.cpp +++ b/src/emscripten/nact_emscripten.cpp @@ -42,6 +42,10 @@ void NACT::output_console(const char *format, ...) #endif } +void NACT::set_skip_menu_state(bool enabled, bool checked) +{ +} + bool NACT::handle_platform_event(const SDL_Event& e) { return false; diff --git a/src/linux/nact_linux.cpp b/src/linux/nact_linux.cpp index 537f2c3..eb86efa 100644 --- a/src/linux/nact_linux.cpp +++ b/src/linux/nact_linux.cpp @@ -24,6 +24,10 @@ void NACT::output_console(const char *format, ...) #endif } +void NACT::set_skip_menu_state(bool enabled, bool checked) +{ +} + bool NACT::handle_platform_event(const SDL_Event& e) { return false; diff --git a/src/sys/msgskip.cpp b/src/sys/msgskip.cpp new file mode 100644 index 0000000..ddfdd88 --- /dev/null +++ b/src/sys/msgskip.cpp @@ -0,0 +1,81 @@ +#include "msgskip.h" +#include "nact.h" +#include "fileio.h" + +namespace { + +// Parameters for Bloom filter that can contain 47,543 items with 0.5% false +// positive rate. +const int BLOOM_FILTER_SIZE = 1 << 19; // 64KiB +const int BLOOM_FILTER_HASHES = 8; + +const char MSGSKIP_FILENAME[] = "MSGSKIP.DAT"; + +// Knuth's multiplicative hash. +inline uint32 hash(uint32 x, int s) { + return (x * 2654435761ULL) >> s; +} + +} // namespace + +MsgSkip::MsgSkip(NACT* nact) + : nact(nact), + bloom(new uint8[BLOOM_FILTER_SIZE / 8]), + skip_enabled(false), + menu_enabled(true) +{ + memset(bloom, 0, BLOOM_FILTER_SIZE / 8); + FILEIO fio; + if (fio.Fopen(MSGSKIP_FILENAME, FILEIO_READ_BINARY | FILEIO_SAVEDATA)) { + uint32 size = fio.Fgetw(); + size |= fio.Fgetw() << 16; + if (size == BLOOM_FILTER_SIZE) + fio.Fread(bloom, BLOOM_FILTER_SIZE / 8, 1); + fio.Fclose(); + } +} + +MsgSkip::~MsgSkip() +{ + FILEIO fio; + if (fio.Fopen(MSGSKIP_FILENAME, FILEIO_WRITE_BINARY | FILEIO_SAVEDATA)) { + fio.Fputw(BLOOM_FILTER_SIZE & 0xffff); + fio.Fputw(BLOOM_FILTER_SIZE >> 16); + fio.Fwrite(bloom, BLOOM_FILTER_SIZE / 8, 1); + fio.Fclose(); + } + delete[] bloom; +} + +void MsgSkip::enable_skip(bool enable) { + if (skip_enabled != enable) { + skip_enabled = enable; + nact->set_skip_menu_state(menu_enabled, skip_enabled); + } +} + +void MsgSkip::on_message(int page, int addr) +{ + if (!bloom) + return; + uint32 h1 = hash(page, 5); + uint32 h2 = hash(addr, 11); + uint8 unseen = 0; + for (int i = 0; i < BLOOM_FILTER_HASHES; i++) { + uint32 h = (h1 + i * h2) % BLOOM_FILTER_SIZE; + uint8 bit = 1 << (h & 7); + unseen |= (bloom[h >> 3] & bit) ^ bit; + bloom[h >> 3] |= bit; + } + if (unseen) { + if (skip_enabled || menu_enabled) { + skip_enabled = menu_enabled = false; + nact->set_skip_menu_state(menu_enabled, skip_enabled); + } + } else { + if (!menu_enabled) { + menu_enabled = true; + nact->set_skip_menu_state(menu_enabled, skip_enabled); + } + } +} diff --git a/src/sys/msgskip.h b/src/sys/msgskip.h new file mode 100644 index 0000000..d0226d7 --- /dev/null +++ b/src/sys/msgskip.h @@ -0,0 +1,24 @@ +#ifndef _MSGSKIP_H_ +#define _MSGSKIP_H_ + +#include "common.h" + +class NACT; + +class MsgSkip { + public: + MsgSkip(NACT* nact); + ~MsgSkip(); + + void enable_skip(bool enable); + bool is_skip_enabled() const { return skip_enabled; } + void on_message(int page, int addr); + + private: + NACT* nact; + uint8* bloom; + bool skip_enabled; + bool menu_enabled; +}; + +#endif diff --git a/src/sys/nact.cpp b/src/sys/nact.cpp index 325b1c6..b60b8b2 100644 --- a/src/sys/nact.cpp +++ b/src/sys/nact.cpp @@ -8,6 +8,7 @@ #include "nact.h" #include "ags.h" #include "mako.h" +#include "msgskip.h" #include "dri.h" #include "../fileio.h" #include "crc32.h" @@ -67,7 +68,6 @@ NACT::NACT(int sys_ver, uint32 crc32_a, uint32 crc32_b, const char* font_file, c seed = SDL_GetTicks(); // 乱数の種 text_wait_time = 100; // メッセージ表示のウェイト text_wait_enb = false; - text_skip_enb = false; mouse_sence = 16; // マウス移動感知 menu_window = text_window = 1; @@ -86,6 +86,7 @@ NACT::NACT(int sys_ver, uint32 crc32_a, uint32 crc32_b, const char* font_file, c // 各種クラス生成 ags = new AGS(this, font_file); mako = new MAKO(this, mako_config); + msgskip = new MsgSkip(this); #ifdef USE_JOY // 入力初期化 @@ -101,13 +102,9 @@ NACT::NACT(int sys_ver, uint32 crc32_a, uint32 crc32_b, const char* font_file, c NACT::~NACT() { - // 各種クラス開放 - if(ags) { - delete ags; - } - if(mako) { - delete mako; - } + delete ags; + delete mako; + delete msgskip; // シナリオ開放 if(scenario_data) { @@ -318,6 +315,8 @@ void NACT::message() text_refresh = false; } } + if (!ags->draw_menu) + msgskip->on_message(scenario_page, scenario_addr); // TODO: Convert hankaku to zenkaku output_console(buf); diff --git a/src/sys/nact.h b/src/sys/nact.h index bf9a7c0..070f764 100644 --- a/src/sys/nact.h +++ b/src/sys/nact.h @@ -83,6 +83,7 @@ constexpr char SJIS_NEXT_PAGE [] = "\x8E\x9F\x82\xCC\x83\x79\x81\x5B\x83\x57"; / class AGS; class MAKO; class MAKOConfig; +class MsgSkip; class NACT { @@ -94,6 +95,7 @@ public: protected: AGS* ags; MAKO* mako; + MsgSkip* msgskip; // コマンドパーサ void execute(); @@ -251,8 +253,8 @@ public: static const int get_sys_ver(uint32 crc32_a, uint32 crc32_b); const char* get_title(); void text_wait(); + void set_skip_menu_state(bool enabled, bool checked); - bool text_skip_enb; // メッセージスキップ bool text_wait_enb; // テキスト表示のウェイト有効/無効 int sys_ver; uint32 crc32_a; // ADISK diff --git a/src/sys/nact_sys1.cpp b/src/sys/nact_sys1.cpp index 5df7f7f..f5dc05d 100644 --- a/src/sys/nact_sys1.cpp +++ b/src/sys/nact_sys1.cpp @@ -14,6 +14,7 @@ #include "nact.h" #include "ags.h" #include "mako.h" +#include "msgskip.h" #include "crc32.h" #include "../fileio.h" @@ -784,7 +785,7 @@ void NACT_Sys1::cmd_a() { output_console("A\n"); - if(!text_skip_enb) { + if(!msgskip->is_skip_enabled()) { // Pushマークの表示 if(show_push) { ags->draw_push(text_window); diff --git a/src/sys/nact_sys2.cpp b/src/sys/nact_sys2.cpp index ee4a018..0e0cf7e 100644 --- a/src/sys/nact_sys2.cpp +++ b/src/sys/nact_sys2.cpp @@ -9,6 +9,7 @@ #include "nact.h" #include "ags.h" #include "mako.h" +#include "msgskip.h" #include "crc32.h" #include "../fileio.h" @@ -748,7 +749,7 @@ void NACT_Sys2::cmd_a() { output_console("A\n"); - if(!text_skip_enb) { + if(!msgskip->is_skip_enabled()) { // Pushマークの表示 if(show_push) { ags->draw_push(text_window); diff --git a/src/sys/nact_sys3.cpp b/src/sys/nact_sys3.cpp index d4a73e4..4ce1a74 100644 --- a/src/sys/nact_sys3.cpp +++ b/src/sys/nact_sys3.cpp @@ -9,6 +9,7 @@ #include "nact.h" #include "ags.h" #include "mako.h" +#include "msgskip.h" #include "crc32.h" #include "../fileio.h" #include "texthook.h" @@ -373,7 +374,7 @@ void NACT_Sys3::cmd_a() output_console("A\n"); - if(!text_skip_enb) { + if(!msgskip->is_skip_enabled()) { // Pushマークの表示 if(show_push) { ags->draw_push(text_window); diff --git a/src/win/nact_win.cpp b/src/win/nact_win.cpp index 25935dd..a9540d7 100644 --- a/src/win/nact_win.cpp +++ b/src/win/nact_win.cpp @@ -4,6 +4,7 @@ #include "SDL_syswm.h" #include "ags.h" #include "mako.h" +#include "msgskip.h" #include "../res3/resource.h" extern SDL_Window* g_window; @@ -130,6 +131,16 @@ void NACT::output_console(const char *format, ...) #endif } +void NACT::set_skip_menu_state(bool enabled, bool checked) +{ + HWND hwnd = get_hwnd(g_window); + HMENU hmenu = GetMenu(hwnd); + EnableMenuItem(hmenu, ID_TEXT_SKIP, enabled ? MF_ENABLED : MF_GRAYED); + CheckMenuItem(hmenu, ID_TEXT_SKIP, MF_BYCOMMAND | + checked ? MFS_CHECKED : MFS_UNCHECKED); + DrawMenuBar(hwnd); +} + bool NACT::handle_platform_event(const SDL_Event& e) { if (e.type != SDL_SYSWMEVENT) @@ -163,9 +174,7 @@ bool NACT::handle_platform_event(const SDL_Event& e) mako->select_sound(BGM_CD); break; case ID_TEXT_SKIP: - text_skip_enb = !text_skip_enb; - CheckMenuItem(GetMenu(get_hwnd(g_window)), ID_TEXT_SKIP, MF_BYCOMMAND | - text_skip_enb ? MFS_CHECKED : MFS_UNCHECKED); + msgskip->enable_skip(!msgskip->is_skip_enabled()); break; case ID_TEXT_WAIT: text_wait_enb = !text_wait_enb;