Skip to content

Instantly share code, notes, and snippets.

@acerspyro
Created July 4, 2020 22:30
Show Gist options
  • Save acerspyro/b4f1d2ee13a28709800b6cee6c45fb61 to your computer and use it in GitHub Desktop.
Save acerspyro/b4f1d2ee13a28709800b6cee6c45fb61 to your computer and use it in GitHub Desktop.
/**
* Resolves a given path relative to the game's home directory
*
* @param relpath
* relpath is a special string that can represent path parameters.
*
* 1. Find all &'s and split the string at that point
* 2. Find <, > and all the text in-between and strips everything from the string
* 3. Find . and .. notations and resolve them
* 4. Loop until no more &'s are found
*
* This method converts an std::string into a C string for pointer manipulation purposes.
* @replaces path()
* @return
*/
std::string tools::resolvepath(std::string relpath)
{
/* String to work on, memory copy of relpath's C string */
char *curpart = strdup(relpath.c_str());
/* Loop over strings separated by & */
while(true)
{
/**
* Find the first occurence of '&' and replace with \0
* This cuts the string into two C strings, essentially a separate file
*/
char *endpart = strchr(curpart, '&');
if(endpart) *endpart = '\0'; // Only null-terminate if endpart isn't nullptr
/**
* Find the first < and the last > and cull anything in-between
* Anything between < and > is a special string and must be stripped for parsing
*/
if(curpart[0] == '<')
{
char *filepart = strrchr(curpart, '>');
if(!filepart) return relpath; // Fail
curpart = filepart + 1; // Move pointer to the end of what we found (+1 to account for the last >)
}
char *prevdir;
char *curdir = curpart;
/**
* Find single and double dots (., ..) and resolves them
* Produces a clean path string
*/
while(true)
{
prevdir = curdir[0] == PATHDIV
? curdir + 1
: curdir;
curdir = strchr(prevdir, PATHDIV);
if(!curdir) break;
/* Found single dot, strip it out as it's useless */
if(prevdir + 1 == curdir && prevdir[0] == '.')
{
memmove(prevdir, curdir + 1, strlen(curdir + 1) + 1); // Double +1 for null termination
curdir = prevdir;
}
/* Found double dot, remove double dot and previous directory */
else if(curdir[1] == '.' && curdir[2] == '.' && curdir[3] == PATHDIV)
{
if(prevdir + 2 == curdir && prevdir[0] == '.' && prevdir[1] == '.') continue;
memmove(prevdir, curdir + 4, strlen(curdir + 4) + 1);
if(prevdir - 2 >= curpart && prevdir[-1] == PATHDIV)
{
prevdir -= 2;
while(prevdir - 1 >= curpart && prevdir[-1] != PATHDIV) --prevdir;
}
curdir = prevdir;
}
}
/**
* Small hack to make sure the string doesn't break
* Place back the & we replaced with \0 at the beginning of the loop
* Then, move on to the next piece of string
*/
if(endpart)
{
*endpart = '&';
curpart = endpart + 1;
}
else break;
}
return std::string(curpart);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment