Skip to content

Commit

Permalink
Paths resolving updates (#12)
Browse files Browse the repository at this point in the history
* Opcode 2001 returning only resolved paths.

* Updates in path resolving.

Added support for parent directory references "..\" in paths.
Replaced virtual path numbers with text keywords.
  • Loading branch information
MiranDMC authored Oct 28, 2023
1 parent 7f674cd commit 91cdc4f
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 59 deletions.
10 changes: 5 additions & 5 deletions cleo_sdk/CLEO.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ static eLogicalOperation& operator--(eLogicalOperation& o)
}

// CLEO virtual path prefixes. Expandable with CLEO_ResolvePath
const char DIR_GAME[] = "0:"; // game root directory
const char DIR_USER[] = "1:"; // game save directory
const char DIR_SCRIPT[] = "2:"; // current script directory
const char DIR_CLEO[] = "3:"; // game\cleo directory
const char DIR_MODULES[] = "4:"; // game\cleo\modules directory
const char DIR_GAME[] = "root:"; // game root directory
const char DIR_USER[] = "userfiles:"; // game save directory
const char DIR_SCRIPT[] = "."; // current script directory
const char DIR_CLEO[] = "cleo:"; // game\cleo directory
const char DIR_MODULES[] = "modules:"; // game\cleo\modules directory

