1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-07 03:12:36 +01:00

Replace findPathRealCase with FileIndex methods

This introduces indexGameDirectory and findFilePath.
indexGameDirectory indexes the files in the game directory.
findFilePath returns the on-disk location of a case-insensitive
game data path.
This commit is contained in:
Daniel Evans 2016-08-28 16:23:34 +01:00
parent 772c93d463
commit 674ce55af3
4 changed files with 99 additions and 45 deletions

View File

@ -49,6 +49,7 @@ GameData::~GameData()
void GameData::load()
{
index.indexGameDirectory(datpath);
index.indexTree(datpath);
parseDAT(datpath+"/data/default.dat");
@ -109,9 +110,9 @@ void GameData::parseDAT(const std::string& path)
}
else if(cmd == "IPL")
{
std::string fixedpath = fixPath(line.substr(space+1));
fixedpath = findPathRealCase(datpath, fixedpath);
loadIPL(fixedpath);
auto path = line.substr(space+1);
auto systempath = index.findFilePath(path);
loadIPL(systempath.native());
}
else if(cmd == "TEXDICTION")
{
@ -182,10 +183,9 @@ void GameData::loadCOL(const size_t zone, const std::string& name)
LoaderCOL col;
std::string realPath = fixPath(name);
realPath = findPathRealCase(datpath, realPath);
auto systempath = index.findFilePath(name).native();
if(col.load(realPath)) {
if(col.load(systempath)) {
for( size_t i = 0; i < col.instances.size(); ++i ) {
collisions[col.instances[i]->name] = std::move(col.instances[i]);
}
@ -434,13 +434,13 @@ void GameData::loadWeaponDAT(const std::string &name)
bool GameData::loadAudioStream(const std::string &name)
{
auto filePath = findPathRealCase(datpath + "/audio/", name);
auto systempath = index.findFilePath("audio/" + name).native();
if (engine->cutsceneAudio.length() > 0) {
engine->sound.stopMusic(engine->cutsceneAudio);
}
if (engine->sound.loadMusic(name, filePath)) {
if (engine->sound.loadMusic(name, systempath)) {
engine->cutsceneAudio = name;
return true;
}
@ -450,18 +450,18 @@ bool GameData::loadAudioStream(const std::string &name)
bool GameData::loadAudioClip(const std::string& name, const std::string& fileName)
{
auto filePath = findPathRealCase(datpath + "/audio/", fileName);
if (fileName.find(".mp3") != fileName.npos)
auto systempath = index.findFilePath("audio/" + fileName).native();
if (systempath.find(".mp3") != std::string::npos)
{
logger->error("Data", "MP3 Audio unsupported outside cutscenes");
return false;
}
bool loaded = engine->sound.loadSound(name, filePath);
bool loaded = engine->sound.loadSound(name, systempath);
if ( ! loaded) {
logger->error("Data", "Error loading audio clip "+ filePath);
logger->error("Data", "Error loading audio clip "+ systempath);
return false;
}

View File

@ -1,47 +1,23 @@
#include <algorithm>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/range/iterator_range.hpp>
#include <platform/FileIndex.hpp>
#include <loaders/LoaderIMG.hpp>
using namespace boost::filesystem;
/**
* Finds the 'real' case for a path, to get around the fact that Rockstar's data is usually the wrong case.
* @param base The base of the path to start looking from.
* @param path the lowercase path.
*/
std::string findPathRealCase(const std::string& base_src, const std::string& path_src)
void FileIndex::indexGameDirectory(const fs::path& base_path)
{
path base(base_src);
path searchpath(path_src);
gamedatapath_ = base_path;
// Iterate over each component of the path
for(const path& path_component : boost::make_iterator_range(searchpath.begin(), searchpath.end())) {
std::string cmpLower = path_component.string();
std::transform(cmpLower.begin(), cmpLower.end(), cmpLower.begin(), ::tolower);
for(const path& entry : boost::make_iterator_range(recursive_directory_iterator(base_path), {})) {
if(is_regular_file(entry)) {
std::string name = entry.native();
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
// Search the current base path for a filename matching the component we're searching for
bool found = false;
for(const path& entry : boost::make_iterator_range(directory_iterator(base), {})) {
std::string lowerName = entry.filename().string();
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
if(lowerName == cmpLower) {
// We got a match, so add it to base and continue
base /= entry.filename();
found = true;
break;
}
}
if(!found) {
throw std::runtime_error("Can't find real path case of " + path_src);
filesystemfiles_[name] = entry;
}
}
return base.string();
}
void FileIndex::indexTree(const std::string& root)

View File

@ -2,15 +2,66 @@
#define RWENGINE_FILEINDEX_HPP
#include "FileHandle.hpp"
#include <boost/filesystem.hpp>
#include <boost/functional/hash.hpp>
#include <string>
#include <map>
#include <unordered_map>
std::string findPathRealCase(const std::string& base_src, const std::string& path_src);
namespace fs = boost::filesystem;
namespace std
{
template<> struct hash<fs::path>
{
size_t operator()(const fs::path& p) const
{
return fs::hash_value(p);
}
};
}
class FileIndex
{
private:
/**
* Mapping type (lower case name) => (on disk name)
*/
using FileSystemMap = std::unordered_map<fs::path, fs::path>;
fs::path gamedatapath_;
FileSystemMap filesystemfiles_;
public:
/**
* @brief indexDirectory finds the true case for each file in the tree
* @param base_path
*
* This is used to build the mapping of lower-case file paths to the
* true case on the file system for platforms where this is an issue.
*
*/
void indexGameDirectory(const fs::path& base_path);
/**
* @brief findFilePath finds disk path for a game data file
* @param path
* @return The file path as it exists on disk
*/
fs::path findFilePath(std::string path)
{
auto backslash = std::string::npos;
while ((backslash = path.find("\\")) != std::string::npos) {
path.replace(backslash, 1, "/");
}
auto realpath = gamedatapath_ / path;
std::string name = realpath.native();
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
return filesystemfiles_[name];
}
struct IndexData
{
/// Lowercase identifying filename

View File

@ -5,6 +5,33 @@
BOOST_AUTO_TEST_SUITE(FileIndexTests)
#if RW_TEST_WITH_DATA
BOOST_AUTO_TEST_CASE(test_directory_paths)
{
FileIndex index;
index.indexGameDirectory(Global::getGamePath());
{
std::string upperpath { "DATA/CULLZONE.DAT" };
auto truepath = index.findFilePath(upperpath);
BOOST_ASSERT(! truepath.empty());
BOOST_CHECK(upperpath != truepath);
fs::path expected {Global::getGamePath()};
expected /= "data/CULLZONE.DAT";
BOOST_CHECK_EQUAL(truepath.native(), expected.native());
}
{
std::string upperpath { "DATA/MAPS/COMNBTM/COMNBTM.IPL" };
auto truepath = index.findFilePath(upperpath);
BOOST_ASSERT(! truepath.empty());
BOOST_CHECK(upperpath != truepath);
fs::path expected {Global::getGamePath()};
expected /= "data/maps/comnbtm/comNbtm.ipl";
BOOST_CHECK_EQUAL(truepath.native(), expected.native());
}
}
BOOST_AUTO_TEST_CASE(test_index)
{
FileIndex index;