Files
2020-08-19 20:37:30 +02:00

386 lines
9.8 KiB
SourcePawn

/* File System Parsers */
// Strip filename from path.
void StripPathFilename(char[] path)
{
strcopy(path, FindCharInString(path, '/', true) + 1, path);
}
// Return the filename and extension from a given path.
void GetPathBasename(char[] path, char[] buffer, int maxlength)
{
int check = -1;
if ((check = FindCharInString(path, '/', true)) != -1 ||
(check = FindCharInString(path, '\\', true)) != -1)
{
strcopy(buffer, maxlength, path[check+1]);
}
else
{
strcopy(buffer, maxlength, path);
}
}
// Add http protocol to url if it's missing.
void PrefixURL(char[] buffer, int maxlength, const char[] url)
{
if (strncmp(url, "http://", 7) != 0 && strncmp(url, "https://", 8) != 0)
{
Format(buffer, maxlength, "http://%s", url);
}
else
{
strcopy(buffer, maxlength, url);
}
}
// Split URL into hostname, location, and filename. No trailing slashes.
void ParseURL(const char[] url, char[] host, int maxHost, char[] location, int maxLoc, char[] filename, int maxName)
{
// Strip url prefix.
int idx = StrContains(url, "://");
idx = (idx != -1) ? idx + 3 : 0;
char dirs[16][64];
int total = ExplodeString(url[idx], "/", dirs, sizeof(dirs), sizeof(dirs[]));
// host
Format(host, maxHost, "%s", dirs[0]);
// location
location[0] = '\0';
for (int i = 1; i < total - 1; i++)
{
Format(location, maxLoc, "%s/%s", location, dirs[i]);
}
// filename
Format(filename, maxName, "%s", dirs[total-1]);
}
// Converts Updater SMC file paths into paths relative to the game folder.
void ParseSMCPathForLocal(const char[] path, char[] buffer, int maxlength)
{
char dirs[16][64];
int total = ExplodeString(path, "/", dirs, sizeof(dirs), sizeof(dirs[]));
if (StrEqual(dirs[0], "Path_SM"))
{
BuildPath(Path_SM, buffer, maxlength, "");
}
else // Path_Mod
{
buffer[0] = '\0';
}
// Construct the path and create directories if needed.
for (int i = 1; i < total - 1; i++)
{
Format(buffer, maxlength, "%s%s/", buffer, dirs[i]);
if(!DirExists(buffer))
{
CreateDirectory(buffer, 511);
}
}
// Add the filename to the end of the path.
Format(buffer, maxlength, "%s%s", buffer, dirs[total-1]);
}
// Converts Updater SMC file paths into paths relative to the plugin's update URL.
void ParseSMCPathForDownload(const char[] path, char[] buffer, int maxlength)
{
char dirs[16][64];
int total = ExplodeString(path, "/", dirs, sizeof(dirs), sizeof(dirs[]));
// Construct the path.
buffer[0] = '\0';
for (int i = 1; i < total; i++)
{
Format(buffer, maxlength, "%s/%s", buffer, dirs[i]);
}
}
// Parses a plugin's update file.
// Logs update notes and begins download if required.
// Returns true if an update was available.
static Handle SMC_Sections;
static Handle SMC_DataTrie;
static Handle SMC_DataPack;
static int SMC_LineNum;
bool ParseUpdateFile(int index, const char[] path)
{
/* Return true if an update was available. */
SMC_Sections = CreateArray(64);
SMC_DataTrie = CreateTrie();
SMC_DataPack = CreateDataPack();
SMC_LineNum = 0;
Handle smc = SMC_CreateParser();
SMC_SetRawLine(smc, Updater_RawLine);
SMC_SetReaders(smc, Updater_NewSection, Updater_KeyValue, Updater_EndSection);
char sBuffer[MAX_URL_LENGTH];
Handle hPack;
bool bUpdate = false;
SMCError err = SMC_ParseFile(smc, path);
if (err == SMCError_Okay)
{
// Initialize data
Handle hPlugin = IndexToPlugin(index);
Handle hFiles = Updater_GetFiles(index);
ClearArray(hFiles);
// current version.
char sCurrentVersion[16];
if (!GetPluginInfo(hPlugin, PlInfo_Version, sCurrentVersion, sizeof(sCurrentVersion)))
{
strcopy(sCurrentVersion, sizeof(sCurrentVersion), "Null");
}
// latest version.
char smcLatestVersion[16];
if (GetTrieValue(SMC_DataTrie, "version->latest", hPack))
{
ResetPack(hPack);
ReadPackString(hPack, smcLatestVersion, sizeof(smcLatestVersion));
}
// Check if we have the latest version.
if (!StrEqual(sCurrentVersion, smcLatestVersion))
{
char sFilename[64];
char sName[64];
GetPluginFilename(hPlugin, sFilename, sizeof(sFilename));
if (GetPluginInfo(hPlugin, PlInfo_Name, sName, sizeof(sName)))
{
Updater_Log("Update available for \"%s\" (%s). Current: %s - Latest: %s", sName, sFilename, sCurrentVersion, smcLatestVersion);
}
else
{
Updater_Log("Update available for \"%s\". Current: %s - Latest: %s", sFilename, sCurrentVersion, smcLatestVersion);
}
if (GetTrieValue(SMC_DataTrie, "information->notes", hPack))
{
ResetPack(hPack);
int iCount = 0;
while (IsPackReadable(hPack, 1))
{
ReadPackString(hPack, sBuffer, sizeof(sBuffer));
Updater_Log(" [%i] %s", iCount++, sBuffer);
}
}
// Log update notes, save file list, and begin downloading.
if (g_bGetDownload && Fwd_OnPluginDownloading(hPlugin) == Plugin_Continue)
{
// Get previous version.
char smcPrevVersion[16];
if (GetTrieValue(SMC_DataTrie, "version->previous", hPack))
{
ResetPack(hPack);
ReadPackString(hPack, smcPrevVersion, sizeof(smcPrevVersion));
}
// Check if we only need the patch files.
if (StrEqual(sCurrentVersion, smcPrevVersion) && GetTrieValue(SMC_DataTrie, "patch->plugin", hPack))
{
ParseSMCFilePack(index, hPack, hFiles);
if (g_bGetSource && GetTrieValue(SMC_DataTrie, "patch->source", hPack))
{
ParseSMCFilePack(index, hPack, hFiles);
}
}
else if (GetTrieValue(SMC_DataTrie, "files->plugin", hPack))
{
ParseSMCFilePack(index, hPack, hFiles);
if (g_bGetSource && GetTrieValue(SMC_DataTrie, "files->source", hPack))
{
ParseSMCFilePack(index, hPack, hFiles);
}
}
Updater_SetStatus(index, Status_Downloading);
}
else
{
// We don't want to spam the logs with the same update notification.
Updater_SetStatus(index, Status_Updated);
}
bUpdate = true;
}
#if defined DEBUG
iCount = 0;
Updater_DebugLog(" ");
Updater_DebugLog("SMC DEBUG");
ResetPack(SMC_DataPack);
while (IsPackReadable(SMC_DataPack, 1))
{
ReadPackString(SMC_DataPack, sBuffer, sizeof(sBuffer));
Updater_DebugLog("%s", sBuffer);
if (GetTrieValue(SMC_DataTrie, sBuffer, hPack))
{
iCount = 0;
ResetPack(hPack);
while (IsPackReadable(hPack, 1))
{
ReadPackString(hPack, sBuffer, sizeof(sBuffer));
Updater_DebugLog(" [%i] %s", iCount++, sBuffer);
}
}
}
Updater_DebugLog("END SMC DEBUG");
Updater_DebugLog(" ");
#endif
}
else
{
Updater_Log("SMC parsing error on line %d", SMC_LineNum);
Updater_GetURL(index, sBuffer, sizeof(sBuffer));
Updater_Log(" [0] URL: %s", sBuffer);
if (SMC_GetErrorString(err, sBuffer, sizeof(sBuffer)))
{
Updater_Log(" [1] ERROR: %s", sBuffer);
}
}
// Clean up SMC data.
ResetPack(SMC_DataPack);
while (IsPackReadable(SMC_DataPack, 1))
{
ReadPackString(SMC_DataPack, sBuffer, sizeof(sBuffer));
if (GetTrieValue(SMC_DataTrie, sBuffer, hPack))
{
CloseHandle(hPack);
}
}
CloseHandle(SMC_Sections);
CloseHandle(SMC_DataTrie);
CloseHandle(SMC_DataPack);
CloseHandle(smc);
return bUpdate;
}
void ParseSMCFilePack(int index, Handle hPack, Handle hFiles)
{
// Prepare URL
char urlprefix[MAX_URL_LENGTH];
char url[MAX_URL_LENGTH];
char dest[PLATFORM_MAX_PATH];
char sBuffer[MAX_URL_LENGTH];
Updater_GetURL(index, urlprefix, sizeof(urlprefix));
StripPathFilename(urlprefix);
ResetPack(hPack);
while (IsPackReadable(hPack, 1))
{
ReadPackString(hPack, sBuffer, sizeof(sBuffer));
// Merge url.
ParseSMCPathForDownload(sBuffer, url, sizeof(url));
Format(url, sizeof(url), "%s%s", urlprefix, url);
// Make sure the current plugin path matches the update.
ParseSMCPathForLocal(sBuffer, dest, sizeof(dest));
char sLocalBase[64];
char sPluginBase[64];
char sFilename[64];
GetPathBasename(dest, sLocalBase, sizeof(sLocalBase));
GetPathBasename(sFilename, sPluginBase, sizeof(sPluginBase));
if (StrEqual(sLocalBase, sPluginBase))
{
StripPathFilename(dest);
Format(dest, sizeof(dest), "%s/%s", dest, sFilename);
}
// Save the file location for later.
PushArrayString(hFiles, dest);
// Add temporary file extension.
Format(dest, sizeof(dest), "%s.%s", dest, TEMP_FILE_EXT);
// Begin downloading file.
AddToDownloadQueue(index, url, dest);
}
}
public SMCResult Updater_RawLine(Handle smc, const char[] line, int lineno)
{
SMC_LineNum = lineno;
return SMCParse_Continue;
}
public SMCResult Updater_NewSection(Handle smc, const char[] name, bool opt_quotes)
{
PushArrayString(SMC_Sections, name);
return SMCParse_Continue;
}
public SMCResult Updater_KeyValue(Handle smc, const char[] key, const char[] value, bool key_quotes, bool value_quotes)
{
char sCurSection[MAX_URL_LENGTH];
char sKey[MAX_URL_LENGTH];
Handle hPack;
GetArrayString(SMC_Sections, GetArraySize(SMC_Sections)-1, sCurSection, sizeof(sCurSection));
FormatEx(sKey, sizeof(sKey), "%s->%s", sCurSection, key);
StringToLower(sKey);
if (!GetTrieValue(SMC_DataTrie, sKey, hPack))
{
hPack = CreateDataPack();
SetTrieValue(SMC_DataTrie, sKey, hPack);
WritePackString(SMC_DataPack, sKey);
}
WritePackString(hPack, value);
return SMCParse_Continue;
}
public SMCResult Updater_EndSection(Handle smc)
{
if (GetArraySize(SMC_Sections))
{
RemoveFromArray(SMC_Sections, GetArraySize(SMC_Sections)-1);
}
return SMCParse_Continue;
}
stock void StringToLower(char[] input)
{
int length = strlen(input);
for (int i = 0; i < length; i++)
{
input[i] = CharToLower(input[i]);
}
}