Initial release

This commit is contained in:
Gergely Polonkai 2013-09-21 23:05:25 +02:00
commit a9f55d3076
73 changed files with 12581 additions and 0 deletions

68
Makefile Normal file
View File

@ -0,0 +1,68 @@
########################################
#
# build parameters
#
########################################
# installation prefix
PREFIX=/usr/local
########################################
#
# do not modify rest of this file
#
########################################
OPTIMIZE=#-O6 -march=pentium4 -mfpmath=sse -fomit-frame-pointer -funroll-loops
PROFILER=#-pg
DEBUG=#-ggdb
CXXFLAGS=-pipe -Wall $(OPTIMIZE) $(DEBUG) `sdl-config --cflags` -DPREFIX=L\"$(PREFIX)\" $(PROFILER)
LNFLAGS=-pipe -lSDL_ttf -lfreetype `sdl-config --libs` -lz -lSDL_mixer $(PROFILER)
INSTALL=install
TARGET=einstein
SOURCES=puzgen.cpp main.cpp screen.cpp resources.cpp utils.cpp game.cpp \
widgets.cpp iconset.cpp puzzle.cpp rules.cpp \
verthins.cpp random.cpp horhints.cpp menu.cpp font.cpp \
conf.cpp storage.cpp tablestorage.cpp regstorage.cpp \
topscores.cpp opensave.cpp descr.cpp options.cpp messages.cpp \
formatter.cpp buffer.cpp unicode.cpp convert.cpp table.cpp \
i18n.cpp lexal.cpp streams.cpp tokenizer.cpp sound.cpp
OBJECTS=puzgen.o main.o screen.o resources.o utils.o game.o \
widgets.o iconset.o puzzle.o rules.o verthints.o random.o \
horhints.o menu.o font.o conf.o storage.o options.o \
tablestorage.o regstorage.o topscores.o opensave.o descr.o \
messages.o formatter.o buffer.o unicode.o convert.o table.o \
i18n.o lexal.o streams.o tokenizer.o sound.o
HEADERS=screen.h main.h exceptions.h resources.h utils.h \
widgets.h iconset.h puzzle.h verthints.h random.h horhints.h \
font.h conf.h storage.h tablestorage.h regstorage.h \
topscores.h opensave.h game.h descr.h options.h messages.h \
foramtter.h buffer.h visitor.h unicode.h convert.h table.h \
i18n.h lexal.h streams.h tokenizer.h sound.h
.cpp.o:
$(CXX) $(CXXFLAGS) -c $<
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CXX) $(LNFLAGS) $(OBJECTS) -o $(TARGET)
clean:
rm -f $(OBJECTS) core* *core $(TARGET) *~
depend:
@makedepend $(SOURCES) 2> /dev/null
run: $(TARGET)
./$(TARGET)
install: $(TARGET)
$(INSTALL) -s -D $(TARGET) $(PREFIX)/bin/$(TARGET)
$(INSTALL) -D einstein.res $(PREFIX)/share/einstein/res/einstein.res
# DO NOT DELETE THIS LINE -- make depend depends on it.

63
Makefile.macosx Normal file
View File

@ -0,0 +1,63 @@
########################################
#
# build parameters
#
########################################
# installation prefix
PREFIX=/usr/local
########################################
#
# do not modify rest of this file
#
########################################
OPTIMIZE=#-O6 -march=pentium4 -mfpmath=sse -fomit-frame-pointer -funroll-loops
PROFILER=#-pg
DEBUG=-ggdb
CXXFLAGS=-pipe -Wall $(OPTIMIZE) $(DEBUG) -I/Library/Frameworks/SDL.framework/Headers/ -I/Library/Frameworks/SDL_ttf.framework/Headers/ -I/Library/Frameworks/SDL_mixer.framework/Headers/ -DPREFIX=L\"$(PREFIX)\" $(PROFILER)
LNFLAGS=-pipe -framework Cocoa -framework SDL_ttf -framework SDL -framework SDL_mixer -lSDLmain -lz $(PROFILER)
TARGET=einstein
SOURCES=puzgen.cpp main.cpp screen.cpp resources.cpp utils.cpp game.cpp \
widgets.cpp iconset.cpp puzzle.cpp rules.cpp \
verthins.cpp random.cpp horhints.cpp menu.cpp font.cpp \
conf.cpp storage.cpp tablestorage.cpp regstorage.cpp \
topscores.cpp opensave.cpp descr.cpp options.cpp messages.cpp \
formatter.cpp buffer.cpp unicode.cpp convert.cpp table.cpp \
i18n.cpp lexal.cpp streams.cpp tokenizer.cpp sound.cpp
OBJECTS=puzgen.o main.o screen.o resources.o utils.o game.o \
widgets.o iconset.o puzzle.o rules.o verthints.o random.o \
horhints.o menu.o font.o conf.o storage.o options.o \
tablestorage.o regstorage.o topscores.o opensave.o descr.o \
messages.o formatter.o buffer.o unicode.o convert.o table.o \
i18n.o lexal.o streams.o tokenizer.o sound.o
HEADERS=screen.h main.h exceptions.h resources.h utils.h \
widgets.h iconset.h puzzle.h verthints.h random.h horhints.h \
font.h conf.h storage.h tablestorage.h regstorage.h \
topscores.h opensave.h game.h descr.h options.h messages.h \
foramtter.h buffer.h visitor.h unicode.h convert.h table.h \
i18n.h lexal.h streams.h tokenizer.h sound.h
.cpp.o:
$(CXX) $(CXXFLAGS) -c $<
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CXX) $(LNFLAGS) $(OBJECTS) -o $(TARGET)
clean:
rm -f $(OBJECTS) core* *core $(TARGET) *~
depend:
@makedepend $(SOURCES) 2> /dev/null
run: $(TARGET)
./$(TARGET)
# DO NOT DELETE

49
Makefile.win Normal file
View File

@ -0,0 +1,49 @@
OPTIMIZE=-O3 #-march=pentium4 -mfpmath=sse -fomit-frame-pointer -funroll-loops
DEBUG=#-ggdb
CXXFLAGS=-Wall $(OPTIMIZE) $(DEBUG) -Ic:/mingw/include/sdl -mwindows
LNFLAGS=-lmingw32 -lSDLmain -mwindows
LIBS=-lmingw32 -lSDLmain -lSDL_ttf -lSDL -lfreetype -lz -lSDL_mixer
TARGET=einstein
SOURCES=puzgen.cpp main.cpp screen.cpp resources.cpp utils.cpp game.cpp \
widgets.cpp iconset.cpp puzzle.cpp rules.cpp \
verthins.cpp random.cpp horhints.cpp menu.cpp font.cpp \
conf.cpp storage.cpp tablestorage.cpp regstorage.cpp \
topscores.cpp opensave.cpp descr.cpp options.cpp messages.cpp \
formatter.cpp buffer.cpp unicode.cpp convert.cpp table.cpp \
i18n.cpp lexal.cpp streams.cpp tokenizer.cpp sound.cpp
OBJECTS=puzgen.o main.o screen.o resources.o utils.o game.o \
widgets.o iconset.o puzzle.o rules.o verthints.o random.o \
horhints.o menu.o font.o conf.o storage.o options.o \
tablestorage.o regstorage.o topscores.o opensave.o descr.o \
messages.o formatter.o buffer.o unicode.o convert.o table.o \
i18n.o lexal.o streams.o tokenizer.o sound.o
HEADERS=screen.h main.h exceptions.h resources.h utils.h \
widgets.h iconset.h puzzle.h verthints.h random.h horhints.h \
font.h conf.h storage.h tablestorage.h regstorage.h \
topscores.h opensave.h game.h descr.h options.h messages.h \
foramtter.h buffer.h visitor.h unicode.h convert.h table.h \
i18n.h lexal.h streams.h tokenizer.h sound.h
.cpp.o:
$(CXX) $(CXXFLAGS) -c $<
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CXX) $(LNFLAGS) $(OBJECTS) $(LIBS) -o $(TARGET)
clean:
del $(TARGET).exe
del *.o
depend:
@makedepend $(SOURCES) 2> /dev/null
run: $(TARGET)
./$(TARGET)
# DO NOT DELETE

3
README Normal file
View File

@ -0,0 +1,3 @@
To build and install einstein edit Makefile and run `make install` as root.

104
buffer.cpp Normal file
View File

@ -0,0 +1,104 @@
#include "buffer.h"
#include <string.h>
#include "exceptions.h"
#include "unicode.h"
Buffer::Buffer(int sz, int alloc)
{
allocated = alloc;
size = sz;
if (size > allocated)
allocated = size;
if (allocated < 1024)
allocated = 1024;
data = malloc(allocated);
if (! data)
throw Exception(L"Error allocating memory for Buffer");
}
Buffer::~Buffer()
{
free(data);
}
void Buffer::setSize(size_t sz)
{
if (sz > allocated) {
int newAl = allocated + sz + 1024;
void *d = realloc(data, newAl);
if (! d)
throw Exception(L"Error expanding buffer memory");
data = d;
allocated = newAl;
}
size = sz;
}
size_t Buffer::getSize()
{
return size;
}
size_t Buffer::getAllocated()
{
return allocated;
}
void* Buffer::getData()
{
return data;
}
void Buffer::gotoPos(int offset)
{
currentPos = offset;
}
size_t Buffer::putData(const unsigned char *d, size_t length)
{
if (size < currentPos + length)
setSize(currentPos + length);
memcpy((unsigned char*)data + currentPos, d, length);
currentPos += length;
return length;
}
size_t Buffer::putInteger(int v)
{
unsigned char b[4];
int i, ib;
for (i = 0; i < 4; i++) {
ib = v & 0xFF;
v = v >> 8;
b[i] = ib;
}
return putData(b, 4);
}
size_t Buffer::putUtf8(const std::wstring &string)
{
std::string s(toUtf8(string));
putInteger(s.length());
putData((const unsigned char*)s.c_str(), s.length());
return 4 + s.length();
}
size_t Buffer::putByte(unsigned char value)
{
if (size < (size_t)currentPos + 1)
setSize(currentPos + 1);
((unsigned char*)data)[currentPos] = value;
currentPos++;
return 1;
}

79
buffer.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef __BUFFER_H__
#define __BUFFER_H__
#include <stdlib.h>
#include <string>
/// Dynamic growing buffer
class Buffer
{
private:
size_t size;
size_t allocated;
void *data;
int currentPos;
public:
/// Create buffer
/// \param size initial size of buffer
/// \param allocated bytes to allocate
Buffer(int size=0, int allocated=1024);
~Buffer();
public:
/// Set buffer size, expands memory if needed
/// \param size new size
void setSize(size_t size);
/// Get current size of buffer
size_t getSize();
/// Get actual bytes used by buffer
size_t getAllocated();
/// Get pointer to data
void* getData();
public:
/// Move pointer to specified position.
/// \param offset offset from buffer start
void gotoPos(int offset);
/// Add data to buffer and advance current position by data length.
/// Grow buffer if needed.
/// \param data pointer to data
/// \param length data size.
size_t putData(const unsigned char *data, size_t length);
/// Add data to buffer and advance current position by data length.
/// Grow buffer if needed.
/// \param data pointer to data
/// \param length data size.
size_t putData(const char *data, size_t length) {
return putData((const unsigned char*)data, length);
};
/// Add integer to buffer and advance current position by 4.
/// Grow buffer if needed.
/// \param value value to add.
size_t putInteger(int value);
/// Add string to buffer encoded in UTF-8 and advance current
/// position by string length. String stored prefixed by
/// string length.
/// Grow buffer if needed.
/// \param string value to add.
size_t putUtf8(const std::wstring &string);
/// Add byte to buffer and advance current position by 1.
/// Grow buffer if needed.
/// \param value value to add.
size_t putByte(unsigned char value);
};
#endif

1449
conf.cpp Normal file

File diff suppressed because it is too large Load Diff

107
conf.h Normal file
View File

@ -0,0 +1,107 @@
/*
* Configuration file parser
* Copyright (c) 2002 Alexander Babichev
*/
#ifndef __CONF_H__
#define __CONF_H__
struct _STable;
typedef struct _STable* HTable;
struct _STableIterator;
typedef struct _STableIterator* HTableIterator;
#define TYPE_INT 1
#define TYPE_STRING 2
#define TYPE_FLOAT 3
#define TYPE_TABLE 4
/* create empty table */
HTable table_create();
/* read table from file */
HTable table_read(const char *filename);
/* free table */
void table_free(HTable table);
/* get table iterator positioned before table start */
HTableIterator table_get_iter(HTable table);
/* free table iterator */
void table_free_iter(HTableIterator iterator);
/* move iterator. return 1 on success */
int table_iter_next(HTableIterator iterator);
/* get name of table field */
char* table_iter_get_name(HTableIterator iterator);
/* get type of table field */
int table_iter_get_type(HTableIterator iterator);
/* get current table field as newly allocated string */
char* table_iter_get_str(HTableIterator iterator, int *err);
/* get current table field as static string valid till next iterator operation */
char* table_iter_get_strs(HTableIterator iterator, int *err);
/* get current table field as int */
int table_iter_get_int(HTableIterator iterator, int *err);
/* get current table field as double */
double table_iter_get_double(HTableIterator iterator, int *err);
/* get current table field as table */
HTable table_iter_get_table(HTableIterator iterator, int *err);
/* return 0 if field not exists in table */
int table_is_field_exists(HTable table, char *name);
/* get type of table field or -1 if field not exists */
int table_get_field_type(HTable table, char *field);
/* get table field as newly allocated string */
char* table_get_str(HTable table, char *field, char *dflt, int *err);
/* get table field as static string */
char* table_get_strs(HTable table, char *field, char *dflt, int *err);
/* get table field as int */
int table_get_int(HTable table, char *field, int dflt, int *err);
/* get table field as double */
double table_get_double(HTable table, char *field, double dflt, int *err);
/* get table field as table */
HTable table_get_table(HTable table, char *field, HTable dflt, int *err);
/* print table to new allocated string */
char* table_to_str(HTable table, int print_braces, int butify, int spaces);
/* set table field as string */
int table_set_str(HTable table, const char *field, const char *val);
/* set table field as int */
int table_set_int(HTable table, const char *field, int val);
/* set table field as double */
int table_set_double(HTable table, const char *field, double val);
/* set table field as table */
int table_set_table(HTable table, const char *field, HTable val);
/* append string field to table */
int table_append_str(HTable table, const char *val);
/* append int field to table */
int table_append_int(HTable table, int val);
/* append double field to table */
int table_append_double(HTable table, double val);
/* append tablee field to table */
int table_append_table(HTable table, HTable val);
#endif

69
convert.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "convert.h"
std::wstring toLowerCase(const std::wstring &s)
{
std::wstring res;
int len = s.length();
for (int i = 0; i < len; i++)
res += (wchar_t)towlower(s[i]);
return res;
}
std::wstring toUpperCase(const std::wstring &s)
{
std::wstring res;
int len = s.length();
for (int i = 0; i < len; i++)
res += (wchar_t)towupper(s[i]);
return res;
}
std::wstring numToStr(int num)
{
wchar_t buf[30];
#ifdef WIN32
swprintf(buf, L"%i", num);
#else
swprintf(buf, 29, L"%i", num);
#endif
buf[29] = 0;
return std::wstring(buf);
}
std::wstring numToStr(unsigned int num)
{
wchar_t buf[30];
#ifdef WIN32
swprintf(buf, L"%u", num);
#else
swprintf(buf, 29, L"%i", num);
#endif
buf[29] = 0;
return std::wstring(buf);
}
int strToInt(const std::wstring &str)
{
int n;
wchar_t *endptr;
n = wcstol(str.c_str(), &endptr, 10);
if ((! str.c_str()[0]) || (endptr[0]))
throw Exception(L"Invalid integer '" + str + L"'");
return n;
}
double strToDouble(const std::wstring &str)
{
double n;
wchar_t *endptr;
n = wcstod(str.c_str(), &endptr);
if ((! str.c_str()[0]) || (endptr[0]))
throw Exception(L"Invalid double '" + str + L"'");
return n;
}

53
convert.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef __CONVERT_H__
#define __CONVERT_H__
#include <iostream>
#include <sstream>
#include <string>
#include "exceptions.h"
#include "unicode.h"
/// Convert value to string
/// \param x value
template <typename T>
inline std::wstring toString(const T &x)
{
#ifndef WIN32
std::wostringstream o;
if (! (o << x))
throw Exception(L"Can't convert " + fromMbcs(typeid(x).name())
+ L" to string");
return o.str();
#else // Mingw doesn't support std::wostringstream yet :-(
std::ostringstream o;
if (! (o << x))
throw Exception(L"Can't convert " + fromMbcs(typeid(x).name())
+ L" to string");
return fromMbcs(o.str());
#endif
}
/// Convert string to lower case.
std::wstring toLowerCase(const std::wstring &s);
/// Convert string to upper case
std::wstring toUpperCase(const std::wstring &s);
/// Convert integer to string.
std::wstring numToStr(int num);
/// Convert unsigned integer to string.
std::wstring numToStr(unsigned int num);
/// Convert string to integer
int strToInt(const std::wstring &str);
/// Conver string to double
double strToDouble(const std::wstring &str);
#endif

367
descr.cpp Normal file
View File

