Skip to content

Commit

Permalink
Large scale refactor/rewrite of the AddonManager, adding cleaner sepa…
Browse files Browse the repository at this point in the history
…ration between repository addons and installed ones
  • Loading branch information
Grumbel committed Aug 25, 2014
1 parent f80eed6 commit 0aab3bf
Show file tree
Hide file tree
Showing 12 changed files with 596 additions and 578 deletions.
149 changes: 98 additions & 51 deletions src/addon/addon.cpp
Expand Up @@ -20,67 +20,72 @@
#include <stdexcept>
#include <sstream>

#include "addon/md5.hpp"
#include "lisp/parser.hpp"
#include "util/reader.hpp"
#include "util/writer.hpp"
#include "util/log.hpp"

std::string
Addon::get_md5() const
namespace {

static const char* s_allowed_characters = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

Addon::Type addon_type_from_string(const std::string& type)
{
if (!installed)
if (type == "world")
{
if (stored_md5.empty())
{
log_warning << "Add-on not installed and no stored MD5 available" << std::endl;
}
return stored_md5;
return Addon::WORLD;
}
else if (!calculated_md5.empty())
else if (type == "worldmap")
{
return calculated_md5;
return Addon::WORLDMAP;
}
else if (installed_physfs_filename.empty())
else if (type == "levelset")
{
throw std::runtime_error("Tried to calculate MD5 of Add-on with unknown filename");
return Addon::LEVELSET;
}
else
{
// TODO: this does not work as expected for some files -- IFileStream seems to not always behave like an ifstream.
//IFileStream ifs(installed_physfs_filename);
//std::string md5 = MD5(ifs).hex_digest();

MD5 md5;
PHYSFS_file* file;
file = PHYSFS_openRead(installed_physfs_filename.c_str());
unsigned char buffer[1024];
while (true) {
PHYSFS_sint64 len = PHYSFS_read(file, buffer, 1, sizeof(buffer));
if (len <= 0) break;
md5.update(buffer, len);
}
PHYSFS_close(file);

calculated_md5 = md5.hex_digest();
log_debug << "MD5 of " << title << ": " << calculated_md5 << std::endl;

return calculated_md5;
throw std::runtime_error("not a valid Addon::Type: " + type);
}
}

void
} // namespace

std::unique_ptr<Addon>
Addon::parse(const Reader& lisp)
{
std::unique_ptr<Addon> addon(new Addon);

try
{
lisp.get("kind", kind);
lisp.get("title", title);
lisp.get("author", author);
lisp.get("license", license);
lisp.get("http-url", http_url);
lisp.get("file", suggested_filename);
lisp.get("md5", stored_md5);
if (!lisp.get("id", addon->m_id))
{
throw std::runtime_error("(id ...) field missing from addon description");
}

if (addon->m_id.empty())
{
throw std::runtime_error("addon id is empty");
}

if (addon->m_id.find_first_not_of(s_allowed_characters) != std::string::npos)
{
throw std::runtime_error("addon id contains illegal characters: " + addon->m_id);
}

lisp.get("version", addon->m_version);

std::string type;
lisp.get("type", type);
addon->m_type = addon_type_from_string(type);

lisp.get("title", addon->m_title);
lisp.get("author", addon->m_author);
lisp.get("license", addon->m_license);
lisp.get("http-url", addon->m_http_url);
lisp.get("md5", addon->m_md5);

return addon;
}
catch(const std::exception& err)
{
Expand All @@ -90,16 +95,22 @@ Addon::parse(const Reader& lisp)
}
}

void
std::unique_ptr<Addon>
Addon::parse(const std::string& fname)
{
try
{
lisp::Parser parser;
const lisp::Lisp* root = parser.parse(fname);
const lisp::Lisp* addon = root->get_lisp("supertux-addoninfo");
if(!addon) throw std::runtime_error("file is not a supertux-addoninfo file.");
parse(*addon);
if(!addon)
{
throw std::runtime_error("file is not a supertux-addoninfo file.");
}
else
{
return parse(*addon);
}
}
catch(const std::exception& err)
{
Expand All @@ -109,18 +120,54 @@ Addon::parse(const std::string& fname)
}
}

