nuclide/src/platform/updates.qc

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();
}