@ -0,0 +1,367 @@
#include "descr.h"
#include <string>
#include <list>
#include <vector>
#include <map>
#include "widgets.h"
#include "unicode.h"
#include "messages.h"
#include "convert.h"
#include "utils.h"
#include "tokenizer.h"
#define WIDTH 600
#define HEIGHT 500
#define CLIENT_WIDTH 570
#define CLIENT_HEIGHT 390
#define START_X 115
#define START_Y 100
class TextPage
{
private:
std::vector<Widget*> widgets;
public:
TextPage() { };
~TextPage();
public:
Widget* getWidget(int no) { return widgets[no]; };
int getWidgetsCount() const { return widgets.size(); };
void add(Widget *widget) { widgets.push_back(widget); };
bool isEmpty() const { return ! widgets.size(); };
};
class TextParser
{
private:
Tokenizer tokenizer;
std::vector<TextPage*> pages;
Font &font;
int spaceWidth;
int charHeight;
std::map<std::wstring, SDL_Surface*> images;
int offsetX;
int offsetY;
int pageWidth;
int pageHeight;
public:
TextParser(const std::wstring &text, Font &font,
int x, int y, int width, int height);
~TextParser();
public:
TextPage* getPage(unsigned int no);
private:
void addLine(TextPage *page, std::wstring &line, int &curPosY,
int &lineWidth);
void parseNextPage();
bool isImage(const std::wstring &name);
std::wstring keywordToImage(const std::wstring &name);
SDL_Surface* getImage(const std::wstring &name);
};
class CursorCommand;
// Otobrazhaet pravila igry na ekran
class Description
{
private:
typedef std::list<Widget *> WidgetsList;
WidgetsList widgets;
CursorCommand *prevCmd; // Sobytie na nazhatie knopki <PREV>
CursorCommand *nextCmd; // Sobytie na nazhatie knopki <PREV>
Area area; // Mesto gde risovat'
//std::vector<RulesPage *> pages; // Spisok stranits teksta
unsigned int currentPage; // Tekuschaja stranitsa dlja prosmotra
Font *titleFont; // Shrift dlja risovanija zagolovka okna
Font *buttonFont; // Shrift dlja risovanija knopok v okne
Font *textFont; // Shrift dlja risovanija teksta
unsigned int textHeight; // Vysota stroki teksta
TextParser *text;
public:
Description(Area *parentArea);
~Description();
public:
void run();
void updateInfo(); // Vyvodit informatsiju na stranitsu
TextPage *getPage(unsigned int no) { return text->getPage(no); };
private:
void printPage(); // Vyvodit tekuschuju stranitsu pravil
void deleteWidgets(); // Udaljaet neispol'zuemye metki i kartinki iz oblasti vyvoda informatsii (Area)
};
//Nuzhen pri nazhatii na knopku <NEXT> ili <PREV> v dialoge spiska pravil
class CursorCommand: public Command
{
private:
int step; // Cherez skol'ko stranits listat'
Description &description; // Ukazatel' na ob"ekt Description
unsigned int *value; // Ukazatel' na tekuschij nomer stranitsy
public:
CursorCommand(int step, Description &d, unsigned int *v);
virtual ~CursorCommand() { };
public:
virtual void doAction(); // Obrabatyvaet sobytija
};
void showDescription(Area *parentArea)
{
Description d(parentArea);
d.run();
}
Description::Description(Area *parentArea)
{
currentPage = 0;
//area.add(parentArea, false);
titleFont = new Font(L"nova.ttf", 26);
buttonFont = new Font(L"laudcn2.ttf", 14);
textFont = new Font(L"laudcn2.ttf", 16);
textHeight = (int)(textFont->getHeight(L"A") * 1.0);
text = new TextParser(msg(L"rulesText"), *textFont, START_X, START_Y,
CLIENT_WIDTH, CLIENT_HEIGHT);
prevCmd = new CursorCommand(-1, *this, &currentPage);
nextCmd = new CursorCommand(1, *this, &currentPage);
}
Description::~Description()
{
deleteWidgets();
delete text;
delete titleFont;
delete buttonFont;
delete textFont;
}
void Description::deleteWidgets()
{
for (WidgetsList::iterator i = widgets.begin();
i != widgets.end(); i++)
area.remove(*i);
widgets.clear();
}
void Description::updateInfo()
{
deleteWidgets();
printPage();
area.draw();
}
void Description::run()
{
area.add(new Window(100, 50, WIDTH, HEIGHT, L"blue.bmp"));
area.add(new Label(titleFont, 250, 60, 300, 40, Label::ALIGN_CENTER, Label::ALIGN_MIDDLE, 255, 255, 0, msg(L"rules")));
area.add(new Button(110, 515, 80, 25, buttonFont, 255, 255, 0, L"blue.bmp", msg(L"prev"), prevCmd));
area.add(new Button(200, 515, 80, 25, buttonFont, 255, 255, 0, L"blue.bmp", msg(L"next"), nextCmd));
ExitCommand exitCmd(area);
area.add(new Button(610, 515, 80, 25, buttonFont, 255, 255, 0, L"blue.bmp", msg(L"close"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
printPage();
area.run();
}
void Description::printPage()
{
TextPage *page = text->getPage(currentPage);
if (! page)
return;
int len = page->getWidgetsCount();
for (int i = 0; i < len; i++) {
Widget *w = page->getWidget(i);
if (w) {
widgets.push_back(w);
area.add(w);
}
}
}
CursorCommand::CursorCommand(int s, Description &d, unsigned int *v):
description(d)
{
step = s;
value = v;
}
void CursorCommand::doAction()
{
if ((! *value) && (0 > step))
return;
unsigned int newPageNo = *value + step;
TextPage *page = description.getPage(newPageNo);
if (page) {
*value = newPageNo;
description.updateInfo();
}
}
TextPage::~TextPage()
{
for (std::vector<Widget*>::iterator i = widgets.begin();
i != widgets.end(); i++)
delete *i;
}
TextParser::TextParser(const std::wstring &text, Font &font,
int x, int y, int width, int height): tokenizer(text), font(font)
{
spaceWidth = font.getWidth(L' ');
charHeight = font.getWidth(L'A');
offsetX = x;
offsetY = y;
pageWidth = width;
pageHeight = height;
}
TextParser::~TextParser()
{
for (std::vector<TextPage*>::iterator i = pages.begin();
i != pages.end(); i++)
delete *i;
for (std::map<std::wstring, SDL_Surface*>::iterator i = images.begin();
i != images.end(); i++)
delete (*i).second;
}
void TextParser::addLine(TextPage *page, std::wstring &line, int &curPosY,
int &lineWidth)
{
if (0 < line.length()) {
page->add(new Label(&font, offsetX, offsetY + curPosY,
255,255,255, line, false));
line.clear();
curPosY += 10 + charHeight;
lineWidth = 0;
}
}
bool TextParser::isImage(const std::wstring &name)
{
int len = name.length();
return (3 < len) && (L'$' == name[0]) && (L'$' == name[len - 1]);
}
std::wstring TextParser::keywordToImage(const std::wstring &name)
{
return name.substr(1, name.length() - 2);
}
SDL_Surface* TextParser::getImage(const std::wstring &name)
{
SDL_Surface *img = images[name];
if (! img) {
img = loadImage(name);
images[name] = img;
}
return img;
}
void TextParser::parseNextPage()
{
if (tokenizer.isFinished())
return;
int curPosY = 0;
int lineWidth = 0;
TextPage *page = new TextPage();
std::wstring line;
while (true) {
Token t = tokenizer.getNextToken();
if (Token::Eof == t.getType())
break;
if (Token::Para == t.getType()) {
if (0 < line.length())
addLine(page, line, curPosY, lineWidth);
if (! page->isEmpty())
curPosY += 10;
} else if (Token::Word == t.getType()) {
const std::wstring &word = t.getContent();
if (isImage(word)) {
addLine(page, line, curPosY, lineWidth);
SDL_Surface *image = getImage(keywordToImage(word));
if ((image->h + curPosY < pageHeight) || page->isEmpty()) {
int x = offsetX + (pageWidth - image->w) / 2;
page->add(new Picture(x, offsetY + curPosY, image));
curPosY += image->h;
} else {
tokenizer.unget(t);
break;
}
} else {
int width = font.getWidth(word);
if (lineWidth + width > pageWidth) {
if (! lineWidth) {
line = word;
addLine(page, line, curPosY, lineWidth);
} else {
addLine(page, line, curPosY, lineWidth);
if (curPosY >= pageHeight) {
tokenizer.unget(t);
break;
}
line = word;
lineWidth = width;
}
} else {
lineWidth += width;
if (line.size()) {
line += L' ';
lineWidth += spaceWidth;
}
line += word;
}
}
}
if (curPosY >= pageHeight)
break;
}
addLine(page, line, curPosY, lineWidth);
if (! page->isEmpty())
pages.push_back(page);
else
delete page;
}
TextPage* TextParser::getPage(unsigned int no)
{
while ((! tokenizer.isFinished()) && (pages.size() <= no))
parseNextPage();
if (pages.size() <= no)
return NULL;
else
return pages[no];
}

12
descr.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef __DESCR_H__
#define __DESCR_H__
#include "widgets.h"
void showDescription(Area *parentArea);
#endif

BIN
einstein.res Normal file

Binary file not shown.

24
exceptions.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __EXCEPTIONS_H__
#define __EXCEPTIONS_H__
#include <string>
#include <iostream>
class Exception
{
private:
std::wstring message;
public:
Exception(const std::wstring& msg) { message = msg; /*std::cout << msg << std::endl;*/ };
virtual ~Exception() { };
public:
const std::wstring& getMessage() const { return message; };
};
#endif

117
font.cpp Normal file
View File

@ -0,0 +1,117 @@
#include "font.h"
#include "main.h"
#include "utils.h"
#include "unicode.h"
static Uint16 *convBuf = NULL;
static size_t bufSize = 0;
static Uint16 *strToUint16(const std::wstring &text)
{
const wchar_t *str = text.c_str();
if ((! str) || (sizeof(wchar_t) == sizeof(Uint16)))
return (Uint16*)str;
else {
size_t len = wcslen(str);
if (! convBuf) {
size_t sz = len * 2 + 1;
convBuf = (Uint16*)malloc(sizeof(Uint16) * sz);
bufSize = sz;
} else
if (bufSize < len + 1) {
size_t sz = len * 2 + 1;
convBuf = (Uint16*)realloc(convBuf, sizeof(Uint16) * sz);
// I should check if it is NULL, but I'm too lazy today
bufSize = sz;
}
for (unsigned int i = 0; i <= len; i++)
convBuf[i] = (Uint16)str[i];
return convBuf;
}
}
Font::Font(const std::wstring &name, int ptsize)
{
int size;
data = resources->getRef(name, size);
if (! data)
throw Exception(name + L" not found");
SDL_RWops *op = SDL_RWFromMem(data, size);
font = TTF_OpenFontRW(op, 1, ptsize);
if (! font)
throw Exception(L"Error loading font " + name);
}
Font::~Font()
{
TTF_CloseFont(font);
resources->delRef(data);
}
void Font::draw(SDL_Surface *s, int x, int y, int r, int g, int b,
bool shadow, const std::wstring &text)
{
if (text.length() < 1)
return;
Uint16 *str = strToUint16(text);
if (shadow) {
SDL_Color color = { 1, 1, 1, 1 };
SDL_Surface *surface = TTF_RenderUNICODE_Blended(font, str, color);
SDL_Rect src = { 0, 0, surface->w, surface->h };
SDL_Rect dst = { x+1, y+1, surface->w, surface->h };
SDL_BlitSurface(surface, &src, s, &dst);
SDL_FreeSurface(surface);
}
SDL_Color color = { r, g, b, 0 };
SDL_Surface *surface = TTF_RenderUNICODE_Blended(font, str, color);
SDL_Rect src = { 0, 0, surface->w, surface->h };
SDL_Rect dst = { x, y, surface->w, surface->h };
SDL_BlitSurface(surface, &src, s, &dst);
SDL_FreeSurface(surface);
}
void Font::draw(int x, int y, int r, int g, int b, bool shadow,
const std::wstring &text)
{
draw(screen.getSurface(), x, y, r,g,b, shadow, text);
}
int Font::getWidth(const std::wstring &text)
{
int w, h;
Uint16 *str = strToUint16(text);
TTF_SizeUNICODE(font, str, &w, &h);
return w;
}
int Font::getWidth(wchar_t ch)
{
int minx, maxx, miny, maxy, advance;
TTF_GlyphMetrics(font, (Uint16)ch, &minx, &maxx, &miny, &maxy, &advance);
return advance;
}
int Font::getHeight(const std::wstring &text)
{
int w, h;
Uint16 *str = strToUint16(text);
TTF_SizeUNICODE(font, str, &w, &h);
return h;
}
void Font::getSize(const std::wstring &text, int &width, int &height)
{
Uint16 *str = strToUint16(text);
TTF_SizeUNICODE(font, str, &width, &height);
}

32
font.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __FONT_H__
#define __FONT_H__
#include <string>
#include <SDL_ttf.h>
class Font
{
private:
TTF_Font *font;
void *data;
public:
Font(const std::wstring &name, int ptsize);
~Font();
public:
void draw(SDL_Surface *s, int x, int y, int r, int g, int b,
bool shadow, const std::wstring &text);
void draw(int x, int y, int r, int g, int b, bool shadow,
const std::wstring &text);
int getWidth(const std::wstring &text);
int getWidth(wchar_t ch);
int getHeight(const std::wstring &text);
void getSize(const std::wstring &text, int &width, int &height);
};
#endif

187
formatter.cpp Normal file
View File

@ -0,0 +1,187 @@
#include "formatter.h"
#include "utils.h"
#include "convert.h"
#define ADD_ARG(t) \
commands[commandsCnt].type = t; \
argNo = readInt(data + offset); \
if (argNo > maxArg) \
maxArg = argNo; \
commands[commandsCnt].data = (void*)argNo; \
commandsCnt++;
Formatter::Formatter(unsigned char *data, int offset)
{
int cnt = readInt(data + offset);
if (! cnt) {
commandsCnt = argsCnt = 0;
commands = NULL;
args = NULL;
}
offset += 4;
commands = new Command[cnt];
commandsCnt = 0;
int maxArg = 0, argNo;
for (int i = 0; i < cnt; i++) {
int type = data[offset];
offset++;
int size = readInt(data + offset);
offset += 4;
switch (type) {
case 1:
commands[commandsCnt].type = TEXT_COMMAND;
commands[commandsCnt].data = new std::wstring(
fromUtf8((char*)data + offset, size));
commandsCnt++;
break;
case 2: ADD_ARG(INT_ARG); break;
case 3: ADD_ARG(STRING_ARG); break;
case 4: ADD_ARG(FLOAT_ARG); break;
case 5: ADD_ARG(DOUBLE_ARG); break;
}
offset += size;
}
argsCnt = maxArg;
if (! argsCnt)
args = NULL;
else {
args = new CmdType[argsCnt];
memset(args, 0, sizeof(CmdType) * argsCnt);
for (int i = 0; i < commandsCnt; i++) {
Command &c = commands[i];
if ((c.type == INT_ARG) || (c.type == STRING_ARG) ||
(c.type == FLOAT_ARG) || (c.type == DOUBLE_ARG))
{
int no = (int)c.data;
args[no - 1] = c.type;
}
}
}
}
Formatter::~Formatter()
{
for (int i = 0; i < commandsCnt; i++)
if (TEXT_COMMAND == commands[i].type)
delete (std::wstring*)(commands[i].data);
if (commands)
delete[] commands;
if (args)
delete[] args;
}
std::wstring Formatter::getMessage() const
{
std::wstring s;
for (int i = 0; i < commandsCnt; i++)
if (TEXT_COMMAND == commands[i].type)
s += *(std::wstring*)(commands[i].data);
return s;
}
class ArgValue
{
public:
virtual ~ArgValue() { };
virtual std::wstring format(Formatter::Command *command) = 0;
};
template <typename T>
class TemplatedArgValue: public ArgValue
{
private:
T value;
public:
TemplatedArgValue(const T &v) { value = v; };
virtual std::wstring format(Formatter::Command *command) {
return toString(value);
};
};
class StrArgValue: public ArgValue
{
private:
std::wstring value;
public:
StrArgValue(const std::wstring &v): value(v) { };
virtual std::wstring format(Formatter::Command *command) {
return value;
};
};
std::wstring Formatter::format(std::vector<ArgValue*> &argValues) const
{
std::wstring s;
int no;
for (int i = 0; i < commandsCnt; i++) {
Command *cmd = &commands[i];
switch (cmd->type) {
case TEXT_COMMAND:
s += *(std::wstring*)(cmd->data);
break;
case STRING_ARG:
case INT_ARG:
no = (int)cmd->data - 1;
if (no < (int)argValues.size())
s += argValues[no]->format(cmd);
break;
default: ;
}
}
return s;
}
std::wstring Formatter::format(va_list ap) const
{
if (! argsCnt)
return getMessage();
std::vector<ArgValue*> argValues;
for (int i = 0; i < argsCnt; i++) {
switch (args[i]) {
case INT_ARG:
argValues.push_back(new TemplatedArgValue<int>
(va_arg(ap, int)));
break;
case STRING_ARG:
argValues.push_back(new StrArgValue(va_arg(ap, wchar_t*)));
break;
case DOUBLE_ARG:
argValues.push_back(new TemplatedArgValue<double>
(va_arg(ap, double)));
break;
case FLOAT_ARG:
argValues.push_back(new TemplatedArgValue<float>
((float)va_arg(ap, double)));
break;
default:
i = argsCnt;
}
}
std::wstring s = format(argValues);
for (std::vector<ArgValue*>::iterator i = argValues.begin();
i != argValues.end(); i++)
delete *i;
return s;
}

63
formatter.h Normal file
View File

@ -0,0 +1,63 @@
#ifndef __FORMATTER_H__
#define __FORMATTER_H__
#include <stdarg.h>
#include <string>
#include <vector>
class ArgValue;
/// Localized message formatter
class Formatter
{
public:
typedef enum {
EMPTY_CMD = 0,
TEXT_COMMAND,
INT_ARG,
STRING_ARG,
DOUBLE_ARG,
FLOAT_ARG
} CmdType;
typedef struct
{
CmdType type;
void *data;
} Command;
private:
int commandsCnt;
int argsCnt;
Command *commands;
CmdType *args;
public:
/// Create localized message from message buffer.
/// \param data buffer contained message file
/// \param offset offset to message from buffer start
Formatter(unsigned char *data, int offset);
~Formatter();
public:
/// Get message text.
std::wstring getMessage() const;
/// Fromat message
/// \param ap list of arguments
std::wstring format(va_list ap) const;
private:
std::wstring format(std::vector<ArgValue*> &argValues) const;
};
#endif

569
game.cpp Normal file
View File

@ -0,0 +1,569 @@
#include "main.h"
#include "utils.h"
#include "widgets.h"
#include "puzzle.h"
#include "verthints.h"
#include "horhints.h"
#include "widgets.h"
#include "font.h"
#include "topscores.h"
#include "opensave.h"
#include "options.h"
#include "game.h"
#include "messages.h"
#include "sound.h"
#include "descr.h"
class GameBackground: public Widget
{
public:
virtual void draw();
};
void GameBackground::draw()
{
// draw background
drawWallpaper(L"rain.bmp");
// draw title
SDL_Surface *tile = loadImage(L"title.bmp");
screen.draw(8, 10, tile);
SDL_FreeSurface(tile);
Font titleFont(L"nova.ttf", 28);
titleFont.draw(screen.getSurface(), 20, 20, 255,255,0, true,
msg(L"einsteinPuzzle"));
screen.addRegionToUpdate(0, 0, screen.getWidth(), screen.getHeight());
}
class ToggleHintCommand: public Command
{
private:
VertHints *verHints;
HorHints *horHints;
public:
ToggleHintCommand(VertHints *v, HorHints *h) {
verHints = v;
horHints = h;
};
virtual void doAction() {
verHints->toggleExcluded();
horHints->toggleExcluded();
};
};
class Watch: public TimerHandler, public Widget
{
private:
Uint32 lastRun;
Uint32 elapsed;
bool stoped;
int lastUpdate;
Font *font;
public:
Watch();
Watch(std::istream &stream);
virtual ~Watch();
public:
virtual void onTimer();
void stop();
void start();
virtual void draw();
int getElapsed() { return elapsed; };
void save(std::ostream &stream);
void reset();
};
Watch::Watch()
{
lastRun = elapsed = lastUpdate = 0;
stop();
font = new Font(L"luximb.ttf", 16);
}
Watch::Watch(std::istream &stream)
{
elapsed = readInt(stream);
lastUpdate = 0;
stop();
font = new Font(L"luximb.ttf", 16);
}
Watch::~Watch()
{
delete font;
}
void Watch::onTimer()
{
if (stoped)
return;
Uint32 now = SDL_GetTicks();
elapsed += now - lastRun;
lastRun = now;
int seconds = elapsed / 1000;
if (seconds != lastUpdate)
draw();
}
void Watch::stop()
{
stoped = true;
}
void Watch::start()
{
stoped = false;
lastRun = SDL_GetTicks();
}
void Watch::draw()
{
int time = elapsed / 1000;
std::wstring s = secToStr(time);
int x = 700;
int y = 24;
int w, h;
font->getSize(s, w, h);
SDL_Rect rect = { x-2, y-2, w+4, h+4 };
SDL_FillRect(screen.getSurface(), &rect,
SDL_MapRGB(screen.getSurface()->format, 0, 0, 255));
font->draw(x, y, 255,255,255, true, s);
screen.addRegionToUpdate(x-2, y-2, w+4, h+4);
lastUpdate = time;
}
void Watch::save(std::ostream &stream)
{
writeInt(stream, elapsed);
}
void Watch::reset()
{
elapsed = lastUpdate = 0;
lastRun = SDL_GetTicks();
}
class PauseGameCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Widget *background;
public:
PauseGameCommand(Area *a, Watch *w, Widget *bg) {
gameArea = a;
watch = w;
background = bg;
};
virtual void doAction() {
watch->stop();
Area area;
area.add(background, false);
Font font(L"laudcn2.ttf", 16);
area.add(new Window(280, 275, 240, 50, L"greenpattern.bmp", 6));
area.add(new Label(&font, 280, 275, 240, 50, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"paused")));
area.add(new AnyKeyAccel());
area.run();
sound->play(L"click.wav");
gameArea->updateMouse();
gameArea->draw();
watch->start();
};
};
class WinCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Game *game;
public:
WinCommand(Area *a, Watch *w, Game *g) {
gameArea = a;
watch = w;
game = g;
};
virtual void doAction() {
sound->play(L"applause.wav");
watch->stop();
Font font(L"laudcn2.ttf", 20);
showMessageWindow(gameArea, L"marble1.bmp",
500, 70, &font, 255,0,0, msg(L"won"));
gameArea->draw();
TopScores scores;
int score = watch->getElapsed() / 1000;
int pos = -1;
if (! game->isHinted()) {
if ((! scores.isFull()) || (score < scores.getMaxScore())) {
std::wstring name = enterNameDialog(gameArea);
pos = scores.add(name, score);
}
}
showScoresWindow(gameArea, &scores, pos);
gameArea->finishEventLoop();
};
};
class OkDlgCommand: public Command
{
private:
bool &res;
Area *area;
public:
OkDlgCommand(Area *a, bool &r): res(r) {
area = a;
};
virtual void doAction() {
res = true;
area->finishEventLoop();
};
};
class FailCommand: public Command
{
private:
Area *gameArea;
Game *game;
public:
FailCommand(Area *a, Game *g) { gameArea = a; game = g; };
virtual void doAction() {
sound->play(L"glasbk2.wav");
bool restart = false;
bool newGame = false;
Font font(L"laudcn2.ttf", 24);
Font btnFont(L"laudcn2.ttf", 14);
Area area;
area.add(gameArea);
area.add(new Window(220, 240, 360, 140, L"redpattern.bmp", 6));
area.add(new Label(&font, 250, 230, 300, 100, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"loose")));
OkDlgCommand newGameCmd(&area, newGame);
area.add(new Button(250, 340, 90, 25, &btnFont, 255,255,0,
L"redpattern.bmp", msg(L"startNew"), &newGameCmd));
OkDlgCommand restartCmd(&area, restart);
area.add(new Button(350, 340, 90, 25, &btnFont, 255,255,0,
L"redpattern.bmp", msg(L"tryAgain"), &restartCmd));
ExitCommand exitCmd(area);
area.add(new Button(450, 340, 90, 25, &btnFont, 255,255,0,
L"redpattern.bmp", msg(L"exit"), &exitCmd));
area.run();
if (restart || newGame) {
if (newGame)
game->newGame();
else
game->restart();
gameArea->draw();
gameArea->updateMouse();
} else
gameArea->finishEventLoop();
};
};
class CheatAccel: public Widget
{
private:
Command *command;
std::wstring typed;
std::wstring cheat;
public:
CheatAccel(const std::wstring s, Command *cmd): cheat(s) {
command = cmd;
};
public:
virtual bool onKeyDown(SDLKey key, unsigned char ch) {
if ((key >= SDLK_a) && (key <= SDLK_z)) {
wchar_t s = L'a' + key - SDLK_a;
typed += s;
if (typed.length() == cheat.length()) {
if (command && (typed == cheat))
command->doAction();
} else {
int pos = typed.length() - 1;
if (typed[pos] == cheat[pos])
return false;
}
}
if (typed.length() > 0)
typed = L"";
return false;
}
};
class CheatCommand: public Command
{
private:
Area *gameArea;
public:
CheatCommand(Area *a) { gameArea = a; };
virtual void doAction() {
Font font(L"nova.ttf", 30);
showMessageWindow(gameArea, L"darkpattern.bmp",
500, 100, &font, 255,255,255,
msg(L"iddqd"));
gameArea->draw();
};
};
class SaveGameCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Widget *background;
Game *game;
public:
SaveGameCommand(Area *a, Watch *w, Widget *bg, Game *g) {
gameArea = a;
watch = w;
background = bg;
game = g;
};
virtual void doAction() {
watch->stop();
Area area;
area.add(background, false);
saveGame(&area, game);
gameArea->updateMouse();
gameArea->draw();
watch->start();
};
};
class GameOptionsCommand: public Command
{
private:
Area *gameArea;
public:
GameOptionsCommand(Area *a) {
gameArea = a;
};
virtual void doAction() {
showOptionsWindow(gameArea);
gameArea->updateMouse();
gameArea->draw();
};
};
class HelpCommand: public Command
{
private:
Area *gameArea;
Watch *watch;
Widget *background;
public:
HelpCommand(Area *a, Watch *w, Widget *b) {
gameArea = a;
watch = w;
background = b;
};
virtual void doAction() {
watch->stop();
Area area;
area.add(background, false);
area.draw();
showDescription(&area);
gameArea->updateMouse();
gameArea->draw();
watch->start();
};
};
Game::Game()
{
genPuzzle();
possibilities = new Possibilities();
openInitial(*possibilities, rules);
puzzle = new Puzzle(iconSet, solvedPuzzle, possibilities);
verHints = new VertHints(iconSet, rules);
horHints = new HorHints(iconSet, rules);
watch = new Watch();
}
Game::Game(std::istream &stream)
{
pleaseWait();
loadPuzzle(solvedPuzzle, stream);
loadRules(rules, stream);
memcpy(savedSolvedPuzzle, solvedPuzzle, sizeof(solvedPuzzle));
savedRules = rules;
possibilities = new Possibilities(stream);
puzzle = new Puzzle(iconSet, solvedPuzzle, possibilities);
verHints = new VertHints(iconSet, rules, stream);
horHints = new HorHints(iconSet, rules, stream);
watch = new Watch(stream);
hinted = true;
}
Game::~Game()
{
delete watch;
delete possibilities;
delete verHints;
delete horHints;
delete puzzle;
deleteRules();
}
void Game::save(std::ostream &stream)
{
savePuzzle(solvedPuzzle, stream);
saveRules(rules, stream);
possibilities->save(stream);
verHints->save(stream);
horHints->save(stream);
watch->save(stream);
}
void Game::deleteRules()
{
for (Rules::iterator i = rules.begin(); i != rules.end(); i++)
delete *i;
rules.clear();
}
void Game::pleaseWait()
{
drawWallpaper(L"rain.bmp");
Window window(230, 260, 340, 80, L"greenpattern.bmp", 6);
window.draw();
Font font(L"laudcn2.ttf", 16);
Label label(&font, 280, 275, 240, 50, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"loading"));
label.draw();
screen.addRegionToUpdate(0, 0, screen.getWidth(), screen.getHeight());
screen.flush();
}
void Game::genPuzzle()
{
pleaseWait();
int horRules, verRules;
do {
if (rules.size() > 0)
deleteRules();
::genPuzzle(solvedPuzzle, rules);
getHintsQty(rules, verRules, horRules);
} while ((horRules > 24) || (verRules > 15));
memcpy(savedSolvedPuzzle, solvedPuzzle, sizeof(solvedPuzzle));
savedRules = rules;
hinted = false;
}
void Game::resetVisuals()
{
possibilities->reset();
openInitial(*possibilities, rules);
puzzle->reset();
verHints->reset(rules);
horHints->reset(rules);
watch->reset();
}
void Game::newGame()
{
genPuzzle();
resetVisuals();
}
void Game::restart()
{
memcpy(solvedPuzzle, savedSolvedPuzzle, sizeof(solvedPuzzle));
rules = savedRules;
resetVisuals();
hinted = true;
}
#define BUTTON(x, y, text, cmd) \
area.add(new Button(x, y, 94, 30, &btnFont, 255,255,0, \
L"btn.bmp", msg(text), false, cmd));
void Game::run()
{
Area area;
Font btnFont(L"laudcn2.ttf", 14);
area.setTimer(300, watch);
GameBackground *background = new GameBackground();
area.add(background);
CheatCommand cheatCmd(&area);
area.add(new CheatAccel(L"iddqd", &cheatCmd));
WinCommand winCmd(&area, watch, this);
FailCommand failCmd(&area, this);
puzzle->setCommands(&winCmd, &failCmd);
area.add(puzzle, false);
area.add(verHints, false);
area.add(horHints, false);
PauseGameCommand pauseGameCmd(&area, watch, background);
BUTTON(12, 400, L"pause", &pauseGameCmd)
ToggleHintCommand toggleHintsCmd(verHints, horHints);
BUTTON(119, 400, L"switch", &toggleHintsCmd)
SaveGameCommand saveCmd(&area, watch, background, this);
BUTTON(12, 440, L"save", &saveCmd)
GameOptionsCommand optionsCmd(&area);
BUTTON(119, 440, L"options", &optionsCmd)
ExitCommand exitGameCmd(area);
BUTTON(226, 400, L"exit", &exitGameCmd)
area.add(new KeyAccel(SDLK_ESCAPE, &exitGameCmd));
HelpCommand helpCmd(&area, watch, background);
BUTTON(226, 440, L"help", &helpCmd)
area.add(watch, false);
watch->start();
area.run();
}

58
game.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef __GAME_H__
#define __GAME_H__
#include <iostream>
#include "puzgen.h"
#include "verthints.h"
#include "horhints.h"
#include "puzzle.h"
class Watch;
class Game
{
private:
SolvedPuzzle solvedPuzzle;
Rules rules;
Possibilities *possibilities;
VertHints *verHints;
HorHints *horHints;
IconSet iconSet;
Puzzle *puzzle;
Watch *watch;
bool hinted;
SolvedPuzzle savedSolvedPuzzle;
Rules savedRules;
public:
Game();
Game(std::istream &stream);
~Game();
public:
SolvedPuzzle& getSolvedPuzzle() { return solvedPuzzle; };
Rules& getRules() { return rules; };
Possibilities* getPossibilities() { return possibilities; };
VertHints* getVerHints() { return verHints; };
HorHints* getHorHints() { return horHints; };
void save(std::ostream &stream);
void run();
bool isHinted() { return hinted; };
void setHinted() { hinted = true; };
void restart();
void newGame();
private:
void deleteRules();
void pleaseWait();
void genPuzzle();
void resetVisuals();
};
#endif

207
horhints.cpp Normal file
View File

@ -0,0 +1,207 @@
#include "horhints.h"
#include "main.h"
#include "utils.h"
#include "sound.h"
#define HINTS_COLS 3
#define HINTS_ROWS 8
#define TILE_GAP_X 4
#define TILE_GAP_Y 4
#define TILE_X 348
#define TILE_Y 68
#define TILE_WIDTH 48
#define TILE_HEIGHT 48
HorHints::HorHints(IconSet &is, Rules &r): iconSet(is)
{
reset(r);
}
HorHints::HorHints(IconSet &is, Rules &rl, std::istream &stream): iconSet(is)
{
int qty = readInt(stream);
for (int i = 0; i < qty; i++) {
int no = readInt(stream);
numbersArr.push_back(no);
Rule *r = getRule(rl, no);
int excluded = readInt(stream);
if (excluded) {
excludedRules.push_back(r);
rules.push_back(NULL);
} else {
excludedRules.push_back(NULL);
rules.push_back(r);
}
}
showExcluded = readInt(stream);
int x, y;
SDL_GetMouseState(&x, &y);
highlighted = getRuleNo(x, y);
}
void HorHints::reset(Rules &r)
{
rules.clear();
excludedRules.clear();
numbersArr.clear();
int no = 0;
for (Rules::iterator i = r.begin(); i != r.end(); i++) {
Rule *rule = *i;
if (rule->getShowOpts() == Rule::SHOW_HORIZ) {
rules.push_back(rule);
excludedRules.push_back(NULL);
numbersArr.push_back(no);
}
no++;
}
showExcluded = false;
int x, y;
SDL_GetMouseState(&x, &y);
highlighted = getRuleNo(x, y);
}
void HorHints::draw()
{
for (int i = 0; i < HINTS_ROWS; i++)
for (int j = 0; j < HINTS_COLS; j++)
drawCell(j, i, true);
}
void HorHints::drawCell(int col, int row, bool addToUpdate)
{
int x = TILE_X + col * (TILE_WIDTH*3 + TILE_GAP_X);
int y = TILE_Y + row * (TILE_HEIGHT + TILE_GAP_Y);
Rule *r = NULL;
int no = row * HINTS_COLS + col;
if (no < (int)rules.size())
if (showExcluded)
r = excludedRules[no];
else
r = rules[no];
if (r)
r->draw(x, y, iconSet, no == highlighted);
else
for (int i = 0; i < 3; i++)
screen.draw(x + TILE_HEIGHT*i, y, iconSet.getEmptyHintIcon());
if (addToUpdate)
screen.addRegionToUpdate(x, y, TILE_WIDTH*3, TILE_HEIGHT);
}
bool HorHints::onMouseButtonDown(int button, int x, int y)
{
if (button != 3)
return false;
int no = getRuleNo(x, y);
if (no < 0) return false;
int row = no / HINTS_COLS;
int col = no - row * HINTS_COLS;
if (showExcluded) {
Rule *r = excludedRules[no];
if (r) {
sound->play(L"whizz.wav");
rules[no] = r;
excludedRules[no] = NULL;
drawCell(col, row);
}
} else {
Rule *r = rules[no];
if (r) {
sound->play(L"whizz.wav");
rules[no] = NULL;
excludedRules[no] = r;
drawCell(col, row);
}
}
return true;
}
void HorHints::toggleExcluded()
{
showExcluded = !showExcluded;
draw();
}
bool HorHints::onMouseMove(int x, int y)
{
int no = getRuleNo(x, y);
if (no != highlighted) {
int old = highlighted;
highlighted = no;
if (isActive(old)) {
int row = old / HINTS_COLS;
int col = old - row * HINTS_COLS;
drawCell(col, row);
}
if (isActive(no)) {
int row = no / HINTS_COLS;
int col = no - row * HINTS_COLS;
drawCell(col, row);
}
}
return false;
}
int HorHints::getRuleNo(int x, int y)
{
if (! isInRect(x, y, TILE_X, TILE_Y, (TILE_WIDTH*3 + TILE_GAP_X) * HINTS_COLS,
(TILE_HEIGHT + TILE_GAP_Y) * HINTS_ROWS))
return -1;
x = x - TILE_X;
y = y - TILE_Y;
int col = x / (TILE_WIDTH*3 + TILE_GAP_X);
if (col * (TILE_WIDTH*3 + TILE_GAP_X) + TILE_WIDTH*3 < x)
return -1;
int row = y / (TILE_HEIGHT + TILE_GAP_Y);
if (row * (TILE_HEIGHT + TILE_GAP_Y) + TILE_HEIGHT < y)
return -1;
int no = row * HINTS_COLS + col;
if (no >= (int)rules.size())
return -1;
return no;
}
bool HorHints::isActive(int ruleNo)
{
if ((ruleNo < 0) || (ruleNo >= (int)rules.size()))
return false;
Rule *r = showExcluded ? excludedRules[ruleNo] : rules[ruleNo];
return r != NULL;
}
void HorHints::save(std::ostream &stream)
{
int cnt = numbersArr.size();
writeInt(stream, cnt);
for (int i = 0; i < cnt; i++) {
writeInt(stream, numbersArr[i]);
writeInt(stream, rules[i] ? 0 : 1);
}
writeInt(stream, showExcluded ? 1 : 0);
}

41
horhints.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __HORHINTS_H__
#define __HORHINTS_H__
#include <vector>
#include "iconset.h"
#include "puzgen.h"
#include "widgets.h"
class HorHints: public Widget
{
private:
IconSet &iconSet;
typedef std::vector<Rule*> RulesArr;
RulesArr rules;
RulesArr excludedRules;
std::vector<int> numbersArr;
bool showExcluded;
int highlighted;
public:
HorHints(IconSet &is, Rules &rules);
HorHints(IconSet &is, Rules &rules, std::istream &stream);
public:
virtual void draw();
void drawCell(int col, int row, bool addToUpdate=true);
virtual bool onMouseButtonDown(int button, int x, int y);
void toggleExcluded();
int getRuleNo(int x, int y);
virtual bool onMouseMove(int x, int y);
bool isActive(int ruleNo);
void save(std::ostream &stream);
void reset(Rules &rules);
};
#endif

430
i18n.cpp Normal file
View File

