Commits

Anonymous committed 9fb330f

First version of KSokoban game for KDE4

Comments (0)

Files changed (112)

+------------------------------------------------------------------------
+ksokoban original game was written by:
+
+Anders Widell <awl@hem.passagen.se>
+
+Porting to KDE4/Qt4 was made by Łukasz Kalamłacki <kalamlacki@gmail.com>
+
+------------------------------------------------------------------------
+The levels were taken from:
+
+xsokoban 3.3c for X-windows
+	www:	http://xsokoban.lcs.mit.edu/xsokoban.html
+	author:	Andrew Myers <andru@lcs.mit.edu>
+
+MacSokoban 3.0.3 for Macintosh
+	www:	http://www.lysator.liu.se/~ingemar/games.html
+	author:	Ingemar Ragnemalm <ingemar@lysator.liu.se>
+
+Sokoban 2.4 for Macintosh
+	www:	http://members.aol.com/SokobanMac/
+	author:	Scott Lindhurst <scottl@alumni.Princeton.EDU>
+
+------------------------------------------------------------------------
+The levels originally come from:
+
+Original               the 50 original sokoban levels
+                       made by Thinking rabbit Inc. in Japan
+Extra                  some more levels from xsokoban
+Still more             by J. Franklin Mentzer <wryter@aol.com>
+MacTommy inventions    by a guy called Tommy in Pennsylvania
+Yoshio's autogenerated by Yoshio Murase <yoshio@asahi.email.ne.jp>
+                       see http://www.ne.jp/asahi/ai/yoshio/sokoban/main.htm
+For the kids           by Ross (W.R.) Brown <ross@bnr.ca>
+Simple Sokoban         simplified original levels
+		       by Phil Shapiro <pshapiro@his.com>
+Dimitri & Yorick       by Jacques Duthen <duthen@club-internet.fr>
+
+------------------------------------------------------------------------
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <QFile>
+#include <KApplication>
+#include <KGlobal>
+#include <KStandardDirs>
+
+#include "Bookmark.h"
+#include "History.h"
+#include "LevelMap.h"
+
+void
+Bookmark::fileName(QString &p) {
+  p = KGlobal::dirs()->saveLocation("appdata");
+
+  QString n;
+  n.setNum(number_);
+  p += "/bookmark" + n;
+}
+
+Bookmark::Bookmark(int _num) :
+  number_(_num), collection_(-1), level_(-1), moves_(0), data_("") {
+
+  QString p;
+  fileName(p);
+
+  FILE *file = fopen(p.toLatin1(), "r");
+  if (file == NULL) return;
+
+  char buf[4096];
+  buf[0] = '\0';
+  fgets (buf, 4096, file);
+  if (sscanf(buf, "%d %d %d", &collection_, &level_, &moves_) != 3) {
+    collection_ = level_ = -1;
+    data_ = "";
+    fclose(file);
+    return;
+  }
+
+  data_ = "";
+  int len;
+  while (!feof(file)) {
+    len = fread(buf, 1, 4095, file);
+    if (ferror(file)) break;
+    buf[len] = '\0';
+    data_ += buf;
+  }
+  fclose(file);        
+
+  data_ = data_.trimmed();
+}
+
+
+
+void
+Bookmark::set(int _collection, int _level, int _moves, History *_h) {
+  assert(_collection >= 0);
+  if (_collection < 0) return;
+
+  collection_ = _collection;
+  level_ = _level;
+  moves_ = _moves;
+
+  data_ = "";
+  _h->save(data_);
+  
+  QString p;
+  fileName(p);
+  FILE *file = fopen(QFile::encodeName(p), "w");
+  if (file == NULL) return;
+  fprintf(file, "%d %d %d\n", collection_, level_, moves_);
+  fprintf(file, "%s\n", data_.toLatin1().constData());
+  fclose(file);
+}
+
+bool
+Bookmark::goTo(LevelMap *_map, History *_h) {
+  return _h->load(_map, data_.toLatin1()) != 0;
+}
+
+
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <QFile>
+#include <KApplication>
+#include <KGlobal>
+#include <KStandardDirs>
+
+#include "Bookmark.h"
+#include "History.h"
+#include "LevelMap.h"
+
+void
+Bookmark::fileName(QString &p) {
+  p = KGlobal::dirs()->saveLocation("appdata");
+
+  QString n;
+  n.setNum(number_);
+  p += "/bookmark" + n;
+}
+
+Bookmark::Bookmark(int _num) :
+  number_(_num), collection_(-1), level_(-1), moves_(0), data_("") {
+
+  QString p;
+  fileName(p);
+
+  FILE *file = fopen(p.toLatin1(), "r");
+  if (file == NULL) return;
+
+  char buf[4096];
+  buf[0] = '\0';
+  fgets (buf, 4096, file);
+  if (sscanf(buf, "%d %d %d", &collection_, &level_, &moves_) != 3) {
+    collection_ = level_ = -1;
+    data_ = "";
+    fclose(file);
+    return;
+  }
+
+  data_ = "";
+  int len;
+  while (!feof(file)) {
+    len = fread(buf, 1, 4095, file);
+    if (ferror(file)) break;
+    buf[len] = '\0';
+    data_ += buf;
+  }
+  fclose(file);        
+
+  data_ = data_.trimmed();
+}
+
+
+
+void
+Bookmark::set(int _collection, int _level, int _moves, History *_h) {
+  assert(_collection >= 0);
+  if (_collection < 0) return;
+
+  collection_ = _collection;
+  level_ = _level;
+  moves_ = _moves;
+
+  data_ = "";
+  _h->save(data_);
+  
+  QString p;
+  fileName(p);
+  FILE *file = fopen(QFile::encodeName(p), "w");
+  if (file == NULL) return;
+  fprintf(file, "%d %d %d\n", collection_, level_, moves_);
+  fprintf(file, "%s\n", data_.toLatin1());
+  fclose(file);
+}
+
+bool
+Bookmark::goTo(LevelMap *_map, History *_h) {
+  return _h->load(_map, data_.toLatin1().constData()) != 0;
+}
+
+
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef BOOKMARK_H
+#define BOOKMARK_H
+
+class History;
+class LevelMap;
+
+#include <QString>
+
+class Bookmark {
+public:
+  Bookmark(int _num);
+
+  int collection() const { return collection_; }
+  int level() const { return level_; }
+  int moves() const { return moves_; }
+  //int pushes() { return pushes_; }
+
+  void set(int _collection, int _level, int _moves, History *_h);
+  bool goTo(LevelMap *_map, History *_h);
+
+private:
+  void fileName(QString &p);
+
+  int     number_;
+  int     collection_;
+  int     level_;
+  int     moves_;
+  //int     pushes_;
+  QString data_;
+};
+
+#endif  /* BOOKMARK_H */
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef BOOKMARK_H
+#define BOOKMARK_H
+
+class History;
+class LevelMap;
+
+#include <QString>
+
+class Bookmark {
+public:
+  Bookmark(int _num);
+
+  int collection() const { return collection_; }
+  int level() const { return level_; }
+  int moves() const { return moves_; }
+  //int pushes() { return pushes_; }
+
+  void set(int _collection, int _level, int _moves, History *_h);
+  bool goTo(LevelMap *_map, History *_h);
+
+private:
+  void fileName(QString &p);
+
+  int     number_;
+  int     collection_;
+  int     level_;
+  int     moves_;
+  //int     pushes_;
+  QString data_;
+};
+
+#endif  /* BOOKMARK_H */
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <QList>
+
+#include "History.h"
+#include "Move.h"
+#include "MoveSequence.h"
+#include "LevelMap.h"
+
+History::History() {
+  //past_.setAutoDelete(true);
+  //future_.setAutoDelete(true);
+}
+History::~History() {
+	for(QList<Move*>::iterator it=past_.begin(); it!=past_.end(); it++)
+		delete *it;
+	for(QList<Move*>::iterator it=future_.begin(); it!=future_.end(); it++)
+		delete *it;
+}
+
+void
+History::add(Move *_m) {
+  future_.clear();
+  past_.append(_m);
+}
+
+void
+History::clear() {
+  past_.clear();
+  future_.clear();
+}
+
+void
+History::save(QString &_str) {
+  
+
+  for(QList<Move*>::Iterator iterator = past_.begin(); iterator != past_.end(); ++iterator) {
+    (*iterator)->save(_str);
+  }
+  _str += '-';
+
+  
+  for(QList<Move*>::Iterator iterator = future_.begin(); iterator != future_.end(); ++iterator) {
+    (*iterator)->save(_str);
+  }
+}
+
+const char *
+History::load(LevelMap *map, const char *_str) {
+  Move *m;
+  int x = map->xpos();
+  int y = map->ypos();
+
+  clear();
+  while (*_str != '\0' && *_str != '-') {
+    m = new Move(x, y);
+    _str = m->load(_str);
+    if (_str == 0) return 0;
+    x = m->finalX();
+    y = m->finalY();
+    past_.append(m);
+    if (!m->redo(map)) {
+      //printf("redo failed: %s\n", _str);
+      //abort();
+      return 0;
+    }
+  }
+  if (*_str != '-') return 0;
+
+  _str++;
+  while (*_str != '\0') {
+    m = new Move(x, y);
+    _str = m->load(_str);
+    if (_str == 0) return 0;
+    x = m->finalX();
+    y = m->finalY();
+    future_.append(m);
+  }
+
+  return _str;
+}
+
+bool
+History::redo(LevelMap *map) {
+  if (future_.isEmpty()) return false;
+
+  Move *m=future_.takeAt(0);
+  past_.append(m);
+  return m->redo(map);
+}
+
+MoveSequence *
+History::deferRedo(LevelMap *map) {
+  if (future_.isEmpty()) return 0;
+
+  Move *m=future_.takeAt(0);
+  past_.append(m);
+  return new MoveSequence(m, map);
+}
+
+bool
+History::undo(LevelMap *map) {
+  if (past_.isEmpty()) return false;
+
+  Move *m = past_.takeAt(past_.count ()-1);
+  future_.insert(0, m);
+  return m->undo(map);
+}
+
+MoveSequence *
+History::deferUndo(LevelMap *map) {
+  if (past_.isEmpty()) return 0;
+
+  Move *m = past_.takeAt(past_.count()-1);
+  future_.insert(0, m);
+  return new MoveSequence(m, map, true);
+}
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <QList>
+
+#include "History.h"
+#include "Move.h"
+#include "MoveSequence.h"
+#include "LevelMap.h"
+
+History::History() {
+  //past_.setAutoDelete(true);
+  //future_.setAutoDelete(true);
+}
+
+
+void
+History::add(Move *_m) {
+  future_.clear();
+  past_.append(_m);
+}
+
+void
+History::clear() {
+  past_.clear();
+  future_.clear();
+}
+
+void
+History::save(QString &_str) {
+  QList<Move*>::Iterator past_iterator = past_.begin();
+  Move *m = (*past_iterator);
+
+  while (past_iterator != past_.end()) {
+    m->save(_str);
+    ++past_iterator;
+    m = (*past_iterator);
+  }
+  _str += '-';
+
+  QList<Move*>::Iterator future_iterator = future_.begin();  
+  m = (*future_iterator);
+  while (future_iterator != future_.end()) {
+    m->save(_str);
+    ++future_iterator;
+    m = (*future_iterator);
+  }
+}
+
+const char *
+History::load(LevelMap *map, const char *_str) {
+  Move *m;
+  int x = map->xpos();
+  int y = map->ypos();
+
+  clear();
+  while (*_str != '\0' && *_str != '-') {
+    m = new Move(x, y);
+    _str = m->load(_str);
+    if (_str == 0) return 0;
+    x = m->finalX();
+    y = m->finalY();
+    past_.append(m);
+    if (!m->redo(map)) {
+      //printf("redo failed: %s\n", _str);
+      //abort();
+      return 0;
+    }
+  }
+  if (*_str != '-') return 0;
+
+  _str++;
+  while (*_str != '\0') {
+    m = new Move(x, y);
+    _str = m->load(_str);
+    if (_str == 0) return 0;
+    x = m->finalX();
+    y = m->finalY();
+    future_.append(m);
+  }
+
+  return _str;
+}
+
+bool
+History::redo(LevelMap *map) {
+  if (future_.isEmpty()) return false;
+
+  Move *m=future_.takeAt(0);
+  past_.append(m);
+  return m->redo(map);
+}
+
+MoveSequence *
+History::deferRedo(LevelMap *map) {
+  if (future_.isEmpty()) return 0;
+
+  Move *m=future_.takeAt(0);
+  past_.append(m);
+  return new MoveSequence(m, map);
+}
+
+bool
+History::undo(LevelMap *map) {
+  if (past_.isEmpty()) return false;
+
+  Move *m = past_.takeAt(past_.count ()-1);
+  future_.insert(0, m);
+  return m->undo(map);
+}
+
+MoveSequence *
+History::deferUndo(LevelMap *map) {
+  if (past_.isEmpty()) return 0;
+
+  Move *m = past_.takeAt(past_.count()-1);
+  future_.insert(0, m);
+  return new MoveSequence(m, map, true);
+}
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef HISTORY_H
+#define HISTORY_H
+
+#include <QList>
+#include <QString>
+
+#include "Move.h"
+class MoveSequence;
+
+/**
+ * Maintains movement history
+ *
+ * @short   Maintains game movement history
+ * @author  Anders Widell <d95-awi@nada.kth.se>
+ * @version 0.1
+ * @see     PlayField
+ */
+
+class History {
+private:
+  QList<Move*> past_;
+  QList<Move*> future_;
+
+protected:
+
+public:
+  History();
+  ~History();
+  /**
+   * Add a move to the history. Deletes all currently undone moves.
+   */
+  void add(Move *_m);
+  /**
+   * Clear the history and delete all Move objects stored in it.
+   */
+  void clear();
+
+  void save(QString &_str);
+  const char *load(LevelMap *map, const char *_str);
+  bool redo(LevelMap *map);
+  MoveSequence *deferRedo(LevelMap *map);
+  bool undo(LevelMap *map);
+  MoveSequence *deferUndo(LevelMap *map);
+};
+
+#endif  /* HISTORY_H */
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef HISTORY_H
+#define HISTORY_H
+
+#include <QList>
+#include <QString>
+
+#include "Move.h"
+class MoveSequence;
+
+/**
+ * Maintains movement history
+ *
+ * @short   Maintains game movement history
+ * @author  Anders Widell <d95-awi@nada.kth.se>
+ * @version 0.1
+ * @see     PlayField
+ */
+
+class History {
+private:
+  QList<Move*> past_;
+  QList<Move*> future_;
+
+protected:
+
+public:
+  History();
+  /**
+   * Add a move to the history. Deletes all currently undone moves.
+   */
+  void add(Move *_m);
+  /**
+   * Clear the history and delete all Move objects stored in it.
+   */
+  void clear();
+
+  void save(QString &_str);
+  const char *load(LevelMap *map, const char *_str);
+  bool redo(LevelMap *map);
+  MoveSequence *deferRedo(LevelMap *map);
+  bool undo(LevelMap *map);
+  MoveSequence *deferUndo(LevelMap *map);
+};
+
+#endif  /* HISTORY_H */
+#include <stdio.h>
+#include <stdlib.h>
+#include "HtmlPrinter.h"
+
+void
+HtmlPrinter::wall (bool up, bool down, bool left, bool right)
+{
+  switch ( (up!=0)           |
+	   ((down!=0)  << 1) |
+	   ((left!=0)  << 2) |
+	   ((right!=0) << 3)) {
+  case 0:
+  case 1:
+  case 2:
+  case 3:
+    image ("vertiwall");
+    break;
+  case 4:
+  case 5:
+  case 6:
+  case 7:
+    image ("eastwall");
+    break;
+  case 8:
+  case 9:
+  case 10:
+  case 11:
+    image ("westwall");
+    break;
+  case 12:
+  case 13:
+  case 14:
+  case 15:
+    image ("horizwall");
+    break;
+
+  default:
+    abort ();
+  }
+}
+
+void
+HtmlPrinter::image (const char *name) {
+  printf ("<td><img src=%s.gif width=40 height=40><br clear=all>\n", name);
+}
+
+void
+HtmlPrinter::empty () {
+  printf ("<td>\n");
+}
+
+void
+HtmlPrinter::printSquare (LevelMap *lm, int x, int y) {
+  if (lm->xpos () == x && lm->ypos () == y) {
+    image (lm->goal (x, y) ? "saveman" : "man");
+    return;
+  }
+  if (lm->empty (x, y)) {
+    if (lm->floor (x, y)) {
+      image (lm->goal (x, y) ? "goal" : "floor");
+    } else {
+      empty ();
+    }
+    return;
+  }
+  if (lm->wall (x, y)) {
+    wall (lm->wallUp (x, y),
+	  lm->wallDown (x, y),
+	  lm->wallLeft (x, y),
+	  lm->wallRight (x, y));
+    return;
+  }
+  if (lm->object (x, y)) {
+    image (lm->goal (x, y) ? "treasure" : "object");
+    return;
+  }
+}
+
+void
+HtmlPrinter::printHtml (LevelMap *lm) {
+  printf ("\
+<html>\n\
+<head>\n\
+<title>ksokoban level</title>\n\
+</head>\n\
+<body background=background.gif>\n\
+");
+  printf ("<table border=0 cellspacing=0 cellpadding=0>\n");
+  for (int y=0; y<lm->height(); y++) {
+    printf ("<tr>\n");
+    for (int x=0; x<lm->width(); x++) {
+      printSquare (lm, x, y);
+    }
+  }
+  printf ("\
+</table>\n\
+</body>\n\
+</html>\n\
+");
+}
+#ifndef HTMLPRINTER_H
+#define HTMLPRINTER_H
+
+#include "LevelMap.h"
+
+class HtmlPrinter {
+public:
+  static void printHtml (LevelMap *lm);
+
+protected:
+  static void wall (bool up, bool down, bool left, bool right);
+  static void image (const char *name);
+  static void empty ();
+  static void printSquare (LevelMap *lm, int x, int y);
+};
+
+
+#endif  /* HTMLPRINTER_H */
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <assert.h>
+#include <QPainter>
+#include <QPixmap>
+#include <QImage>
+#include <QColor>
+
+#include "ImageData.h"
+
+ImageData::ImageData() : indexSize_(0), size_(0), halfSize_(0), floor_(10,10) {
+  random.setSeed(0);
+  QPainter paint(&floor_);
+  paint.fillRect(0,0,10,10, QColor(0x66,0x66,0x66, 255));
+}
+
+ImageData::~ImageData() {
+}
+
+void
+ImageData::expandIndex(int size) {
+  size++;
+  assert(size < 2500);
+
+  upperLargeIndex_.resize(size);
+  lowerLargeIndex_.resize(size);
+  leftSmallIndex_.resize(size);
+  rightSmallIndex_.resize(size);
+
+  for (int i=indexSize_; i<size; i++) {
+    upperLargeIndex_[i] = random.getLong(LARGE_STONES);
+    lowerLargeIndex_[i] = random.getLong(LARGE_STONES);
+    leftSmallIndex_[i] = random.getLong(SMALL_STONES);
+    rightSmallIndex_[i] = random.getLong(SMALL_STONES);
+  }
+
+  indexSize_ = size;
+}
+
+const QPixmap &
+ImageData::upperLarge(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return largeStone_xpm_[(unsigned char)upperLargeIndex_[index]];
+}
+
+const QPixmap &
+ImageData::lowerLarge(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return largeStone_xpm_[(unsigned char)lowerLargeIndex_[index]];
+}
+
+const QPixmap &
+ImageData::leftSmall(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return smallStone_xpm_[(unsigned char)leftSmallIndex_[index]];
+}
+
+const QPixmap &
+ImageData::rightSmall(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return smallStone_xpm_[(unsigned char)rightSmallIndex_[index]];
+}
+
+int
+ImageData::resize(int size) {
+  assert(size > 0);
+  size &= ~1u;
+  if (size == size_) return size;
+
+  size_ = size;
+  halfSize_ = size/2;
+
+  for (int i=0; i<SMALL_STONES; i++) {
+    image2pixmap(images_[i].scaled(halfSize_, halfSize_), smallStone_xpm_[i]);
+//     smallStone_xpm_[i].convertFromImage(images_[i].smoothScale(halfSize_, halfSize_), QPixmap::ColorOnly|QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither|QPixmap::AvoidDither);
+  }
+
+  for (int i=0; i<LARGE_STONES; i++) {
+    image2pixmap(images_[SMALL_STONES+i].scaled(size_, halfSize_), largeStone_xpm_[i]);
+//     largeStone_xpm_[i].convertFromImage(images_[SMALL_STONES+i].smoothScale(size_, halfSize_) , QPixmap::ColorOnly|QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither|QPixmap::AvoidDither);
+  }
+
+  objectImg_ = images_[SMALL_STONES+LARGE_STONES].scaled(size_, size_);
+  
+  // Use copy() because if the size is not changed, smoothScale is not
+  // really a copy
+  // Use {[Geometry] height=753 width=781} to test
+
+  if (objectImg_.width() == size_) objectImg_ = objectImg_.copy();
+
+  image2pixmap(objectImg_, otherPixmaps_[0], false);
+  brighten(objectImg_);
+  image2pixmap(objectImg_, brightObject_, false);
+
+  QImage img = images_[SMALL_STONES+LARGE_STONES+1].scaled(size_, size_);
+  if (img.width() == size_) img = img.copy();
+
+  image2pixmap(img, otherPixmaps_[1], false);
+  brighten(img);
+  image2pixmap(img, brightTreasure_, false);
+
+  for (int i=2; i<OTHER_IMAGES; i++) {
+    image2pixmap(images_[SMALL_STONES+LARGE_STONES+i].scaled(size_, size_), otherPixmaps_[i]);
+//     otherPixmaps_[i].convertFromImage(images_[SMALL_STONES+LARGE_STONES+i].smoothScale(size_, size_), QPixmap::ColorOnly|QPixmap::OrderedDither|QPixmap::OrderedAlphaDither|QPixmap::AvoidDither);
+  }
+  floor_ = floor_.scaled(size_, size_);
+  return size_;
+}
+
+// Don't use DiffuseDither for the objects on the "floor" since
+// it gives spurious dots on the floor around them
+
+void
+ImageData::image2pixmap(QImage img, QPixmap& xpm, bool diffuse) {
+  xpm.convertFromImage(img,
+		       (diffuse ?
+			(Qt::DiffuseDither|Qt::DiffuseAlphaDither) :
+			(Qt::OrderedDither|Qt::OrderedAlphaDither))|
+		       Qt::ColorOnly|Qt::AvoidDither);
+}
+
+void
+ImageData::brighten(QImage& img) {
+  assert(img.depth() == 32);
+
+  for (int y=0; y<img.height(); y++) {
+    for (int x=0; x<img.width(); x++) {
+      QRgb rgb = img.pixel(x, y);
+      int r = qRed(rgb);
+      int g = qGreen(rgb);
+      int b = qBlue(rgb);
+
+      if (r > g && r > b) {
+       // only modify redish pixels
+
+       QColor col(r, g, b);
+       QColor lcol = col.light(130);
+
+       img.setPixel(x, y, lcol.rgb());
+      }
+    }
+  }
+}
+
+void
+ImageData::wall(QPainter &p, int x, int y, int index, bool left, bool right) {
+  if (left) p.drawPixmap(x, y, upperLarge(index-1), halfSize_, 0, -1, -1);
+  else p.drawPixmap(x, y, leftSmall(index));
+
+  if (right) p.drawPixmap(x+halfSize_, y, upperLarge(index), 0, 0, halfSize_, -1);
+  else p.drawPixmap(x+halfSize_, y, rightSmall(index));
+
+  p.drawPixmap(x, y+halfSize_, lowerLarge(index));
+}
+
+void
+ImageData::floor(QPainter &p, int x, int y) {
+  //p.eraseRect(x, y, size_, size_);
+  p.drawPixmap(x, y, floor_);
+}
+
+void
+ImageData::goal(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[2]);
+}
+
+void
+ImageData::man(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[3]);
+}
+
+void
+ImageData::object(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[0]);
+}
+
+void
+ImageData::saveman(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[4]);
+}
+
+void
+ImageData::treasure(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[1]);
+}
+
+void
+ImageData::brightObject(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, brightObject_);
+}
+
+void
+ImageData::brightTreasure(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, brightTreasure_);
+}
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <assert.h>
+#include <Qpainter>
+#include <QPixmap>
+#include <QImage>
+#include <QColor>
+
+#include "ImageData.h"
+
+ImageData::ImageData() : indexSize_(0), size_(0), halfSize_(0) {
+  random.setSeed(0);
+}
+
+ImageData::~ImageData() {
+}
+
+void
+ImageData::expandIndex(int size) {
+  size++;
+  assert(size < 2500);
+
+  upperLargeIndex_.resize(size);
+  lowerLargeIndex_.resize(size);
+  leftSmallIndex_.resize(size);
+  rightSmallIndex_.resize(size);
+
+  for (int i=indexSize_; i<size; i++) {
+    upperLargeIndex_[i] = random.getLong(LARGE_STONES);
+    lowerLargeIndex_[i] = random.getLong(LARGE_STONES);
+    leftSmallIndex_[i] = random.getLong(SMALL_STONES);
+    rightSmallIndex_[i] = random.getLong(SMALL_STONES);
+  }
+
+  indexSize_ = size;
+}
+
+const QPixmap &
+ImageData::upperLarge(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return largeStone_xpm_[(unsigned char)upperLargeIndex_[index]];
+}
+
+const QPixmap &
+ImageData::lowerLarge(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return largeStone_xpm_[(unsigned char)lowerLargeIndex_[index]];
+}
+
+const QPixmap &
+ImageData::leftSmall(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return smallStone_xpm_[(unsigned char)leftSmallIndex_[index]];
+}
+
+const QPixmap &
+ImageData::rightSmall(int index) {
+  assert(index >= 0);
+  if (indexSize_ <= index) expandIndex(index);
+  return smallStone_xpm_[(unsigned char)rightSmallIndex_[index]];
+}
+
+int
+ImageData::resize(int size) {
+  assert(size > 0);
+  size &= ~1u;
+  if (size == size_) return size;
+
+  size_ = size;
+  halfSize_ = size/2;
+
+  for (int i=0; i<SMALL_STONES; i++) {
+    image2pixmap(images_[i].smoothScale(halfSize_, halfSize_), smallStone_xpm_[i]);
+//     smallStone_xpm_[i].convertFromImage(images_[i].smoothScale(halfSize_, halfSize_), QPixmap::ColorOnly|QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither|QPixmap::AvoidDither);
+  }
+
+  for (int i=0; i<LARGE_STONES; i++) {
+    image2pixmap(images_[SMALL_STONES+i].smoothScale(size_, halfSize_), largeStone_xpm_[i]);
+//     largeStone_xpm_[i].convertFromImage(images_[SMALL_STONES+i].smoothScale(size_, halfSize_) , QPixmap::ColorOnly|QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither|QPixmap::AvoidDither);
+  }
+
+  objectImg_ = images_[SMALL_STONES+LARGE_STONES].smoothScale(size_, size_);
+
+  // Use copy() because if the size is not changed, smoothScale is not
+  // really a copy
+  // Use {[Geometry] height=753 width=781} to test
+
+  if (objectImg_.width() == size_) objectImg_ = objectImg_.copy();
+
+  image2pixmap(objectImg_, otherPixmaps_[0], false);
+  brighten(objectImg_);
+  image2pixmap(objectImg_, brightObject_, false);
+
+  QImage img = images_[SMALL_STONES+LARGE_STONES+1].smoothScale(size_, size_);
+  if (img.width() == size_) img = img.copy();
+
+  image2pixmap(img, otherPixmaps_[1], false);
+  brighten(img);
+  image2pixmap(img, brightTreasure_, false);
+
+  for (int i=2; i<OTHER_IMAGES; i++) {
+    image2pixmap(images_[SMALL_STONES+LARGE_STONES+i].smoothScale(size_, size_), otherPixmaps_[i]);
+//     otherPixmaps_[i].convertFromImage(images_[SMALL_STONES+LARGE_STONES+i].smoothScale(size_, size_), QPixmap::ColorOnly|QPixmap::OrderedDither|QPixmap::OrderedAlphaDither|QPixmap::AvoidDither);
+  }
+
+  return size_;
+}
+
+// Don't use DiffuseDither for the objects on the "floor" since
+// it gives spurious dots on the floor around them
+
+void
+ImageData::image2pixmap(QImage img, QPixmap& xpm, bool diffuse) {
+  xpm.convertFromImage(img,
+		       (diffuse ?
+			(QPixmap::DiffuseDither|QPixmap::DiffuseAlphaDither) :
+			(QPixmap::OrderedDither|QPixmap::OrderedAlphaDither))|
+		       QPixmap::ColorOnly|QPixmap::AvoidDither);
+}
+
+void
+ImageData::brighten(QImage& img) {
+  assert(img.depth() == 32);
+
+  for (int y=0; y<img.height(); y++) {
+    for (int x=0; x<img.width(); x++) {
+      QRgb rgb = img.pixel(x, y);
+      int r = qRed(rgb);
+      int g = qGreen(rgb);
+      int b = qBlue(rgb);
+
+      if (r > g && r > b) {
+       // only modify redish pixels
+
+       QColor col(r, g, b);
+       QColor lcol = col.light(130);
+
+       img.setPixel(x, y, lcol.rgb());
+      }
+    }
+  }
+}
+
+void
+ImageData::wall(QPainter &p, int x, int y, int index, bool left, bool right) {
+  if (left) p.drawPixmap(x, y, upperLarge(index-1), halfSize_);
+  else p.drawPixmap(x, y, leftSmall(index));
+
+  if (right) p.drawPixmap(x+halfSize_, y, upperLarge(index), 0, 0, halfSize_);
+  else p.drawPixmap(x+halfSize_, y, rightSmall(index));
+
+  p.drawPixmap(x, y+halfSize_, lowerLarge(index));
+}
+
+void
+ImageData::floor(QPainter &p, int x, int y) {
+  p.eraseRect(x, y, size_, size_);
+}
+
+void
+ImageData::goal(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[2]);
+}
+
+void
+ImageData::man(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[3]);
+}
+
+void
+ImageData::object(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[0]);
+}
+
+void
+ImageData::saveman(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[4]);
+}
+
+void
+ImageData::treasure(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, otherPixmaps_[1]);
+}
+
+void
+ImageData::brightObject(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, brightObject_);
+}
+
+void
+ImageData::brightTreasure(QPainter &p, int x, int y) {
+  p.drawPixmap(x, y, brightTreasure_);
+}
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <d95-awi@nada.kth.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef IMAGEDATA_H
+#define IMAGEDATA_H
+
+#include <QImage>
+#include <QPixmap>
+
+#include <KRandomSequence>
+
+class QPainter;
+
+#define SMALL_STONES 4
+#define LARGE_STONES 6
+#define OTHER_IMAGES 5
+#define NO_OF_IMAGES (SMALL_STONES + LARGE_STONES + OTHER_IMAGES)
+
+class
+ImageData {
+public:
+  virtual ~ImageData();
+
+  int resize(int size);
+  int size() { return size_; }
+
+  void wall(QPainter &p, int x, int y, int index, bool left, bool right);
+  void floor(QPainter &p, int x, int y);
+  void goal(QPainter &p, int x, int y);
+  void man(QPainter &p, int x, int y);
+  void object(QPainter &p, int x, int y);
+  void saveman(QPainter &p, int x, int y);
+  void treasure(QPainter &p, int x, int y);
+  void brightObject(QPainter &p, int x, int y);
+  void brightTreasure(QPainter &p, int x, int y);
+
+  const QPixmap &background() { return background_; }
+  const QImage& objectImg() const { return objectImg_; }
+
+protected:
+  ImageData();
+
+  void expandIndex(int size);
+  void image2pixmap(QImage img, QPixmap& xpm, bool diffuse=true);
+  void brighten(QImage& img);
+
+  const QPixmap &upperLarge(int index);
+  const QPixmap &lowerLarge(int index);
+  const QPixmap &leftSmall(int index);
+  const QPixmap &rightSmall(int index);
+
+  QImage images_[NO_OF_IMAGES];
+
+  QPixmap smallStone_xpm_[SMALL_STONES];
+  QPixmap largeStone_xpm_[LARGE_STONES];
+  QPixmap otherPixmaps_[OTHER_IMAGES];
+  QPixmap background_, brightObject_, brightTreasure_;
+  QPixmap floor_;
+  QImage  objectImg_;
+  
+  int indexSize_;
+  QByteArray upperLargeIndex_;
+  QByteArray lowerLargeIndex_;
+  QByteArray leftSmallIndex_;
+  QByteArray rightSmallIndex_;
+
+  int size_, halfSize_;
+  KRandomSequence random;
+};
+
+#endif  /* IMAGEDATA_H */
+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998  Anders Widell  <d95-awi@nada.kth.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef IMAGEDATA_H
+#define IMAGEDATA_H
+
+#include <QImage>
+#include <QPixmap>
+
+#include <KRrandomSequence>
+
+class QPainter;
+
+#define SMALL_STONES 4
+#define LARGE_STONES 6
+#define OTHER_IMAGES 5
+#define NO_OF_IMAGES (SMALL_STONES + LARGE_STONES + OTHER_IMAGES)
+
+class
+ImageData {
+public:
+  virtual ~ImageData();
+
+  int resize(int size);
+  int size() { return size_; }
+
+  void wall(QPainter &p, int x, int y, int index, bool left, bool right);
+  void floor(QPainter &p, int x, int y);
+  void goal(QPainter &p, int x, int y);
+  void man(QPainter &p, int x, int y);
+  void object(QPainter &p, int x, int y);
+  void saveman(QPainter &p, int x, int y);
+  void treasure(QPainter &p, int x, int y);
+  void brightObject(QPainter &p, int x, int y);
+  void brightTreasure(QPainter &p, int x, int y);
+
+  const QPixmap &background() { return background_; }
+  const QImage& objectImg() const { return objectImg_; }
+
+protected:
+  ImageData();
+
+  void expandIndex(int size);
+  void image2pixmap(QImage img, QPixmap& xpm, bool diffuse=true);
+  void brighten(QImage& img);
+
+  const QPixmap &upperLarge(int index);
+  const QPixmap &lowerLarge(int index);
+  const QPixmap &leftSmall(int index);
+  const QPixmap &rightSmall(int index);
+
+  QImage images_[NO_OF_IMAGES];
+
+  QPixmap smallStone_xpm_[SMALL_STONES];
+  QPixmap largeStone_xpm_[LARGE_STONES];
+  QPixmap otherPixmaps_[OTHER_IMAGES];
+  QPixmap background_, brightObject_, brightTreasure_;
+  QImage  objectImg_;
+  
+  int indexSize_;
+  QByteArray upperLargeIndex_;
+  QByteArray lowerLargeIndex_;
+  QByteArray leftSmallIndex_;
+  QByteArray rightSmallIndex_;
+
+  int size_, halfSize_;
+  KRandomSequence random;
+};
+
+#endif  /* IMAGEDATA_H */

