File indexing completed on 2021-12-21 12:50:21

0001 #include <stdio.h>
0002 #include <assert.h>
0003 #include <unistd.h>
0004 #include <string.h>
0005 
0006 #include <QFile>
0007 #include <KSharedConfig>
0008 #include <KConfigGroup>
0009 #include "LevelCollection.h"
0010 
0011 #include "Map.h"
0012 #if 1
0013 #define GETUID() 501
0014 #else
0015 #ifndef WIN32
0016 #define GETUID() getuid()
0017 #else
0018 #define GETUID() 1000
0019 #endif
0020 #endif
0021 
0022 
0023 static inline unsigned long
0024 forward(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
0025 {
0026   unsigned long x=(a^b)&0xfffffffful;
0027   return (((x<<c)|(x>>((32ul-c)&31ul)))*d)&0xfffffffful;
0028 }
0029 
0030 static inline unsigned long
0031 backward(unsigned long a, unsigned long b, unsigned long c, unsigned long d)
0032 {
0033   unsigned long x=(a*b)&0xfffffffful;
0034   return (((x<<c)|(x>>((32ul-c)&31ul)))^d)&0xfffffffful;
0035 }
0036 
0037 
0038 void
0039 LevelCollection::indexTextCollection() {
0040   enum states {
0041     BEFORE_NONE, BEFORE_VALID, BEFORE_INVALID,
0042     DURING_NONE, DURING_VALID, DURING_INVALID
0043   } state = BEFORE_NONE;
0044 
0045   int levelstart=0, levelend=0;
0046   for (int pos=0; pos<(data_.size()-1); pos++) {
0047     switch (state) {
0048     case BEFORE_NONE:
0049       switch (data_[pos]) {
0050       case '#': case '.': case '$': case '+': case '*': case '@':
0051     state = BEFORE_VALID;
0052     break;
0053 
0054       case ' ': case '\t': case '\r':
0055     break;
0056 
0057       case '\n':
0058     levelstart = pos + 1;
0059     break;
0060 
0061       default:
0062     state = BEFORE_INVALID;
0063     break;
0064       }
0065       break;
0066 
0067     case BEFORE_VALID:
0068       switch (data_[pos]) {
0069       case '#': case '.': case '$': case '+': case '*': case '@':
0070       case ' ': case '\t': case '\r':
0071     break;
0072 
0073       case '\n':
0074     addLevel(data_.constData() + levelstart);
0075     levelend = levelstart;
0076     state = DURING_NONE;
0077     break;
0078 
0079       default:
0080     state = BEFORE_INVALID;
0081     break;
0082       }
0083       break;
0084 
0085     case BEFORE_INVALID:
0086       switch (data_[pos]) {
0087       case '\n':
0088     levelstart = pos + 1;
0089     state = BEFORE_NONE;
0090     break;
0091       }
0092       break;
0093 
0094     case DURING_NONE:
0095       switch (data_[pos]) {
0096       case '#': case '.': case '$': case '+': case '*': case '@':
0097     state = DURING_VALID;
0098     break;
0099 
0100       case ' ': case '\t': case '\r':
0101     break;
0102 
0103       case '\n':
0104     data_[levelend] = '\0';
0105     levelstart = pos + 1;
0106     state = BEFORE_NONE;
0107     break;
0108 
0109       default:
0110     state = DURING_INVALID;
0111     break;
0112       }
0113       break;
0114 
0115     case DURING_VALID:
0116       switch (data_[pos]) {
0117       case '#': case '.': case '$': case '+': case '*': case '@':
0118       case ' ': case '\t': case '\r':
0119     break;
0120 
0121       case '\n':
0122     levelend = pos;
0123     state = DURING_NONE;
0124     break;
0125 
0126       default:
0127     state = DURING_INVALID;
0128     break;
0129       }
0130       break;
0131 
0132     case DURING_INVALID:
0133       switch (data_[pos]) {
0134       case '\n':
0135     data_[levelend] = '\0';
0136     levelstart = pos + 1;
0137     state = BEFORE_NONE;
0138     break;
0139       }
0140       break;
0141 
0142     default:
0143       assert(0);
0144     }
0145   }
0146 
0147   if (state==DURING_NONE || state==DURING_INVALID) {
0148     data_[levelend] = '\0';
0149   }
0150 }
0151 
0152 void
0153 LevelCollection::loadPrefs() {
0154   if (id_ >= 0) {
0155     KSharedConfigPtr cfg=KSharedConfig::openConfig();
0156     KConfigGroup settingsGroup(cfg, "settings");
0157 
0158     QString key = QString::asprintf("level%d", id_);
0159     level_ = settingsGroup.readEntry(key, QStringLiteral("0")).toInt();
0160 
0161     key = QString::asprintf("status%d", id_);
0162     unsigned long x = settingsGroup.readEntry(key, QStringLiteral("0")).toULong();
0163 
0164     x = backward(x, 0xc1136a15ul, 0x12ul, 0x80ff0b94ul);
0165     x = backward(x, 0xd38fd2ddul, 0x01ul, 0xd4d657b4ul);
0166     x = backward(x, 0x59004eeful, 0x1eul, 0xf6c75e2cul);
0167     x = backward(x, 0x366c3e25ul, 0x0aul, 0x61ebc208ul);
0168     x = backward(x, 0x20a784c9ul, 0x15ul, 0x207d488bul);
0169     x = backward(x, 0xc02864abul, 0x09ul, 0x709e62a3ul);
0170     x = backward(x, 0xe2a60f19ul, 0x0eul, 0x8bb02c07ul);
0171     x = backward(x, 0x3b0e11f3ul, 0x13ul, 0x608aef3ful);
0172 
0173     completedLevels_ = x>>16 & 0x3ff;
0174     if (!settingsGroup.hasKey(key)) completedLevels_ = 0;
0175     if (((x>>26) & 0x3ful) != (unsigned long) id_) completedLevels_ = 0;
0176     if ((x & 0xfffful) != (unsigned long) GETUID()) completedLevels_ = 0;
0177     if (completedLevels_ > noOfLevels_) completedLevels_ = 0;
0178 
0179     if (level_ > completedLevels_) level_ = completedLevels_;
0180     if (level_ >= noOfLevels_) level_ = noOfLevels_-1;
0181     if (level_ < 0) level_ = 0;
0182   } else {
0183     level_ = 0;
0184     completedLevels_ = noOfLevels_;
0185   }
0186 }
0187 
0188 void
0189 LevelCollection::addLevel(const char* _level) {
0190   index_.append(_level);
0191 }
0192 
0193 void
0194 LevelCollection::addData(const char* _data, unsigned _len) {
0195   unsigned pos = data_.size();
0196   data_.resize(pos + _len);
0197   memcpy(data_.data() + pos, _data, _len);
0198 }
0199 
0200 void
0201 LevelCollection::addSeparator() {
0202   unsigned pos = data_.size();
0203   data_.resize(pos + 1);
0204   data_[pos] = '\0';
0205 }
0206 
0207 LevelCollection::LevelCollection(const char *_def, int _len,
0208                  const QString &_name, int _id) :
0209   level_(0), completedLevels_(0), noOfLevels_(0),
0210   name_(_name), id_(_id) {
0211 
0212   addData(_def, _len);
0213   addSeparator();
0214 
0215   indexTextCollection();
0216 
0217   noOfLevels_ = index_.size();
0218 
0219   loadPrefs();
0220 }
0221 
0222 LevelCollection::LevelCollection(const QString &_path, const QString &_name,
0223                  int _id) :
0224   level_(0), completedLevels_(0), noOfLevels_(0),
0225   name_(_name), path_(_path), id_(_id) {
0226 
0227   char buf[1024];
0228   int len;
0229 
0230   QFile file(path_);
0231   if (file.open(QIODevice::ReadOnly)) {
0232     while ((len = file.read(buf, 1024)) > 0) {
0233       addData((const char *) buf, len);
0234     }
0235     file.close();
0236     addSeparator();
0237   }
0238 
0239   indexTextCollection();
0240 
0241   noOfLevels_ = index_.size();
0242 
0243   loadPrefs();
0244 
0245 }
0246 
0247 LevelCollection::~LevelCollection() {
0248   if (id_ >= 0) {
0249     KSharedConfigPtr cfg=KSharedConfig::openConfig();
0250     KConfigGroup settingsGroup(cfg,"settings");
0251 
0252     const QString key = QString::asprintf("level%d", id_);
0253     settingsGroup.writeEntry(key, QStringLiteral("%1").arg(level_));
0254   }
0255 }
0256 
0257 
0258 void
0259 LevelCollection::levelCompleted() {
0260   if (completedLevels_ < (level_+1)) completedLevels_ = level_+1;
0261 
0262   if (id_ >= 0) {
0263     unsigned long x=(((unsigned long) GETUID()) & 0xfffful);
0264     x |= ((unsigned long) id_)<<26;
0265     x |= ((unsigned long) completedLevels_)<<16;
0266 
0267     x = forward(x, 0x608aef3ful, 0x0dul, 0xfb00ef3bul);
0268     x = forward(x, 0x8bb02c07ul, 0x12ul, 0x2a37dd29ul);
0269     x = forward(x, 0x709e62a3ul, 0x17ul, 0x23607603ul);
0270     x = forward(x, 0x207d488bul, 0x0bul, 0xc31fd579ul);
0271     x = forward(x, 0x61ebc208ul, 0x16ul, 0xbcffadadul);
0272     x = forward(x, 0xf6c75e2cul, 0x02ul, 0xa2baa00ful);
0273     x = forward(x, 0xd4d657b4ul, 0x1ful, 0x7e129575ul);
0274     x = forward(x, 0x80ff0b94ul, 0x0eul, 0x92fc153dul);
0275 
0276     const QString key = QString::asprintf("status%d", id_);
0277 
0278     KSharedConfigPtr cfg=KSharedConfig::openConfig();
0279     KConfigGroup settingsGroup(cfg, "settings");
0280     settingsGroup.writeEntry(key, QStringLiteral("%1").arg(x));
0281     cfg->sync();
0282   }
0283 }
0284 
0285 
0286 void
0287 LevelCollection::level(int _level) {
0288   assert(_level >= 0 && _level < noOfLevels_);
0289 
0290   level_ = _level;
0291   if (level_ > completedLevels_) level_ = completedLevels_;
0292   if (level_ >= noOfLevels_) level_ = noOfLevels_ - 1;
0293   if (level_ < 0) level_ = 0;
0294 }
0295 
0296 static int
0297 minX(const char *def) {
0298   int min_x = 10000;
0299 
0300   int x=0;
0301   for (int pos=0; def[pos]; pos++) {
0302     switch(def[pos]) {
0303     case '\n':
0304       x = 0;
0305       break;
0306 
0307     case ' ':
0308       x++;
0309       break;
0310 
0311     case '\t':
0312       x = (x+8) & ~7;
0313       break;
0314 
0315     case '\r':
0316       break;
0317 
0318     default:
0319       if (x < min_x) min_x = x;
0320       break;
0321     }
0322   }
0323 
0324   return min_x == 10000 ? -1 : min_x;
0325 }
0326 
0327 
0328 bool
0329 LevelCollection::loadLevel(Map *_map) {
0330   _map->clearMap();
0331 
0332   const char *def = index_[level_];
0333   bool goodMap = true;
0334   int x=0, y=0, goalsLeft=0;
0335 
0336   int min_x = minX(def);
0337   if (min_x < 0) {
0338     min_x = 0;
0339     goodMap = false;
0340   }
0341 
0342 
0343   _map->xpos_ = -1;
0344   _map->ypos_ = -1;
0345 
0346   for (int pos=0; def[pos]; pos++) {
0347     switch(def[pos]) {
0348     case '\n':
0349       y++;
0350       x = 0;
0351       break;
0352 
0353     case ' ':
0354       x++;
0355       break;
0356 
0357     case '\t':
0358       x = (x+8) & ~7;
0359       break;
0360 
0361     case '@':
0362       if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
0363       else {
0364     _map->xpos_ = x-min_x;
0365     _map->ypos_ = y;
0366       }
0367       x++;
0368       break;
0369 
0370     case '$':
0371       if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
0372       else _map->map(x-min_x, y, OBJECT);
0373       x++;
0374       break;
0375 
0376     case '.':
0377       if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
0378       else {
0379     _map->map(x-min_x, y, GOAL);
0380     goalsLeft++;
0381       }
0382       x++;
0383       break;
0384 
0385     case '#':
0386       if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
0387       else _map->map(x-min_x, y, WALL);
0388       x++;
0389       break;
0390 
0391     case '+':
0392       if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
0393       else {
0394     _map->xpos_ = x-min_x;
0395     _map->ypos_ = y;
0396     _map->map(x-min_x, y, GOAL);
0397     goalsLeft++;
0398       }
0399       x++;
0400       break;
0401 
0402     case '*':
0403       if (x-min_x > MAX_X || y > MAX_Y) goodMap = false;
0404       else _map->map(x-min_x, y, OBJECT|GOAL);
0405       x++;
0406       break;
0407 
0408     case '\r':
0409       break;
0410 
0411     default:
0412       goodMap = false;
0413       break;
0414     }
0415   }
0416 
0417   if (_map->objectsLeft() != goalsLeft) goodMap = false;
0418   if (_map->completed()) goodMap = false;
0419 
0420   if (_map->badCoords(_map->xpos_, _map->ypos_)) goodMap = false;
0421   else {
0422     if (!_map->empty(_map->xpos_, _map->ypos_)) goodMap = false;
0423     else if (!_map->fillFloor(_map->xpos_, _map->ypos_)) goodMap = false;
0424   }
0425 
0426   return goodMap;
0427 }
0428