@ -0,0 +1,430 @@
#include "i18n.h"
#include <locale.h>
#include "unicode.h"
#include "convert.h"
#ifdef WIN32
#include <windows.h>
//#include <winnls.h>
#endif
Locale locale;
#ifdef WIN32
static struct _CountryMap {
wchar_t iso2[3];
char iso3[4];
} countries[] = {
{ L"AF", "AFG" },
{ L"AL", "ALB" },
{ L"DZ", "DZA" },
{ L"AS", "ASM" },
{ L"AD", "AND" },
{ L"AO", "AGO" },
{ L"AI", "AIA" },
{ L"AQ", "ATA" },
{ L"AG", "ATG" },
{ L"AR", "ARG" },
{ L"AM", "ARM" },
{ L"AW", "ABW" },
{ L"AU", "AUS" },
{ L"AT", "AUT" },
{ L"AZ", "AZE" },
{ L"BS", "BHS" },
{ L"BH", "BHR" },
{ L"BD", "BGD" },
{ L"BB", "BRB" },
{ L"BY", "BLR" },
{ L"BE", "BEL" },
{ L"BZ", "BLZ" },
{ L"BJ", "BEN" },
{ L"BM", "BMU" },
{ L"BT", "BTN" },
{ L"BO", "BOL" },
{ L"BA", "BIH" },
{ L"BW", "BWA" },
{ L"BV", "BVT" },
{ L"BR", "BRA" },
{ L"IO", "IOT" },
{ L"BN", "BRN" },
{ L"BG", "BGR" },
{ L"BF", "BFA" },
{ L"BI", "BDI" },
{ L"KH", "KHM" },
{ L"CM", "CMR" },
{ L"CA", "CAN" },
{ L"CV", "CPV" },
{ L"KY", "CYM" },
{ L"CF", "CAF" },
{ L"TD", "TCD" },
{ L"CL", "CHL" },
{ L"CN", "CHN" },
{ L"CX", "CXR" },
{ L"CC", "CCK" },
{ L"CO", "COL" },
{ L"KM", "COM" },
{ L"CD", "COD" },
{ L"CG", "COG" },
{ L"CK", "COK" },
{ L"CR", "CRI" },
{ L"CI", "CIV" },
{ L"HR", "HRV" },
{ L"CU", "CUB" },
{ L"CY", "CYP" },
{ L"CZ", "CZE" },
{ L"DK", "DNK" },
{ L"DJ", "DJI" },
{ L"DM", "DMA" },
{ L"DO", "DOM" },
{ L"TL", "TLS" },
{ L"EC", "ECU" },
{ L"EG", "EGY" },
{ L"SV", "SLV" },
{ L"GQ", "GNQ" },
{ L"ER", "ERI" },
{ L"EE", "EST" },
{ L"ET", "ETH" },
{ L"FK", "FLK" },
{ L"FO", "FRO" },
{ L"FJ", "FJI" },
{ L"FI", "FIN" },
{ L"FR", "FRA" },
{ L"FX", "FXX" },
{ L"GF", "GUF" },
{ L"PF", "PYF" },
{ L"TF", "ATF" },
{ L"GA", "GAB" },
{ L"GM", "GMB" },
{ L"GE", "GEO" },
{ L"DE", "DEU" },
{ L"GH", "GHA" },
{ L"GI", "GIB" },
{ L"GR", "GRC" },
{ L"GL", "GRL" },
{ L"GD", "GRD" },
{ L"GP", "GLP" },
{ L"GU", "GUM" },
{ L"GT", "GTM" },
{ L"GN", "GIN" },
{ L"GW", "GNB" },
{ L"GY", "GUY" },
{ L"HT", "HTI" },
{ L"HM", "HMD" },
{ L"HN", "HND" },
{ L"HK", "HKG" },
{ L"HU", "HUN" },
{ L"IS", "ISL" },
{ L"IN", "IND" },
{ L"ID", "IDN" },
{ L"IR", "IRN" },
{ L"IQ", "IRQ" },
{ L"IE", "IRL" },
{ L"IL", "ISR" },
{ L"IT", "ITA" },
{ L"JM", "JAM" },
{ L"JP", "JPN" },
{ L"JO", "JOR" },
{ L"KZ", "KAZ" },
{ L"KE", "KEN" },
{ L"KI", "KIR" },
{ L"KP", "PRK" },
{ L"KR", "KOR" },
{ L"KW", "KWT" },
{ L"KG", "KGZ" },
{ L"LA", "LAO" },
{ L"LV", "LVA" },
{ L"LB", "LBN" },
{ L"LS", "LSO" },
{ L"LR", "LBR" },
{ L"LY", "LBY" },
{ L"LI", "LIE" },
{ L"LT", "LTU" },
{ L"LU", "LUX" },
{ L"MO", "MAC" },
{ L"MK", "MKD" },
{ L"MG", "MDG" },
{ L"MW", "MWI" },
{ L"MY", "MYS" },
{ L"MV", "MDV" },
{ L"ML", "MLI" },
{ L"MT", "MLT" },
{ L"MH", "MHL" },
{ L"MQ", "MTQ" },
{ L"MR", "MRT" },
{ L"MU", "MUS" },
{ L"YT", "MYT" },
{ L"MX", "MEX" },
{ L"FM", "FSM" },
{ L"MD", "MDA" },
{ L"MC", "MCO" },
{ L"MN", "MNG" },
{ L"MS", "MSR" },
{ L"MA", "MAR" },
{ L"MZ", "MOZ" },
{ L"MM", "MMR" },
{ L"NA", "NAM" },
{ L"NR", "NRU" },
{ L"NP", "NPL" },
{ L"NL", "NLD" },
{ L"AN", "ANT" },
{ L"NC", "NCL" },
{ L"NZ", "NZL" },
{ L"NI", "NIC" },
{ L"NE", "NER" },
{ L"NG", "NGA" },
{ L"NU", "NIU" },
{ L"NF", "NFK" },
{ L"MP", "MNP" },
{ L"NO", "NOR" },
{ L"OM", "OMN" },
{ L"PK", "PAK" },
{ L"PW", "PLW" },
{ L"PS", "PSE" },
{ L"PA", "PAN" },
{ L"PG", "PNG" },
{ L"PY", "PRY" },
{ L"PE", "PER" },
{ L"PH", "PHL" },
{ L"PN", "PCN" },
{ L"PL", "POL" },
{ L"PT", "PRT" },
{ L"PR", "PRI" },
{ L"QA", "QAT" },
{ L"RE", "REU" },
{ L"RO", "ROU" },
{ L"RU", "RUS" },
{ L"RW", "RWA" },
{ L"KN", "KNA" },
{ L"LC", "LCA" },
{ L"VC", "VCT" },
{ L"WS", "WSM" },
{ L"SM", "SMR" },
{ L"ST", "STP" },
{ L"SA", "SAU" },
{ L"SN", "SEN" },
{ L"SC", "SYC" },
{ L"SL", "SLE" },
{ L"SG", "SGP" },
{ L"SK", "SVK" },
{ L"SI", "SVN" },
{ L"SB", "SLB" },
{ L"SO", "SOM" },
{ L"ZA", "ZAF" },
{ L"GS", "SGS" },
{ L"ES", "ESP" },
{ L"LK", "LKA" },
{ L"SH", "SHN" },
{ L"PM", "SPM" },
{ L"SD", "SDN" },
{ L"SR", "SUR" },
{ L"SJ", "SJM" },
{ L"SZ", "SWZ" },
{ L"SE", "SWE" },
{ L"CH", "CHE" },
{ L"SY", "SYR" },
{ L"TW", "TWN" },
{ L"TJ", "TJK" },
{ L"TZ", "TZA" },
{ L"TH", "THA" },
{ L"TG", "TGO" },
{ L"TK", "TKL" },
{ L"TO", "TON" },
{ L"TT", "TTO" },
{ L"TN", "TUN" },
{ L"TR", "TUR" },
{ L"TM", "TKM" },
{ L"TC", "TCA" },
{ L"TV", "TUV" },
{ L"UG", "UGA" },
{ L"UA", "UKR" },
{ L"AE", "ARE" },
{ L"GB", "GBR" },
{ L"US", "USA" },
{ L"UM", "UMI" },
{ L"UY", "URY" },
{ L"UZ", "UZB" },
{ L"VU", "VUT" },
{ L"VA", "VAT" },
{ L"VE", "VEN" },
{ L"VN", "VNM" },
{ L"VG", "VGB" },
{ L"VI", "VIR" },
{ L"WF", "WLF" },
{ L"EH", "ESH" },
{ L"YE", "YEM" },
{ L"YU", "YUG" },
{ L"ZM", "ZMB" },
{ L"ZW", "ZWE" },
{ L"", "" }
};
static wchar_t* mapIso3ContryToIso2(char *iso3)
{
if (! iso3)
return L"";
struct _CountryMap *m = countries;
while (m && m->iso3[0]) {
if (! strcmp(iso3, m->iso3))
return m->iso2;
m++;
}
return L"";
}
#endif
Locale::Locale()
{
#ifndef WIN32
parseLocale(fromMbcs(setlocale(LC_ALL, "")));
#else
setlocale(LC_ALL, "");
char buf[100];
int len;
len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVCTRYNAME, buf, 99);
if (len > 0)
country = mapIso3ContryToIso2(buf);
len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, buf, 99);
if (len > 0) {
/* according to MSDN:
LOCALE_SABBREVLANGNAME Abbreviated name of the language,
created by taking the 2-letter language abbreviation from the
ISO Standard 639 and adding a third letter, as appropriate,
to indicate the sublanguage.
*/
if (len == 4) // exclude subvariant letter
buf[2] = 0;
language = toLowerCase(fromMbcs(buf));
}
len = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, buf, 99);
if (len > 0)
encoding = L"CP" + std::wstring(fromMbcs(buf)); // FIXME!
#endif
setlocale(LC_NUMERIC, "C"); // hack because of numbers in Lua
}
Locale::Locale(const Locale &locale): language(locale.language),
country(locale.country), encoding(locale.encoding)
{
}
void Locale::parseLocale(const std::wstring &name)
{
int pos = name.find(L'.');
std::wstring langAndCountry;
if (pos >= 0) {
encoding = name.substr(pos + 1);
langAndCountry = name.substr(0, pos);
} else {
encoding = L"";
langAndCountry = name;
}
pos = langAndCountry.find(L'_');
if (pos < 0) {
language = langAndCountry;
country = L"";
} else {
language = langAndCountry.substr(0, pos);
country = langAndCountry.substr(pos + 1);
}
language = toLowerCase(language);
country = toUpperCase(country);
encoding = toUpperCase(encoding);
}
static bool isLowerCase(const std::wstring &s)
{
int len = s.length();
for (int i = 0; i < len; i++) {
char ch = s[i];
if ((ch < L'a') || (ch > L'z'))
return false;
}
return true;
}
static bool isUpperCase(const std::wstring &s)
{
int len = s.length();
for (int i = 0; i < len; i++) {
char ch = s[i];
if ((ch < L'A') || (ch > L'Z'))
return false;
}
return true;
}
void splitFileName(const std::wstring &fileName, std::wstring &name,
std::wstring &ext, std::wstring &lang, std::wstring &country)
{
int pos = fileName.find_last_of(L'.');
if (pos <= 0) {
ext = L"";
name = fileName;
} else {
name = fileName.substr(0, pos);
ext = fileName.substr(pos + 1);
}
pos = name.find_last_of('_');
if ((pos <= 0) || (name.length() - pos != 3)) {
lang = L"";
country = L"";
} else {
std::wstring l = name.substr(pos + 1);
std::wstring s = name.substr(0, pos);
if (isUpperCase(l)) { // country
name = s;
country = l;
pos = name.find_last_of('_');
if ((pos <= 0) || (name.length() - pos != 3))
lang = L"";
else {
std::wstring l = name.substr(pos + 1);
std::wstring s = name.substr(0, pos);
if (isLowerCase(l)) { // language
name = s;
lang = l;
} else // invalid
lang = L"";
}
} else if (isLowerCase(l)) { // language
name = s;
lang = l;
country = L"";
} else { // invalid
lang = L"";
country = L"";
}
}
}
int getScore(const std::wstring &lang, const std::wstring &country,
const Locale &locale)
{
if ((! country.length()) && (! lang.length())) // locale independent
return 1;
int score = 0;
if (locale.getCountry().length() && (locale.getCountry() == country))
score += 2;
if (locale.getLanguage().length() && (locale.getLanguage() == lang))
score += 4;
return score;
}

58
i18n.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef __I18N_H__
#define __I18N_H__
/// \file i18n.h
/// Locale related functions
#include <string>
/// Description of current locale
class Locale
{
private:
std::wstring language;
std::wstring country;
std::wstring encoding;
public:
/// Load locale
Locale();
/// Copy constructor
Locale(const Locale &locale);
public:
/// Get current country.
const std::wstring& getCountry() const { return country; };
/// Get current language.
const std::wstring& getLanguage() const { return language; };
/// Get current encoding.
const std::wstring& getEncoding() const { return encoding; };
private:
void parseLocale(const std::wstring &name);
};
// split file name to file name, extension, language name and country
// for exmaple, "story_ru_RU.txt" shoud be splited to
// name="story", extension="txt", language="ru", country="RU"
void splitFileName(const std::wstring &fileName, std::wstring &name,
std::wstring &ext, std::wstring &lang, std::wstring &country);
// calculate relevance score between language, country and
// current locale
int getScore(const std::wstring &lang, const std::wstring &country,
const Locale &locale);
extern Locale locale;
#endif

57
iconset.cpp Normal file
View File

@ -0,0 +1,57 @@
#include <string.h>
#include "iconset.h"
#include "utils.h"
IconSet::IconSet()
{
std::wstring buf = L"xy.bmp";
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++) {
buf[1] = L'1' + j;
buf[0] = L'a' + i;
smallIcons[i][j][0] = loadImage(buf);
smallIcons[i][j][1] = adjustBrightness(smallIcons[i][j][0], 1.5, false);
buf[0] = L'A' + i;
largeIcons[i][j][0] = loadImage(buf);
largeIcons[i][j][1] = adjustBrightness(largeIcons[i][j][0], 1.5, false);
}
emptyFieldIcon = loadImage(L"tile.bmp");
emptyHintIcon = loadImage(L"hint-tile.bmp");
nearHintIcon[0] = loadImage(L"hint-near.bmp");
nearHintIcon[1] = adjustBrightness(nearHintIcon[0], 1.5, false);
sideHintIcon[0] = loadImage(L"hint-side.bmp");
sideHintIcon[1] = adjustBrightness(sideHintIcon[0], 1.5, false);
betweenArrow[0] = loadImage(L"betwarr.bmp", true);
betweenArrow[1] = adjustBrightness(betweenArrow[0], 1.5, false);
}
IconSet::~IconSet()
{
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++)
for (int k = 0; k < 2; k++) {
SDL_FreeSurface(smallIcons[i][j][k]);
SDL_FreeSurface(largeIcons[i][j][k]);
}
SDL_FreeSurface(emptyFieldIcon);
SDL_FreeSurface(emptyHintIcon);
SDL_FreeSurface(nearHintIcon[0]);
SDL_FreeSurface(nearHintIcon[1]);
SDL_FreeSurface(sideHintIcon[0]);
SDL_FreeSurface(sideHintIcon[1]);
SDL_FreeSurface(betweenArrow[0]);
SDL_FreeSurface(betweenArrow[1]);
}
SDL_Surface* IconSet::getLargeIcon(int row, int num, bool h)
{
return largeIcons[row][num-1][h ? 1 : 0];
}
SDL_Surface* IconSet::getSmallIcon(int row, int num, bool h)
{
return smallIcons[row][num-1][h ? 1 : 0];
}

32
iconset.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __ICONSET_H__
#define __ICONSET_H__
#include <SDL.h>
class IconSet
{
private:
SDL_Surface *smallIcons[6][6][2];
SDL_Surface *largeIcons[6][6][2];
SDL_Surface *emptyFieldIcon, *emptyHintIcon, *nearHintIcon[2];
SDL_Surface *sideHintIcon[2], *betweenArrow[2];
public:
IconSet();
virtual ~IconSet();
public:
SDL_Surface* getLargeIcon(int row, int num, bool highlighted);
SDL_Surface* getSmallIcon(int row, int num, bool highlighted);
SDL_Surface* getEmptyFieldIcon() { return emptyFieldIcon; };
SDL_Surface* getEmptyHintIcon() { return emptyHintIcon; };
SDL_Surface* getNearHintIcon(bool h) { return nearHintIcon[h ? 1 : 0]; };
SDL_Surface* getSideHintIcon(bool h) { return sideHintIcon[h ? 1 : 0]; };
SDL_Surface* getBetweenArrow(bool h) { return betweenArrow[h ? 1 : 0]; };
};
#endif

248
lexal.cpp Normal file
View File

@ -0,0 +1,248 @@
#include "lexal.h"
#include "convert.h"
Lexeme::Lexeme(Type t, const std::wstring &cont, int line, int pos)
{
type = t;
content = cont;
this->line = line;
this->pos = pos;
}
std::wstring Lexeme::getPosStr() const
{
return Lexal::posToStr(line, pos);
}
Lexal::Lexal(UtfStreamReader &rdr): reader(rdr)
{
line = 1;
pos = 0;
}
static bool isIdentStart(wchar_t ch) {
return ((L'a' <= ch) && (L'z' >= ch)) || ((L'A' <= ch) && (L'Z' >= ch))
|| (L'_' == ch);
}
static bool isIdentCont(wchar_t ch) {
return ((L'a' <= ch) && (L'z' >= ch)) || ((L'A' <= ch) && (L'Z' >= ch))
|| (L'_' == ch) || (L'.' == ch) || ((L'0' <= ch) && (L'9' >= ch));
}
static bool isWhiteSpace(wchar_t ch) {
return (L' ' == ch) || (L'\t' == ch) || (L'\n' == ch) || (L'\r' == ch);
}
static bool isDigit(wchar_t ch) {
return (L'0' <= ch) && (L'9' >= ch);
}
static bool isSymbol(wchar_t ch) {
return (L'{' == ch) || (L'}' == ch) || (L',' == ch) || (L'=' == ch)
|| (L';' == ch);
}
static bool isQuote(wchar_t ch) {
return (L'\'' == ch) || (L'"' == ch);
}
std::wstring Lexal::posToStr(int line, int pos)
{
return L"(" + toString(line) + L":" + toString(pos) + L")";
}
Lexeme Lexal::getNext()
{
skipSpaces();
if (reader.isEof())
return Lexeme(Lexeme::Eof, L"", line, pos);
int startLine = line;
int startPos = pos;
wchar_t ch = reader.getNextChar();
pos++;
if (isIdentStart(ch))
return readIdent(startLine, startPos, ch);
else if (isDigit(ch))
return readNumber(startLine, startPos, ch);
else if (isQuote(ch))
return readString(startLine, startPos, ch);
else if (isSymbol(ch))
return Lexeme(Lexeme::Symbol, toString(ch), startLine, startPos);
throw Exception(L"Invalid character at "+ posToStr(startLine, startPos));
}
Lexeme Lexal::readString(int startLine, int startPos, wchar_t quote)
{
std::wstring str;
bool closed = false;
while (! reader.isEof()) {
wchar_t ch = reader.getNextChar();
pos++;
if ('\n' == ch) {
line++;
pos = 0;
} else if ('\\' == ch) {
if (! reader.isEof()) {
wchar_t nextCh = reader.getNextChar();
if (isWhiteSpace(nextCh))
throw Exception(L"Invalid escape sequence at " +
posToStr(line, pos));
pos++;
switch (nextCh) {
case L'\t': str += L'\t'; break;
case L'\n': str += L'\n'; break;
case L'\r': str += L'\r'; break;
default:
str += nextCh;
}
}
} else if (quote == ch) {
closed = true;
break;
} else
str += ch;
}
if (! closed)
throw Exception(L"String at " + posToStr(startLine, startPos)
+ L" doesn't closed");
return Lexeme(Lexeme::String, str, startLine, startPos);
}
Lexeme Lexal::readNumber(int startLine, int startPos, wchar_t first)
{
std::wstring number;
number += first;
Lexeme::Type type = Lexeme::Integer;
while (! reader.isEof()) {
wchar_t ch = reader.getNextChar();
pos++;
if (isDigit(ch))
number += ch;
else if (L'.' == ch) {
if (Lexeme::Integer == type) {
type = Lexeme::Float;
number += ch;
} else
throw Exception(L"To many dots in number at " +
posToStr(line, pos));
} else if ((! isSymbol(ch)) && (! isWhiteSpace(ch)))
throw Exception(L"invalid number at " + posToStr(line, pos));
else {
pos--;
reader.ungetChar(ch);
break;
}
}
if (L'.' == number[number.length() - 1])
throw Exception(L"Missing digit after dot at " + posToStr(line, pos));
return Lexeme(type, number, startLine, startPos);
}
Lexeme Lexal::readIdent(int startLine, int startPos, wchar_t first)
{
std::wstring ident;
ident += first;
while (! reader.isEof()) {
wchar_t ch = reader.getNextChar();
if (! isIdentCont(ch)) {
reader.ungetChar(ch);
break;
}
ident += ch;
pos++;
}
return Lexeme(Lexeme::Ident, ident, startLine, startPos);
}
void Lexal::skipToLineEnd()
{
while (! reader.isEof()) {
wchar_t ch = reader.getNextChar();
pos++;
if ('\n' == ch) {
pos = 0;
line++;
return;
}
}
}
void Lexal::skipMultilineComment(int startLine, int startPos)
{
while (! reader.isEof()) {
wchar_t ch = reader.getNextChar();
pos++;
if ('\n' == ch) {
pos = 0;
line++;
} else if (('*' == ch) && (! reader.isEof())) {
wchar_t nextCh = reader.getNextChar();
if ('/' != nextCh)
reader.ungetChar(nextCh);
}
}
throw Exception(L"Remark started at " + posToStr(startLine, startPos)
+ L" is not closed");
}
void Lexal::skipSpaces()
{
while (! reader.isEof()) {
wchar_t ch = reader.getNextChar();
pos++;
if (! isWhiteSpace(ch)) {
if ('#' == ch)
skipToLineEnd();
else {
bool finish = false;
if (('/' == ch) && (! reader.isEof())) {
wchar_t nextCh = reader.getNextChar();
pos++;
if ('/' == nextCh)
skipToLineEnd();
else if ('*' == nextCh)
skipMultilineComment(line, pos);
else {
pos--;
reader.ungetChar(nextCh);
finish = true;
}
} else
finish = true;
if (finish) {
pos--;
reader.ungetChar(ch);
return;
}
}
} else
if ('\n' == ch) {
pos = 0;
line++;
}
}
}

66
lexal.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef __LEXAL_H__
#define __LEXAL_H__
#include "streams.h"
class Lexeme
{
public:
typedef enum Type {
String,
Integer,
Float,
Ident,
Symbol,
Eof
};
private:
int line;
int pos;
Type type;
std::wstring content;
public:
Lexeme() { };
Lexeme(Type type, const std::wstring &content, int line, int pos);
~Lexeme() { };
public:
const Type getType() const { return type; };
const std::wstring getContent() const { return content; };
std::wstring getPosStr() const;
int getLine() const { return line; };
int getPos() const { return pos; };
};
class Lexal
{
private:
UtfStreamReader &reader;
int line;
int pos;
public:
Lexal(UtfStreamReader &reader);
~Lexal() { };
public:
Lexeme getNext();
static std::wstring posToStr(int line, int pos);
private:
void skipSpaces();
void skipToLineEnd();
void skipMultilineComment(int startLine, int startPos);
Lexeme readIdent(int startLine, int startPos, wchar_t first);
Lexeme readNumber(int startLine, int startPos, wchar_t first);
Lexeme readString(int startLine, int startPos, wchar_t quote);
};
#endif

113
main.cpp Normal file
View File

@ -0,0 +1,113 @@
#include <stdlib.h>
#include <iostream>
#include <SDL.h>
#include <SDL_main.h>
#include <SDL_ttf.h>
#include "main.h"
#include "utils.h"
#include "storage.h"
#include "unicode.h"
#include "messages.h"
#include "sound.h"
Screen screen;
Random rndGen;
static void initScreen()
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
throw Exception(std::wstring(L"Error initializing SDL: ") +
fromMbcs(SDL_GetError()));
atexit(SDL_Quit);
if (TTF_Init())
throw Exception(L"Error initializing font engine");
screen.setMode(VideoMode(800, 600, 24,
getStorage()->get(L"fullscreen", 1) != 0));
screen.initCursors();
SDL_Surface *mouse = loadImage(L"cursor.bmp");
SDL_SetColorKey(mouse, SDL_SRCCOLORKEY, SDL_MapRGB(mouse->format, 0, 0, 0));
screen.setMouseImage(mouse);
SDL_FreeSurface(mouse);
SDL_WM_SetCaption("Einstein", NULL);
#ifdef __APPLE__
screen.setCursor(false);
#else
screen.setCursor(getStorage()->get(L"niceCursor", 1));
#endif
}
static void initAudio()
{
sound = new Sound();
sound->setVolume((float)getStorage()->get(L"volume", 20) / 100.0f);
}
#ifdef __APPLE__
static std::wstring getResourcesPath(const std::wstring& path)
{
int idx = path.find_last_of(L'/');
return path.substr(0, idx) + L"/../../";
}
#endif
static void loadResources(const std::wstring &selfPath)
{
StringList dirs;
#ifdef WIN32
dirs.push_back(getStorage()->get(L"path", L"") + L"\\res");
#else
#ifdef __APPLE__
dirs.push_back(getResourcesPath(selfPath));
#else
dirs.push_back(PREFIX L"/share/einstein/res");
dirs.push_back(fromMbcs(getenv("HOME")) + L"/.einstein/res");
#endif
#endif
dirs.push_back(L"res");
dirs.push_back(L".");
resources = new ResourcesCollection(dirs);
msg.load();
}
/*static void checkBetaExpire()
{
if (1124832535L + 60L*60L*24L*40L < time(NULL)) {
Font font(L"laudcn2.ttf", 16);
Area area;
showMessageWindow(&area, L"darkpattern.bmp",
700, 100, &font, 255,255,255,
msg(L"expired"));
}
}*/
int main(int argc, char *argv[])
{
#ifndef WIN32
ensureDirExists(fromMbcs(getenv("HOME")) + std::wstring(L"/.einstein"));
#endif
try {
loadResources(fromUtf8(argv[0]));
initScreen();
initAudio();
// checkBetaExpire();
menu();
getStorage()->flush();
} catch (Exception &e) {
std::cerr << L"ERROR: " << e.getMessage() << std::endl;
} catch (...) {
std::cerr << L"ERROR: Unknown exception" << std::endl;
}
screen.doneCursors();
return 0;
}

16
main.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __MAIN_H__
#define __MAIN_H__
#include "exceptions.h"
#include "screen.h"
#include "resources.h"
#include "random.h"
extern Screen screen;
extern Random rndGen;
void menu();
#endif

201
menu.cpp Normal file
View File