InternalCollections.cpp

+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef USE_LIBZ
+#include <zlib.h>
+#endif
+
+#include "InternalCollections.h"
+
+#ifndef LEVELS_INCLUDED
+#define LEVELS_INCLUDED 1
+#include "levels/data.c"
+#endif
+#include <KLocale>
+
+#define BUFSIZE (128*1024)
+
+// static const int collection_save_id[] = {
+//   0, 1, 3, 5, 9, 6, 7, 8, 2, 4
+// };
+
+static const int collection_save_id[] = {
+  10, 11, 12, 13, 14
+};
+
+int
+InternalCollections::configCollection2Real (int collection) {
+  for (int i=0; i < (int) (sizeof (collection_save_id) / sizeof (int)); i++) {
+    if (collection_save_id[i] == collection) return i;
+  }
+  return 0;
+}
+
+int
+InternalCollections::realCollection2Config(int collection) {
+  assert(collection < (int) (sizeof (collection_save_id) / sizeof (int)));
+  return collection_save_id[collection];
+}
+
+QString
+InternalCollections::collectionName(int _level) {
+  switch (_level) {
+  case 0:
+    return i18n("Sasquatch");
+    break;
+
+  case 1:
+    return i18n("Mas Sasquatch");
+    break;
+
+  case 2:
+    return i18n("Sasquatch III");
+    break;
+
+  case 3:
+    return i18n("Microban (easy)");
+    break;
+
+  case 4:
+    return i18n("Sasquatch IV");
+    break;
+  }
+
+  assert(false);
+  return QString();
+}
+
+
+InternalCollections::InternalCollections() {
+  int datasize, levelnum=0;
+
+#ifdef USE_LIBZ
+  data_ = (char *) malloc(BUFSIZE);
+  if (data_ == NULL) abort();
+
+  datasize = BUFSIZE;
+  uncompress ((unsigned char *) data_, (long unsigned int *) &datasize, level_data_, sizeof (level_data_));
+  data_ = (char *) realloc(data_, datasize);
+  if (data_ == NULL) abort ();
+#else
+  datasize = sizeof (level_data_);
+  data_ = (char *) malloc(datasize);
+  if (data_ == NULL) abort();
+  memcpy(data_, level_data_, datasize);
+#endif
+
+  int start=0, end=0, name=0;
+  enum {NAME, DATA} state=NAME;
+  while (end < datasize) {
+    switch (state) {
+    case NAME:
+      if (data_[end] == '\n') {
+	data_[end] = '\0';
+	state = DATA;
+      }
+      end++;
+      start = end;
+      break;
+
+    case DATA:
+      if (isalpha(data_[end])) {
+// 	collections_.add(new LevelCollection(data_+start, end-start, data_+name, collection_save_id[levelnum]));
+	add(new LevelCollection(data_+start, end-start, collectionName(levelnum), collection_save_id[levelnum]));
+	//printf("Level found: '%s'\n", data_+name);
+	levelnum++;
+	name = end;
+	state = NAME;
+      }
+      end++;
+      break;
+
+    default:
+      assert(0);
+    }
+  }
+  if (state == DATA) {
+//     collections_.add(new LevelCollection(data_+start, end-start, data_+name, collection_save_id[levelnum]));
+    add(new LevelCollection(data_+start, end-start, collectionName(levelnum), collection_save_id[levelnum]));
+    //printf("***Level found: '%s'\n", data_+name);
+  }
+  //printf("numlevels: %d/%d\n", levelnum+1, collections_.size());
+}
+
+InternalCollections::~InternalCollections() {
+  for (unsigned i=0; i<collections_.size(); i++) {
+    delete collections_[i];
+  }
+
+  free(data_);
+}
+
+int
+InternalCollections::collections() {
+  return collections_.size();
+}
+
+LevelCollection *
+InternalCollections::operator[](int n) {
+  return collections_[n];
+}
+
+void
+InternalCollections::add(LevelCollection* c) {
+   collections_.append(c);
+}