Addon::Addon() :
m_id(),
m_version(0),
m_type(),
m_title(),
m_author(),
m_license(),
m_http_url(),
m_md5(),
m_install_filename(),
m_enabled(false)
{}

std::string
Addon::get_filename() const
{
return get_id() + ".zip";
}

std::string
Addon::get_install_filename() const
{
return m_install_filename;
}

bool
Addon::is_installed() const
{
return !m_install_filename.empty();
}

bool
Addon::operator==(const Addon& addon2) const
Addon::is_enabled() const
{
std::string s1 = this->get_md5();
std::string s2 = addon2.get_md5();
return m_enabled;
}

if ((s1 != "") && (s2 != "")) return (s1 == s2);
void
Addon::set_install_filename(const std::string& absolute_filename)
{
m_install_filename = absolute_filename;
}

if (this->title != addon2.title) return false;
if (this->author != addon2.author) return false;
if (this->kind != addon2.kind) return false;
return true;
void
Addon::set_enabled(bool v)
{
m_enabled = v;
}


/* EOF */
91 changes: 38 additions & 53 deletions src/addon/addon.hpp
Expand Up @@ -17,74 +17,59 @@
#ifndef HEADER_SUPERTUX_ADDON_ADDON_HPP
#define HEADER_SUPERTUX_ADDON_ADDON_HPP

#include <assert.h>
#include <memory>
#include <string>

#include "util/reader_fwd.hpp"

class AddonDescription
class Addon
{
public:
std::string kind;
std::string title;
std::string author;
std::string license;
std::string http_url;
/** filename suggested by addon author, e.g. "pak0.zip" */
std::string suggested_filename;

AddonDescription() :
kind(),
title(),
author(),
license(),
http_url(),
suggested_filename()
{}
};
static std::unique_ptr<Addon> parse(const Reader& lisp);
static std::unique_ptr<Addon> parse(const std::string& fname);

/** Represents an (available or installed) Add-on, e.g. a level set */
class Addon : public AddonDescription
{
public:
int id;
enum Type { WORLD, WORLDMAP, LEVELSET };

/** PhysFS filename on disk, e.g. "pak0.zip" */
std::string installed_physfs_filename;
private:
// fields provided by the addon.zip itself
std::string m_id;
int m_version;
Type m_type;
std::string m_title;
std::string m_author;
std::string m_license;

// additional fields provided for addons from an addon repository
std::string m_http_url;
std::string m_md5;

// fields filled by the AddonManager
std::string m_install_filename;
bool m_enabled;

/** complete path and filename on disk, e.g. "/home/sommer/.supertux2/pak0.zip" */
std::string installed_absolute_filename;
private:
Addon();

std::string stored_md5;
bool installed;
bool loaded;
public:
std::string get_id() const { return m_id; }

/** Get MD5, based either on installed file's contents or stored value */
std::string get_md5() const;
Type get_type() const { return m_type; }
std::string get_title() const { return m_title; }
std::string get_author() const { return m_author; }
std::string get_license() const { return m_license; }

/** Read additional information from given contents of a (supertux-addoninfo ...) block */
void parse(const Reader& lisp);
std::string get_http_url() const { return m_http_url; }
std::string get_md5() const { return m_md5; }

/** Read additional information from given file */
void parse(const std::string& fname);
std::string get_filename() const;
std::string get_install_filename() const;

/** Checks if Add-on is the same as given one. If available, checks
MD5 sum, else relies on kind, author and title alone. */
bool operator==(const Addon& addon2) const;
bool is_installed() const;
bool is_enabled() const;

public:
friend class AddonManager;

mutable std::string calculated_md5;

Addon(int id_) :
id(id_),
installed_physfs_filename(),
installed_absolute_filename(),
stored_md5(),
installed(),
loaded(),
calculated_md5()
{};
void set_install_filename(const std::string& absolute_filename);
void set_enabled(bool v);

private:
Addon(const Addon&) = delete;
Expand Down

0 comments on commit 0aab3bf

Please sign in to comment.