@ -0,0 +1,201 @@
#include <vector>
#include "main.h"
#include "utils.h"
#include "widgets.h"
#include "topscores.h"
#include "opensave.h"
#include "game.h"
#include "descr.h"
#include "options.h"
#include "messages.h"
class MenuBackground: public Widget
{
public:
virtual void draw();
};
void MenuBackground::draw()
{
SDL_Surface *title = loadImage(L"nova.bmp");
screen.draw(0, 0, title);
SDL_FreeSurface(title);
Font font(L"nova.ttf", 28);
std::wstring s(msg(L"einsteinFlowix"));
int width = font.getWidth(s);
font.draw((screen.getWidth() - width) / 2, 30, 255,255,255, true, s);
Font urlFont(L"luximb.ttf", 16);
s = L"http://games.flowix.com";
width = urlFont.getWidth(s);
urlFont.draw((screen.getWidth() - width) / 2, 60, 255,255,0, true, s);
screen.addRegionToUpdate(0, 0, screen.getWidth(), screen.getHeight());
}
class NewGameCommand: public Command
{
private:
Area *area;
public:
NewGameCommand(Area *a) { area = a; };
virtual void doAction() {
Game game;
game.run();
area->updateMouse();
area->draw();
};
};
class LoadGameCommand: public Command
{
private:
Area *area;
public:
LoadGameCommand(Area *a) { area = a; };
virtual void doAction() {
Game *game = loadGame(area);
if (game) {
game->run();
delete game;
}
area->updateMouse();
area->draw();
};
};
class TopScoresCommand: public Command
{
private:
Area *area;
public:
TopScoresCommand(Area *a) { area = a; };
virtual void doAction() {
TopScores scores;
showScoresWindow(area, &scores);
area->updateMouse();
area->draw();
};
};
class RulesCommand: public Command
{
private:
Area *area;
public:
RulesCommand(Area *a) { area = a; };
virtual void doAction() {
showDescription(area);
area->updateMouse();
area->draw();
};
};
class OptionsCommand: public Command
{
private:
Area *area;
public:
OptionsCommand(Area *a) { area = a; };
virtual void doAction() {
showOptionsWindow(area);
area->updateMouse();
area->draw();
};
};
class AboutCommand: public Command
{
private:
Area *parentArea;
public:
AboutCommand(Area *a) { parentArea = a; };
virtual void doAction() {
Area area;
Font titleFont(L"nova.ttf", 26);
Font font(L"laudcn2.ttf", 14);
Font urlFont(L"luximb.ttf", 16);
#define LABEL(pos, c, f, text) area.add(new Label(&f, 220, pos, 360, 20, \
Label::ALIGN_CENTER, Label::ALIGN_MIDDLE, 255,255,c, text));
area.add(parentArea);
area.add(new Window(220, 160, 360, 280, L"blue.bmp"));
area.add(new Label(&titleFont, 250, 165, 300, 40, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"about")));
LABEL(240, 255, font, msg(L"einsteinPuzzle"))
LABEL(260, 255, font, msg(L"version"))
LABEL(280, 255, font, msg(L"copyright"))
LABEL(330, 0, urlFont, L"http://games.flowix.com")
#undef LABEL
ExitCommand exitCmd(area);
area.add(new Button(360, 400, 80, 25, &font, 255,255,0, L"blue.bmp",
msg(L"ok"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
area.add(new KeyAccel(SDLK_RETURN, &exitCmd));
area.run();
parentArea->updateMouse();
parentArea->draw();
};
};
static Button* menuButton(int y, Font *font, const std::wstring &text,
Command *cmd=NULL)
{
return new Button(550, y, 220, 30, font, 0,240,240, 30,255,255, text, cmd);
}
void menu()
{
Area area;
Font font(L"laudcn2.ttf", 20);
area.add(new MenuBackground());
area.draw();
NewGameCommand newGameCmd(&area);
area.add(menuButton(340, &font, msg(L"newGame"), &newGameCmd));
LoadGameCommand loadGameCmd(&area);
area.add(menuButton(370, &font, msg(L"loadGame"), &loadGameCmd));
TopScoresCommand topScoresCmd(&area);
area.add(menuButton(400, &font, msg(L"topScores"), &topScoresCmd));
RulesCommand rulesCmd(&area);
area.add(menuButton(430, &font, msg(L"rules"), &rulesCmd));
OptionsCommand optionsCmd(&area);
area.add(menuButton(460, &font, msg(L"options"), &optionsCmd));
AboutCommand aboutCmd(&area);
area.add(menuButton(490, &font, msg(L"about"), &aboutCmd));
ExitCommand exitMenuCmd(area);
area.add(menuButton(520, &font, msg(L"exit"), &exitMenuCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitMenuCmd));
area.draw();
screen.addRegionToUpdate(0, 0, screen.getWidth(), screen.getHeight());
screen.flush();
area.run();
}

135
messages.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <stdarg.h>
#include "messages.h"
#include "formatter.h"
#include "resources.h"
#include "exceptions.h"
#include "buffer.h"
#include "utils.h"
#include "unicode.h"
Messages msg;
Messages::Messages()
{
}
Messages::~Messages()
{
}
class ResVisitor: public Visitor<Resource*>
{
private:
Messages &messages;
Buffer *buffer;
public:
ResVisitor(Messages &m, Buffer *b): messages(m) { buffer = b; };
virtual void onVisit(Resource *&r) {
messages.loadFromResource(r, buffer);
}
};
void Messages::load()
{
Buffer buffer;
ResVisitor loader(*this, &buffer);
resources->forEachInGroup(L"messages", loader);
}
void Messages::loadFromResource(Resource *res, Buffer *buffer)
{
if (! res) return;
int cnt = res->getVariantsCount();
for (int i = 0; i < cnt; i++) {
ResVariant *var = res->getVariant(i);
if (var) {
try {
int score = var->getI18nScore();
var->getData(*buffer);
loadBundle(score, (unsigned char*)buffer->getData(),
buffer->getSize());
} catch (Exception &e) {
std::cerr << std::wstring(L"Error loading text bundle " +
res->getName() + L": " + e.getMessage());
}
}
}
}
std::wstring Messages::getMessage(const std::wstring &key) const
{
StrMap::const_iterator i = messages.find(key);
if (i != messages.end())
return (*i).second.message->getMessage();
else
return key;
}
std::wstring Messages::format(const wchar_t *key, va_list ap) const
{
std::wstring s;
StrMap::const_iterator i = messages.find(key);
if (i != messages.end())
s = (*i).second.message->format(ap);
else
s = key;
return s;
}
std::wstring Messages::format(const wchar_t *key, ...) const
{
va_list ap;
va_start(ap, key);
std::wstring s = format(key, ap);
va_end(ap);
return s;
}
std::wstring Messages::operator ()(const wchar_t *key, ...) const
{
va_list ap;
va_start(ap, key);
std::wstring s = format(key, ap);
va_end(ap);
return s;
}
void Messages::loadBundle(int score, unsigned char *data, size_t size)
{
if ((data[0] != 'C') || (data[1] != 'M') || (data[2] != 'F'))
throw Exception(L"Invalid format of message file");
if (readInt(data + 3) != 1)
throw Exception(L"Unknown version of message file");
int offset = readInt(data + size - 4);
int cnt = readInt(data + offset);
offset += 4;
for (int i = 0; i < cnt; i++) {
int sz = readInt(data + offset);
offset += 4;
if (sz > 0) {
std::wstring name(fromUtf8((char*)data + offset, sz));
int msgOffset = readInt(data + offset + sz);
StrMap::iterator i = messages.find(name);
if (i == messages.end()) {
ScoredStr ss = { score, new Formatter(data, msgOffset) };
messages[name] = ss;
} else {
ScoredStr &ss = (*i).second;
if (ss.score <= score) {
ss.score = score;
ss.message = new Formatter(data, msgOffset);
}
}
}
offset += sz + 4;
}
}

67
messages.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef __MESSAGES_H__
#define __MESSAGES_H__
#include <map>
#include <string>
#include <stdarg.h>
class Resource;
class Formatter;
class Buffer;
/// Localized messages formatter
class Messages
{
private:
typedef struct {
int score;
Formatter *message;
} ScoredStr;
typedef std::map<std::wstring, ScoredStr> StrMap;
StrMap messages;
public:
/// Create empty messages table.
Messages();
~Messages();
public:
/// Load message tables from resources.
void load();
/// Get simple text string
/// \param key message key
std::wstring getMessage(const std::wstring &key) const;
/// Shorter alias for getMessage
/// \param key message key
std::wstring operator [](const std::wstring &key) const {
return getMessage(key);
};
/// Format message
/// \param key message key
std::wstring format(const wchar_t *key, ...) const;
/// Shorter alias for format
/// \param key message key
std::wstring operator ()(const wchar_t *key, ...) const;
/// Load messages from resource
/// \param res resource
void loadFromResource(Resource *res, Buffer *buffer);
private:
void loadBundle(int score, unsigned char *data, size_t size);
std::wstring format(const wchar_t *key, va_list ap) const;
};
extern Messages msg;
#endif

285
opensave.cpp Normal file
View File

@ -0,0 +1,285 @@
#include <time.h>
#include <fstream>
#include "exceptions.h"
#include "utils.h"
#include "widgets.h"
#include "storage.h"
#include "opensave.h"
#include "unicode.h"
#include "convert.h"
#include "messages.h"
#define MAX_SLOTS 10
class SavedGame
{
private:
std::wstring fileName;
bool exists;
std::wstring name;
public:
SavedGame(const std::wstring &fileName);
SavedGame(const SavedGame &s): fileName(s.fileName), name(s.name) {
exists = s.exists;
};
public:
const std::wstring& getFileName() { return fileName; };
std::wstring getName() { return exists ? name : msg(L"empty"); };
bool isExists() { return exists; };
};
SavedGame::SavedGame(const std::wstring &s): fileName(s)
{
exists = false;
try {
std::ifstream stream(toMbcs(fileName).c_str(), std::ifstream::in |
std::ifstream::binary);
if (stream.fail())
throw Exception(L"Can't open file");
name = readString(stream);
stream.close();
exists = true;
} catch (...) { }
}
class OkCommand: public Command
{
private:
Area &area;
bool *ok;
public:
OkCommand(Area &a, bool *o): area(a) { ok = o; };
virtual void doAction() {
*ok = true;
area.finishEventLoop();
};
};
class SaveCommand: public Command
{
private:
SavedGame &savedGame;
Area *parentArea;
bool *saved;
Font *font;
std::wstring defaultName;
Game *game;
public:
SaveCommand(SavedGame &sg, Font *f, Area *area, bool *s,
const std::wstring &dflt, Game *g): savedGame(sg), defaultName(dflt)
{
parentArea = area;
saved = s;
font = f;
game = g;
};
public:
virtual void doAction() {
Area area;
area.add(parentArea, false);
area.add(new Window(170, 280, 460, 100, L"blue.bmp"));
std::wstring name;
if (savedGame.isExists())
name = savedGame.getName();
else
name = defaultName;
area.add(new Label(font, 180, 300, 255,255,0, msg(L"enterGame")));
area.add(new InputField(340, 300, 280, 26, L"blue.bmp", name, 20,
255,255,0, font));
ExitCommand exitCmd(area);
OkCommand okCmd(area, saved);
area.add(new Button(310, 340, 80, 25, font, 255,255,0, L"blue.bmp",
msg(L"ok"), &okCmd));
area.add(new Button(400, 340, 80, 25, font, 255,255,0, L"blue.bmp",
msg(L"cancel"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
area.add(new KeyAccel(SDLK_RETURN, &okCmd));
area.run();
if (*saved) {
*saved = false;
try {
std::ofstream stream(toMbcs(savedGame.getFileName()).
c_str(), std::ofstream::out | std::ofstream::binary);
if (stream.fail())
throw Exception(L"Error creating save file");
writeString(stream, name);
game->save(stream);
if (stream.fail())
throw Exception(L"Error saving game");
stream.close();
*saved = true;
} catch (...) {
showMessageWindow(&area, L"redpattern.bmp", 300, 80, font,
255,255,255, msg(L"saveError"));
}
parentArea->finishEventLoop();
} else {
parentArea->updateMouse();
parentArea->draw();
}
};
};
static std::wstring getSavesPath()
{
#ifndef WIN32
std::wstring path(fromMbcs(getenv("HOME")) +
std::wstring(L"/.einstein/save"));
#else
std::wstring path(getStorage()->get(L"path", L""));
if (path.length() > 0)
path += L"\\";
path += L"save";
#endif
ensureDirExists(path);
return path;
}
typedef std::list<SavedGame> SavesList;
static void showListWindow(SavesList &list, Command **commands,
const std::wstring &title, Area &area, Font *font)
{
Font titleFont(L"nova.ttf", 26);
area.add(new Window(250, 90, 300, 420, L"blue.bmp"));
area.add(new Label(&titleFont, 250, 95, 300, 40, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, title));
ExitCommand exitCmd(area);
area.add(new Button(360, 470, 80, 25, font, 255,255,0, L"blue.bmp",
msg(L"close"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
int pos = 150;
int no = 0;
for (SavesList::iterator i = list.begin(); i != list.end(); i++) {
SavedGame &game = *i;
area.add(new Button(260, pos, 280, 25, font, 255,255,255, L"blue.bmp",
game.getName(), commands[no++]));
pos += 30;
}
area.run();
}
bool saveGame(Area *parentArea, Game *game)
{
std::wstring path = getSavesPath();
Area area;
area.add(parentArea, false);
Font font(L"laudcn2.ttf", 14);
bool saved = false;
SavesList list;
Command **commands = new Command*[MAX_SLOTS];
for (int i = 0; i < MAX_SLOTS; i++) {
SavedGame sg(path + L"/" + toString(i) + L".sav");
list.push_back(sg);
commands[i] = new SaveCommand(*(--(list.end())), &font,
&area, &saved, L"game " + toString(i+1), game);
}
showListWindow(list, commands, msg(L"saveGame"), area, &font);
for (int i = 0; i < MAX_SLOTS; i++)
delete commands[i];
delete[] commands;
return saved;
}
class LoadCommand: public Command
{
private:
SavedGame &savedGame;
Area *parentArea;
bool *saved;
Font *font;
std::wstring defaultName;
Game **game;
public:
LoadCommand(SavedGame &sg, Font *f, Area *area, Game **g):
savedGame(sg)
{
parentArea = area;
font = f;
game = g;
};
public:
virtual void doAction() {
try {
std::ifstream stream(toMbcs(savedGame.getFileName()).c_str(),
std::ifstream::in | std::ifstream::binary);
if (stream.fail())
throw Exception(L"Error opening save file");
readString(stream);
Game *g = new Game(stream);
if (stream.fail())
throw Exception(L"Error loading game");
stream.close();
*game = g;
} catch (...) {
showMessageWindow(parentArea, L"redpattern.bmp", 300, 80, font,
255,255,255, L"Error loadng game");
}
parentArea->finishEventLoop();
};
};
Game* loadGame(Area *parentArea)
{
std::wstring path = getSavesPath();
Area area;
area.add(parentArea, false);
Font font(L"laudcn2.ttf", 14);
Game *newGame = NULL;
SavesList list;
Command **commands = new Command*[MAX_SLOTS];
for (int i = 0; i < MAX_SLOTS; i++) {
SavedGame sg(path + L"/" + toString(i) + L".sav");
list.push_back(sg);
if (sg.isExists())
commands[i] = new LoadCommand(*(--(list.end())), &font, &area,
&newGame);
else
commands[i] = NULL;
}
showListWindow(list, commands, msg(L"loadGame"), area, &font);
for (int i = 0; i < MAX_SLOTS; i++)
delete commands[i];
delete[] commands;
return newGame;
}

16
opensave.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __OPENSAVE_H__
#define __OPENSAVE_H__
#include "widgets.h"
#include "game.h"
bool saveGame(Area *parentArea, Game *game);
Game* loadGame(Area *parentArea);
#endif

88
options.cpp Normal file
View File

@ -0,0 +1,88 @@
#include "options.h"
#include "storage.h"
#include "main.h"
#include "messages.h"
#include "sound.h"
class OptionsChangedCommand: public Command
{
private:
bool &fullscreen;
bool &niceCursor;
float &volume;
Area *area;
public:
OptionsChangedCommand(Area *a, bool &fs, bool &ns, float &v):
fullscreen(fs), niceCursor(ns), volume(v) {
area = a;
};
virtual void doAction() {
bool oldFullscreen = (getStorage()->get(L"fullscreen", 1) != 0);
bool oldCursor = (getStorage()->get(L"niceCursor", 1) != 0);
float oldVolume = (float)getStorage()->get(L"volume", 20) / 100.0f;
if (fullscreen != oldFullscreen) {
getStorage()->set(L"fullscreen", fullscreen);
screen.setMode(VideoMode(800, 600, 24, fullscreen));
}
#ifndef __APPLE__
if (niceCursor != oldCursor) {
getStorage()->set(L"niceCursor", niceCursor);
screen.setCursor(niceCursor);
}
#endif
if (volume != oldVolume) {
getStorage()->set(L"volume", (int)(volume * 100.0f));
sound->setVolume(volume);
}
getStorage()->flush();
area->finishEventLoop();
};
};
#define LABEL(y, s) \
area.add(new Label(&font, 300, y, 300, 20, Label::ALIGN_LEFT, \
Label::ALIGN_MIDDLE, 255,255,255, msg(s)));
#define CHECKBOX(y, var) \
area.add(new Checkbox(265, y, 20, 20, &font, 255,255,255, L"blue.bmp", \
var));
#define OPTION(y, s, var) LABEL(y, s) CHECKBOX(y, var)
void showOptionsWindow(Area *parentArea)
{
Font titleFont(L"nova.ttf", 26);
Font font(L"laudcn2.ttf", 14);
bool fullscreen = (getStorage()->get(L"fullscreen", 1) != 0);
bool niceCursor = (getStorage()->get(L"niceCursor", 1) != 0);
float volume = ((float)getStorage()->get(L"volume", 20)) / 100.0f;
Area area;
area.add(parentArea);
area.add(new Window(250, 170, 300, 260, L"blue.bmp"));
area.add(new Label(&titleFont, 250, 175, 300, 40, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, 255,255,0, msg(L"options")));
OPTION(260, L"fullscreen", fullscreen);
#ifndef __APPLE__
OPTION(280, L"niceCursor", niceCursor);
#endif
area.add(new Label(&font, 265, 330, 300, 20, Label::ALIGN_LEFT,
Label::ALIGN_MIDDLE, 255,255,255, msg(L"volume")));
area.add(new Slider(360, 332, 160, 16, volume));
ExitCommand exitCmd(area);
OptionsChangedCommand okCmd(&area, fullscreen, niceCursor, volume);
area.add(new Button(315, 390, 85, 25, &font, 255,255,0, L"blue.bmp",
msg(L"ok"), &okCmd));
area.add(new Button(405, 390, 85, 25, &font, 255,255,0, L"blue.bmp",
msg(L"cancel"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
area.add(new KeyAccel(SDLK_RETURN, &okCmd));
area.run();
}

13
options.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __OPTIONS_H__
#define __OPTIONS_H__
#include "widgets.h"
void showOptionsWindow(Area *area);
#endif

394
puzgen.cpp Normal file
View File

@ -0,0 +1,394 @@
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <list>
#include "puzgen.h"
#include "exceptions.h"
#include "utils.h"
Possibilities::Possibilities()
{
reset();
}
Possibilities::Possibilities(std::istream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
for (int element = 0; element < PUZZLE_SIZE; element++)
pos[col][row][element] = readInt(stream);
}
void Possibilities::reset()
{
for (int i = 0; i < PUZZLE_SIZE; i++)
for (int j = 0; j < PUZZLE_SIZE; j++)
for (int k = 0; k < PUZZLE_SIZE; k++)
pos[i][j][k] = k + 1;
}
void Possibilities::checkSingles(int row)
{
int cellsCnt[PUZZLE_SIZE]; // count of elements in cells
int elsCnt[PUZZLE_SIZE]; // total count of elements in row
int elements[PUZZLE_SIZE]; // one element of each cell
int elCells[PUZZLE_SIZE]; // one cell of each element
memset(cellsCnt, 0, sizeof(cellsCnt));
memset(elsCnt, 0, sizeof(elsCnt));
memset(elements, 0, sizeof(elements));
memset(elCells, 0, sizeof(elCells));
// check if there is only one element left in cell(col, row)
for (int col = 0; col < PUZZLE_SIZE; col++)
for (int i = 0; i < PUZZLE_SIZE; i++) {
if (pos[col][row][i]) {
elsCnt[i]++;
elCells[i] = col;
cellsCnt[col]++;
elements[col] = i + 1;
}
}
bool changed = false;
// check for cells with single element
for (int col = 0; col < PUZZLE_SIZE; col++) {
if ((cellsCnt[col] == 1) && (elsCnt[elements[col] - 1] != 1)) {
// there is only one element in cell but it used somewhere else
int e = elements[col] - 1;
for (int i = 0; i < PUZZLE_SIZE; i++)
if (i != col)
pos[i][row][e] = 0;
changed = true;
}
}
// check for single element without exclusive cell
for (int el = 0; el < PUZZLE_SIZE; el++)
if ((elsCnt[el] == 1) && (cellsCnt[elCells[el]] != 1)) {
int col = elCells[el];
for (int i = 0; i < PUZZLE_SIZE; i++)
if (i != el)
pos[col][row][i] = 0;
changed = true;
}
if (changed)
checkSingles(row);
}
void Possibilities::exclude(int col, int row, int element)
{
if (! pos[col][row][element - 1])
return;
pos[col][row][element - 1] = 0;
checkSingles(row);
}
void Possibilities::set(int col, int row, int element)
{
for (int i = 0; i < PUZZLE_SIZE; i++)
if ((i != element - 1))
pos[col][row][i] = 0;
else
pos[col][row][i] = element;
for (int j = 0; j < PUZZLE_SIZE; j++)
if (j != col)
pos[j][row][element - 1] = 0;
checkSingles(row);
}
bool Possibilities::isPossible(int col, int row, int element)
{
return pos[col][row][element - 1] == element;
}
bool Possibilities::isDefined(int col, int row)
{
int solvedCnt = 0, unsolvedCnt = 0;
for (int i = 0; i < PUZZLE_SIZE; i++)
if (! pos[col][row][i])
unsolvedCnt++;
else
solvedCnt++;
return ((unsolvedCnt == PUZZLE_SIZE-1) && (solvedCnt == 1));
}
int Possibilities::getDefined(int col, int row)
{
for (int i = 0; i < PUZZLE_SIZE; i++)
if (pos[col][row][i])
return i + 1;
return 0;
}
bool Possibilities::isSolved()
{
for (int i = 0; i < PUZZLE_SIZE; i++)
for (int j = 0; j < PUZZLE_SIZE; j++)
if (! isDefined(i, j))
return false;
return true;
}
bool Possibilities::isValid(SolvedPuzzle &puzzle)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
if (! isPossible(col, row, puzzle[row][col]))
return false;
return true;
}
int Possibilities::getPosition(int row, int element)
{
int cnt = 0;
int lastPos = -1;
for (int i = 0; i < PUZZLE_SIZE; i++)
if (pos[i][row][element - 1] == element) {
cnt++;
lastPos = i;
}
return cnt == 1 ? lastPos : -1;
}
void Possibilities::print()
{
for (int row = 0; row < PUZZLE_SIZE; row++) {
std::cout << (char)('A' + row) << " ";
for (int col = 0; col < PUZZLE_SIZE; col++) {
for (int i = 0; i < PUZZLE_SIZE; i++)
if (pos[col][row][i])
std::cout << pos[col][row][i];
else
std::cout << " ";
std::cout << " ";
}
std::cout << std::endl;
}
}
void Possibilities::makePossible(int col, int row, int element)
{
pos[col][row][element-1] = element;
}
void Possibilities::save(std::ostream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
for (int element = 0; element < PUZZLE_SIZE; element++)
writeInt(stream, pos[col][row][element]);
}
static void shuffle(short arr[PUZZLE_SIZE])
{
int a, b, c;
for (int i = 0; i < 30; i++) {
a = (int)(((double)PUZZLE_SIZE)*rand()/(RAND_MAX+1.0));
if ((a < 0) || (a >= PUZZLE_SIZE)) {
std::cerr << "Index error" << std::endl;
exit(1);
}
b = (int)(((double)PUZZLE_SIZE)*rand()/(RAND_MAX+1.0));
if ((b < 0) || (b >= PUZZLE_SIZE)) {
std::cerr << "Index error" << std::endl;
exit(1);
}
c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
}
static bool canSolve(SolvedPuzzle &puzzle, Rules &rules)
{
Possibilities pos;
bool changed = false;
do {
changed = false;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule *rule = *i;
if (rule->apply(pos)) {
changed = true;
if (! pos.isValid(puzzle)) {
std::cout << "after error:" << std::endl;
pos.print();
throw Exception(L"Invalid possibilities after rule " +
rule->getAsText());
}
}
}
} while (changed);
bool res = pos.isSolved();
return res;
}
static void removeRules(SolvedPuzzle &puzzle, Rules &rules)
{
bool possible;
do {
possible = false;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule *rule = *i;
Rules excludedRules = rules;
excludedRules.remove(rule);
if (canSolve(puzzle, excludedRules)) {
possible = true;
rules.remove(rule);
delete rule;
break;
}
}
} while (possible);
}
static void genRules(SolvedPuzzle &puzzle, Rules &rules)
{
bool rulesDone = false;
do {
Rule *rule = genRule(puzzle);
if (rule) {
std::wstring s = rule->getAsText();
for (std::list<Rule*>::iterator i = rules.begin();
i != rules.end(); i++)
if ((*i)->getAsText() == s) {
delete rule;
rule = NULL;
break;
}
if (rule) {
//printf("adding rule %s\n", rule->getAsText().c_str());
rules.push_back(rule);
rulesDone = canSolve(puzzle, rules);
}
}
} while (! rulesDone);
}
/*static void printPuzzle(SolvedPuzzle &puzzle)
{
for (int i = 0; i < PUZZLE_SIZE; i++) {
char prefix = 'A' + i;
for (int j = 0; j < PUZZLE_SIZE; j++) {
if (j)
std::cout << " ";
std::cout << prefix << puzzle[i][j];
}
std::cout << std::endl;
}
}
static void printRules(Rules &rules)
{
for (Rules::iterator i = rules.begin(); i != rules.end(); i++)
std::cout << (*i)->getAsText() << std::endl;;
}*/
void genPuzzle(SolvedPuzzle &puzzle, Rules &rules)
{
for (int i = 0; i < PUZZLE_SIZE; i++) {
for (int j = 0; j < PUZZLE_SIZE; j++)
puzzle[i][j] = j + 1;
shuffle(puzzle[i]);
}
genRules(puzzle, rules);
removeRules(puzzle, rules);
//printPuzzle(puzzle);
//printRules(rules);
}
void openInitial(Possibilities &possib, Rules &rules)
{
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule *r = *i;
if (r->applyOnStart())
r->apply(possib);
}
}
void getHintsQty(Rules &rules, int &vert, int &horiz)
{
vert = 0;
horiz = 0;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
Rule::ShowOptions so = (*i)->getShowOpts();
switch (so) {
case Rule::SHOW_VERT: vert++; break;
case Rule::SHOW_HORIZ: horiz++; break;
default: ;
}
}
}
void savePuzzle(SolvedPuzzle &puzzle, std::ostream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
writeInt(stream, puzzle[row][col]);
}
void loadPuzzle(SolvedPuzzle &puzzle, std::istream &stream)
{
for (int row = 0; row < PUZZLE_SIZE; row++)
for (int col = 0; col < PUZZLE_SIZE; col++)
puzzle[row][col] = readInt(stream);
}
Rule* getRule(Rules &rules, int no)
{
int j = 0;
for (Rules::iterator i = rules.begin(); i != rules.end(); i++) {
if (j == no)
return *i;
j++;
}
throw Exception(L"Rule is not found");
}
/*int main(int argc, char *argv[])
{
srand(time(NULL));
Rules rules;
Puzzle puzzle;
genPuzzle(puzzle, rules);
printPuzzle(puzzle);
printRules(rules);
return 0;
}*/

81
puzgen.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __PUZGEN_H__
#define __PUZGEN_H__
#include <string>
#include <list>
#include <iostream>
#include "iconset.h"
#define PUZZLE_SIZE 6
typedef short SolvedPuzzle[PUZZLE_SIZE][PUZZLE_SIZE];
class Possibilities
{
private:
short pos[PUZZLE_SIZE][PUZZLE_SIZE][PUZZLE_SIZE];
public:
Possibilities();
Possibilities(std::istream &stream);
public:
void exclude(int col, int row, int element);
void set(int col, int row, int element);
bool isPossible(int col, int row, int element);
bool isDefined(int col, int row);
int getDefined(int col, int row);
int getPosition(int row, int element);
bool isSolved();
void print();
bool isValid(SolvedPuzzle &puzzle);
void makePossible(int col, int row, int element);
void save(std::ostream &stream);
void reset();
void checkSingles(int row);
};
class Rule
{
public:
typedef enum {
SHOW_VERT,
SHOW_HORIZ,
SHOW_NOTHING
} ShowOptions;
public:
virtual ~Rule() { };
public:
virtual std::wstring getAsText() = 0;
virtual bool apply(Possibilities &pos) = 0;
virtual bool applyOnStart() { return false; };
virtual ShowOptions getShowOpts() { return SHOW_NOTHING; };
virtual void draw(int x, int y, IconSet &iconSet, bool highlight) = 0;
virtual void save(std::ostream &stream) = 0;
};
typedef std::list<Rule*> Rules;
void genPuzzle(SolvedPuzzle &puzzle, Rules &rules);
void openInitial(Possibilities &possib, Rules &rules);
Rule* genRule(SolvedPuzzle &puzzle);
void getHintsQty(Rules &rules, int &vert, int &horiz);
Rule* getRule(Rules &rules, int no);
void savePuzzle(SolvedPuzzle &puzzle, std::ostream &stream);
void loadPuzzle(SolvedPuzzle &puzzle, std::istream &stream);
void saveRules(Rules &rules, std::ostream &stream);
void loadRules(Rules &rules, std::istream &stream);
#endif

196
puzzle.cpp Normal file
View File

@ -0,0 +1,196 @@
#include "puzzle.h"
#include "main.h"
#include "utils.h"
#include "sound.h"
#define FIELD_OFFSET_X 12
#define FIELD_OFFSET_Y 68
#define FIELD_GAP_X 4
#define FIELD_GAP_Y 4
#define FIELD_TILE_WIDTH 48
#define FIELD_TILE_HEIGHT 48
Puzzle::Puzzle(IconSet &is, SolvedPuzzle &s, Possibilities *p):
iconSet(is), solved(s)
{
possib = p;
reset();
}
Puzzle::~Puzzle()
{
}
void Puzzle::reset()
{
valid = true;
win = false;
int x, y;
SDL_GetMouseState(&x, &y);
getCellNo(x, y, hCol, hRow, subHNo);
}
void Puzzle::draw()
{
for (int i = 0; i < PUZZLE_SIZE; i++)
for (int j = 0; j < PUZZLE_SIZE; j++)
drawCell(i, j, true);
}
void Puzzle::drawCell(int col, int row, bool addToUpdate)
{
int posX = FIELD_OFFSET_X + col * (FIELD_TILE_WIDTH + FIELD_GAP_X);
int posY = FIELD_OFFSET_Y + row * (FIELD_TILE_HEIGHT + FIELD_GAP_Y);
if (possib->isDefined(col, row)) {
int element = possib->getDefined(col, row);
if (element > 0)
screen.draw(posX, posY, iconSet.getLargeIcon(row, element,
(hCol == col) && (hRow == row)));
} else {
screen.draw(posX, posY, iconSet.getEmptyFieldIcon());
int x = posX;
int y = posY + (FIELD_TILE_HEIGHT / 6);
for (int i = 0; i < 6; i++) {
if (possib->isPossible(col, row, i + 1))
screen.draw(x, y, iconSet.getSmallIcon(row, i + 1,
(hCol == col) && (hRow == row) && (i + 1 == subHNo)));
if (i == 2) {
x = posX;
y += (FIELD_TILE_HEIGHT / 3);
} else
x += (FIELD_TILE_WIDTH / 3);
}
}
if (addToUpdate)
screen.addRegionToUpdate(posX, posY, FIELD_TILE_WIDTH,
FIELD_TILE_HEIGHT);
}
void Puzzle::drawRow(int row, bool addToUpdate)
{
for (int i = 0; i < PUZZLE_SIZE; i++)
drawCell(i, row, addToUpdate);
}
bool Puzzle::onMouseButtonDown(int button, int x, int y)
{
int col, row, element;
if (! getCellNo(x, y, col, row, element))
return false;
if (! possib->isDefined(col, row)) {
/*if (button == 3) {
for (int i = 1; i <= PUZZLE_SIZE; i++)
possib->makePossible(col, row, i);
drawCell(col, row);
}
} else {*/
if (element == -1)
return false;
if (button == 1) {
if (possib->isPossible(col, row, element)) {
possib->set(col, row, element);
sound->play(L"laser.wav");
}
} else if (button == 3) {
if (possib->isPossible(col, row, element)) {
possib->exclude(col, row, element);
sound->play(L"whizz.wav");
}
/*else
possib->makePossible(col, row, element);*/
}
drawRow(row);
}
bool valid = possib->isValid(solved);
if (! valid)
onFail();
else
if (possib->isSolved() && valid)
onVictory();
return true;
}
void Puzzle::onFail()
{
if (failCommand)
failCommand->doAction();
}
void Puzzle::onVictory()
{
if (winCommand)
winCommand->doAction();
}
bool Puzzle::getCellNo(int x, int y, int &col, int &row, int &subNo)
{
col = row = subNo = -1;
if (! isInRect(x, y, FIELD_OFFSET_X, FIELD_OFFSET_Y,
(FIELD_TILE_WIDTH + FIELD_GAP_X) * PUZZLE_SIZE,
(FIELD_TILE_HEIGHT + FIELD_GAP_Y) * PUZZLE_SIZE))
return false;
x = x - FIELD_OFFSET_X;
y = y - FIELD_OFFSET_Y;
col = x / (FIELD_TILE_WIDTH + FIELD_GAP_X);
if (col * (FIELD_TILE_WIDTH + FIELD_GAP_X) + FIELD_TILE_WIDTH < x)
return false;
row = y / (FIELD_TILE_HEIGHT + FIELD_GAP_Y);
if (row * (FIELD_TILE_HEIGHT + FIELD_GAP_Y) + FIELD_TILE_HEIGHT < y)
return false;
x = x - col * (FIELD_TILE_WIDTH + FIELD_GAP_X);
y = y - row * (FIELD_TILE_HEIGHT + FIELD_GAP_Y)
- FIELD_TILE_HEIGHT / 6;
if ((y < 0) || (y >= (FIELD_TILE_HEIGHT / 3) * 2))
return true;
int cCol = x / (FIELD_TILE_WIDTH / 3);
if (cCol >= 3) {
col = row = -1;
return false;
}
int cRow = y / (FIELD_TILE_HEIGHT / 3);
subNo = cRow * 3 + cCol + 1;
return true;
}
bool Puzzle::onMouseMove(int x, int y)
{
int oldCol = hCol;
int oldRow = hRow;
int oldElement = subHNo;
getCellNo(x, y, hCol, hRow, subHNo);
if ((hCol != oldCol) || (hRow != oldRow) || (subHNo != oldElement)) {
if ((oldCol != -1) && (oldRow != -1))
drawCell(oldCol, oldRow);
if ((hCol != -1) && (hRow != -1))
drawCell(hCol, hRow);
}
return false;
}
void Puzzle::setCommands(Command *win, Command *fail)
{
winCommand = win;
failCommand = fail;
}

44
puzzle.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef __PUZZLE_H__
#define __PUZZLE_H__
#include "iconset.h"
#include "puzgen.h"
#include "widgets.h"
class Puzzle: public Widget
{
private:
Possibilities *possib;
IconSet &iconSet;
bool valid;
bool win;
SolvedPuzzle &solved;
int hCol, hRow;
int subHNo;
Command *winCommand, *failCommand;
public:
Puzzle(IconSet &is, SolvedPuzzle &solved, Possibilities *possib);
virtual ~Puzzle();
public:
virtual void draw();
void drawRow(int row, bool addToUpdate=true);
void drawCell(int col, int row, bool addToUpdate=true);
Possibilities* getPossibilities() { return possib; };
virtual bool onMouseButtonDown(int button, int x, int y);
bool isValid() const { return valid; };
bool victory() const { return win; };
void onFail();
void onVictory();
bool getCellNo(int x, int y, int &col, int &row, int &subNo);
virtual bool onMouseMove(int x, int y);
void setCommands(Command *winCommand, Command *failCommand);
void reset();
};
#endif

159
random.cpp Normal file
View File

@ -0,0 +1,159 @@
#include "random.h"
#include "utils.h"
/* Period parameters */
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
Random::Random()
{
mti = RAND_N+1; /* mti==RAND_N+1 means mt[RAND_N] is not initialized */
struct timeval tv;
gettimeofday(&tv);
unsigned long long int s = tv.tv_sec * 1000000 + tv.tv_usec;
initLong(s);
}
Random::Random(unsigned long int seed)
{
mti = RAND_N+1; /* mti==RAND_N+1 means mt[RAND_N] is not initialized */
initLong(seed);
}
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
Random::Random(int init_key[], int key_length)
{
mti = RAND_N+1; /* mti==RAND_N+1 means mt[RAND_N] is not initialized */
int i, j, k;
initLong(19650218UL);
i=1; j=0;
k = (RAND_N>key_length ? RAND_N : key_length);
for (; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+ init_key[j] + j; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++; j++;
if (i>=RAND_N) { mt[0] = mt[RAND_N-1]; i=1; }
if (j>=key_length) j=0;
}
for (k=RAND_N-1; k; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
- i; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
if (i>=RAND_N) { mt[0] = mt[RAND_N-1]; i=1; }
}
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
}
Random::~Random()
{
}
/* initializes mt[RAND_N] with a seed */
void Random::initLong(unsigned long s)
{
mt[0]= s & 0xffffffffUL;
for (mti=1; mti<RAND_N; mti++) {
mt[mti] =
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
mt[mti] &= 0xffffffffUL;
/* for >32 bit machines */
}
}
/* generates a random number on [0,0xffffffff]-interval */
unsigned long Random::genInt32(void)
{
unsigned long y;
static unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= RAND_N) { /* generate N words at one time */
int kk;
if (mti == RAND_N+1) /* if init_genrand() has not been called, */
initLong(5489UL); /* a default initial seed is used */
for (kk=0;kk<RAND_N-M;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for (;kk<RAND_N-1;kk++) {
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
mt[kk] = mt[kk+(M-RAND_N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[RAND_N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
mt[RAND_N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
/* generates a random number on [0,0x7fffffff]-interval */
long Random::genInt31()
{
return (long)(genInt32()>>1);
}
/* generates a random number on [0,1]-real-interval */
double Random::genReal1(void)
{
return genInt32()*(1.0/4294967295.0);
/* divided by 2^32-1 */
}
/* generates a random number on [0,1)-real-interval */
double Random::genReal2()
{
return genInt32()*(1.0/4294967296.0);
/* divided by 2^32 */
}
/* generates a random number on (0,1)-real-interval */
double Random::genReal3(void)
{
return (((double)genInt32()) + 0.5)*(1.0/4294967296.0);
/* divided by 2^32 */
}
/* generates a random number on [0,1) with 53-bit resolution*/
double Random::genReal53(void)
{
unsigned long a=genInt32()>>5, b=genInt32()>>6;
return(a*67108864.0+b)*(1.0/9007199254740992.0);
}
/* generate integer random number on [0, range) int interval */
int Random::genInt(int range)
{
int v = (int)(genReal2() * range);
if (v == range) printf("ooops!\n");
return v;
}

41
random.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __RANDOM_H__
#define __RANDOM_H__
class Random
{
private:
#define RAND_N 624
unsigned long mt[RAND_N]; /* the array for the state vector */
int mti; /* mti==RAND_N+1 means mt[RAND_N] is not initialized */
public:
Random();
Random(unsigned long int seed);
Random(int keys[], int length);
~Random();
public:
/* generates a random number on [0,0xffffffff]-interval */
unsigned long int genInt32();
/* generates a random number on [0,0x7fffffff]-interval */
long int genInt31();
/* generates a random number on [0,1]-real-interval */
double genReal1();
/* generates a random number on [0,1)-real-interval */
double genReal2();
/* generates a random number on (0,1)-real-interval */
double genReal3();
/* generates a random number on [0,1) with 53-bit resolution*/
double genReal53();
/* generate integer random number on [0, range) int interval */
int genInt(int range);
private:
void initLong(unsigned long int s);
};
#endif

131
regstorage.cpp Normal file
View File

@ -0,0 +1,131 @@
#ifdef WIN32 // Win32 only
#include "regstorage.h"
#include "unicode.h"
RegistryStorage::RegistryStorage()
{
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SOFTWARE\\Flowix Games\\Einstein\\2.0",
0, KEY_READ, &globalKey))
globalKey = NULL;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
"SOFTWARE\\Flowix Games\\Einstein\\2.0",
0, KEY_READ | KEY_WRITE, &userKey))
{
if (RegCreateKeyEx(HKEY_CURRENT_USER,
"SOFTWARE\\Flowix Games\\Einstein\\2.0", 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
NULL, &userKey, NULL))
userKey = NULL;
}
}
RegistryStorage::~RegistryStorage()
{
if (globalKey)
RegCloseKey(globalKey);
if (userKey)
RegCloseKey(userKey);
}
int RegistryStorage::get(const std::wstring &name, int dflt)
{
std::string uname(toUtf8(name));
if (globalKey) {
DWORD data;
DWORD size = sizeof(data);
DWORD type;
if (! RegQueryValueEx(globalKey, uname.c_str(), NULL, &type,
(BYTE*)&data, &size))
if (type == REG_DWORD)
return data;
}
if (userKey) {
DWORD data;
DWORD size = sizeof(data);
DWORD type;
if (! RegQueryValueEx(userKey, uname.c_str(), NULL, &type,
(BYTE*)&data, &size))
if (type == REG_DWORD)
return data;
}
return dflt;
}
std::wstring RegistryStorage::get(const std::wstring &name, const std::wstring &dflt)
{
std::string uname(toUtf8(name));
if (globalKey) {
DWORD size = 0;
DWORD type;
if (! RegQueryValueEx(globalKey, uname.c_str(), NULL, &type,
NULL, &size))
{
if ((type == REG_SZ) && (size > 0)) {
char *data = new char[size + 1];
if (! RegQueryValueEx(globalKey, uname.c_str(), NULL, &type,
(BYTE*)data, &size))
{
std::wstring s(fromUtf8(data));
delete[] data;
return s;
}
delete[] data;
} else
return L"";
}
}
if (userKey) {
DWORD size = 0;
DWORD type;
if (! RegQueryValueEx(userKey, uname.c_str(), NULL, &type,
NULL, &size))
{
if ((type == REG_SZ) && (size > 0)) {
char *data = new char[size];
if (! RegQueryValueEx(userKey, uname.c_str(), NULL, &type,
(BYTE*)data, &size))
{
std::wstring s(fromUtf8(data));
delete[] data;
return s;
}
delete[] data;
} else
return L"";
}
}
return dflt;
}
void RegistryStorage::set(const std::wstring &name, int value)
{
std::string uname(toUtf8(name));
if (userKey) {
DWORD data = value;
RegSetValueEx(userKey, uname.c_str(), 0, REG_DWORD,
(BYTE*)&data, sizeof(data));
}
}
void RegistryStorage::set(const std::wstring &name, const std::wstring &value)
{
std::string uname(toUtf8(name));
std::string uval(toUtf8(value));
if (userKey)
RegSetValueEx(userKey, uname.c_str(), 0, REG_SZ,
(BYTE*)uval.c_str(), (uval.length() + 1) * sizeof(char));
}
#endif

32
regstorage.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __REGSTORAGE_H__
#define __REGSTORAGE_H__
#ifdef WIN32 // Win32 only
#include <windows.h>
#include "storage.h"
class RegistryStorage: public Storage
{
private:
HKEY globalKey;
HKEY userKey;
public:
RegistryStorage();
virtual ~RegistryStorage();
public:
virtual int get(const std::wstring &name, int dflt) ;
virtual std::wstring get(const std::wstring &name,
const std::wstring &dflt);
virtual void set(const std::wstring &name, int value);
virtual void set(const std::wstring &name, const std::wstring &value);
virtual void flush() { };
};
#endif
#endif

674
resources.cpp Normal file
View File

@ -0,0 +1,674 @@
#include <algorithm>
#include <sys/types.h>
#include <dirent.h>
#include <zlib.h>
#include "resources.h"
#include "exceptions.h"
#include "unicode.h"
#include "convert.h"
#include "i18n.h"
#include "utils.h"
ResourcesCollection *resources = NULL;
///////////////////////////////////////////////////////////////////
//
// UnpackedResourceStream
//
///////////////////////////////////////////////////////////////////
class UnpackedResourceStream: public ResourceStream
{
private:
std::ifstream &stream;
size_t size;
long offset;
long pos;
public:
UnpackedResourceStream(std::ifstream &stream, long offset,
size_t size);
public:
virtual size_t getSize() { return size; };
virtual void seek(long offset);
virtual void read(char *buffer, size_t size);
virtual long getPos() { return pos; };
};
UnpackedResourceStream::UnpackedResourceStream(std::ifstream &s,
long off, size_t sz): stream(s)
{
offset = off;
size = sz;
pos = 0;
}
void UnpackedResourceStream::seek(long off)
{
if ((off < 0) || ((size_t)off > size))
throw Exception(L"Invalid seek in ResourceStream");
pos = off;
}
void UnpackedResourceStream::read(char *buffer, size_t sz)
{
if (! buffer)
throw Exception(L"Invalid buffer in ResourceStream");
if (sz + pos > size)
throw Exception(L"Attempt of reading after resource end");
stream.seekg(offset + pos, std::ios::beg);
stream.read(buffer, sz);
pos += sz;
}
///////////////////////////////////////////////////////////////////
//
// MemoryResourceStream
//
///////////////////////////////////////////////////////////////////
class MemoryResourceStream: public ResourceStream
{
private:
char *data;
size_t size;
ResVariant *resource;
long pos;
public:
MemoryResourceStream(ResVariant *resource);
virtual ~MemoryResourceStream();
public:
virtual size_t getSize() { return size; };
virtual void seek(long offset);
virtual void read(char *buffer, size_t size);
virtual long getPos() { return pos; };
};
MemoryResourceStream::MemoryResourceStream(ResVariant *res)
{
resource = res;
data = (char*)res->getRef(size);
pos = 0;
}
MemoryResourceStream::~MemoryResourceStream()
{
resource->delRef(data);
}
void MemoryResourceStream::seek(long off)
{
if ((off < 0) || ((size_t)off > size))
throw Exception(L"Invalid seek in ResourceStream");
pos = off;
}
void MemoryResourceStream::read(char *buffer, size_t sz)
{
if (! buffer)
throw Exception(L"Invalid buffer in ResourceStream");
if (sz + pos > size)
throw Exception(L"Attempt of reading after resource end");
memcpy(buffer, data, sz);
pos += sz;
}
///////////////////////////////////////////////////////////////////
//
// ResourceFile
//
///////////////////////////////////////////////////////////////////
ResourceFile::ResourceFile(const std::wstring &fileName, Buffer *buf):
name(fileName)
{
if (buf) {
buffer = buf;
ownBuffer = false;
} else {
buffer = new Buffer();
ownBuffer = true;
}
stream.open(toMbcs(fileName).c_str(), std::ios::in | std::ios::binary);
if (stream.fail())
throw Exception(L"Error loading resource file '" + name + L"'");
char sign[4];
stream.read(sign, 4);
int readed = stream.gcount();
if (stream.fail() || (readed != 4) || (sign[0] != 'C') ||
(sign[1] != 'R') || (sign[2] != 'F') || sign[3])
throw Exception(L"Invalid resource file '" + name + L"'");
int major = readInt(stream);
readed = stream.gcount();
int minor = readInt(stream);
readed += stream.gcount();
priority = readInt(stream);
readed += stream.gcount();
if (stream.fail() || (readed != 12) || (major != 2) || (minor < 0))
throw Exception(L"Incompatible version of resource file '" +
name + L"'");
}
ResourceFile::~ResourceFile()
{
stream.close();
if (ownBuffer)
delete buffer;
}
void ResourceFile::getDirectory(Directory &directory)
{
stream.seekg(-8, std::ios::end);
if (stream.fail())
throw Exception(L"Error reading " + name + L" directory");
int start = readInt(stream);
int count = readInt(stream);
stream.seekg(start, std::ios::beg);
if (stream.fail())
throw Exception(L"Error reading " + name + L" directory");
for (int i = 0; i < count; i++) {
DirectoryEntry entry;
entry.name = readString(stream);
entry.unpackedSize = readInt(stream);
entry.offset = readInt(stream);
entry.packedSize = readInt(stream);
entry.level = readInt(stream);
entry.group = readString(stream);
directory.push_back(entry);
}
if (stream.fail())
throw Exception(L"Error reading " + name + L" directory");
}
void ResourceFile::unpack(char *in, int inSize, char *out, int outSize)
{
z_stream zs;
memset(&zs, 0, sizeof(z_stream));
zs.next_in = (Bytef*)in;
zs.avail_in = inSize;
zs.next_out = (Bytef*)out;
zs.avail_out = outSize;
if (inflateInit(&zs) != Z_OK)
throw Exception(name + L": Error initializing inflate stream.");
if (inflate(&zs, Z_FINISH) != Z_STREAM_END)
throw Exception(name + L": Error decompresing element.");
if (inflateEnd(&zs) != Z_OK)
throw Exception(name + L": Error finishing decompresing.");
}
void ResourceFile::load(char *buf, long offset, long packedSize,
long unpackedSize, int level)
{
char *inBuf=NULL;
try {
if (! level) {
stream.seekg(offset, std::ios::beg);
stream.read(buf, unpackedSize);
return;
}
buffer->setSize(packedSize);
stream.seekg(offset, std::ios::beg);
stream.read((char*)buffer->getData(), packedSize);
unpack((char*)buffer->getData(), packedSize, buf, unpackedSize);
} catch (Exception &e) {
if (inBuf) free(inBuf);
throw e;
} catch (...) {
if (inBuf) free(inBuf);
throw Exception(name + L": Error loading resource");
}
}
void* ResourceFile::load(long offset, long packedSize, long unpackedSize,
int level)
{
char *outBuf=NULL;
try {
outBuf = (char*)malloc(unpackedSize);
if (! outBuf)
throw Exception(name + L": Error allocating memory");
load(outBuf, offset, packedSize, unpackedSize, level);
} catch (Exception &e) {
if (outBuf) free(outBuf);
throw e;
} catch (...) {
if (outBuf) free(outBuf);
throw Exception(name + L": Error loading resource");
}
return outBuf;
}
///////////////////////////////////////////////////////////////////
//
// SimpleResourceFile
//
///////////////////////////////////////////////////////////////////
SimpleResourceFile::SimpleResourceFile(const std::wstring &fileName,
Buffer *buf): ResourceFile(fileName, buf)
{
Directory entries;
getDirectory(entries);
for (Directory::iterator i = entries.begin(); i != entries.end(); i++) {
DirectoryEntry &e = *i;
directory[e.name] = e;
}
}
void* SimpleResourceFile::load(const std::wstring &name, int &size)
{
DirectoryMap::iterator i = directory.find(name);
if (i != directory.end()) {
DirectoryEntry &e = (*i).second;
size = e.unpackedSize;
return ResourceFile::load(e.offset, e.packedSize, e.unpackedSize,
e.level);
} else
throw Exception(L"Resource '" + name + L"' not found");
}
void SimpleResourceFile::load(const std::wstring &name, Buffer &outBuf)
{
DirectoryMap::iterator i = directory.find(name);
if (i != directory.end()) {
DirectoryEntry &e = (*i).second;
outBuf.setSize(e.unpackedSize);
ResourceFile::load((char*)outBuf.getData(), e.offset,
e.packedSize, e.unpackedSize, e.level);
} else
throw Exception(L"Resource '" + name + L"' not found");
}
///////////////////////////////////////////////////////////////////
//
// ResVariant
//
///////////////////////////////////////////////////////////////////
ResVariant::ResVariant(ResourceFile *f, int score,
const ResourceFile::DirectoryEntry &e)
{
file = f;
i18nScore = score;
offset = e.offset;
unpackedSize = e.unpackedSize;
packedSize = e.packedSize;
level = e.level;
refCnt = 0;
data = NULL;
}
ResVariant::~ResVariant()
{
if (data)
free((char*)data - sizeof(ResVariant*));
}
void* ResVariant::getRef()
{
if (! refCnt) {
char* d = (char*)malloc(unpackedSize + sizeof(void*));
if (! d)
throw Exception(L"ResVariant::getRef memory allocation error");
ResVariant *self = this;
file->load(d + sizeof(self), offset, packedSize, unpackedSize,
level);
memcpy(d, &self, sizeof(self));
data = d + sizeof(self);
}
refCnt++;
return data;
}
void* ResVariant::getRef(size_t &sz)
{
sz = unpackedSize;
return getRef();
}
void ResVariant::delRef(void *dta)
{
if ((! refCnt) || (dta != data))
throw Exception(L"Invalid ResVariant::delRef call");
refCnt--;
if (! refCnt) {
free((char*)data - sizeof(ResVariant*));
data = NULL;
}
}
void* ResVariant::getDynData()
{
if (! refCnt)
return file->load(offset, packedSize, unpackedSize, level);
else {
char* d = (char*)malloc(unpackedSize);
if (! d)
throw Exception(L"ResVariant::getDynData memory allocation error");
memcpy(d, data, unpackedSize);
return data;
}
}
void ResVariant::getData(Buffer &buffer)
{
buffer.setSize(unpackedSize);
if (! refCnt)
file->load((char*)buffer.getData(), offset, packedSize,
unpackedSize, level);
else
memcpy((char*)buffer.getData(), data, unpackedSize);
}
ResourceStream* ResVariant::createStream()
{
if (refCnt || level)
return new MemoryResourceStream(this);
else
return new UnpackedResourceStream(file->getStream(), offset,
packedSize);
}
///////////////////////////////////////////////////////////////////
//
// Resource
//
///////////////////////////////////////////////////////////////////
Resource::Resource(ResourceFile *file, int i18nScore,
const ResourceFile::DirectoryEntry &entry, const std::wstring &n):
name(n)
{
addVariant(file, i18nScore, entry);
}
Resource::~Resource()
{
for (Variants::iterator i = variants.begin(); i != variants.end(); i++)
delete *i;
}
class ScorePredicate
{
public:
int score;
ScorePredicate(int sc) { score = sc; }
bool operator() (const ResVariant *r) const {
return r->getI18nScore() == score;
};
};
class ResVariantMoreThen
{
public:
bool operator() (const ResVariant *v1, const ResVariant *v2) const {
return v1->getI18nScore() > v2->getI18nScore();
};
};
void Resource::addVariant(ResourceFile *file, int i18nScore,
const ResourceFile::DirectoryEntry &entry)
{
if (! variants.size()) {
variants.push_back(new ResVariant(file, i18nScore, entry));
return;
}
ScorePredicate p(i18nScore);
Variants::iterator i = std::find_if(variants.begin(), variants.end(), p);
if (i != variants.end()) {
delete *i;
*i = new ResVariant(file, i18nScore, entry);
} else {
variants.push_back(new ResVariant(file, i18nScore, entry));
ResVariantMoreThen comparator;
std::sort(variants.begin(), variants.end(), comparator);
}
}
void* Resource::getRef(int variant)
{
return variants[variant]->getRef();
}
void* Resource::getRef(int *size, int variant)
{
if (size)
*size = variants[variant]->getSize();
return variants[variant]->getRef();
}
void Resource::delRef(void *data)
{
ResVariant *v = *(ResVariant**)((char*)data - sizeof(ResVariant*));
v->delRef(data);
}
void Resource::getData(Buffer &buffer, int variant)
{
variants[variant]->getData(buffer);
}
ResourceStream* Resource::createStream(int variant)
{
return variants[variant]->createStream();
}
///////////////////////////////////////////////////////////////////
//
// ResourcesCollection
//
///////////////////////////////////////////////////////////////////
class ResFileMoreThen
{
public:
bool operator() (const ResourceFile *f1, const ResourceFile *f2) const {
return f1->getPriority() > f2->getPriority();
};
};
ResourcesCollection::ResourcesCollection(StringList &directories)
{
loadResourceFiles(directories);
ResFileMoreThen comparator;
std::sort(files.begin(), files.end(), comparator);
processFiles();
}
ResourcesCollection::~ResourcesCollection()
{
for (ResourcesMap::iterator i = resources.begin(); i != resources.end(); i++)
delete (*i).second;
for (ResourceFiles::iterator i = files.begin(); i != files.end(); i++)
delete *i;
}
void ResourcesCollection::loadResourceFiles(StringList &directories)
{
for (StringList::iterator i = directories.begin();
i != directories.end(); i++)
{
const std::wstring &d = *i;
DIR *dir = opendir(toMbcs(d).c_str());
if (dir) {
struct dirent *de;
while ((de = readdir(dir)))
if (de->d_name[0] != '.') {
std::wstring s(fromMbcs(de->d_name));
if ((s.length() > 4) &&
(toLowerCase(s.substr(s.length() - 4)) == L".res"))
files.push_back(new ResourceFile(d + L"/" + s, &buffer));
}
closedir(dir);
}
}
}
void ResourcesCollection::processFiles()
{
ResourceFile::Directory dir;
for (std::vector<ResourceFile*>::iterator i = files.begin();
i != files.end(); i++)
{
ResourceFile *file = *i;
file->getDirectory(dir);
for (ResourceFile::Directory::iterator j = dir.begin();
j != dir.end(); j++)
{
ResourceFile::DirectoryEntry &de = *j;
std::wstring name, ext, language, country;
splitFileName(de.name, name, ext, language, country);
int score = getScore(language, country, locale);
if (score > 0) {
std::wstring resName = name + L"." + ext;
Resource *res = resources[resName];
if (! res) {
res = new Resource(file, score, de, resName);
resources[resName] = res;
if (de.group.length())
groups[de.group].push_back(res);
} else
res->addVariant(file, score, de);
}
}
dir.clear();
}
}
Resource* ResourcesCollection::getResource(const std::wstring &name)
{
Resource *r = resources[name];
if (! r)
throw Exception(L"Resource '" + name + L"' not found");
return r;
}
void* ResourcesCollection::getRef(const std::wstring &name, int &size)
{
Resource *r = getResource(name);
ResVariant *v = r->getVariant(0);
size = v->getSize();
return v->getRef();
}
void* ResourcesCollection::getRef(const std::wstring &name)
{
Resource *r = getResource(name);
ResVariant *v = r->getVariant(0);
return v->getRef();
}
ResourceStream* ResourcesCollection::createStream(const std::wstring &name)
{
Resource *r = getResource(name);
return r->createStream();
}
void ResourcesCollection::delRef(void *data)
{
ResVariant *v = *(ResVariant**)((char*)data - sizeof(ResVariant*));
v->delRef(data);
}
void ResourcesCollection::forEachInGroup(const std::wstring &name,
Visitor<Resource*> &visitor)
{
if (groups.count(name) > 0) {
ResourcesList &l = groups[name];
for (ResourcesList::iterator i = l.begin(); i != l.end(); i++) {
Resource *r = *i;
visitor.onVisit(r);
}
}
}
void ResourcesCollection::loadData(const std::wstring &name, Buffer &buffer)
{
Resource *r = getResource(name);
r->getData(buffer);
}
///////////////////////////////////////////////////////////////////
//
// ResDataHolder
//
///////////////////////////////////////////////////////////////////
ResDataHolder::ResDataHolder()
{
data = NULL;
size = 0;
}
ResDataHolder::ResDataHolder(const std::wstring &name)
{
load(name);
}
ResDataHolder::~ResDataHolder()
{
if (data)
resources->delRef(data);
}
void ResDataHolder::load(const std::wstring &name)
{
int s;
data = resources->getRef(name, s);
size = (size_t)s;
}

413
resources.h Normal file
View File

@ -0,0 +1,413 @@
#ifndef __RESOURCES_H__
#define __RESOURCES_H__
/** \file resources.h
* Classes for resources loading and unloading.
*/
#include <string>
#include <fstream>
#include <list>
#include <map>
#include <vector>
#include "visitor.h"
#include "buffer.h"
typedef std::list<std::wstring> StringList;
class ResourceFile;
/// Abstract interface for streamed resource access
class ResourceStream
{
public:
virtual ~ResourceStream() { };
/// Get size of resource
virtual size_t getSize() = 0;
/// Seek into resource.
/// \param offset offset from resource start
virtual void seek(long offset) = 0;
/// Read data from resource into buffer.
/// \param buffer buffer of size bytes.
/// \param size number of bytes to read.
virtual void read(char *buffer, size_t size) = 0;
/// Get current position
virtual long getPos() = 0;
/// Return true if end of resource reached
virtual bool isEof() { return (size_t)getPos() >= getSize(); };
/// Get count of bytes left
virtual long getAvailable() { return (long)getSize() - getPos(); };
};
/***
* Low level resource file interface.
* Never use it, use ResourcesCollection instead.
***/
class ResourceFile
{
private:
std::ifstream stream; /// resource file input stream
int priority; /// priority of resource file
std::wstring name; /// resource file name
Buffer *buffer;
bool ownBuffer;
public:
/// Resource file directory entry
typedef struct {
std::wstring name; /// resource name
long offset; /// offset from start of resource file
long packedSize; /// compressed size
long unpackedSize; /// uncompressed size
std::wstring group; /// group name
int level; /// pack level
} DirectoryEntry;
/// List of directory entries.
typedef std::list<DirectoryEntry> Directory;
public:
/// Create resource file. Throws exception if file can't be opened.
/// \param fileName the name of resource file.
/// \param buffer buffer for temporary data.
/// Can be shared with other resource files.
ResourceFile(const std::wstring &fileName, Buffer *buffer=NULL);
virtual ~ResourceFile();
public:
/// Load directory listing.
/// \param directory list where resource entries will be placed.
void getDirectory(Directory &directory);
/// Load data in preallocated buffer buf of size unpackedSize.
/// \param buf buffer of unpackedSize bytes where unpacked data will
/// be placed
/// \param offset offset from start of resource file to packed data
/// \param packedSize size of packed resource
/// \param unpackedSize size of unpacked resource
void load(char *buf, long offset, long packedSize,
long unpackedSize, int level);
/// Allocate buffer and load data. Memory returned by this
/// method must be freed by free() function call.
/// \param offset offset from start of resource file to packed data
/// \param packedSize size of packed resource
/// \param unpackedSize size of unpacked resource
void* load(long offset, long packedSize, long unpackedSize,
int level);
/// Get priority of this resource file.
int getPriority() const { return priority; };
/// Get the name of resource file.
const std::wstring& getFileName() const { return name; };
/// Get file stream.
std::ifstream& getStream() { return stream; };
private:
/// Unpack memory buffer
/// \param in buffer of inSize bytes filled with packed data
/// \param inSize size of packed data
/// \param out buffer of outSize bytes where unpacked data will
/// be placed
/// \param outSize size of unpacked data
void unpack(char *in, int inSize, char *out, int outSize);
};
/// Simple resource file wrapper.
/// Used at boot time when ResourcesCollection
/// is not available yet.
class SimpleResourceFile: public ResourceFile
{
private:
typedef std::map<std::wstring, DirectoryEntry> DirectoryMap;
DirectoryMap directory; /// Directory map.
public:
/// Open resource file. Throws exception if file can't be opened.
/// \param fileName the name of resource file.
/// \param buffer buffer for temporary data.
/// Can be shared with other resource files.
SimpleResourceFile(const std::wstring &fileName, Buffer *buffer=NULL);
public:
/// Load data. Memory returned by this method should be freed
/// by free() function call.
/// \param name name of resource
/// \param size returns size of resource
virtual void* load(const std::wstring &name, int &size);
/// Load data into the buffer.
/// \param name name of resource
/// \param buffer buffer for resource data
virtual void load(const std::wstring &name, Buffer &buffer);
};
/// Internationalized resource entity.
class ResVariant
{
private:
int i18nScore;
ResourceFile *file;
long offset;
long unpackedSize;
long packedSize;
int refCnt;
void *data;
int level;
public:
/// Create resource variation.
/// \param file reesource file
/// \param score locale compability score
/// \param entry entry in global resources directory
ResVariant(ResourceFile *file, int score,
const ResourceFile::DirectoryEntry &entry);
~ResVariant();
public:
/// Return locale compability score.
int getI18nScore() const { return i18nScore; };
/// Get pointer to unpacked resource data.
/// Must be freed after use this delRef()
void* getRef();
/// Get pointer to unpacked resource data and return resource size.
/// Must be freed after use this delRef().
/// \param size returned size of resource data.
void* getRef(size_t &size);
/// Delete referene to data.
/// \param data pointer to unpacked data.
void delRef(void *data);
/// Return reference counter.
int getRefCount() const { return refCnt; };
/// Is data managed by this object
/// \param data pointer to dataa
bool isDataOwned(void *data) const { return refCnt && data == this->data; };
/// return data.
/// destroy it after use with free()
void* getDynData();
/// returns size of data
long getSize() const { return unpackedSize; };
/// Return data in buffer
/// \param buffer buffer to store data.
void getData(Buffer &buffer);
/// Create ResourceStream for resource.
/// This may be usefull for large streams unpacked data,
/// for example video and sound.
/// Delete stream after use.
ResourceStream* createStream();
};
/// Internationalized resources.
/// Collection of localized data (ResVariant) with single name.
class Resource
{
private:
typedef std::vector<ResVariant*> Variants;
Variants variants;
std::wstring name;
public:
/// Create resource and add first entry
/// \param file resource file
/// \param i18nScore locale compability score
/// \param entry resource entry in global directory
/// \param name name of resource
Resource(ResourceFile *file, int i18nScore,
const ResourceFile::DirectoryEntry &entry,
const std::wstring &name);
~Resource();
public:
/// Add resource variant.
/// \param file resource file
/// \param i18nScore locale compability score
/// \param entry resource entry in global directory
void addVariant(ResourceFile *file, int i18nScore,
const ResourceFile::DirectoryEntry &entry);
/// Get number of variants.
int getVariantsCount() const { return variants.size(); };
/// Geturns variant object.
/// \param variant variant number.
ResVariant* getVariant(int variant=0) { return variants[variant]; };
/// Load data from variant.
/// Data must be freed with delRef().
/// \param variant variant number.
void* getRef(int variant=0);
/// Load data from variant.
/// data must be freed with delRef()
/// \param size size of data.
/// \param variant variant number.
void* getRef(int *size, int variant=0);
/// Unload data
/// param data pointer to data.
void delRef(void *data);
/// Get size of data.
/// \param variant variant number.
long getSize(int variant=0) { return variants[variant]->getSize(); };
/// Get name of this resource.
const std::wstring& getName() const { return name; };
/// Load data into buffer.
/// \param buffer buffer for data.
/// \param variant variant number.
void getData(Buffer &buffer, int variant=0);
/// Create ResourceStream for resource.
/// This may be usefull for large streams unpacked data,
/// for example video and sound.
/// Delete stream after use.
/// \param variant variant number.
ResourceStream* createStream(int variant=0);
};
/// Internationalized resource files collection.
/// When ResourceCollection created all resources checked against
/// current locale. Resources not belonged to current locale are
/// ignored, resources that can be used in current locale sorted
/// by locale relevance score.
/// All resources names interpeted as name[_language][_COUNTRY].extension
/// where name is name of resource, language is optional two letters
/// ISO language code, country is two letters ISO country code
/// and extension is file extension.
/// After processing resource name will be truncated to name.extension.
/// By default resource with highest locale relevance score will
/// be loaded. Howether, it is possible to enumerate all variants
/// by using Resource class interface.
/// Resource files loaded in order specified by their priority.
/// Resources from file loaded later can replace resources
/// loaded before it.
class ResourcesCollection
{
private:
/// Map resource names to resources.
typedef std::map<std::wstring, Resource*> ResourcesMap;
/// List of resources.
typedef std::list<Resource*> ResourcesList;
/// Map group names to resources list.
typedef std::map<std::wstring, ResourcesList> ResourcesListMap;
/// List of resource files.
typedef std::vector<ResourceFile*> ResourceFiles;
ResourcesMap resources; /// Map of all available resources.
ResourcesListMap groups; /// Map of all available groups.
ResourceFiles files; /// List of resource files.
Buffer buffer; /// Temporary buffer for resource files.
public:
/// Load resource files, make grouping and i18n optimizations.
ResourcesCollection(StringList &directories);
~ResourcesCollection();
public:
/// Returns resource entry.
/// If resource not found Exception will be thrown.
Resource* getResource(const std::wstring &name);
/// Load resource.
/// Loaded data must be freed with delRef method.
void* getRef(const std::wstring &name, int &size);
/// Load resource.
/// Loaded data must be freed with delRef method.
void* getRef(const std::wstring &name);
/// Delete reference to resource.
void delRef(void *data);
/// Visit all group members.
void forEachInGroup(const std::wstring &groupName,
Visitor<Resource*> &visitor);
/// Create ResourceStream for resource.
/// This may be usefull for large streams unpacked data,
/// for example video and sound.
/// Delete stream after use.
/// \param name name of resource.
ResourceStream* createStream(const std::wstring &name);
/// Load data into buffer.
/// Usually you don't need this, use getRef instead.
/// \param name name of resource.
/// \param buffer buffer for data.
void loadData(const std::wstring &name, Buffer &buffer);
private:
/// Open resource files.
void loadResourceFiles(StringList &directories);
/// Make grouping and locale processing.
void processFiles();
};
/// Helper class for simple resource loading and unloading
class ResDataHolder
{
private:
void *data;
size_t size;
public:
/// Create holder without data
ResDataHolder();
/// Create holder and load data
ResDataHolder(const std::wstring &name);
~ResDataHolder();
public:
/// Load resource data
void load(const std::wstring &name);
/// Returns pointer to resource data
void* getData() const { return data; };
/// Returns size of data
size_t getSize() const { return size; };
};
/// Global collection of resources.
/// Careated at first step at boot time stage2
extern ResourcesCollection *resources;
#endif

563
rules.cpp Normal file
View File

@ -0,0 +1,563 @@
#include "puzgen.h"
#include "utils.h"
#include "main.h"
#include "convert.h"
#include "unicode.h"
static std::wstring getThingName(int row, int thing)
{
std::wstring s;
s += (wchar_t)(L'A' + row);
s += toString(thing);
return s;
}
class NearRule: public Rule
{
private:
int thing1[2];
int thing2[2];
public:
NearRule(SolvedPuzzle puzzle);
NearRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
private:
bool applyToCol(Possibilities &pos, int col, int nearRow, int nearNum,
int thisRow, int thisNum);
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_HORIZ; };
virtual void save(std::ostream &stream);
};
NearRule::NearRule(SolvedPuzzle puzzle)
{
int col1 = rndGen.genInt(PUZZLE_SIZE);
thing1[0] = rndGen.genInt(PUZZLE_SIZE);
thing1[1] = puzzle[thing1[0]][col1];
int col2;
if (col1 == 0)
col2 = 1;
else
if (col1 == PUZZLE_SIZE-1)
col2 = PUZZLE_SIZE-2;
else
if (rndGen.genInt(2))
col2 = col1 + 1;
else
col2 = col1 - 1;
thing2[0] = rndGen.genInt(PUZZLE_SIZE);
thing2[1] = puzzle[thing2[0]][col2];
}
NearRule::NearRule(std::istream &stream)
{
thing1[0] = readInt(stream);
thing1[1] = readInt(stream);
thing2[0] = readInt(stream);
thing2[1] = readInt(stream);
}
bool NearRule::applyToCol(Possibilities &pos, int col, int nearRow, int nearNum,
int thisRow, int thisNum)
{
bool hasLeft, hasRight;
if (col == 0)
hasLeft = false;
else
hasLeft = pos.isPossible(col - 1, nearRow, nearNum);
if (col == PUZZLE_SIZE-1)
hasRight = false;
else
hasRight = pos.isPossible(col + 1, nearRow, nearNum);
if ((! hasRight) && (! hasLeft) && pos.isPossible(col, thisRow, thisNum)) {
pos.exclude(col, thisRow, thisNum);
return true;
} else
return false;
}
bool NearRule::apply(Possibilities &pos)
{
bool changed = false;
for (int i = 0; i < PUZZLE_SIZE; i++) {
if (applyToCol(pos, i, thing1[0], thing1[1], thing2[0], thing2[1]))
changed = true;
if (applyToCol(pos, i, thing2[0], thing2[1], thing1[0], thing1[1]))
changed = true;
}
if (changed)
apply(pos);
return changed;
}
std::wstring NearRule::getAsText()
{
return getThingName(thing1[0], thing1[1]) +
L" is near to " + getThingName(thing2[0], thing2[1]);
}
void NearRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(thing1[0], thing1[1], h);
screen.draw(x, y, icon);
screen.draw(x + icon->h, y, iconSet.getNearHintIcon(h));
screen.draw(x + icon->h*2, y, iconSet.getLargeIcon(thing2[0], thing2[1], h));
}
void NearRule::save(std::ostream &stream)
{
writeString(stream, L"near");
writeInt(stream, thing1[0]);
writeInt(stream, thing1[1]);
writeInt(stream, thing2[0]);
writeInt(stream, thing2[1]);
}
class DirectionRule: public Rule
{
private:
int row1, thing1;
int row2, thing2;
public:
DirectionRule(SolvedPuzzle puzzle);
DirectionRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
private:
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_HORIZ; };
virtual void save(std::ostream &stream);
};
DirectionRule::DirectionRule(SolvedPuzzle puzzle)
{
row1 = rndGen.genInt(PUZZLE_SIZE);
row2 = rndGen.genInt(PUZZLE_SIZE);
int col1 = rndGen.genInt(PUZZLE_SIZE - 1);
int col2 = rndGen.genInt(PUZZLE_SIZE - col1 - 1) + col1 + 1;
thing1 = puzzle[row1][col1];
thing2 = puzzle[row2][col2];
}
DirectionRule::DirectionRule(std::istream &stream)
{
row1 = readInt(stream);
thing1 = readInt(stream);
row2 = readInt(stream);
thing2 = readInt(stream);
}
bool DirectionRule::apply(Possibilities &pos)
{
bool changed = false;
for (int i = 0; i < PUZZLE_SIZE; i++) {
if (pos.isPossible(i, row2, thing2)) {
pos.exclude(i, row2, thing2);
changed = true;
}
if (pos.isPossible(i, row1, thing1))
break;
}
for (int i = PUZZLE_SIZE-1; i >= 0; i--) {
if (pos.isPossible(i, row1, thing1)) {
pos.exclude(i, row1, thing1);
changed = true;
}
if (pos.isPossible(i, row2, thing2))
break;
}
return changed;
}
std::wstring DirectionRule::getAsText()
{
return getThingName(row1, thing1) +
L" is from the left of " + getThingName(row2, thing2);
}
void DirectionRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(row1, thing1, h);
screen.draw(x, y, icon);
screen.draw(x + icon->h, y, iconSet.getSideHintIcon(h));
screen.draw(x + icon->h*2, y, iconSet.getLargeIcon(row2, thing2, h));
}
void DirectionRule::save(std::ostream &stream)
{
writeString(stream, L"direction");
writeInt(stream, row1);
writeInt(stream, thing1);
writeInt(stream, row2);
writeInt(stream, thing2);
}
class OpenRule: public Rule
{
private:
int col, row, thing;
public:
OpenRule(SolvedPuzzle puzzle);
OpenRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
virtual bool applyOnStart() { return true; };
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted) { };
virtual ShowOptions getShowOpts() { return SHOW_NOTHING; };
virtual void save(std::ostream &stream);
};
OpenRule::OpenRule(SolvedPuzzle puzzle)
{
col = rndGen.genInt(PUZZLE_SIZE);
row = rndGen.genInt(PUZZLE_SIZE);
thing = puzzle[row][col];
}
OpenRule::OpenRule(std::istream &stream)
{
col = readInt(stream);
row = readInt(stream);
thing = readInt(stream);
}
bool OpenRule::apply(Possibilities &pos)
{
if (! pos.isDefined(col, row)) {
pos.set(col, row, thing);
return true;
} else
return false;
}
std::wstring OpenRule::getAsText()
{
return getThingName(row, thing) + L" is at column " + toString(col+1);
}
void OpenRule::save(std::ostream &stream)
{
writeString(stream, L"open");
writeInt(stream, col);
writeInt(stream, row);
writeInt(stream, thing);
}
class UnderRule: public Rule
{
private:
int row1, thing1, row2, thing2;
public:
UnderRule(SolvedPuzzle puzzle);
UnderRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_VERT; };
virtual void save(std::ostream &stream);
};
UnderRule::UnderRule(SolvedPuzzle puzzle)
{
int col = rndGen.genInt(PUZZLE_SIZE);
row1 = rndGen.genInt(PUZZLE_SIZE);
thing1 = puzzle[row1][col];
do {
row2 = rndGen.genInt(PUZZLE_SIZE);
} while (row2 == row1) ;
thing2 = puzzle[row2][col];
}
UnderRule::UnderRule(std::istream &stream)
{
row1 = readInt(stream);
thing1 = readInt(stream);
row2 = readInt(stream);
thing2 = readInt(stream);
}
bool UnderRule::apply(Possibilities &pos)
{
bool changed = false;
for (int i = 0; i < PUZZLE_SIZE; i++) {
if ((! pos.isPossible(i, row1, thing1)) &&
pos.isPossible(i, row2, thing2))
{
pos.exclude(i, row2, thing2);
changed = true;
}
if ((! pos.isPossible(i, row2, thing2)) &&
pos.isPossible(i, row1, thing1))
{
pos.exclude(i, row1, thing1);
changed = true;
}
}
return changed;
}
std::wstring UnderRule::getAsText()
{
return getThingName(row1, thing1) + L" is the same column as " +
getThingName(row2, thing2);
}
void UnderRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(row1, thing1, h);
screen.draw(x, y, icon);
screen.draw(x, y + icon->h, iconSet.getLargeIcon(row2, thing2, h));
}
void UnderRule::save(std::ostream &stream)
{
writeString(stream, L"under");
writeInt(stream, row1);
writeInt(stream, thing1);
writeInt(stream, row2);
writeInt(stream, thing2);
}
class BetweenRule: public Rule
{
private:
int row1, thing1;
int row2, thing2;
int centerRow, centerThing;
public:
BetweenRule(SolvedPuzzle puzzle);
BetweenRule(std::istream &stream);
virtual bool apply(Possibilities &pos);
virtual std::wstring getAsText();
private:
virtual void draw(int x, int y, IconSet &iconSet, bool highlighted);
virtual ShowOptions getShowOpts() { return SHOW_HORIZ; };
virtual void save(std::ostream &stream);
};
BetweenRule::BetweenRule(SolvedPuzzle puzzle)
{
centerRow = rndGen.genInt(PUZZLE_SIZE);
row1 = rndGen.genInt(PUZZLE_SIZE);
row2 = rndGen.genInt(PUZZLE_SIZE);
int centerCol = rndGen.genInt(PUZZLE_SIZE - 2) + 1;
centerThing = puzzle[centerRow][centerCol];
if (rndGen.genInt(2)) {
thing1 = puzzle[row1][centerCol - 1];
thing2 = puzzle[row2][centerCol + 1];
} else {
thing1 = puzzle[row1][centerCol + 1];
thing2 = puzzle[row2][centerCol - 1];
}
}
BetweenRule::BetweenRule(std::istream &stream)
{
row1 = readInt(stream);
thing1 = readInt(stream);
row2 = readInt(stream);
thing2 = readInt(stream);
centerRow = readInt(stream);
centerThing = readInt(stream);
}
bool BetweenRule::apply(Possibilities &pos)
{
bool changed = false;
if (pos.isPossible(0, centerRow, centerThing)) {
changed = true;
pos.exclude(0, centerRow, centerThing);
}
if (pos.isPossible(PUZZLE_SIZE-1, centerRow, centerThing)) {
changed = true;
pos.exclude(PUZZLE_SIZE-1, centerRow, centerThing);
}
bool goodLoop;
do {
goodLoop = false;
for (int i = 1; i < PUZZLE_SIZE-1; i++) {
if (pos.isPossible(i, centerRow, centerThing)) {
if (! ((pos.isPossible(i-1, row1, thing1) &&
pos.isPossible(i+1, row2, thing2)) ||
(pos.isPossible(i-1, row2, thing2) &&
pos.isPossible(i+1, row1, thing1))))
{
pos.exclude(i, centerRow, centerThing);
goodLoop = true;
}
}
}
for (int i = 0; i < PUZZLE_SIZE; i++) {
bool leftPossible, rightPossible;
if (pos.isPossible(i, row2, thing2)) {
if (i < 2)
leftPossible = false;
else
leftPossible = (pos.isPossible(i-1, centerRow, centerThing)
&& pos.isPossible(i-2, row1, thing1));
if (i >= PUZZLE_SIZE - 2)
rightPossible = false;
else
rightPossible = (pos.isPossible(i+1, centerRow, centerThing)
&& pos.isPossible(i+2, row1, thing1));
if ((! leftPossible) && (! rightPossible)) {
pos.exclude(i, row2, thing2);
goodLoop = true;
}
}
if (pos.isPossible(i, row1, thing1)) {
if (i < 2)
leftPossible = false;
else
leftPossible = (pos.isPossible(i-1, centerRow, centerThing)
&& pos.isPossible(i-2, row2, thing2));
if (i >= PUZZLE_SIZE - 2)
rightPossible = false;
else
rightPossible = (pos.isPossible(i+1, centerRow, centerThing)
&& pos.isPossible(i+2, row2, thing2));
if ((! leftPossible) && (! rightPossible)) {
pos.exclude(i, row1, thing1);
goodLoop = true;
}
}
}
if (goodLoop)
changed = true;
} while (goodLoop);
return changed;
}
std::wstring BetweenRule::getAsText()
{
return getThingName(centerRow, centerThing) +
L" is between " + getThingName(row1, thing1) + L" and " +
getThingName(row2, thing2);
}
void BetweenRule::draw(int x, int y, IconSet &iconSet, bool h)
{
SDL_Surface *icon = iconSet.getLargeIcon(row1, thing1, h);
screen.draw(x, y, icon);
screen.draw(x + icon->w, y, iconSet.getLargeIcon(centerRow, centerThing, h));
screen.draw(x + icon->w*2, y, iconSet.getLargeIcon(row2, thing2, h));
SDL_Surface *arrow = iconSet.getBetweenArrow(h);
screen.draw(x + icon->w - (arrow->w - icon->w) / 2, y + 0, arrow);
}
void BetweenRule::save(std::ostream &stream)
{
writeString(stream, L"between");
writeInt(stream, row1);
writeInt(stream, thing1);
writeInt(stream, row2);
writeInt(stream, thing2);
writeInt(stream, centerRow);
writeInt(stream, centerThing);
}
Rule* genRule(SolvedPuzzle &puzzle)
{
int a = rndGen.genInt(14);
switch (a) {
case 0:
case 1:
case 2:
case 3: return new NearRule(puzzle);
case 4: return new OpenRule(puzzle);
case 5:
case 6: return new UnderRule(puzzle);
case 7:
case 8:
case 9:
case 10: return new DirectionRule(puzzle);
case 11:
case 12:
case 13: return new BetweenRule(puzzle);
default: return genRule(puzzle);
}
}
void saveRules(Rules &rules, std::ostream &stream)
{
writeInt(stream, rules.size());
for (Rules::iterator i = rules.begin(); i != rules.end(); i++)
(*i)->save(stream);
}
void loadRules(Rules &rules, std::istream &stream)
{
int no = readInt(stream);
for (int i = 0; i < no; i++) {
std::wstring ruleType = readString(stream);
Rule *r;
if (ruleType == L"near")
r = new NearRule(stream);
else if (ruleType == L"open")
r = new OpenRule(stream);
else if (ruleType == L"under")
r = new UnderRule(stream);
else if (ruleType == L"direction")
r = new DirectionRule(stream);
else if (ruleType == L"between")
r = new BetweenRule(stream);
else
throw Exception(L"invalid rule type " + ruleType);
rules.push_back(r);
}
}

309
screen.cpp Normal file
View File

@ -0,0 +1,309 @@
#include <SDL.h>
#include "screen.h"
#include "exceptions.h"
#include "unicode.h"
Screen::Screen()
{
screen = NULL;
mouseImage = NULL;
mouseSave = NULL;
mouseVisible = false;
regionsList = NULL;
maxRegionsList = 0;
}
Screen::~Screen()
{
SDL_SetCursor(cursor);
if (mouseImage) SDL_FreeSurface(mouseImage);
if (mouseSave) SDL_FreeSurface(mouseSave);
if (regionsList) free(regionsList);
}
const VideoMode Screen::getVideoMode() const
{
return VideoMode(screen->w, screen->h, screen->format->BitsPerPixel, fullScreen);
}
void Screen::setMode(const VideoMode& mode)
{
fullScreen = mode.isFullScreen();
int flags = SDL_SWSURFACE /*| SDL_OPENGL*/;
if (fullScreen)
flags = flags | SDL_FULLSCREEN;
screen = SDL_SetVideoMode(mode.getWidth(), mode.getHeight(), mode.getBpp(), flags);
if (! screen)
throw Exception(L"Couldn't set video mode: " +
fromMbcs((SDL_GetError())));
}
std::vector<VideoMode> Screen::getFullScreenModes() const
{
std::vector<VideoMode> modes;
return modes;
}
int Screen::getWidth() const
{
if (screen)
return screen->w;
else
throw Exception(L"No video mode selected");
}
int Screen::getHeight() const
{
if (screen)
return screen->h;
else
throw Exception(L"No video mode selected");
}
void Screen::centerMouse()
{
if (screen)
SDL_WarpMouse(screen->w / 2, screen->h / 2);
else
throw Exception(L"No video mode selected");
}
void Screen::setMouseImage(SDL_Surface *image)
{
if (mouseImage) {
SDL_FreeSurface(mouseImage);
mouseImage = NULL;
}
if (mouseSave) {
SDL_FreeSurface(mouseSave);
mouseSave = NULL;
}
if (! image) return;
mouseImage = SDL_DisplayFormat(image);
if (! mouseImage)
throw Exception(L"Error creating surface");
//mouseSave = SDL_DisplayFormat(image);
mouseSave = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCCOLORKEY,
image->w, image->h, screen->format->BitsPerPixel,
screen->format->Rmask, screen->format->Gmask,
screen->format->Bmask, screen->format->Amask);
if (! mouseSave) {
SDL_FreeSurface(mouseImage);
throw Exception(L"Error creating buffer surface");
}
SDL_SetColorKey(mouseImage, SDL_SRCCOLORKEY,
SDL_MapRGB(mouseImage->format, 0, 0, 0));
}
void Screen::hideMouse()
{
if (! mouseVisible)
return;
if (! niceCursor) {
mouseVisible = false;
return;
}
if (mouseSave) {
SDL_Rect src = { 0, 0, mouseSave->w, mouseSave->h };
SDL_Rect dst = { saveX, saveY, mouseSave->w, mouseSave->h };
if (src.w > 0) {
SDL_BlitSurface(mouseSave, &src, screen, &dst);
addRegionToUpdate(dst.x, dst.y, dst.w, dst.h);
}
}
mouseVisible = false;
}
void Screen::showMouse()
{
if (mouseVisible)
return;
if (! niceCursor) {
mouseVisible = true;
return;
}
if (mouseImage && mouseSave) {
int x, y;
SDL_GetMouseState(&x, &y);
saveX = x;
saveY = y;
SDL_Rect src = { 0, 0, mouseSave->w, mouseSave->h };
SDL_Rect dst = { x, y, mouseImage->w, mouseImage->h };
if (src.w > 0) {
SDL_BlitSurface(screen, &dst, mouseSave, &src);
SDL_BlitSurface(mouseImage, &src, screen, &dst);
addRegionToUpdate(dst.x, dst.y, dst.w, dst.h);
}
}
mouseVisible = true;
}
void Screen::updateMouse()
{
hideMouse();
showMouse();
}
void Screen::flush()
{
if (! regions.size()) return;
if (! regionsList) {
regionsList = (SDL_Rect*)malloc(sizeof(SDL_Rect) * regions.size());
if (! regionsList) {
regions.clear();
throw Exception(L"Error allocating regions buffer");
}
maxRegionsList = regions.size();
} else {
if (maxRegionsList < (int)regions.size()) {
SDL_Rect *r = (SDL_Rect*)realloc(regionsList,
sizeof(SDL_Rect) * regions.size());
if (! r) {
regions.clear();
free(regionsList);
throw Exception(L"Error incrementing regions buffer");
}
regionsList = r;
maxRegionsList = regions.size();
}
}
int j = 0;
for (std::list<SDL_Rect>::iterator i = regions.begin();
i != regions.end(); i++, j++)
regionsList[j] = *i;
SDL_UpdateRects(screen, regions.size(), regionsList);
regions.clear();
}
void Screen::addRegionToUpdate(int x, int y, int w, int h)
{
if (((x >= getWidth()) || (y >= getHeight())) || (0 >= w) || (0 >= h))
return;
if ((x + w < 0) || (y + h < 0))
return;
if (x + w > getWidth())
w = getWidth() - x;
if (y + h > getHeight())
h = getHeight() - y;
if (0 > x) {
w = w + x;
x = 0;
}
if (0 > y) {
h = h + y;
y = 0;
}
SDL_Rect r = { x, y, w, h };
regions.push_back(r);
}
void Screen::setPixel(int x, int y, int r, int g, int b)
{
SDL_LockSurface(screen);
int bpp = screen->format->BytesPerPixel;
Uint32 pixel = SDL_MapRGB(screen->format, r, g, b);
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8*)screen->pixels + y * screen->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
SDL_UnlockSurface(screen);
}
void Screen::draw(int x, int y, SDL_Surface *tile)
{
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { x, y, tile->w, tile->h };
SDL_BlitSurface(tile, &src, screen, &dst);
}
void Screen::setCursor(bool nice)
{
if (nice == niceCursor)
return;
bool oldVisible = mouseVisible;
if (mouseVisible)
hideMouse();
niceCursor = nice;
if (niceCursor)
SDL_SetCursor(emptyCursor);
else
SDL_SetCursor(cursor);
if (oldVisible)
showMouse();
}
void Screen::initCursors()
{
cursor = SDL_GetCursor();
Uint8 t = 0;
emptyCursor = SDL_CreateCursor(&t, &t, 8, 1, 0, 0);
}
void Screen::doneCursors()
{
if (niceCursor)
SDL_SetCursor(cursor);
SDL_FreeCursor(emptyCursor);
}
SDL_Surface* Screen::createSubimage(int x, int y, int width, int height)
{
SDL_Surface *s = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCCOLORKEY,
width, height, screen->format->BitsPerPixel,
screen->format->Rmask, screen->format->Gmask,
screen->format->Bmask, screen->format->Amask);
if (! s)
throw Exception(L"Error creating buffer surface");
SDL_Rect src = { x, y, width, height };
SDL_Rect dst = { 0, 0, width, height };
SDL_BlitSurface(screen, &src, s, &dst);
return s;
}