InternalCollections.h

+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998-2000  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef INTERNALCOLLECTIONS_H
+#define INTERNALCOLLECTIONS_H
+
+#include <assert.h>
+#include <QString>
+#include <QVector>
+
+#include "LevelCollection.h"
+
+class InternalCollections {
+public:
+  InternalCollections();
+  ~InternalCollections();
+
+  static int toInternalId(int _id) {
+    if (_id < 10 || _id > 14) return 1000;
+    return _id - 10;
+  }
+
+  int collections();
+  LevelCollection *operator[](int n);
+
+private:
+  void add(LevelCollection* c);
+
+  static int configCollection2Real(int collection);
+  static int realCollection2Config(int collection);
+  static QString collectionName(int _level);
+
+  QVector<LevelCollection*> collections_;
+  char *data_;
+
+};
+
+#endif  /* INTERNALCOLLECTIONS_H */

InternalCollections.h~

+/*
+ *  ksokoban - a Sokoban game for KDE
+ *  Copyright (C) 1998-2000  Anders Widell  <awl@hem.passagen.se>
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef INTERNALCOLLECTIONS_H
+#define INTERNALCOLLECTIONS_H
+
+#include <assert.h>
+#include <QString>
+#include <QVector>
+
+#include "LevelCollection.h"
+
+class InternalCollections {
+public:
+  InternalCollections();
+  ~InternalCollections();
+
+  static int toInternalId(int _id) {
+    if (_id < 10 || _id > 14) return 1000;
+    return _id - 10;
+  }
+
+  int collections();
+  LevelCollection *operator[](int n);
+
+private:
+  void add(LevelCollection* c);
+
+  static int configCollection2Real(int collection);
+  static int realCollection2Config(int collection);
+  static QString collectionName(int _level);
+
+  QValueVector<LevelCollection> collections_;
+  char *data_;
+
+};
+
+#endif  /* INTERNALCOLLECTIONS_H */

