aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2010-01-14 01:38:38 +0100
committerAlexander Sulfrian <alexander@sulfrian.net>2013-01-05 17:17:44 +0100
commit7a4604e5b1c0280d1ad76c6742a6bc6c9ea92545 (patch)
tree31662e563475a1d95aaf98558de06c1f3d85a643 /src
parent73c923aed131ccdedc4b6a1a1bd1862960a8aa45 (diff)
downloadusdx-7a4604e5b1c0280d1ad76c6742a6bc6c9ea92545.tar.gz
usdx-7a4604e5b1c0280d1ad76c6742a6bc6c9ea92545.tar.xz
usdx-7a4604e5b1c0280d1ad76c6742a6bc6c9ea92545.zip
major refactor of stats structure
Diffstat (limited to '')
-rw-r--r--src/base/database.cpp403
-rw-r--r--src/base/database.hpp157
-rw-r--r--src/base/stats.cpp386
-rw-r--r--src/base/stats.hpp203
-rw-r--r--src/base/stats_database.cpp196
-rw-r--r--src/base/stats_database.hpp76
6 files changed, 895 insertions, 526 deletions
diff --git a/src/base/database.cpp b/src/base/database.cpp
index 7fc2c825..a8b9aded 100644
--- a/src/base/database.cpp
+++ b/src/base/database.cpp
@@ -30,25 +30,17 @@
namespace usdx
{
- const int db_version = 1;
- const char usdx_scores[] = "us_scores";
- const char usdx_songs[] = "us_songs";
- const char usdx_statistics_info[] = "us_statistics_info";
+ log4cxx::LoggerPtr Database::log = log4cxx::Logger::getLogger("usdx.base.Database");
- StatDatabase* StatDatabase::instance;
- log4cxx::LoggerPtr StatDatabase::log = log4cxx::Logger::getLogger("usdx.base.StatDatabase");
-
- StatDatabase::StatDatabase(const std::string filename)
+ Database::Database(const std::string filename)
{
- this->filename = filename;
-
- if (SQLITE_OK != sqlite3_open_v2(this->filename.c_str(), &this->database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
+ if (SQLITE_OK != sqlite3_open_v2(filename.c_str(), &this->database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
sqlite3_close(database);
throw "Error opening database.";
}
}
- StatDatabase::~StatDatabase(void)
+ Database::~Database(void)
{
/* frees database */
LOG4CXX_DEBUG(log, "Closing Database");
@@ -57,12 +49,7 @@ namespace usdx
database = NULL;
}
- StatDatabase* StatDatabase::get_instance()
- {
- return instance;
- }
-
- sqlite3_stmt *StatDatabase::sqlite_prepare(const std::string sqlStatement)
+ sqlite3_stmt *Database::sqlite_prepare(const std::string sqlStatement)
{
sqlite3_stmt *sqliteStatement;
if (SQLITE_OK != sqlite3_prepare_v2(database, sqlStatement.c_str(), sqlStatement.length(), &sqliteStatement, NULL)) {
@@ -75,14 +62,14 @@ namespace usdx
return sqliteStatement;
}
- void StatDatabase::sqlite_exec(const std::string sqlStatement)
+ void Database::sqlite_exec(const std::string sqlStatement)
{
sqlite3_stmt *sqliteStatement = sqlite_prepare(sqlStatement);
sqlite3_step(sqliteStatement);
sqlite3_finalize(sqliteStatement);
}
- const bool StatDatabase::sqlite_table_exists(const std::string table)
+ const bool Database::sqlite_table_exists(const std::string table)
{
std::string sql = "select [name] from [sqlite_master] where [type] = 'table' and [tbl_name] = ?1;";
sqlite3_stmt *sqliteStatement = sqlite_prepare(sql);
@@ -102,13 +89,9 @@ namespace usdx
return result;
}
- const bool StatDatabase::sqlite_table_contains_column(const std::string table, const std::string column)
+ const bool Database::sqlite_table_contains_column(const std::string table, const std::string column)
{
- std::string sqlStatement = "PRAGMA TABLE_INFO([";
- sqlStatement += table;
- sqlStatement += "]);";
-
- sqlite3_stmt *sqliteStatement = sqlite_prepare(sqlStatement);
+ sqlite3_stmt *sqliteStatement = sqlite_prepare("PRAGMA TABLE_INFO([" + table + "]);");
bool result = false;
int rc = sqlite3_step(sqliteStatement);
@@ -127,138 +110,7 @@ namespace usdx
return result;
}
- void StatDatabase::init(const std::string filename)
- {
- LOG4CXX_DEBUG(log, "Initializing Database: " << filename);
-
- instance = new StatDatabase(filename);
-
- try {
- if (! instance->sqlite_table_exists(usdx_statistics_info)) {
- // add table usdx_statistics_info, needed in the conversion from 1.01 to 1.1
- LOG4CXX_INFO(log, "Outdated song database found - missing table'" << usdx_statistics_info << "'");
-
- std::string sqlStatement;
-
- sqlStatement = "CREATE TABLE IF NOT EXISTS [";
- sqlStatement += usdx_statistics_info;
- sqlStatement += "] ([ResetTime] Integer);";
-
- instance->sqlite_exec(sqlStatement);
-
- // insert creation timestamp
- sqlStatement = "INSERT INTO [";
- sqlStatement += usdx_statistics_info;
- sqlStatement += "] ([ResetTime]) VALUES (?1);";
-
- sqlite3_stmt *sqliteStatement = instance->sqlite_prepare(sqlStatement);
- sqlite3_bind_int(sqliteStatement, 1, time(NULL));
- sqlite3_step(sqliteStatement);
- sqlite3_finalize(sqliteStatement);
- }
-
- int version = instance->get_version();
-
- bool finalizeConversion = false;
- if (version == 0 && instance->sqlite_table_exists("US_Scores")) {
- // convert data from 1.01 to 1.1
- // part #1 - prearrangement
- // rename old tables - to be able to insert new table structures
- instance->sqlite_exec("ALTER TABLE US_Scores RENAME TO us_scores_101;");
- instance->sqlite_exec("ALTER TABLE US_Songs RENAME TO us_songs_101;");
- }
-
- if (version == 0) {
- // Set version number after creation
- instance->set_version(db_version);
- }
-
- // SQLite does not handle VARCHAR(n) or INT(n) as expected.
- // Texts do not have a restricted length, no matter which type is used,
- // so use the native TEXT type. INT(n) is always INTEGER.
- // In addition, SQLiteTable3 will fail if other types than the native SQLite
- // types are used (especially FieldAsInteger). Also take care to write the
- // types in upper-case letters although SQLite does not care about this -
- // SQLiteTable3 is very sensitive in this regard.
- std::string sqlStatement;
-
- sqlStatement = "CREATE TABLE IF NOT EXISTS [";
- sqlStatement += usdx_scores;
- sqlStatement += "] (";
- sqlStatement += "[SongID] INTEGER NOT NULL, ";
- sqlStatement += "[Difficulty] INTEGER NOT NULL, ";
- sqlStatement += "[Player] TEXT NOT NULL, ";
- sqlStatement += "[Score] INTEGER NOT NULL, ";
- sqlStatement += "[Date] INTEGER NULL";
- sqlStatement += ");";
-
- instance->sqlite_exec(sqlStatement);
-
- sqlStatement = "CREATE TABLE IF NOT EXISTS [";
- sqlStatement += usdx_songs;
- sqlStatement += "] (";
- sqlStatement += "[ID] INTEGER PRIMARY KEY, ";
- sqlStatement += "[Artist] TEXT NOT NULL, ";
- sqlStatement += "[Title] TEXT NOT NULL, ";
- sqlStatement += "[TimesPlayed] INTEGER NOT NULL, ";
- sqlStatement += "[Rating] INTEGER NULL";
- sqlStatement += ");";
-
- instance->sqlite_exec(sqlStatement);
-
- if (finalizeConversion) {
- // convert data from 1.01 to 1.1
- // part #2 - accomplishment
- LOG4CXX_INFO(log, "Outdated song database found - begin conversion from V1.01 to V1.1");
-
- // insert old values into new db-schemes (/tables)
- sqlStatement = "INSERT INTO [";
- sqlStatement += usdx_scores;
- sqlStatement += "] SELECT [SongID], [Difficulty], [Player], [Score] FROM [us_scores_101];";
- instance->sqlite_exec(sqlStatement);
-
- sqlStatement = "INSERT INTO [";
- sqlStatement += usdx_songs;
- sqlStatement += "] SELECT [ID], [Artist], [Title], [TimesPlayed], NULL FROM [us_songs_101];";
- instance->sqlite_exec(sqlStatement);
-
- // now drop old tables
- instance->sqlite_exec("DROP TABLE us_scores_101;");
- instance->sqlite_exec("DROP TABLE us_songs_101;");
- }
-
- // add column rating to cUS_Songs
- // just for users of nightly builds and developers!
- if (! instance->sqlite_table_contains_column(usdx_songs, "Rating")) {
- LOG4CXX_INFO(log, "Outdated song database found - adding column rating to '" << usdx_songs << "'");
-
- sqlStatement = "ALTER TABLE [";
- sqlStatement += usdx_songs;
- sqlStatement += "] ADD COLUMN [Rating] INTEGER NULL;";
- instance->sqlite_exec(sqlStatement);
- }
-
- //add column date to cUS-Scores
- if (! instance->sqlite_table_contains_column(usdx_scores, "Date")) {
- LOG4CXX_INFO(log, "Outdated score database found - adding column date to '" << usdx_scores << "'");
-
- sqlStatement = "ALTER TABLE [";
- sqlStatement += usdx_scores;
- sqlStatement += "] ADD COLUMN [Date] INTEGER NULL;";
- instance->sqlite_exec(sqlStatement);
- }
- }
- catch (...) {
- // TODO:
- // catch (Execption e) {
- // LOG4CXX_ERROR(log, e.message);
-
- delete instance;
- // throw e;
- }
- }
-
- int StatDatabase::get_version(void)
+ const int Database::get_version(void)
{
int result = -1;
sqlite3_stmt *sqliteStatement = sqlite_prepare("PRAGMA user_version;");
@@ -272,7 +124,7 @@ namespace usdx
return result;
}
- void StatDatabase::set_version(int version)
+ void Database::set_version(const int version)
{
// format the PRAGMA statement (PRAGMA does _not_ support parameters)
std::ostringstream sqlStatementBuffer (std::ostringstream::out);
@@ -280,237 +132,4 @@ namespace usdx
sqlite_exec(sqlStatementBuffer.str());
}
-
-/* void StatDatabase::read_score(Song *song)
- {
- // TODO
- // var
- // TableData: TSQLiteUniTable;
- // Difficulty: integer;
- // I: integer;
- // PlayerListed: boolean;
- // begin
- // if not Assigned(ScoreDB) then
- // Exit;
-
- // TableData := nil;
- // try
- // // Search Song in DB
- // TableData := ScoreDB.GetUniTable(
- // 'SELECT [Difficulty], [Player], [Score], [Date] FROM [' + cUS_Scores + '] ' +
- // 'WHERE [SongID] = (' +
- // 'SELECT [ID] FROM [' + cUS_Songs + '] ' +
- // 'WHERE [Artist] = ? AND [Title] = ? ' +
- // 'LIMIT 1) ' +
- // 'ORDER BY [Score] DESC;', //no LIMIT! see filter below!
- // [Song.Artist, Song.Title]);
-
- // // Empty Old Scores
- // SetLength(Song.Score[0], 0); //easy
- // SetLength(Song.Score[1], 0); //medium
- // SetLength(Song.Score[2], 0); //hard
-
- // // Go through all Entrys
- // while (not TableData.EOF) do
- // begin
- // // Add one Entry to Array
- // Difficulty := TableData.FieldAsInteger(TableData.FieldIndex['Difficulty']);
- // if ((Difficulty >= 0) and (Difficulty <= 2)) and
- // (Length(Song.Score[Difficulty]) < 5) then
- // begin
- // //filter player
- // PlayerListed:=false;
- // if (Length(Song.Score[Difficulty])>0) then
- // begin
- // for I := 0 to Length(Song.Score[Difficulty]) - 1 do
- // begin
- // if (Song.Score[Difficulty, I].Name = TableData.FieldByName['Player']) then
- // begin
- // PlayerListed:=true;
- // break;
- // end;
- // end;
- // end;
-
- // if not PlayerListed then
- // begin
- // SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1);
-
- // Song.Score[Difficulty, High(Song.Score[Difficulty])].Name :=
- // TableData.FieldByName['Player'];
- // Song.Score[Difficulty, High(Song.Score[Difficulty])].Score :=
- // TableData.FieldAsInteger(TableData.FieldIndex['Score']);
- // Song.Score[Difficulty, High(Song.Score[Difficulty])].Date :=
- // FormatDate(TableData.FieldAsInteger(TableData.FieldIndex['Date']));
- // end;
- // end;
-
- // TableData.Next;
- // end; // while
-
- // except
- // for Difficulty := 0 to 2 do
- // begin
- // SetLength(Song.Score[Difficulty], 1);
- // Song.Score[Difficulty, 1].Name := 'Error Reading ScoreDB';
- // end;
- // end;
-
- // TableData.Free;
- }
-
- void StatDatabase::add_score(Song *song, int level, const char* name, int score)
- {
- // TODO
- // var
- // ID: integer;
- // TableData: TSQLiteTable;
- // begin
- // if not Assigned(ScoreDB) then
- // Exit;
-
- // // Prevent 0 Scores from being added EDIT: ==> UScreenTop5.pas!
- // //if (Score <= 0) then
- // // Exit;
-
- // TableData := nil;
-
- // try
-
- // ID := ScoreDB.GetTableValue(
- // 'SELECT [ID] FROM [' + cUS_Songs + '] ' +
- // 'WHERE [Artist] = ? AND [Title] = ?',
- // [Song.Artist, Song.Title]);
- // if (ID = 0) then
- // begin
- // // Create song if it does not exist
- // ScoreDB.ExecSQL(
- // 'INSERT INTO [' + cUS_Songs + '] ' +
- // '([ID], [Artist], [Title], [TimesPlayed]) VALUES ' +
- // '(NULL, ?, ?, 0);',
- // [Song.Artist, Song.Title]);
- // // Get song-ID
- // ID := ScoreDB.GetLastInsertRowID();
- // end;
- // // Create new entry
- // ScoreDB.ExecSQL(
- // 'INSERT INTO [' + cUS_Scores + '] ' +
- // '([SongID] ,[Difficulty], [Player], [Score], [Date]) VALUES ' +
- // '(?, ?, ?, ?, ?);',
- // [ID, Level, Name, Score, DateTimeToUnix(Now())]);
-
- // except on E: Exception do
- // Log.LogError(E.Message, 'TDataBaseSystem.AddScore');
- // end;
-
- // TableData.Free;
- }
-
- void StatDatabase::write_score(Song *song)
- {
- // TODO
- // if not Assigned(ScoreDB) then
- // Exit;
-
- // try
- // // Increase TimesPlayed
- // ScoreDB.ExecSQL(
- // 'UPDATE [' + cUS_Songs + '] ' +
- // 'SET [TimesPlayed] = [TimesPlayed] + 1 ' +
- // 'WHERE [Title] = ? AND [Artist] = ?;',
- // [Song.Title, Song.Artist]);
- // except on E: Exception do
- // Log.LogError(E.Message, 'TDataBaseSystem.WriteScore');
- // end;
- }
-
-*/
- const std::string StatDatabase::get_filename(void)
- {
- return this->filename;
- }
-
-
- StatResult* StatDatabase::get_stats(StatResult *list, short count, unsigned int page, bool reversed)
- {
- // TODO
- return NULL;
- }
-
-/* unsigned int StatDatabase::get_total_entrys(StatType type)
- {
- // TODO
- // var
- // Query: string;
- // begin
- // Result := 0;
-
- // if not Assigned(ScoreDB) then
- // Exit;
-
- // try
- // // Create query
- // case Typ of
- // stBestScores:
- // Query := 'SELECT COUNT([SongID]) FROM [' + cUS_Scores + '];';
- // stBestSingers:
- // Query := 'SELECT COUNT(DISTINCT [Player]) FROM [' + cUS_Scores + '];';
- // stMostSungSong:
- // Query := 'SELECT COUNT([ID]) FROM [' + cUS_Songs + '];';
- // stMostPopBand:
- // Query := 'SELECT COUNT(DISTINCT [Artist]) FROM [' + cUS_Songs + '];';
- // end;
-
- // Result := ScoreDB.GetTableValue(Query);
- // except on E: Exception do
- // Log.LogError(E.Message, 'TDataBaseSystem.GetTotalEntrys');
- // end;
- }
-*/
- time_t StatDatabase::get_stat_reset(void)
- {
- int result = -1;
-
- std::string sqlStatement = "SELECT [ResetTime] FROM [";
- sqlStatement += usdx_statistics_info;
- sqlStatement += "];";
-
- sqlite3_stmt *sqliteStatement = sqlite_prepare(sqlStatement);
-
- int rc = sqlite3_step(sqliteStatement);
- if (rc == SQLITE_ROW) {
- result = sqlite3_column_int(sqliteStatement, 0);
- }
-
- sqlite3_finalize(sqliteStatement);
- return (time_t)result;
- }
-
- char* StatDatabase::format_date(char* time, size_t max, time_t timestamp)
- {
- if (timestamp != 0) {
- struct tm tmp;
-
- if (localtime_r(&timestamp, &tmp)) {
- strftime(time, max, "%d.%m.%y" /* TODO: Language.Translate("STAT_FORMAT_DATE")*/, &tmp);
- return time;
- }
- }
-
- if (max > 0) {
- time[0] = '\0';
- return time;
- }
-
- return NULL;
- }
-
- /* recursive delete the list items */
- StatResult::~StatResult(void)
- {
- if (this->next) {
- delete this->next;
- this->next = NULL;
- }
- }
};
diff --git a/src/base/database.hpp b/src/base/database.hpp
index 08087c7f..ca13ec8c 100644
--- a/src/base/database.hpp
+++ b/src/base/database.hpp
@@ -27,41 +27,39 @@
#ifndef DATABASE_HPP
#define DATABASE_HPP
-#include <ctime>
-#include <iostream>
#include <string>
#include <sqlite3.h>
#include <log4cxx/logger.h>
namespace usdx
{
- class StatResult;
-
- /* Wrapper for statistic database */
- class StatDatabase
+ /**
+ * Abstract base class for all sqlite databases.
+ */
+ class Database
{
private:
static log4cxx::LoggerPtr log;
- /**
- * Filename of the opened statistic database.
- */
- std::string filename;
-
+ protected:
/**
* Internal reference to the sqlite database handle of the open
* sqlite database.
*/
sqlite3 *database;
+ Database(std::string filename);
+ virtual ~Database(void);
+
+ public:
/**
- * Wrapper arround the sqlite_prepare_v2 function with propper
+ * Wrapper around the sqlite_prepare_v2 function with propper
* logging and exception throwing on error.
*
* @param sqlStatement SQL Statement for preparing to
* sqlite3_stmt
- * @return Pointner to a sqlite3_stmt used for binding
- * parameters and excuting the statement. Need to be freed
+ * @return Pointer to a sqlite3_stmt used for binding
+ * parameters and executing the statement. Need to be freed
* with sqlite3_finalize.
*/
sqlite3_stmt *sqlite_prepare(const std::string sqlStatement);
@@ -89,133 +87,24 @@ namespace usdx
*/
const bool sqlite_table_contains_column(const std::string table, const std::string column);
- // Singleton
- StatDatabase(std::string filename);
-
- static StatDatabase* instance;
-
- protected:
- int get_version(void);
- void set_version(int version);
-
- public:
- static StatDatabase* get_instance();
-
- ~StatDatabase(void);
-
/**
- * Opens a sqlite3 database from the given path and initializes
- * that class by saving a reference to the open database handle.
+ * Queries the user version from the sqlite database. This is a
+ * free settable additional field to identify the version of the
+ * schemata in the database.
*
- * @param filename Filename of a database to open.
+ * @see set_version(const int version)
+ * @return Value of the user_version setting of the sqlite
+ * database
*/
- static void init(const std::string filename);
-
-/* void read_score(Song *song);
- void add_score(Song *song, int level, const char* name, int score);
- void write_score(Song *song);
-*/
- const std::string get_filename(void);
-
- StatResult* get_stats(StatResult *list, short count, unsigned int page, bool reversed);
-// unsigned int get_total_entrys(StatType type);
+ const int get_version(void);
/**
- * Get the timestamp of the last reset of the database.
+ * Set the user version of the database.
*
- * @return Timestamp
+ * @see get_version(void)
+ * @param version Current scheme version.
*/
- time_t get_stat_reset(void);
-
- /**
- * Convert a timestamp to a data representation in a string.
- *
- * @param time Pointer to a char buffer that will contain the
- * the date string.
- * @param max Maximum bytes that could be written to the buffer.
- * @param timestamp Timestamp to convert to the string.
- * @return Pointer to the buffer supplied as first parameter,
- * containing:
- * - only a '\\0' at first position if timestamp was
- * 0 or if max was to short to contain the date
- * - the date string with the terminating '\\0'
- */
- char* format_date(char* time, size_t max, time_t timestamp);
-
-#ifdef STAT_DATABASE_TEST
- // for testing private members
- friend class StatDatabaseTest;
-#endif
- };
-
- /* Element for linked list with pointer to next */
- class StatResult
- {
- private:
- StatResult() {};
- StatResult(const StatResult &source) {};
- void operator=(const StatResult &source) {};
-
- protected:
- StatResult *next;
-
- public:
- virtual ~StatResult(void);
- };
-
- class StatResultBestScores : StatResult
- {
- private:
- char *singer;
- unsigned short score;
- unsigned short difficulty;
- char *song_artist;
- char *song_title;
- time_t date;
-
- public:
- StatResultBestScores(
- char *singer,
- unsigned short score,
- unsigned short difficulty,
- char* song_artist,
- char* song_title,
- time_t date);
- ~StatResultBestScores(void);
- };
-
- class StatResultBestSingers : StatResult
- {
- private:
- char *singer;
- unsigned short average_score;
-
- public:
- StatResultBestSingers(char *singer, unsigned short average_score);
- ~StatResultBestSingers(void);
- };
-
- class StatResultMostSungSong : StatResult
- {
- private:
- char *song_artist;
- char *song_title;
- unsigned short times_sung;
-
- public:
- StatResultMostSungSong(char* song_artist, char* song_title, unsigned short times_sung);
- ~StatResultMostSungSong(void);
- };
-
- class StatResultMostSungBand : StatResult
- {
- private:
- char *song_artist;
- unsigned short times_sung;
-
- public:
- StatResultMostSungBand(char* song_artist, unsigned short times_sung);
- ~StatResultMostSungBand(void);
+ void set_version(const int version);
};
};
diff --git a/src/base/stats.cpp b/src/base/stats.cpp
new file mode 100644
index 00000000..613b4f33
--- /dev/null
+++ b/src/base/stats.cpp
@@ -0,0 +1,386 @@
+/*
+ * UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "stats.hpp"
+
+namespace usdx
+{
+ StatDatabase *Stats::db = NULL;
+ std::string Stats::filename = std::string("");
+
+ log4cxx::LoggerPtr Stats::log =
+ log4cxx::Logger::getLogger("usdx.base.Stats");
+
+ StatDatabase *Stats::get_database(void)
+ {
+ if (filename == "")
+ {
+ LOG4CXX_ERROR(log, "You have to set a filename first.");
+ throw "You have to set a filename first.";
+ }
+
+ if (db == NULL) {
+ db = new StatDatabase(filename);
+ }
+
+ return db;
+ }
+
+ int Stats::get_count(std::string query)
+ {
+ int result = 0;
+ sqlite3_stmt *sqliteStatement =
+ get_database()->sqlite_prepare(query);
+
+ int rc = sqlite3_step(sqliteStatement);
+ if (rc == SQLITE_ROW) {
+ result = sqlite3_column_int(sqliteStatement, 0);
+ }
+
+ sqlite3_finalize(sqliteStatement);
+ return result;
+
+ }
+
+ void Stats::set_filename(std::string filename)
+ {
+ Stats::filename = filename;
+
+ // close old database
+ delete db;
+ db = NULL;
+ }
+
+ std::string Stats::get_filename(void)
+ {
+ return filename;
+ }
+
+ time_t Stats::get_stat_reset(void)
+ {
+ int result = -1;
+
+ sqlite3_stmt *sqliteStatement = get_database()->sqlite_prepare(
+ "SELECT [ResetTime] FROM [" +
+ get_database()->usdx_statistics_info + "];");
+
+ int rc = sqlite3_step(sqliteStatement);
+ if (rc == SQLITE_ROW) {
+ result = sqlite3_column_int(sqliteStatement, 0);
+ }
+
+ sqlite3_finalize(sqliteStatement);
+ return (time_t)result;
+ }
+
+/* void Stats::add_score(Song *song, int level, const char* player, int score)
+ {
+ // TODO
+ // var
+ // ID: integer;
+ // TableData: TSQLiteTable;
+ // begin
+ // if not Assigned(ScoreDB) then
+ // Exit;
+
+ // // Prevent 0 Scores from being added EDIT: ==> UScreenTop5.pas!
+ // //if (Score <= 0) then
+ // // Exit;
+
+ // TableData := nil;
+
+ // try
+
+ // ID := ScoreDB.GetTableValue(
+ // 'SELECT [ID] FROM [' + cUS_Songs + '] ' +
+ // 'WHERE [Artist] = ? AND [Title] = ?',
+ // [Song.Artist, Song.Title]);
+ // if (ID = 0) then
+ // begin
+ // // Create song if it does not exist
+ // ScoreDB.ExecSQL(
+ // 'INSERT INTO [' + cUS_Songs + '] ' +
+ // '([ID], [Artist], [Title], [TimesPlayed]) VALUES ' +
+ // '(NULL, ?, ?, 0);',
+ // [Song.Artist, Song.Title]);
+ // // Get song-ID
+ // ID := ScoreDB.GetLastInsertRowID();
+ // end;
+ // // Create new entry
+ // ScoreDB.ExecSQL(
+ // 'INSERT INTO [' + cUS_Scores + '] ' +
+ // '([SongID] ,[Difficulty], [Player], [Score], [Date]) VALUES ' +
+ // '(?, ?, ?, ?, ?);',
+ // [ID, Level, Name, Score, DateTimeToUnix(Now())]);
+
+ // except on E: Exception do
+ // Log.LogError(E.Message, 'TDataBaseSystem.AddScore');
+ // end;
+
+ // TableData.Free;
+ }
+*/
+
+/* void Stats::add_song(Song *song)
+ {
+ // TODO
+ // if not Assigned(ScoreDB) then
+ // Exit;
+
+ // try
+ // // Increase TimesPlayed
+ // ScoreDB.ExecSQL(
+ // 'UPDATE [' + cUS_Songs + '] ' +
+ // 'SET [TimesPlayed] = [TimesPlayed] + 1 ' +
+ // 'WHERE [Title] = ? AND [Artist] = ?;',
+ // [Song.Title, Song.Artist]);
+ // except on E: Exception do
+ // Log.LogError(E.Message, 'TDataBaseSystem.WriteScore');
+ // end;
+ }
+*/
+
+ StatResultBestScores::StatResultBestScores(char *singer, unsigned short score, unsigned short difficulty,
+ char* song_artist, char* song_title, time_t date)
+ {
+ this->singer = std::string(singer);
+ this->score = score;
+ this->difficulty = difficulty;
+ this->song_artist = std::string(song_artist);
+ this->song_title = std::string(song_title);
+ this->date = date;
+ this->next = NULL;
+ }
+
+ StatResultBestScores::StatResultBestScores(char* song_artist, char* song_title)
+ {
+ this->next = NULL;
+
+ // get score for this song from db
+ // TODO
+ // var
+ // TableData: TSQLiteUniTable;
+ // Difficulty: integer;
+ // I: integer;
+ // PlayerListed: boolean;
+ // begin
+ // if not Assigned(ScoreDB) then
+ // Exit;
+
+ // TableData := nil;
+ // try
+ // // Search Song in DB
+ // TableData := ScoreDB.GetUniTable(
+ // 'SELECT [Difficulty], [Player], [Score], [Date] FROM [' + cUS_Scores + '] ' +
+ // 'WHERE [SongID] = (' +
+ // 'SELECT [ID] FROM [' + cUS_Songs + '] ' +
+ // 'WHERE [Artist] = ? AND [Title] = ? ' +
+ // 'LIMIT 1) ' +
+ // 'ORDER BY [Score] DESC;', //no LIMIT! see filter below!
+ // [Song.Artist, Song.Title]);
+
+ // // Empty Old Scores
+ // SetLength(Song.Score[0], 0); //easy
+ // SetLength(Song.Score[1], 0); //medium
+ // SetLength(Song.Score[2], 0); //hard
+
+ // // Go through all Entrys
+ // while (not TableData.EOF) do
+ // begin
+ // // Add one Entry to Array
+ // Difficulty := TableData.FieldAsInteger(TableData.FieldIndex['Difficulty']);
+ // if ((Difficulty >= 0) and (Difficulty <= 2)) and
+ // (Length(Song.Score[Difficulty]) < 5) then
+ // begin
+ // //filter player
+ // PlayerListed:=false;
+ // if (Length(Song.Score[Difficulty])>0) then
+ // begin
+ // for I := 0 to Length(Song.Score[Difficulty]) - 1 do
+ // begin
+ // if (Song.Score[Difficulty, I].Name = TableData.FieldByName['Player']) then
+ // begin
+ // PlayerListed:=true;
+ // break;
+ // end;
+ // end;
+ // end;
+
+ // if not PlayerListed then
+ // begin
+ // SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1);
+
+ // Song.Score[Difficulty, High(Song.Score[Difficulty])].Name :=
+ // TableData.FieldByName['Player'];
+ // Song.Score[Difficulty, High(Song.Score[Difficulty])].Score :=
+ // TableData.FieldAsInteger(TableData.FieldIndex['Score']);
+ // Song.Score[Difficulty, High(Song.Score[Difficulty])].Date :=
+ // FormatDate(TableData.FieldAsInteger(TableData.FieldIndex['Date']));
+ // end;
+ // end;
+
+ // TableData.Next;
+ // end; // while
+
+ // except
+ // for Difficulty := 0 to 2 do
+ // begin
+ // SetLength(Song.Score[Difficulty], 1);
+ // Song.Score[Difficulty, 1].Name := 'Error Reading ScoreDB';
+ // end;
+ // end;
+
+ // TableData.Free;
+ }
+
+ StatResultBestScores::~StatResultBestScores(void)
+ {
+ if (next) {
+ delete next;
+ next = NULL;
+ }
+ }
+
+ StatResultBestScores *StatResultBestScores::get_next()
+ {
+ return next;
+ }
+
+ int StatResultBestScores::get_count(void)
+ {
+ return Stats::get_count("SELECT COUNT([SongID]) FROM [" +
+ get_database()->usdx_scores + "];");
+ }
+
+ StatResultBestScores *StatResultBestScores::get_stats()
+ {
+ // TODO
+ return NULL;
+ }
+
+
+
+ StatResultBestSingers::StatResultBestSingers(char *singer, unsigned short average_score)
+ {
+ this->singer = std::string(singer);
+ this->average_score = average_score;
+ this->next = NULL;
+ }
+
+ StatResultBestSingers::~StatResultBestSingers(void)
+ {
+ if (next) {
+ delete next;
+ next = NULL;
+ }
+ }
+
+ StatResultBestSingers *StatResultBestSingers::get_next()
+ {
+ return next;
+ }
+
+ int StatResultBestSingers::get_count(void)
+ {
+ return Stats::get_count(
+ "SELECT COUNT(DISTINCT [Player]) FROM ["
+ + get_database()->usdx_scores + "];");
+ }
+
+ StatResultBestSingers *StatResultBestSingers::get_stats()
+ {
+ // TODO
+ return NULL;
+ }
+
+
+ StatResultMostSungSong::StatResultMostSungSong(char* song_artist, char* song_title, unsigned short times_sung)
+ {
+ this->song_artist = std::string(song_artist);
+ this->song_title = std::string(song_title);
+ this->times_sung = times_sung;
+ this->next = NULL;
+ }
+
+ StatResultMostSungSong::~StatResultMostSungSong(void)
+ {
+ if (next) {
+ delete next;
+ next = NULL;
+ }
+ }
+
+ StatResultMostSungSong *StatResultMostSungSong::get_next()
+ {
+ return next;
+ }
+
+ int StatResultMostSungSong::get_count(void)
+ {
+ return Stats::get_count("SELECT COUNT([ID]) FROM [" +
+ get_database()->usdx_scores + "];");
+ }
+
+ StatResultMostSungSong *StatResultMostSungSong::get_stats()
+ {
+ // TODO
+ return NULL;
+ }
+
+
+ StatResultMostSungBand::StatResultMostSungBand(char* song_artist, unsigned short times_sung)
+ {
+ this->song_artist = std::string(song_artist);
+ this->times_sung = times_sung;
+ this->next = NULL;
+ }
+
+ StatResultMostSungBand::~StatResultMostSungBand(void)
+ {
+ if (next) {
+ delete next;
+ next = NULL;
+ }
+ }
+
+ StatResultMostSungBand *StatResultMostSungBand::get_next()
+ {
+ return next;
+ }
+
+ int StatResultMostSungBand::get_count(void)
+ {
+ return Stats::get_count(
+ "SELECT COUNT(DISTINCT [Artist]) FROM [" +
+ get_database()->usdx_scores + "];");
+ }
+
+ StatResultMostSungBand *StatResultMostSungBand::get_stats()
+ {
+ // TODO
+ return NULL;
+ }
+}
diff --git a/src/base/stats.hpp b/src/base/stats.hpp
new file mode 100644
index 00000000..b3068024
--- /dev/null
+++ b/src/base/stats.hpp
@@ -0,0 +1,203 @@
+/*
+ * UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef STATS_HPP
+#define STATS_HPP
+
+#include <string>
+#include <log4cxx/logger.h>
+#include "stats_database.hpp"
+
+namespace usdx
+{
+ /**
+ * Virtual base class for all different statistics.
+ */
+ class Stats
+ {
+ private:
+ static log4cxx::LoggerPtr log;
+
+ static StatDatabase *db;
+ static std::string filename;
+
+ protected:
+ /**
+ * Get the current StatDatabase object for making sqlite
+ * operations. On first execution after setting a filename, the
+ * database is open and initialized. If no filename was set an
+ * exception will be thrown. This function tries to ensure, that
+ * there is only one instance of StatDatabase. (not thread safe)
+ *
+ * @see set_filename(std::string filename)
+ * @return pointer to the StatDatabase object
+ */
+ static StatDatabase *get_database(void);
+
+ Stats(void) {};
+
+ /**
+ * Abstract virtual deconstructor. This is here to be able to
+ * delete all subclasses correctly even if there is only a Stats
+ * pointer.
+ */
+ virtual ~Stats(void) {};
+
+ /**
+ * TODO
+ */
+ static int get_count(std::string query);
+
+ public:
+ /**
+ * Get the timestamp of the last reset of the database.
+ *
+ * @return Timestamp
+ */
+ time_t get_stat_reset(void);
+
+ /**
+ * Set the filename of the file to use as statistic
+ * database. The filename has to be set before executing the
+ * get_database member function. If this function is called, the
+ * current statistic database is closed and a new with the given
+ * filename will be open on next access.
+ *
+ * @param filename Filename to use as statistic database.
+ */
+ static void set_filename(std::string filename);
+
+ /**
+ * Get the filename used as statistic database.
+ *
+ * @return Filename of the statistics database.
+ */
+ static std::string get_filename();
+
+ /**
+ * TODO
+ */
+// static void add_score(Song *song, int level, const char* player, int score);
+
+ /**
+ * TODD
+ */
+// static void add_song(Song *song);
+ };
+
+ class StatResultBestScores : public Stats
+ {
+ private:
+ std::string singer;
+ unsigned short score;
+ unsigned short difficulty;
+ std::string song_artist;
+ std::string song_title;
+ time_t date;
+
+ StatResultBestScores *next;
+
+ StatResultBestScores(
+ char *singer,
+ unsigned short score,
+ unsigned short difficulty,
+ char* song_artist,
+ char* song_title,
+ time_t date);
+ public:
+ /**
+ * TODO
+ */
+ StatResultBestScores(char* song_artist, char* song_title);
+
+ ~StatResultBestScores(void);
+
+ static int get_count(void);
+ static StatResultBestScores *get_stats();
+
+ StatResultBestScores *get_next();
+ };
+
+
+ class StatResultBestSingers : public Stats
+ {
+ private:
+ std::string singer;
+ unsigned short average_score;
+
+ StatResultBestSingers *next;
+
+ StatResultBestSingers(char *singer, unsigned short average_score);
+ public:
+ ~StatResultBestSingers(void);
+
+ static int get_count(void);
+ static StatResultBestSingers *get_stats();
+
+ StatResultBestSingers *get_next();
+ };
+
+
+ class StatResultMostSungSong : public Stats
+ {
+ private:
+ std::string song_artist;
+ std::string song_title;
+ unsigned short times_sung;
+
+ StatResultMostSungSong *next;
+
+ StatResultMostSungSong(char* song_artist, char* song_title, unsigned short times_sung);
+ public:
+ ~StatResultMostSungSong(void);
+
+ static int get_count(void);
+ static StatResultMostSungSong *get_stats();
+
+ StatResultMostSungSong *get_next();
+ };
+
+
+ class StatResultMostSungBand : public Stats
+ {
+ private:
+ std::string song_artist;
+ unsigned short times_sung;
+
+ StatResultMostSungBand *next;
+
+ StatResultMostSungBand(char* song_artist, unsigned short times_sung);
+ public:
+ ~StatResultMostSungBand(void);
+
+ static int get_count(void);
+ static StatResultMostSungBand *get_stats();
+
+ StatResultMostSungBand *get_next();
+ };
+};
+
+#endif
diff --git a/src/base/stats_database.cpp b/src/base/stats_database.cpp
new file mode 100644
index 00000000..deb896ef
--- /dev/null
+++ b/src/base/stats_database.cpp
@@ -0,0 +1,196 @@
+/*
+ * UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "stats_database.hpp"
+
+namespace usdx
+{
+ const int db_version = 1;
+ const std::string StatDatabase::usdx_scores =
+ "us_scores";
+
+ const std::string StatDatabase::usdx_songs =
+ "us_songs";
+
+ const std::string StatDatabase::usdx_statistics_info =
+ "us_statistics_info";
+
+ log4cxx::LoggerPtr StatDatabase::log =
+ log4cxx::Logger::getLogger("usdx.base.StatDatabase");
+
+ StatDatabase::StatDatabase(const std::string filename) :
+ Database(filename)
+ {
+ LOG4CXX_DEBUG(log, "Initializing Database: " << filename);
+
+ if (! sqlite_table_exists(usdx_statistics_info)) {
+ // add table usdx_statistics_info, needed in the
+ // conversion from 1.01 to 1.1
+ LOG4CXX_INFO(log, "Outdated song database found " <<
+ "- missing table'" <<
+ usdx_statistics_info << "'");
+
+ sqlite_exec("CREATE TABLE IF NOT EXISTS [" +
+ usdx_statistics_info +
+ "] ([ResetTime] Integer);");
+
+ // insert creation timestamp
+ sqlite3_stmt *sqliteStatement =
+ sqlite_prepare("INSERT INTO [" +
+ usdx_statistics_info +
+ "] ([ResetTime]) VALUES (?1);");
+
+ sqlite3_bind_int(sqliteStatement, 1, time(NULL));
+ sqlite3_step(sqliteStatement);
+ sqlite3_finalize(sqliteStatement);
+ }
+
+ int version = get_version();
+
+ bool finalizeConversion = false;
+ if (version == 0 && sqlite_table_exists("US_Scores")) {
+ // convert data from 1.01 to 1.1
+ // part #1 - prearrangement: rename old tables
+ // to be able to insert new table structures
+ sqlite_exec("ALTER TABLE US_Scores RENAME TO us_scores_101;");
+ sqlite_exec("ALTER TABLE US_Songs RENAME TO us_songs_101;");
+ }
+
+ if (version == 0) {
+ // Set version number after creation
+ set_version(db_version);
+ }
+
+ // SQLite does not handle VARCHAR(n) or INT(n) as expected.
+ // Texts do not have a restricted length, no matter which type
+ // is used, so use the native TEXT type. INT(n) is always
+ // INTEGER. In addition, SQLiteTable3 will fail if other types
+ // than the native SQLite types are used (especially
+ // FieldAsInteger). Also take care to write the types in
+ // upper-case letters although SQLite does not care about this -
+ // SQLiteTable3 is very sensitive in this regard.
+ std::string sqlStatement;
+
+ sqlStatement = "CREATE TABLE IF NOT EXISTS [";
+ sqlStatement += usdx_scores;
+ sqlStatement += "] (";
+ sqlStatement += "[SongID] INTEGER NOT NULL, ";
+ sqlStatement += "[Difficulty] INTEGER NOT NULL, ";
+ sqlStatement += "[Player] TEXT NOT NULL, ";
+ sqlStatement += "[Score] INTEGER NOT NULL, ";
+ sqlStatement += "[Date] INTEGER NULL";
+ sqlStatement += ");";
+
+ sqlite_exec(sqlStatement);
+
+ sqlStatement = "CREATE TABLE IF NOT EXISTS [";
+ sqlStatement += usdx_songs;
+ sqlStatement += "] (";
+ sqlStatement += "[ID] INTEGER PRIMARY KEY, ";
+ sqlStatement += "[Artist] TEXT NOT NULL, ";
+ sqlStatement += "[Title] TEXT NOT NULL, ";
+ sqlStatement += "[TimesPlayed] INTEGER NOT NULL, ";
+ sqlStatement += "[Rating] INTEGER NULL";
+ sqlStatement += ");";
+
+ sqlite_exec(sqlStatement);
+
+ if (finalizeConversion) {
+ // convert data from 1.01 to 1.1
+ // part #2 - accomplishment
+ LOG4CXX_INFO(log, "Outdated song database found - " <<
+ "begin conversion from V1.01 to V1.1");
+
+ // insert old values into new db-schemes (/tables)
+ sqlStatement = "INSERT INTO [";
+ sqlStatement += usdx_scores;
+ sqlStatement += "] SELECT [SongID], ";
+ sqlStatement += "[Difficulty], [Player], ";
+ sqlStatement += "[Score] FROM [us_scores_101];";
+ sqlite_exec(sqlStatement);
+
+ sqlStatement = "INSERT INTO [";
+ sqlStatement += usdx_songs;
+ sqlStatement += "] SELECT [ID], [Artist], ";
+ sqlStatement += "[Title], [TimesPlayed], NULL ";
+ sqlStatement += "FROM [us_songs_101];";
+ sqlite_exec(sqlStatement);
+
+ // now drop old tables
+ sqlite_exec("DROP TABLE us_scores_101;");
+ sqlite_exec("DROP TABLE us_songs_101;");
+ }
+
+ // add column rating to cUS_Songs
+ // just for users of nightly builds and developers!
+ if (! sqlite_table_contains_column(usdx_songs, "Rating")) {
+ LOG4CXX_INFO(log, "Outdated song database found - " <<
+ "adding column rating to '" <<
+ usdx_songs << "'");
+
+ sqlite_exec("ALTER TABLE [" + usdx_songs +
+ "] ADD COLUMN [Rating] INTEGER NULL;");
+ }
+
+ //add column date to cUS-Scores
+ if (! sqlite_table_contains_column(usdx_scores, "Date")) {
+ LOG4CXX_INFO(log, "Outdated score database found - " <<
+ "adding column date to '" <<
+ usdx_scores << "'");
+
+ sqlite_exec("ALTER TABLE [" + usdx_scores +
+ "] ADD COLUMN [Date] INTEGER NULL;");
+ }
+ }
+
+ StatDatabase::~StatDatabase(void)
+ {
+ this->Database::~Database();
+ }
+
+ char* StatDatabase::format_date(char* time,
+ size_t max,
+ time_t timestamp)
+ {
+ if (timestamp != 0) {
+ struct tm tmp;
+
+ if (localtime_r(&timestamp, &tmp)) {
+ strftime(time, max, "%d.%m.%y"
+ /* TODO: Language.Translate("STAT_FORMAT_DATE")*/,
+ &tmp);
+ return time;
+ }
+ }
+
+ if (max > 0) {
+ time[0] = '\0';
+ return time;
+ }
+
+ return NULL;
+ }
+};
diff --git a/src/base/stats_database.hpp b/src/base/stats_database.hpp
new file mode 100644
index 00000000..8ca3ff9c
--- /dev/null
+++ b/src/base/stats_database.hpp
@@ -0,0 +1,76 @@
+/*
+ * UltraStar Deluxe - Karaoke Game
+ *
+ * UltraStar Deluxe is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef STATS_DATABASE_HPP
+#define STATS_DATABASE_HPP
+
+#include <ctime>
+#include <string>
+#include <log4cxx/logger.h>
+#include "database.hpp"
+
+namespace usdx
+{
+ /**
+ * Wrapper for statistic database.
+ */
+ class StatDatabase : public Database
+ {
+ private:
+ static log4cxx::LoggerPtr log;
+
+ public:
+ StatDatabase(std::string filename);
+ ~StatDatabase(void);
+
+ /**
+ * Convert a timestamp to a data representation in a string.
+ *
+ * @param time Pointer to a char buffer that will contain the
+ * the date string.
+ * @param max Maximum bytes that could be written to the buffer.
+ * @param timestamp Timestamp to convert to the string.
+ * @return Pointer to the buffer supplied as first parameter,
+ * containing:
+ * - only a '\\0' at first position if timestamp was
+ * 0 or if max was to short to contain the date
+ * - the date string with the terminating '\\0'
+ */
+ static char* format_date(char* time, size_t max, time_t timestamp);
+
+ static const std::string usdx_scores;
+ static const std::string usdx_songs;
+ static const std::string usdx_statistics_info;
+
+
+#ifdef STAT_DATABASE_TEST
+ // for testing private members
+ friend class StatDatabaseTest;
+#endif
+ };
+};
+
+#endif