78
screen.h Normal file
View File

@ -0,0 +1,78 @@
#ifndef __SCREEN_H__
#define __SCREEN_H__
#include "SDL.h"
#include <vector>
#include <list>
class VideoMode
{
private:
int width;
int height;
int bpp;
bool fullScreen;
public:
VideoMode(int w, int h, int bpp, bool fullscreen)
{
width = w;
height = h;
this->bpp = bpp;
this->fullScreen = fullscreen;
}
public:
int getWidth() const { return width; };
int getHeight() const { return height; };
int getBpp() const { return bpp; };
bool isFullScreen() const { return fullScreen; };
};
class Screen
{
private:
SDL_Surface *screen;
bool fullScreen;
SDL_Surface *mouseImage;
SDL_Surface *mouseSave;
std::list<SDL_Rect> regions;
bool mouseVisible;
SDL_Rect *regionsList;
int maxRegionsList;
int saveX, saveY;
bool niceCursor;
SDL_Cursor *cursor, *emptyCursor;
public:
Screen();
~Screen();
public:
const VideoMode getVideoMode() const;
int getWidth() const;
int getHeight() const;
void setMode(const VideoMode& mode);
std::vector<VideoMode> getFullScreenModes() const;
void centerMouse();
void setMouseImage(SDL_Surface *image);
void hideMouse();
void showMouse();
void updateMouse();
void flush();
void addRegionToUpdate(int x, int y, int w, int h);
void setPixel(int x, int y, int r, int g, int b);
SDL_Surface* getSurface() { return screen; };
void draw(int x, int y, SDL_Surface *surface);
void setCursor(bool nice);
void initCursors();
void doneCursors();
SDL_Surface* createSubimage(int x, int y, int width, int height);
};
#endif