LevelCollection.cpp

+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <QFile>
+#include <KGlobal>
+#include <KSharedConfig>
+#include <KConfigGroup>
+#include "LevelCollection.h"
+
+#include "Map.h"
+
+
+static inline unsigned long
+forward(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+  unsigned long x=(a^b)&0xfffffffful;
+  return (((x<<c)|(x>>((32ul-c)&31ul)))*d)&0xfffffffful;
+}
+
+static inline unsigned long
+backward(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
+{
+  unsigned long x=(a*b)&0xfffffffful;
+  return (((x<<c)|(x>>((32ul-c)&31ul)))^d)&0xfffffffful;
+}
+
+
+void
+LevelCollection::indexTextCollection() {
+  enum states {
+    BEFORE_NONE, BEFORE_VALID, BEFORE_INVALID,
+    DURING_NONE, DURING_VALID, DURING_INVALID
+  } state = BEFORE_NONE;
+
+  int levelstart=0, levelend=0;
+  for (unsigned pos=0; pos<(data_.size()-1); pos++) {
+    switch (state) {
+    case BEFORE_NONE:
+      switch (data_[pos]) {
+      case '#': case '.': case '$': case '+': case '*': case '@':
+	state = BEFORE_VALID;
+	break;
+
+      case ' ': case '\t': case '\r':
+	break;
+
+      case '\n':
+	levelstart = pos + 1;
+	break;
+
+      default:
+	state = BEFORE_INVALID;
+	break;
+      }
+      break;
+
+    case BEFORE_VALID:
+      switch (data_[pos]) {
+      case '#': case '.': case '$': case '+': case '*': case '@':
+      case ' ': case '\t': case '\r':
+	break;
+
+      case '\n':
+	addLevel(data_.constData() + levelstart);
+	levelend = levelstart;
+	state = DURING_NONE;
+	break;
+
+      default:
+	state = BEFORE_INVALID;
+	break;
+      }
+      break;
+
+    case BEFORE_INVALID:
+      switch (data_[pos]) {
+      case '\n':
+	levelstart = pos + 1;
+	state = BEFORE_NONE;
+	break;
+      }
+      break;
+
+    case DURING_NONE:
+      switch (data_[pos]) {
+      case '#': case '.': case '$': case '+': case '*': case '@':
+	state = DURING_VALID;
+	break;
+
+      case ' ': case '\t': case '\r':
+	break;
+
+      case '\n':
+	data_[levelend] = '\0';
+	levelstart = pos + 1;
+	state = BEFORE_NONE;
+	break;
+
+      default:
+	state = DURING_INVALID;
+	break;
+      }
+      break;
+
+    case DURING_VALID:
+      switch (data_[pos]) {
+      case '#': case '.': case '$': case '+': case '*': case '@':
+      case ' ': case '\t': case '\r':
+	break;
+
+      case '\n':
+	levelend = pos;
+	state = DURING_NONE;
+	break;
+
+      default:
+	state = DURING_INVALID;
+	break;
+      }
+      break;
+
+    case DURING_INVALID:
+      switch (data_[pos]) {
+      case '\n':
+	data_[levelend] = '\0';
+	levelstart = pos + 1;
+	state = BEFORE_NONE;
+	break;
+      }
+      break;
+
+    default:
+      assert(0);
+    }
+  }
+
+  if (state==DURING_NONE || state==DURING_INVALID) {
+    data_[levelend] = '\0';
+  }
+}
+
+void
+LevelCollection::loadPrefs() {
+  if (id_ >= 0) {
+    KSharedConfigPtr cfg=KGlobal::config();
+	KConfigGroup settingsGroup(cfg, "settings");
+
+    QString key;
+    key.sprintf("level%d", id_);
+    level_ = settingsGroup.readEntry(key, QString("0")).toInt();
+
+    key.sprintf("status%d", id_);
+    unsigned long x = settingsGroup.readEntry(key, QString("0")).toULong();
+
+    x = backward(x, 0xc1136a15ul, 0x12ul, 0x80ff0b94ul);
+    x = backward(x, 0xd38fd2ddul, 0x01ul, 0xd4d657b4ul);
+    x = backward(x, 0x59004eeful, 0x1eul, 0xf6c75e2cul);
+    x = backward(x, 0x366c3e25ul, 0x0aul, 0x61ebc208ul);
+    x = backward(x, 0x20a784c9ul, 0x15ul, 0x207d488bul);
+    x = backward(x, 0xc02864abul, 0x09ul, 0x709e62a3ul);
+    x = backward(x, 0xe2a60f19ul, 0x0eul, 0x8bb02c07ul);
+    x = backward(x, 0x3b0e11f3ul, 0x13ul, 0x608aef3ful);
+
+    completedLevels_ = x>>16 & 0x3ff;
+    if (!settingsGroup.hasKey(key)) completedLevels_ = 0;
+    if (((x>>26) & 0x3ful) != (unsigned long) id_) completedLevels_ = 0;
+    if ((x & 0xfffful) != (unsigned long) getuid()) completedLevels_ = 0;
+    if (completedLevels_ > noOfLevels_) completedLevels_ = 0;
+
+    if (level_ > completedLevels_) level_ = completedLevels_;
+    if (level_ >= noOfLevels_) level_ = noOfLevels_-1;
+    if (level_ < 0) level_ = 0;
+  } else {
+    level_ = 0;
+    completedLevels_ = noOfLevels_;
+  }
+}
+
+void
+LevelCollection::addLevel(const char* _level) {
+  index_.append(_level);
+}
+
+void
+LevelCollection::addData(const char* _data, unsigned _len) {
+  unsigned pos = data_.size();
+  data_.resize(pos + _len);
+  memcpy(data_.data() + pos, _data, _len);
+}
+
+void
+LevelCollection::addSeparator() {
+  unsigned pos = data_.size();
+  data_.resize(pos + 1);
+  data_[pos] = '\0';
+}
+
+LevelCollection::LevelCollection(const char *_def, int _len,
+				 const QString &_name, int _id) :
+  level_(0), completedLevels_(0), noOfLevels_(0),
+  name_(_name), id_(_id) {
+
+  addData(_def, _len);
+  addSeparator();
+
+  indexTextCollection();
+
+  noOfLevels_ = index_.size();
+
+  loadPrefs();
+}
+
+LevelCollection::LevelCollection(const QString &_path, const QString &_name,
+				 int _id) :
+  level_(0), completedLevels_(0), noOfLevels_(0),
+  name_(_name), path_(_path), id_(_id) {
+
+  char buf[1024];
+  int len;
+
+  QFile file(path_);
+  if (file.open(QIODevice::ReadOnly)) {
+    while ((len = file.read(buf, 1024)) > 0) {
+      addData((const char *) buf, len);
+    }
+    file.close();
+    addSeparator();
+  }
+
+  indexTextCollection();
+
+  noOfLevels_ = index_.size();
+
+  loadPrefs();
+
+}
+
+LevelCollection::~LevelCollection() {
+  if (id_ >= 0) {
+    KSharedConfigPtr cfg=KGlobal::config();
+    KConfigGroup settingsGroup(cfg,"settings");
+
+    QString key;
+    key.sprintf("level%d", id_);
+    settingsGroup.writeEntry(key, QString("%1").arg(level_));
+  }
+}
+
+
+void
+LevelCollection::levelCompleted() {
+  if (completedLevels_ < (level_+1)) completedLevels_ = level_+1;
+
+  if (id_ >= 0) {
+    unsigned long x=(((unsigned long) getuid()) & 0xfffful);
+    x |= ((unsigned long) id_)<<26;
+    x |= ((unsigned long) completedLevels_)<<16;
+
+    x = forward(x, 0x608aef3ful, 0x0dul, 0xfb00ef3bul);
+    x = forward(x, 0x8bb02c07ul, 0x12ul, 0x2a37dd29ul);
+    x = forward(x, 0x709e62a3ul, 0x17ul, 0x23607603ul);
+    x = forward(x, 0x207d488bul, 0x0bul, 0xc31fd579ul);
+    x = forward(x, 0x61ebc208ul, 0x16ul, 0xbcffadadul);
+    x = forward(x, 0xf6c75e2cul, 0x02ul, 0xa2baa00ful);
+    x = forward(x, 0xd4d657b4ul, 0x1ful, 0x7e129575ul);
+    x = forward(x, 0x80ff0b94ul, 0x0eul, 0x92fc153dul);
+
+    QString key;
+    key.sprintf("status%d", id_);
+
+    KSharedConfigPtr cfg=KGlobal::config();
+    KConfigGroup settingsGroup(cfg, "settings");
+    settingsGroup.writeEntry(key, QString("%1").arg(x));
+    cfg->sync();
+  }
+}
+
+
+void
+LevelCollection::level(int _level) {
+  assert(_level >= 0 && _level < noOfLevels_);
+
+  level_ = _level;
+  if (level_ > completedLevels_) level_ = completedLevels_;
+  if (level_ >= noOfLevels_) level_ = noOfLevels_ - 1;
+  if (level_ < 0) level_ = 0;
+}
+
+static int
+minX(const char *def) {
+  int min_x = 10000;
+
+  int x=0;
+  for (int pos=0; def[pos]; pos++) {
+    switch(def[pos]) {
+    case '\n':
+      x = 0;
+      break;
+
+    case ' ':
+      x++;
+      break;
+
+    case '\t':
+      x = (x+8) & ~7;
+      break;
+
+    case '\r':
+      break;
+
+    default:
+      if (x < min_x) min_x = x;
+      break;
+    }
+  }
+
+  return min_x == 10000 ? -1 : min_x;
+}
+
+
+bool
+LevelCollection::loadLevel(Map *_map) {
+  _map->clearMap();
+
+  const char *def = index_[level_];
+  bool goodMap = true;
+  int x=0, y=0, goalsLeft=0;
+
+  int min_x = minX(def);
+  if (min_x < 0) {
+    min_x = 0;
+    goodMap = false;
+  }
+
+
+  _map->xpos_ = -1;
+  _map->ypos_ = -1;
+
+  for (int pos=0; def[pos]; pos++) {
+    switch(def[pos]) {
+    case '\n':
+      y++;
+      x = 0;
+      break;
+
+    case ' ':
+      x++;
+      break;
+
+    case '\t':
+      x = (x+8) & ~7;
+      break;
+
+    case '@':
+      if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+      else {
+	_map->xpos_ = x-min_x;
+	_map->ypos_ = y;
+      }
+      x++;
+      break;
+
+    case '$':
+      if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
+      else _map->map(x-min_x, y, OBJECT);
+      x++;