From 27af072b0d8d4693bb7356c3e7f4d5aa6be923fe Mon Sep 17 00:00:00 2001 From: Will Toohey Date: Wed, 23 Jun 2021 09:32:21 +1000 Subject: [PATCH] v2.2: fix winxp, support ifs-in-ifs --- layeredfs/hook.cpp | 11 +++---- layeredfs/layeredfs.vcxproj | 3 ++ layeredfs/modpath_handler.cpp | 5 +-- layeredfs/ramfs_demangler.cpp | 43 ++++++++++++++++++++------ layeredfs/utils.cpp | 23 +++++++++++--- layeredfs/utils.h | 1 + layeredfs/winxp_mutex.hpp | 58 +++++++++++++++++++++++++++++++++++ 7 files changed, 121 insertions(+), 23 deletions(-) create mode 100644 layeredfs/winxp_mutex.hpp diff --git a/layeredfs/hook.cpp b/layeredfs/hook.cpp index e89358a..f8b96fd 100644 --- a/layeredfs/hook.cpp +++ b/layeredfs/hook.cpp @@ -16,7 +16,6 @@ using std::string; #include #include #include -#include #include "3rd_party/MinHook.h" #pragma comment(lib, "minhook.lib") @@ -33,12 +32,13 @@ using std::string; //#include "jubeat.h" #include "texture_packer.h" #include "modpath_handler.h" +#include "winxp_mutex.hpp" // let me use the std:: version, damnit #undef max #undef min -#define VER_STRING "2.1" +#define VER_STRING "2.2" #ifdef _DEBUG #define DBG_VER_STRING "_DEBUG" @@ -77,7 +77,7 @@ typedef struct image { // ifs_textures["data/graphics/ver04/logo.ifs/tex/4f754d4f424f092637a49a5527ece9bb"] will be "konami" static std::unordered_map ifs_textures; -static std::mutex ifs_textures_mtx; +static CriticalSectionLock ifs_textures_mtx; typedef std::unordered_set string_set; @@ -673,9 +673,8 @@ AVS_FILE hook_avs_fs_open(const char* name, uint16_t mode, int flags) { auto norm_path = *_norm_path; auto mod_path = find_first_modfile(norm_path); - if (!mod_path) { - // mod ifs paths use _ifs - string_replace(norm_path, ".ifs", "_ifs"); + // mod ifs paths use _ifs, go one at a time for ifs-inside-ifs + while (!mod_path && string_replace_first(norm_path, ".ifs", "_ifs")) { mod_path = find_first_modfile(norm_path); } diff --git a/layeredfs/layeredfs.vcxproj b/layeredfs/layeredfs.vcxproj index 236ea32..09fb077 100644 --- a/layeredfs/layeredfs.vcxproj +++ b/layeredfs/layeredfs.vcxproj @@ -126,6 +126,7 @@ None MultiThreaded stdcpp17 + /Zc:threadSafeInit- %(AdditionalOptions) Windows @@ -146,6 +147,7 @@ MultiThreaded None stdcpp17 + /Zc:threadSafeInit- Windows @@ -181,6 +183,7 @@ + diff --git a/layeredfs/modpath_handler.cpp b/layeredfs/modpath_handler.cpp index 570e87f..bbf9f61 100644 --- a/layeredfs/modpath_handler.cpp +++ b/layeredfs/modpath_handler.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include "ramfs_demangler.h" @@ -10,6 +9,7 @@ #include "config.hpp" #include "utils.h" #include "avs.h" +#include "winxp_mutex.hpp" using std::nullopt; @@ -80,7 +80,7 @@ void cache_mods(void) { // data, data2, data_op2 etc // data is "flat", all others must have their own special subfolders static vector game_folders; -static std::mutex game_folders_mtx; +static CriticalSectionLock game_folders_mtx; optional normalise_path(string &path) { // one-off init @@ -228,6 +228,7 @@ optional find_first_cached_item(const string &norm_path) { } optional find_first_modfile(const string &norm_path) { + //logf_verbose("%s(%s)", __FUNCTION__, norm_path.c_str()); if (config.developer_mode) { for (auto &dir : available_mods()) { auto mod_path = dir + "/" + norm_path; diff --git a/layeredfs/ramfs_demangler.cpp b/layeredfs/ramfs_demangler.cpp index 7b4dca2..9cdd764 100644 --- a/layeredfs/ramfs_demangler.cpp +++ b/layeredfs/ramfs_demangler.cpp @@ -32,13 +32,13 @@ #include #include -#include #include #include "3rd_party/hat-trie/htrie_map.h" #include "ramfs_demangler.h" #include "utils.h" +#include "winxp_mutex.hpp" using namespace std; @@ -56,14 +56,17 @@ static unordered_map ram_load_map; static tsl::htrie_map ramfs_map; static tsl::htrie_map mangling_map; -static std::mutex mangling_mtx; +static CriticalSectionLock mangling_mtx; + +// since we call this from a function that is already taking the lock +static void ramfs_demangler_demangle_if_possible_nolock(std::string& raw_path); void ramfs_demangler_on_fs_open(const std::string& norm_path, AVS_FILE open_result) { if (open_result < 0 || !string_ends_with(norm_path.c_str(), ".ifs")) { return; } - const std::lock_guard lock(mangling_mtx); + mangling_mtx.lock(); auto existing_info = cleanup_map.find(norm_path); if (existing_info != cleanup_map.end()) { @@ -89,10 +92,12 @@ void ramfs_demangler_on_fs_open(const std::string& norm_path, AVS_FILE open_resu }; cleanup_map[norm_path] = cleanup; open_file_map[open_result] = norm_path; + + mangling_mtx.unlock(); } void ramfs_demangler_on_fs_read(AVS_FILE context, void* dest) { - const std::lock_guard lock(mangling_mtx); + mangling_mtx.lock(); auto find = open_file_map.find(context); if (find != open_file_map.end()) { @@ -106,26 +111,30 @@ void ramfs_demangler_on_fs_read(AVS_FILE context, void* dest) { cleanup->second.buffer = dest; } } + + mangling_mtx.unlock(); } void ramfs_demangler_on_fs_mount(const char* mountpoint, const char* fsroot, const char* fstype, const char* flags) { - const std::lock_guard lock(mangling_mtx); + mangling_mtx.lock(); if (!strcmp(fstype, "ramfs")) { void* buffer; if (!flags) { logf_verbose("ramfs has no flags?"); + mangling_mtx.unlock(); return; } const char* baseptr = strstr(flags, "base="); if (!baseptr) { logf_verbose("ramfs has no base pointer?"); + mangling_mtx.unlock(); return; } buffer = (void*)strtoull(baseptr + strlen("base="), NULL, 0); - + auto find = ram_load_map.find(buffer); if (find != ram_load_map.end()) { auto orig_path = find->second; @@ -152,18 +161,32 @@ void ramfs_demangler_on_fs_mount(const char* mountpoint, const char* fsroot, con } } else if(string_ends_with(fsroot, ".ifs")) { - //logf_verbose("imagefs mount mapped to %s", fsroot); - mangling_map[mountpoint] = (string)fsroot; + // this fixes ifs-inside-ifs by demangling the root location too + string root = (string)fsroot; + ramfs_demangler_demangle_if_possible_nolock(root); + logf_verbose("imagefs mount mapped to %s", root.c_str()); + mangling_map[mountpoint] = root; } } + + mangling_mtx.unlock(); } void ramfs_demangler_demangle_if_possible(std::string& raw_path) { - const std::lock_guard lock(mangling_mtx); + mangling_mtx.lock(); auto search = mangling_map.longest_prefix(raw_path); if (search != mangling_map.end()) { //logf_verbose("can demangle %s to %s", search.key().c_str(), search->c_str()); string_replace(raw_path, search.key().c_str(), search->c_str()); } -} \ No newline at end of file + + mangling_mtx.unlock(); +} + +static void ramfs_demangler_demangle_if_possible_nolock(std::string& raw_path) { + auto search = mangling_map.longest_prefix(raw_path); + if (search != mangling_map.end()) { + string_replace(raw_path, search.key().c_str(), search->c_str()); + } +} diff --git a/layeredfs/utils.cpp b/layeredfs/utils.cpp index e5cf41d..e974e43 100644 --- a/layeredfs/utils.cpp +++ b/layeredfs/utils.cpp @@ -1,12 +1,11 @@ #include "utils.h" #include "avs.h" - -#include +#include "winxp_mutex.hpp" #define SUPPRESS_PRINTF void logf(char* fmt, ...) { - static std::mutex log_mutex; + static CriticalSectionLock log_mutex; static FILE* logfile = NULL; static bool tried_to_open = false; va_list args; @@ -18,15 +17,16 @@ void logf(char* fmt, ...) { #endif // don't reopen every time: slow as shit if (!tried_to_open) { - const std::lock_guard lock(log_mutex); + log_mutex.lock(); if (!logfile) { fopen_s(&logfile, "ifs_hook.log", "w"); } tried_to_open = true; + log_mutex.unlock(); } if (logfile) { - const std::lock_guard lock(log_mutex); + log_mutex.lock(); va_start(args, fmt); vfprintf(logfile, fmt, args); @@ -35,6 +35,8 @@ void logf(char* fmt, ...) { if(config.developer_mode) fflush(logfile); + + log_mutex.unlock(); } } @@ -81,6 +83,17 @@ void string_replace(std::string &str, const char* from, const char* to) { } } +bool string_replace_first(std::string& str, const char* from, const char* to) { + auto pos = str.find(from); + if (pos == std::string::npos) { + return false; + } + + str.replace(pos, strlen(from), to); + + return true; +} + wchar_t *str_widen(const char *src) { int nchars; diff --git a/layeredfs/utils.h b/layeredfs/utils.h index b77c03c..d1966cc 100644 --- a/layeredfs/utils.h +++ b/layeredfs/utils.h @@ -20,6 +20,7 @@ void logf(char* fmt, ...); char* snprintf_auto(const char* fmt, ...); int string_ends_with(const char * str, const char * suffix); void string_replace(std::string &str, const char* from, const char* to); +bool string_replace_first(std::string &str, const char* from, const char* to); wchar_t *str_widen(const char *src); void str_tolower_inline(char* str); void str_tolower_inline(std::string &str); diff --git a/layeredfs/winxp_mutex.hpp b/layeredfs/winxp_mutex.hpp new file mode 100644 index 0000000..8734d4c --- /dev/null +++ b/layeredfs/winxp_mutex.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +// This class is a lightweight replacement for std::mutex on Windows platforms. +// std::mutex does not work on Windows XP SP2 with the latest VC++ libraries, +// because it utilizes the Concurrency Runtime that is only supported on Windows +// XP SP3 and above. + +// mon addition: avoid std::lock_guard. It uses thread local storage and is just, in general, pain to compile properly. +// This sucks, because RAII is awesome. + +class CriticalSectionLock { +public: + CriticalSectionLock() { InitializeCriticalSection(&critical_section_); } + ~CriticalSectionLock() { DeleteCriticalSection(&critical_section_); } + void lock() { EnterCriticalSection(&critical_section_); } + void unlock() { LeaveCriticalSection(&critical_section_); } + +private: + CRITICAL_SECTION critical_section_; +}; + +/** +Copyright 2008 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + +**/ \ No newline at end of file