62
sound.cpp Normal file
View File

@ -0,0 +1,62 @@
#include "sound.h"
#include <iostream>
#include <SDL_events.h>
#include "resources.h"
Sound *sound;
Sound::Sound()
{
int audio_rate = 22050;
Uint16 audio_format = AUDIO_S16; /* 16-bit stereo */
int audio_channels = 2;
int audio_buffers = 1024;
disabled = Mix_OpenAudio(audio_rate, audio_format, audio_channels,
audio_buffers);
if (disabled)
std::cout << "Audio is disabled" << std::endl;
}
Sound::~Sound()
{
if (! disabled)
Mix_CloseAudio();
for (ChunkMap::iterator i = chunkCache.begin(); i != chunkCache.end(); i++)
Mix_FreeChunk((*i).second);
Mix_CloseAudio();
}
void Sound::play(const std::wstring &name)
{
if (disabled || (! enableFx))
return;
Mix_Chunk *chunk = NULL;
ChunkMap::iterator i = chunkCache.find(name);
if (i != chunkCache.end())
chunk = (*i).second;
else {
ResDataHolder data(name);
chunk = Mix_LoadWAV_RW(SDL_RWFromMem(data.getData(), data.getSize()),
0);
chunkCache[name] = chunk;
}
if (chunk) {
Mix_VolumeChunk(chunk, (int)(volume * 128.0f));
Mix_PlayChannel(-1, chunk, 0);
}
SDL_PumpEvents();
}
void Sound::setVolume(float v)
{
volume = v;
enableFx = 0.01 < volume;
}

35
sound.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __SOUND_H__
#define __SOUND_H__
#include <string>
#include <map>
#include <SDL_mixer.h>
class Sound
{
private:
bool disabled;
typedef std::map<std::wstring, Mix_Chunk*> ChunkMap;
ChunkMap chunkCache;
bool enableFx;
float volume;
public:
Sound();
~Sound();
public:
void play(const std::wstring &name);
void setVolume(float volume);
};
extern Sound *sound;
#endif

48
storage.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "storage.h"
#ifndef WIN32
#include "tablestorage.h"
#else
#include "regstorage.h"
#endif
class StorageHolder
{
private:
Storage *storage;
public:
StorageHolder();
~StorageHolder();
public:
Storage* getStorage() { return storage; };
};
StorageHolder::StorageHolder()
{
#ifndef WIN32
storage = new TableStorage();
#else
storage = new RegistryStorage();
#endif
}
StorageHolder::~StorageHolder()
{
if (storage)
delete storage;
}
static StorageHolder storageHolder;
Storage* getStorage()
{
return storageHolder.getStorage();
}

27
storage.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __STORAGE_H__
#define __STORAGE_H__
#include <string>
class Storage
{
public:
virtual ~Storage() { };
public:
virtual int get(const std::wstring &name, int dflt) = 0;
virtual std::wstring get(const std::wstring &name,
const std::wstring &dflt) = 0;
virtual void set(const std::wstring &name, int value) = 0;
virtual void set(const std::wstring &name, const std::wstring &value) = 0;
virtual void flush() = 0;
};
Storage* getStorage();
#endif

57
streams.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "streams.h"
#include "exceptions.h"
#include "unicode.h"
UtfStreamReader::UtfStreamReader(std::ifstream *s)
{
stream = s;
}
UtfStreamReader::~UtfStreamReader()
{
}
// This function is very slow because of poor fromUtf8 function design
wchar_t UtfStreamReader::getNextChar()
{
unsigned char buf[10];
if (0 < backBuf.size()) {
wchar_t wc = backBuf.front();
backBuf.pop_front();
return wc;
}
if (! stream->good())
throw Exception(L"Error reading from stream 1");
int sz = stream->readsome((char*)buf, 1);
if (1 != sz)
throw Exception(L"Error reading from stream 2");
int size = getUtf8Length(buf[0]);
if (size > 1) {
sz = stream->readsome((char*)buf + 1, size - 1);
if (size - 1 != sz)
throw Exception(L"Error reading from stream 3");
}
buf[size] = 0;
std::string s((char*)buf);
std::wstring ws(fromUtf8(s));
if (1 != ws.length())
throw Exception(L"Error converting UTF-8 character to wide character");
return ws[0];
}
void UtfStreamReader::ungetChar(wchar_t ch)
{
backBuf.push_back(ch);
}
bool UtfStreamReader::isEof()
{
if (stream->eof())
return true; // FIXME: it doesn't work. why?
return EOF == stream->peek();
}

