From 3127602c573f1eccc497baf9606429f305220077 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Fri, 20 Aug 2010 12:56:46 +0200 Subject: [PATCH] Added file_finder component. Used by sound system. --- CMakeLists.txt | 12 ++++- apps/openmw/mwsound/soundmanager.cpp | 30 +++++++----- components/file_finder/file_finder.hpp | 62 ++++++++++++++++++++++++ components/file_finder/filename_less.hpp | 49 +++++++++++++++++++ components/file_finder/search.cpp | 28 +++++++++++ components/file_finder/search.hpp | 20 ++++++++ 6 files changed, 188 insertions(+), 13 deletions(-) create mode 100644 components/file_finder/file_finder.hpp create mode 100644 components/file_finder/filename_less.hpp create mode 100644 components/file_finder/search.cpp create mode 100644 components/file_finder/search.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b4d2900b17..ca21824b6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,14 @@ set(TO_UTF8_HEADER ${COMP_DIR}/to_utf8/to_utf8.hpp) source_group(components\\to_utf8 FILES ${TO_UTF8} ${TO_UTF8_HEADER}) +set(FILE_FINDER + ${COMP_DIR}/file_finder/search.cpp) +set(FILE_FINDER_HEADER + ${COMP_DIR}/file_finder/file_finder.hpp + ${COMP_DIR}/file_finder/filename_less.hpp + ${COMP_DIR}/file_finder/search.hpp) +source_group(components\\file_finder FILES ${FILE_FINDER} ${FILE_FINDER_HEADER}) + set(ESM_STORE ${COMP_DIR}/esm_store/store.cpp) set(ESM_STORE_HEADER @@ -82,10 +90,10 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp) source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER}) set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC} ${TO_UTF8} - ${COMPILER} ${INTERPRETER} ${ESM}) + ${COMPILER} ${INTERPRETER} ${ESM} ${FILE_FINDER}) set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER} ${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER} ${TO_UTF8_HEADER} - ${INTERPRETER_HEADER}) + ${INTERPRETER_HEADER} ${FILE_FINDER_HEADER}) # source directory: libs diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index 2c6ab6339a..262c7a9749 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -1,4 +1,3 @@ - #include "soundmanager.hpp" #include @@ -8,6 +7,7 @@ using namespace std; #include #include +#include #include #include #include @@ -74,12 +74,17 @@ namespace MWSound Mangle::Sound::OgreListenerMover cameraTracker; const ESMS::ESMStore &store; - std::string dir; typedef std::map IDMap; typedef std::map PtrMap; PtrMap sounds; + // This is used for case insensitive and slash-type agnostic file + // finding. It takes DOS paths (any case, \\ slashes or / slashes) + // relative to the sound dir, and translates them into full paths + // of existing files in the filesystem, if they exist. + FileFinder::FileFinder files; + SoundImpl(Ogre::Root *root, Ogre::Camera *camera, const ESMS::ESMStore &str, const std::string &soundDir) @@ -87,6 +92,7 @@ namespace MWSound , updater(mgr) , cameraTracker(mgr) , store(str) + , files(soundDir) { cout << "Sound output: " << SOUND_OUT << endl; cout << "Sound decoder: " << SOUND_IN << endl; @@ -96,21 +102,20 @@ namespace MWSound // Tell Ogre to update the sound system each frame root->addFrameListener(&updater); + } - dir = soundDir + "/"; + bool hasFile(const std::string &str) + { + return files.has(str); } // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) std::string convertPath(const std::string &str) { - std::string file = dir + str; -#ifndef WIN32 - // Actually / path separators should work in Windows too, they - // just aren't necessary. - std::replace(file.begin(), file.end(), '\\', '/'); -#endif - return file; + if(hasFile(str)) + return files.lookup(str); + return ""; } // Convert a soundId to file name, and modify the volume @@ -277,7 +282,10 @@ namespace MWSound { // The range values are not tested if(!mData) return; - mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 10000, false); + if(mData->hasFile(filename)) + mData->add(mData->convertPath(filename), ptr, "_say_sound", 1, 1, 100, 10000, false); + else + cout << "Sound file " << filename << " not found, skipping.\n"; } bool SoundManager::sayDone (MWWorld::Ptr ptr) const diff --git a/components/file_finder/file_finder.hpp b/components/file_finder/file_finder.hpp new file mode 100644 index 0000000000..eda73a85f1 --- /dev/null +++ b/components/file_finder/file_finder.hpp @@ -0,0 +1,62 @@ +#ifndef FILE_FINDER_MAIN_H +#define FILE_FINDER_MAIN_H + +#include "search.hpp" +#include "filename_less.hpp" +#include + +namespace FileFinder +{ + +class FileFinder +{ + std::map table; + + struct Inserter : ReturnPath + { + FileFinder *owner; + int cut; + + void add(const boost::filesystem::path &pth) + { + std::string file = pth.file_string(); + std::string key = file.substr(cut); + owner->table[key] = file; + } + }; + + Inserter inserter; + +public: + FileFinder(const boost::filesystem::path &path, bool recurse=true) + { + inserter.owner = this; + + // Remember the original path length, so we can cut it away from + // the relative paths used as keys + std::string pstring = path.file_string(); + inserter.cut = pstring.size(); + + // If the path does not end in a slash, then boost will add one + // later, which means one more character we have to remove. + char last = pstring[pstring.size()-1]; + if(last != '\\' && last != '/') + inserter.cut++; + + // Fill the map + find(path, inserter, recurse); + } + + bool has(const std::string& file) const + { + return table.find(file) != table.end(); + } + + // Find the full path from a relative path. + const std::string &lookup(const std::string& file) const + { + return table.find(file)->second; + } +}; +} +#endif diff --git a/components/file_finder/filename_less.hpp b/components/file_finder/filename_less.hpp new file mode 100644 index 0000000000..9f251b1c84 --- /dev/null +++ b/components/file_finder/filename_less.hpp @@ -0,0 +1,49 @@ +#ifndef FILE_FINDER_LESS_H +#define FILE_FINDER_LESS_H + +#include +#include + +namespace FileFinder{ + +// Used for maps of file paths. Compares file paths, but ignores case +// AND treats \ and / as the same character. +struct path_less +{ + int compareChar(char a, char b) const + { + if(a>b) return 1; + else if(a= 'a' && a <= 'z') a += 'A'-'a'; + else if(a == '\\') a = '/'; + if(b >= 'a' && b <= 'z') b += 'A'-'a'; + else if(b == '\\') b = '/'; + return compareChar(a,b); + } + + int compareString(const char *a, const char *b) const + { + while(*a && *b) + { + int i = comparePathChar(*a,*b); + if(i != 0) return i; + a++; b++; + } + // At this point, one or both of the chars is a null terminator. + // Normal char comparison will get the correct final result here. + return compareChar(*a,*b); + } + + bool operator() (const std::string& a, const std::string& b) const + { + return compareString(a.c_str(), b.c_str()) < 0; + } +}; + +} +#endif diff --git a/components/file_finder/search.cpp b/components/file_finder/search.cpp new file mode 100644 index 0000000000..b05b30e835 --- /dev/null +++ b/components/file_finder/search.cpp @@ -0,0 +1,28 @@ +#include "search.hpp" + +#include + +using namespace std; +using namespace boost::filesystem; + +void FileFinder::find(const path & dir_path, ReturnPath &ret, bool recurse) +{ + if ( !exists( dir_path ) ) + { + cout << "Path " << dir_path << " not found\n"; + return; + } + + directory_iterator end_itr; // default construction yields past-the-end + for ( directory_iterator itr(dir_path); + itr != end_itr; + ++itr ) + { + if ( is_directory( *itr ) ) + { + if(recurse) find(*itr, ret); + } + else + ret.add(*itr); + } +} diff --git a/components/file_finder/search.hpp b/components/file_finder/search.hpp new file mode 100644 index 0000000000..4e16fb64a0 --- /dev/null +++ b/components/file_finder/search.hpp @@ -0,0 +1,20 @@ +#ifndef FILE_FINDER_SEARCH_H +#define FILE_FINDER_SEARCH_H + +#include +#include + +namespace FileFinder +{ + struct ReturnPath + { + virtual void add(const boost::filesystem::path &pth) = 0; + }; + + /** Search the given path and return all file paths through 'ret'. If + recurse==true, all files in subdirectories are returned as well. + */ + void find(const boost::filesystem::path & dir_path, ReturnPath &ret, bool recurse=true); +} + +#endif