Skip to content

Commit

Permalink
Make the Ineluki MP3 Path canonical and change logic of the default f…
Browse files Browse the repository at this point in the history
…ile finder search to detect directories. Fixes EasyRPG#939
  • Loading branch information
Ghabry committed Aug 2, 2016
1 parent 8f3bb39 commit 3469b16
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 15 deletions.
47 changes: 47 additions & 0 deletions src/filefinder.cpp
Expand Up @@ -236,6 +236,39 @@ std::string FileFinder::MakePath(const std::string &dir, std::string const& name
return str;
}

std::string FileFinder::MakeCanonical(std::string const& path, int initial_deepness) {
std::vector<std::string> path_components = SplitPath(path);
std::vector<std::string> path_can;

for (std::string path_comp : path_components) {
if (path_comp == "..") {
if (initial_deepness > 0) {
// Ignore
--initial_deepness;
} else if (path_can.size() > 0) {
path_can.pop_back();
} else {
Output::Debug("Path traversal out of game directory: %s", path.c_str());
}
} else if (path_comp == ".") {
// ignore
} else {
path_can.push_back(path_comp);
}
}

std::string ret;
for (std::string s : path_can) {
ret = MakePath(ret, s);
}

return ret;
}

std::vector<std::string> FileFinder::SplitPath(std::string const& path) {
return Utils::Tokenize(path, R"([\\/])");
}