41
streams.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __STREAMS_H__
#define __STREAMS_H__
#include <fstream>
#include <list>
/// Read utf-8 file and convert it to wide characters
class UtfStreamReader
{
private:
/// Pointer to file stream
std::ifstream *stream;
/// Push back buffet
std::list<wchar_t> backBuf;
public:
/// Create utf-8 stream reader.
/// \param stream pointer to file stream.
UtfStreamReader(std::ifstream *stream);
/// Destructor
~UtfStreamReader();
public:
/// Read next unicode character.
wchar_t getNextChar();
/// Push back character.
/// \param ch character to push back
void ungetChar(wchar_t ch);
/// Check if end of file reached.
bool isEof();
};
#endif

434
table.cpp Normal file
View File

@ -0,0 +1,434 @@
#include "table.h"
#include <fstream>
#include "convert.h"
#include "unicode.h"
#include "streams.h"
#include "lexal.h"
#include "exceptions.h"
class IntValue: public Value
{
private:
int value;
public:
IntValue(int val) { value = val; };
virtual ~IntValue() { };
public:
virtual Type getType() const { return Value::Integer; };
virtual int asInt() const { return value; };
virtual double asDouble() const { return value; };
virtual std::wstring asString() const { return toString(value); };
virtual ::Table* asTable() const {
throw Exception(L"Can't convert integer to table");
};
virtual Value* clone() const { return new IntValue(value); };
};
class DoubleValue: public Value
{
private:
double value;
public:
DoubleValue(double val) { value = val; };
virtual ~DoubleValue() { };
public:
virtual Type getType() const { return Value::Double; };
virtual int asInt() const { return (int)value; };
virtual double asDouble() const { return value; };
virtual std::wstring asString() const { return toString(value); };
virtual ::Table* asTable() const {
throw Exception(L"Can't convert double to table");
};
virtual Value* clone() const { return new DoubleValue(value); };
};
class StringValue: public Value
{
private:
std::wstring value;
public:
StringValue(const std::wstring& val): value(val) { };
virtual ~StringValue() { };
public:
virtual Type getType() const { return Value::String; };
virtual int asInt() const { return strToInt(value); };
virtual double asDouble() const { return strToDouble(value); };
virtual std::wstring asString() const { return value; };
virtual ::Table* asTable() const {
throw Exception(L"Can't convert string to table");
};
virtual Value* clone() const { return new StringValue(value); };
};
class TableValue: public Value
{
private:
::Table *value;
public:
TableValue(::Table *val) { value = val; };
virtual ~TableValue() { delete value; };
public:
virtual Type getType() const { return Value::Table; };
virtual int asInt() const {
throw Exception(L"Can't convert table to int");
};
virtual double asDouble() const {
throw Exception(L"Can't convert table to double");
};
virtual std::wstring asString() const {
throw Exception(L"Can't convert table to string");
};
virtual ::Table* asTable() const { return value; };
virtual Value* clone() const {
return new TableValue(new ::Table(*value));
};
};
Table::Table(const Table &table)
{
*this = table;
}
Table::Table(const std::string &fileName)
{
lastArrayIndex = 0;
std::ifstream stream(fileName.c_str(),
std::ios::binary | std::ios::in);
if (! stream.good())
throw Exception(L"Error opening file '" + fromMbcs(fileName) + L"");
UtfStreamReader reader(&stream);
Lexal lexal = Lexal(reader);
parse(lexal, false, 0, 0);
}
Table::Table(Lexal &lexal, int line, int pos)
{
lastArrayIndex = 0;
parse(lexal, true, line, pos);
}
Table::Table()
{
lastArrayIndex = 0;
}
Table::~Table()
{
for (ValuesMap::iterator i = fields.begin(); i != fields.end(); i++)
delete (*i).second;
}
Table& Table::operator = (const Table &table)
{
if (this == &table)
return *this;
fields.clear();
lastArrayIndex = table.lastArrayIndex;
for (ValuesMap::const_iterator i = table.fields.begin();
i != table.fields.end(); i++)
fields[(*i).first] = (*i).second->clone();
return *this;
}
static Value* lexToValue(Lexal &lexal, const Lexeme &lexeme)
{
switch (lexeme.getType())
{
case Lexeme::Ident:
case Lexeme::String:
return new StringValue(lexeme.getContent());
case Lexeme::Integer:
return new IntValue(strToInt(lexeme.getContent()));
case Lexeme::Float:
return new DoubleValue(strToDouble(lexeme.getContent()));
case Lexeme::Symbol:
if (L"{" == lexeme.getContent())
return new TableValue(new Table(lexal, lexeme.getLine(),
lexeme.getPos()));
default:
throw Exception(L"Invalid lexeme type at " + lexeme.getPosStr());
}
}
void Table::addValuePair(Lexal &lexal, const std::wstring &name)
{
Lexeme lex = lexal.getNext();
if (Lexeme::Eof == lex.getType())
throw Exception(L"Unexpected end of file");
fields[name] = lexToValue(lexal, lex);
}
void Table::addArrayElement(Lexal &lexal, const Lexeme &lexeme)
{
fields[numToStr(lastArrayIndex)] = lexToValue(lexal, lexeme);
lastArrayIndex++;
}
void Table::parse(Lexal &lexal, bool needBracket, int startLine, int startPos)
{
Lexeme lex;
bool read = true;
while (true) {
if (read) {
lex = lexal.getNext();
read = true;
}
Lexeme::Type type = lex.getType();
if (Lexeme::Eof == type) {
if (! needBracket)
return;
else
throw Exception(L"Table started at " + Lexal::posToStr(
startLine, startPos) + L" is never finished");
} else if ((Lexeme::Symbol == type) && (L"}" == lex.getContent())
&& needBracket)
return;
else if ((Lexeme::Symbol == type) && ((L"," == lex.getContent()) ||
(L";" == lex.getContent()))) {
// ignore separator
} else if (Lexeme::Ident == type) {
Lexeme nextLex = lexal.getNext();
if (Lexeme::Symbol == nextLex.getType()) {
if (L"=" == nextLex.getContent())
addValuePair(lexal, lex.getContent());
else {
addArrayElement(lexal, lex);
lex = nextLex;
read = false;
}
} else
throw Exception(L"Unexpected token at " + Lexal::posToStr(
startLine, startPos));
} else
addArrayElement(lexal, lex);
}
}
bool Table::hasKey(const std::wstring &key) const
{
return (fields.end() != fields.find(key));
}
static std::wstring encodeString(const std::wstring &str)
{
std::wstring res;
int len = str.length();
res += L"\"";
for (int i = 0; i < len; i++) {
wchar_t ch = str[i];
switch (ch) {
case '\n': res += L"\\n"; break;
case '\r': res += L"\\r"; break;
case '\'': res += L"\\'"; break;
case '\"': res += L"\\\""; break;
case '\\': res += L"\\\\"; break;
default:
res += ch;
}
}
res += L"\"";
return res;
}
static std::wstring printValue(Value *value, bool butify, int spaces)
{
if (! value)
return L"";
switch (value->getType()) {
case Value::Integer:
return toString(value->asInt());
case Value::Double:
{
std::wstring s = toString(value->asDouble());
if (s.find(L'.') >= s.length())
s.append(L".0");
return s;
}
case Value::String:
return encodeString(value->asString());
case Value::Table:
return value->asTable()->toString(true, butify, spaces);
}
return L"";
}
bool Table::isArray() const
{
int size = fields.size();
for (int i = 0; i < size; i++) {
if (! hasKey(::toString(i)))
return false;
}
return true;
}
std::wstring Table::toString() const
{
return toString(false, true, 0);
}
static bool isInteger(const std::wstring &str)
{
int sz = str.length();
for (int i = 0; i < sz; i++) {
wchar_t ch = str[i];
if ((ch < L'0') || (ch > L'9'))
return false;
}
return true;
}
std::wstring Table::toString(bool printBraces, bool butify, int spaces) const
{
std::wstring res;
if (printBraces)
res += butify ? L"{\n" : L"{";
bool printNames = ! isArray();
for (ValuesMap::const_iterator i = fields.begin(); i != fields.end();
i++)
{
const std::wstring &name = (*i).first;
Value *value = (*i).second;
if (butify)
for (int j = 0; j < spaces; j++)
res += L" ";
else
res += L" ";
if (printNames && (! isInteger(name)))
res += name + L" = ";
res += printValue(value, butify, spaces + 4);
if (printNames)
res += butify ? L";\n" : L";";
else
res += butify ? L",\n" : L",";
}
if (printBraces) {
if (! butify)
res += L" }";
else {
int ident = (spaces >= 4) ? spaces - 4 : 0;
for (int j = 0; j < ident; j++)
res += L" ";
res += L"}";
}
}
return res;
}
Value::Type Table::getType(const std::wstring &key) const
{
ValuesMap::const_iterator i = fields.find(key);
if (i == fields.end())
throw Exception(L"Field '" + key + L"' doesn't exists in the table");
return (*i).second->getType();
}
std::wstring Table::getString(const std::wstring &key,
const std::wstring &dflt) const
{
ValuesMap::const_iterator i = fields.find(key);
return (i != fields.end()) ? (*i).second->asString() : dflt;
}
int Table::getInt(const std::wstring &key, int dflt) const
{
ValuesMap::const_iterator i = fields.find(key);
return (i != fields.end()) ? ((*i).second ? (*i).second->asInt() : dflt) : dflt;
}
double Table::getDouble(const std::wstring &key, double dflt) const
{
ValuesMap::const_iterator i = fields.find(key);
return (i != fields.end()) ? (*i).second->asDouble() : dflt;
}
Table* Table::getTable(const std::wstring &key, Table *dflt) const
{
ValuesMap::const_iterator i = fields.find(key);
return (i != fields.end()) ? (*i).second->asTable() : dflt;
}
void Table::setValue(const std::wstring &key, Value *value)
{
ValuesMap::const_iterator i = fields.find(key);
if (i != fields.end())
delete (*i).second;
fields[key] = value;
}
void Table::setString(const std::wstring &key, const std::wstring &value)
{
setValue(key, new StringValue(value));
}
void Table::setInt(const std::wstring &key, int value)
{
setValue(key, new IntValue(value));
}
void Table::setDouble(const std::wstring &key, double value)
{
setValue(key, new DoubleValue(value));
}
void Table::setTable(const std::wstring &key, Table *value)
{
setValue(key, new TableValue(value));
}
void Table::save(const std::wstring &fileName) const
{
std::ofstream stream(toMbcs(fileName).c_str(), std::ios::out
| std::ios::binary);
if (! stream.good())
throw Exception(L"Can't open '" + fileName + L"' for writing");
std::string utfStr(toUtf8(toString()));
stream.write(utfStr.c_str(), utfStr.length());
if (! stream.good())
throw Exception(L"Can't write table to file '" + fileName + L"'");
stream.close();
}

84
table.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef __TABLE_H__
#define __TABLE_H__
#include <string>
#include <map>
#include "lexal.h"
class Table;
class Value
{
public:
typedef enum Type {
Integer,
Double,
String,
Table
};
public:
virtual ~Value() { };
public:
virtual ::Table* asTable() const = 0;
virtual Type getType() const = 0;
virtual int asInt() const = 0;
virtual double asDouble() const = 0;
virtual std::wstring asString() const = 0;
virtual Value* clone() const = 0;
};
class Table
{
public:
typedef std::map<std::wstring, Value*> ValuesMap;
typedef ValuesMap::const_iterator Iterator;
private:
ValuesMap fields;
int lastArrayIndex;
public:
Table();
Table(const Table &table);
Table(const std::string &fileName);
Table(Lexal &lexal, int startLine, int startPos);
~Table();
public:
Table& operator = (const Table &table);
std::wstring toString() const;
std::wstring toString(bool printBraces, bool butify, int ident) const;
bool isArray() const;
void save(const std::wstring &fileName) const;
public:
Iterator begin() const { return fields.begin(); };
Iterator end() const { return fields.end(); };
bool hasKey(const std::wstring &key) const;
Value::Type getType(const std::wstring &key) const;
std::wstring getString(const std::wstring &key, const std::wstring &dflt = L"") const;
int getInt(const std::wstring &key, int dflt=0) const;
double getDouble(const std::wstring &key, double dflt=0) const;
Table* getTable(const std::wstring &key, Table *dflt=NULL) const;
void setString(const std::wstring &key, const std::wstring &value);
void setInt(const std::wstring &key, int value);
void setDouble(const std::wstring &key, double value);
void setTable(const std::wstring &key, Table *value);
private:
void parse(Lexal &lexal, bool needBracket, int startLine, int startPos);
void addArrayElement(Lexal &lexal, const Lexeme &lexeme);
void addValuePair(Lexal &lexal, const std::wstring &name);
void setValue(const std::wstring &key, Value *value);
};
#endif

59
tablestorage.cpp Normal file
View File

@ -0,0 +1,59 @@
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include "tablestorage.h"
#include "unicode.h"
#include "exceptions.h"
TableStorage::TableStorage()
{
try {
table = Table(toMbcs(getFileName()));
} catch (Exception &e) {
std::cerr << e.getMessage() << std::endl;
} catch (...) {
std::cerr << "Unknown config file error" << std::endl;
}
}
TableStorage::~TableStorage()
{
flush();
}
std::wstring TableStorage::getFileName()
{
#ifndef WIN32
return std::wstring(fromMbcs(getenv("HOME"))) + L"/.einstein/einsteinrc";
#else
return L"einstein.cfg";
#endif
}
int TableStorage::get(const std::wstring &name, int dflt)
{
return table.getInt(name, dflt);
}
std::wstring TableStorage::get(const std::wstring &name,
const std::wstring &dflt)
{
return table.getString(name, dflt);
}
void TableStorage::set(const std::wstring &name, int value)
{
table.setInt(name, value);
}
void TableStorage::set(const std::wstring &name, const std::wstring &value)
{
table.setString(name, value);
}
void TableStorage::flush()
{
table.save(getFileName());
}

32
tablestorage.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef __TABLESTORAGE_H__
#define __TABLESTORAGE_H__
#include "storage.h"
#include "table.h"
class TableStorage: public Storage
{
private:
Table table;
public:
TableStorage();
virtual ~TableStorage();
public:
virtual int get(const std::wstring &name, int dflt);
virtual std::wstring get(const std::wstring &name,
const std::wstring &dflt);
virtual void set(const std::wstring &name, int value);
virtual void set(const std::wstring &name, const std::wstring &value);
virtual void flush();
private:
std::wstring getFileName();
};
#endif

72
tokenizer.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "tokenizer.h"
#include <iostream>
std::wstring Token::toString() const
{
if (Word == type)
return L"Word: '" + content + L"'";
else if (Para == type)
return L"Para";
else if (Eof == type)
return L"Eof";
else
return L"Unknown";
}
static bool wisspace(wchar_t ch)
{
//std::cout << "'" << ch << "' " << ((int)ch) << " " << L'\n' << std::endl;
return (L' ' == ch) || (L'\n' == ch) || (L'\r' == ch)
|| (L'\t' == ch) || (L'\f' == ch) || (L'\v' == ch);
}
bool Tokenizer::skipSpaces(bool notSearch)
{
int len = text.length();
bool foundDoubleReturn = false;
while ((len > currentPos) && wisspace(text[currentPos])) {
currentPos++;
if ((! notSearch) && (L'\n' == text[currentPos - 1])
&& (currentPos < len) && (L'\n' == text[currentPos]))
notSearch = foundDoubleReturn = true;
}
return foundDoubleReturn;
}
Token Tokenizer::getNextToken()
{
if (0 < stack.size()) {
Token t(*stack.begin());
stack.pop_front();
return t;
}
int len = text.length();
if (skipSpaces(! currentPos) && (currentPos < len))
return Token(Token::Para);
if (currentPos >= len)
return Token(Token::Eof);
int wordStart = currentPos;
while ((len > currentPos) && (! wisspace(text[currentPos])))
currentPos++;
return Token(Token::Word, text.substr(wordStart, currentPos - wordStart));
}
void Tokenizer::unget(const Token &token)
{
stack.push_back(token);
}
bool Tokenizer::isFinished()
{
if (0 < stack.size())
return false;
return currentPos >= (int)text.length();
}

61
tokenizer.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef __TOKENIZER_H__
#define __TOKENIZER_H__
#include <string>
#include <list>
class Tokenizer;
class Token
{
friend class Tokenizer;
public:
enum Type {
Word,
Para,
Eof
};
private:
Type type;
std::wstring content;
private:
Token(Type type) { this->type = type; };
Token(Type type, const std::wstring &content): content(content) {
this->type = type;
}
public:
Type getType() const { return type; };
const std::wstring& getContent() const { return content; };
std::wstring toString() const;
};
class Tokenizer
{
private:
std::wstring text;
int currentPos;
std::list<Token> stack;
public:
Tokenizer(const std::wstring &s): text(s) { currentPos = 0; };
public:
Token getNextToken();
void unget(const Token &token);
bool isFinished();
private:
bool skipSpaces(bool notSearch);
};
#endif

175
topscores.cpp Normal file
View File

@ -0,0 +1,175 @@
#include "topscores.h"
#include "storage.h"
#include "utils.h"
#include "font.h"
#include "convert.h"
#include "messages.h"
TopScores::TopScores()
{
Storage *storage = getStorage();
for (int i = 0; i < MAX_SCORES; i++) {
int score = storage->get(L"top_score_" + toString(i), -1);
if (score < 0)
break;
std::wstring name = storage->get(L"top_name_" + toString(i), L"");
add(name, score);
}
modifed = false;
}
TopScores::~TopScores()
{
save();
}
int TopScores::add(const std::wstring &name, int score)
{
if (score >= getMaxScore() || (scores.size() < 1)) {
if (! isFull()) {
Entry e = { name, score };
scores.push_back(e);
modifed = true;
return scores.size() - 1;
}
return -1;
}
int pos = 0;
for (ScoresList::iterator i = scores.begin(); i != scores.end(); i++) {
Entry &e = *i;
if (e.score > score) {
Entry ne = { name, score };
scores.insert(i, ne);
modifed = true;
break;
}
pos++;
}
while (scores.size() > MAX_SCORES) {
modifed = true;
scores.erase(--scores.end());
}
return modifed ? pos : -1;
}
void TopScores::save()
{
if (! modifed)
return;
Storage *storage = getStorage();
int no = 0;
for (ScoresList::iterator i = scores.begin(); i != scores.end(); i++) {
Entry &e = *i;
storage->set(L"top_name_" + toString(no), e.name);
storage->set(L"top_score_" + toString(no), e.score);
no++;
}
storage->flush();
modifed = false;
}
TopScores::ScoresList& TopScores::getScores()
{
return scores;
}
int TopScores::getMaxScore()
{
if (scores.size() < 1)
return -1;
ScoresList::iterator i = scores.end();
i--;
return (*i).score;
}
class ScoresWindow: public Window
{
public:
ScoresWindow(int x, int y, TopScores *scores, int highlight);
};
ScoresWindow::ScoresWindow(int x, int y, TopScores *scores, int highlight):
Window(x, y, 320, 350, L"blue.bmp")
{
Font titleFont(L"nova.ttf", 26);
Font entryFont(L"laudcn2.ttf", 14);
Font timeFont(L"luximb.ttf", 14);
std::wstring txt = msg(L"topScores");
int w = titleFont.getWidth(txt);
titleFont.draw(background, (320 - w) / 2, 15, 255,255,0, true, txt);
TopScores::ScoresList &list = scores->getScores();
int no = 1;
int pos = 70;
for (TopScores::ScoresList::iterator i = list.begin();
i != list.end(); i++)
{
TopScores::Entry &e = *i;
std::wstring s(toString(no) + L".");
int w = entryFont.getWidth(s);
int c = ((no - 1) == highlight) ? 0 : 255;
entryFont.draw(background, 30 - w, pos, 255,255,c, true, s);
SDL_Rect rect = { 40, pos-20, 180, 40 };
SDL_SetClipRect(background, &rect);
entryFont.draw(background, 40, pos, 255,255,c, true, e.name);
SDL_SetClipRect(background, NULL);
s = secToStr(e.score);
w = timeFont.getWidth(s);
timeFont.draw(background, 305-w, pos, 255,255,c, true, s);
pos += 20;
no++;
}
}
void showScoresWindow(Area *parentArea, TopScores *scores, int highlight)
{
Area area;
Font font(L"laudcn2.ttf", 16);
area.add(parentArea);
area.add(new ScoresWindow(240, 125, scores, highlight));
ExitCommand exitCmd(area);
area.add(new Button(348, 430, 90, 25, &font, 255,255,0, L"blue.bmp",
msg(L"ok"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
area.run();
}
std::wstring enterNameDialog(Area *parentArea)
{
Area area;
Font font(L"laudcn2.ttf", 16);
area.add(parentArea);
area.add(new Window(170, 280, 460, 100, L"blue.bmp"));
Storage *storage = getStorage();
std::wstring name = storage->get(L"lastName", msg(L"anonymous"));
area.add(new Label(&font, 180, 300, 255,255,0, msg(L"enterName")));
area.add(new InputField(350, 300, 270, 26, L"blue.bmp", name, 20,
255,255,0, &font));
ExitCommand exitCmd(area);
area.add(new Button(348, 340, 90, 25, &font, 255,255,0, L"blue.bmp",
msg(L"ok"), &exitCmd));
area.add(new KeyAccel(SDLK_ESCAPE, &exitCmd));
area.add(new KeyAccel(SDLK_RETURN, &exitCmd));
area.run();
storage->set(L"lastName", name);
return name;
}

44
topscores.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef __TOPSCORES_H__
#define __TOPSCORES_H__
#include <list>
#include <string>
#include "widgets.h"
#define MAX_SCORES 10
class TopScores
{
public:
typedef struct {
std::wstring name;
int score;
} Entry;
typedef std::list<Entry> ScoresList;
private:
ScoresList scores;
bool modifed;
public:
TopScores();
~TopScores();
public:
int add(const std::wstring &name, int scores);
void save();
ScoresList& getScores();
int getMaxScore();
bool isFull() { return scores.size() >= MAX_SCORES; };
};
void showScoresWindow(Area *area, TopScores *scores, int highlightPos=-1);
std::wstring enterNameDialog(Area *area);
#endif

595
unicode.cpp Normal file
View File

@ -0,0 +1,595 @@
#include <wchar.h>
#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
#endif
#include "unicode.h"
#include "exceptions.h"
/// Returns length of wide character in utf-8
#define UTF8_LENGTH(Char) \
((Char) < 0x80 ? 1 : \
((Char) < 0x800 ? 2 : \
((Char) < 0x10000 ? 3 : \
((Char) < 0x200000 ? 4 : \
((Char) < 0x4000000 ? 5 : 6)))))
#define UTF8_COMPUTE(Char, Mask, Len) \
if (Char < 128) \
{ \
Len = 1; \
Mask = 0x7f; \
} \
else if ((Char & 0xe0) == 0xc0) \
{ \
Len = 2; \
Mask = 0x1f; \
} \
else if ((Char & 0xf0) == 0xe0) \
{ \
Len = 3; \
Mask = 0x0f; \
} \
else if ((Char & 0xf8) == 0xf0) \
{ \
Len = 4; \
Mask = 0x07; \
} \
else if ((Char & 0xfc) == 0xf8) \
{ \
Len = 5; \
Mask = 0x03; \
} \
else if ((Char & 0xfe) == 0xfc) \
{ \
Len = 6; \
Mask = 0x01; \
} \
else \
Len = -1;
#ifndef WIN32
static const char utf8_skip_data[256] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
};
const char * const g_utf8_skip = utf8_skip_data;
#define g_utf8_next_char(p) (char *)((p) + g_utf8_skip[*(unsigned char *)(p)])
#define UTF8_GET(Result, Chars, Count, Mask, Len) \
(Result) = (Chars)[0] & (Mask); \
for ((Count) = 1; (Count) < (Len); ++(Count)) \
{ \
if (((Chars)[(Count)] & 0xc0) != 0x80) \
{ \
(Result) = -1; \
break; \
} \
(Result) <<= 6; \
(Result) |= ((Chars)[(Count)] & 0x3f); \
}
/* Like g_utf8_get_char, but take a maximum length
* and return (wchar_t)-2 on incomplete trailing character
*/
static inline wchar_t
g_utf8_get_char_extended (const char *p,
size_t max_len)
{
unsigned int i, len;
wchar_t wc = (unsigned char) *p;
if (wc < 0x80)
{
return wc;
}
else if (wc < 0xc0)
{
return (wchar_t)-1;
}
else if (wc < 0xe0)
{
len = 2;
wc &= 0x1f;
}
else if (wc < 0xf0)
{
len = 3;
wc &= 0x0f;
}
else if (wc < 0xf8)
{
len = 4;
wc &= 0x07;
}
else if (wc < 0xfc)
{
len = 5;
wc &= 0x03;
}
else if (wc < 0xfe)
{
len = 6;
wc &= 0x01;
}
else
{
return (wchar_t)-1;
}
if (max_len >= 0 && len > max_len)
{
for (i = 1; i < max_len; i++)
{
if ((((unsigned char *)p)[i] & 0xc0) != 0x80)
return (wchar_t)-1;
}
return (wchar_t)-2;
}
for (i = 1; i < len; ++i)
{
wchar_t ch = ((unsigned char *)p)[i];
if ((ch & 0xc0) != 0x80)
{
if (ch)
return (wchar_t)-1;
else
return (wchar_t)-2;
}
wc <<= 6;
wc |= (ch & 0x3f);
}
if (UTF8_LENGTH(wc) != len)
return (wchar_t)-1;
return wc;
}
/**
* g_utf8_get_char:
* @p: a pointer to Unicode character encoded as UTF-8
*
* Converts a sequence of bytes encoded as UTF-8 to a Unicode character.
* If @p does not point to a valid UTF-8 encoded character, results are
* undefined. If you are not sure that the bytes are complete
* valid Unicode characters, you should use g_utf8_get_char_validated()
* instead.
*
* Return value: the resulting character
**/
wchar_t
g_utf8_get_char (const char *p)
{
int i, mask = 0, len;
wchar_t result;
unsigned char c = (unsigned char) *p;
UTF8_COMPUTE (c, mask, len);
if (len == -1)
return (wchar_t)-1;
UTF8_GET (result, p, i, mask, len);
return result;
}
/**
* g_utf8_to_ucs4:
* @str: a UTF-8 encoded string
* @len: the maximum length of @str to use. If @len < 0, then
* the string is nul-terminated.
* @items_read: location to store number of bytes read, or %NULL.
* If %NULL, then %G_CONVERT_ERROR_PARTIAL_INPUT will be
* returned in case @str contains a trailing partial
* character. If an error occurs then the index of the
* invalid input is stored here.
* @items_written: location to store number of characters written or %NULL.
* The value here stored does not include the trailing 0
* character.
* @error: location to store the error occuring, or %NULL to ignore
* errors. Any of the errors in #GConvertError other than
* %G_CONVERT_ERROR_NO_CONVERSION may occur.
*
* Convert a string from UTF-8 to a 32-bit fixed width
* representation as UCS-4. A trailing 0 will be added to the
* string after the converted text.
*
* Return value: a pointer to a newly allocated UCS-4 string.
* This value must be freed with g_free(). If an
* error occurs, %NULL will be returned and
* @error set.
**/
wchar_t *
g_utf8_to_ucs4 (const char *str,
long len,
long *items_read,
long *items_written,
wchar_t **error)
{
wchar_t *result = NULL;
int n_chars, i;
const char *in;
in = str;
n_chars = 0;
while ((len < 0 || str + len - in > 0) && *in)
{
wchar_t wc = g_utf8_get_char_extended (in, str + len - in);
if (wc & 0x80000000)
{
if (wc == (wchar_t)-2)
{
if (items_read)
break;
else
if (error)
*error = L"Partial character sequence at end of input";
}
else
if (error)
*error = L"Invalid byte sequence in conversion input";
goto err_out;
}
n_chars++;
in = g_utf8_next_char (in);
}
result = (wchar_t*)malloc((n_chars + 1) * sizeof(wchar_t));
in = str;
for (i=0; i < n_chars; i++)
{
result[i] = g_utf8_get_char (in);
in = g_utf8_next_char (in);
}
result[i] = 0;
if (items_written)
*items_written = n_chars;
err_out:
if (items_read)
*items_read = in - str;
return result;
}
/**
* g_unichar_to_utf8:
* @c: a ISO10646 character code
* @outbuf: output buffer, must have at least 6 bytes of space.
* If %NULL, the length will be computed and returned
* and nothing will be written to @outbuf.
*
* Converts a single character to UTF-8.
*
* Return value: number of bytes written
**/
int
g_unichar_to_utf8 (wchar_t c,
char *outbuf)
{
unsigned int len = 0;
int first;
int i;
if (c < 0x80)
{
first = 0;
len = 1;
}
else if (c < 0x800)
{
first = 0xc0;
len = 2;
}
else if (c < 0x10000)
{
first = 0xe0;
len = 3;
}
else if (c < 0x200000)
{
first = 0xf0;
len = 4;
}
else if (c < 0x4000000)
{
first = 0xf8;
len = 5;
}
else
{
first = 0xfc;
len = 6;
}
if (outbuf)
{
for (i = len - 1; i > 0; --i)
{
outbuf[i] = (c & 0x3f) | 0x80;
c >>= 6;
}
outbuf[0] = c | first;
}
return len;
}
/**
* g_ucs4_to_utf8:
* @str: a UCS-4 encoded string
* @len: the maximum length of @str to use. If @len < 0, then
* the string is terminated with a 0 character.
* @items_read: location to store number of characters read read, or %NULL.
* @items_written: location to store number of bytes written or %NULL.
* The value here stored does not include the trailing 0
* byte.
* @error: location to store the error occuring, or %NULL to ignore
* errors. Any of the errors in #GConvertError other than
* %G_CONVERT_ERROR_NO_CONVERSION may occur.
*
* Convert a string from a 32-bit fixed width representation as UCS-4.
* to UTF-8. The result will be terminated with a 0 byte.
*
* Return value: a pointer to a newly allocated UTF-8 string.
* This value must be freed with g_free(). If an
* error occurs, %NULL will be returned and
* @error set.
**/
char *
g_ucs4_to_utf8 (const wchar_t *str,
long len,
long *items_read,
long *items_written,
wchar_t **error)
{
int result_length;
char *result = NULL;
char *p;
int i;
result_length = 0;
for (i = 0; len < 0 || i < len ; i++)
{
if (!str[i])
break;
if ((unsigned)str[i] >= 0x80000000)
{
if (items_read)
*items_read = i;
if (error)
*error = L"Character out of range for UTF-8";
goto err_out;
}
result_length += UTF8_LENGTH (str[i]);
}
result = (char*)malloc (result_length + 1);
p = result;
i = 0;
while (p < result + result_length)
p += g_unichar_to_utf8 (str[i++], p);
*p = '\0';
if (items_written)
*items_written = p - result;
err_out:
if (items_read)
*items_read = i;
return result;
}
std::string toUtf8(const std::wstring &str)
{
long readed, writed;
wchar_t *errMsg = NULL;
char *res = g_ucs4_to_utf8(str.c_str(), str.length(), &readed,
&writed, &errMsg);
if (! res) {
if (errMsg)
throw Exception(errMsg);
else
throw Exception(L"Error converting text to UTF-8");
}
std::string s(res);
free(res);
return s;
}
std::wstring fromUtf8(const std::string &str)
{
long readed, writed;
wchar_t *errMsg = NULL;
wchar_t *res = g_utf8_to_ucs4(str.c_str(), str.length(), &readed,
&writed, &errMsg);
if (! res) {
if (errMsg)
throw Exception(errMsg);
else
throw Exception(L"Error converting text from UTF-8");
}
std::wstring s(res);
free(res);
return s;
}
#else
std::string toUtf8(const std::wstring &str)
{
if (! str.length())
return "";
int len = str.length();
int bufSize = (len + 1) * 6 + 1;
char buf[bufSize];
int res = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), len + 1,
buf, bufSize, NULL, NULL);
if (! res)
throw Exception(L"Error converting UCS-2 to UTF-8");
return buf;
}
std::wstring fromUtf8(const std::string &str)
{
if (! str.length())
return L"";
int len = str.length();
wchar_t buf[len + 1];
int res = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), len + 1,
buf, len + 1);
if (! res)
throw Exception(L"Error converting UTF-8 to UCS-2");
return buf;
}
std::string toOem(const std::wstring &str)
{
if (! str.length())
return "";
int len = str.length();
int bufSize = (len + 1) * 6 + 1;
char buf[bufSize];
int res = WideCharToMultiByte(CP_OEMCP, 0, str.c_str(), len + 1,
buf, bufSize, NULL, NULL);
if (! res)
throw Exception(L"Error converting UCS-2 to OEM");
return buf;
}
std::wstring fromOem(const std::string &str)
{
if (! str.length())
return L"";
int len = str.length();
wchar_t buf[len + 1];
int res = MultiByteToWideChar(CP_OEMCP, 0, str.c_str(), len + 1,
buf, len + 1);
if (! res)
throw Exception(L"Error converting OEM to UCS-2");
return buf;
}
#endif
std::wstring fromUtf8(const char *str, int len)
{
char *buf = (char*)malloc(len + 1);
if (! buf)
throw Exception(L"Error allocating memory");
memcpy(buf, str, len);
buf[len] = 0;
std::string s(buf);
free(buf);
return fromUtf8(s);
}
std::string toMbcs(const std::wstring &str)
{
int len = str.length();
if (! len)
return "";
else {
int maxSize = MB_CUR_MAX * len;
char buf[maxSize + 1];
size_t l = wcstombs(buf, str.c_str(), maxSize);
if ((size_t)-1 == -l) { // convert what we can
std::string res;
for (int i = 0; i < len; i++) {
int b = wctomb(buf, str[i]);
if (0 < b) {
buf[b] = 0;
res += buf;
}
}
return res;
} else {
buf[l] = 0;
return buf;
}
}
}
std::wstring fromMbcs(const std::string &str)
{
int maxLen = str.length();
wchar_t ws[maxLen + 1];
size_t cnt = mbstowcs(ws, str.c_str(), maxLen);
if (cnt == (size_t)-1) {
return L"";
}
ws[cnt] = 0;
return ws;
}
std::ostream& operator << (std::ostream &stream, const std::wstring &str)
{
#ifdef WIN32
if ((stream == std::cout) || (stream == std::cerr) ||
(stream == std::clog))
stream << toOem(str);
else
#endif
stream << toMbcs(str);
return stream;
}
int getUtf8Length(unsigned char c)
{
int mask, len;
UTF8_COMPUTE(c, mask, len);
if (-1 == len)
throw Exception(L"Invalid utf-8 character");
else
return len;
}

