diff --git a/src/spice2x/hooks/avshook.cpp b/src/spice2x/hooks/avshook.cpp index 0fc1cdc..d58e618 100644 --- a/src/spice2x/hooks/avshook.cpp +++ b/src/spice2x/hooks/avshook.cpp @@ -1,7 +1,10 @@ #include "avshook.h" +#include +#include #include #include +#include #include "avs/core.h" #include "avs/ea3.h" @@ -20,8 +23,12 @@ static bool FAKE_FILE_OPEN = false; static std::map ROM_FILE_MAP; static std::string *ROM_FILE_CONTENTS = nullptr; +static std::mutex lstat_cache_mutex; +static robin_hood::unordered_map> lstat_cache; + namespace hooks::avs::config { bool DISABLE_VFS_DRIVE_REDIRECTION = false; + bool DISABLE_CACHE = false; bool LOG = false; }; @@ -79,6 +86,74 @@ static bool is_spam_file(const char *file) { return false; } +static bool is_ldj_cacheable_file(const char *file) { + static const char *spam_prefixes[] = { + // iidx33: profile background customization, spams on every frame during card entry + "/data/graphic/entry_card", + // iidx32+: movie thumbnails, spams on every frame during song select + "/data/graphic/thumbnail/", + "/data/graphic/movie_thumbnail/", + }; + + for (auto &spam : spam_prefixes) { + if (string_begins_with(file, spam)) { + return true; + } + } + return false; +} + +static int cached_avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) { + assert(name != nullptr); + assert(st != nullptr); + + const std::string filename(name); + int value = 0; + std::lock_guard lock(lstat_cache_mutex); + + // if this was already seen, return cached result + if (lstat_cache.contains(filename)) { + const auto &result = lstat_cache.at(filename); + value = result.first; + memcpy(st, &result.second, sizeof(*st)); + WRAP_DEBUG_FMT( + "(cached) file={}, a={}, m={}, c={}, size={}, u={}", + name, + st->st_atime, + st->st_mtime, + st->st_ctime, + st->filesize, + st->unk1); + return value; + } + + // if not, call the original avs function + // this returns 1 on success with valid values in st + // return 0 on failure, with filesize set to 0 + // maybe this could be used for future optimization + value = avs::core::avs_fs_lstat(name, st); + WRAP_DEBUG_FMT( + "file={} a={}, m={}, c={}, size={}, u={}", + name, + st->st_atime, + st->st_mtime, + st->st_ctime, + st->filesize, + st->unk1); + + // and cache it if there is room (prevent unconstrained growth) + if (lstat_cache.size() <= 4096) { + // c++ was a mistake + lstat_cache.emplace( + std::piecewise_construct, + std::forward_as_tuple(filename), + std::forward_as_tuple(value, *st)); + } + + // done + return value; +} + static int avs_fs_fstat(avs::core::avs_file_t fd, struct avs::core::avs_stat *st) { if (is_fake_fd(fd) && ROM_FILE_CONTENTS) { if (st) { @@ -104,7 +179,12 @@ static int avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) { return 1; } - auto value = avs::core::avs_fs_lstat(name, st); + if (!hooks::avs::config::DISABLE_CACHE && (st != nullptr) && + avs::game::is_model("LDJ") && is_ldj_cacheable_file(name)) { + return cached_avs_fs_lstat(name, st); + } + + const auto value = avs::core::avs_fs_lstat(name, st); if (!is_spam_file(name)) { WRAP_DEBUG_FMT("name: {}", name); } diff --git a/src/spice2x/hooks/avshook.h b/src/spice2x/hooks/avshook.h index a0c83d3..de78a0c 100644 --- a/src/spice2x/hooks/avshook.h +++ b/src/spice2x/hooks/avshook.h @@ -9,6 +9,7 @@ namespace hooks::avs { namespace config { extern bool DISABLE_VFS_DRIVE_REDIRECTION; + extern bool DISABLE_CACHE; extern bool LOG; }; diff --git a/src/spice2x/launcher/launcher.cpp b/src/spice2x/launcher/launcher.cpp index 18d831e..e81f529 100644 --- a/src/spice2x/launcher/launcher.cpp +++ b/src/spice2x/launcher/launcher.cpp @@ -752,6 +752,9 @@ int main_implementation(int argc, char *argv[]) { if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) { hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true; } + if (options[launcher::Options::DisableAvsCache].is_active()) { + hooks::avs::config::DISABLE_CACHE = true; + } if (options[launcher::Options::ScreenResizeConfigPath].is_active()) { cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE = options[launcher::Options::ScreenResizeConfigPath].value_text(); diff --git a/src/spice2x/launcher/options.cpp b/src/spice2x/launcher/options.cpp index f3e22e8..778e216 100644 --- a/src/spice2x/launcher/options.cpp +++ b/src/spice2x/launcher/options.cpp @@ -1766,6 +1766,14 @@ static const std::vector OPTION_DEFINITIONS = { .type = OptionType::Bool, .category = "Development", }, + { + // DisableAvsCache + .title = "Disable AVS Cache", + .name = "avscachedisable", + .desc = "Disable optimization used for some games to cache data file info (only for IIDX32+ for now)", + .type = OptionType::Bool, + .category = "Development", + }, { .title = "Output PEB", .name = "pebprint", diff --git a/src/spice2x/launcher/options.h b/src/spice2x/launcher/options.h index 26d130b..6bed3fd 100644 --- a/src/spice2x/launcher/options.h +++ b/src/spice2x/launcher/options.h @@ -196,6 +196,7 @@ namespace launcher { DisableSignalHandling, DisableDebugHooks, DisableAvsVfsDriveMountRedirection, + DisableAvsCache, OutputPEB, DumpSystemInfo, QKSArgs,