XuluMenu/arm9/source/args.cpp
ApacheThunder d9547241d3 Initial Release Commit...
* Initial version.
2023-11-18 02:22:29 -06:00

226 lines
7.4 KiB
C++

/*-----------------------------------------------------------------
Copyright (C) 2005 - 2017
Michael "Chishm" Chisholm
Dave "WinterMute" Murphy
Claudio "sverx"
Michael "mtheall" Theall
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
------------------------------------------------------------------*/
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <unistd.h>
#include <utility>
#include <vector>
#include "args.h"
using namespace std;
static const string NDS_EXT = ".nds";
static const string SRL_EXT = ".srl";
static const string DSI_EXT = ".dsi";
static const string ARG_EXT = ".argv";
static const string EXT_EXT = ".ext";
static const char EXT_DIR[] = "/nds";
static const char SEPARATORS[] = "\n\r\t ";
/* Checks if s1 ends with s2, ignoring case.
Returns true if it does, false otherwise.
*/
static bool strCaseEnd(const string& s1, const string& s2) {
return (s1.size() >= s2.size() && strcasecmp(s1.c_str() + s1.size() - s2.size(), s2.c_str()) == 0);
}
/* Parses the contents of the file given by filename into argarray. Arguments
are tokenized based on whitespace.
*/
static bool parseArgFileAll(const string& filename, vector<string>& argarray) {
FILE *argfile = fopen(filename.c_str(), "rb");
if (!argfile)return false;
char *line = NULL;
size_t lineSize = 0;
while (__getline(&line, &lineSize, argfile) >= 0) {
// Find comment and end string there
char *pstr = strchr(line, '#');
if (pstr)*pstr = '\0';
// Tokenize arguments
char *saveptr;
pstr = strtok_r(line, SEPARATORS, &saveptr);
while (pstr) {
argarray.emplace_back(pstr);
pstr = strtok_r(NULL, SEPARATORS, &saveptr);
}
}
if (line)free(line);
fclose(argfile);
return argarray.size() > 0;
}
/* Parses the argument file given by filename and returns the NDS file that it
* points to.
*/
static bool parseArgFileNds(const std::string& filename, std::string& ndsPath) {
bool success = false;
FILE *argfile = fopen(filename.c_str(), "rb");
if (!argfile)return false;
char *line = NULL;
size_t lineSize = 0;
while (__getline(&line, &lineSize, argfile) >= 0) {
char *pstr = NULL;
// Find comment and end string there
pstr = strchr(line, '#');
if (pstr)*pstr = '\0';
// Tokenize arguments
char *saveptr;
pstr = strtok_r(line, SEPARATORS, &saveptr);
if (pstr) {
// Only want the first token, which should be the NDS file name
ndsPath = pstr;
success = true;
break;
}
}
if (line)free(line);
fclose(argfile);
return success;
}
/* Converts a plain filename into an absolute path. If it's already an absolute
* path, it is returned as-is. If basePath is NULL, the current working directory
* is used.
* Returns true on success, false on failure.
*/
static bool toAbsPath(const string& filename, const char* basePath, string& filePath) {
// Copy existing absolute or empty paths
if (filename.size() == 0 || filename[0] == '/') {
filePath = filename;
return true;
}
if (basePath == NULL) {
// Get current working directory (uses C-strings)
vector<char> cwd(PATH_MAX);
if (getcwd (cwd.data(), cwd.size()) == NULL)return false; // Path was too long, abort
// Copy CWD into path
filePath = cwd.data();
} else {
// Just copy the base path
filePath = basePath;
}
// Ensure there's a path separator
if (filePath.back() != '/')filePath += '/';
// Now append the filename
filePath += filename;
return true;
}
/* Convert a dataFilePath to the path of the ext file that specifies the
* handler.
* Returns true on success, false on failure
*/
static bool toExtPath(const string& dataFilePath, string& extFilePath) {
// Figure out what the file extension is
size_t extPos = dataFilePath.rfind('.');
if (extPos == string::npos)return false;
extPos += 1;
if (extPos >= dataFilePath.size())return false;
// Construct handler path from extension. Handlers are in the EXT_DIR and
// end with EXT_EXT.
const string ext = dataFilePath.substr(extPos);
if (!toAbsPath(ext, EXT_DIR, extFilePath))return false;
extFilePath += EXT_EXT;
return true;
}
bool argsNdsPath(const std::string& filePath, std::string& ndsPath) {
if (strCaseEnd(filePath, NDS_EXT) || strCaseEnd(filePath, SRL_EXT) || strCaseEnd(filePath, DSI_EXT)) {
ndsPath = filePath;
return true;
} else if (strCaseEnd(filePath, ARG_EXT)) {
return parseArgFileNds(filePath, ndsPath);
} else {
// This is a data file associated with a handler NDS by an ext file
string extPath;
if (!toExtPath(filePath, extPath))return false;
string ndsRelPath;
if (!parseArgFileNds(extPath, ndsRelPath))return false;
// Handler is in EXT_DIR
return toAbsPath(ndsRelPath, EXT_DIR, ndsPath);
}
return false;
}
bool argsFillArray(const string& filePath, vector<string>& argarray) {
// Ensure argarray is empty
argarray.clear();
if (strCaseEnd(filePath, NDS_EXT) || strCaseEnd(filePath, SRL_EXT) || strCaseEnd(filePath, DSI_EXT)) {
string absPath;
if (!toAbsPath(filePath, NULL, absPath))return false;
argarray.push_back(move(absPath));
} else if (strCaseEnd(filePath, ARG_EXT)) {
if (!parseArgFileAll(filePath, argarray))return false;
// Ensure argv[0] is absolute path
string absPath;
if (!toAbsPath(argarray[0], NULL, absPath))return false;
argarray[0] = absPath;
} else {
// This is a data file associated with a handler NDS by an ext file
string extPath;
if (!toExtPath(filePath, extPath))return false;
// Read the arg file for the extension handler
if (!parseArgFileAll(extPath, argarray))return false;
// Extension handler relative path is relative to EXT_DIR, not CWD
string absPath;
if (!toAbsPath(argarray[0], EXT_DIR, absPath))return false;
argarray[0] = absPath;
// Add the data filename to the end. Its path is relative to CWD.
if (!toAbsPath(filePath, NULL, absPath))return false;
argarray.push_back(move(absPath));
}
return (argarray.size() > 0) && (strCaseEnd(argarray[0], NDS_EXT) || strCaseEnd(argarray[0], SRL_EXT) || strCaseEnd(argarray[0], DSI_EXT));
}
vector<string> argsGetExtensionList() {
vector<string> extensionList;
// Always supported files: NDS binaries and predefined argument lists
extensionList.push_back(NDS_EXT);
extensionList.push_back(SRL_EXT);
extensionList.push_back(DSI_EXT);
extensionList.push_back(ARG_EXT);
// Get a list of extension files: argument lists associated with a file type
DIR *dir = opendir (EXT_DIR);
if (dir) {
for (struct dirent* dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) {
// Add the name component of all files ending with EXT_EXT to the list
if (dirent->d_type != DT_REG)continue;
if (dirent->d_name[0] != '.' && strCaseEnd(dirent->d_name, EXT_EXT)) {
size_t extPos = strlen(dirent->d_name) - EXT_EXT.size();
dirent->d_name[extPos] = '\0';
extensionList.push_back(dirent->d_name);
}
}
closedir(dir);
}
return extensionList;
}