44
unicode.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef __UNICODE_H__
#define __UNICODE_H__
/// \file unicode.h
/// Definition of UNICODE handling rotinues.
#include <string>
#include <iostream>
/// Convert unicode string to multibyte string in UTF-8 encoding
/// \param str string in unicode
std::string toUtf8(const std::wstring &str);
/// Convert multibyte string in UTF-8 encoding to unicode string
/// \param str string in UTF-8 encoding
std::wstring fromUtf8(const std::string &str);
/// Convert multibyte string in UTF-8 encoding to unicode string
/// \param str string in UTF-8 encoding
/// \param len string length in bytes
std::wstring fromUtf8(const char *str, int len);
/// Convert unicode string to multibyte string in system default encoding
/// \param str string in unicode
std::string toMbcs(const std::wstring &str);
/// Convert unicode string to multibyte string in system default encoding
/// \param str string in unicode
std::wstring fromMbcs(const std::string &str);
/// Convert unicode string to default multibyte encoding
/// and write it to stream.
/// \param stream stream
/// \param str string to output
std::ostream& operator << (std::ostream &stream, const std::wstring &str);
/// Returns length of UTF-8 character in bytes by first UTF-8 character byte.
/// \param c first byte of UTF-8 character.
int getUtf8Length(unsigned char c);
#endif

419
utils.cpp Normal file
View File

@ -0,0 +1,419 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <math.h>
#include <wchar.h>
//#ifndef WIN32
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
//#endif
#include <fstream>
#include "utils.h"
#include "main.h"
#include "unicode.h"
#include "sound.h"
int getCornerPixel(SDL_Surface *surface)
{
SDL_LockSurface(surface);
int bpp = surface->format->BytesPerPixel;
Uint8 *p = (Uint8*)surface->pixels;
int pixel = 0;
switch (bpp) {
case 1: pixel = *p; break;
case 2: pixel = *(Uint16 *)p; break;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
pixel = p[0] << 16 | p[1] << 8 | p[2];
else
pixel = p[0] | p[1] << 8 | p[2] << 16;
break;
case 4: pixel = *(Uint32 *)p; break;
default: pixel = 0; /* shouldn't happen, but avoids warnings */
}
SDL_UnlockSurface(surface);
return pixel;
}
SDL_Surface* loadImage(const std::wstring &name, bool transparent)
{
int size;
void *bmp;
bmp = resources->getRef(name, size);
if (! bmp)
throw Exception(name + L" is not found");
SDL_RWops *op = SDL_RWFromMem(bmp, size);
SDL_Surface *s = SDL_LoadBMP_RW(op, 0);
SDL_FreeRW(op);
resources->delRef(bmp);
if (! s)
throw Exception(L"Error loading " + name);
SDL_Surface *screenS = SDL_DisplayFormat(s);
SDL_FreeSurface(s);
if (! screenS)
throw Exception(L"Error translating to screen format " + name);
if (transparent)
SDL_SetColorKey(screenS, SDL_SRCCOLORKEY, getCornerPixel(screenS));
return screenS;
}
#ifdef WIN32
#include <sys/timeb.h>
struct timezone { };
int gettimeofday(struct timeval* tp, int* /*tz*/)
{
struct timeb tb;
ftime(&tb);
tp->tv_sec = tb.time;
tp->tv_usec = 1000*tb.millitm;
return 0;
}
int gettimeofday(struct timeval* tp, struct timezone* /*tz*/)
{
return gettimeofday(tp, (int*)NULL);
}
#endif
int gettimeofday(struct timeval* tp)
{
#ifdef WIN32
return gettimeofday(tp, (int*)NULL);
#else
struct timezone tz;
return gettimeofday(tp, &tz);
#endif
}
void drawWallpaper(const std::wstring &name)
{
SDL_Surface *tile = loadImage(name);
SDL_Rect src = { 0, 0, tile->w, tile->h };
SDL_Rect dst = { 0, 0, tile->w, tile->h };
for (int y = 0; y < screen.getHeight(); y += tile->h)
for (int x = 0; x < screen.getWidth(); x += tile->w) {
dst.x = x;
dst.y = y;
SDL_BlitSurface(tile, &src, screen.getSurface(), &dst);
}
SDL_FreeSurface(tile);
}
void setPixel(SDL_Surface *s, int x, int y, int r, int g, int b)
{
int bpp = s->format->BytesPerPixel;
Uint32 pixel = SDL_MapRGB(s->format, r, g, b);
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8*)s->pixels + y * s->pitch + x * bpp;
switch (bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
void getPixel(SDL_Surface *surface, int x, int y,
Uint8 *r, Uint8 *g, Uint8 *b)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to retrieve */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
Uint32 pixel;
switch (bpp) {
case 1: pixel = *p; break;
case 2: pixel = *(Uint16 *)p; break;
case 3:
if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
pixel = p[0] << 16 | p[1] << 8 | p[2];
else
pixel = p[0] | p[1] << 8 | p[2] << 16;
break;
case 4: pixel = *(Uint32 *)p; break;
default: pixel = 0; /* shouldn't happen, but avoids warnings */
}
SDL_GetRGB(pixel, surface->format, r, g, b);
}
static int gammaTable[256];
static double lastGamma = -1.0;
void adjustBrightness(SDL_Surface *image, int x, int y, double k)
{
if (lastGamma != k) {
for (int i = 0; i <= 255; i++) {
gammaTable[i] = (int)(255.0 * pow((double)i / 255.0, 1.0 / k) + 0.5);
if (gammaTable[i] > 255)
gammaTable[i] = 255;
}
lastGamma = k;
}
Uint8 r, g, b;
getPixel(image, x, y, &r, &g, &b);
setPixel(image, x, y, gammaTable[r], gammaTable[g], gammaTable[b]);
}
SDL_Surface* adjustBrightness(SDL_Surface *image, double k, bool transparent)
{
if (lastGamma != k) {
for (int i = 0; i <= 255; i++) {
gammaTable[i] = (int)(255.0 * pow((double)i / 255.0, 1.0 / k) + 0.5);
if (gammaTable[i] > 255)
gammaTable[i] = 255;
}
lastGamma = k;
}
SDL_Surface *s = SDL_DisplayFormat(image);
if (! s)
throw Exception(L"Error converting image to display format");
SDL_LockSurface(s);
Uint8 r, g, b;
for (int j = 0; j < s->h; j++)
for (int i = 0; i < s->w; i++) {
getPixel(s, i, j, &r, &g, &b);
setPixel(s, i, j, gammaTable[r], gammaTable[g], gammaTable[b]);
}
SDL_UnlockSurface(s);
if (transparent)
SDL_SetColorKey(s, SDL_SRCCOLORKEY, getCornerPixel(s));
return s;
}
class CenteredBitmap: public Widget
{
private:
SDL_Surface *tile;
int x, y;
public:
CenteredBitmap(const std::wstring &fileName) {
tile = loadImage(fileName);
x = (screen.getWidth() - tile->w) / 2;
y = (screen.getHeight() - tile->h) / 2;
};
virtual ~CenteredBitmap() {
SDL_FreeSurface(tile);
};
virtual void draw() {
screen.draw(x, y, tile);
screen.addRegionToUpdate(x, y, tile->w, tile->h);
};
};
void showWindow(Area *parentArea, const std::wstring &fileName)
{
Area area;
area.add(parentArea);
area.add(new CenteredBitmap(fileName));
area.add(new AnyKeyAccel());
area.run();
sound->play(L"click.wav");
}
bool isInRect(int evX, int evY, int x, int y, int w, int h)
{
return ((evX >= x) && (evX < x + w) && (evY >= y) && (evY < y + h));
}
std::wstring secToStr(int time)
{
int hours = time / 3600;
int v = time - hours * 3600;
int minutes = v / 60;
int seconds = v - minutes * 60;
wchar_t buf[50];
#ifdef WIN32
swprintf(buf, L"%02i:%02i:%02i", hours, minutes, seconds);
#else
swprintf(buf, 50, L"%02i:%02i:%02i", hours, minutes, seconds);
#endif
return buf;
}
void showMessageWindow(Area *parentArea, const std::wstring &pattern,
int width, int height, Font *font, int r, int g, int b,
const std::wstring &msg)
{
Area area;
int x = (screen.getWidth() - width) / 2;
int y = (screen.getHeight() - height) / 2;
area.add(parentArea);
area.add(new Window(x, y, width, height, pattern, 6));
area.add(new Label(font, x, y, width, height, Label::ALIGN_CENTER,
Label::ALIGN_MIDDLE, r, g, b, msg));
area.add(new AnyKeyAccel());
area.run();
sound->play(L"click.wav");
}
void drawBevel(SDL_Surface *s, int left, int top, int width, int height,
bool raised, int size)
{
double k, f, kAdv, fAdv;
if (raised) {
k = 2.6;
f = 0.1;
kAdv = -0.2;
fAdv = 0.1;
} else {
f = 2.6;
k = 0.1;
fAdv = -0.2;
kAdv = 0.1;
}
for (int i = 0; i < size; i++) {
for (int j = i; j < height - i - 1; j++)
adjustBrightness(s, left + i, top + j, k);
for (int j = i; j < width - i; j++)
adjustBrightness(s, left + j, top + i, k);
for (int j = i+1; j < height - i; j++)
adjustBrightness(s, left + width - i - 1, top + j, f);
for (int j = i; j < width - i - 1; j++)
adjustBrightness(s, left + j, top + height - i - 1, f);
k += kAdv;
f += fAdv;
}
}
//#ifndef WIN32
void ensureDirExists(const std::wstring &fileName)
{
std::string s(toMbcs(fileName));
struct stat buf;
if (! stat(s.c_str(), &buf)) {
if (! S_ISDIR(buf.st_mode))
unlink(s.c_str());
else
return;
}
#ifndef WIN32
mkdir(s.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
#else
mkdir(s.c_str());
#endif
}
/*#else
void ensureDirExists(const std::wstring &fileName)
{
PORT ME!
}
#endif*/
int readInt(std::istream &stream)
{
if (stream.fail())
throw Exception(L"Error reading string");
unsigned char buf[4];
stream.read((char*)buf, 4);
if (stream.fail())
throw Exception(L"Error reading string");
return buf[0] + buf[1] * 256 + buf[2] * 256 * 256 +
buf[3] * 256 * 256 * 256;
}
std::wstring readString(std::istream &stream)
{
std::string str;
char c;
if (stream.fail())
throw Exception(L"Error reading string");
c = stream.get();
while (c && (! stream.fail())) {
str += c;
c = stream.get();
}
if (stream.fail())
throw Exception(L"Error reading string");
return fromUtf8(str);
}
void writeInt(std::ostream &stream, int v)
{
unsigned char b[4];
int i, ib;
for (i = 0; i < 4; i++) {
ib = v & 0xFF;
v = v >> 8;
b[i] = ib;
}
stream.write((char*)&b, 4);
}
void writeString(std::ostream &stream, const std::wstring &value)
{
std::string s(toUtf8(value));
stream.write(s.c_str(), s.length() + 1);
}
int readInt(unsigned char *buf)
{
return buf[0] + buf[1] * 256 + buf[2] * 256 * 256 +
buf[3] * 256 * 256 * 256;
}

44
utils.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef __UTILS_H__
#define __UTILS_H__
#include <SDL.h>
#include <string>
#ifdef WIN32
#include <sys/time.h>
#endif
#include <iostream>
#include "resources.h"
#include "widgets.h"
SDL_Surface* loadImage(const std::wstring &name, bool transparent=false);
SDL_Surface* adjustBrightness(SDL_Surface *image, double k, bool transparent=false);
int gettimeofday(struct timeval* tp);
void drawWallpaper(const std::wstring &name);
void showWindow(Area *area, const std::wstring &fileName);
bool isInRect(int evX, int evY, int x, int y, int w, int h);
std::wstring numToStr(int no);
void adjustBrightness(SDL_Surface *image, int x, int y, double k);
std::wstring secToStr(int time);
void showMessageWindow(Area *area, const std::wstring &pattern,
int width, int height, Font *font, int r, int g, int b,
const std::wstring &msg);
int getCornerPixel(SDL_Surface *surface);
void getPixel(SDL_Surface *surface, int x, int y,
Uint8 *r, Uint8 *g, Uint8 *b);
void setPixel(SDL_Surface *s, int x, int y, int r, int g, int b);
void drawBevel(SDL_Surface *s, int left, int top, int width, int height,
bool raised, int size);
void ensureDirExists(const std::wstring &fileName);
int readInt(std::istream &stream);
std::wstring readString(std::istream &stream);
void writeInt(std::ostream &stream, int value);
void writeString(std::ostream &stream, const std::wstring &value);
/// Read 4-bytes integer from memory.
int readInt(unsigned char *buffer);
#endif

193
verthints.cpp Normal file
View File

@ -0,0 +1,193 @@
#include "verthints.h"
#include "main.h"
#include "utils.h"
#include "puzgen.h"
#include "sound.h"
#define TILE_NUM 15
#define TILE_GAP 4
#define TILE_X 12
#define TILE_Y 495
#define TILE_WIDTH 48
#define TILE_HEIGHT 48
VertHints::VertHints(IconSet &is, Rules &r): iconSet(is)
{
reset(r);
}
VertHints::VertHints(IconSet &is, Rules &rl, std::istream &stream): iconSet(is)
{
int qty = readInt(stream);
for (int i = 0; i < qty; i++) {
int no = readInt(stream);
numbersArr.push_back(no);
Rule *r = getRule(rl, no);
int excluded = readInt(stream);
if (excluded) {
excludedRules.push_back(r);
rules.push_back(NULL);
} else {
excludedRules.push_back(NULL);
rules.push_back(r);
}
}
showExcluded = readInt(stream);
int x, y;
SDL_GetMouseState(&x, &y);
highlighted = getRuleNo(x, y);
}
void VertHints::reset(Rules &r)
{
rules.clear();
excludedRules.clear();
numbersArr.clear();
int no = 0;
for (Rules::iterator i = r.begin(); i != r.end(); i++) {
Rule *rule = *i;
if (rule->getShowOpts() == Rule::SHOW_VERT) {
rules.push_back(rule);
excludedRules.push_back(NULL);
numbersArr.push_back(no);
}
no++;
}
showExcluded = false;
int x, y;
SDL_GetMouseState(&x, &y);
highlighted = getRuleNo(x, y);
}
void VertHints::draw()
{
for (int i = 0; i < TILE_NUM; i++)
drawCell(i, true);
}
void VertHints::drawCell(int col, bool addToUpdate)
{
int x = TILE_X + col * (TILE_WIDTH + TILE_GAP);
int y = TILE_Y;
Rule *r = NULL;
if (col < (int)rules.size()) {
if (showExcluded)
r = excludedRules[col];
else
r = rules[col];
}
if (r)
r->draw(x, y, iconSet, highlighted == col);
else {
screen.draw(x, y, iconSet.getEmptyHintIcon());
screen.draw(x, y + TILE_HEIGHT, iconSet.getEmptyHintIcon());
}
if (addToUpdate)
screen.addRegionToUpdate(x, y, TILE_WIDTH, TILE_HEIGHT*2);
}
bool VertHints::onMouseButtonDown(int button, int x, int y)
{
if (button != 3)
return false;
int no = getRuleNo(x, y);
if (no < 0) return false;
if (no < (int)rules.size()) {
if (showExcluded) {
Rule *r = excludedRules[no];
if (r) {
sound->play(L"whizz.wav");
rules[no] = r;
excludedRules[no] = NULL;
drawCell(no);
}
} else {
Rule *r = rules[no];
if (r) {
sound->play(L"whizz.wav");
rules[no] = NULL;
excludedRules[no] = r;
drawCell(no);
}
}
}
return true;
}
void VertHints::toggleExcluded()
{
showExcluded = !showExcluded;
draw();
}
bool VertHints::onMouseMove(int x, int y)
{
int no = getRuleNo(x, y);
if (no != highlighted) {
int old = highlighted;
highlighted = no;
if (isActive(old))
drawCell(old);
if (isActive(no))
drawCell(no);
}
return false;
}
int VertHints::getRuleNo(int x, int y)
{
if (! isInRect(x, y, TILE_X, TILE_Y, (TILE_WIDTH + TILE_GAP) * TILE_NUM,
TILE_HEIGHT * 2))
return -1;
x = x - TILE_X;
y = y - TILE_Y;
int no = x / (TILE_WIDTH + TILE_GAP);
if (no * (TILE_WIDTH + TILE_GAP) + TILE_WIDTH < x)
return -1;
return no;
}
bool VertHints::isActive(int ruleNo)
{
if ((ruleNo < 0) || (ruleNo >= (int)rules.size()))
return false;
Rule *r = showExcluded ? excludedRules[ruleNo] : rules[ruleNo];
return r != NULL;
}
void VertHints::save(std::ostream &stream)
{
int cnt = numbersArr.size();
writeInt(stream, cnt);
for (int i = 0; i < cnt; i++) {
writeInt(stream, numbersArr[i]);
writeInt(stream, rules[i] ? 0 : 1);
}
writeInt(stream, showExcluded ? 1 : 0);
}

41
verthints.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef __VERTHINTS_H__
#define __VERTHINTS_H__
#include <vector>
#include "iconset.h"
#include "puzgen.h"
#include "widgets.h"
class VertHints: public Widget
{
private:
IconSet &iconSet;
typedef std::vector<Rule*> RulesArr;
RulesArr rules;
RulesArr excludedRules;
std::vector<int> numbersArr;
bool showExcluded;
int highlighted;
public:
VertHints(IconSet &is, Rules &rules);
VertHints(IconSet &is, Rules &rules, std::istream &stream);
public:
virtual void draw();
void drawCell(int col, bool addToUpdate=true);
virtual bool onMouseButtonDown(int button, int x, int y);
void toggleExcluded();
int getRuleNo(int x, int y);
virtual bool onMouseMove(int x, int y);
bool isActive(int ruleNo);
void save(std::ostream &stream);
void reset(Rules &rules);
};
#endif

18
visitor.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __VISITOR_H__
#define __VISITOR_H__
/// Abstract visitor
template <typename T>
class Visitor
{
public:
virtual ~Visitor() { };
/// Called at every visit
virtual void onVisit(T &t) = 0;
};
#endif

1013
widgets.cpp Normal file

File diff suppressed because it is too large Load Diff

322
widgets.h Normal file
View File

@ -0,0 +1,322 @@
#ifndef __WIDGETS_H__
#define __WIDGETS_H__
#include <string>
#include <list>
#include <set>
#include <SDL.h>
#include "font.h"
class Command
{
public:
virtual ~Command() { };
virtual void doAction() = 0;
};
class Area;
class Widget
{
protected:
Area *area;
public:
virtual ~Widget() { };
public:
virtual bool onMouseButtonDown(int button, int x, int y) { return false; };
virtual bool onMouseButtonUp(int button, int x, int y) { return false; };
virtual bool onMouseMove(int x, int y) { return false; };
virtual void draw() { };
virtual void setParent(Area *a) { area = a; };
virtual bool onKeyDown(SDLKey key, unsigned char ch) { return false; };
virtual bool destroyByArea() { return true; };
};
class Button: public Widget
{
protected:
int left, top, width, height;
SDL_Surface *image, *highlighted;
bool mouseInside;
Command *command;
public:
Button(int x, int y, const std::wstring &name, Command *cmd=NULL,
bool transparent=true);
Button(int x, int y, int width, int height, Font *font,
int fR, int fG, int fB, int hR, int hG, int hB,
const std::wstring &text, Command *cmd=NULL);
Button(int x, int y, int width, int height, Font *font,
int r, int g, int b, const std::wstring &background,
const std::wstring &text, Command *cmd=NULL);
Button(int x, int y, int width, int height, Font *font,
int r, int g, int b, const std::wstring &background,
const std::wstring &text, bool bevel, Command *cmd=NULL);
virtual ~Button();
public:
virtual void draw();
void getBounds(int &left, int &top, int &width, int &height);
int getLeft() const { return left; };
int getTop() const { return top; };
int getWidth() const { return width; };
int getHeight() const { return height; };
virtual bool onMouseButtonDown(int button, int x, int y);
virtual bool onMouseMove(int x, int y);
void moveTo(int x, int y) { left = x; top = y; };
};
class KeyAccel: public Widget
{
protected:
SDLKey key;
Command *command;
public:
KeyAccel(SDLKey key, Command *command);
virtual bool onKeyDown(SDLKey key, unsigned char ch);
};
class TimerHandler
{
public:
virtual ~TimerHandler() { };
virtual void onTimer() = 0;
};
class Area: public Widget
{
private:
typedef std::list<Widget*> WidgetsList;
WidgetsList widgets;
std::set<Widget*> notManagedWidgets;
bool terminate;
Uint32 time;
TimerHandler *timer;
public:
Area();
virtual ~Area();
public:
void add(Widget *widget, bool manage=true);
void remove(Widget *widget);
void handleEvent(const SDL_Event &event);
void run();
void finishEventLoop();
virtual void draw();
void setTimer(Uint32 interval, TimerHandler *handler);
void updateMouse();
virtual bool destroyByArea() { return false; };
};
class ExitCommand: public Command
{
private:
Area &area;
public:
ExitCommand(Area &a): area(a) { }
virtual void doAction() {
area.finishEventLoop();
};
};
class AnyKeyAccel: public Widget
{
protected:
Command *command;
public:
AnyKeyAccel(); // use exit command by default
AnyKeyAccel(Command *command);
virtual ~AnyKeyAccel();
public:
virtual bool onKeyDown(SDLKey key, unsigned char ch);
virtual bool onMouseButtonDown(int button, int x, int y);
};
class Window: public Widget
{
protected:
int left, top, width, height;
SDL_Surface *background;
public:
Window(int x, int y, int w, int h, const std::wstring &background,
bool frameWidth=4, bool raised=true);
virtual ~Window();
public:
virtual void draw();
};
class Label: public Widget
{
public:
typedef enum {
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
} HorAlign;
typedef enum {
ALIGN_TOP,
ALIGN_MIDDLE,
ALIGN_BOTTOM
} VerAlign;
protected:
Font *font;
std::wstring text;
int left, top, width, height;
int red, green, blue;
HorAlign hAlign;
VerAlign vAlign;
bool shadow;
public:
Label(Font *font, int x, int y, int r, int g, int b,
std::wstring text, bool shadow=true);
Label(Font *font, int x, int y, int width, int height,
HorAlign hAlign, VerAlign vAlign, int r, int g, int b,
const std::wstring &text);
public:
virtual void draw();
};
class InputField: public Window, public TimerHandler
{
private:
std::wstring &text;
int maxLength;
int cursorPos;
int red, green, blue;
Font *font;
Uint32 lastCursor;
bool cursorVisible;
char lastChar;
Uint32 lastKeyUpdate;
public:
InputField(int x, int y, int w, int h, const std::wstring &background,
std::wstring &name, int maxLength, int r, int g, int b, Font *font);
~InputField();
public:
virtual void draw();
virtual void setParent(Area *a);
virtual void onTimer();
virtual bool onKeyDown(SDLKey key, unsigned char ch);
virtual bool onKeyUp(SDLKey key);
virtual void onCharTyped(unsigned char ch);
private:
void moveCursor(int pos);
};
class Checkbox: public Widget
{
protected:
int left, top, width, height;
SDL_Surface *image, *highlighted;
SDL_Surface *checkedImage, *checkedHighlighted;
bool &checked;
bool mouseInside;
public:
Checkbox(int x, int y, int width, int height, Font *font,
int r, int g, int b, const std::wstring &background,
bool &checked);
virtual ~Checkbox();
public:
virtual void draw();
void getBounds(int &left, int &top, int &width, int &height);
int getLeft() const { return left; };
int getTop() const { return top; };
int getWidth() const { return width; };
int getHeight() const { return height; };
virtual bool onMouseButtonDown(int button, int x, int y);
virtual bool onMouseMove(int x, int y);
void moveTo(int x, int y) { left = x; top = y; };
};
class Picture: public Widget
{
protected:
int left;
int top;
int width;
int height;
SDL_Surface *image;
bool managed;
public:
Picture(int x, int y, const std::wstring &name, bool transparent=true);
Picture(int x, int y, SDL_Surface *image);
virtual ~Picture();
public:
virtual void draw();
void moveX(const int newX);
void getBounds(int &l, int &t, int &w, int &h);
int getLeft() const { return left; };
int getTop() const { return top; };
int getWidth() const { return width; };
int getHeight() const { return height; };
};
class Slider: public Widget
{
private:
int left, top, width, height;
float &value;
SDL_Surface *background;
SDL_Surface *slider;
SDL_Surface *activeSlider;
bool highlight;
bool dragging;
int dragOffsetX;
public:
Slider(int x, int y, int width, int height, float &value);
virtual ~Slider();
public:
virtual void draw();
virtual bool onMouseButtonDown(int button, int x, int y);
virtual bool onMouseButtonUp(int button, int x, int y);
virtual bool onMouseMove(int x, int y);
private:
void createBackground();
void createSlider(int size);
int valueToX(float value);
float xToValue(int pos);
};
#endif