legacymenu: Update driver handling in downloader

Dynamically generated drivers have introduced a few breaking changes:

- Now, all drivers, whether generated or not, are stored into
GfLocalDir.
- Driver indexes, and therefore directory names, are recalculated every
time GfDrivers::reload is called, so the in-game must find the next
available index number on a given subdirectory.
- As a consequence of this, the file-based timestamp mechanism used in
cars and tracks cannot be used for drivers, since the directory name can
(and will) change at any time. Instead, GfDrivers::self() is looked up.


Former-commit-id: 05dcbec981a449be50e8656f105481b77881508b
Former-commit-id: c965c211b9e427f37317c84c16a6746f1b817eff
This commit is contained in:
Xavier Del Campo Romero 2024-11-19 08:04:09 +01:00
parent 30144d1dfd
commit 8014db4013
3 changed files with 147 additions and 67 deletions

View file

@ -9,10 +9,12 @@
*/
#include "asset.h"
#include <drivers.h>
#include <tgf.h>
#include <cjson/cJSON.h>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
@ -75,7 +77,8 @@ std::string Asset::path() const
return "cars/models/";
case Asset::driver:
return "drivers/";
// In the case of drivers, category means "driver type".
return "drivers/" + category + "/";
case Asset::track:
return "tracks/" + category + "/";
@ -84,6 +87,56 @@ std::string Asset::path() const
return "";
}
std::string Asset::basedir() const
{
switch (type)
{
case Asset::car:
case Asset::track:
return GfDataDir();
case Asset::driver:
return GfLocalDir();
}
return "";
}
std::string Asset::dstdir() const
{
switch (type)
{
case Asset::car:
case Asset::track:
return path() + directory;
case Asset::driver:
{
int idx = 0;
// In the case of drivers, category means "driver type".
std::vector<GfDriver *> drivers =
GfDrivers::self()->getDriversWithTypeAndCategory(category);
for (const GfDriver *d : drivers)
{
int drvidx = d->getInterfaceIndex();
if (d->getName() == name)
{
idx = drvidx;
break;
}
else if (drvidx >= idx)
idx = drvidx + 1;
}
return path() + std::to_string(idx) + "/";
}
}
return "";
}
int Asset::parse(const cJSON *c)
{
struct field
@ -173,3 +226,88 @@ bool Asset::operator==(const Asset &other) const
directory == other.directory &&
size == other.size;
}
int Asset::needs_update(bool &out) const
{
switch (type)
{
case Asset::type::car:
case Asset::type::track:
{
std::string path = basedir() + this->path() + directory
+ "/.revision";
return needs_update(path, out);
}
case Asset::type::driver:
return needs_update_drv(out);
}
return -1;
}
int Asset::needs_update(const std::string &path, bool &out) const
{
std::ifstream f(path, std::ios::binary);
if (!f.is_open())
return -1;
char v[sizeof "18446744073709551615"];
f.getline(v, sizeof v);
if (f.fail())
{
GfLogError("Error while reading revision\n");
return -1;
}
unsigned long long rev;
try
{
size_t pos;
rev = std::stoull(v, &pos);
if (pos != strlen(v))
{
GfLogError("Invalid number: %s\n", v);
return -1;
}
}
catch (const std::invalid_argument &e)
{
GfLogError("caught std::invalid_argument with %s\n", v);
return -1;
}
catch (const std::out_of_range &e)
{
GfLogError("caught std::out_of_range with %s\n", v);
return -1;
}
out = revision > rev;
return 0;
}
int Asset::needs_update_drv(bool &out) const
{
// In the case of drivers, category means "driver type".
std::vector<GfDriver *> drivers =
GfDrivers::self()->getDriversWithTypeAndCategory(category);
for (const GfDriver *d : drivers)
if (d->getName() == name)
{
int idx = d->getInterfaceIndex();
std::string path = basedir() + this->path() + std::to_string(idx)
+ "/.revision";
return needs_update(path, out);
}
return -1;
}

View file

@ -26,10 +26,15 @@ public:
size_t size;
unsigned long long revision;
std::string path() const;
std::string basedir() const;
std::string dstdir() const;
int needs_update(bool &out) const;
private:
int parse(const std::string &s, unsigned long long &size);
int check_dir(const std::string &d) const;
int needs_update(const std::string &path, bool &out) const;
int needs_update_drv(bool &out) const;
};
#endif

View file

@ -571,14 +571,6 @@ int DownloadsMenu::extract(const entry *e, const std::string &src,
std::string &error) const
{
const Asset &a = e->a;
const char *data = GfDataDir();
if (!data)
{
GfLogError("GfDataDir failed\n");
return -1;
}
std::string name;
if (randname(name))
@ -588,9 +580,9 @@ int DownloadsMenu::extract(const entry *e, const std::string &src,
return -1;
}
std::string tmp = data + name + "/";
std::string base = a.basedir(), tmp = base + name + "/";
unzip u(src, tmp, a.directory);
std::string dst = data + a.path() + a.directory,
std::string dst = base + a.dstdir(),
tmpd = tmp + a.directory;
int ret = -1;
@ -697,61 +689,6 @@ int DownloadsMenu::asset_fetched(CURLcode result, CURL *h, const sink *s,
return ret;
}
static int needs_update(const Asset &a, bool &out)
{
const char *data = GfDataDir();
if (!data)
{
GfLogError("GfDataDir failed\n");
return -1;
}
std::string path = data + a.path() + a.directory + "/.revision";
std::ifstream f(path, std::ios::binary);
if (!f.is_open())
return -1;
char v[sizeof "18446744073709551615"];
f.getline(v, sizeof v);
if (f.fail())
{
GfLogError("Error while reading revision\n");
return -1;
}
unsigned long long rev;
try
{
size_t pos;
rev = std::stoull(v, &pos);
if (pos != strlen(v))
{
GfLogError("Invalid number: %s\n", v);
return -1;
}
}
catch (const std::invalid_argument &e)
{
GfLogError("caught std::invalid_argument with %s\n", v);
return -1;
}
catch (const std::out_of_range &e)
{
GfLogError("caught std::out_of_range with %s\n", v);
return -1;
}
out = a.revision > rev;
return 0;
}
int DownloadsMenu::thumbnail_fetched(CURLcode result, CURL *h, const sink *s,
std::string &error)
{
@ -762,7 +699,7 @@ int DownloadsMenu::thumbnail_fetched(CURLcode result, CURL *h, const sink *s,
{
bool update;
if (needs_update(e->a, update))
if (e->a.needs_update(update))
e->state = entry::download;
else if (update)
e->state = entry::update;