Skip to content

Commit

Permalink
Merge branch 'feature/addon-manager'
Browse files Browse the repository at this point in the history
  • Loading branch information
Grumbel committed Aug 25, 2014
2 parents 8415881 + 2b303a8 commit 5066ad6
Show file tree
Hide file tree
Showing 15 changed files with 1,177 additions and 597 deletions.
184 changes: 114 additions & 70 deletions src/addon/addon.cpp
@@ -1,5 +1,6 @@
// SuperTux - Add-on
// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
// 2014 Ingo Ruhnke <grumbel@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
Expand All @@ -20,112 +21,155 @@
#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
{
if (!installed) {
if (stored_md5 == "") { log_warning << "Add-on not installed and no stored MD5 available" << std::endl; }
return stored_md5;
}

if (calculated_md5 != "") return calculated_md5;
namespace {

if (installed_physfs_filename == "") throw std::runtime_error("Tried to calculate MD5 of Add-on with unknown filename");
static const char* s_allowed_characters = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

// 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);
Addon::Type addon_type_from_string(const std::string& type)
{
if (type == "world")
{
return Addon::WORLD;
}
else if (type == "worldmap")
{
return Addon::WORLDMAP;
}
else if (type == "levelset")
{
return Addon::LEVELSET;
}
else
{
throw std::runtime_error("not a valid Addon::Type: " + type);
}
PHYSFS_close(file);

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

return calculated_md5;
}

void
} // namespace

std::unique_ptr<Addon>
Addon::parse(const Reader& lisp)
{
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);
} catch(std::exception& e) {
std::unique_ptr<Addon> addon(new Addon);

try
{
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("url", addon->m_url);
lisp.get("md5", addon->m_md5);

return addon;
}
catch(const std::exception& err)
{
std::stringstream msg;
msg << "Problem when parsing addoninfo: " << e.what();
msg << "Problem when parsing addoninfo: " << err.what();
throw std::runtime_error(msg.str());
}
}

void
Addon::parse(std::string fname)
std::unique_ptr<Addon>
Addon::parse(const std::string& fname)
{
try {
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);
} catch(std::exception& e) {
if(!addon)
{
throw std::runtime_error("file is not a supertux-addoninfo file.");
}
else
{
return parse(*addon);
}
}
catch(const std::exception& err)
{
std::stringstream msg;
msg << "Problem when reading addoninfo '" << fname << "': " << e.what();
msg << "Problem when reading addoninfo '" << fname << "': " << err.what();
throw std::runtime_error(msg.str());
}
}

void
Addon::write(lisp::Writer& writer) const
Addon::Addon() :
m_id(),
m_version(0),
m_type(),
m_title(),
m_author(),
m_license(),
m_url(),
m_md5(),
m_install_filename(),
m_enabled(false)
{}

std::string
Addon::get_filename() const
{
writer.start_list("supertux-addoninfo");
if (kind != "") writer.write("kind", kind);
if (title != "") writer.write("title", title);
if (author != "") writer.write("author", author);
if (license != "") writer.write("license", license);
if (http_url != "") writer.write("http-url", http_url);
if (suggested_filename != "") writer.write("file", suggested_filename);
if (stored_md5 != "") writer.write("md5", stored_md5);
writer.end_list("supertux-addoninfo");
return get_id() + ".zip";
}

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

bool
Addon::is_installed() const
{
lisp::Writer writer(fname);
write(writer);
return !m_install_filename.empty();
}

bool
Addon::operator==(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, const std::string& md5)
{
m_install_filename = absolute_filename;
m_md5 = md5;
}

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 */
121 changes: 52 additions & 69 deletions src/addon/addon.hpp
@@ -1,5 +1,6 @@
// SuperTux - Add-on
// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
// 2014 Ingo Ruhnke <grumbel@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
Expand All @@ -17,82 +18,64 @@
#ifndef HEADER_SUPERTUX_ADDON_ADDON_HPP
#define HEADER_SUPERTUX_ADDON_ADDON_HPP

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

#include "util/reader_fwd.hpp"
#include "util/writer_fwd.hpp"

/**
* Represents an (available or installed) Add-on, e.g. a level set
*/
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;
/** PhysFS filename on disk, e.g. "pak0.zip" */
std::string installed_physfs_filename;
/** complete path and filename on disk, e.g. "/home/sommer/.supertux2/pak0.zip" */
std::string installed_absolute_filename;
std::string stored_md5;
bool installed;
bool loaded;

/**
* Get MD5, based either on installed file's contents or stored value
*/
std::string get_md5() const;

/**
* Read additional information from given contents of a (supertux-addoninfo ...) block
*/
void parse(const Reader& lisp);

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

/**
* Writes out Add-on metainformation to a Lisp Writer
*/
void write(Writer& writer) const;

/**
* Writes out Add-on metainformation to a file
*/
void write(std::string fname) 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==(Addon addon2) const;

protected:
friend class AddonManager;

mutable std::string calculated_md5;

Addon() :
kind(),
title(),
author(),
license(),
http_url(),
suggested_filename(),
installed_physfs_filename(),
installed_absolute_filename(),
stored_md5(),
installed(),
loaded(),
calculated_md5()
{};
static std::unique_ptr<Addon> parse(const Reader& lisp);
static std::unique_ptr<Addon> parse(const std::string& fname);

enum Type { WORLD, WORLDMAP, LEVELSET };

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_url;
std::string m_md5;

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

private:
Addon();

public:
std::string get_id() const { return m_id; }
int get_version() const { return m_version; }

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

std::string get_url() const { return m_url; }
std::string get_md5() const { return m_md5; }

std::string get_filename() const;
std::string get_install_filename() const;

bool is_installed() const;
bool is_enabled() const;

void set_install_filename(const std::string& absolute_filename, const std::string& md5);
void set_enabled(bool v);

private:
Addon(const Addon&) = delete;
Addon& operator=(const Addon&) = delete;
};

#endif
Expand Down

0 comments on commit 5066ad6

Please sign in to comment.