Initial release
This commit is contained in:
commit
a9f55d3076
68
Makefile
Normal file
68
Makefile
Normal 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
63
Makefile.macosx
Normal 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
49
Makefile.win
Normal 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
3
README
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
To build and install einstein edit Makefile and run `make install` as root.
|
||||
|
104
buffer.cpp
Normal file
104
buffer.cpp
Normal 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
79
buffer.h
Normal 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
|
||||
|
107
conf.h
Normal file
107
conf.h
Normal 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
69
convert.cpp
Normal 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
53
convert.h
Normal 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
367
descr.cpp
Normal 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, ¤tPage);
|
||||
nextCmd = new CursorCommand(1, *this, ¤tPage);
|
||||
}
|
||||
|
||||
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
12
descr.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __DESCR_H__
|
||||
#define __DESCR_H__
|
||||
|
||||
|
||||
#include "widgets.h"
|
||||
|
||||
|
||||
void showDescription(Area *parentArea);
|
||||
|
||||
|
||||
#endif
|
||||
|
BIN
einstein.res
Normal file
BIN
einstein.res
Normal file
Binary file not shown.
24
exceptions.h
Normal file
24
exceptions.h
Normal 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
117
font.cpp
Normal 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
32
font.h
Normal 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
187
formatter.cpp
Normal 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
63
formatter.h
Normal 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
569
game.cpp
Normal 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
58
game.h
Normal 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
207
horhints.cpp
Normal 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
41
horhints.h
Normal 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
430
i18n.cpp
Normal 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
58
i18n.h
Normal 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
57
iconset.cpp
Normal 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
32
iconset.h
Normal 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
248
lexal.cpp
Normal 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
66
lexal.h
Normal 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
113
main.cpp
Normal 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
16
main.h
Normal 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
201
menu.cpp
Normal 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
135
messages.cpp
Normal 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
67
messages.h
Normal 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
285
opensave.cpp
Normal 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
16
opensave.h
Normal 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
88
options.cpp
Normal 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
13
options.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef __OPTIONS_H__
|
||||
#define __OPTIONS_H__
|
||||
|
||||
|
||||
#include "widgets.h"
|
||||
|
||||
|
||||
|
||||
void showOptionsWindow(Area *area);
|
||||
|
||||
|
||||
#endif
|
||||
|
394
puzgen.cpp
Normal file
394
puzgen.cpp
Normal 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
81
puzgen.h
Normal 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
196
puzzle.cpp
Normal 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
44
puzzle.h
Normal 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
159
random.cpp
Normal 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
41
random.h
Normal 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
131
regstorage.cpp
Normal 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
32
regstorage.h
Normal 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
674
resources.cpp
Normal 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
413
resources.h
Normal 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
563
rules.cpp
Normal 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
309
screen.cpp
Normal 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
78
screen.h
Normal 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
62
sound.cpp
Normal 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
35
sound.h
Normal 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
48
storage.cpp
Normal 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
27
storage.h
Normal 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
57
streams.cpp
Normal 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
41
streams.h
Normal 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
434
table.cpp
Normal 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
84
table.h
Normal 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
59
tablestorage.cpp
Normal 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
32
tablestorage.h
Normal 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
72
tokenizer.cpp
Normal 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
61
tokenizer.h
Normal 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
175
topscores.cpp
Normal 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
44
topscores.h
Normal 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
595
unicode.cpp
Normal 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
44
unicode.h
Normal 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
419
utils.cpp
Normal 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
44
utils.h
Normal 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
193
verthints.cpp
Normal 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
41
verthints.h
Normal 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
18
visitor.h
Normal 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
1013
widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
322
widgets.h
Normal file
322
widgets.h
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user