413 lines
11 KiB
Plaintext
413 lines
11 KiB
Plaintext
/*
|
|
* Copyright (c) 2016-2023 Vera Visions LLC.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
string name;
|
|
string category;
|
|
string title;
|
|
string version;
|
|
string description;
|
|
string license;
|
|
string author;
|
|
string website;
|
|
string installed;
|
|
updateState_t state;
|
|
updateAction_t pending_action;
|
|
int size;
|
|
int uid;
|
|
string preview_image;
|
|
float dlpercentage;
|
|
} update_t;
|
|
|
|
string(float id, float b) getgamedirinfo = #0;
|
|
string(int packageidx, int desiredfield) getpackagemanagerinfo = #0;
|
|
|
|
#define FN_UPDATE_IMGURL "http://www.frag-net.com/dl/img/%s.jpg"
|
|
int g_platform_update_count;
|
|
update_t *updates;
|
|
|
|
var updaterStatus_t updater_package_status = UPDATER_NONE;
|
|
|
|
void
|
|
Updates_Init(void)
|
|
{
|
|
string packages;
|
|
|
|
/* first, see if our game info sets any packages. */
|
|
packages = GameLibrary_GetInfo(GAMEINFO_PACKAGELIST);
|
|
|
|
/* we have no hard-coded list of supported packages, so query frag-net.com */
|
|
if (!packages) {
|
|
string gamedir = cvar_string("fs_game");
|
|
print(sprintf("Querying package names for %s\n", gamedir));
|
|
uri_get(sprintf("http://www.frag-net.com/dl/packages_%s", uri_escape(gamedir)), MODSERVER_REQ_PKGNAMES);
|
|
updater_package_status = UPDATER_PENDING;
|
|
} else {
|
|
updater_package_status = UPDATER_INITIALIZED;
|
|
}
|
|
}
|
|
|
|
updaterStatus_t
|
|
Updates_GetUpdaterStatus(void)
|
|
{
|
|
return updater_package_status;
|
|
}
|
|
|
|
int
|
|
Updates_GetPackageCount(void)
|
|
{
|
|
return g_platform_update_count;
|
|
}
|
|
|
|
/** Checks a given package name and sees if it's in the list of recommended packages. */
|
|
bool
|
|
Updates_IsRecommended(string packageName)
|
|
{
|
|
string newName = "";
|
|
int countPkg = 0i;
|
|
string packageList = GameLibrary_GetInfo(GAMEINFO_PACKAGELIST);
|
|
|
|
/* cancel out early if need be */
|
|
if not (packageList)
|
|
return true;
|
|
|
|
/* get rid of the version string FTEQW appends */
|
|
tokenizebyseparator(packageName, "=");
|
|
newName = argv(0);
|
|
|
|
/* iterate over the recommended packages */
|
|
countPkg = (int)tokenizebyseparator(packageList, ";");
|
|
|
|
for (int i = 0i; i < countPkg; i++) {
|
|
/* there's a match */
|
|
if (newName == argv(i))
|
|
return true;
|
|
}
|
|
|
|
/* if nothing is found at all. */
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
Updates_RefreshStateValues(int packageID)
|
|
{
|
|
int pkgUID = updates[packageID].uid;
|
|
string installedState = getpackagemanagerinfo(pkgUID, GPMI_INSTALLED);
|
|
string actionState = getpackagemanagerinfo(pkgUID, GPMI_ACTION);
|
|
|
|
switch (actionState) {
|
|
case "user":
|
|
updates[packageID].pending_action = UPDATEACTION_INSTALL;
|
|
break;
|
|
case "reinstall":
|
|
updates[packageID].pending_action = UPDATEACTION_REINSTALL;
|
|
break;
|
|
case "purge":
|
|
updates[packageID].pending_action = UPDATEACTION_UNINSTALL;
|
|
break;
|
|
case "auto":
|
|
updates[packageID].pending_action = UPDATEACTION_AUTOINSTALL;
|
|
break;
|
|
case "disable":
|
|
updates[packageID].pending_action = UPDATEACTION_DISABLE;
|
|
break;
|
|
case "retain":
|
|
/*updates[packageID].pending_action = UPDATEACTION_RETAIN;
|
|
break;*/
|
|
default:
|
|
updates[packageID].pending_action = UPDATEACTION_NONE;
|
|
}
|
|
|
|
switch (installedState) {
|
|
case "present":
|
|
updates[packageID].state = UPDATESTATE_DISABLED;
|
|
break;
|
|
case "enabled":
|
|
updates[packageID].state = UPDATESTATE_ENABLED;
|
|
break;
|
|
case "corrupt":
|
|
updates[packageID].state = UPDATESTATE_CORRUPT;
|
|
break;
|
|
case "pending":
|
|
updates[packageID].state = UPDATESTATE_PENDING;
|
|
break;
|
|
default:
|
|
updates[packageID].state = UPDATESTATE_NONE;
|
|
}
|
|
|
|
updates[packageID].dlpercentage = stof(installedState);
|
|
|
|
/* HACK: the engine doesn't seem to set pending while installing, so let us do the job then */
|
|
if (updates[packageID].dlpercentage > 0)
|
|
updates[packageID].state = UPDATESTATE_PENDING;
|
|
|
|
/* HACK: enabled AND pending installation? smells like an engine bug! */
|
|
if (updates[packageID].state == UPDATESTATE_ENABLED) {
|
|
if (updates[packageID].pending_action == UPDATEACTION_INSTALL) {
|
|
updates[packageID].pending_action = UPDATEACTION_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
Updates_Refresh(void)
|
|
{
|
|
int c = 0i;
|
|
g_platform_update_count = 0i;
|
|
|
|
/* clear */
|
|
if (updates) {
|
|
memfree(updates);
|
|
}
|
|
|
|
/* count all updates that we've got in our package sources */
|
|
for (int i = 0i; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
|
|
g_platform_update_count++;
|
|
}
|
|
|
|
updates = memalloc(sizeof(update_t) * g_platform_update_count);
|
|
|
|
/* fill in all the package values */
|
|
for (int i = 0i; i < g_platform_update_count; i++) {
|
|
int id = i;
|
|
|
|
/* skip not recommended packages */
|
|
if (Updates_IsRecommended(getpackagemanagerinfo(id, GPMI_NAME)) == false)
|
|
continue;
|
|
|
|
updates[c].name = getpackagemanagerinfo(id, GPMI_NAME);
|
|
updates[c].category = getpackagemanagerinfo(id, GPMI_CATEGORY);
|
|
updates[c].title = getpackagemanagerinfo(id, GPMI_TITLE);
|
|
updates[c].version = getpackagemanagerinfo(id, GPMI_VERSION);
|
|
updates[c].description = getpackagemanagerinfo(id, GPMI_DESCRIPTION);
|
|
updates[c].license = getpackagemanagerinfo(id, GPMI_LICENSE);
|
|
updates[c].author = getpackagemanagerinfo(id, GPMI_AUTHOR);
|
|
updates[c].website = getpackagemanagerinfo(id, GPMI_WEBSITE);
|
|
|
|
updates[c].size = (int)stof(getpackagemanagerinfo(id, GPMI_FILESIZE));
|
|
updates[c].uid = i;
|
|
tokenizebyseparator(getpackagemanagerinfo(id, GPMI_NAME), "=");
|
|
updates[c].preview_image = sprintf(FN_UPDATE_IMGURL, argv(0));
|
|
Updates_RefreshStateValues(c);
|
|
|
|
c++;
|
|
}
|
|
|
|
print(sprintf("Updates packages for this game: %i (%i Total)\n", c, g_platform_update_count));
|
|
g_platform_update_count = c;
|
|
}
|
|
|
|
__variant
|
|
Updates_GetInfo(int packageID, updateType_t fieldType)
|
|
{
|
|
if (packageID >= g_platform_update_count || packageID < 0i) {
|
|
print(sprintf("Updates_GetInfo: Invalid package id %i!\n", packageID));
|
|
return __NULL__;
|
|
}
|
|
|
|
Updates_RefreshStateValues(packageID);
|
|
|
|
switch (fieldType) {
|
|
case UPDATE_NAME:
|
|
return (string)updates[packageID].name;
|
|
break;
|
|
case UPDATE_CATEGORY:
|
|
return (string)updates[packageID].category;
|
|
break;
|
|
case UPDATE_TITLE:
|
|
return (string)updates[packageID].title;
|
|
break;
|
|
case UPDATE_VERSION:
|
|
return (string)updates[packageID].version;
|
|
break;
|
|
case UPDATE_DESCRIPTION:
|
|
return (string)updates[packageID].description;
|
|
break;
|
|
case UPDATE_LICENSE:
|
|
return (string)updates[packageID].license;
|
|
break;
|
|
case UPDATE_AUTHOR:
|
|
return (string)updates[packageID].author;
|
|
break;
|
|
case UPDATE_WEBSITE:
|
|
return (string)updates[packageID].website;
|
|
break;
|
|
case UPDATE_STATE:
|
|
return (updateState_t)updates[packageID].state;
|
|
break;
|
|
case UPDATE_ACTION:
|
|
return (updateAction_t)updates[packageID].pending_action;
|
|
break;
|
|
case UPDATE_FILESIZE:
|
|
return (int)updates[packageID].size;
|
|
break;
|
|
case UPDATE_PREVIEWIMAGE:
|
|
return (string)updates[packageID].preview_image;
|
|
break;
|
|
case UPDATE_STATUSSTRING:
|
|
/* if we have a action, focus on that */
|
|
switch (updates[packageID].pending_action) {
|
|
case UPDATEACTION_INSTALL:
|
|
if (updates[packageID].dlpercentage > 0.0) {
|
|
return sprintf("%d %%", updates[packageID].dlpercentage);
|
|
} else {
|
|
return _("UPDATE_PENDING_INSTALL");
|
|
}
|
|
break;
|
|
case UPDATEACTION_REINSTALL:
|
|
return _("UPDATE_PENDING_REINSTALL");
|
|
break;
|
|
case UPDATEACTION_UNINSTALL:
|
|
return _("UPDATE_PENDING_UNINSTALL");
|
|
break;
|
|
case UPDATEACTION_AUTOINSTALL:
|
|
return _("UPDATE_PENDING_AUTOINSTALL");
|
|
break;
|
|
case UPDATEACTION_DISABLE:
|
|
return _("UPDATE_PENDING_DISABLE");
|
|
break;
|
|
case UPDATEACTION_RETAIN:
|
|
return _("UPDATE_PENDING_RETAIN");
|
|
break;
|
|
default:
|
|
switch (updates[packageID].state) {
|
|
case UPDATESTATE_DISABLED:
|
|
return _("UPDATE_DISABLED");
|
|
break;
|
|
case UPDATESTATE_ENABLED:
|
|
return _("UPDATE_ENABLED");
|
|
break;
|
|
case UPDATESTATE_CORRUPT:
|
|
return _("UPDATE_CORRUPT");
|
|
break;
|
|
default:
|
|
return _("UPDATE_NOTINSTALLED");
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case UPDATE_DLPERCENTAGE:
|
|
return updates[packageID].dlpercentage;
|
|
break;
|
|
default:
|
|
return __NULL__;
|
|
}
|
|
}
|
|
|
|
bool
|
|
Updates_Available(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Updates_Toggle(int packageID)
|
|
{
|
|
if (packageID >= g_platform_update_count || packageID < 0i) {
|
|
print(sprintf("Updates_Toggle: Invalid package id %i!\n", packageID));
|
|
return false;
|
|
}
|
|
|
|
switch (updates[packageID].pending_action) {
|
|
case UPDATEACTION_INSTALL:
|
|
case UPDATEACTION_REINSTALL:
|
|
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
|
|
break;
|
|
case UPDATEACTION_UNINSTALL:
|
|
case UPDATEACTION_DISABLE:
|
|
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
|
|
break;
|
|
default:
|
|
if (updates[packageID].state == UPDATESTATE_ENABLED) {
|
|
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
|
|
} else {
|
|
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
|
|
}
|
|
break;
|
|
}
|
|
localcmd(sprintf("platformRefreshUpdates %i\n", packageID));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Updates_Install(int packageID)
|
|
{
|
|
if (packageID >= g_platform_update_count || packageID < 0i) {
|
|
print(sprintf("Updates_Install: Invalid package id %i!\n", packageID));
|
|
return false;
|
|
}
|
|
|
|
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
|
|
localcmd(sprintf("platformRefreshUpdates %i\n", packageID));
|
|
print(sprintf("Marking package %s for install.\n", updates[packageID].title));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Updates_Remove(int packageID)
|
|
{
|
|
if (packageID >= g_platform_update_count || packageID < 0i) {
|
|
print(sprintf("Updates_Remove: Invalid package id %i!\n", packageID));
|
|
return false;
|
|
}
|
|
|
|
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
|
|
localcmd(sprintf("platformRefreshUpdates %i\n", packageID));
|
|
print(sprintf("Marking package %s for 'removal'.\n", updates[packageID].title));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Updates_Destroy(int packageID)
|
|
{
|
|
if (packageID >= g_platform_update_count || packageID < 0i) {
|
|
print(sprintf("Updates_Destroy: Invalid package id %i!\n", packageID));
|
|
return false;
|
|
}
|
|
|
|
localcmd(sprintf("pkg del %s\n", updates[packageID].name));
|
|
localcmd(sprintf("platformRefreshUpdates %i\n", packageID));
|
|
print(sprintf("Marking package %s for 'deletion'.\n", updates[packageID].title));
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Updates_ApplyPendingChanges(void)
|
|
{
|
|
cvar_set("menu_updating", "1");
|
|
localcmd("pkg apply\n");
|
|
print("Applying package changes.\n");
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Updater_URI_Callback(float id, float code, string data, int resourcebytes)
|
|
{
|
|
/* game does not have a recommended package listing remotely? make something up. */
|
|
if (code == 404) {
|
|
string gameDir = cvar_string("fs_game");
|
|
games[GameLibrary_GetCurrentGame()].pkgname = strcat("cg_", gameDir, ";game_", gameDir, ";");
|
|
|
|
} else {
|
|
//print(sprintf("URI: %d %d %S %i\n", id, code, data, resourcebytes));
|
|
games[GameLibrary_GetCurrentGame()].pkgname = data;
|
|
}
|
|
|
|
updater_package_status = UPDATER_INITIALIZED;
|
|
Updates_Refresh();
|
|
} |