// argument of CLEO_RegisterCallback
enum class eCallbackId : DWORD
Expand Down
46 changes: 34 additions & 12 deletions source/CCustomOpcodeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1485,15 +1485,29 @@ namespace CLEO {
OpcodeResult __stdcall opcode_0A99(CRunningScript *thread)
{
auto paramType = *thread->GetBytePointer();
if (paramType >= 1 && paramType <= 8)
if (paramType == DT_BYTE ||
paramType == DT_WORD ||
paramType == DT_DWORD ||
paramType == DT_VAR ||
paramType == DT_LVAR ||
paramType == DT_VAR_ARRAY ||
paramType == DT_LVAR_ARRAY)
{
// numbered predefined paths
DWORD param;
*thread >> param;
DWORD param; *thread >> param;

const char* path;
switch(param)
{
case 0: path = DIR_GAME; break;
case 1: path = DIR_USER; break;
case 2: path = DIR_SCRIPT; break;
default:
LOG_WARNING("Value (%d) not known by opcode [0A99] in script %s", param, ((CCustomScript*)thread)->GetInfoStr().c_str());
return OR_CONTINUE;
}

std::string path = std::to_string(param);
path += ":";
reinterpret_cast<CCustomScript*>(thread)->SetWorkDir(path.c_str());
reinterpret_cast<CCustomScript*>(thread)->SetWorkDir(path);
}
else
{
Expand Down Expand Up @@ -2032,11 +2046,8 @@ namespace CLEO {
break;

default:
{
SHOW_ERROR("Invalid type (%02X) of the first argument in opcode [0AB1] in script %s \nScript suspended.", *thread->GetBytePointer(), ((CCustomScript*)thread)->GetInfoStr().c_str());

return CCustomOpcodeSystem::ErrorSuspendScript(thread);
}
}

ScmFunction* scmFunc = new ScmFunction(thread);
Expand Down Expand Up @@ -3007,9 +3018,20 @@ namespace CLEO {

if(fullPath != 0)
{
std::ostringstream ss;
ss << script->GetScriptFileDir() << "\\" << script->GetScriptFileName();
CLEO_WriteStringOpcodeParam(thread, ss.str().c_str());
const size_t len =
strlen(script->GetScriptFileDir()) +
1 + // path separator
strlen(script->GetScriptFileName());

std::string path;
path.reserve(len);

path = script->GetScriptFileDir();
path.push_back('\\');
path.append(script->GetScriptFileName());
path = script->ResolvePath(path.c_str()); // real absolute path

CLEO_WriteStringOpcodeParam(thread, path.c_str());
}
else
CLEO_WriteStringOpcodeParam(thread, script->GetScriptFileName());
Expand Down
92 changes: 52 additions & 40 deletions source/CScriptEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,57 +665,69 @@ namespace CLEO
return {};
}

std::string result;
if (strlen(path) < 2 || path[1] != ':') // does not start with drive letter
try
{
result = (customWorkDir != nullptr) ? customWorkDir : GetWorkDir();
if (!result.empty() && result.back() == '\\') result.pop_back();
auto fsPath = std::filesystem::path(path);

if (strlen(path) > 0)
// check for virtual path root
enum class VPref{ None, Game, User, Script, Cleo, Modules } virtualPrefix = VPref::None;
auto root = fsPath.begin();
if(root != fsPath.end())
{
if(!result.empty()) result.push_back('\\');
result.append(path);
if(*root == DIR_GAME) virtualPrefix = VPref::Game;
else if (*root == DIR_USER) virtualPrefix = VPref::User;
else if (*root == DIR_SCRIPT) virtualPrefix = VPref::Script;
else if (*root == DIR_CLEO) virtualPrefix = VPref::Cleo;
else if (*root == DIR_MODULES) virtualPrefix = VPref::Modules;
}
}
else
{
result = path;
}

// predefined CLEO paths starting with '[digit]:'
if (result.length() < 2 || result[1] != ':' ||
result[0] < DIR_GAME[0] || result[0] > DIR_MODULES[0]) // supported range
{
return result; // not predefined path prefix found
}
// not virtual
if(virtualPrefix == VPref::None)
{
if(fsPath.is_relative())
{
auto workDir = ResolvePath(GetWorkDir());
fsPath = workDir / fsPath;
}

if (result[0] == DIR_USER[0]) // saves/settings location
{
return std::string(GetUserDirectory()) + &result[2]; // original path without '1:' prefix;
}
return std::filesystem::weakly_canonical(fsPath).string();
}

if (result[0] == DIR_SCRIPT[0]) // current script location
{
std::string resolved = ResolvePath(GetScriptFileDir());
resolved += &result[2]; // original path without '2:' prefix;
return resolved;
}
// expand virtual paths
std::filesystem::path resolved;

// game root directory
std::string resolved = CFileMgr::ms_rootDirName;
if(!resolved.empty() && resolved.back() == '\\') resolved.pop_back();
if (virtualPrefix == VPref::User) // user files location
{
resolved = GetUserDirectory();
}
else
if (virtualPrefix == VPref::Script) // this script's source file location
{
resolved = ResolvePath(GetScriptFileDir());
}
else
{
// all remaing variants starts with game root
resolved = std::filesystem::path(CFileMgr::ms_rootDirName);

switch(virtualPrefix)
{
case(VPref::Cleo): resolved /= "cleo"; break;
case(VPref::Modules): resolved /= "cleo\\cleo_modules"; break;
}
}

if (result[0] == DIR_CLEO[0]) // cleo directory
{
resolved += "\\cleo";
// append all but virtual prefix from original path
for(auto it = ++fsPath.begin(); it != fsPath.end(); it++)
resolved /= *it;

return std::filesystem::weakly_canonical(resolved).string(); // collapse "..\" uses
}
else if (result[0] == DIR_MODULES[0]) // cleo modules directory
catch (const std::exception& ex)
{
resolved += "\\cleo\\cleo_modules";
TRACE("Error while resolving path: %s", ex.what());
return {};
}

resolved += &result[2]; // original path without 'X:' prefix
return resolved;
}

std::string CCustomScript::GetInfoStr(bool currLineInfo) const
Expand Down Expand Up @@ -1299,7 +1311,7 @@ namespace CLEO

// store script file directory and name
std::filesystem::path path = szFileName;
path = std::filesystem::absolute(path);
path = std::filesystem::weakly_canonical(path);
scriptFileDir = path.parent_path().string();
scriptFileName = path.filename().string();

Expand Down
3 changes: 1 addition & 2 deletions source/FileEnumerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ void FilesWalk(const char* directory, const char* extension, T callback)
continue; // skip directories
}

//auto result = std::filesystem::absolute(std::string(baseDir) + wfd.cFileName);
auto result = std::filesystem::path(std::string(baseDir) + wfd.cFileName); // ModLoader supports only relative paths...
auto result = std::filesystem::weakly_canonical(std::string(baseDir) + wfd.cFileName); // will use CWD if input path was relative!
callback(result.string().c_str(), result.filename().string().c_str());

} while (FindNextFile(hSearch, &wfd));
Expand Down

0 comments on commit 91cdc4f

Please sign in to comment.