#ifdef _WIN32
std::string GetFontsPath() {
#ifdef __WINRT__
Expand Down Expand Up @@ -476,6 +509,20 @@ std::string FileFinder::FindDefault(const DirectoryTree& tree, const std::string

std::string FileFinder::FindDefault(const DirectoryTree& tree, const std::string& name) {
DirectoryTree const& p = tree;

std::vector<std::string> path_comps = SplitPath(name);
if (path_comps.size() > 1) {
// When the searched name contains a directory search in this directory
// instead of the root

std::string f;
for (auto it = path_comps.begin() + 1; it != path_comps.end(); ++it) {
f = MakePath(f, *it);
}

return FindDefault(path_comps[0], f);
}

string_map const& files = p.files;

string_map::const_iterator const it = files.find(Utils::LowerCase(name));
Expand Down
35 changes: 27 additions & 8 deletions src/filefinder.h
Expand Up @@ -25,6 +25,7 @@
#include <cstdio>
#include <ios>
#include <unordered_map>
#include <vector>

/**
* FileFinder contains helper methods for finding case
Expand All @@ -43,14 +44,14 @@ namespace FileFinder {
*/
void Quit();

/*
* { case lowered path, real path }
*/
/**
* { case lowered path, real path }
*/
typedef std::unordered_map<std::string, std::string> string_map;

/*
* { case lowered directory name, non directory file list }
*/
/**
* { case lowered directory name, non directory file list }
*/
typedef std::unordered_map<std::string, string_map> sub_members_type;

struct DirectoryTree {
Expand Down Expand Up @@ -99,7 +100,7 @@ namespace FileFinder {
std::string FindDefault(const DirectoryTree& tree, const std::string& dir, const std::string& name);

/**
* Finds a file in the root of a custom project tree.
* Finds a file from the root of a custom project tree.
*
* @param tree Project tree to search
* @param name the path and name
Expand Down Expand Up @@ -183,9 +184,27 @@ namespace FileFinder {
*
* @param dir base directory.
* @param name file name to be appended to dir.
* @return normalized path string.
* @return combined path
*/
std::string MakePath(std::string const& dir, std::string const& name);

/**
* Converts a path to the canonical equivalent.
* This generates a path that does not contain ".." or "." directories.
*
* @param path Path to normalize
* @param initial_deepness How deep the passed path is relative to the game root
* @return canonical path
*/
std::string MakeCanonical(std::string const& path, int initial_deepness);

/**
* Splits a path in it's components.
*
* @param path Path to split
* @return Vector containing path components
*/
std::vector<std::string> SplitPath(std::string const& path);

/**
* GetDirectoryMembers member listing mode.
Expand Down
13 changes: 7 additions & 6 deletions src/game_system.cpp
Expand Up @@ -315,26 +315,27 @@ void Game_System::OnBgmReady(FileRequestResult* result) {
return;
}

if (Utils::EndsWith(path, ".link")) {
if (Utils::EndsWith(result->file, ".link")) {
Output::Debug("Ineluki link file: %s", path.c_str());

// Handle Ineluki's MP3 patch
std::shared_ptr<std::fstream> stream = FileFinder::openUTF8(path, std::ios_base::out);
std::shared_ptr<std::fstream> stream = FileFinder::openUTF8(path, std::ios_base::in);
if (!stream) {
Output::Warning("Ineluki link read error: %s", path.c_str());
return;
}

// The first line contains the path to the actual audio file to play
std::string line;
std::getline(*stream.get(), line);
std::string line = Utils::ReadLine(*stream.get());

Output::Debug("Linking to: %s", line.c_str());

// Fixme: Will not work in emscripten because the file was not requested for download
std::string ineluki_path = FileFinder::FindDefault(line);
std::string line_canonical = FileFinder::MakeCanonical(line, 1);

std::string ineluki_path = FileFinder::FindDefault(line_canonical);
if (ineluki_path.empty()) {
Output::Debug("Music not found: %s", line);
Output::Debug("Music not found: %s", line_canonical.c_str());
return;
}

Expand Down
40 changes: 39 additions & 1 deletion src/utils.cpp
Expand Up @@ -18,8 +18,8 @@
// Headers
#include "utils.h"
#include <algorithm>
#include <cctype>
#include <random>
#include <regex>

namespace {
std::mt19937 rng;
Expand Down Expand Up @@ -303,3 +303,41 @@ int32_t Utils::GetRandomNumber(int32_t from, int32_t to) {
void Utils::SeedRandomNumberGenerator(int32_t seed) {
rng.seed(seed);
}

// via https://stackoverflow.com/questions/6089231/
std::string Utils::ReadLine(std::istream &is) {
std::string out;

std::istream::sentry se(is, true);
std::streambuf* sb = is.rdbuf();

for(;;) {
int c = sb->sbumpc();
switch (c) {
case '\n':
return out;
case '\r':
if (sb->sgetc() == '\n') {
sb->sbumpc();
}
return out;
case EOF:
// Also handle the case when the last line has no line ending
if (out.empty()) {
is.setstate(std::ios::eofbit);
}
return out;
default:
out += (char)c;
}
}
}

std::vector<std::string> Utils::Tokenize(const std::string &str_to_tokenize, const std::string &token_re) {
std::regex reg(token_re);
std::sregex_token_iterator iter(str_to_tokenize.begin(), str_to_tokenize.end(), reg, -1);
std::sregex_token_iterator end;
std::vector<std::string> vec(iter, end);

return vec;
}
20 changes: 20 additions & 0 deletions src/utils.h
Expand Up @@ -20,6 +20,7 @@

#include <string>
#include <sstream>
#include <vector>
#include "system.h"

namespace Utils {
Expand Down Expand Up @@ -128,6 +129,25 @@ namespace Utils {
*/
void SeedRandomNumberGenerator(int32_t seed);

/**
* Reads a line from a stream and returns it.
* Same as std::getline but handles linebreaks independent of the platform
* correctly.
*
* @param is Input stream to read
* @return Content of the read line
*/
std::string ReadLine(std::istream& is);

/**
* Splits a string into tokens specified by a regular expression.
*
* @param str_to_tokenize String that is tokenized
* @param token_re Regular expression specificing the token
* @return vector containing the elements between the tokens
*/
std::vector<std::string> Tokenize(const std::string& str_to_tokenize, const std::string& token_re);

} // namespace Utils

#endif

0 comments on commit 3469b16

Please sign in to comment.