blobby-1.0rc3/src/blobnet/adt/Queue.hpp0000644000175000017500000001147012042452372021330 0ustar danielknobedanielknobe/*============================================================================= blobNet Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #ifndef _QUEUE_HPP_ #define _QUEUE_HPP_ /* Includes */ #include #include namespace BlobNet { namespace ADT { /*! \class Queue \brief ADT for a Queue with some extra functionality \note This class needs a cleanup. There are some dummy methods */ template class Queue { public: /// @brief constructor, creates an queue Queue(); /// @brief deconstructor, destroys an queue ~Queue(); /// @brief constructor, creates a copy of an queue /// @param originalCopy The queue which will be copied Queue(const Queue& originalCopy); const QueueType& operator[] (unsigned int position) const; QueueType& operator[] (unsigned int position); bool operator= (const Queue& original_copy); /// @brief Count of elements in the queue /// @return Count of elements inline const unsigned int size() const; /// @brief Adds an element to the queue /// @param input Element to add void push(const QueueType& input); /// @brief Adds an element to the head of the queue /// @param input Element to add void pushAtHead(const QueueType& input); /// @brief Pops the first element of queue. Check if queue is not empty before. /// @return First element of queue inline const QueueType pop(); /// @brief Deletes all elements of the array inline void clear(); /// @brief Reorganizes the queue. Really dump implemented at the moment void compress(); /// @brief Checks if element is in queue or not /// @param q Element bool find(QueueType q); /// @brief Returns first element of queue. Really dump implemented at the moment /// @return First element inline const QueueType peek() const; /// @brief Deletes an element. This is not very fast /// @param position Index of element void del(unsigned int position); /// @brief Reallocates the queue with no elements /// @param size Size of internal array size void clearAndForceAllocation(int size); private: typedef std::deque ContainerType; ContainerType array; }; template Queue::Queue() { } template Queue::~Queue() { } template Queue::Queue(const Queue& original_copy) { this->array = original_copy->array; } template inline const QueueType& Queue::operator[] (unsigned int position) const { return this->array.at(position); } template inline QueueType& Queue::operator[] (unsigned int position) { return this->array.at(position); } template bool Queue::operator= (const Queue& original_copy) { return this->array = original_copy->array; } template inline const unsigned int Queue::size() const { return this->array.size(); } template void Queue::push(const QueueType& input) { this->array.push_back(input); } template void Queue::pushAtHead(const QueueType& input) { this->array.push_front(input); } template inline const QueueType Queue::pop() { QueueType tmp = this->array.front(); this->array.pop_front(); return tmp; } template inline void Queue::clear() { this->array.clear(); } template void Queue::compress() { } template bool Queue::find(QueueType q) { typename ContainerType::iterator it; it = std::find(this->array.begin(), this->array.end(), q); return it != this->array.end(); } template inline const QueueType Queue::peek( void ) const { return this->array.front(); } template void Queue::del(unsigned int position) { this->array.erase(this->array.begin() + position); } template void Queue::clearAndForceAllocation(int size) { return this->array.clear(); } } } #endif blobby-1.0rc3/src/blobnet/layer/Http.cpp0000644000175000017500000001040212042452372021514 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "Http.hpp" /* includes */ #include #include #include #include #include #include namespace BlobNet { namespace Layer { /* implementation */ Http::Http(const std::string& hostname, const int& port) : mHostname(hostname) , mPort(port) { } Http::~Http() { } void Http::request(const std::string& path, std::stringstream& response) { // Create TCP/IP Socket SOCKET inOutSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(inOutSocket == INVALID_SOCKET) { throw Exception::HttpException("Can't create HTTP-Socket."); } // Connect to the host const char* ipAddress = mSocketLayer.nameToIP(mHostname.c_str()); if(ipAddress == NULL) { throw Exception::HttpException("Can't resolve IP Address."); } if(mSocketLayer.Connect(inOutSocket, inet_addr(ipAddress), mPort) == -1) { throw Exception::HttpException("Can't connect to host."); }; // Message for a simple request std::string request = "GET /" + path + " HTTP/1.1\r\nHost: " + mHostname + "\r\n\r\n"; // Write the whole message int bytesSend = 0; do { bytesSend += mSocketLayer.Write(inOutSocket, request.c_str(), request.size()); if(bytesSend == -1) { throw Exception::HttpException("Can't send the request to host."); } } while(bytesSend < request.size()); // Read the header of the response and extract content size std::stringstream header; readHeader(inOutSocket, header); int contentSize = getContentSize(header); // Read the body readBody(inOutSocket, response, contentSize); close(inOutSocket); } void Http::readHeader(int inOutSocket, std::stringstream& response) { // Parser variables State state = something; bool done = false; // Read header for(char c; (!done) && (recv(inOutSocket, &c, 1, 0) > 0); response << c) { switch(c) { case '\r': if(state == something) { state = cr; break; } if(state == crlf) { state = crlfcr; break; } state = error; done = true; break; case '\n': if(state == cr) { state = crlf; break; } if(state == crlfcr) { state = headerDone; return; } state = error; done = true; break; default: state = something; break; } } throw Exception::HttpException("Can't read response."); } void Http::readBody(int inOutSocket, std::stringstream& response, int contentSize) { // Parser variables int counter = 0; State state = something; // Read body for(char c; recv(inOutSocket, &c, 1, 0) > 0; response << c) { counter += 1; if(counter == contentSize) { return; } } throw Exception::HttpException("Can't read response."); } int Http::getContentSize(std::stringstream& response) { // Parser variables std::string token; State state = something; // Data variables int contentSize = -1; // Parser while(response >> token) { switch(state) { case something: if(token == "Content-Length:") { state = contentlength; } break; case contentlength: state = headerDone; return atoi(token.c_str()); } } throw Exception::HttpException("Can't get contentsize of http response."); } } } blobby-1.0rc3/src/blobnet/layer/Http.hpp0000644000175000017500000000372212042452372021530 0ustar danielknobedanielknobe/*============================================================================= blobNet Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #ifndef _BLOBNET_LAYER_HTTP_HPP_ #define _BLOBNET_LAYER_HTTP_HPP_ /* Includes */ #include "../exception/HttpException.hpp" #include "../../raknet/SocketLayer.h" #include namespace BlobNet { namespace Layer { /*! \class Http \brief Simple Layer for HTTP communication over TCP/IP */ class Http { public: /// @brief constructor, connects to an host Http(const std::string& hostname, const int& port); /// @brief deconstructor, closes connection to host ~Http(); /// @brief sends a request /// @param path Path to the requested file /// @param response Stringstream where the response will pushed in void request(const std::string& path, std::stringstream& response); private: void readHeader(int inOutSocket, std::stringstream& response); void readBody(int inOutSocket, std::stringstream& response, int contentSize); int getContentSize(std::stringstream& response); std::string mHostname; int mPort; SocketLayer mSocketLayer; enum State { cr, crlf, crlfcr, headerDone, contentlength, something, error }; }; } } #endif blobby-1.0rc3/src/blobnet/exception/HttpException.hpp0000644000175000017500000000254612042452372024274 0ustar danielknobedanielknobe/*============================================================================= blobNet Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #ifndef _BLOBNET_EXCEPTION_HTTPEXCEPTION_HPP_ #define _BLOBNET_EXCEPTION_HTTPEXCEPTION_HPP_ /* Includes */ #include namespace BlobNet { namespace Exception { /*! \class HttpException \brief Runtimeexception for HttpErrors */ class HttpException : public std::runtime_error { public: /// @brief constructor /// @param message Message to describe the error HttpException(const std::string& message) : std::runtime_error(message) { }; }; } } #endif blobby-1.0rc3/data/gfx/font_small/font10.bmp0000644000175000017500000000036612042452376022217 0ustar danielknobedanielknobeBM6( ```VUV$`^`ZPPP_\` _  efez{zpÛK0L rp !+!yw  +@  Z' ' \,[oMnCt^-Js}iA:bWHQY0blobby-1.0rc3/data/gfx/font_small/font31.bmp0000644000175000017500000000036612042452376022222 0ustar danielknobedanielknobeBM6( *,*!'!R_R'4'vv  .O-ut1i2$k#..IL / Y -6B (-]40SR9{Ga1blobby-1.0rc3/data/gfx/font_small/font13.bmp0000644000175000017500000000036612042452376022222 0ustar danielknobedanielknobeBM6( }&{idH>.$)'b|,kUscaO~T*M861oS/3(.k{e)4#;*YR9-" C4U/ Q # # 3 -(H#slSKKA-c"blobby-1.0rc3/data/gfx/font_small/font14.bmp0000644000175000017500000000036612042452376022223 0ustar danielknobedanielknobeBM6( /Y+h M@5*F6M,1E)58), M9% EN#> 9P.kJCF[)MW$KV'S^ ]W?izWoV&blobby-1.0rc3/data/gfx/font_small/font32.bmp0000644000175000017500000000036612042452376022223 0ustar danielknobedanielknobeBM6( 4+  *I 1QCX pO k#,F0z x,-%$.` /kZGG,.NUB'1 0]]9 LP > &C ""<((m Oblobby-1.0rc3/data/gfx/font_small/font50.bmp0000644000175000017500000000036612042452376022223 0ustar danielknobedanielknobeBM6( 2ERnCp /#/((  blobby-1.0rc3/data/gfx/font_small/font15.bmp0000644000175000017500000000036612042452376022224 0ustar danielknobedanielknobeBM6( 'xc/+RQ/eV f:  ze`<2?/,G86;/.  xfbǠmjqo{jh O@@ԩoo1blobby-1.0rc3/data/gfx/font_small/font33.bmp0000644000175000017500000000036612042452376022224 0ustar danielknobedanielknobeBM6(   ,`.T U 9l;*'n 2Y3,+ 3; 7b;H"9OW^CZ O8Fl%>Jv blobby-1.0rc3/data/gfx/font_small/font51.bmp0000644000175000017500000000036612042452376022224 0ustar danielknobedanielknobeBM6( blobby-1.0rc3/data/gfx/font_small/font16.bmp0000644000175000017500000000036612042452376022225 0ustar danielknobedanielknobeBM6( M5&M#&l2<v?>QQ}rr}|^VMzggA43B30XH<|Q $V;7eTJkoM#7Q88TK%2J5|["blobby-1.0rc3/data/gfx/font_small/font34.bmp0000644000175000017500000000036612042452376022225 0ustar danielknobedanielknobeBM6( ,< 7Ko/k +i5..KN8FJX7Oa%#Ubz:^#vp:~B$ [([  Pa%EXs 9blobby-1.0rc3/data/gfx/font_small/font52.bmp0000644000175000017500000000036612042452376022225 0ustar danielknobedanielknobeBM6( )*')  ~3Ybɸfa1 2blobby-1.0rc3/data/gfx/font_small/font53.bmp0000644000175000017500000000036612042452376022226 0ustar danielknobedanielknobeBM6( bbbKKK,,,@AA ABU]Y3 5XvbCzL-. [h ( JO+/blobby-1.0rc3/data/gfx/font_small/font35.bmp0000644000175000017500000000036612042452376022226 0ustar danielknobedanielknobeBM6( g$$ {"#'3UX U 4= Y%6_6o ,= 0R C($>:^Ht~,!vv}T34d%f[]ruY \!8:,.\blobby-1.0rc3/data/gfx/font_small/font00.bmp0000644000175000017500000000036612042452376022216 0ustar danielknobedanielknobeBM6( F^FCA41X`ba\x\)PMpp1B1 KH [m~m;Q;(0,NVNuu31 G blobby-1.0rc3/data/gfx/font_small/font03.bmp0000644000175000017500000000036612042452376022221 0ustar danielknobedanielknobeBM6( 45Bnttzdi02V FGV01<QRyJJ++Z("f ? :  704-_C %$9YT#t h;1 ?XE_U`ZC?! pblobby-1.0rc3/data/gfx/font_small/font21.bmp0000644000175000017500000000036612042452376022221 0ustar danielknobedanielknobeBM6( DNEPhT'E//d:j)awi=PB2J8 caeqivzZ}c4f!!]TY\9(:\blobby-1.0rc3/data/gfx/font_small/font04.bmp0000644000175000017500000000036612042452376022222 0ustar danielknobedanielknobeBM6( Q 1> *#   6&b_+(# W!XW+, Yvv68no#$c MMblobby-1.0rc3/data/gfx/font_small/font22.bmp0000644000175000017500000000036612042452376022222 0ustar danielknobedanielknobeBM6( MB7#)}-s.KP)6"+]77 T .4,d7`'((7HR><FG,^/-C1;p:# !][&G&  B? KyJ5b4A?  WaW5<5blobby-1.0rc3/data/gfx/font_small/font40.bmp0000644000175000017500000000036612042452376022222 0ustar danielknobedanielknobeBM6( ]Z%%(( j7 (DC'#0/7A6N20}R3.S9I:N9|yMr>  |\0%\GgC  jM~~@@D5 f`Sp)tblobby-1.0rc3/data/gfx/font_small/font23.bmp0000644000175000017500000000036612042452376022223 0ustar danielknobedanielknobeBM6( 8 E99o((R 99 4nm!!iCC!!VRHL,,?" 3I=?_L&31 38+RQ*1.R=8]7blobby-1.0rc3/data/gfx/font_small/font05.bmp0000644000175000017500000000036612042452376022223 0ustar danielknobedanielknobeBM6( > ',- 6{<)Z2+I0@XR5:ut*< &"0 =xMATR  (i ,Kg+W -3K2_k&   ^c=B-3 Nblobby-1.0rc3/data/gfx/font_small/font41.bmp0000644000175000017500000000036612042452376022223 0ustar danielknobedanielknobeBM6( no5 !c8Y PO,zh\ /JE>1B% Op*   H0.O62yMadD4 ! yN#6R.0% ! 8 ofF7$sblobby-1.0rc3/data/gfx/font_small/font24.bmp0000644000175000017500000000036612042452376022224 0ustar danielknobedanielknobeBM6( v2"-$13RHMT Gb`?-- a)(9zo ''HCCs* 1ggAAqycD2H pn%!&/#0a_e^S]blobby-1.0rc3/data/gfx/font_small/font06.bmp0000644000175000017500000000036612042452376022224 0ustar danielknobedanielknobeBM6( 4#!2 ;p$ 4T:JQ"T,EOFNv #0"'\h#p0)Z$4+oɊ%_A(_%fz!AMe?\blobby-1.0rc3/data/gfx/font_small/font42.bmp0000644000175000017500000000036612042452376022224 0ustar danielknobedanielknobeBM6( B>OMpoCBR%6tg ~}`\a*!1b9/0x -&'cd]<]NVXPwJy$W"*,V1@Ca'/?;!(V[!#blobby-1.0rc3/data/gfx/font_small/font25.bmp0000644000175000017500000000036612042452376022225 0ustar danielknobedanielknobeBM6( %QOop+0iAm~+BH J,-F*Ύ+B?@??*  ''T ;jhjKJ"}< :423nIm@+>Qv>q#2g EA{8u-$E4/ S$=J^JTN<:"=tT:D"  [m=y?Z;Pblobby-1.0rc3/data/gfx/font_small/font08.bmp0000644000175000017500000000036612042452376022226 0ustar danielknobedanielknobeBM6( K5aYjYH+J y>o9 B4Cbh !<!gDhzF|M`b7 H "BE@d4f[.UâB D KHB@( )blobby-1.0rc3/data/gfx/font_small/font44.bmp0000644000175000017500000000036612042452376022226 0ustar danielknobedanielknobeBM6( UzT @O?P~Oblobby-1.0rc3/data/gfx/font_small/font27.bmp0000644000175000017500000000036612042452376022227 0ustar danielknobedanielknobeBM6( ) t_ f+ " b(8+ 0<-i a)+  c ?E(,7'?I  .1^  LWJk9zC-{blobby-1.0rc3/data/gfx/font_small/font09.bmp0000644000175000017500000000036612042452376022227 0ustar danielknobedanielknobeBM6( "GJG9Q9  6F5dc(_&utc_)/(ŏ YXSQvu87   1-# ec308 /<_;MJIE:6"blobby-1.0rc3/data/gfx/font_small/font45.bmp0000644000175000017500000000036612042452376022227 0ustar danielknobedanielknobeBM6(  $"$)&)blobby-1.0rc3/data/gfx/font_small/font28.bmp0000644000175000017500000000036612042452376022230 0ustar danielknobedanielknobeBM6( z y- zf#QyTPD_ CX)`+.5<&%NCD+~3" Q K%#.&/ 6#&3]n .gAGvT 1\DB73)y2\y)bA*=GNeǜ;blobby-1.0rc3/data/gfx/font_small/font46.bmp0000644000175000017500000000036612042452376022230 0ustar danielknobedanielknobeBM6( N9;3!3v0yXZe%h3 5blobby-1.0rc3/data/gfx/font_small/font29.bmp0000644000175000017500000000036612042452376022231 0ustar danielknobedanielknobeBM6( W^ 23??h6 :< HKI S!%O k "1G g,CDaLb@V5F$1 blobby-1.0rc3/data/gfx/font_small/font47.bmp0000644000175000017500000000036612042452376022231 0ustar danielknobedanielknobeBM6( HG  _]].+-/W.@=F4;7L]}795 2 PM%k#-+b`_^blobby-1.0rc3/data/gfx/font_small/font48.bmp0000644000175000017500000000036612042452376022232 0ustar danielknobedanielknobeBM6( xzx(*(,,,TXTblobby-1.0rc3/data/gfx/font_small/font49.bmp0000644000175000017500000000036612042452376022233 0ustar danielknobedanielknobeBM6( bbbKKK,,,@AA U]YXvbCzL [h ( JO+/blobby-1.0rc3/data/scripts/old/com_10B2ex.lua0000644000175000017500000001136712042452377022241 0ustar danielknobedanielknobe-- Com 1.0 extrem Beta 2 Schmetter-Mod -- by Oreon, Axji & Enormator -- Name: com_10eB2_Schmettermod -- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln -- Weltkonstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 323 -- Ber�hrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS -- Charakter CONST_ANGRIFFSGRUNDWERT_MIN = 30 CONST_ANGRIFFSGRUNDWERT_MAX = 55 MIN_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MIN MAX_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MAX ANGRIFFSEINSCHRAENKUNG_HINTEN = 10 -- sonstige Einstellungen servexVersetzung=-7 --Wert ist so gewaehlt, dass der Ball nah ans Netz fliegt, der Gegner ihn aber grade nicht erreichen kann -- ***ANFANG*** function OnOpponentServe() moveto(130) end function OnServe(ballready) servex=ballx()+servexVersetzung naechsterBallSchmettern = true generatenaechsterBallSchmettern() moveto(servex) if ballready and (servex-2 < posx()) and (posx() < servex+2) then jump() end end function OnGame() target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,1) --X Ziel in Blobbyhoehe targets = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,2) --X Richtung (-1 oder 1) bei Einschlag targetNetz = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_NETZ_HOEHE,1) --X Ziel in Netzhoehe (Netzrollerberechnung) targetJump = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,1) --X Ziel in Schmetterhoehe naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht if (ballx() > CONST_NETZ_RECHTS) then --Wenn Ball auf rechter Spielfeldseite dann generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen end if (target > CONST_MITTE) and (ballx() > CONST_NETZ_RECHTS) then --Wenn der Ball mich nix angeht moveto(135) --Dann auf Standartposition warten else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then if ((math.abs(bspeedx()) < 4) or (estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,2) < 0)) then sprungattacke(angriffsstaerke) else if (targetJump < CONST_MITTE / 2) then sprungattacke(-35) --an Rueckwand spielen else sprungattacke(0) --weiterleiten end end return end moveto(target) end end function sprungattacke(p_angriffsstaerke) p_angriffsstaerke=math.max(p_angriffsstaerke, MIN_ANGRIFFSSTAERKE + ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so hoch spielen (kommt nicht auf die andere Seite) p_angriffsstaerke=math.min(p_angriffsstaerke, MAX_ANGRIFFSSTAERKE - ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so tief spielen (kommt ans Netz) moveto(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die St�rke des gew�nschten schlages angegeben if (bally() < 580) and (bspeedy() < 0) then jump() end end function naechsterBallSchmetternFlagTesten() naechsterBallSchmettern = true end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) end function estimImpact(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding n�tigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx()/math.abs(bspeedx()) if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = math.abs(resultX - CONST_BALL_RADIUS) + CONST_BALL_RADIUS estimbspeedx=-estimbspeedx KollisionLinks = true else KollisionLinks = false end if (resultX > CONST_NETZ_RECHTS) and (estimbspeedx > 0) and ((KollisionLinks == true) or (ballx() < CONST_NETZ_LINKS)) then -- Abpraller am Netz unterhalb der Kugel erst wenn Netzroller ausgeschlossen sind resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end if (Frage == 1) then return resultX end if (Frage == 2) then return estimbspeedx end endblobby-1.0rc3/data/scripts/old/hyperion03.lua0000644000175000017500000001006612042452377022435 0ustar danielknobedanielknobe-- Hyperion v0.3 by Hyperion -- Ein paar Ideen stammen von anderen Bots, z.B. vom Combot -- Playtesting hauptsächlich gegen Combot und Dumpjump -- Bot hat noch einige Schwierigkeiten bei Netzbällen, der Aufschlag ist zu variabel und dadurch nicht gut genug -- Die Spielweise ist nicht berechnet, sondern durch Playtesting entstanden -- Wird sich vielleicht bei der nächsten Version ändern, ich habe allerdings noch keine echte Schwäche gefunden, deswegen bin ich ein bisschen unmotiviert. -- Kann noch nicht blocken und macht ab und zu Fehlaufschläge. -- Hat in meinen Tests gegen alle anderen Bots gewonnen (und gegen mich leider auch). Verliert zwar ab und zu durch Netzroller, aber ich habs nicht geschafft zwei Punkte hintereinander zu machen. -- Der Code ist stellenweise ziemlich schlecht, ich weiß. -- [Die Dimensionen der Blobbywelt] field_width = 800 field_middle = 400 ball_radius = 31.5 net_radius = 7 net_height = 323 -- Die Kugel am Netz mit eingeschlossen, das ist im Sourcecode falsch dokumentiert blobby_groesse = 89 blobby_head = 25 blobby_bottom_radius = 33 blobby_center = 100+89/2 ground = 100 plane = 220.5 -- [Die Physik in der Blobbywelt] -- Die Zeit wird in steps=t gemessen, ein step entspricht dabei einen Frame, die Geschwindigkeit ist also von der Framerate abhängig (die Spielgeschwindigkeit wird auch über die Framerate gesteuert!!! ball_grav = 0.28 -- Der Ball fällt also langsamer als der Blobby auf die Erde zurück blobby_grav = 0.44 --eigentlich 0.88 aber wenn man sprungtaste gedrückt hält dann so, da es ohne sinnlos ist, kümmern wir uns nicht um die andere blobby_speed = 4.5 blobby_jump_speed = 14.5 -- [Hilfsfunktionen] function timetoy(y) -- Funktion arbeitet korrekt, driftet nur t=0.001 pro step a=ball_grav v=bspeedy() y0=bally() t=(v/a-1/10+math.sqrt(v^2/a^2-v/(5*a)+1/100+2*(y0-y)/a)) return t end function timetox(x) v=bspeedx() x0=ballx() t=(x-x0)/v return t end function ypos(t) y=bally()+(bspeedy()-(ball_grav/10))*t-(1/2)*ball_grav*t^2 return y end function estimatef(height) x=ballx()+bspeedx()*timetoy(height) return x end function estimate(y) x=estimatef(y) if (x>ball_radius) and (x<361.5) then impact=x end if (x361.5) then if (ballx()<361.5) then if(ypos(timetox(361.5))3) then moveto(x-40) end if (timetoy(333)<10) then jump() end else x=estimate(333) if (math.abs((x-20)-posx())>3) then moveto(x-20) end if (timetoy(333)<10) then jump() end end end function ueberspielen() if (posx()<280) then x=estimate(437) if (math.abs((x-30)-posx())>3) then moveto(x-30) end if (timetoy(437)<24) then jump() end else x=estimate(437) if (math.abs((x-15)-posx())>3) then moveto(x-15) end if (timetoy(437)<24) then jump() end end end function schmettern() x=estimate(437) t=timetoy(437) if (t>24) and (math.abs((x-90)-posx())>3) then moveto(x-90) end if (t<24) then jump() end if (t<5) then right() end end -- [Hauptprogramm] function OnServe(ballready) if (new==nil) then new=1 end if (new==1) then j=math.random() p=math.random(30,55) new=2 end if (j>0.5) then if (math.abs(posx()-200)>3) then moveto(200) else jump() end else if (math.abs(posx()-(200-p))>3) then moveto(200-p) else jump() end end end function OnOpponentServe() moveto(200) end function OnGame() new=1 x=estimate(220.5) if (x<420) then if (touches()==0) then moveto(x) newp=1 end if (touches()==1) then if (newp==1) then p1=math.random() newp=2 end if (oppx()>300) then if(p1>0.4) then ueberspielen() else schmettern() end else if(p1>0.6) then ueberspielen() else schmettern() end end end if (touches()==2) then retten() end else moveto(200) end end blobby-1.0rc3/data/scripts/old/bert.ai0000644000175000017500000000340612042452377021201 0ustar danielknobedanielknobe// Note: This still needs to be ported to lua var bsizx,bsizy,cpud1; function main() { var i,alti,j,factor,direct, touchcnt,x,y,delay,bx,by,vbx,vby; bsizx=64; bsizy=64; j=side(); factor=j*2-1; delay=0; while (1) { stop(); stopjump(); bx=ballx(); by=bally(); vbx=bspeedx(); vby=bspeedy(); touchcnt=touches(); x=posx(); y=posy(); direct=1; i=estimate(200); if (i>-9000) { alti=i; if (j==1) { if (i>800-bsizx /2) { i=1600-i-bsizx;direct=0; } if (i<405+bsizx /2) { i=810+bsizx-i;direct=0; } if (i<500) { if (touchcnt<2) { i=i-bsizx /2; } } } else { if (i<0+bsizx /2) { i=-i+bsizx; direct=0; } if (i>395-bsizx /2) { i=790-bsizx-i; direct=0; } if (i>300) { if (touchcnt<2) { i=i+bsizx /2; } } } i=i+(bsizx / 3)*factor; //if (i>=x+7) right(); //if (i<=x-7) left(); moveto(i); } stopjump(); if(vby<=10) if (factor*(x-bx)<21) if (factor*(bx-x)<7) if (abs(bx-x)<120) if (abs(vby)<65) if (by>200) if (by-y>70) if ((by<400) || (vby==0)) if (abs(vbx)<20) if (!balldown()) jump(); if (random(300)==0) jump(); if (i>-9000) { if ((abs(i-x)>75) && (abs(bx-x)<65) && (vby<0) ) { jump(); } } if (touching()) { stop(); } if (!launched()) { delay+=1; stopjump(); if (delay>=30) jump(); } wait(); } //while } //function blobby-1.0rc3/data/scripts/old/com_10eB1.lua0000644000175000017500000001151512042452377022043 0ustar danielknobedanielknobe-- Com 1.0 human Beta 1 -- by Oreon, Axji & Enormator -- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln -- Weltkonstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 323 -- Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS -- Charakter CONST_ANGRIFFSGRUNDWERT_MIN = 30 CONST_ANGRIFFSGRUNDWERT_MAX = 55 MIN_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MIN MAX_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MAX -- ***ANFANG*** function OnOpponentServe() moveto(130) end function OnServe(ballready) naechsterBallSchmettern = true generatenaechsterBallSchmettern() moveto(ballx()) if ballready and (ballx()-3 < posx()) and (posx() < ballx()+3) then jump() end end function OnGame() naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,1) --X Ziel in Blobbyhoehe targets = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,2) --X Richtung (-1 oder 1) bei Einschlag targetNetz = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_NETZ_HOEHE,1) --X Ziel in Netzhoehe (Netzrollerberechnung) targetJump = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,1) --X Ziel in Schmetterhoehe if (ballx() > CONST_NETZ_RECHTS) then --Wenn Ball auf rechter Spielfeldseite dann generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen end if (target > CONST_MITTE) and (ballx() > CONST_NETZ_RECHTS) then --Wenn der Ball mich nix angeht moveto(135) --Dann auf Standartposition warten else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then if ((math.abs(bspeedx()) < 4) or (bspeedx() < 0)) then sprungattacke(angriffsstaerke) else if (targetJump < CONST_MITTE / 2) then sprungattacke(-30) --an Rueckwand spielen else sprungattacke(0) --weiterleiten end end return end moveto(target) end end function sprungattacke(p_angriffsstaerke) if (bally() < 550) and (math.abs(ballx() - posx()) > 200) then -- Falls nicht schmetterbar moveto (target - 25) --Dann Notloesung versuchen return end moveto(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben if (bally() < 580) and (bspeedy() < 0) then jump() end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false return end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 1) and (math.abs(bspeedx()) < 2) then -- schon nach der 1ten Beruehrung angreifen wenn der Ball gut kommt naechsterBallSchmettern = true return end if (touches() == 2) then -- nach der 2. Berhrung angreifen naechsterBallSchmettern = true return end naechsterBallSchmettern = false end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) end function estimImpact(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx()/math.abs(bspeedx()) if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = math.abs(resultX - CONST_BALL_RADIUS) + CONST_BALL_RADIUS estimbspeedx=-estimbspeedx KollisionLinks = true else KollisionLinks = false end if (resultX > CONST_NETZ_RECHTS) and (estimbspeedx > 0) and ((KollisionLinks == true) or (ballx() < CONST_NETZ_LINKS)) then -- Abpraller am Netz unterhalb der Kugel erst wenn Netzroller ausgeschlossen sind resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end if (Frage == 1) then return resultX end if (Frage == 2) then return estimbspeedx end endblobby-1.0rc3/data/scripts/old/hyp07.lua0000644000175000017500000001456612042452377021415 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x) function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function est(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end -- if (x1>360) then x1=2*360-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=est(x1,y1,vx1,vy1,targety,2) break end end return x2,y2 end function nothing() --[[ if (math.abs(xe-xo)>0.1) then xo=xe for test=30,0,-3 do t1=test y1=yp(t1) t2=math.floor(tb(y,vy,y1+31.5+25+19,2)) y2=yb(y,vy,t2) x2,y2,vx2,vy2,t2,_=est(x,y,vx,vy,y2+0.1) t2=t2 if (vx2>2) then n=0 elseif (vx2>0.5) then n=5 else n=13 end xi,yi=impact(x2,y2,vx2,vy2,pos(x2-n*4.5),y1,220,1,0) if (math.abs(posx()-(x2-n*4.5))/4.580) and (t2-1>t1) and (xi>400) then break end end end move(x2-n*4.5) if (t2<=t1) then jump() end --]] end function OnOpponentServe() xdo=0 xe=0 s=1 touch=0 xodo=0 change=1 stop=0 t1,t2,y1,y2,x,y,vx,vy=0,0,0,0,0,0,0,0 calc=0 if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end xo=0 c=0 end function OnServe(ballready) xe=0 s=1 xodo=0 xdo=0 change=1 touch=0 stop=0 t1,t2,y1,y2,x,y,vx,vy=0,0,0,0,0,0,0,0 xo=0 c=0 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-180)<2) then jump() else move(180) end end end function OnGame() if (change==0) and (touches()=te) then jump() end if (math.abs(posx()-360)/4.50.2) then xodo=xdo imp=0 x1=0 for t=27,0,-3 do t1f=t y1f=yp(t1f) t2f=math.floor(tb(y,vy,y1f+h,2)) if (t2f>0) then -- debug(0) -- debug(t2f) y2f=yb(y,vy,t2f) -- debug(y2f) vy2f=vy-g*t2f x2f,y2f,vx2f,vy2f,t2f,_=est(x,y,vx,vy,y2f-vy2f/10,2) -- debug(t2f) -- debug(y2f) -- debug(vy) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) impt=0 if (t1f<=t2f-2) then for x1f=pos(x2f)-25*4.5,pos(x2f)+4.5,4.5 do impf,_=impact(x2f,y2f,vx2f,vy2f,pos(x1f),y1f,220,1,0) if (impf>440) and (math.abs(posx()-x1f)/4.5400) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>400) then break end end end end t2=t2+1 end t2=t2-1 if (s==1) then move(x1) if (t2<=t1+0.1) then jump() end end else move(200) end end if (bally()<140) then s=0 end -- debug(0) -- debug(x2) -- debug(ballx()) -- debug(y2) -- debug(bally()) -- debug(vx2) -- debug(bspeedx()) -- debug(vy2) -- debug(bspeedy()) -- debug(xe) -- debug(imp) -- debug(t2) -- debug(0) end blobby-1.0rc3/data/scripts/old/com_10eB2.lua0000644000175000017500000001253612042452377022050 0ustar danielknobedanielknobe-- Com 1.0 extrem Beta 2 -- by Oreon, Axji & Enormator -- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln -- Weltkonstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 323 -- Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS -- Charakter CONST_ANGRIFFSGRUNDWERT_MIN = 30 CONST_ANGRIFFSGRUNDWERT_MAX = 55 MIN_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MIN MAX_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MAX ANGRIFFSEINSCHRAENKUNG_HINTEN = 10 -- sonstige Einstellungen servexVersetzung=-7 --Wert ist so gewaehlt, dass der Ball nah ans Netz fliegt, der Gegner ihn aber grade nicht erreichen kann -- ***ANFANG*** function OnOpponentServe() moveto(130) end function OnServe(ballready) servex=ballx()+servexVersetzung naechsterBallSchmettern = true generatenaechsterBallSchmettern() moveto(servex) if ballready and (servex-2 < posx()) and (posx() < servex+2) then jump() end end function OnGame() target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,1) --X Ziel in Blobbyhoehe targets = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,2) --X Richtung (-1 oder 1) bei Einschlag targetNetz = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_NETZ_HOEHE,1) --X Ziel in Netzhoehe (Netzrollerberechnung) targetJump = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,1) --X Ziel in Schmetterhoehe naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht if (ballx() > CONST_NETZ_RECHTS) then --Wenn Ball auf rechter Spielfeldseite dann generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen end if (target > CONST_MITTE) and (ballx() > CONST_NETZ_RECHTS) then --Wenn der Ball mich nix angeht moveto(135) --Dann auf Standartposition warten else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then if ((math.abs(bspeedx()) < 4) or (estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,2) < 0)) then sprungattacke(angriffsstaerke) else if (targetJump < CONST_MITTE / 2) then sprungattacke(-35) --an Rueckwand spielen else sprungattacke(0) --weiterleiten end end return end moveto(target) end end function sprungattacke(p_angriffsstaerke) p_angriffsstaerke=math.max(p_angriffsstaerke, MIN_ANGRIFFSSTAERKE + ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so hoch spielen (kommt nicht auf die andere Seite) p_angriffsstaerke=math.min(p_angriffsstaerke, MAX_ANGRIFFSSTAERKE - ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so tief spielen (kommt ans Netz) moveto(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben if (bally() < 580) and (bspeedy() < 0) then jump() end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false return end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 1) and (math.abs(bspeedx()) < 2) then -- schon nach der 1ten Beruehrung angreifen wenn der Ball gut kommt naechsterBallSchmettern = true return end if (touches() == 2) then -- nach der 2. Berhrung angreifen naechsterBallSchmettern = true return end naechsterBallSchmettern = false end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) end function estimImpact(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx()/math.abs(bspeedx()) if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = math.abs(resultX - CONST_BALL_RADIUS) + CONST_BALL_RADIUS estimbspeedx=-estimbspeedx KollisionLinks = true else KollisionLinks = false end if (resultX > CONST_NETZ_RECHTS) and (estimbspeedx > 0) and ((KollisionLinks == true) or (ballx() < CONST_NETZ_LINKS)) then -- Abpraller am Netz unterhalb der Kugel erst wenn Netzroller ausgeschlossen sind resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end if (Frage == 1) then return resultX end if (Frage == 2) then return estimbspeedx end endblobby-1.0rc3/data/scripts/old/hyp09.lua0000644000175000017500000002164212042452377021410 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 p0=0 p1=0.5 p2=1 h=31.5+19+25 estt=0 nettime=0 touch=0 est=0 p=0.4 esto=10 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function lob() if (math.abs(est-esto)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto=est imp=0 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5700) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (impt>imp) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>740) then break end end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) end end function attack() if (math.abs(est-esto)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 h2=900 -- debug(-10) esto=est imp=0 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5400) and (math.abs(posx()-x1f)/4.5+10) and (x1f<360) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f xat=xaf yat=yaf vxat=vxaf vyat=vyaf break end end h2t=yb(yat,vyat,(400-xat)/vxat) if (h2t316+31.5) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t xa=xat ya=yat vxa=vxat vya=vyat end h2=yb(ya,vya,(400-xa)/vxa) if (h2<316+31.5+10) and (h2>316+31.5) then break end end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) end end function netp() if (math.abs(est-esto)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto=est imp=0 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5400) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (impt>imp) and (impt<431.5) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>428) then break end end end t2=t2+1 end t2=t2-1 if (x1>0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) end end function OnOpponentServe() y1p=144.5 if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end valid=1 end function OnServe(ballready) y1p=144.5 est=700 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-180)<2) then jump() else move(180) end end valid=1 end function OnGame() yp2=y1p y1p=oppy() vp=y1p-yp2 esttold=estt netold=net toucho=touch touch=touches() if (touch400-31.5) then move (250) elseif (est<400-10) and (est>400-22) then move(200) elseif (est<400) and (est>400-10) then move(180) else move(230) end end blobby-1.0rc3/data/scripts/old/com_10hB1.lua0000644000175000017500000001434012042452377022045 0ustar danielknobedanielknobe-- Com 1.0 human Beta 1 -- by Oreon, Axji & Enormator -- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln quatschFlag = 0 mood = math.random(1,3)+3 --Startlaune zwischen 4 und 6 -- Weltkonstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 323 -- Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS -- Charakter CONST_MIN_MOOD = 0 CONST_MAX_MOOD = 10 CONST_ANGRIFFSGRUNDWERT_MIN = 35 CONST_ANGRIFFSGRUNDWERT_MAX = 60 MIN_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MIN-mood MAX_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MAX-mood CONST_GEDULD = 0.01 --weniger ist mehr Geduld -- ***ANFANG*** function OnOpponentServe() BallLinks=false if (LauneBerechnet == false) then mood=mood-1 --schlechter gelaunt LauneBerechnen () --anwenden auf Angriffswert LauneBerechnet = true end moveto(130) end function OnServe(ballready) BallLinks=true if (LauneBerechnet == false) then mood=mood+1 --besser gelaunt LauneBerechnen () --anwenden auf Angriffswert LauneBerechnet = true end naechsterBallSchmettern = true generatenaechsterBallSchmettern() if (quatschFlag == 0) then quatschFlag = math.random(5,10) end if (mood > quatschFlag) then --je besser gelaunt, desto wahrscheinlicher Quatsch quatschFlag = enormerQuatsch() else moveto(ballx()) if ballready and (ballx()-3 < posx()) and (posx() < ballx()+3) then jump() end end end function OnGame() debug (mood) if (BallLinks == (ballx() > CONST_MITTE)) then --Bei jedem Ballwechsel mood = mood - CONST_GEDULD --um CONST_GEDULD schlechter gelaunt sein LauneBerechnen () BallLinks = (ballx() < CONST_MITTE) end LauneBerechnet=false --Flag setzen fr Berechnung beim Aufschlag quatschFlag=0 --Flag setzen fr Berechnung beim Aufschlag los=false --Flag setzen fr Berechnung beim Aufschlag naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG) --X Ziel in Blobbyhoehe targetNetz = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_NETZ_HOEHE) --X Ziel in Netzhoehe (Netzrollerberechnung) targetJump = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP) --X Ziel in Schmetterhoehe if (ballx() > CONST_NETZ_RECHTS) then --Wenn Ball auf rechter Spielfeldseite dann generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen end if (target > CONST_MITTE) and (ballx() > CONST_NETZ_RECHTS) then --Wenn der Ball mich nix angeht moveto(135) --Dann auf Standartposition warten else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then sprungattacke(angriffsstaerke) return end moveto(target) end end function sprungattacke(p_angriffsstaerke) if (bally() < 550) and (math.abs(ballx() - posx()) > 200) then -- Falls nicht schmetterbar moveto (target - 25) --Dann Notloesung versuchen return end if ((bally() < 600) and (bspeedy() < 0)) or (math.abs(bspeedx()) > 2) or (math.abs(targetJump-posx()) > 40) then -- erst im letzten Moment bewegen -> unvorhersehbar moveto(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben end if (bally() < 580) and (bspeedy() < 0) then jump() end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false return end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 1) and (math.abs(bspeedx()) < 2) and (mood < CONST_MAX_MOOD) then -- schon nach der 1ten Beruehrung angreifen wenn der Ball gut kommt und er nicht zu gut gelaunt ist naechsterBallSchmettern = true return end if (touches() == 2) then -- nach der 2. Berhrung angreifen naechsterBallSchmettern = true return end naechsterBallSchmettern = false end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) -- variiert mit der Laune end function estimImpact(bx,by,vbx,vby,destY) -- erlaubt ein besseres Estimate mit ein paar umbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx() if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = math.abs(resultX - CONST_BALL_RADIUS) + CONST_BALL_RADIUS estimbspeedx=-estimbspeedx KollisionLinks = true else KollisionLinks = false end if (resultX > CONST_NETZ_RECHTS) and (estimbspeedx > 0) and ((KollisionLinks == true) or (ballx() < CONST_NETZ_LINKS)) then -- Abpraller am Netz unterhalb der Kugel erst wenn Netzroller ausgeschlossen sind resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end return resultX end function enormerQuatsch() if los then left() jump() else right() end if (posx() > 350) then los=true end if (posx() < 60) then los=false return CONST_MAX_MOOD + 1 -- MaxLaune+1 kann nie erreicht werden -> stop end return quatschFlag -- Wenn nicht fertig, dann nix aendern end function LauneBerechnen () if (mood < CONST_MIN_MOOD) then mood = 0 end if (mood > CONST_MAX_MOOD) then mood = 10 end MIN_ANGRIFFSSTAERKE=CONST_ANGRIFFSGRUNDWERT_MIN-mood MAX_ANGRIFFSSTAERKE=CONST_ANGRIFFSGRUNDWERT_MAX-mood endblobby-1.0rc3/data/scripts/old/com_10.lua0000644000175000017500000001330312042452377021510 0ustar danielknobedanielknobe-- Combot 1.0 -- by Oreon, Axji & Enormator -- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln -- Weltkonstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 323 -- Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS -- Charakter CONST_ANGRIFFSGRUNDWERT_MIN = 30 CONST_ANGRIFFSGRUNDWERT_MAX = 55 MIN_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MIN MAX_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MAX ANGRIFFSEINSCHRAENKUNG_HINTEN = 10 -- sonstige Einstellungen servexVersetzung=-7 --Wert ist so gewaehlt, dass der Ball nah ans Netz fliegt, der Gegner ihn aber grade nicht erreichen kann -- ***ANFANG*** function OnOpponentServe() movetoX(130) end function OnServe(ballready) servex=ballx()+servexVersetzung naechsterBallSchmettern = true generatenaechsterBallSchmettern() if ballready and movetoX(servex) then jump() end end function OnGame() target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,1) --X Ziel in Blobbyhoehe targets = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,2) --X Richtung (-1 oder 1) bei Einschlag targetNetz = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_NETZ_HOEHE,1) --X Ziel in Netzhoehe (Netzrollerberechnung) targetJump = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,1) --X Ziel in Schmetterhoehe naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht if (ballx() > CONST_NETZ_RECHTS) then --Wenn Ball auf rechter Spielfeldseite dann generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen end if (target > CONST_MITTE) and (ballx() > CONST_NETZ_RECHTS) then --Wenn der Ball mich nix angeht movetoX(135) --Dann auf Standartposition warten else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then if ((math.abs(bspeedx()) < 4) or (estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,2) < 0)) then sprungattacke(angriffsstaerke) else if (targetJump < CONST_MITTE / 2) then sprungattacke(-35) --an Rueckwand spielen else sprungattacke(0) --weiterleiten end end return end movetoX(target) end end function sprungattacke(p_angriffsstaerke) p_angriffsstaerke=math.max(p_angriffsstaerke, MIN_ANGRIFFSSTAERKE + ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so hoch spielen (kommt nicht auf die andere Seite) p_angriffsstaerke=math.min(p_angriffsstaerke, MAX_ANGRIFFSSTAERKE - ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so tief spielen (kommt ans Netz) movetoX(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben if (bally() < 580) and (bspeedy() < 0) then jump() end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false return end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 1) and (math.abs(bspeedx()) < 2) then -- schon nach der 1ten Beruehrung angreifen wenn der Ball gut kommt naechsterBallSchmettern = true return end if (touches() == 2) then -- nach der 2. Berhrung angreifen naechsterBallSchmettern = true return end naechsterBallSchmettern = false end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) end function estimImpact(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx()/math.abs(bspeedx()) if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = math.abs(resultX - CONST_BALL_RADIUS) + CONST_BALL_RADIUS estimbspeedx=-estimbspeedx KollisionLinks = true else KollisionLinks = false end if (resultX > CONST_NETZ_RECHTS) and (estimbspeedx > 0) and ((KollisionLinks == true) or (ballx() < CONST_NETZ_LINKS)) then -- Abpraller am Netz unterhalb der Kugel erst wenn Netzroller ausgeschlossen sind resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end if (Frage == 1) then return resultX end if (Frage == 2) then return estimbspeedx end end function movetoX (x) if (math.abs(posx()-x)>math.abs(posx()+4.5-x)) then right() done=false else if (math.abs(posx()-x)>math.abs(posx()-4.5-x)) then left() done=false else done=true end end return done end blobby-1.0rc3/data/scripts/old/hyp010.lua0000644000175000017500000002214512042452377021457 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 p0=0 p1=0.5 p2=1 h=31.5+19+25 estt=0 nettime=0 touch=0 est=0 p=0.4 esto1=10 esto2=10 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function lob() if (math.abs(est-esto2)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto2=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5730) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end if (impfimp) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>740) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) --p=0.5 end end function attack() if (math.abs(est-esto1)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 h2=900 -- debug(-10) esto1=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5400) and (math.abs(posx()-x1f)/4.5+10) and (x1f<360) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f xat=xaf yat=yaf vxat=vxaf vyat=vyaf break end end h2t=yb(yat,vyat,(400-xat)/vxat) if (h2t316+31.5+10) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t xa=xat ya=yat vxa=vxat vya=vyat end h2=yb(ya,vya,(400-xa)/vxa) if (h2>316+31.5+10) and (h2<316+31.5+45) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) --p=0.9 end end function netp() if (math.abs(est-esto)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto=est imp=0 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5400) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (impt>imp) and (impt<431.5) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>428) then break end end end t2=t2+1 end t2=t2-1 if (x1>0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) end end function OnOpponentServe() y1p=144.5 if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end valid=1 end function OnServe(ballready) y1p=144.5 est=700 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-180)<2) then jump() else move(180) end end valid=1 end function OnGame() yp2=y1p y1p=oppy() vp=y1p-yp2 esttold=estt netold=net toucho=touch touch=touches() if (touch400-31.5) then move (250) elseif (est<400-10) and (est>400-22) then move(200) elseif (est<400) and (est>400-10) then move(180) else move(230) end end blobby-1.0rc3/data/scripts/old/hyp011com.lua0000644000175000017500000002677412042452377022173 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 p0=0 p1=0.5 p2=1 h=31.5+19+25 estt=0 nettime=0 touch=0 est=0 p=0.4 esto1=10 esto2=10 esto3=10 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function lob() if (math.abs(est-esto2)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto2=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5730) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end if (impfimp) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>740) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) --p=0.5 end end function attack() if (math.abs(est-esto1)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 h2=900 -- debug(-10) esto1=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5400) and (math.abs(posx()-x1f)/4.5+10) and (x1f<360) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f xat=xaf yat=yaf vxat=vxaf vyat=vyaf break end end h2t=yb(yat,vyat,(400-xat)/vxat) if (h2t316+31.5+10) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t xa=xat ya=yat vxa=vxat vya=vyat end h2=yb(ya,vya,(400-xa)/vxa) if (h2>316+31.5+10) and (h2<316+31.5+45) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) --p=0.9 end end function netp() if (math.abs(est-esto3)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto3=est imp=500 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5380) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (impt0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est-4.5) end end function posplay() if (math.abs(est-esto3)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto3=est imp=500 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5280) and (impf<340) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (math.abs(impt-310)0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) end end function OnOpponentServe() y1p=144.5 if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end valid=1 nettime=0 phase=0 end function OnServe(ballready) nettime=0 y1p=144.5 phase=0 est=700 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-180)<2) then jump() else move(180) end end valid=1 end function OnGame() yp2=y1p y1p=oppy() vp=y1p-yp2 esttold=estt netold=net toucho=touch touch=touches() if (touch431.5) or (est<368.5)) then nettime=10 phase=4 elseif (phase==4) and (nettime>0) then nettime=nettime-1 else if (est>431.5) then phase=3 elseif (est<431.5) and (est>368.5) then phase=2 else phase=1 end end if (math.sqrt((ballx()-400)^2+(bally()-316)^2)<(31.5+7)) and (math.sqrt((bspeedx())^2+(bspeedy())^2)<2) then phase=5 end --1 Player --2 Ball -- debug(0) -- debug(est) -- debug(imp) -- debug(t2) -- -- if (est<(400-31.5)) then -- if (p<1) then netp() -- else lob() -- end -- elseif (est<400-22) and (est>400-31.5) then -- move (250) -- elseif (est<400-10) and (est>400-22) then -- move(200) -- elseif (est<400) and (est>400-10) then -- move(180) -- else -- move(100) -- end if (phase==3) then move(100) elseif (phase==1) then -- if (p<0.4) then -- attack() -- elseif (p>=0.4) and (p<0.7) then -- lob() -- else if (touches()==0) then posplay() elseif (touches()==1) then netp() else attack() end -- end elseif (phase==2) then if (tnet<=tp(393)+1) or (nettime>0) then jump() end if (math.abs(posx()-360)/4.5-10<=tnet) then left() else right() end elseif (phase==4) then right() jump() elseif (phase==5) then if (posx()>300) then jump() end right() end end blobby-1.0rc3/data/scripts/old/hyp011.lua0000644000175000017500000002675212042452377021470 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 p0=0 p1=0.5 p2=1 h=31.5+19+25 estt=0 nettime=0 touch=0 est=0 p=0.4 esto1=10 esto2=10 esto3=10 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function lob() if (math.abs(est-esto2)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto2=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5730) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end if (impfimp) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>740) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) --p=0.5 end end function attack() if (math.abs(est-esto1)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 h2=900 -- debug(-10) esto1=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5400) and (math.abs(posx()-x1f)/4.5+10) and (x1f<360) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f xat=xaf yat=yaf vxat=vxaf vyat=vyaf break end end h2t=yb(yat,vyat,(400-xat)/vxat) if (h2t316+31.5+10) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t xa=xat ya=yat vxa=vxat vya=vyat end h2=yb(ya,vya,(400-xa)/vxa) if (h2>316+31.5+10) and (h2<316+31.5+45) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) --p=0.9 end end function netp() if (math.abs(est-esto3)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto3=est imp=500 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5380) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (impt0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est-4.5) end end function posplay() if (math.abs(est-esto3)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto3=est imp=500 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5280) and (impf<340) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (math.abs(impt-310)0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est) end end function OnOpponentServe() y1p=144.5 if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end valid=1 nettime=0 phase=0 end function OnServe(ballready) nettime=0 y1p=144.5 phase=0 est=700 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-180)<2) then jump() else move(180) end end valid=1 end function OnGame() yp2=y1p y1p=oppy() vp=y1p-yp2 esttold=estt netold=net toucho=touch touch=touches() if (touch431.5) or (est<368.5)) then nettime=10 phase=4 elseif (phase==4) and (nettime>0) then nettime=nettime-1 else if (est>431.5) then phase=3 elseif (est<431.5) and (est>368.5) then phase=2 else phase=1 end end if (math.sqrt((ballx()-400)^2+(bally()-316)^2)<(31.5+7)) and (math.sqrt((bspeedx())^2+(bspeedy())^2)<2) then phase=5 end --1 Player --2 Ball -- debug(0) -- debug(est) -- debug(imp) -- debug(t2) -- -- if (est<(400-31.5)) then -- if (p<1) then netp() -- else lob() -- end -- elseif (est<400-22) and (est>400-31.5) then -- move (250) -- elseif (est<400-10) and (est>400-22) then -- move(200) -- elseif (est<400) and (est>400-10) then -- move(180) -- else -- move(100) -- end if (phase==3) then move(100) elseif (phase==1) then if (p<0.4) then attack() elseif (p>=0.4) and (p<0.7) then lob() else if (touches()==0) then posplay() elseif (touches()==1) then netp() else attack() end end elseif (phase==2) then if (tnet<=tp(393)+1) or (nettime>0) then jump() end if (math.abs(posx()-360)/4.5-10<=tnet) then left() else right() end elseif (phase==4) then right() jump() elseif (phase==5) then if (posx()>300) then jump() end right() end end blobby-1.0rc3/data/scripts/old/hyperion6.lua0000644000175000017500000001522612042452377022363 0ustar danielknobedanielknobe-- Dies ist der BlobbyVolley2 Bot "Hyperion" -- geschrieben und getestet wurde der Bot mit der SVN-Version -- Die Version Blobby0.6 hatte verschiedene Bugs, unter anderem bei der oppx() und der estimate() Funktion -- -- Hyperion ver. 0.6 -- - Einige Konstanten die die physikalische Welt in BlobbyVolley2 beschreiben g=0.28 g_p=0.88 v0=14.5 v_p=4.5 jb_p=0.44 r1=31.5 -- -- - kleine unkomplizierte Hilfsfunktionen die ich benötige function max(a,b) if (a>b) then return a else return b end end function min(a,b) if (a2.6) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.6) then left() end end function t1_y(y,vy,height) -- Eingabe: Position und Geschwindigkeit des Balles, Höhe die der Ball erreichen soll -- Ausgabe: Ausgabe der Zeit bis zur Höhe height if (vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0) then return -1 else return -1/10+vy/g-math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function t2_y(y,vy,height) -- Eingabe: Position und Geschwindigkeit des Balles, Höhe die der Ball erreichen soll -- Ausgabe: Ausgabe der Zeit bis zur Höhe height if (vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0) then return -1 else return -1/10+vy/g+math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function y_p(y,t) -- Eingabe: Position und Geschwindigkeit des Players, Zeitpunkt an dem man die y-Koordinate des Players wissen möchte -- Ausgabe: Höhe des Players nach der Zeit t return y+(v0+jb_p/2+g_p/10)*t-1/2*(g_p-jb_p)*t^2 end tp_peak=(14.5+0.44/2+0.88/10)/(0.88-0.44) yp_max=y_p(144.5,tp_peak) function time(t) return 1/5*math.ceil(5*t) end function t2_yp(y,vy,height) y=144.5 return (v0+jb_p/2+g_p/10)/(g_p-jb_p)+math.sqrt((v0+jb_p/2+g_p/10)^2/(g_p-jb_p)^2+2*y/(g_p-jb_p)) end -- -- - Komplizierte Funktionen die die Game-Engine nachbilden und so Einschlagpunkte, etc. berechnen. function collide(x,y,vx,vy,x2,y2,r2) -- Berechnet, ob und nach welcher Zeit der Ball im Zustand (x,y,vx,vy) mit der Kugel an (x2,y2) mit Radius r2 kollidiert local leftb=x2-r2-r1 local rightb=x2+r2+r1 local lowerb=y2-r2-r1 local upperb=y2+r2+r1 local txlb=time((leftb-x)/vx) -- Zeit zur linken Begrenzung local txrb=time((rightb-x)/vx) -- Zeit zur rechten Begrenzung local tyla=time(t1_y(y,vy,lowerb)) --untere Grenze steigend (ascending) local tyld=time(t2_y(y,vy,lowerb)) --untere Grenze fallend (descending) local tyua=time(t1_y(y,vy,upperb)) --obere Grenze steigend (ascending) local tyud=time(t2_y(y,vy,upperb)) --obere Grenze fallend (descending) local tp=time(vy/g-1/10) -- Zeit bis die Ballkurve auf dem Höhepunkt ist (kann in der Vergangenheit liegen) local t1,t2,t_coll=0,0,-1 if (vx>0) then t1=max(max(txlb,tyla),0) t2=min(txrb,tyld) else t1=max(max(txrb,tyla),0) t2=min(txlb,tyld) end if (t10) then t_wall=time((768.5-x)/vx) t_net=time((361.5-x)/vx) else t_wall=time((31.5-x)/vx) t_net=time((438.5-x)/vx) end local t=10000 if ((t_netsphere>0) and (t_netsphere0) and (t_net0) and (t_wallt) then if (t==t_netsphere) then t_ret=t_ret+t vx_ret=0 vy_ret=0 x_ret=400 y_ret=316 collision=0 end if (t==t_net) or (t==t_wall) then t_ret=t_ret+t x=x+vx*t y=y_b(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else t_ret=t_ret+t_height vx_ret=vx vy_ret=vy-g*t_height x_ret=x+vx*t_height y_ret=y_b(y,vy,t_height) collision=0 end end -- while Ende return x_ret,y_ret,vx_ret,vy_ret,t_ret end function impact(x,y,vx,vy,xpos,ypos) -- schätzt den Einschlagsort des Balles wenn er mit dem Blobby an Position xpos kollidiert ist und dann losfliegt. -- Funktioniert mit minimalem Fehler r1=31.5 r2=25 --local x,y,vx,vy,t1=estimate_t(x,y,vx,vy,(ypos+19+25)+31.5) Die Wete haben schon nahe genug zu sein local t=time(collide(x,y,vx,vy,xpos,ypos+19,25)) if(t>0) then x=x+vx*t y=y_b(y,vy,t) dx=x-xpos dy=y-(ypos+19) l=math.sqrt(dx^2+dy^2) vx=dx/l vy=dy/l x=x+vx*3 y=y+vy*3 vy=vy*13.125 vx=vx*13.125 -- x=x+vx/5 -- y=y+vy/5 x,y,vx,vy,t=estimate_t(x,y,vx,vy,220.5) return x,y,vx,vy,t else return -1,-1,-1,-1,-1 end end -- Funktionsende function xtoplayto(target,height) local x,y,vx,vy,t=estimate_t(ballx(),bally(),bspeedx(),bspeedy(),height+(25+19)+31.5+5) local xpos=estimate_t(ballx(),bally(),bspeedx(),bspeedy(),height+(25+19)+31.5) local sgn=0 if (x -- - High-Level Funktionen die bestimmen wo man s function stellen(tox,height) --t2_yp --t2_y if (tox<390) then elseif (390410) then end move(xplayto(tox,posy())) end function schmettern() end function ueberspielen() end -- -- - Die Hauptfunktionen des Spiels function OnOpponentServe() end function OnServe(ballready) if (math.abs(math.floor(posx()/4.5)-posx()/4.5)<0.4) then if(math.abs(180-posx())<2) then jump() else moveto(180) end else moveto(400) end old=5 end function OnGame() x1=ballx() y1=bally() vx1=bspeedx() vy1=bspeedy() x2=oppx() y2=163.5 r2=25 xe=estimate_t(x1,y1,vx1,vy1,220.5) --debug(xe) -- debug(x2) xr,yr,vxr,vyr,tr=impact(x1,y1,vx1,vy1,x2,144.5) -- debug(xr) -- debug(0) if (xe<400) then if (touches()==0) then test=xtoplayto(320,144.5) move(test) else test=xtoplayto(400,144.5) move(test-3.1) end elseif (xe==400) then move(180) else move(180) end old=touches() endblobby-1.0rc3/data/scripts/old/hyp012.lua0000644000175000017500000002654512042452377021471 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 h=31.5+19+25 estt=0 nettime=0 touch=0 est=0 p=math.random()/2+0.5 esto1=10 esto2=10 esto3=10 esto4=10 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function lob() if (math.abs(est-esto2)>0.2) then esto2=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5730) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end if (impfimp) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>740) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else -- move(est) p=math.random() esto2=0 end end function attack() if (math.abs(est-esto1)>0.2) then h2=900 esto1=est imp=0 x1=0 t=30 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(pos(x2f)+4.5))/4.5400) and (math.abs(posx()-x1f)/4.5+10) and (x1f<360) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f xat=xaf yat=yaf vxat=vxaf vyat=vyaf break end end h2t=yb(yat,vyat,(400-xat)/vxat) if (h2t316+31.5+10) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t xa=xat ya=yat vxa=vxat vya=vyat end h2=yb(ya,vya,(400-xa)/vxa) if (h2>316+31.5+10) and (h2<316+31.5+60) then break end end if (t>12) then t=t-6 else t=t-4 end end t2=t2+1 end t2=t2-1 if (x1>0) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else -- move(est-5) --p=0.9 p=math.random() esto1=0 end end function netp() if (math.abs(est-esto3)>0.2) then esto3=est imp=500 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5380) and (math.abs(posx()-x1f)/4.50) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (impt0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else -- move(est-4.5) p=math.random() esto3=0 end end function playto(dest) --310 if (math.abs(est-esto4)>0.2) then x1,y1,t1,x2,y2,t2,vx2,vy2,x1f,y1f,t1f,x2f,y2f,t2f,vx2f,vy2f,x1t,y1t,t1t,x2t,y2t,t2t,vx2t,vy2t=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 t2g=0 -- debug(-10) esto4=est imp=500 x1=0 for t=33,0,-3 do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,nn=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(tp2(posy(),vp))1) and (t2f>1) and ((math.abs(posx()-(x2f+4.5))/4.5dest-30) and (impf0) then impt=impf x1t=x1f y1t=y1f t1t=t1f x2t=x2f y2t=y2f t2t=t2f vx2t=vx2f vy2t=vy2f break end end if (math.abs(impt-dest)0) and (est<368.5) then move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else -- move(est) p=math.random() esto4=0 end end function OnOpponentServe() pp=math.random() y1p=144.5 if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end valid=1 nettime=0 phase=0 imp1=0 imp2=0 imp=0 active=1 end function OnServe(ballready) pp=math.random() active=1 nettime=0 y1p=144.5 phase=0 est=700 imp1=0 imp2=0 imp=0 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-198)<2) then jump() else move(198) end end valid=1 end function OnGame() yp2=y1p y1p=oppy() vp=y1p-yp2 if (math.abs(imp1-imp)>0.1) then imp2=imp1 imp1=imp end esttold=estt netold=net toucho=touch touch=touches() if (touch431.5) or (est<368.5)) then nettime=11 phase=4 elseif (phase==4) and (nettime>0) then nettime=nettime-1 else if (est>431.5) then phase=3 elseif (est<431.5) and (est>368.5) then phase=2 else phase=1 end end else phase=6 end if (bally()<130) then active=0 end if (math.sqrt((ballx()-400)^2+(bally()-316)^2)<(31.5+7)) and (math.sqrt((bspeedx())^2+(bspeedy())^2)<2) then phase=5 end if (phase==3) then move(170) elseif (phase==1) then if (p<0.5) then if (touch==0) then playto(310) elseif (touch==1) then if (p<0.2) then netp() elseif (p>=0.2) and (p<0.35) then playto(pp*300+450) elseif (p>=0.35) and (p<0.5) then attack() end else attack() end else if (touch==0) then if (p>=0.5) and (p<0.75) then playto(pp*300+450) elseif (p>=0.75) and (p<=1.0) then attack() end else attack() end end elseif (phase==2) then if (tnet<=tp(393)+1) or (nettime>0) then jump() end if (math.abs(posx()-360)/4.5-10<=tnet) then left() else right() end elseif (phase==4) then right() jump() elseif (phase==5) then if (posx()>300) then jump() end right() end if ((x1==0) or (imp==0)) and (phase==1) then -- debug(-1) move(est) end --debug(phase) end blobby-1.0rc3/data/scripts/old/hyp013.lua0000644000175000017500000002431312042452377021461 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 h=31.5+19+25 estt=0 p=0.654 function reset() est=0 imp,imp1,imp2=0,0,0 nettime=0 touch=0 esto1=0 esto2=0 esto3=0 esto4=0 esto5=0 esto6=0 phase=0 valid=1 active=1 nettime=0 y1p=posy() end function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos2(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=vx vyr=vy n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jum,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,7,0.2 do if (jum==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function playto(position) if (math.abs(est-esto3)>0.2) then delta=30 esto3=est imp,impt,impf=0,0,0 x1=0 t=27 typ1=60 typ2=120 if (position<400) then n1=2 n2=-3 elseif (position>600) then n1=-4 n2=-11 else n1=-1 n2=-8 end while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(math.ceil((tp2(posy(),vp))))1) and (t2f>1) then for x1f=pos(x2f)+n1*4.5,pos(x2f)+n2*4.5,-4.5 do impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,x1f,y1f,220,j,0) if (impf>position-delta) and (math.abs(posx()-x1f)/4.5+10) and (x1f<330) and (impfposition+delta) and ((x1f-pos(x2f))/4.5<0) then break end end end if (imp>position-delta) and (imp15) then t=t-6 else t=t-3 end end t2=t2+1 end if (x1>0) and (imp>0) then t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(est+4.5) end end function net() if (math.abs(est-esto2)>0.2) then esto2=est imp,impt,impf=0,0,0 x1=0 t=27 typ1=60 typ2=120 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(math.ceil((tp2(posy(),vp))))+21) and (t2f>1) then for x1f=pos(x2f)-1*4.5,pos(x2f)-10*4.5,-4.5 do impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,x1f,y1f,220,j,0) if (impf>400) and (math.abs(posx()-x1f)/4.50) and (x1f<330) and (impf<438) then imp=impf x1=x1f y1=y1f t1=t1f x2=x2f y2=y2f t2=t2f vx2=vx2f vy2=vy2f xa=xaf ya=yaf vxa=vxaf vya=vyaf break end if (imp>400) then break end end end if (imp>400) and (imp<438) then break end if (t>15) then t=t-6 else t=t-3 end end t2=t2+1 end if (x1>0) and (imp>0) then t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else playto(600) end end function attack() if (math.abs(est-esto1)>0.2) then h2=900 esto1=est imp,impt,impf=0,0,0 x1=0 t=27 typ1=60 typ2=120 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(math.ceil((tp2(posy(),vp))))+21) and (t2f>1) then for x1f=pos(x2f)-20*4.5,pos(x2f)-4*4.5,4.5 do impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,x1f,y1f,220,j,0) h1=yb(yaf,vyaf,(380-xaf)/vxaf) h2=yb(yaf,vyaf,(420-xaf)/vxaf) height=316+7+31.5 if (impf>432) and (math.abs(posx()-x1f)/4.5+10) and (x1f<330) and (h2>height+typ1) and (h1>height+typ1) and (h2432) and (h2>height+typ2) then break end end end if (imp>0) then break end if (t>15) then t=t-6 else t=t-3 end end t2=t2+1 end if (x1>0) and (imp>0) then t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else playto(600) end end function OnOpponentServe() reset() if (math.abs(pos2(posx())-posx())>1) then move(400) else move(200) end end function OnServe(ballready) reset() if (math.abs(pos2(posx())-posx())>1) then move(400) else if (math.abs(posx()-198)<2) then jump() else move(198) end end end function OnGame() yp2=y1p y1p=posy() vp=y1p-yp2 if (math.abs(imp1-imp)>0.1) then imp2=imp1 imp1=imp end esttold=estt netold=netc toucho=touch touch=touches() if (touch438.5) or (est<361.5)) then nettime=11 phase=4 elseif (phase==4) and (nettime>0) then nettime=nettime-1 else if (est>438.5) then phase=2 elseif (est<438.5) and (est>361.5) then if (((vyestt-g*(80-estt)/vxestt)>0) or (vxestt<0)) and ((360-posx())/4.5=0.2)) then attack() else playto(600) end elseif (p>=0.6) and (p<0.9) then if (p<0.7) and (touch==0) then playto(300) else playto(770) end -- elseif (p>=0.9) and (p<0.95) then -- playto(480) else if (p<0.95) and (touch==0) then playto(300) elseif (touch==1) or ((touch==0) and (p>=0.95)) then playto(480) else playto(600) end end elseif (phase==3) then if (tnet<=tp(393)-3) or (nettime>0) then jump() end if (math.abs(posx()-360)/4.5-7<=tnet) then left() else right() end elseif (phase==4) then right() jump() elseif (phase==5) then move(200) elseif (phase==6) then if (posx()>300) then jump() end right() end if ((x1==0) or (imp==0)) and (phase==1) then move(est) end -- debug(0) -- debug(imp) -- debug(est) end blobby-1.0rc3/data/scripts/old/com_04c.lua0000644000175000017500000000766412042452377021673 0ustar danielknobedanielknobe-- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln angriffsstaerke = 50 -- versetzung zum Ball je grsser desto tiefer fliegt der Service 0-64( 64 ist ca CONST_BALLRADIUS + CONST_BLOBBY_BAUCH_RADIUS) angriffsstaerkeNeuBerechnen =false -- neuberechnung von angriffsstaerke => Variable Angriffe -- Konstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 157 -- Linke Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS function OnOpponentServe() moveto(120) generatenaechsterBallSchmettern() -- der gegner soll den smash ja nicht vorhersagen knnen end function OnServe(ballready) moveto(ballx()) wait = wait + 1 naechsterBallSchmettern = true if ballready then if wait > 90 then jump() wait = 0 end end end function OnGame() naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG) if naechsterBallSchmettern then sprungattacke(angriffsstaerke) return end -- nun kmmern wir uns um die Blle die ans netz prallen if ((target > CONST_NETZ_LINKS) and (ballx() < CONST_NETZ_LINKS)) then netzappraller(target) return end moveto(target) end function netzappraller(p_target) moveto(CONST_NETZ_LINKS - (p_target - CONST_NETZ_LINKS) + math.abs(bspeedx()*bspeedx()*1.4)) end function sprungattacke(p_angriffsstaerke) moveto(ballx()-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben if bally() < 580 then jump() end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false angriffsstaerkeNeuBerechnen = true -- da wrde es sicher auch einen Inteligenteren Test geben. return end if (angriffsstaerkeNeuBerechnen == true) then generatenaechsterBallSchmettern() end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 2) then -- nach der 2. Berhrung angreifen if (bally() > 500) then -- erst wenn der Ball hoeher als 500 fliegt sonst gibt es keinen schnen Schmetterball. naechsterBallSchmettern = true end return end if ((ballx() < (400-CONST_BALL_RADIUS)) and (bally() < 200) and (math.abs(bspeedx()) < 40)) then -- der ball knnte noch drber fliegen ** noch optimieren naechsterBallSchmettern = true return end end function generatenaechsterBallSchmettern() angriffsstaerkeNeuBerechnen = false; angriffsstaerke = math.random(15,64) -- variiert mit der Angriffsstrke also auch mit den Anschlgen end function estimImpact(bx,by,vbx,vby,destY) -- erlaubt ein besseres Estimate mit ein paar umbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der Hinteren Ebene resultX = math.abs(CONST_BALL_RADIUS - resultX) + CONST_BALL_RADIUS end if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = CONST_FELD_LAENGE - (resultX - CONST_FELD_LAENGE) end -- test -- if(resultX > CONST_MITTE - CONST_BALL_RADIUS)then -- resultX = -resultX -- end return resultX end function cleanMoveTo(position) -- eine nette Spielerei ;) if (posx() ~= position) then moveto(position) end end blobby-1.0rc3/data/scripts/old/com_04d.lua0000644000175000017500000000762312042452377021667 0ustar danielknobedanielknobe-- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln angriffsstaerke = 50 -- versetzung zum Ball je grsser desto tiefer fliegt der Service 0-64( 64 ist ca CONST_BALLRADIUS + CONST_BLOBBY_BAUCH_RADIUS) angriffsstaerkeNeuBerechnen =false -- neuberechnung von angriffsstaerke => Variable Angriffe -- Konstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 157 -- Linke Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS function OnOpponentServe() moveto(120) generatenaechsterBallSchmettern() -- der gegner soll den smash ja nicht vorhersagen knnen end function OnServe(ballready) moveto(ballx()) wait = wait + 1 naechsterBallSchmettern = true if ballready then if wait > 90 then jump() wait = 0 end end end function OnGame() naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG) if target > 440 then moveto(120) else if naechsterBallSchmettern then sprungattacke(angriffsstaerke) return end -- nun kmmern wir uns um die Blle die ans netz prallen if ((target > CONST_NETZ_LINKS) and (ballx() < CONST_NETZ_LINKS)) then netzappraller(target) return end moveto(target) end end function netzappraller(p_target) moveto(CONST_NETZ_LINKS - (p_target - CONST_NETZ_LINKS) + math.abs(bspeedx()*bspeedx()*1.4)) end function sprungattacke(p_angriffsstaerke) moveto(ballx()-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben if bally() < 580 then jump() end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false angriffsstaerkeNeuBerechnen = true -- da wrde es sicher auch einen Inteligenteren Test geben. return end if (angriffsstaerkeNeuBerechnen == true) then generatenaechsterBallSchmettern() end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 2) then -- nach der 2. Berhrung angreifen if (bally() > 500) then -- erst wenn der Ball hoeher als 500 fliegt sonst gibt es keinen schnen Schmetterball. naechsterBallSchmettern = true end return end if ((ballx() < (400-CONST_BALL_RADIUS)) and (bally() < 200) and (math.abs(bspeedx()) < 40)) then -- der ball knnte noch drber fliegen ** noch optimieren naechsterBallSchmettern = true return end end function generatenaechsterBallSchmettern() angriffsstaerkeNeuBerechnen = false; angriffsstaerke = math.random(20,55) -- variiert mit der Angriffsstrke also auch mit den Anschlgen end function estimImpact(bx,by,vbx,vby,destY) -- erlaubt ein besseres Estimate mit ein paar umbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der Hinteren Ebene resultX = math.abs(CONST_BALL_RADIUS - resultX) + CONST_BALL_RADIUS end if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = CONST_FELD_LAENGE - (resultX - CONST_FELD_LAENGE) end return resultX end function cleanMoveTo(position) -- eine nette Spielerei ;) if (posx() ~= position) then moveto(position) end end blobby-1.0rc3/data/scripts/old/gintonicV6.lua0000644000175000017500000001741212042452377022465 0ustar danielknobedanielknobe--Gin Tonic v6 - No Comments, sorry :P CT_ServeSelf = { 152, 163, 180, 195, 205, 240 } CT_ServeOpp = { 140, 200, 240 } CT_ServeIndex = 0 CT_Tolerance = 5 CT_Action = "" CT_ShotDecision = 0 CT_NextGround = 9999 CT_LastTouches = 0 CT_LastHeight = 0 CT_WaitCounter = 0 CT_WaitName = "" CT_WaitMoveTo = 0 function IsAt(position) return (math.abs(posx()-position) <= CT_Tolerance) end function Wait(name, time, moveto) if (CT_WaitName == name) then if (CT_WaitCounter == 0) then return false end end CT_WaitCounter = time CT_WaitName = name CT_WaitMoveTo = moveto return true end function WaitQueue() if (CT_WaitCounter > 0) then CT_WaitCounter = CT_WaitCounter - 1 if (CT_WaitMoveTo > 0) then if (not IsAt(CT_WaitMoveTo)) then moveto(CT_WaitMoveTo) end end return true else return false end end function ResetWait() CT_WaitCounter = 0 CT_WaitName = "" end function OnOpponentServe() if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,3) end if (not IsAt(CT_ServeOpp[CT_ServeIndex])) then moveto(CT_ServeOpp[CT_ServeIndex]) end end function OnServe(ballready) if (WaitQueue()) then return end if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,6) end if (ballready) then if (Wait("ServeDelay",math.random(28,90),CT_ServeSelf[CT_ServeIndex]+math.random(-150, 150))) then return end if (IsAt(CT_ServeSelf[CT_ServeIndex])) then jump() else moveto(CT_ServeSelf[CT_ServeIndex]) end else if (posx() < 150) then jump() end moveto(40) end end function OnGame() ResetWait() CT_ServeIndex = 0 local timeJump = timeToHitHeight(380, 390, 20) local timeGround = timeToHitHeight(200, 222, 40) local timeBlock = timeToOppSmash(390) local estimhx = r_estimx(timeJump) local estimGround = r_estimx(timeGround) local estimBlock = r_estimx(timeBlock) local block = 0 local wallcoll = willHitWall(time) if (timeBlock ~= -1) then block = timeBlock+(estimBlock-400)/13 end if (timeBlock == -1) then timeBlock = 9999 end if (timeJump == -1) then estimhx = 9999 end if (timeGround == -1) then estimGround = 210 end if (posy() < CT_LastHeight and posy() > 150 and posy() < 330) then CT_Action = "" end CT_LastHeight = posy() if (CT_Action == "NetBlock") then if ((posy() < 150) or (timeBlock < 4 and oppy() < 150) or (ballx() <= posx()) or (touches() <= 0 and bspeedx() > 10)) then CT_Action = "" else jump() moveto(400) return end elseif (CT_Action == "JumpPlayFwd") then if (posy() < 150) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (posy() > 300) then if (math.abs(bally()-posy()) < 18) then moveto(ballx()+bspeedx()) elseif (estimhx < 200) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(3,5) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() > 600 and oppy() < 150) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(4,6) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() < 600 and oppy() > 180) then moveto(estimhx-40) else moveto(estimhx-60) end else moveto(estimhx-60) end return end elseif (CT_Action == "JumpPlayRev") then if (posy() < 150) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(4,9) end moveto(estimhx+5*CT_ShotDecision) return end end if (touches() ~= CT_LastTouches) then CT_LastTouches = touches() CT_NextGround = math.random(-20,20) end if (CT_Action == "") then if ((ballx() < 400 or bspeedx() < -2) and estimGround < 400) then if (touches() >= 2) then moveto(estimGround+(posx()-500)/22) elseif (math.abs(bspeedx()) > 8) then moveto(estimGround) else moveto(estimGround+CT_NextGround) end elseif (estimhx < 600 and math.abs(bspeedx()) < 6) then moveto(280) elseif (estimhx > 650) then moveto(250) else moveto(190) end end if (posy() > 150) then return end if (timeBlock >= 17 and timeBlock <= 19) then if (posx() > 210 and estimBlock > 395 and estimBlock < 640) then jump() moveto(400) CT_Action = "NetBlock" return end end if (timeJump >= 17 and timeJump <= 19) then if (bspeedx() <= 7 and estimhx >= 65 and estimhx <= 420 and posx()-estimhx <= 120 and (bspeedx() >= -7 or (not wallcoll))) then if (estimGround > 400 or bally() > 250) then CT_Action = "JumpPlayFwd" CT_ShotDecision = 0 jump() end end if ((wallcoll or bspeedx() >= -7) and estimhx <= 250 and posx()-estimhx <= 180 and posx()-estimhx >= -120) then if (estimGround > 400 or bally() > 250) then if (CT_Action == "JumpPlayFwd" and (touches() >= 2 or math.random(100) > 15)) then return end CT_Action = "JumpPlayRev" CT_ShotDecision = 0 jump() end end end end function timeToHitHeight(minheight, maxheight, depth) local i = 0 for i=1, depth do if (estimy(i) >= minheight and estimy(i) <= maxheight and estimx(i) <= 420) then return i end end return -1 end function timeToOppSmash(height) if (bally() < height) then return -1 end local i = 0 for i=1, 17 do if (estimy(i) < height) then return i end end return -1 end function r_estimx(time) local estim = estimx(time) if estim < 31.5 then estim = 63-estim end if estim > 768.5 then estim = 1537-estim end if (bally() < 330) then if (ballx() < 400 and estim > 400) then estim = 723-estim end if (ballx() > 400 and estim < 400) then estim = 877-estim end end return estim end function willHitWall(time) if (estimx(time) < 31.5) then return true end return false endblobby-1.0rc3/data/scripts/old/gintonicV7.lua0000644000175000017500000001754012042452377022470 0ustar danielknobedanielknobe--Gin Tonic v7 - No Comments, sorry :P CT_ServeSelf = { 152, 163, 180, 195, 205, 240 } CT_ServeOpp = { 140, 200, 240 } CT_ServeIndex = 0 CT_Tolerance = 5 CT_Action = "" CT_ShotDecision = 0 CT_NextGround = 9999 CT_LastTouches = 0 CT_LastHeight = 0 CT_WaitCounter = 0 CT_WaitName = "" CT_WaitMoveTo = 0 function IsAt(position) return (math.abs(posx()-position) <= CT_Tolerance) end function Wait(name, time, moveto) if (CT_WaitName == name) then if (CT_WaitCounter == 0) then return false end end CT_WaitCounter = time CT_WaitName = name CT_WaitMoveTo = moveto return true end function WaitQueue() if (CT_WaitCounter > 0) then CT_WaitCounter = CT_WaitCounter - 1 if (CT_WaitMoveTo > 0) then if (not IsAt(CT_WaitMoveTo)) then moveto(CT_WaitMoveTo) end end return true else return false end end function ResetWait() CT_WaitCounter = 0 CT_WaitName = "" end function OnOpponentServe() if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,3) end if (not IsAt(CT_ServeOpp[CT_ServeIndex])) then moveto(CT_ServeOpp[CT_ServeIndex]) end end function OnServe(ballready) if (WaitQueue()) then return end if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,6) end if (ballready) then if (Wait("ServeDelay",math.random(28,90),CT_ServeSelf[CT_ServeIndex]+math.random(-150, 150))) then return end if (IsAt(CT_ServeSelf[CT_ServeIndex])) then jump() else moveto(CT_ServeSelf[CT_ServeIndex]) end else if (posx() < 150) then jump() end moveto(40) end end function OnGame() ResetWait() CT_ServeIndex = 0 local timeJump = timeToHitHeight(380, 390, 20) local timeGround = timeToHitHeight(200, 222, 40) local timeBlock = timeToOppSmash(390) local estimhx = r_estimx(timeJump) local estimGround = r_estimx(timeGround) local estimBlock = r_estimx(timeBlock) local block = 0 local wallcoll = willHitWall(timeJump) if (timeBlock ~= -1) then block = timeBlock+(estimBlock-400)/13 end if (timeBlock == -1) then timeBlock = 9999 end if (timeJump == -1) then estimhx = 9999 end if (timeGround == -1) then estimGround = 210 end if (posy() < CT_LastHeight and posy() > 150 and posy() < 330) then CT_Action = "" end CT_LastHeight = posy() if (CT_Action == "NetBlock") then if ((posy() < 150) or (timeBlock < 4 and oppy() < 150) or (ballx() <= posx()) or (touches() <= 0 and bspeedx() > 10)) then CT_Action = "" else jump() moveto(400) return end elseif (CT_Action == "JumpPlayFwd") then if (posy() < 150) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (posy() > 300) then if (math.abs(bally()-posy()) < 18) then moveto(ballx()+bspeedx()) elseif (estimhx < 200) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(3,5) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() > 600 and oppy() < 150) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(4,6) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() < 600 and oppy() > 180) then moveto(estimhx-40) else moveto(estimhx-60) end else moveto(estimhx-60) end return end elseif (CT_Action == "JumpPlayRev") then if (posy() < 150 or touches() ~= CT_LastTouches) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(4,9) end moveto(estimhx+5*CT_ShotDecision) return end end if (touches() ~= CT_LastTouches) then CT_LastTouches = touches() CT_NextGround = math.random(-20,20) end if (CT_Action == "") then if ((ballx() < 400 or bspeedx() < -2) and estimGround < 400) then if (touches() >= 2) then moveto(estimGround+(posx()-500)/22) elseif (math.abs(bspeedx()) > 8) then moveto(estimGround) else moveto(estimGround+CT_NextGround) end elseif (estimhx < 600 and math.abs(bspeedx()) < 6) then moveto(280) elseif (estimhx > 650) then moveto(250) else moveto(190) end end if (posy() > 150) then return end if (touches() > 2) then return end if (timeBlock >= 17 and timeBlock <= 19) then if (posx() > 210 and estimBlock > 395 and estimBlock < 640 and not wallcoll) then jump() moveto(400) CT_Action = "NetBlock" return end end if (timeJump >= 17 and timeJump <= 19) then if (bspeedx() <= 7 and estimhx >= 65 and estimhx <= 420 and posx()-estimhx <= 120 and (bspeedx() >= -7 or not wallcoll)) then if (estimGround > 400 or bally() > 250) then CT_Action = "JumpPlayFwd" CT_ShotDecision = 0 jump() end end if ((wallcoll or bspeedx() >= -7) and estimhx <= 250 and posx()-estimhx <= 180 and posx()-estimhx >= -120) then if (estimGround > 400 or bally() > 250) then if (CT_Action == "JumpPlayFwd" and (touches() >= 2 or math.random(100) > 15)) then return end CT_Action = "JumpPlayRev" CT_ShotDecision = 0 jump() end end end end function timeToHitHeight(minheight, maxheight, depth) local i = 0 for i=1, depth do if (estimy(i) >= minheight and estimy(i) <= maxheight and estimx(i) <= 420) then return i end end return -1 end function timeToOppSmash(height) if (bally() < height) then return -1 end local i = 0 for i=1, 17 do if (estimy(i) < height) then return i end end return -1 end function r_estimx(time) local estim = estimx(time) if estim < 31.5 then estim = 63-estim end if estim > 768.5 then estim = 1537-estim end if (bally() < 330) then if (ballx() < 400 and estim > 400) then estim = 723-estim end if (ballx() > 400 and estim < 400) then estim = 877-estim end end return estim end function willHitWall(time) if (estimx(time) < 31.5) then return true end return false endblobby-1.0rc3/data/scripts/old/gintonicV8.lua0000644000175000017500000002040312042452377022461 0ustar danielknobedanielknobe--Gin Tonic v8 - No Comments, sorry :P CT_ServeSelf = { 152, 163, 180, 195, 205, 240 } CT_ServeOpp = { 140, 200, 240 } CT_ServeIndex = 0 CT_Tolerance = 5 CT_Action = "" CT_ShotDecision = 0 CT_NextGround = 9999 CT_LastTouches = 9999 CT_LastHeight = 0 CT_SkipNextBlock = 0 CT_WaitCounter = 0 CT_WaitName = "" CT_WaitMoveTo = 0 function IsAt(position) return (math.abs(posx()-position) <= CT_Tolerance) end function Wait(name, time, moveto) if (CT_WaitName == name) then if (CT_WaitCounter == 0) then return false end end CT_WaitCounter = time CT_WaitName = name CT_WaitMoveTo = moveto return true end function WaitQueue() if (CT_WaitCounter > 0) then CT_WaitCounter = CT_WaitCounter - 1 if (CT_WaitMoveTo > 0) then if (not IsAt(CT_WaitMoveTo)) then moveto(CT_WaitMoveTo) end end return true else return false end end function ResetWait() CT_WaitCounter = 0 CT_WaitName = "" end function OnOpponentServe() if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,3) end if (not IsAt(CT_ServeOpp[CT_ServeIndex])) then moveto(CT_ServeOpp[CT_ServeIndex]) end end function OnServe(ballready) if (WaitQueue()) then return end if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,6) end if (ballready) then if (Wait("ServeDelay",math.random(28,90),CT_ServeSelf[CT_ServeIndex]+math.random(-150, 150))) then return end if (IsAt(CT_ServeSelf[CT_ServeIndex])) then jump() else moveto(CT_ServeSelf[CT_ServeIndex]) end else if (posx() < 150) then jump() end moveto(40) end end function OnGame() ResetWait() CT_ServeIndex = 0 local timeJump = timeToHitHeight(380, 390, 20) local timeGround = timeToHitHeight(200, 222, 40) local timeBlock = timeToOppSmash(390) local estimhx = r_estimx(timeJump) local estimGround = r_estimx(timeGround) local estimBlock = r_estimx(timeBlock) local block = 0 local wallcoll = willHitWall(timeJump) if (timeBlock ~= -1) then timeBlock = timeBlock+(estimBlock-400)/13 end if (timeBlock == -1) then timeBlock = 9999 end if (timeJump == -1) then estimhx = 9999 end if (timeGround == -1) then estimGround = 210 end if (CT_SkipNextBlock == 0) then CT_SkipNextBlock = math.random(1,10) end if (posy() < CT_LastHeight and posy() > 150 and posy() < 330) then CT_Action = "" end CT_LastHeight = posy() if (CT_Action == "NetBlock") then if ((posy() < 150) or (timeBlock <= 8 and oppy() < 150) or (ballx() <= posx()) or (touches() <= 0 and bspeedx() > 9)) then CT_Action = "" else jump() moveto(400) return end elseif (CT_Action == "JumpPlayFwd") then if (posy() < 150) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (posy() > 300) then if (math.abs(bally()-posy()) < 18) then moveto(ballx()+bspeedx()) elseif (estimhx < 200) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(3,5) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() > 600 and oppy() < 150) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(4,6) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() < 600 and oppy() > 180) then moveto(estimhx-40) else moveto(estimhx-60) end else moveto(estimhx-50) end return end elseif (CT_Action == "JumpPlayRev") then if (posy() < 150 or touches() ~= CT_LastTouches) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (CT_ShotDecision == 0 and touches() == 2) then CT_ShotDecision = math.random(5,7) end if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(3,8) end if (math.abs(bally()-posy()) < 18) then moveto(ballx()+bspeedx()) else moveto(estimhx+5*CT_ShotDecision) end return end end if (touches() ~= CT_LastTouches) then CT_LastTouches = touches() CT_NextGround = math.random(-20,20) CT_SkipNextBlock = 0 end if (CT_Action == "") then if ((ballx() < 400 or bspeedx() < -2 or bspeedx() > 10) and estimGround < 400) then if (touches() >= 2) then moveto(estimGround+(posx()-500)/22) elseif (math.abs(bspeedx()) > 8) then moveto(estimGround) else moveto(estimGround+CT_NextGround) end elseif (estimhx < 650 and math.abs(bspeedx()) < 6) then moveto(215) elseif (estimhx > 650) then moveto(250) else moveto(180) end end if (posy() > 150) then return end if (touches() > 2) then return end if (timeBlock >= 23 and timeBlock <= 25 and CT_SkipNextBlock ~= 1) then if (posx() > 210 and estimBlock > 395 and estimBlock < 650 and not wallcoll) then jump() moveto(400) CT_Action = "NetBlock" return end end if (timeJump >= 17 and timeJump <= 19) then if (bspeedx() <= 7 and estimhx >= 65 and estimhx <= 420 and posx()-estimhx <= 120 and (bspeedx() >= -7 or not wallcoll)) then if (estimGround > 400 or bally() > 250) then CT_Action = "JumpPlayFwd" CT_ShotDecision = 0 jump() end end if ((wallcoll or bspeedx() >= -7) and estimhx <= 250 and posx()-estimhx <= 180 and posx()-estimhx >= -120) then if (estimGround > 400 or bally() > 250) then if (CT_Action == "JumpPlayFwd" and (touches() >= 2 or math.random(100) > 15)) then return end CT_Action = "JumpPlayRev" CT_ShotDecision = 0 jump() end end end end function timeToHitHeight(minheight, maxheight, depth) local i = 0 for i=1, depth do if (estimy(i) >= minheight and estimy(i) <= maxheight) then return i end end return -1 end function timeToOppSmash(height) if (bally() < height) then return -1 end local i = 0 for i=1, 17 do if (estimy(i) < height) then return i end end return -1 end function r_estimx(time) local estim = estimx(time) if estim < 31.5 then estim = 63-estim end if estim > 768.5 then estim = 1537-estim end if (bally() < 330) then if (ballx() < 400 and estim > 400) then estim = 723-estim end if (ballx() > 400 and estim < 400) then estim = 877-estim end end return estim end function willHitWall(time) if (estimx(time) < 31.5) then return true end if (estimx(time) > 768.5) then return true end return false endblobby-1.0rc3/data/scripts/old/hyp8.lua0000644000175000017500000002201612042452377021323 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 p0=0 p1=0.5 p2=1 h=31.5+19+25 estt=0 nettime=0 touch=0 est=0 p=0.4 esto=10 function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x) function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (t==ts) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=0 vyr=0 n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jump,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,20,0.2 do if (jump==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function netp() if (math.abs(est-esto)>0.2) then esto=est imp=0 x1=0 for t=27,0,-3 do t1f=t y1f=yp(t1f) t2f=math.floor(tb(y,vy,y1f+h,2)) if (t2f>0) then y2f=yb(y,vy,t2f) vy2f=vy-g*t2f x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/10,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) impt=0 if (t1f<=t2f-2) and (math.abs(posx()-x2f)/4.5400) and (math.abs(posx()-x1f)/4.5imp) and (impt<431.5) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>415) then break end end end end t2=t2+1 end t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>150) then jump() end end function lob() if (math.abs(est-esto)>0.2) then esto=est imp=0 x1=0 for t=27,0,-3 do t1f=t y1f=yp(t1f) t2f=math.floor(tb(y,vy,y1f+h,2)) if (t2f>0) then y2f=yb(y,vy,t2f) vy2f=vy-g*t2f x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/10,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) impt=0 if (t1f<=t2f-2) then for x1f=pos(x2f)+4.5,pos(x2f)-15*4.5,-4.5 do impf,_=impact(x2f,y2f,vx2f,vy2f,pos(x1f),y1f,220,1,0) if (impf>700) and (math.abs(posx()-x1f)/4.5imp) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t end if (imp>750) then break end end end end t2=t2+1 end t2=t2-1 move(x1) if (t2<=t1+0.1) then jump() end end function attack() h2=900 if (math.abs(est-esto)>0.2) then esto=est imp=0 x1=0 for t=27,0,-3 do t1f=t y1f=yp(t1f) t2f=math.floor(tb(y,vy,y1f+h,2)) if (t2f>0) then y2f=yb(y,vy,t2f) vy2f=vy-g*t2f x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/10,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) impt=0 if (t1f<=t2f-2) then for x1f=pos(x2f)-25*4.5,pos(x2f)+4.5,4.5 do impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,pos(x1f),y1f,220,1,0) if (impf>400) and (math.abs(posx()-x1f)/4.5316+7+31.5) then imp=impt x1=x1t y1=y1t t1=t1t x2=x2t y2=y2t t2=t2t vx2=vx2t vy2=vy2t xa=xat ya=yat vxa=vxat vya=vyat end h2=yb(ya,vya,(400-xa)/vxa) if (h2<316+7+31.5+20) and (h2>316+7+20) then break end end end end t2=t2+1 t2t=t2t+1 end if (imp>400) then t2=t2-1 move(x1) if (t2<=t1+0.1) then jump() end else t2t=t2t-1 move(x1t) if (t2t<=t1t+0.1) then jump() end end end function OnOpponentServe() if (math.abs(pos(posx())-posx())>1) then move(400) else move(200) end valid=1 end function OnServe(ballready) est=700 if (math.abs(pos(posx())-posx())>1) then move(400) else if (math.abs(posx()-180)<2) then jump() else move(180) end end valid=1 end function OnGame() esttold=estt netold=net x,y,vx,vy=ballx(),bally(),bspeedx(),bspeedy() estt,_,_,_,tnet,net=estimate(x,y,vx,vy,220) if (math.abs(esttold-estt)<0.1) then -- Schutz gegen Fehler bei der estimate-Funktion während Netz/Wandkollisionen est=estt end -- phase1 -> Eigener Angriff -- phase2 -> Gegner hat den Ball -- phase3 -> Netzball blocken -- phase4 -> Ball ist nicht mehr gültig if (bally()<144) then valid=0 end if (net==1) then -- klare Netzkollision if (yb(y,vy,(380-x)/vx)>370) then phase=3 else phase=4 end else if (netold==1) then -- Ball hat gerade das Netz getroffen nettime=8 -- Rest des Kollisionsskriptes abarbeiten phase=3 elseif (netold==0) and (nettime>0) then nettime=nettime-1 -- Zeit runterzählen für den Rest des Kollisionsskriptes phase=3 else -- Keine direkte Netzkollision und deren Nachwirkungen if (est>431.5) and (est<460) and (oppx()<700) then -- Ball kommt nahe ans Netz und der Gegner kann blocken phase=3 elseif (est<400-31.5) then -- eigener Angriff phase=1 else -- gegnerischer Angriff phase=2 end end end if (valid==0) then phase=5 end -- Überschreibt alle vorherigen Berechnungen toucho=touch touch=touches() if (touch=tnet) then jump() end if (math.abs(posx()-360)/4.5 CONST_NETZ_RECHTS) then --Wenn Ball auf rechter Spielfeldseite dann generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen end if (target > CONST_MITTE) and (ballx() > CONST_NETZ_RECHTS) then --Wenn der Ball mich nix angeht movetoX(135) --Dann auf Standartposition warten else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then if ((math.abs(bspeedx()) < 4) or (estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,2) < 0)) then sprungattacke(angriffsstaerke) else if (targetJump < CONST_MITTE / 2) then sprungattacke(-35) --an Rueckwand spielen else sprungattacke(0) --weiterleiten end end return end movetoX(target) end end function sprungattacke(p_angriffsstaerke) p_angriffsstaerke=math.max(p_angriffsstaerke, MIN_ANGRIFFSSTAERKE + ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so hoch spielen (kommt nicht auf die andere Seite) p_angriffsstaerke=math.min(p_angriffsstaerke, MAX_ANGRIFFSSTAERKE - ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so tief spielen (kommt ans Netz) movetoX(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben if (bally() < 580) and (bspeedy() < 0) then jump() end end function naechsterBallSchmetternFlagTesten() naechsterBallSchmettern = true end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) end function estimImpact(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx()/math.abs(bspeedx()) if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = math.abs(resultX - CONST_BALL_RADIUS) + CONST_BALL_RADIUS estimbspeedx=-estimbspeedx KollisionLinks = true else KollisionLinks = false end if (resultX > CONST_NETZ_RECHTS) and (estimbspeedx > 0) and ((KollisionLinks == true) or (ballx() < CONST_NETZ_LINKS)) then -- Abpraller am Netz unterhalb der Kugel erst wenn Netzroller ausgeschlossen sind resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end if (Frage == 1) then return resultX end if (Frage == 2) then return estimbspeedx end end function movetoX (x) if (math.abs(posx()-x)>math.abs(posx()+4.5-x)) then right() done=false else if (math.abs(posx()-x)>math.abs(posx()-4.5-x)) then left() done=false else done=true end end return done end blobby-1.0rc3/data/scripts/old/axji.lua0000644000175000017500000000231012042452377021361 0ustar danielknobedanielknobefunction OnOpponentServe() moveto(120) end wait = 0 serv = true aggroservice = 64 -- versetzung zum Ball je gr�sser desto tiefer fliegt der Service 0-64 function OnServe(ballready) moveto(ballx()) -- unter den Ball stellen wait = wait + 1 -- waittimer anwerfen serv = true -- servflag setzen if ballready then -- ball bereit if wait > 90 then -- 90 einheiten gewartet jump() -- Springen also eigentlich den Ball aufwerfen wait = 0 -- Wait timer zur�cksetzen end end end function OnGame() -- bestimmen wenn der service vertig ist if (ballx() > 400) then -- sobald der ball auf der anderen seite ist ;) serv = false -- serv flag zur�cksetzen end if serv then -- sprunganschlag code moveto(estimate()-aggroservice) -- Bewegt sich unter den Ball und schl�gt mit einer gewissen aggressivit�t an bei 64 knapp �bers Netz -- warscheinlich w�re ein Match.random() angebracht if bally() < 550 then -- wenn der Ball wieder runter kommt jump() end else if (estimate() < 0) then -- Falls der Ball von Hinten kommt moveto(math.abs(estimate())-100) -- funktioniert noch nicht richtig else moveto(estimate()-20) --sonst immer leicht hinter dem Ball bleiben end end end blobby-1.0rc3/data/gf2x/font_small/font10.bmp0000644000175000017500000000036612042452401022266 0ustar danielknobedanielknobeBM6( ```VUV$`^`ZPPP_\` _  efez{zpÛK0L rp !+!yw  +@  Z' ' \,[oMnCt^-Js}iA:bWHQY0blobby-1.0rc3/data/gf2x/font_small/font31.bmp0000644000175000017500000000036612042452401022271 0ustar danielknobedanielknobeBM6( *,*!'!R_R'4'vv  .O-ut1i2$k#..IL / Y -6B (-]40SR9{Ga1blobby-1.0rc3/data/gf2x/font_small/font13.bmp0000644000175000017500000000036612042452401022271 0ustar danielknobedanielknobeBM6( }&{idH>.$)'b|,kUscaO~T*M861oS/3(.k{e)4#;*YR9-" C4U/ Q # # 3 -(H#slSKKA-c"blobby-1.0rc3/data/gf2x/font_small/font14.bmp0000644000175000017500000000036612042452401022272 0ustar danielknobedanielknobeBM6( /Y+h M@5*F6M,1E)58), M9% EN#> 9P.kJCF[)MW$KV'S^ ]W?izWoV&blobby-1.0rc3/data/gf2x/font_small/font32.bmp0000644000175000017500000000036612042452401022272 0ustar danielknobedanielknobeBM6( 4+  *I 1QCX pO k#,F0z x,-%$.` /kZGG,.NUB'1 0]]9 LP > &C ""<((m Oblobby-1.0rc3/data/gf2x/font_small/font50.bmp0000644000175000017500000000036612042452401022272 0ustar danielknobedanielknobeBM6( 2ERnCp /#/((  blobby-1.0rc3/data/gf2x/font_small/font15.bmp0000644000175000017500000000036612042452401022273 0ustar danielknobedanielknobeBM6( 'xc/+RQ/eV f:  ze`<2?/,G86;/.  xfbǠmjqo{jh O@@ԩoo1blobby-1.0rc3/data/gf2x/font_small/font33.bmp0000644000175000017500000000036612042452401022273 0ustar danielknobedanielknobeBM6(   ,`.T U 9l;*'n 2Y3,+ 3; 7b;H"9OW^CZ O8Fl%>Jv blobby-1.0rc3/data/gf2x/font_small/font51.bmp0000644000175000017500000000036612042452401022273 0ustar danielknobedanielknobeBM6( blobby-1.0rc3/data/gf2x/font_small/font16.bmp0000644000175000017500000000036612042452401022274 0ustar danielknobedanielknobeBM6( M5&M#&l2<v?>QQ}rr}|^VMzggA43B30XH<|Q $V;7eTJkoM#7Q88TK%2J5|["blobby-1.0rc3/data/gf2x/font_small/font34.bmp0000644000175000017500000000036612042452401022274 0ustar danielknobedanielknobeBM6( ,< 7Ko/k +i5..KN8FJX7Oa%#Ubz:^#vp:~B$ [([  Pa%EXs 9blobby-1.0rc3/data/gf2x/font_small/font52.bmp0000644000175000017500000000036612042452401022274 0ustar danielknobedanielknobeBM6( )*')  ~3Ybɸfa1 2blobby-1.0rc3/data/gf2x/font_small/font53.bmp0000644000175000017500000000036612042452401022275 0ustar danielknobedanielknobeBM6( bbbKKK,,,@AA ABU]Y3 5XvbCzL-. [h ( JO+/blobby-1.0rc3/data/gf2x/font_small/font35.bmp0000644000175000017500000000036612042452401022275 0ustar danielknobedanielknobeBM6( g$$ {"#'3UX U 4= Y%6_6o ,= 0R C($>:^Ht~,!vv}T34d%f[]ruY \!8:,.\blobby-1.0rc3/data/gf2x/font_small/font00.bmp0000644000175000017500000000036612042452401022265 0ustar danielknobedanielknobeBM6( F^FCA41X`ba\x\)PMpp1B1 KH [m~m;Q;(0,NVNuu31 G blobby-1.0rc3/data/gf2x/font_small/font03.bmp0000644000175000017500000000036612042452401022270 0ustar danielknobedanielknobeBM6( 45Bnttzdi02V FGV01<QRyJJ++Z("f ? :  704-_C %$9YT#t h;1 ?XE_U`ZC?! pblobby-1.0rc3/data/gf2x/font_small/font21.bmp0000644000175000017500000000036612042452401022270 0ustar danielknobedanielknobeBM6( DNEPhT'E//d:j)awi=PB2J8 caeqivzZ}c4f!!]TY\9(:\blobby-1.0rc3/data/gf2x/font_small/font04.bmp0000644000175000017500000000036612042452401022271 0ustar danielknobedanielknobeBM6( Q 1> *#   6&b_+(# W!XW+, Yvv68no#$c MMblobby-1.0rc3/data/gf2x/font_small/font22.bmp0000644000175000017500000000036612042452401022271 0ustar danielknobedanielknobeBM6( MB7#)}-s.KP)6"+]77 T .4,d7`'((7HR><FG,^/-C1;p:# !][&G&  B? KyJ5b4A?  WaW5<5blobby-1.0rc3/data/gf2x/font_small/font40.bmp0000644000175000017500000000036612042452401022271 0ustar danielknobedanielknobeBM6( ]Z%%(( j7 (DC'#0/7A6N20}R3.S9I:N9|yMr>  |\0%\GgC  jM~~@@D5 f`Sp)tblobby-1.0rc3/data/gf2x/font_small/font23.bmp0000644000175000017500000000036612042452401022272 0ustar danielknobedanielknobeBM6( 8 E99o((R 99 4nm!!iCC!!VRHL,,?" 3I=?_L&31 38+RQ*1.R=8]7blobby-1.0rc3/data/gf2x/font_small/font05.bmp0000644000175000017500000000036612042452401022272 0ustar danielknobedanielknobeBM6( > ',- 6{<)Z2+I0@XR5:ut*< &"0 =xMATR  (i ,Kg+W -3K2_k&   ^c=B-3 Nblobby-1.0rc3/data/gf2x/font_small/font41.bmp0000644000175000017500000000036612042452401022272 0ustar danielknobedanielknobeBM6( no5 !c8Y PO,zh\ /JE>1B% Op*   H0.O62yMadD4 ! yN#6R.0% ! 8 ofF7$sblobby-1.0rc3/data/gf2x/font_small/font24.bmp0000644000175000017500000000036612042452401022273 0ustar danielknobedanielknobeBM6( v2"-$13RHMT Gb`?-- a)(9zo ''HCCs* 1ggAAqycD2H pn%!&/#0a_e^S]blobby-1.0rc3/data/gf2x/font_small/font06.bmp0000644000175000017500000000036612042452401022273 0ustar danielknobedanielknobeBM6( 4#!2 ;p$ 4T:JQ"T,EOFNv #0"'\h#p0)Z$4+oɊ%_A(_%fz!AMe?\blobby-1.0rc3/data/gf2x/font_small/font42.bmp0000644000175000017500000000036612042452401022273 0ustar danielknobedanielknobeBM6( B>OMpoCBR%6tg ~}`\a*!1b9/0x -&'cd]<]NVXPwJy$W"*,V1@Ca'/?;!(V[!#blobby-1.0rc3/data/gf2x/font_small/font25.bmp0000644000175000017500000000036612042452401022274 0ustar danielknobedanielknobeBM6( %QOop+0iAm~+BH J,-F*Ύ+B?@??*  ''T ;jhjKJ"}< :423nIm@+>Qv>q#2g EA{8u-$E4/ S$=J^JTN<:"=tT:D"  [m=y?Z;Pblobby-1.0rc3/data/gf2x/font_small/font08.bmp0000644000175000017500000000036612042452401022275 0ustar danielknobedanielknobeBM6( K5aYjYH+J y>o9 B4Cbh !<!gDhzF|M`b7 H "BE@d4f[.UâB D KHB@( )blobby-1.0rc3/data/gf2x/font_small/font44.bmp0000644000175000017500000000036612042452401022275 0ustar danielknobedanielknobeBM6( UzT @O?P~Oblobby-1.0rc3/data/gf2x/font_small/font27.bmp0000644000175000017500000000036612042452401022276 0ustar danielknobedanielknobeBM6( ) t_ f+ " b(8+ 0<-i a)+  c ?E(,7'?I  .1^  LWJk9zC-{blobby-1.0rc3/data/gf2x/font_small/font09.bmp0000644000175000017500000000036612042452401022276 0ustar danielknobedanielknobeBM6( "GJG9Q9  6F5dc(_&utc_)/(ŏ YXSQvu87   1-# ec308 /<_;MJIE:6"blobby-1.0rc3/data/gf2x/font_small/font45.bmp0000644000175000017500000000036612042452401022276 0ustar danielknobedanielknobeBM6(  $"$)&)blobby-1.0rc3/data/gf2x/font_small/font28.bmp0000644000175000017500000000036612042452401022277 0ustar danielknobedanielknobeBM6( z y- zf#QyTPD_ CX)`+.5<&%NCD+~3" Q K%#.&/ 6#&3]n .gAGvT 1\DB73)y2\y)bA*=GNeǜ;blobby-1.0rc3/data/gf2x/font_small/font46.bmp0000644000175000017500000000036612042452401022277 0ustar danielknobedanielknobeBM6( N9;3!3v0yXZe%h3 5blobby-1.0rc3/data/gf2x/font_small/font29.bmp0000644000175000017500000000036612042452401022300 0ustar danielknobedanielknobeBM6( W^ 23??h6 :< HKI S!%O k "1G g,CDaLb@V5F$1 blobby-1.0rc3/data/gf2x/font_small/font47.bmp0000644000175000017500000000036612042452401022300 0ustar danielknobedanielknobeBM6( HG  _]].+-/W.@=F4;7L]}795 2 PM%k#-+b`_^blobby-1.0rc3/data/gf2x/font_small/font48.bmp0000644000175000017500000000036612042452401022301 0ustar danielknobedanielknobeBM6( xzx(*(,,,TXTblobby-1.0rc3/data/gf2x/font_small/font49.bmp0000644000175000017500000000036612042452401022302 0ustar danielknobedanielknobeBM6( bbbKKK,,,@AA U]YXvbCzL [h ( JO+/blobby-1.0rc3/doc/codeconvention/codeconvention.pdf0000644000175000017500000047350112042452365024052 0ustar danielknobedanielknobe%PDF-1.5 % 1 0 obj << /S /GoTo /D (section.1) >> endobj 4 0 obj (Structure of files) endobj 5 0 obj << /S /GoTo /D (subsection.1.1) >> endobj 8 0 obj (Includes) endobj 9 0 obj << /S /GoTo /D (subsection.1.2) >> endobj 12 0 obj (Coding Style) endobj 13 0 obj << /S /GoTo /D (subsubsection.1.2.1) >> endobj 16 0 obj (Common) endobj 17 0 obj << /S /GoTo /D (subsubsection.1.2.2) >> endobj 20 0 obj (Classes) endobj 21 0 obj << /S /GoTo /D (subsection.1.3) >> endobj 24 0 obj (Namespaces) endobj 25 0 obj << /S /GoTo /D (section.2) >> endobj 28 0 obj (Things you should not do) endobj 29 0 obj << /S /GoTo /D (subsection.2.1) >> endobj 32 0 obj (Pattern) endobj 33 0 obj << /S /GoTo /D (section.3) >> endobj 36 0 obj (Stuff that must be integrated in this file correctly) endobj 37 0 obj << /S /GoTo /D [38 0 R /Fit] >> endobj 40 0 obj << /Length 204 /Filter /FlateDecode >> stream xڅAk0 :xгoc4n;qi{zHXaW'̆ A YΪ6s?~ ʧA5]n-;> endobj 41 0 obj << /D [38 0 R /XYZ 88.291 795.961 null] >> endobj 42 0 obj << /D [38 0 R /XYZ 89.291 757.701 null] >> endobj 39 0 obj << /Font << /F20 43 0 R /F21 44 0 R >> /ProcSet [ /PDF /Text ] >> endobj 48 0 obj << /Length 4309 /Filter /FlateDecode >> stream x]s6>>RWGLvǍJ%+ْȒWx_7Hٙ 0GF|3~y+GB3_|`2Ӗ9 %υOn7DYW0/\|;Wg/G u=pP`cQ܎'1QJrbM7]OTz\+wUfq;o*\Πt38Ce9-,A=phOj '2oZgR;%;t֙;NQXrRW$!T0xkHoVL({f5ax¥cqQNo,5\Po?Zë?ƉϿ1u'TikiFVZ@38vE0\JoV:r4F_'N˘cz,{<#8Sl fV6,o-d8{ÏqޕSq|pփ3]Y%h% <\ǁM|Ꞩ Q Q5Qhx4gA;xXmfm7PEt+zF*kӬ)@کEt::ڦ T SPHMuMԔQCԖQs.ZԆ$Uuu£T` ?ΛDxpp4'pƩn+n-a}k|M[,0?6W"D+.|t @-& Qo!% NY+tEBT"ΉZԢ1E8SM 9fݡcר5ҸD% [ŷdЍQg|A=8TuKs)8Ur_m `֋tg2NU)C~|< >top;:L:^T2%7y_,pa'0BE_TVs wecT;m(KpՊ/yg,Gg\ϊN).ፁGЂ$͢fg )8'C\pFR(ҠmDBʃ hJS\ߧ"m%V`8 u E0,\5IoVi`uj&-A>N@ # ^gdĉ̳6^$8|vH71E3#RGseY/-௩ _|=Ǽ[ъ%r4Me^0DE#iiMJDe7.sAMLED)he[cg6.(fi6R D]5'j{e:bNSSWN]S["[ÜtgHiN<+?bp+'gm-2'K QvG,|S ыB"WQ%P׋Ɲ5rJeהˊ$iAVSkjVe &ܨ={sX<"5IJj~[,{kДp4z!@8tȴE0,\5HoV@0UmM*oyg<:wyȨj-eho%)o ylⴜqw6b0Q6,/1CkP jMTB7+,ՕpŸ:NTWҕSWWdp?]Yu 1A1:`c9uԱ# cËR۟Uz؎ i>|ta;(,E'XflGK˫]PoBV䝉Ӿ8"jXgnG Rq^O-8q^Ѥ zya]0oicqK;ˍWT'j0dTPwm q} Ʃ+-a1⭁I|J;"H uZtO"IcdV e+{y[>^XglȽbߍqI駬[H}LbzJ0^%:NP2Q*ԞPq ):*S"5*z(Pot& hMU̓(if Nj0NE]9uEm o۟U ru:} ۱KLba&ar>/1yɛZ( p2ArFINVY"gQ\cɛ-Y[DZu=eu޹fFǹRDQg{xo`}l ;q^#NE[d搥="?~MnCɀ6 h$`$"<V<ִ /:Ɲ^&ϣ]yޫYqF&Uc'8 8tE0\HooV]*eҜ`oquU"Vo*mAz30^"d ),m;D-;≣.gT  ?P Y׺iVM"lqq2pC޹ec?ѹ"מ{ۯ=t+zXY pk0N-]9u-m [o5۟UZ,TXg`V{p#`m偰x{+] F]uOZ:˘J[\ͽ{Կ ۓQRd]G;}|t,x@AOq*Áq ʩ l`X8xkÕ.ެ:Y'm0NԱΜ:ؑձ[)*+Bo#c4Lm${ZSu3sY0z)jDaM6>l 죮t0d6=8բuբE0E\JooV] ,%`c]9uձձ[+F|JKU&ޡ4>oAbf{$Zz^o2z, //+weWr={wo6󣧂x*3Q)"(G63g<{+*}xEOE߃r6Lط$U<`JxwpRES gM UCb5?|\o6]0/j88K>YÏJ}]u|t@tۥ֫C^l?; yF={#> endobj 49 0 obj << /D [47 0 R /XYZ 88.291 795.961 null] >> endobj 2 0 obj << /D [47 0 R /XYZ 89.291 757.701 null] >> endobj 51 0 obj << /D [47 0 R /XYZ 89.291 699.945 null] >> endobj 52 0 obj << /D [47 0 R /XYZ 89.291 700.522 null] >> endobj 55 0 obj << /D [47 0 R /XYZ 89.291 691.057 null] >> endobj 56 0 obj << /D [47 0 R /XYZ 89.291 681.593 null] >> endobj 57 0 obj << /D [47 0 R /XYZ 89.291 672.128 null] >> endobj 59 0 obj << /D [47 0 R /XYZ 89.291 662.664 null] >> endobj 60 0 obj << /D [47 0 R /XYZ 89.291 653.199 null] >> endobj 61 0 obj << /D [47 0 R /XYZ 89.291 643.735 null] >> endobj 62 0 obj << /D [47 0 R /XYZ 89.291 634.271 null] >> endobj 63 0 obj << /D [47 0 R /XYZ 89.291 624.806 null] >> endobj 64 0 obj << /D [47 0 R /XYZ 89.291 615.342 null] >> endobj 65 0 obj << /D [47 0 R /XYZ 89.291 605.877 null] >> endobj 66 0 obj << /D [47 0 R /XYZ 89.291 596.413 null] >> endobj 67 0 obj << /D [47 0 R /XYZ 89.291 586.948 null] >> endobj 68 0 obj << /D [47 0 R /XYZ 89.291 577.484 null] >> endobj 69 0 obj << /D [47 0 R /XYZ 89.291 568.019 null] >> endobj 70 0 obj << /D [47 0 R /XYZ 89.291 558.555 null] >> endobj 71 0 obj << /D [47 0 R /XYZ 89.291 549.09 null] >> endobj 72 0 obj << /D [47 0 R /XYZ 89.291 539.626 null] >> endobj 73 0 obj << /D [47 0 R /XYZ 89.291 530.161 null] >> endobj 74 0 obj << /D [47 0 R /XYZ 89.291 520.697 null] >> endobj 75 0 obj << /D [47 0 R /XYZ 89.291 511.232 null] >> endobj 76 0 obj << /D [47 0 R /XYZ 89.291 501.768 null] >> endobj 77 0 obj << /D [47 0 R /XYZ 89.291 492.303 null] >> endobj 78 0 obj << /D [47 0 R /XYZ 89.291 482.839 null] >> endobj 79 0 obj << /D [47 0 R /XYZ 89.291 473.374 null] >> endobj 6 0 obj << /D [47 0 R /XYZ 89.291 436.44 null] >> endobj 10 0 obj << /D [47 0 R /XYZ 89.291 297.837 null] >> endobj 14 0 obj << /D [47 0 R /XYZ 89.291 248.03 null] >> endobj 46 0 obj << /Font << /F20 43 0 R /F17 50 0 R /F33 53 0 R /F34 54 0 R /F41 58 0 R /F45 80 0 R >> /ProcSet [ /PDF /Text ] >> endobj 83 0 obj << /Length 1269 /Filter /FlateDecode >> stream xXKs6WVrQ|Kdҩ㺓N(1! _]`A' `Rl? g?~7g4Ealeq6[yL٦]Qq ?YFBR(zZ0Mbvm vG'(sӼC3unl6iE)ھOT2{m%Q åPMqGO }"4X/Y _QoKk.([+H=U~&EY5&5U'9 y_P Zzi! -L*͖h{ VMb2N Iqyp*riE ',}M,m&+5z ^<`ktEhɄ8<1h\V%ڎDbWDžnfUW nݛ~Eٻft-8]_.. !,6+\j8AT Z;P,Q Qa3HJCغe3TNĔCye[G2|Hޣ|Pxb/CIi)%!Nݱ^FQy%d;fɺrLhBjݸv0ӗp_ e`(⚅ǦPЊ&w|V [?t]|fҢeŊF m4Qkźa?F'}FpFf-y;ڇ/$/ ;ȜqI*L[LDZ i_?DHװ` A Ǵ辏 PB|"2l^.RhVXօC4;qz}+VEeܦfJm1}HזVbz! j@ wU1ڟE~7\~+bՃ{c Li5:x: rw11ϥm˜}yd"kyG㔂2b5(4"}@E] &?~LZ>u\0~Ns40\wuA_W3;G5 YOuQ5 \o32$L%x||gqy* J~9 _ endstream endobj 82 0 obj << /Type /Page /Contents 83 0 R /Resources 81 0 R /MediaBox [0 0 595.276 841.89] /Parent 45 0 R >> endobj 84 0 obj << /D [82 0 R /XYZ 88.291 795.961 null] >> endobj 18 0 obj << /D [82 0 R /XYZ 89.291 463.553 null] >> endobj 22 0 obj << /D [82 0 R /XYZ 89.291 229.437 null] >> endobj 81 0 obj << /Font << /F45 80 0 R /F17 50 0 R /F20 43 0 R >> /ProcSet [ /PDF /Text ] >> endobj 88 0 obj << /Length 863 /Filter /FlateDecode >> stream xڕUMo6WHDK"c7E.@Sʢ@QM;lZDqf z[/n‹>Hr!ۼyy"2L~c$I6~ Ҍ|1=f鱭;edc}yQȋC bȚ)sg!Y;[Ӽ X{oVZaƴ-a^ ΘQ*?b3b E37hC$\<<"*gy!O I!6;EtjxC/C n8HL9{:Th:u{AD&D"lbD)y_ك0 % sGvRo7k]nu}F vn dsӕ[%iw%whtVmMifO{;Ձ֡+` =o}EJo~Rm@$^g-i Zn\hhrAPոw'g%;X5}%otByX$1@픪! \f~.=S;4U'h8>\(419g|)rW}O'`,E߇"V$AOCۋÔm%r^ӻOX @Ce2Wz#QĐD<9ಮTc׏M0ϪoKlYU aN'dH xh׊WW^"`pSWOyt:4շIT0?+{|9E TXg@K8Iu_I*>8 lLԲ#rFaNװHQ坿:aӨ1+xV8E?Z endstream endobj 87 0 obj << /Type /Page /Contents 88 0 R /Resources 86 0 R /MediaBox [0 0 595.276 841.89] /Parent 45 0 R /Annots [ 85 0 R ] >> endobj 85 0 obj << /Type /Annot /Border[0 0 1]/H/I/C[0 1 1] /Rect [167.689 646.344 433.135 659.245] /Subtype/Link/A<> >> endobj 89 0 obj << /D [87 0 R /XYZ 88.291 795.961 null] >> endobj 26 0 obj << /D [87 0 R /XYZ 89.291 757.701 null] >> endobj 30 0 obj << /D [87 0 R /XYZ 89.291 705.004 null] >> endobj 34 0 obj << /D [87 0 R /XYZ 89.291 630.901 null] >> endobj 86 0 obj << /Font << /F20 43 0 R /F17 50 0 R /F45 80 0 R /F46 90 0 R >> /ProcSet [ /PDF /Text ] >> endobj 92 0 obj [525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] endobj 94 0 obj [777.8] endobj 96 0 obj [826.4] endobj 97 0 obj [531.1] endobj 98 0 obj [413.2 413.2 531.3 826.4 295.1 354.2 295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1 826.4 826.4 826.4 501.8 826.4 795.9 752.1 767.4 811.1 722.6 693.1 833.5 795.9 382.6 545.5 825.4 663.5 972.9 795.9 826.4 722.6 826.4 781.6 590.3 767.4 795.9 795.9 1091 795.9 795.9 649.3 295.1 531.3 295.1 555.6 795.9 295.1 531.3 590.3 472.2 590.3 472.2 324.7 531.3 590.3 295.1 324.7 560.7 295.1 885.4 590.3 531.3 590.3 560.7 414.1 419.1 413.2 590.3 560.7 767.4 560.7 560.7] endobj 99 0 obj [583.3 555.5 555.5 833.3 833.3 500 277.8 373.8 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 777.8 777.8 777.8 472.2 777.8 750 708.3 722.2 763.9 680.5 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.5 777.8 736.1 555.5 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 555.5 750 277.8 500 555.5 444.5 555.5 444.5 305.5 500 555.5 277.8 305.5 527.8 277.8 833.3 555.5 500 555.5 527.8 391.7 394.4 388.9 555.5 527.8 722.2 527.8 527.8 444.5] endobj 100 0 obj [272 326.4 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 761.6 761.6 761.6 462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.2 734 353.2 503 761.2 611.8 897.3 734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 500 272 555.6 734 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6 544 516.8 380.8 386.2 380.8 544 516.8 707.2 516.8 516.8] endobj 101 0 obj [641.7 586.1 586.1 891.7 891.7 550 366.7 577.5 916.7 550 1029.1 830.6 305.5 427.8 427.8 550 855.6 305.5 366.7 305.5 550 550 550 550 550 550 550 550 550 550 550 305.5 305.5 894.4 855.6 894.4 519.5 733.3 733.3 733.3 702.8 794.5 641.7 611.1 733.3 794.5 330.5 519.5 763.9 580.5 977.8 794.5 794.5 702.8 794.5 702.8 611.1 733.3 763.9 733.3 1038.9 733.3 733.3 672.2 343 575 343 555.5 733.3 305.5 525 561.1 488.9 561.1 511.1 336.1 550 561.1 255.5 286.1 530.5 255.5 866.7 561.1 550 561.1 561.1 372.2 421.7 404.2 561.1 500 744.5 500 500] endobj 102 0 obj << /Length1 2620 /Length2 30939 /Length3 0 /Length 32446 /Filter /FlateDecode >> stream xڴUX[5 ww)! /w ?;oSOɘ>ǚkU1ٛA.,lE%u{;c;: ƅHM-4vI.S/؂ J3'@ bdT]Mj H;x:YYX;ooqdgQb(ۻV:{hilk7hudU4UѳsupwW-44ebR@FO \@Yl]IJCLCWUwvw\Oi`Ws'{,]\XYY,\]X,XlOnd;m 2b ;U(ZAN+Tr&wLۿ@4vWQUU`glrA`CcWg_2hFw@JV9'ͿKwoWnmS{s+[/; fEX\<\OLRQ`T d&aog7}V`\/(7Uo`xUJX v ` p?AgPgP88û?  p> ~LعŘ cqMmo oݟl&<MVڻ:ܶş-~ߘkS KOK `? xy܍l ܮbn` l8b?bp@'+?rquSX>Cڒ֕Ldg, V:[yfqvtܞ?1\Lrh==DzDQ}u]9b~doԶ2?jDC |ֳ׿}x{x3sq9'SZ~/_ӿoYo@Sĥy{S`ԦR_2j~:KDy@€LB{EY/:8/_*'Dw}|PFrX43:)s?sMgƷ4G%ۻ9_1~P藵øͲ7c;bz,v-Nv@cFw-1}uޅ U$I) Kq}YN/ܹďB@ѣ*d|skgI ڜcF~+]QT+݇6Tv2^ꙡm"BH R%L_utG_5.G$2OXF BXRP U6X- `(XǽysYq.}\W͞Z#3$~߸q@VF&%ׯ:sI5 d.|H<T񶋇lJ?ÐW^P=sZ2|O[T_a`v8sȣc-D.8gī9cqj0i\wډѡuhʍZKO3 r/YOIX>!q1J@X3We*nO/kGo*'@d*sYj8 ]Ŕ C^HcE 9䚅z{ >(|dzp15|9*.IԦM[/|upERN#}omDnE{ARncӿO"{Ӷ)=( *Ĉ-` -q]0˝tрo2E#++R<Z G:x#(0gxm8PuDݴXߏQrn*7Tggf hD\,,Zos;׸~B,4j?DZNˑmW87SmEeT}YRJ۳bBɱs;bzMf>+xԮ.Nh*|RDW W/g9 A֒aVգ:21awhwð$UAc+]h2,;j/T{ 'n:Ve~.mޕw +{6d: IxV|gvs 63:T~9e_B>[>)dlt17| K.J=LȀ ECfsC|blqlxd!t#9)yu' (k15Xx)!:#pG‰1Qi1e'ߔESrb"D:LbƖ֯4o\A}C)Շ%nUj^gWd9ӂ˽HOwO?ѩh qdz3BX:6邯mǾSoatI6ZЈuB(rRfFQɱW#ըzEiFa]XGBG :.guު:q6;_+@ ܈"حՕ%d/u%m$ ʴx)ǵ{_bNiHynf,G>gA<9h mvwހ] K_T:V[DŽ?\l?[ 64g%8~FS Jk˄/v:E@wx=|AN>T}\8nFχ$vjPA6DΈ}aeO> @!!뒢qQlZG.q;q$o>#%-% @.a9В"FR-\t))$X0}l7Tu9y##AIѲGCrN߬2+RWyüF!2 GE3ܞzzc{st]YC`Of;Sꝡm# 6+'+@*7Ͷ׉Z(ƽZł7{V)2;q@\Rm*aAV%\>Zq#W^,*?|:'_V}AAWIxvā>ߺZLxK^u4?|i]];s!2^0kl95 FS5f<ql'~"]D_rnk.OSlz~WίiJ;>MA*S~w~NJJʹď՝vi$2zh&3̛;r^=| k N)uo.#W3ݹ{ۥ)9A3*q3UTb;ʏUׅive * !Wy1oVLM/$gI5fnG&zo\B5-TqNGF7?X&ix3ZѲzH;%쑥DxVbLG>#I yUO Kŷ%r)akKGNd`@G-_f؇h3|z _eE٪k*GCds3OUk 5q&"剘L1Ϝ,3v/1{Ⱥ{r턺UUjڇW[Ző XH䱿 Ĩ,#,MV/eD.xB{[:_8IC<5=Vڵfku5W)1!hjAqOmX}2>ϣۙLi`-b9~ w5l1a(ξ.x㿌z.w3ФB؎.jAw$އ97GȜ~fuc$yj0uQP!|\k><#tNZ='Rcm`B::n#@,:N,dHsǏ˻c;dUsI$iق)h+xUCkܷ:)tvm#̞œ2g.*zOBNO=IyŊY&rNd<-)oO2$*kjnhnW\'x IƻEן.k]!Q5 Y=5+\%uݛ%;!5ۤ½Y6Kf&3|(;a5Jq:OtX>6 ?9K*bIÕaTQ;";*\ Yan5~W~"-KjB.GhB\źOr>=ֺU.X\.ŕ$(EX ja~CG۟=:O&2pƹ~EL ֦ ]U#su'\b2:7oJ uơ$'N{fF%{~Ȅ!h8f>*?,*b`@8;~ իblS#2By |7_ΎCg/IkL۞8tUMY, ۑ[: h*ص!DY>YwX'#L qoa\lY1׋hGߣ`NO Ǐ3"T ;\K, ZQ3 )}ӂHzꏠL->5m@E{-z1R* (աblE~MT ER qj4i@.N"+ydé젵pA:wei+uk-[*v,vB106Kɔ(Zyx8*y?n0%}␏_0 ůh4h7IK^NpQG/_L8[Z>`nzl06H3Zp<$_7Jal;>lR tm`3@*0n5m Yf}[ŦZu) LnRgӎ im57a ) k$+ǼO\Rm7q>@]rF-h2؊arqT;.52L}/]1ACl'i3FNCƉ׵rYGؗ BOPqȪi3뮙ym_8tfFKmFTف!&GP3n|/#t,XHџ-qK-B Z=E+*[5pKĞ  םeEm /ly+`[-}î'zQՍy,}A81(EsrO (|`PjyƜEV'?0xM7Vd?+~¬k$F/Qvx7$+Oy 9_o yh$WjZeR!jɶyZY3rjS^UpUagXX-Z $W2dĸS7[ǾFP9U9+ĢLخKmBB ~bcOqmx2ͳ!˂o4|=tNuDu=E|4ٗQ]N&DJ#C>r7Iܮq!Pvhdba)z"5ɠu b ̒h :id `Bz0Gt46}zݝS  ͭOeJMzxCCFc8$-i o> NDž4z5AjIP,+ ðsLk2~ΔN2oTl"tv{ JqR֝ljOEBKӹ>:RZrQ2ųȿ^Nˏ%vA/|ȧX ";/TiEpa<[zBRdzd1ilI##jJٰanːaVeQY` |j_W$+)ΡC);ګ qgtSn^z6$JJ(MieO QݼhAh5 sAϞ履a /p q Ab3AJd\R+7VjsZ%wa\s/g$h ~ʧȟe7( N: 44 U$+YI鬢x/726Ir#Kcc[3zaıiqbLۆb ^GR&!m6  kdum j4fʗG+RᙅYyYa羖%c(l_?ؗM@* g]8fҟp,nqƌX/*@ȿ! ll!UGaIymEj\CP&I5\$'m>{N$[\^oдԧ_KMJ갿1hC Ew@<ִxP:R~ DR-m1xi ~/E}9M~5EKg]0؜R>אn1:9> xǢ:xs tLl pESm%%5!k^g!*9i7C9jԦi[NRlŶs8_Oor}A(~LSzh3@chQ?BI&7]U6($N/RϥQ]]@`)lYMLOэDaHZs\"J}WH7fgiŕG^">ޏ!C|%Pϱ8{d(BIu -N1)' ߍpnА甭P Ǵ3;,{h`[h m)-Hig.GrB5%gVRŴALQ]:s:ԴRpԶM^1e+13CeBӓv`Ӫ7\!3$:0,n̹(ga6|AHk:+V@ݓDex 4 ,h5j<w#4lC+;Ek35bc2#͚Bl}{^s`JZq6tn,pD1A`Pdq˄8\'sN_أاw xhzb@c< y{縘+S'u)L2f&ҙޡ0e_]Mi}|zV'L)SOH/6ZEFƔupPywdJxSGe$*Q":f[QAP:p@Ő~\vCb43q &cX#m|2גQ=f2Hz1]K w- q"8{D Vy)ܹ[ECBB 9j7鴀]h '"yӜ#:8 e{IFNM0š|$:R;kw"cowe'@&Mg%]kRoIC bҹgíbݚQXZH{kG o˛K%u>N=6|݊(Zeٶ;uuOA%%|rl 8_!vfk.AMt}7ܣOsAn22|cl/TWhcE^zS~H՛! \-*7=)ybZW"oF F[G2hUkNM6ѯa&BUupu\[܆jKU._$:bRnj}Ԝ*jMFÞ` aP{{"у$7b#b~LU#jb^UjG&^Ŕk_{sDlb3 rU:s ܜS%^?hHفLD64V>M0܍[r 0LQ՗' ۭY6H8WSsar5+@Ad^2V ^V\i?'-G( "qV/R. XRwZ@[nUB{ 9A,H=e*vsGo@^+(;)UOHiΕaEFQ'״q Pc^pV]x}Y+p_|cB$!ѩ3?_5IDnc>2NЩZko`G3'[F񟍥_m=dD!+jC0$OS pߗjzI8and..p%lgNgR~3oH:zF2d;[ -0o xݙ}hp=6G }8f@:߉ԉ2SaV#NF/+xe82d zˮ=菑1~r]3ձy@,$?ۺ^*ƄQ/jS?c:Z٨qkut/B}zS:{wʷ|sBc&:\MЯb![q~Jl*s<a?~jRi™˯\6֔THTgsX?"m+f⽣=)jm YIy> JPӠ7Ms@OE@ _E{'1cor }Ws1*IVhL_¾nY{9Bُ:ƙˏr-<.o'#[A1B Z j~[^9U .YGv?tę:?YRZ,d%ziQ~!*b  GY_%4}ig('pbv͋MNwaX"k&rfKr1 KCSg*eĈ3Ԟw}Q.4N/4/4 mϮ1_u֯,yI}Dӂ/ԧVę!E烓ˑIDfq"M!4F25_-s P( >?M ;QxTuNIYo$7lyϫW4ƚJSC|*A|YI +Nԧd\R0ي^KlQ~U;5v9P8d6:CDo_Xmeɺ!UKd}+B`U4Ɉ%>?jG)sRjgifWQm}@#K'bT2D~_RaY0]sh/gqLoj0Hs=Cf|;Qc-wboXG1X|!).e}Lʈ^c9šg)/ȧI/jܻWi>{x-A%R>G`ƻj&5*3TKbAc -4Ơ_zOrg?#igZ+ujl(Z99jop>WUNYeFHWr<թK֫YlFa"H%$!KE_'50 v6 /bt_^ 4}1!2fD$HpTH/.4Yn}VM~;-3?SIKXs{!b74MO3;Etۉ:Fs.7\Xb˸ħ5qq U*5s k.=kPR-f[,3 $TK]lFki1o(/?DQeyt]S >Gڐ~wySNMlÅ[iLT`/P:?:3tIK\ekcr1odnBx"czq/)mxn 6&)'w/HaF.JH?oxwuYt2=<齻9c7u+kz|'$~Tsk&F=1AVֻ(A~Aug~ `3b>EUUKS( *\W/gv*0%ޡʺ37ljn@6uм1qxZ-Qo 1RI0WfPiD3j48)e6*j^V.D?㵧@ H! U&@ Lv(~}0ve7ܪ6uES͚ .l^ھlv\`$Z"26zmCx):3,8@qwZ2]yO]i)4[HuXʣ*^G\gh,(°txBO3=,Vzzaw:א`:{ud8Dxn#?T4px[IU D|FU0ǎ5.k  !ɒlm-/]0cqŒVRVl%gT6ՉOK%Wnվۑ2yFfit8awl :` [6.WVv>*}P^ l|7[9\e+@-3z,-TDd 2f~ZĆW^F7줄⛊8&0U2e[m*7rv'j)NVy֩t,|UWPy@^NGTfbe P$;Z/Zާ6[[,*"LR4~Vt\KG)+&qӸrG{P1h9RJ4P)Gn+qyy.mq6K’w )eu Xpx1/^z8!YjˋC̘bAug1 )#uow?\@fYAANGN5!OjPYkFcU 7&ZW ڮݺK5O}t8eLƒ4׉;\l=wHlDzdn+N^x~3TO{ oY&Pfh=(}P +W/3b1=3{=3%M{A (Q議w^fx8[Lr~%:n<]jK:@9+r!PNɂlbxè@j}ٳR׺A+=w!]sKQ|L~㈷2^x߷e۷Y .1]! nbSкlI A]'1&C)A!z[?,0yA$0)"s2Y,"ȃXss;$2kFS?g~ K˅ Bp efGO;C}b XLK SvU3b^tÛ _Wðg!٥j fvN -?4@ʢVkz :V^^>8}?|yXa"0|ېOT#69ne惹H̳"n! TipX~ſiBK;:-јF_f5JezlCe~+Sftxsu*0q5[w]Zxb }v՗ǓBCkl$+UFvי4J[mUvdSˌJȚot]'>.I(Xp+ }8wޟU|O-36++ĽGei:<u@=ܴ[H(C*c?ZV} Խvb ځVN: P+j`;J2jy$Izjl1{A~#a~m*yv`=F N8$)ƪC&=Ym;&ȧ0 Q3xx|i5KI%wGknVc(w݉O/sKU$e3xh5RlŢ鉼c ~C!ɷ] JTeqit]ry=V YQŹ -@ذ p5~!u*KH%}N&Fu R̈*-P^>ܻFNU*W̖!l9~nop;Rf7Dal~XtHe0NI'TG+͚+)DV&<5v<81֨Y Z!wlGij8?vaQA$SUo+~@}lFqQEE Y|#&%)AгVЊAl ӀlYqvTͪd9bbb ,ʗo쿇Qζ#ģ<Rkl\XlbG>`CDI޲@'7 &<7i{K->}XEIf-VR]:I"ש;һ{co8LΧSCܠuC|PNբְ% !MGyTϿ![O!DϠ?-`m6b;ȋZ Kg qp5knVW p wgygs,0nv\:&cQI 5N~"r"椅Z3:&r+1071 7iqUzV4oٳT_nÇr^Ü6bz4N!?Sy)$b**6Dֳn lmfQAz%`$˅XT gR5>ƾ*A\@NxbdK}h3έ &SaS(a'k  1EyU|4׺b+*ËSH*j|C>q a_<Ɉ.eirjA =)L7 Zʥ. JnțQo#HH#$_r $ Ǹ06m cָ8}m#hؗŀ?d^buzb޾(;-X.ccD&$.LCIZ۝L>" V/p+ 8.Vq'ʑ0UԲ8]_לvG;|iڔ8-HGxBZ .-j;  R%,3mOi^[ǵ 6hE $07$k8}9U DD*J>,5ƊdX R'HSĖߞC0+2X'p(K#< HʚxUVAjzB߽jwTH&eU`鯴#(ϙagAbZ+ [|,x^~6/1GtO=[_~j4ѭi#)}\%4D}9:D#^?.ELiM95hDƣ? ;[EAnp C\vގ&A9_RJ7Xq@,Ck=bd_?O5=L%(ucOVJ sbNp=HMnlH4TKM@f=8n r' 3FeԲJB?ZhڙIc1$[~$TH@Q` $a*}}FlXcPT2[pEkG6nkO]I9? jO|Lt:?HAWрU{d5]X'VK7 eXj|P[~uo0ZhLmVp8O,ʶ 8 S.v~ LryEFxyj 12DBplkmAfݹ ɇ<}W鸖XoJuTIƮRԬ}=A"`?i[ެ@ ѹZa/OxA&UPĔpà^ FZ~B[tz&@-HJdT=]<.MKtZ{t|;0 ~| âҞQs'F~-Ah_ #16uاj)Ze]@Xh}MyqS_ZT ^Z3ף2T 7F׫Oxn@ pt\ QK#|eQvb\Փ.NjĎPO-m858^>#C_c'J(RO(UL3ZNj~+&FLXY64 OJg E؈N%?g.v:O$CD>.CBpSVU&:cM>x7&YR<X4p}f( 7/,ҥ5bi|הԓ>C_¯^7BN# : *_,-JQsԇ՜Ra02OLJ!ڜ\C2tS|mSErQ(sc碌lSEFJvI| rTyr<.dˉd܁C;e=!Eʼ!w]n8l._+l4,7A=D0#n"ۋu(/#U8n4uAN+4FQ`Gw8]:FvSn[o(E5$w4|CaR,9:%RZF^6[v/)c= ^V9dlP/GVNom+ ,~c^{\"+e(- 7u%&R$4Ve&>jQ~ĥЋ=>ꀳ5ٻm˟%B:lSoKm5OhOA䲎˧}LGf9dm$jPu˰p<+(M MJ~4!A4Cnv=RxS ?]M )IwRQDJɊR&{:}cqXSiE>̜$ P``Zj'9S OT$$RxR} MSAjpsZEϸ"`VA 2D8݂딤{$jS堛u\QHC`|sd!!T| L?Ľ8Z,( Mřmϵ<_1F-/PxP62/_kh2/bY>ס x}F E<Χ =ݹHSnk2I<lܑ؈V"EvW`rv"^5<[I5>.}"1Ms3$;=Yq|lud"V k)#I&F$]qSʞǙUR8BzQF`xG" ԧ/Ti7R|L/"Z*QQ0}͢Q, &Kk}:p@7֭wƫ[LS8ޟX#hpz.'¦Ըo0.ި7 s=#$;2CxTmDbuG@}-E93ZpPG.uάff䶲hkxMG9%axf1YW{^}<>2KyGUr+~?ǨDJhLTum8H0288J ׾iڋ)p~t 5:*M_KH0<kCb[Q\=庩* J.w"gAH~YBd;je%%K L/<[Is bxS ApJ,eQF1g8 w\OU0nkr:xTLSʕ`(b \R޵#*&Dvߌׅ*aqq`$!>0_R /%oM8Zf'u0YtmͱD(ǺH'9x)@}mWcNIIUmNQ*8X-o+˻sf.D4L$@ϵ2Lx0b:Jb%L[qFVP/Lb1VQ9+U?УfBÞX@uض>,BJq>mT6l*_E<٤:wLX4.xt=TN['G"fC< LrL}8<>I,$k^#B)_=^闪JfsN%Z9J81auk!3 kW~؀]c|Dw  Ԁ޲`["ƾ>^<L A9N_j`d>OYJK􀷂]Áksdp8A*1nށ bZ&zuoUtlBΪ. ,=X3I<Ҟ|\WB%/dv 3hocDY|J]4&!% &sIa!:iQKdeX1= gt5?o pwu UgؔIo -Z&\mhqGz5WGU5٩SY[5B%r 5%c۳Z,()W0<Cyh2QKg J9vgeF%PK<|7o yfCУ5B>и|uWZIBWb] w&}h}O@9 )KVd<5C(j=3ײ5[tC߱/;v }:  wأϔ`dnn)hd0 륅`JuNwU# &elAήu{%(xZ,j'YDc4Ce^7^l#J?b d7'Ug^`8iGqswYcL2% &,*OkyHKCnE:H$`_?ǐ1CyH_TSkZ+ߨ/ynJ峑@F'߼Ѯ7<(-󬁹GèI>;REWa&޿ޘYRznK׶LwI:&t6TȍKCluՖ PV|̀YM iA*pqu]qOs~rYjk&o1g2x%dj7GaǺ49Qv*.8!.jՎc.x#/'mף4/k 1 9Oj;0\i3ż/>WQvI!G$oUQ|wV>`՚iX(gJ<7).2@D7maUOոrfť^4X ՈH]PM.1q6TX=3D`% k8}Xʡ#ÊlaXЇlsrgaV[]dёM&<* ! p])_[nXҸo+9vc0jMq>rZgKWs=:5 HJJ/^}jmݹvJ1l)A2Ik1ƛ`S Ǟ`.[ 4V߶7>6b.L~aPL3D>-|ngИEMˡx8 @M0ʀ9_W ;{ƍú;Bj|Xm` (2@粒Vh,#K;h1^(%d?=l4$7h[w Fx_!XW6QY6?'2(L P~ϭ^=r\0Zc?[?YtIdUs@:I QmXDs6A1/6|]\$S,qaQr-9ץ3-cBlɨg+d/N%`C"Kq뵺n Zt ,G̟$uuWϔDEΩ2;yG~<w"( _ ,`I3`*+ű>%\AZ.ꔃڧנC,P5Ck mi&3RHO) 9XF'cРgn"E#Z2~e 0uY~cca/(xh$Br@2.sؙNbPqbȗ? kcׂgZF6iꕑEܻw1NϞ IH-J$AwA"ph:HvD?AQ4qγc"]";_`_u\:u/W`3*-T}n46q9`ZA\Gl;fY&ls=zmTIjP ч(>S؆) ?7x*H5?7n?1@ǼkRv+EH:Oe>S>Y(woJ7l6%=8_NP'NUE' `8'HfMީE8*{n5 _á6tѝ'XxN ^fQ^=Z9ؾur3/yARFvIMm!껪@ڿ1B_,Gl'ҽ֜$FmSWP_ؾW5Ͱ$ N$pwH Eܔf0kxwo%pPh~l@{QZѫyUQ9:IO%]Sxfݘ"C"4Q/Sb90[@W8|iQmy*znmYm %a= a*kIH↡0$N9n$|zy..ShBjHGB*UТgixb̦DMŒ5(ԉݻ~qo/޴ +)jFp!$A ɓUd=v7:V-K-UMo/0NZ^)-lz)gf;aFUXgN#\O, cdJ*qM`$CzHL<! n'L#CY6G}*rfyjB`-}Lʙ*2]-EyS',Q7']8f ei/ N>Bk 0txiK憹;ZE`yvH0Z#=MeϣȾL1-T,Rb/7\JGtHݣa$kѦh\ح^ne21ђig0wk>&5s{7݊RODͥR9s%Dٱˉ[EWWfLUυUͼo'ZΏP>K'i}idl26:o41( ^>ڵX,wGvG%!׋ =S E AH"YG]^:DC\JYП&Trא`%];6VQ hdG5WeNVDb [>|toZ`iJt`CW"\0N1 |!ć9<ؑe2*rR4.#],{dl-l4 k!Bir뷳 ~L3*svH"K̵n?X~mQ#OyA耱uݾh^?=̴|r@nF)IM/ Ef^93|JkL9ۧ'X׫ueղ$C4qܧ{KDZOqr4FvЋ. ͢@=A~toT%!Œ /Mf384[ό%/=峦CcA0*B6BX~0Kk"~Sthi)!Aa33u@AЛͶn #,n`* 2T7$' {4Mp QwZЏTq!?1ݭE*!Cj,jia_}:RN{PW/r b63oQ[^DQ/ 5yˑo%8B|'y40{0ԧ%BFG1B|4)S_BafJ{gĿ 2Yh+GMcg;'Ӧɩ[_%lT}>{Ryn{վ˥-HlH `!ۨtV*jIr: :aS-n4~,݃W=yHz0Z9}DžKzq3|>E/[xjtC%"jq#0D6+ O)ۋܾ₮ '%7>2G7W*[hu<8"U9<¹K,-~ ;, E^蹛&ܓ D}VK!M]ct~AKsgi{saYzɿ =j@``}Jl:! RGs[mplnĀhT|U92#|]lT6[ˑU d2PH6?~&(Qf 4XDf~>QkΎϹ}I8_^OF#XrIX.rBA4&D-OZ _%V2$$]$wѓx/y"tnVdݹ,BP}wW[y[ ĞXR {-&fYՑtxءuL; AdS ̌CGu; r,Җ`j<Èv=DنN9"n.Fj ~rOd.#JYxcdZ"0-CPpcr}jc=_{`ak0M<^eÓFD~fףݼlz@'ϨrAÌPp2bsUa> ʑ 5Pa>}@L>ck(c"@dUpϾCKj,^K%O-H6tS% :_ȫy-?bƚV3m6 @9(X6I^v> e*BO IE7!. fUhmxMj̀ TWhI#Kr4?%4#T|[_]UO|ŕȼN2JéwVsG08i",{cmm$gw["ZZl>T5"k@0ӴDd찱$/ kPCE:L18rij[$)GѶV#;BJK@VⰩe#űeY6 th:Bp!cGB5x)D|.rN+Di‹_D# D&B+9'\'5CGTjq& 槥]#®cF/Ţ i7dNVF7lg7Ηϭd9t 9pSe`*4!'^_wk79wI*x{Ũ@ςuc#Z<1I-qZځ-U R稴wMh46F3x#In%a-5w&$;AlpOXVۑ(&٢ݖӹO^[9ېؿI.rpPlY8b 4-HlZ#Fa9.fF.Bߟ\9v@taƹo1ᗢ<\VJy$^H:bsu7$2K9 phZbB>&:%-5J[93GT^*< p:vP얷:;tEU_:FQ_L MofUv*ϩԋ%qѽ MBlZ> `stZR~\;x%^ z>0cw)Eb™tMZ?wŏs20W>M4 7K#u] BMډ#uӤxszwM 4bl-d[`q H08SF+>ye|V`j 1h]*LȊDh1IN[Sv-] }|T?|f+ip[^-'PG %fM5PycLЪ(ʂx)t ,qsKv;t`z~m"_0 |έj_,̳'zz9 ж?>@"Y9Hg4b6J}gpIC=jl,d6޿ 1#-=Dm JVBda/vvn2z8į%Y>텓VM?\Kc*K۲1d3J*dg g1D)tcN:G-E ۔8X6<~ mMxHؐ7s| %Q'^&PYemmA]r<ꕨeA/Id1u*rnXξ`x+s;]7bxQҋ`ԏFvUj SCXiS3#W |Z;xHH w LSFv s+*j0q2<HHlz9C{sZT@ Xu{4/aI|$ ʵ7l:6zv4nҰHwS/Wql__[鴑',μzVϻUtW|x6-daѦ dE~XmnI:F6ECHeA4w #$U]G'zyL0_<m/r#Y HK/P@MFYgfIdj:vIOF @ta 'HrOh0"Mr56rfu]r3fjC[l\eQGA".i%6N?'gz,.K+5T1.ڨ(gdQpA:$7A"F h|Q[T<7I]>P:\P&g/뢼ԕGXp 󥏓W_өR3bx-LLXy/QnyS$ӯH1D¢{4x(j yzs5s: =(ʼn1(>czljLP B+R(d~z]邠`c?H؆1)4M/ ϽPGYtUxt\t!3ٛ Zbf8}!jXC="8(sQr?SKI Tg-VVjrďgI&ݤ dkBלs\U= endstream endobj 103 0 obj << /Type /FontDescriptor /FontName /VHDCAH+LMRoman10-Regular /Flags 4 /FontBBox [-430 -290 1417 1127] /Ascent 0 /CapHeight 0 /Descent 0 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/A/B/C/D/E/F/G/I/L/M/N/O/P/R/S/T/U/W/X/a/b/bullet/c/colon/comma/d/e/f/ff/fi/four/g/greater/h/hyphen/i/j/k/l/less/m/n/o/one/p/parenleft/parenright/period/plus/question/quoteright/r/s/six/slash/t/three/two/u/v/w/x/y/z) /FontFile 102 0 R >> endobj 104 0 obj << /Length1 1917 /Length2 21440 /Length3 0 /Length 22669 /Filter /FlateDecode >> stream xڴeP\5 ACqBiq ݝ4_gVWxtGYUԙ& iڱ3,mvfVVN$jj GbB@%)Ղ 9*&%nb*`' U ѿH,,!bp01[ 4:Y[vfyf%f2UhL@@shKd>h3Vw; u MFT{oPxj]IJCLCWE5. G'?i+3;mJ@YX\]]- `G f{iXZ9\րoG 8ۙb ;LAvN?N࿕|uzCk! bmp#%/_EE-ڙBg'_7Ȍo JR9oQ^7;svGmMvNVN#V6?/1)_c qe'"? `}R);3 +k'?哴zjP[ٙ=3HN_EHY Vr3dy#f#~-=`qy[^<. O"$6)u_rv`W&R{jq̑Xבg+2Dj߆@[+4/mt`G[鬜@f*VS˿K\|1; k[iY)}=_&6ҽ ZbZ?|,jrʚz2 wl3YYع@GG;,sq<^ װXW3`vDPn._bpW `1d9,m ^}b? 7$5˿!+yUzϖ97뿰:l Ҷ2{WD qrg}]W_ #`7O&Nvv;kk9^OxG_:L-2EZ ~R\Y*gOXG>vD 1 H_oM.XQs]u jcb&gb4)oZ̚iJEm{߲u9554%Z^0ϓ) \L8`cOBC^pb"]b 琉;۱wJ,D2'qџuúLp ay܈ 8nQq$f⸐>KcHS:(\bPoyY>0C=Pb DΨ5Q]F, . 1̚D~eT1uZl,{ Z2b47hi@nϙpxBM34 D]q"?Iqh|(֣U,+~o%<(9Is.)E4S )Ҵ<T@lC?MO>%]MlRc'>NR^&9Eǧ26S[䡫@]{}=+˹q2~=p|t)m0ld~G"Ӣq-|,tfCs¢Si@Mk͋EX;tϪ]~%q _D_JIXFeWg?VEkȪy+& Eew>gp8߹_ã%T:iGg&So >57xj"r`\لћZ%X ܩf/"Eo) lNE0ۘtƱ(}x ,=D,\e~i5#Iǹ{&O6]뚖ITN|N3o@CPDƕV3eT!. g(t4;N:\ FaT-x#"_}ao<}M>3 7l`ENھ/O덲 $LqYK8Ykg| b Jtu/_zan6qtYqQj,1>LB p uGxB}C|<&{P!:ɻWu4)"dLJZ:oֆ8CZckUJds[e9*d[ &V7vӤJ8hw4]Ç/ζ /JJ hkH\z WQt{5>3mcKQXiSM~؏~%ߔ㥔lϭ9 0:= levX>_ [NaUcUҘL=k/RxF7|["Z=+`e>{҉(9LR|nHaR52yZ#֨| rwY;R !^?n.:{FnGw{5V MCha(}T"c>whirv?>:5b-aqm̰\Q-+X_u%~C[r^`䒽h7ϓqX \z;qܙ%%9a~~B|8RLq(t݆8 165f&0آ737AnCӐR,)A¾{v[VGTU)`k> zN9fJcC.Z%-j8vk_X@j*]qJw`+gR(Mq5aïr2=(%ֶ`"61~8FƩvplT@M'J%ƏB |>I4hYBrucMzbFr$?2BehX|bB *&qX-]LNp>L8hHYy=VEuUzk(21/x%Fѯ{7FO9Ά1>z`]w XdC7h \Q?`bJus#HbU>^Nv)EfO XfF$ÁG/{ٸ>Wz_R$I^d5O\{mclAX筽uyivH@㗠9H:mɐ>-QVS!UM]SeL2e5ѓJ-վSo)h LL ҔSTzFyD!;'*^V\TOQZN6籢񬸐%eO܂=GO10 <6p=ֱyoȮ)Bk-xw)7֨AY e>1CcԉnFu>YȱF8\.ګ-y#^4oSMr5m0`?T e0' ]ڭ6 ebommSL흖+6lo3! ղ d&&)"] ηtY",V1lu}/0I6BտoF 7d^t7D ;CČ.XD2]`N?$i|5trV~3MXPә//W>KiOy³4[pde, ]cJЌJ;AKw2+'-47\dlj,K(u.p- K)wυ#m؀)_ 4< ug7s m$Q02:*(X[wklYg^D}!RMh+.<#:Y%[ q0~YzJxh?NL:!9BI6>KCbuׯO#hc;qGD2|Y?w1k{Ah-j!Bs Qj0^5gʔ>DW]gKxz0Dµ<-,I{Iiռy nHXgJ5t:ҘD ط?/[pRp_TL !FT_ɣֈ#׼6{1n!>Rp=?`6W=DUR][JbNrAޝnHZҟ-eʚGΔ%dT.^Lgx6^lZz%zo彉\<#{aaiY%6l7-[23 >?ЯKsyKbEpE4KV:IplKY,9$}[/N+‘ʳT@䲏R lŅz䃰fO`L:#ER.}~ÿ),\Ep6%b~ |@gMߝVqQe3H~r bo(,'D;!C7[1ևIE;iGb?=p zqI kvW4t.@K[6 yڛVƈ RƬ;V -̕-=9N61v-;|iR~.jm*tۗ @Nz鲖'ŋyF,xTvV8!#~u($/d@pɰgb9딗/!i"Һ8_™$ߏ"8K:(kn҂Q\JSz0X)o|Wk 7b%ё<;Xdž8'^Ί5lw\YP |u7QWL"ˑX!aY H*A^Sבs}a [pyş8Uo\ڣqiaM6%szZJ:[M_pj\^VM;!tZ=;L~2Dc؀%j?heeZl+3o~R-i*a}{ Eqm$QF;kv|Tb'448͌Moد5jO(R^v!-|c\Js%8n1qVI+iڎb@"5,a n xP_bܲc+'CXÅX)[\Ң,'Hl{攗&WZ,ƕӠԣրŏU sDZ}Cj]wA]~?:l_JZ7jzIr~{.T,[3#8(6F1qܬBLG{boym2\#LGpu{%4 ӟ2nJAfz'*BdbҜ};_4stv7?ˋ`ŵ~^ˆ{h.a`NWc:=Ցf7*yyٟOR~cٰ0"tj0$Ima1:orF%U>fDao,N%1ߏ3c8+|)T}5S”|G_ͼ\̈dPIDCޑ%}9iUa&dԔ?[Jُ,%#I\'IGOMZ3 E}TDԃٮKMo=r67`f%}GmEz62 do6CL!fNڞY1ʃ5yShᷣA^m{ IK#gm &hw![?'QD>&u?]=MTZ?Qķa Ms6'{4LSNAbh`rTOÎig0%_֧)mi(=TA2t>8_Og`3!Yx|/A/k"@؁SX3   e"~.ug@&p\-¹(dXuEǹ3:w-jy,M4.XJѧ)4=Yh'!>8fDLFƼ¾S9*fH0a=Sj{[1ƹ/QMt5)i>Sw!# }v"Aa;sxDP19D|cqT.q\Sﴨ7YIp ծ!X@w@T"4 g~5)n&RB( ?zyk4oח:v`0j x-k9${;V.ɾR˪oZILzJȅ$rF ;s]G~TOP ͗^=N] c <;-^U^`*i3z*3-ϯ3_t݁kQLe-J,_B>Y4Qv.$w QL&Q*F['| 48"l `565eH!Xg6-L3g1qȠH3)JWM".sB/?M)p6EH 1~gyy 7޻޷w58xP1J Ŀ+>Wpjl햦'|t}AvXeSda.2U XI,8:._.!ս͇ܢi^VX\ҟJks|BkG*zF哇Oװ]CoikϫrL^NnLa.7ACU% V^Vj.qpnawLpul(%X5j?+dr&<s[g;dOF3o ::HV  XKFōa/v'.~.u K浄%^y]VkȮ(5x҄׃Q?bf~jjvc艱  Z='#U?e~NCba$n'%e,0bf[ TFf 0MR&-iDW~^Ig(:*P,܏$;j'7 _fƝ JuݤB=ʗId˱tE*ei-M Ju,:zy}?k3 \?%1pv)ݤ{/u[0V%i!PíA%"`Zñn+65۸|+މGyP3:I jkHPJ|VZCduԞ)Sj9+hq:Op8e|s/J3vbh"g 5)TdcH0(ݱ5q΋6i$cwJޞbȭPE') )%*>蔝B'aypy#n_|OɏR'p6 *KE^5GqnQ14&53Qxo ]ЂfOe}/,N1A*t e;$=3N"fVLՉfn{>x]<#!xUN} fv&RFZԕPs0 O,ނs7βPLd= f8߼XA^KDjq&9ZM}'x &ǂ`Mm;T'Q@YJ: x$NЁg%eSm(.;U ]=m J_"|ch+UiF-d;lam UqR15H@sSɍ.0$[-58Y)zXL{IWC 5n|[}#,|cys)XÐ*"R>I0},*^@V;,Gۿ_'LSmؽ~D 12 &,^f+gc6~lySl u~#ǸI$U}+MZ%{Gۜj> c`hǛp H11h{btZ}CkcfAVe{?Q˸ޞy#drX{bTLg/V;"=+ī}F2gkAap!![w33mbf5r{k%gEK8 @PS$\bcGOzl6HOpF\;Ne.?Quƈ{mT7 /@CbvAa6c]wpg#tKs~ΐ_`)ގɿOA!^0\Orc[ulqejmx Z5 Yk^aϚ^}xg8ol+Vтl|qT`-'tТ\Cҳyh82~o(`U'd:?` ~'.ژ0,hUg,sL۱exKhPǧiEV*qto(/y+`MV\\EyGKEċ }h֦MC9Z\IbBDQ\cֆ.QW ('9EB=GWPM "Z=Xn{k"mNt>4eɀj -6un#;6 ꧛ni,iX,Qy 9Q]b̺<2>,QOvi_G a_4^8`-?s.sCYc)}Juk`x g0E F&e~Ϫܗ6qn*aec}K0[8PΓ ? vVz2ֱ甾;߫^ghfPdҷUd}yî`&CV W\,ߍ4hEӑǎ!d5ZRp Za.޸!Ο~j7dQ w+2+R#4O§t&C:/]s5kGȖ+4v!VRajhㆮ03B: ϤD_P~\Sjr;}pN'C*VIQsѴmt3o7GT+֔ypW0}FEyc+-sar/åQ.+ sb[S.=l,wvCuM_)(?+ gIVۻЉ@GѝA}}2ؓju=OҥҖG+lgf޶ߞ!Ռ^Wݸ%a$|(]dq!oMǎ\f +.hqN~@e /fČ՘%|V x yHivK}Kb%®湡2*95"/Rԟ *ZwiM2rjJ_G=3FD_;Y|ISLQSI\"ʏeVX{<,_G=G.ƏEdҜ RssjdYW <4WCe 2m`_ yDʉRQs3M 7*ljvήۜWMk@8i)s$mY32iN3;öVBFtت =Iܑ[.L4evy(:uޡ epk)e X߁c2C~H"-{C'NYh&a'įs!xtr=>-uY-Q.))[hkV|G ,1Hʿ͙"mVX)i~Ʊ$d/BA 3IBT۪FѾ|\[.B`юX{' J>҃G[ kq5|# if2oO8)h0k4e< Yac'C_5")PݻU2mNhm zˁBh>-~;aG; TyhS'7:c{FA;] [u*m[9MqJ9NX"]ajhP ęiVηViVUEae P(}yyMhQ4ۧ,#IREIz"xdvi2cfv[VP{M!c;&9,XvܽWKœ2?WAclŬ*C̉"8_TKpO6R!qz, {0(HpLxzhn0Z˖"2}M k;u!Zp/ܢTm]=& *{_e&"F .VL)XgMe#kX)1.wugcDDR]Ǻy;Q03ӕcXlז{dlP>/оbWSKşxN[(] rvCZ}?y!k z:E u xg}yU5vR(dm{$&[W)r-dͅ1i OIހKhs"(hX0rC; 6a(=SE"w/7E%F"$^ T }tZ!6Ĺ]$se+66Zl/_)گHmЯiBgvo (>гɃΟ80ia\Zufy?AlU`Tw०fQwx(rfUlR4TkTl idJXPOw "El1Vo5Mֹ5("]=ROS^k=&yWt7An|@;Sm˂.|7)G *w@f-6]3PwRmV| Z1r0 48 :8zR㺪.KyP2W-ir=Zk@̹e.|Y9 EIG&,f?2B;ji]H:"mmMXEݒz+:Ȝ^D}l"L#}U,lؠhIs|z4Dx%&!#g)ů6: Tr'h+X|rX_o{ Dg/=R Ea'л$ t;#zh7S_ס i&*4/4P'0ҺwW KJmN]oDH_o=fN@3ދrOG`y/aKGū鸞9:&-*ԅd>GHJ)>k@[%Py{EbTaD)"xY#Hf-ŧq;*]>MΔʐ?~*yOc>e4q;5EWKu+TŽ.lo\..킀Z@U㔩ூ}DYII U7{|߼1,X]˸4T ɻ?\Zc"2StW3[y5hgڈm ڪsF y aZp׵C+@NFі`Rcy}읂lM[h1v7GƞaփmCy-)2^{Z72oOw%L!q* ݣC |ZP+wkH=Cc]t"6E9@UQi=bq~5S2`ٯU~*^&Iر1֬CvYsq:M+d>^ UbFxpa̅_ M4!)=v=AG\ g`@W%\?/9jr؆M1+/˧&;' XH=%CU\&m!*vpV2/cx-m*ݒ,ӽVq8&*(5#;fN)~ndcMB^"SB$H^_wALy֨f= fqe.'Ֆn1f׽K|Aώ}]R)q':",^uD˸.'Z &ٜC N#Ш L9gLu S"}(Fϵ`).33zA0V~?HO;0Ŀ- ~L]D i~#ImOmطf$*ǜcw?K4yTš]XZ?{ $+!!тF!Vvڷc3h:ؼ..j}n^5D.?gn$6j&iB|=*φkyj7ye6Nԗg)EI'"!=yü?t凢6D1djwꯔ[A3gCܧG#Ժ;}Me(Bn%w!!UK\pazbugb#֑G<V4tٜpQ.j##mܨM}Y&]_x4|NV]ܶ>?.r8mбiLR#콶5CWnsxJ=WX4(k>>2`TBA ifOIea.˳*}HPq&%NUM> Ɛr3cDMCN7bp3 ug5? r\!ø%c6 1#1vi ;,+|:Ԫ"í++gv7$/pTS1>ɞ(EƵ:ЕTCbwIRM4r!$O ͜={ BD_`HP$Lq&ʞ۪ico^jO밽9gZ@U{[ƔGr2B7ۍzczR\)5r#upAgx|8g lS5F}lCdSqBiL6*veM|_)®XDu$ZwE^"iց2;qIFw]7ZkYUr|Ӄ (9r[1=8'J#KXܡȋPQ-RӃ3)%"aڀpqH1bN]lQf)Cfj0bꃂQ_6$V-Nغq8]L4{1+t#y]꼁S^ L@ jQ3- ܶ{b};ڣoT\֒!ۼD#o[H3 =VP*QßGdC CB肛h/W+!u*(.Bz{+,'`KjO hu_sLFN4BAה,uxV&T5B8S3@ EÙ/?i~gs?R=FykBSd,'r.çlo6jJmg&RJ~ +G%,&wS/= Hl[2OK kѷH,r߭`s liBgePfV#_( EGC 6mW# CDPW4am*bqք8+kA&ЦV/SС\b%xՎ`p3G9{-҉o;QӑgAQ9(̒4 >k eo((@%׵|tc _Ƭ[TAR n. v@td0l#mN{HnN_O Ű:F cqvoHP @0!o wTk\P(wAZMzΖ3I^n 4Z7C;%,y NKn"k=XHH5<3sG٢0(ljP`p&*:GȆ5#- UW^N߼"k{yɏ6 ɔLVSFg[ܚ0O ]aRCW9~S+"$OKbY9/;y&A*qfaNm4S`dM&ZfcIn:Ȁ)hy,0_:v`zƯN.^[5٠Qwؒy@}Î8QHUF/+p(W J2 ƴϘѸ/ЪEF굎I\15-Mxi eL>ٱgaȚMAwBNs^sQ{yT"Z:bԊ9SMjF[$JP!$%nΧ} kKaK>7a [IӎE}04hc Ykdk0҇Zbok]Z9k"jJUNOdzvb"ެXfyGe @]ѣvp)༐[I2OR$̜-;L)rA1 `t1nVx2x8ɪb7V cдĂ/`a_iupUi~mbA g势R1*:S͑AH}U5s?6:8pa\'iQ( [vzi}5VYWUڄT֐BZH(_,}u"-9 =]C1L͎R ճADRD>K ~|ZX7ոz~?+kUા-ȦRL]D24EװH=5MJkQsb']Yz1, 'v\@U TіʁKO(,pv$۸i8m'JJ_Ը)yU!Cqi#'u~޽z`[Ea+ٚ WrPPFC F3Xv=TTrE,zfV.P3+v]< sd0MEAB:ұY/!~Y?"ZVt!e""Q8$>)Xn5ԑMSGF}EE1ѻ/== R7iP^yS˺oYI Jǽ$9~\4\ 6́?oeX'!}9g^6Yb3VR4 ޠn_ͫe태zXXGIGwsvsY%ҶN#F:3"f<.(p۷ݕ2m̉Ȓ(䀐)y`P<,:5Ak@A g/i Xpt!u?,{S5v]\eTW7_2+1ް \ڊjmgR&( 6n2B Wo[x:92,Z) ,C K+'sDad^܅X.8OK`9<6FO4&g``wR߆w9zкZaps|=LM)g{((LVԪҦ,-2Y`Z_gڇ 8j ¶XXwc5.$knJ!xObM:(/S%W"-hTL9)XuL$5F[Q6,޴oq" -R8):ޕٳ\|KI^{::ʝ*-Ɓ-Ɍ78(fd'FwMuMΆu2I[eX:!}ƿ|4Mۉ3 UpY10[@OMW]f P!AI[k@0!$-Yvʞ153lK>}k=~ڳMUϗl3ه3D#MBz^Hd]9R>r Ici7Ego ]|dײ_P@} 8zwjA9Ap%Oxퟗ?ZC3vc`bP Xr {k \<T-9#NdB;AD5ĽEA=ڏZ=L~\jl:Ga#CKqIFZf/o*4)/&2=߁Ǻ!|=4'ԸZˆFci v k:3 ݆9؎%t$N>tyk߆2jD|1s >="~)joOO{tA$rbenCzRA54U}]]= ƺUmxYIT?{t\:}L̉PrS#q6DP]\(RM2Rڼ*SVxfVsOfwL5"cZx0cYɳ )fq/}W%/= aOkuBT{d@ۓ~v=R=raAMՁ;%P]õ'T3rT7"ooxEeڈeq{yz~$0T+{, 9:5QTflCmnfrߘ\nU3|Rح W/1e*MH6H<(t3l~ԞJuIxv=-F5+;t\xhF-)~ku5F0Gg2&I eu2k{+e<.- 0qA_0X##e1<" ף OZ?~ZAR}LTe!_ 3F&:xa췦n/?4'`:+eʊml9?i7u%W[SAuAsEeWc]uؗiy/N4~T7dohya`@&[eT Xj V͇QO #BE{,Ut}zep?ѨU%, eȰ.|E̹IE_ 0VIݼ*ҵ&z" 3qׅ5?Ke%=Cr^":0?'%x,G1#[^=)q0E} J"3i+ȨR;Tn@lb.tdbJY"p7Ig* i)u>w} awYWI2"^LUYŸiYbC]u8ݭA ~v:\DyX2t[Q6u* ġgJt?6$aUmb~R0&€4KJXvCZ"#_nCAؙ%V7E[M(^ѴMj΋P ̭5瀱N2msGv+ endstream endobj 105 0 obj << /Type /FontDescriptor /FontName /RINUZG+LMRoman12-Regular /Flags 4 /FontBBox [-422 -280 1394 1127] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle 0 /StemV 65 /XHeight 431 /CharSet (/A/b/comma/d/e/g/h/l/o/one/p/r/s/t/two/u/v/y/zero) /FontFile 104 0 R >> endobj 106 0 obj << /Length1 2598 /Length2 31481 /Length3 0 /Length 32928 /Filter /FlateDecode >> stream xڴeT\[5 ww)nݵ5{p A rowv?o0Kku>c*1H΅ 'bo fPTTbN 8 r(S5 Kg5W;Sp;],@/{Sr& ;g$IVv76A:+$Zڹv&@3/dJ@@7]NtQ{pez6޾@1?ze9[:8"`fiYeQPUc=;v..E^OD\ `a0TTw-}rwd?smmgnfvf;ong W4؄fr0@o巙_o{ki z;@'W?Y&.A?,.mgf Vo׿F)5]As?\66 @[〶6?Zil6t*YXؿ.@ؙۀId[c0n#ibmrvq?-%+'*Af0 ;{SK;s+'Dd+<Ӧ &hgN8&'Io `Lb7I0Iq1$  ` v"A`-X֢ ֢U 0fW]kA`> /5.`8?j>6$0 o G:@pufn@0? b@pVຬ\nG8<l_npKd,} VrGXu:t#L 41 7?,E-=r:,`?J8],@nlbn[i<zDDכ_Xii ~Gc;`zvvAz_0* ŝu:2yA{Ҏ3Z`Av*\F*4>o4Nbi9J eTFql(j 7xsWck¦%#&7f++*#o5#(E#L?1|بC|ӇYcWLhOJ3=!*m] %\ AH/s,=Mi^vjG)}ZT}nZxA1=;>_HHS.e+kƣ٭/&=w,KĂo^qb^ _Gpaֈ~,F.&)NG gJam!?.)Ry6;rf"RЎd2 .)aE6/ `a$t}v_ |#(a5Itbsz,w@A!Mҿqz C/Gc^L/pv`X5v]k~(bv|`IsM R3$O$= J<>C'L*Z1vú4t\Dfs8kAxYgƮ'5YA.HSQ->2F8^0&Wk8Q$.S:&{rԊvmӕ3E8pOaCIXa_G>ǰ1T>%"1š2и^X_^mRw5DzCAwZb2Hu|nB8us }J/1aDO,r gɔ}g 0%[*Qz87h͆>MVoFB뿮LZƠ:o$f5})00؃/CxyHf9stg}Zie ,nSJYAx27EIhR^w9&Q kK9Lb/Ow Vr(bNid$0JJ̗!!]2sѶsܻ=9L-x:x`i-e:V)NE՗4Q Tͼ`nT`+g64`rJ!5hW B۞X-yYv<&vAHH -@&o,"ŐŨ>=MuӷrD:`ɇ[]yWӵs+vbQ[ g?aL6Oذ&g62q jq{o硠n`@K=ԧE۟Xg//[gB#uމN!LZPv^G|‚SNPK):loéERR#d>6܏׈1C4UooF'!z C?7L)RYsar*y<%1 G" 细~}Yy?yU#p#p>Kzۯ]O1*OY6,]}J7j |U'&[Xƴt]u*!\[2.h KR!Ȩ\ej(=58.p`2´+0^:z4Q-E\>v<ԽuevIo==ŗR@ caO^ATA5u xݩ7+Eo4A+?b_Ht9[^)cJE{"a"Cݚ?&',egJr8}Q_߯lJ md 1n .X b)%1(R2g!9J+Ւ!f|mr!+w}6TO^ލ{I]ZlS{pb37ָ ˠPY%5TʣFOxgnD70ϝGGM=}Y+o'<<g;&$CjA|.?eAq` ?ѵűVe |xu^,6J0|"析؂\  yc5rq6Mӗ{'mLk4I?b, A }5q0D!y1o0z[d`7Rrt.mRhzN}8{#!粜r5AeIl0x֮Cb uӴ-6fPU]׷o$5&!L -ĻwoUHZTX1P| E{I(r,sJ:±{`+(!H%!&@Lw>|!1;L3ؿ0N-jNifGqSް:~Mk~MSoe[\E9CUv8H)ǘ V;U3y-N(dIKR c! )bb\Z( {tDÌLY ԣ#Th.Dq^|Ks8C@p'șq SvΦ+ߨA(Tq{';j]V:KˏHYrJ}j:.Zi=9e?-Pl'3NYty=5-DW.SֶYG~_> As!7£obĵ~a /]n/™J)&ʶ;Cw@M7vQ*tt,ş2ZA5)PFupd^i LսA__#2ż0pZ gMb*E?{͜fKz3<*`j&q7#FZ!%x~a Ap5턆U Z k<@~6(L>:ʱ3b3&K%j2cLvbw䷯3y  $ɯ&JL}+1dJF蜺ߎINM#5b-W=8+#ml~6}EA}W/ZlqQ:!}(v/efPi^;CZf &j-ia]TP3ͬ /U!)N?Xeڍ幸xIaLf@f m|o]捅7W@q8ק}^ÚJ~WX<Kɸ/. Z ~vG֟֌D(* i}F Qe-tV$M-u,ȇy>r.y}zN&qtZ~)$N0f6eiM$~iy.)`㻥R=j#?}cUa0 eq/;?rR>]Mɻn ĤY|Il7F5`N@08 o^u(C2t4E~]Kf{nyg%ph_ߧ9۔OR8ptYdh8):̇O|E 6~o~9)%C=\SFHt[>>j"ҏEU+i&UaԐE"ӷ-'(Jܟeq3ߌ #Q.,=Kg,PLoUȉLqDŊ쯛 yY|3ӴCrő.quu h{Ǩ4k۶aO4 Qj|Wm>}g(}Ty+Z5U\"6-2 xi:#%~TCNUVBEwDxJ\9Yk\ylx Wwy!3D;goF^);ʽ hCop)y:ݗ s],t>k9 So|>RCpΖibxMu^>BpA9 +^IFc^ ^߽֧tGw%/ۙEsbt5%Ӏ)U.ރG3 v)۫(qb!N.uz>77LxKyUx6r:?w}%8V;[:2m#3J'n&v uB톉u3T^T0;VmQ Aҟa焀 OL|G}eպ'o:gI+_cK5_vR3kv 4QҽN݃v@E\d>.N牾+}!4<[oj-RR{IcnjzL?wJT6ӗ*TИL}rqNeM6GWNjB?Ys%t`c*5L).cIu/jؼ{>n7xˉ|u4$>$|]睈YuFvf3-G*Ugz+x!GdtF'T,sB$QJ"VZcۀĤ7IN yd ٨)jXekm` $&7SB'ם @IC6z1(vi9en y*9CW &7F/a-aD~!39qU:hit n_sOovA`sf~w*"G2)#nX'q۝M Ӗyt!l~QrDu8A:)/=82~#!(ϟ_9VUU qQ..9\ksA9wjy3KeR8c^^!3XԿ[HaBҨy5N+}~]!er2'+yo& )mBRi7ʐ}d@7G(; g7:ԧFZ{wD!DaǶvZBi ;AЏMad =G&>`ei9yKlevׂ͜[f<.o;zt>79|Zxz>.KHo'Oyv!哩<5tz}TJ+&Ў5rW= net@= /bR06F/3-+3і? 5tA1O*pP6t߭Rhwz\\༗Ity#&K]B2Ja..O3#Y?Wՙ@>\}dM>HDutu 1a5`#"Ggci .ٟF ic KFgk,(Qgg0vN|^nm`s4#J@b1Jov/5.&m|aq@ǩz/fp;ƌ8DKñouLͪ@=k = cj ʾn*a<eNPSUy2Y5zDJK{ثo7KV{WSD6*U|^2Em UCG#􊸴_8>:.VK@>`g;+RPVF)Ϊ!8)TQܑƓz!V$E'[c>2Hk=@Do7(P|!*bY:D(&?rͪfB3XAK U"hu( !h~Å <jJ\y>.ر1 vnU2)4$O+| MAg tͳE#5q ]੝j  )=hR>jO "<Ruzn~W/MZKwh\du%EYH>,Һ?vr'zyr)Ճh&5,V4yؒv #ПA~^ȘMk*<{ ["2PJBiLxzC!u?+7޵ׂp8wU5 o "ǛjXK0a0]1Sÿ !2T]uy?73)RHPPDmIy;xku؟䅣8KJEf$#t ۟ E/?=enSu3[R}h81}8~/bkǀ6Ywz*Q,Ѭ5iLVRAgcg'x 6V7bOzbMHճW uH˪ɩflo68sд-?\EJ<>5GYb߫X @Ϧ\..P^Z:n)fp=6Kmq S q=`;[*>xd!7`7YuzOT5[bG%m}MK, QÅ>c7CzzP!yjEv4-f7[%o꺴2"xXk qiV ]ީT6nX{MDž,]LSžu]".; y%fgƟ֬G TW*WME&qYQV&FF|gG=euk:߅Ϩ`_%V4{AnOÜq$mЬr߇- ]{ EDDR~S3>rlvȬ|rki^H.bNLdU y7Ct\}>YԸvNog:>,;MB1Ԕ9N H5F#Jڛi ԩʧNڶƖ*)5 ;u ;Wv򆷧*h'Xz-Z\&+%ۄ0TԆakˑ>ƚkJUЦQBfU;Rzjt#RREmO*w5mY÷?']&(]/][;& e%n aO./ng17OA[Q<Ӌޣb }4@Z:7#qۆ` M%|q:^m}~i [̌,,6߰Dx>3ᅪ;[%9F4 K_eDzDz6JMBe4\ZcBc5sI~CCtLƑ5,Qi((Z8oG23,El?Y P"xBpaQ|Kࡰ p9JZ𷘿f T\:ug18]Dbźh ıy' yUZV&(>ӿzyߠI͙Ķec16j˚7`cͨbQ}\O+QPOC|~@H{$P˂b>bj!G;A=L7B(x0od㰲ep3aH%?c6!G6媘Oq7YlmjS2rF~%H/ #" +6#"- +465$ 8oX`<~&T$p_54GX&wD)lfBz^RRp b*ԢJF6V./`쥽ftsd[R:K~i,QwLؑjC/v_QP_S1?˴]WfP¨b8 {"j\ݗAV񿸬LQxåhgx`Zn9IٚP=lWgfs77$0[h1ػQO;M $R )1#s!tlOkgYE cбHz1I {"/DcaQ7g,-|ഛG;axkMwkPu.φ3Nq*_TS],q5"pܠc:ȄAr6tQ BX\Ľ,x3B 6}-61_ O*CfKYx#W{4Ң'ɜj?.`3}Q2#N=dbK.{ӄ֢eUļ|Xc/[yh)iF;uN2 3˸T3gw`ͳ|o͹ݰN} f"n$bTx@4c}ZbCG֖qzzJJ g؈8槔p``N~?Fy..2cw lϒ%ICeޔkVE  gR|ֶ]J0eÛپxᰉPqde"y!7^'ԤHYyy $o%{ڿT[бa%$>T KIn|q]58s2Uapaׯ`Ify<ܶMײ5vY&M%bi:Nk?WL9kW<~}.$cov9+)0&[schKEO6AEql*<=/ߣlWGo$[$(CEf!?.|ʸM1V&/s(ջzWe'Ý;ќuBᏘ\6IDk x`DwDQs/l?XZRR9B[.Gq0nrxRPxO $' >"Y6W:?N4n&^klDeD%G: өPWqK@zsy m>չ&5_`tN3RZEaF ώ@|3*^#(`[󗦝(%Tu<(;ku]sd g=|oݩ\Q ƚDO_ Pd_ @d:W0xp~\ u$v'p(rf#wp/JR=3m{-fapx$fN+I$]y^&(mY!ݦZo1uf QQ\[c J$vB+ Z.襵.tj| !}ka2}{`& Zm~:Վ ep/W¼?\^YtmYH˜96CubqMp@)Jj:t) =tu5$IޖY< a~Թe}Βʽ Z=MT5A*{ݼkn,Wt,5["I&|UWCth-g?eO |cEf'qT;ap)EbnHr^ɰ5,?,_l}S=G來p,CFICv^%iTҹe1rKKjb2,~5_q><2zlDY^1 3qICZqd%ߑ8>C?i\_JN|rߧgWQ;}iV)q.0_c|e.SCacؿriQS>ЎPqbjˡs2n:yZ)˝~ewG,UF @.*r5yfrcOk޻O0;+Ϳ*4򐯶 sv еlK5*X+QŮ~[kx4^骗Po0Ǖ!~Z9F rU` Q))7.Ub7|y,Vk[١p}oFCzN,3R V^xx@<:wlDxt~O54eÅJ$|9~eH\c+\mؔQs"8y:%?wMN l5ޫ 2N?sңBpAObwH JAlggLPް%*1N/fMXkV3è~KHzZ!|(Ã/CY<01Jc¨za5z\VΣY|#"N] x> mO4S+`M7K* H#>bZu<kNb޿" ;wTl*0`NoztX-(9nK}]}FDa{A jJ2` 윑aZ=΃㧬[e?r>3D5YW:7~cUbk1GN@TuhQ[#k EmH\WHا {/4aCBe{ ^/2Xf?_99Q7wfnTa8UC"՜g_zde%i+w5"6SDt09Cם%ͧD,m2qEawэTuM&o*mտ2/yGrfJq3MZX73X13cx ',=vڠIlOГT1BRz`8$M@l;_3W+V:؛[z%k.DUXh$o؇&:#D?֬h`3xlE>nFcIv"C }G8GX5_(]hom嚸AUѝazbmKJU\d`,ː\ɤtxov!4׍Ϥ'5f>c¼ʛqpY9oL~6S( Qz.2`8J/q.gvh-$b|o#mv'ۯn/>1'p&4 t_M_~$"XDvu (a}Ӣ&3wVXBwGksRՅ[}~zFO`-J\-L]싸iRSn@i]Н7?(ffO. 5pD~ qC ~˨V71{Bb1LDs4.Nc`GN}"" 4F n K⋓]73hqx-Y1rC0Jrh8 |(sX%#>9bj#MePj] Z[2x؛*?Je tۣpa"^R/P/(RaFevO_CU~g6{񍴓2yW>Ueo%z4>]GH5*Xgޝg_D )1j%=_=Q.{,P_1QYWޡ>> 9>Ce5 8mW/83] )3W*NІ%N25\}V8#ƃ|Ģ{F*!EoJMo^{_>umnWX8֎\U/KU4z*14krބc%ф]JNx:2Xʸr{"[OZXk#jy.IҚrSd[S[pN_} `<%&J=PKĆ/ҹʾI ݖj+_\&4{2'c=t\I, VgTE((F}RI7΀tfePJq`LfzȺ+熏V燚PhoVL%뵘Kվ0)SI=0'F+\(}衠R'c˻҇eK,|cS'$aʁ34r‰6'yU#m{lé87RRwVdlK<اNđ2 r;pkMpF&(%&ݶA3:ɡ֢9&:Ŕo]!7Wjd><”|ʏfyg<TLwY1_<׫?ԈGQ(Q!.aIia鸗o\%Ϝa+.B{u*4KWtux{g}-0Kq}7>x;'py 67 ޛҺ'luMN/\5 =CUۄ :<&GYjI( e&`B0DYBzsUMYqw̩9[7{)Nkׇo^rӆv4mӖ(m,۶m۶m۶m۶m۶]v_O$IzKWܚ{j~,٪~`S9ѸĂsk-h">S+i6"7ɞg4x?MM~hP3!hQ}7fNY^.  CqCH.6h87ZAG;wohnkpo X>\8 ?fLW"Tr(λ<ꮠH3s~b!@ G.hYqx|A$mPcG)ZѲh31Îni"Ŗ DKu 1OeF`n=ؼJTlOʨGff$`Xm!I@/ s0z6$uur$1"؃Gՠ%DO3?@|w{̰pS?Wj-U)5Gnl$#z؉Q21NP~ qbRCԅ Z ۃnQo#C3867P0}d&tW6B ]UK7rUHmGM6'yqC?\#_vzZ_?.m;3&6?oM;EU#VI*e3O p!]W{@_#N94ﯳ ^0U5ΕydZNZdst]6A9ͩxʆ~f~ESBY8e&G>y*> DןszUKxۢ_SO@C@4@l4cO2=x9юu:lإǩhT(ɿYyȫicf A\2j-^T< u}oׁ'm^v68'@l5+S||)s/DBuxj] aLOL#.ΉҜk/$HO~H&<:O.ɶsjy=>U"/6煋gA˖D[çBOqsHvn8x$ijBnYNݜIڥ1yvqe@,4dzy_$ctߴ Wx9'蓣>GU"/B,ca<@ďxHbm< *gB1pȧ$n6Sb#(. 0%8,f~/F<@臩+ yhJ/{JC?$ bik3@)%W uXNDH1Q(G˸H$( +uI3V5uqEVk@b}:zct㡀QALCYdzJԇ,/"V6ԠR؈;􎾭 'tYZ>2!YP,Nvȣ,qlȜK5_T=B>k=Gޣ}">2kh4 ֛^@:˩Ya==x-7qIu~DTNg"Iauql?1 X^Uu_W2O~c%ThQEi 6u^ͺ0Lb,HKq*a`գr%%'FN391z $&닸&]|!R=$A #lR=4V;ȡ>3RwF7A]o|{Qp}mY'uClr|EmId?G#'~0N2/ ڻQvc89ϵzh =T"tޕڪSӫYH{ZᏜ $!o[囬 7]wD7Zʕ!)6#qUJ3RuP(lh5OhoJ<'i>3.ےnE%%#ebzh[G^qwVZ4uv"g1N9=˸}oTAR>%/Aմ},C@}X(L,k-ݒ[OcJUqa}(t@Zkƫ>4ҷwdϿ )Y[riƆLZ ĿUgix(Iw@q$ dTHkkl L1%Ehg6Q ͓{==@ ܉`؞%9߿ݍHm4=?52m~fmo)jK5YE2n ZgfQV dpq{0j]d##& !}&z@ 5cq nU[&E y.>J#aS̟QJ:9˝&A/7o*ztC~eZ=7 )_}@0d2+PG)_f+罀`tL\\a8ZM՝MOTSH1[HY꫶/sVD̃SO1T2Q[V~!! %.eBFbRLF? w6҂J<櫵K=D .5Ջ(9U٬1^+䦧 ( |MБLf/臛PZ,rܦɸa9qD]ӈ"1̖\L%xۚ T0bSXNI )^T7zh$-{[Q h?=Cuċ >cHScoxfZ;kᎨ ǧ[2'!jZ+ĦBv'%d:փ&V}lW`כb~K ԸKˀ8C] XK"Dj1-Z|䊏8(?iW57m5Wn/m}-]xvEI/Q5P e: IX437cѝ8s~) 36'|T98xi٭_эEu_~<&"K"&r;s̽]EP"MynӇBw6iDt-ˎarbq4=JqFN"2 6;Og> x>8n#ؖ]5j8$2 q!ĒPUq1\RNV\8r#-(/[>ޔM/X`W ?)~ADG |yo~A E*s<0dlG]Pa'fNM'fP<(oGERC"bwM2RyҰq%rQcozfw't.yr\dZHm9Ĭ~N9O}M~[3a@b=2h5/}F 0sWj"H_ zoICX D c4k׉W~}$[Gf?NJȨ3ee9Cl`!arɗK @R{ZהɟT1HFqۣs|uZCn m2Jgںu/7W69WYAr"%fl"epu+κܬW U{P rJ 2@qOb}t(c 6+GXj1u;mVKɀR mkjttqA#ld;d=[NILj1P)@hDd9k(#!r'N6yHp^+8j/~RRXdK ?,jx#T?=#Ke. t aNx}d$JQiV3VEo+U@O,1uܖP',BMu )&jl=xSݣD4\f9gKbمyW$,xРǓ1%(r.n`S3jL7.P=:hصKuZf$CLΎ?6%6O {kp @ -A12<*`LLcn=ߞ+Qaeۣ{3-dY6 3a%,}.ÌU ףnP="^a=йV8̶LRr17%gZM]ШA7|x'H࿧eA .8BlOdo^&Se?P3ܶR mgL,iAHF;cUR*W0ɤ?yEDi\5_TK: Z%2^xm8*GBg"+Hx޳_}A1qh1äĬÄ)#!@(==a'\M@xcjЂ3O%?i>rykEq֬cѮ0;TSO3G\ {SS2>RǨr| Y)7,LnS~+o43? + ˉ |2w{qzgE\4ĦsZЅ2|_`rID#E3Ĺm%6G%JBzV1IRu.lDGtJ)wK|&э2>CbC.MC 88^ʴe_ӥVUnJ1RK%b3WfcF0Y?>As@JwXƠ{/:Ic00>ёZpztnxny9H/wGjeֲX Ntl^3Uer$.6JG-Y|EE05i' Ss `;t߁(l+7WnV&zӔ3f7rfIUӱCEKG?+ l\P=?Ns㱣i*nN BG^V VY$JT`6vE!8옃׮<}͖a'r̲Pmb*lȳ@޽߯ڍ{e2>a9ki0pn$^`L`ء kT Y0KUY:%_EUiF&,¦cq488?L( 77Eyp8 Q~<֍ܘצN[O S0(Y$>/D,(FE#xELg;OI e$xfHDCo٥D(hf1MʤG6).*1k*IpE2[_kg[Oo赐13޼ ¶9sy8n ,l#r(|r[$f`S>C>fU[a3xXJAdM~?3 |bJ6* WG4Ryb1T!cMmZ6TEi,[A"0cLO@EAw.#İIº *Ti/+I8b!`;G-wrfOH]7*]Sr,?jt x{ҹRwL`IR th@) ~1KIJZaKMM Ե̡g4u NIog#r(p?0Y^ 0?_-M]ypmF€cM7G8#Ŝsvg3V0!g'jEGhn΅U0ͧ9Acz& [ɩ u3?g # }KI;K O%P `d(-?k* kroy`5%r; jD'd9h|CQNVǃwlx 7vC}'M/>rlv6B&0ЉԦ_6=C?'\0o: +ȆwX+laG+[NoUtM0UIN5^N~=(? !p@7A5H5Ҝa?GcۣἼ|)~~xNjf]K.|gKx2lsx/nnkVeN %^PCV|+3{>0 ZB D7a~O`D.q}jƏBqt䰕w8(5Y]p;yoG?mU*61;OA]"iG-oɃ>{rh0$6[|b]nST*!="@啶(8;Y Bu)Ky. /׏t'V3,$kW, swy#+/6.4ISP8-F."p{,s/ mtho1;w)taR 84d~Lް{-1.A0)b]ZF]`쐟oi&%/Z ]6[.^%k{.r&%r~>TJD4|a[֮7-Pu֎h­N$2_ H>KTT @C{)1P }ew DJs03s&w/uy3>o{n0x?/Cb)`*@)?Fh(dy%?GW( ! wT h47B Fp,5N8bi t;'@(37\Ow#˪me)>l`gn.).?Yp}XsE|_aᘗnqVoǀB)&pW^¿jf,@M d_&9jl ([ti@Ew"4@WeV eaزII{!ˬ n]Ex@zz9_GK?cq{T#D>5<s׵M̷F!$C}&UFdW'~ZOp /\d2x 7qTUֶ+f_Rs P/vHMqi%&,r¥ zȋd-A27U*1{l!5 5샳uQ7 hWi ;f z@U܆W qcphn9+|>XP;Wu5j[>"cQm?؁hOiH; y9b7i)0um}d6"9&2= zOQ_hVֹjrp?aF\aj0X‰[ld֬\b8CjBjftUSg/E>f3FwΦ.kF.=OK:*VW~§*lȾ匴"o`m.;ry]:v58B@2@WױO*2tgagkbRla(5eoyuwEjQž{J6B,1X+6ߓt)3SZrk*х^Y4BWϺGh=/bb=[ϛc@;7>ԌmZKN"BPK~v /)99  \a GD@4t_XK} ; _%EfM$UgW&]mȁŶUY)`^N$ns@p~HbqT'UqEӈ\#_P^\;mZ1 :üan#w1nY"# 6wmf Zobz Џ#]JH^J,k`d_⦘J?j_(fZݘ_O Pы},)B [?e(8s2 HP !5כl_BO) _gocU%6ڼmE-nk~=Ceߧ`ee8x#GaVgYc|2*LjB޸AKJ Nw{JftﷄyYF.pN+[8\8wwepNJlv̞>ط}[z(=PJՔ8hR\.ځ6 Oη-ڢɐn +[hy؆4r6m?j*!y&ٌٗ;հ?bsնp]>'"~0HI>80r--cá5RlafqfAiq Tb=@_!_W qTͦ%؉IG33pz!zǺ$K4|>?rKE>Y:cD~(ѐHQiiaEn(3D`NaHvяȘy~ $m,Wťm%>;Nr89`;eL2 9& ԩeKS0~y`)'7*u]{Z,;#w}ϛKoNp /qiUorbqRb=^+H5h段g{Lf}ygo: #fQy1y6}j~ =  GY"o9𗶫)agiqYy]f;|1Ƌ8v\$@$oopG|fl[M;KX7ڣIZ^.::[RwVAq u<栳+kbp3vᷫF(m~EQ.71h-$et'Gm :($ϭ|KgTx:${Z],fA6n?0$lz͇,,O:o}nbxi(H]㛮uݢ#< " kYlrT~SxmzVi*og_A7]9p )~n&NA7[.e":M/a6zD~#%鋻m`6 %^8|CX.=Ⓗ9Ν_&< ŕ6xDޓv)9Tu^|6uH4 k iqT(ɩkhUg4-gPtqKְ=m%='yx0LfRX7<#aBRzoQS2 1{qpoZ N$ҟ6`YJ7-  /3^~[ϣ=uHF_Ea@kZU@TݦpEmuE*=Z3 8rޒUy%Ol|0j.U$F2%P#?#4@"8̝X|>+⌽"S0˳[z )ٽ}ٜgiΊ|y%AR3!L`5.aRcfF /K03'3/L5^bV Ƭem~gR*yԝ$5RU|)] &5T3ܙտbmw~u=ч4nW'VB.Qw|**)2roy(Qd LA.gB -:`e-)PN<|QVm$z S^{Gcn7[=2L.ͭxl>˺=NVsԏLI/]|;g@b\AkgLo4ޢK|ާUx2@Eb /KkPr{m;tf?ѝIJU] ;3d@RF[;wƟAكw-wgJ|3S>Xm'X 5zûRgSFPH x'Vߒm{ ch.dW _Dzcm}W:iqPFC4Iܠ=# P:}?"OopJX#&+J()N0`nY1sE endstream endobj 107 0 obj << /Type /FontDescriptor /FontName /ZXKLBE+LMRoman8-Regular /Flags 4 /FontBBox [-456 -292 1497 1125] /Ascent 0 /CapHeight 0 /Descent 0 /ItalicAngle 0 /StemV 76 /XHeight 431 /CharSet (/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/R/S/T/U/V/W/Y/a/asteriskmath/at/b/c/comma/d/e/equal/f/five/g/h/i/j/k/l/m/n/nine/o/one/p/parenleft/parenright/period/r/s/semicolon/seven/six/slash/t/three/two/u/underscore/v/w/y/zero) /FontFile 106 0 R >> endobj 108 0 obj << /Length1 2070 /Length2 23604 /Length3 0 /Length 24883 /Filter /FlateDecode >> stream xڴeP[58wwwwơ .-k[p9̜3S߯(YUMAJ/l2JxrNL" [3 # d/f p:[M,LLI=i0<j㿀ə Ҽ<,,`SO@dc0703@nF+5`45j@-*@REQ]IὰKĄ@ :ڟj@wtw>Մմę p::Yi?(ߙjف͍əh`?5K+'t]NgK ) 'I/ݻIv{OMjciWholol0 4A @OE@+ӳ1v3wq6lSӿ*V?왕_6yai qU5zcWzbr<.&37}HDAvvיִ'f3уf?Fs+{3?80[}vJ_ o~M-4kNr9̍m>V+3'}ߏ _եAߙOy?f {[Q> %bk`l/=7'B(#Ihdlj/Mev6~xa{ [~eRsl߇±s_8>6@''_.C]?dʊҪ=+ň̬ۛ-,cGGc`agx1O 02؃S.>s#Ÿ]018Y#.Qo7b0q4W1qMދ2,F!{?,IXNߐ?; {,y9xG@G+??{ {3gKG 8?Z\{ҟ;Ky_XdԴ2{#D]D352G݋ @> ̜Y|+__T@;audlZ+^8[M G@K&j5sO6)P(?$'ţ`_Em=zLYhWޗE\x"OA=0K~ſH&@m>##@}X!e *T{>["s+- A7l7V\P ^]bGm|sE޲ #Q$x\x⣌Aq2=)41i/0l^w2[g93meApMSoZp2O(.kV-}&)#77Qn.y!W^b̮ςO\f\Jv[tڶPtwY;?E>xƹXĜQfh;S*`I,щC8+cx{kLi' Ϧ,;ԡ7: # \jJ[ ksT>g!??eA;~دV2eĮq}r Ln툆d}nyR,Q*frq6UOk/ĞEaA MO-S;3\GM:EE,s?$&6RكL:GUr`M"3몗pJ G9ƊyDY𕳥/M[KhNӨ&Hd8"}2[<wb{|ΎN Blp=mUK]RG<0bd5).IɛO dԢG4,Q$7^?HL D0.5څh ؏iv9 _m)^k]b7mE- C{E^)/?1]x_+3S@AF?iy%=D{_}MkXu3aO{kެ/_wiC+B6uޭIW7700Q`A]z}P$xՒ¨/:!KP=tx_&-SVL4Lζ1~&!vE-fzW,2V##idu=uRل0BV6@x`qj+mj]|(nA }v7mkmT'~Goh8*P+]!{Ecx nүu-19'&JZky@Q v"?N -Xdm}ķu^I[;(ipWǛL-ǝKA J\Vӣ&o(6&*Q~idLC86sąxrffZm2 4 i:70 aRIj_}ʨE+ZJd49!q9vkg[p!773kN7cE:f2;xSjgEP@a=$ǾiG@4<)1C c27w4ITj᫈.4Q+aRRqoK M9jPD gsrXdREopv{Sx$?5 ō7ؔEVn[z>"Ͳl6/̘ʥ~դYɶKiYw오*||"j5]~~q;'] &HK+]ADq?b[',nzHInqv /f%x%N󕾛 &՞ݍ]NQ{grk)]+W=aUNA4nD`9ܚ}.$n2kzr<46UpɾjkXDoق*4u4R:p8B)vr/#C(0Fz??Sb3w>|yAT),V4Wr,n(^2ί, 9ve>I| [x N -CaSv+Wi۠YFs##DW<ĭ⤀&49X_0u%F_}z1R +_ [)yXDy@IIH922ZPx& V츊ɴn_&VQ?hQ,\p%7h67 :Jk/1~ѳ$ewǿN&V.ݕ8ܕca*+0W0yID-7D e;?{AP"J_;ÿr8-$7 y 1ތ\0Z+F"jRb豴<;}*VdELV26$s=.1KK6E™[Se vf;Įm)j0Vu[B&,x 9E1[ՆX}v^]3:Bz5w-vcy5Ef I 2$.m&aĄL-߃a`{v؍$4q'ٜah0Js¾_8DeμjJګ,D=td.jFU_JUؓ3nӪW,3$'B"z\'l(ILNv})HG嫑kF̛;1$&c|l^$ |z>m6UԄJjEѳW/ip/_´3\VY>0K!&~ [JBMkkufӨ vyU Qu蠫۔:IW-W0i/>|u_y~ɤ l~'jqf})pczI ƣ[[?:UX1|T:7Qw!nLrځVۅ4v=~AD(8Re'Pȶt®o%0ˬFV"1-5r )] >a/%_E!fͧ9=#0,m^okCh/o&EmϧtQR^QI",2('U5ib\'%ǼvlSjJGEC?*_H]lgZM,VdOū&M5jyeQP ]3U֗(_hX˻,vR_ ^~n.;%oMZ# 9frK[ݫN/K^sBEyQеmR~t Uk٤Y\h^?6AN՚,GMTBMq=7_[\=>&rwujڛt:vؽp?$*~vK֒bEx>|Kb*ka8'%7ٹcFf\m&&<6‚61ՀCMvIf\.yQ+LB9K5O_XK($%PB`zEB  p4XmC$o+W,v.ۼ Ba#egA$4_bH(?)U~@8ںaKYI5og4NƜCI^ș7(_E6E_JBj ,va覶4bxґ¶b"r#j}kso~&#B}D)wA!V3{#4j4; c]ʧ&O%8v6 O JX1Җ{־03ܵZՏ@[˄`GyY3ղhߌX'# xiL3RNCU-n2*"!'YTqH/|PraS)V9"%\q_etuaFd&z}N N}@`Ԋ03 dgVR~Ty^ɳumP\QLx`yЖ9gARfq3E~JR~C6\\VMw (hQюyqn[{>vCkrO lDD$BojDuA`ShLG`J DK^fM~ԉ%y Q)dv|bIOA-,q=<#3L' _cd`XY娿%察?2-Mm~:#^~9\f*BϦϽ͠Ѓ mQC 17p6AћVzZwݸ1Uc 4gضxYui!=S#H;,CH ?3IMzYs8qUvLd0`6:]ZvGzݤ(uU=/22c٪uyHQZ.U̫Glpt[kep>)/img /ׇIwLOvm,<)%5rA%s#HF[dм}?GT\Yq]̖<GBSgRkݟ1_kEm?3f=z\?4Gv:1n4'R`=[2ĆRdYl8#N4?ptI & X:P2!77 th-xoz䌁.Z鯸w$Ҙ_F9+av(diTRCh79 X {mM+%xQP# m-3؞kI4jvwuͅ1&~dQ9$EWTIeiz"g&cGCQeq+ij2"*/ԄH-J, g.]gǺD=lGlyo@Xoc;3ٱ-I:0cCj̸q$}kO+@oXLHR=_>)!0Ž08UMimqA gvͻ;`NbJސ[juYN9&2Ɏ?]EEÖZ!z5-BV{Hxf'`/]h)w&wwMzω%RĖM  1#g\A α8%`,QˬVE6JP8oiO@ѭN=œ2Э.@y׃ GBDr乧M@mQi`N6BO74?fҐC0A oZ(NkeʔWv7f .hϩibAASЅV)z]slG`L1~0Gn`*{[si=+33@F|Qd3y"<}U;R#zЁiusZx ǍaAsT҇Kt;QZ2W/(a˔=|#M(_W6A4Z"pJyp{@Cq~;>ӥolRD,DvfP+J5N^Sկ{~<J|諕WlHV]@x}ᙊ FK7Vэ0zn&|j4Ccj_W@D$GXD]A'wXv#ИF.a(쇴~9>"= \S/L?~B:pp%_Qf>g d2;< J |*xZWd 6e>1"'V{zAa4zuN3PhbxoR۵C۶虷cUkpU vzKEcu*1lYp-o׻ I膟9KM݌hoL H໲BXL虣KP9y\svBMNXGܪ$m. y3wd~ U_>NlN};`~5aZ>: @CM0&ϗ`̀m g%<-*4ĎdiT~o[d8)M`ͯfV 2M, )Ƴtz÷7,Ụ*c7؜Cp&*]OYURBˉFG,xM~^DI ,{trRuW~:o-qJ B*  .7p{OoD?XupLO}H8i[.B-F"ỌsdoxM^N*?EUHC苈;آR5YDC4` Ұ$:2m:3UAIspwJl]XȰa$]흓x^OGcz !:*M~@_BcŖqoJս*Nzo9U}q+&|a]i:Pz߮Hcqt6:/ƃkE6A_mT%6R$2=RIFZn[vfg .k)!bb@OL ? ̞qG;oTl$"RBZB&tkIk8-G7i*KVȀ+ۻXb ŝ(Z lLqsIQe5[$nУk5ZxvR(oEi/[u>y,iY6>Ld#K_aqhEN±;]( Ahsګ >77YJҍ.|,SLV$)I^S G:iu|fVEq^P~6{S$<`lUNu@'2h2xGOqҲ:QC(_Rw̯sE0{"ZёC,=kc O`BiocU,ZP-`?Z:{lc9!FbF}W¢Z)?g =WPmJbYE=~L07jTB+RϹxzsn8&]̫?5DRp$8E/נ\綜Pq{S# x, Zdҟ\PK [lI|h==@&ƷcOؐ Wtm]sxpK$gt~B2VX< Fd:]HՆfdY)QT P@UQ:Uۤ /DJMjDh~T: , ښsҽZ+17 G`!6ܮDTvnh vlR\H3'Y}bj '(e ,e3*mgt}t\m /<ޖFM7xT+WO#2;8VxUB9-j|8X3'j1 dg1D7I^,~ˆV]Z+eZ+ީ* \$,x.՝B]オs'uˬr5`m ^DV)kXOp|%.C Iy_37б][ȿCz}z+і8Y"ͅ2`Uςo+l9Sl| 3saD前O> OoXt $uDԔڠrک)@ibabI,ԚREhF}&92:i98Z>EUPw ;Nf&]/)x;qJ8XcINd˯O?lr\C7EQT?Y:]#C, orʰb6U,8P[k;VO>;z UYψ lS8I):ӂ11)R#$Ф`_%v6TMc|ДH1K{<@l]ߢ2+dH\ew q"V#l*nIVA-` ~!~SvEigC}Bj#duMKz'nb6K ɉu*B T kbXeKU=L}):0J/E?y;̨yڲQAmF60Mh37ƙ8)9'昳{lf x#\<^;f }Wo>{U]eLػp)Oop<=Dais- TIEee~UzV-U LpmpTj#W Gm9;' H2aB6f|p|ߖ~ ycA9,sK4W{YkRZ͚RQ:r"wR6;/X4{(ζ^wD.׹{D/]"r\G}Gz@8uŢǥ ]k' 7;%dFc!#cn[~UG _JxwB;}fEְ-1[Ws"ܦHb0ҧXrt?QaJ~ӟo{td0laۄ)J|uNEM5mMvX鐥8lxۛ *M\+Jo܃"~h-⻥ytcvlrUfkAco#ZL옆SEm$0F* 5v5xr"n:|o57U_½.$ᆉ~0AAOh' #SrOx >q wqZ,YEL:~õ6R|%`4W ?r$vLm^v=R8^Z먨%@?;"j"l=&Qp*&f5Y)A4Y(k]6Ux$usNAs9a૿!OX-m1 Rt',;s;.ѽNRGQ26)C:q{u˳eג_׮Ϗ 󞬓|G[&^vA,͑ !p(%+G>v[98~b0¤<3[~k|'Á4&DR0}H.\!5Å7hIqHWz3FeȫGl3v]]Viw'cUnjs>)N<[K2oi[KblN#Q#ZijBt/Qaoð;`_Ծ>3V\kx)=`v}xXDw鷤.(QXlICkt.fqyڅTO}'MŒFrJE 6gf~cNnE/mXK+\}!'ߺ)>זo4AwNm-+7{O׈yd-JuYOfoϪu{0 ]2ze^֧)zE V 칮*6+Cql )xeYw4H79]Z2$7-9b:Շ,n~Uvrm9`(KEdx٢5kqܩ#Tb^K˛?ȕY ΃5v#bT«L4yp]A!AAqt6?Kvvf]+6Ed)d˴ El4gǝUHpTk iH:9ϯ/K9ՔJ5V*ɂYf*с 6򍻣]y~>]X`22t] 4a N_jr3Jl0םӵc;کcJZWr,OnlrCሿ K*Lmq?L1@&Ir`uC-qʙ0R2%^}mX;xZJ3\En`hál$E="xRiUl]7 ѸùPe@liv3Pzٯrp ui=獆-2}&QR 4Bvt*23C&#Rힼ=߾K`zvj°/)b2SQWF9gvyGE>B䴉2^B C7s_+ֻ5H¥|($Y~T?7b1 @@)NSswVN`!RH) =_X:}5M~AUj<狶/o_4uJ%Rg*qSK/|Aߠkc&-U^?nJª8hq.+nD3`A#y3L#u#mJvͯ .^`@*HHc=:+nxG:Sd%L4A[hfqoC'$U!oq5b{6O$M7umL^RT6ulaPsM_x_y{ϺgEDEL}[>D1qhc] lp8G>[|'y_,Dܸv0rU2{;OEGE8a6۴]D}Hi4gRv4H}xs[;*o3A{1>s!K[nkPNu oXpY @ve2:` xʦߏXF, "=8ɩ_W!fx $\(ec=ѺhUHU`)i2?g=:BEh}+pC Lu>Q$8tle^+Ak㫂e8 HpwXwwNp[;w篫g{~@G)U*,>]t:q⚜T4'U1H?6/‰&&17MsS2ʢoumBWgtTt*6ԦUd($#@"1;%~ ` ea!gÝ/`[pC q&V>=~/V4бwNMRZy-!$Ynp}fde|#٨d-[s,4` U;HE 9mvfq8:}zz/P+SRkeH*/i cH*˯9 wsV&XS &wC>z.J##smno4qrq|s"nbof.tv)NoOVG#(_8U[hF(b.VR Ic"M>/; f5Y"vF$6b܉eDI[2qfX0!Ӯ?|CO)+j}KfHd5|sVk6hlj %uZLfeOR=.Tqͫk6X> 0? S<[^ B: UJw1#])yw9m{a'Muƚf!<-NtӥȅN!ӸJ:{f2o.Eb{qZ]_X :}l.[쁣b9QrhC (z<6eg:7z7 +R{n,.~GAD)y(a/+\>V&v K]9z%u0๫[w`OvA~'I?{I.` oݑ?Bl3f28,.z5NN *ˏ{"h7( ֵ-@ЈrȜ'+4lot`Zw5{ƦujaG.CVs)/O wb7!oK31.Ia ]圵;B漀h%P5{/A?T]9oƟnjI.~bRh%tg v~C#1gX gsIС)nWN1tg[gpZeFYKsTr]O|Q%DKI gV9ҥ[N=4Ⱥ D-#WECU~* O4Kd1<ȩkInճ 6xKXd 6;j: vaNuQB,O+ɮM3¸{=1J,W1m=/jZ'QLL>YV0v::Fh.9r\f5_X0n"Ǚcb9iNdA+W_˵-̻_Zq@jU$L SaB1n\X9EKZpk>/Uhk`:Ę-yiˈ?[vI)p2u<H}&:R@G_qxk/3poA{^b sBonFaiN/˿-d4k攽2œAqFZ}nKEA4Uݠ}T"父;*&QȺJ/ 蟋pn/K;d=EeO' <闁68Z$E"He6RB)gQ<;>!iA,J"{{|4iH/TL <'++^?i$ly >H$(R Ry#3yƯ++i5G_y~au|Tbϕtj»S;?@{W:0ȤR^ /"r$]z|ʺZ6Lt &J B vHO(RzOmKA#?)@F`80vXi9$O3UOQlZBpA#gQ#~:".ZZ>VTyjZLa6#Z$(lE׉j3zdEzϰ5))EHF{a:aMڷQƜ\pn)} ׫3Zwot}uh2lgl5 m@wb7nCz|"P*ۙE0$R:H͋WZp)436ɣAv{ |@3暶nԲ++^_"E;:C<-\7h '㲥R@=k@&tO cѿ%ꦍNQiVRMTX3HޜL_[. ]#*^'_S{֒PYHy.ְ}el )z}Ѻ IB~k:[9R}~waP%H,k|d|ar;di,EԻI221TQ+y=C1ut$(S?fmUDYƛo릲ʜEIc--:$ G$S^ECr㓭+H˒&vЗeNdoY*kV]#$V+H[\R"FuZTIA˭=qɾ>niٲ6/+C^T(FzcԔoK'+iol tcq"Di`RuŇPYKD`4oےשH=[eTΆ!~.2Sǝ% zä9|V'KsEeD|Ն" L-x=#[>^./f 6xFwb21 +:B]Woxb_OЩU7 9L^$'W" HNNXݥ*DjB"lޣF [BkU+Vtg]/!L7<%4c Y/"!~Dx98&;"+}6cx~Wpq,M~bE)+hdx%%F[`6 \1ˤ3MԖsF-fBN)ܥ,xsLi~& p}+hl'*{hM͑t9%C"+`}=#mgo`hb_ؽ9Q*&COR9ً>Θuov!pih; 7{rvT)wbG4 ?UJC:gWCo?d?($k4XHO?w_V/͊s+WZX/s)kZ.,P31`d{}˅I.ԉDܙ+A`Dۚq#,>Ag: ^טL ?5y˒L>9߉jU="GM4ĥjU`dBM9a@!':5,1]qg1Gً68\&U~LGs,m'AcoP6g?Pw:@lT%4AXۚٶY˭e(B\ + $[d( >Iq7.b\=9Qr'p_4}a(- RF&`]E]6x<ȧ(ZIsܚ?׬Rs&{1vUQ ڥ}lu>֌JOc4p)S/ͅ޺`G^AQEyOh0%?Ѫ3@ A:nKh<(">In+;V?D/6rj\Z}2-cR.?fTQ͐Z^]՟D4,з Y֤|xԕk,7͖56=7y[KmO=8NΕ Dve>յ_ 벒dֱX&A9CrE5u@ѯߠ)*spz:I8QPqwA &r7-[Y HAbKʼ8)u~2s7?0W\ѹ1ڒyjbz`tɗ]h|SB7->/ ؎o6`'ۚ*c ē99G>OLq 3'2K;N%ioXQ503$b,'9( QR6EDJo0S}het/%8aR̳N"70.Xu%y@ɸܫQEJbٺu-.'CflVNNv>JT+6%z%k&Ȑk:D y>]+:к?;Fn[J3E|A8+lJ7 "M>3],\]s|Nk3LEBe. jX-]b -,_ kL[{NMQ^ȲtS^S T]me5 M-|[*VZtevc+'rY/8&ؐeT=Rbݮ3ul2}gH QT ]c>LOjR9Y*h ݥ:DڜGG@Y-{}Bh#%N8\1|9IbVyR<˦ux'Ȫyz4Klf6@VKU#~9;Wޜa|4!]ayj`umA>Y(g5}GT}\8֮k]V ܭ%gS1;үN5q *xm4L+|L<ٜsq g f"$4p#6<=]5 RdzX Czx;Krټ(rE|@!'9Rò,ouËLcN{HBSK])g a;0rr_y:*:թ\q_Cm𧻋4I*iF!j!zqD-VV54 +Ș |9p5Q9<&o *V vWѦZ0gHT䰴OH:&אָF!2x)$l8h@nz :o|Ǥ4fdwjtӟ]S\˄cLdv

c{D?W%yk)EskSY>H}!OcjPt.OϚ5$a>+"X1vGp(wJX!uWt"TTg+MyTx) Oyp|K >Ð"%ϧMYyn'"| g d'ۺ GW"Jź񴩑)4zܱ۠]DG,~*>S ;?s[cp6ruȃ\qH 4J IRҲ[UlP#CƳ=CЎO\]Q_M("%cn5!R| xC`jV~GWX,VX2ue`o77}'>t}߈:hSy$6,ZGN68Cv@в5-L2V0 v皑 endstream endobj 109 0 obj << /Type /FontDescriptor /FontName /QMQOIS+LMSans10-Bold /Flags 4 /FontBBox [-460 -297 1761 1134] /Ascent 694 /CapHeight 694 /Descent -194 /ItalicAngle 0 /StemV 136 /XHeight 458 /CharSet (/C/I/N/P/S/T/a/b/c/d/e/f/ff/fi/g/h/i/l/m/n/o/one/p/period/r/s/t/three/two/u/v/y) /FontFile 108 0 R >> endobj 110 0 obj << /Length1 1684 /Length2 1579 /Length3 0 /Length 2629 /Filter /FlateDecode >> stream xڵT{\LvI1QYR6SS*SHjJhN3gf9g9S. B7eV&hlEmZTr۵L,9|_l՝^S4;bAcBj ,TJ 9(ffr pНp2r@0(fFa9p`8U`:4 6ᐂpèAaK"P5T|!R|Kc@E` A 3W940G)aX<8 oe8xq?F B%d"Lg1l&ݖHX@ȶ_`3'OЈTj \lmhBar!M&b@D.@Gx0$/l)%$;0) F)4l?B(@• ̟? J#ePILAR3nFn D++@H`<3X>^LOh0Gh pttt!uDTJVPH<'lCbBc+@P<Rf"JCa| a8<-ZL'%q12LD!xQbP$ p1rE;> CTA` $]`aO#ɒZ>JT (N3s_RJ$wbD/_ĭI\ I! /$'&yCLB 4;'aO9mBՄ'#B4.` q؁-qsCL`&*b!RQ;::!|>NLN!l1Z2S>!pi\U? !ʑvf脝}Z7'lCuT :}#pvfč $~\c `8Qn\6:Rj޹gP?/_캐qPf§)~UG5<<ҧ> endobj 112 0 obj << /Length1 1920 /Length2 20764 /Length3 0 /Length 21995 /Filter /FlateDecode >> stream xڴsx>mm۶Lliض۶mq;/~g3,Zny%c[C -='@ZFƖFhle`g!!r889lNf9#zza4dNv@_Aщ 15R|ڹ;9DC'ӟhAZ9 I+C uPmm@3+ @PQQT)ʩ+Q~$Vru,BJ*baYeP Whߔ aSOݟ3.@G?e 2}8Z- 7sr㤣suu5uvtu0Oj` x:lcN'3 @h$j/G+?>NNrZG3ǿks'ч#@ 4&@ @O 59_.hq2m+Oo?z6q4wtrWF 㟙J()HφF;6NnN";@ARc![kԎ0'l''[wז66O獝Tl흁@7#3?叚 ޞvv+G h898=iO `lnAe]/5.*ǖX&0tN ϞW-Qg++Yk  ͭ<Ԁ:XXQ h,oddRK8|P_ 1*?dAۏa`e/#,mV&G Gi* HHQ?&bcdklnc `da88Aic_hml>BvN[?dg)Q8t'l>Nzz?DG2:,:,!~Qu> Cd!m᣶ÿ=>j9Z8 Gӿ5hd1p\9?wߵ++99Z̍?^Xp1pr0wӢG?m"eI8h*K~ZIn%7Js$YlI1ukg# /"0J.WIHq,Q<ۊP;hz8tD]ֺٵp m{y 5:ҠW`rN?1I.DMsm k?K&i1$*ŮD=_洑^o^BXU8Nl Z9B4D!K~]YubͥX^8vu%P֬'mp[X̲KYW1 0eډ]67+852tPʔ&;.K_vZs6h\Wf2j`@GK I</9qzda.bV u%>P;ӯzaiRPC]wpe $'hO k*#Vӫ;G\!盢oCw(e#28:5jpPeۂ1SC|9ae$lOiq .{r ;T kisӜQ*5~0Po47t.¦򾦇 _k'*(iQB^!@qR<4T(RWX5)܀B؄i6a ͉<+7@&؁L pԋ_h]ByǹM0䗬Ra;6)G;SǤvn">”H_A"L9-1ln{B4 B\qZwV~VXcD^VM8L26X`b)>-9=g#mV̜Ds7an1~z# N$l#*tpSfYݎs%M'ݖ, \gd!H.hr9 ᅦP]C7*p=TZɰCG9Q~$&ޖWSRpҰ`MF^ 0LEkg5F.0&oC6wsfsVIT+WL5@`=b3%Rjۢ V&>wiÐWq(|t 77tvMYl—CY$`[aw4V_Uо Pa&ۈzӟ t)[T⊃g}:>ŹHBGv)iKtS 1ynu xu+3δ:78Nń78G2(<`=f<qms1mgߊ_Kؤ݉2a79`s]{igoPRj=_Ea49Sm{k>-*ե'IIHWsަ3m|r&4Jzɣp1^It#Mqlz"π&D"-B9jiiClʣ8-!jJtΡ|oU"h7$—5A{OJ VJ-Kg! &•Fwf3C? b|'hLdy1Ƣ[L ̲H!R,'_\U{XS]&X4tq|*zYfQa+\`[`SֵlnvFˆХe*g|1E95 ʔWGf.Ǖ̖0U "h/g-7wþ=f^2y˳wY񴿮tn1b`p9\S TpPA6켇hmu%S 6ܻ! dDڛM\b41)Wߖ=E ޺5gLNp@}Ԟ63&g&:]iZfPD# U nWQxDDKC pCcs3;Y'WSJ eUG: t.jJJnZ7QZ:KA}ȶhX@~O`"OdD(htU>M{"r6JqMB4.LZ7㡋Gog :IG[I/G%K:Z?%^>sF.V뉹 aFߚkkXţ2]AF, K㡾> wu9Gو\VpY+a݉9)I0$\s'䷬Y/eTJi{Rߨ*͔Bc?gHA]HPX4܊'X F|>hJ5e orɤ#v\ 9œ`͗bLI.K I·qTmmqgXڱB#+4n̗U %&gg{.fSև~Uy:"a#l3F;zROjtyOuGxU 55J, ΐSWqE/ML+ q0nZ?CZ+W&e75R\'2+*sҭ^]6lL\d[0hɐ#i4$lW ^a?KR _毴8e~PSB7Zo3|/,XXvYT.,+؁jKрX X2ȋ߳3v;&1%BɊάVαO^t+HĖ]]5ޔUr+ǐ5يAB$3HQ+p?!DL7݃c~0`v1EgH fHy;!sׂ#ICtS ү\q2Ohݙ_)6 "ߢYU'-.7_-hΗW0A>᣻}etHe3nCBc MUWfb4u%p?cs -yR"[4 LSM1S+1yJ4ITY!s}!vrYy\/:ַ''W‡t*dlZa}=큨R|| 鐛OچejK6tq+6HX~ex2)8𺤝tKlI pK WT!U_*#e zBbCYTka]$> f8Qpp s6 /JiNY>, xb;^f'! m7~7bj|'.%85G3Cğ<H/,KZ zv2v5 SH^1ɧ%ɟdS ٔ;mP^C 7_ ;MƏT^1|Ri,JDGAFSgԻ+^^bn ?RH$d#4 ZM"~폘\.:"sܚRΫ @Zs?cz>Z|D=@Y*^\ǕMWWP >a\XH{;qzqk&Ry/"+BKY3rlw: 6Jyl,pjS?" #}SΐCkrӺ4UC!:ta_\WOKg|&!Ħe|T~.B=tBda)m8 !Y{`2O oabw}B-SCKn9Da*-NgpI^>'NpO8dMp~xݝS1M!Cji9LA" u7J϶,Ttd7[]윒Uo14S7%NϚ+ / >2D`D16% ZV])Ҏ=FC`Ub`nQ& u2PЂ~UjXmk PЊ!ob< 2zCo PƬ?Z\dGK6-مkLr0sU2&9ds'V'Y`|ᬽ{J?4Սet|]@4DBNJ,=Yc?=ׇII繏jO`[V3ȒrAF+jOPBB|^WL6Hˣ1h#mWIP]~*ä #ti+'PQMy~;61DvϹ86KjG(:  iVu {qF%sǔ[X`Ӂ+8--#X?+=]vTPӎSERl$ GL9oF9>TR{^e&X8+cwtR ֈF|8n4~zңnIp%8wQjo0ᡫ -kӺiV.NE Oc:Y&L4q<+dGr>jӥ:nm2y#%U~#VdGD'դ$LeA}_jcowAW#~<,NW:>+1_47CDH8/Z1isO-<&M/]:+g+@vzI*Mu [^ 73a*%pot~<>}}a87U9ǕcFz$Mn4[U(`%Go'r[o|W)/IKH4ZmZ ' aʁ>U~e M Y"m:Vr71 7>4(Bگ4/%1TMT w$X"^2ˍ5e4 &eMbPsf)FC't5M[0*Z:ี'WW'JI'*!+LJdRi=f,"-9:3 I0uwZ,v~a.S˼3%S@A-A%-Dƞo яUǒF0nd.v6ٞk7o~q:h!L0&-FW$ $CA6R'r+;H"ﴰ#Oށņw߰K.X? _M zIᡫ->ѡsrzbE@20Lnv}ؕl'n&KDGYeT4hvagz(+8''; A(!S S?HFy25M\΂%jX "|~} ҩ mUD]k4Bݏs3F-bT%,4Jkŧ@[X`NPz(HӃ~ϓt>cWs3x_/ f]\`!*d5{xǗ/AJ$YGR(n'\G TO?\3kƛg@B'dwc?%wC; [:Qw)o| /ݺ|ډ_' ]Y;T]~47 C{/Y7=zl*CYF&tLbozه-u[˧R[A,aޡ"f k)vm%f?h撥QXayi0}AӦ4YlT bfD{{%|7fx&_'b`}~!@XKkFEs61?џc\#묊{*s!1W6 bn9^b,BFop&u/3k[ v3a ^2+(Q/ǓrS3˷z_!%FyKۍ%E1}0)QCT=:xRGĤp_%hKԽo-.i,xP$vLo)1:6~FǦ!amh =$H鑽kܥͪ8jeN"li]r1*AZtŴak.n,y+#, }w]cM`u/dP!<>Z i:^olb =O-CSR>z5el\*p< r-[g ![%h%ռs z@^*[jnD~{k?#׵SD˄ H7qy%n2=aJ>{/Wd׵ꌈH2rȺ0.IGgdԏܐ `k@;XS(5Z^._Iئc/:f{Ccs+Gg( @~xuYX'HpRcQ.(f%W6)[9HrnM^+#z~g?$qKܘ3S^DQ 76Ƞ`dzg2zж.(mfy).,uټl^8IjyPuGؕ2VxG4aSPUYB#7d_` Ț??=7IX=}*8.Da?Rvxvd~/z *+T,t+'EL;v@K@w#aF"r3,8y Âսz)_f $|&r 8!],|HSh`s Zmc+^`҇Gga>(Q74q\ ДW8 +q>c!!%Q]֥}2^0d4 6Qb  p3;Pq XOKV2[f?^Bpcd?YZ|-Vⰻ G1N`b6 fpJYoہ #9b>8";$R2Gm6 ҪcNJ#O@gLgSX q̒өP 5v c~.%O5\2mEؕXH8x%1X׶OIDOFqeG(!V/Su{D>o$Y2ۙY"+rM\mVm'oڻI"DKܞw{[HwOA][?Ε"UytV %m<9'qaZTmL 8̢clɯ5J(|uiG|W D٘!Kh23(~0Tht_vWGlJb҄c'Arչ )mğgD8qzF ?$F56:^BL gTLaQԪyRxԅQ7ja# TF,y2*"RPb`xZ bS11īhPTMMf?ze%Ws\Ú1L_tv}<ǒxMt,pqAךpM:TfRԳrޡ9o&-}r1^ F\GM6ŒW aIVR}N3h7% #%&YE|!#fjf-띍2@Vrǝ3]?v?4*x+;wǃE"~5@+*%*1֗Є cbvBw}U)1^QA)Mk)\LS۞fm/5}{㙈;:vq}usy$To@C#TtP#]wfÂG+J%XpR!N1Uy֌Ņ_t|R-T'SJmOVٽ}{ꑳnbY([(ZW'/ϵũ/ wFmD,ʘ0m2booS>svЪ䰿,́ K^{C XS*4s)5MzYva]#ej1[0e#GRփӤ6!lYLFVSFwG~>tHȌ=/uaS:89̕MLtc嗭:bu 9ΨZ[Yn$Br;hP_dd58wսOOhxceĘϛ@=# R%F- Jbi+\kɎMYmDME*aJw x,w&BʙypWƠ(S YS*TS>!=#l !/[sxLKL~h*ˡb}JfM Fq {`_<}G'S$BߘdH]o'\Don:Lۥ!s`5]\a,T;/j9,RHq8ދ8؞x;Rb2Z͖8/DzF{ɗNS;L\Ы`ox[d׆=LfӿPM_p}zĠ͢)U7'~. ? m{Je4{gx}UJ/M,e: %J6(oo446a7s9F ޳Ts,aE X2,CO؂q +:jN`NPTh:">l(/˓|(u9Ϲ&5(#YT4n8Idxք!lAO@- S b[HhImV|r-'2)Aqf86LҎ>żń#tXNQqBD-{Yh^. {JC$Y$Y?\WNԨ݈+e׹bY[EPO;KSN.yPZ-7 춐 3ƏOmлTW)}ҳ0b\_AH/x(eN=r ֹڴYIDUD(اRD#kUO 1{؍AB)}6pzwb>tx0j62$馛Dt9k)xqNs7w bwQ) #ʞ3nΎ"xCvĵ$FZNC[~%A9A۶N9\EO/Ї"d|r~yLqv^ebGl~~\㐁 V jgK?_ fٮ,\:3}Ci&`*F^ )Hp BG(@cvbT^C5%u*H5ֈVzK(\ "qo@~ m 힝/3n;u^zro+ RRcKhtG$^5.gt>.y~m#Z1FqXg %Eח.bSx|t 1.Oež E:u"l "q|l Zt-Wʋ+&~$5}4weαYN{F}O +M+Xw* _\$F屰úȅ.hgm5V;Z9mY5hg@&ە7TWv; _q5LܠMx c!񰽁O(2|e3X* -}@d'{e\IrL0wQx3dF8Eq8qu6PK_冏pKc7tH7ӡnZ#NRYK'o_K<0<#7mfym/A"y\ͷ3]q'a7OW&֫|O,DׅE H4Y0ٺq ͈/!ߓ%%]]_jp0nk. 3 ᭌ$r4FV\YG|VѾLs H_] 358leWAn2ƌxW `/ }yw._M2}"QIvaFZN7A԰q}'hN[OuECcevs!p nAsZxOq pĀݵqU5KC#4 Ti~c-/bZ="<$ҽ7J LNUx;Ts/82#\<Ԩ;Ʊ \UC0ۆ dARm&v:a#7NB{6B;B+] ryD:"# # - naؽs).J̔A: r9R%[?7aDSuƎRldlj6tk?LJuhϼ>dUak+Jg.f3O/țeW8ZW6U#λ~j7\҃[!WR%_X"<<8=H?X费=!pDģ̖]mcD;~xfٹ%_;t1`h,T# E|+U%dh-6*?;\P+-&ڳiX602Zy {_":O1҉7M 2:/9M@ 1 :i^6lP}HTiPKOUFxDQ&E>jG?6nr$[{L^pKƭi m;e.м%Vzn|iv> 9a L M԰wjʷ,p B$M vyAGkiٞ\e}:·/<$44ɹm 5!9]E?}uHF£Au!AXtDaDl !DÛfȥ_j*nvKZF8$:Qx$s[ͯ]_aSoc-y[>ZZT dSdͰs>_\JZrrNj垧k1֓4*G9yȈ` -uKۈKDA V:'e))FAN#i7 upuêUVYL6X:oL|cadE1lZ[|rKFЀ/_'*3 9#4ki%4kMbU{-@zɹP-nG_zQ-)7l6#5b !p9%%qvkM_}'7'1hֆǢCmzR0{$5aIrjud9m^LoR'[әD"ڋ7XX Q&89~H8hzSs{Ww;,tiȗ=|vjV58=cdRUnŽGBy_{){~@/M6^_|2u?MNRID"6M&YwIoha2mfG$$Wu\0Q`TJwݼ~,Y?$R$wqKTCesD`I5B% w }LGiJ?qEiJ-T _1-@щo#Cvrbedsmޏ0# ;\r>Yy23ŽG~c!mB N*oAۯCIb! 2`nJ âhB^x9 9y=1X9AP+HN!{L Ԅ(`Aul6J8h@b{]0eOK2 LYvB9@m. J`^m/dpBSG" bSna16~~n;Z !ĢJ#0WG-j(&>eJ{ 39;p~sBArZG!л?2jW/2k ;zPðOE36eг5U&k5}qI d/I. /[+R7wA\ CX"Y*)%zIZ~f4A,'3&3~ΨF7I\mIm XޑD &2[N m/_fk݌jMf^ *+*X6zr|!|s*~\Νw~陼+ݛ$+ 8r(h"o]{'xQY\ܝH_k<&iY2%xreĔ,M->E\fU:pt}IVfPS#;D똅5EgCo0b ~*AYR_.o3ԓlQ $_69Thc;퓵2t8l-C}TKr!8 /؜D"쬂|wq#7)caZZr>صldXj:C>ȗW(lI{_B .p#kwk,{RSMEyOr{irh#"4!͛G=>?!ыii`,C}"kf3 ԡkT!{tupa+ȮئCR8FûʬmǻA1/sjKB[ ZL+~SݚĦjRy|CJ텑FX4Oi#i[~; R">x6n[ufǗi,mFrn杞^WK1ۈۖSr$2"/#n*?H&|R71{@3 )[/+N.11JQC"م˩tTMiX%2/QbUA2u[v+JIr6OkLX[9}5E肚[7M y SuVn2 Zb&j 2]\rh KNf&Y]d ;2/7Sk]NQ_-F<#EcDzVt/͵5o/׾ >JctZ#1s5̖K|S²^7xjQkIia+ꤨ#UNG)?P#yD0,XLvi0;krP W;=nⱡSن!=HuD!DKU.tAUW__4a`a3A?0[SB4 ~Itl嫫xA0DN^s5'Ku΋XsL ;d V8t27gP5kT~%R :k1jG u. H'q5k0$Ij9min:WBإE8?ݻ<2N~횫[~zO#'D"ed+Jڠ)%/dCozqv<"6ZV)JD~OMyU<(rw.|'p0QS;,{Alyu~CT/J;YygLBaxEw L^о8B?y, 9F_ `!ۿd,sJ氿!D6 34̇\C̖Lkd\ji,h}5w,'-..U{BF4sj!CIK5`{`?`}A69_Gj򑿪/Kai󂬧fws EjiҀq}^a@]xǂevMwPu{FpZsx$/w< WlV b;u./Ju3V![4ƛx h(tUIPon6A< w6TY+Ȕkxq)exytY?,j.OI}v~ޅ[R ߠ}[ %LГ2#.qn?JҴ6zZQ_s!ɌP?gt"IuGb4&VK19lB՝/52 VYK;]GŞgߙܠ@Z&l.OϚs 2/a(%0jpXց`J\6*Nbb>;8v%- 8!&[ Et~F*˽BLoH]jWNNΠ͊+,z8,|*le>B4BNDČY'H{}g|`~:? WP{i,RQT<^BDj: //o{oNM4٨.UKunU#r5ҏ5Bj1|5 Rl4펩(-[7S9yS?9u @; KycHxpaՂ7+Ij9iW2 ߚs}_ecZ]}}ʠx쯃Czr>Xy  04p ΒJB鼯;VLKxp]G* mul02W墥ÙHҾ7]}ܣȔR|c\.~~Wv1ǿ9Y OvSx:Q(,FB ;F4: nR`%UcȍYKl( 1XS$fX> uG5XhA}P k@mM- M$7sGoN$oNw]" zf<HCc@EXDOǗsppDN/mԄFkf<|S f@qws^Qƴud}͙/ui@8G>+\iP:pٸgP=7#+OhM'_ݑ[zpFs˺[ث~eV 1޿P?c2)R,A~y&`Ͻ|HkkGygGj[2OK kӂVռo^jwrщPjN'.\6 x `z'SX4LDF6Ry ǫ.dU4VzOhjE#LGT@d.F{QF/~úRV/~P\ jϺJmQYWyF m(@2;>_!ItL hbSuә7c[˭nYQtVwo5(a6j#mgs-/Lk%Xa~'<KTе)ˮJن?ܻ,*mٿ/:MM^"Nr4[n}2}kZF6TƂtrd_m!Ętz50i݂Sj'UM* H_ 1!k[m4֑S!%MEώ^;|lG5XT14ӱW:Dg*;0}m秭@/# 4Ěb`ZL3*`;_헯Cauu6ǭT]*Rv64I )9 (37X~!(-SbkHIUnZ h {~c#d$E8ٿXqǍX@&=Ïkg?m|Ė=K}p:7}jruI9m9 tX ((?u}RDBUbܚ׏œ=QhwerkwioOX+Aڛ,{@<Ǵ hI')Dq%U1-y|PAǑ\6CaooN|\I?Ut^ybζTm\69RaDxbl#f@|.k#j|(r`z$ z(b6/(T ]M ة~U#J'\5)lu,9ٗp/I陼B\j @ʠ#]Hэ)yL+$* ^]nBOލ[vIpaq2}^-<;a==cg |HY/)Z7=|_AlZ8ǖ)"dOH!x}g͓8%b࠾`#ՕD>/F2F7ȮΘK{%uAi<,(^Cba"G;t(zռ|iO4sLLJxS3jTMҝQ,~e,?ݷ l <bNG_bZ9 ]ɖ~+U+Н2)@qiic1<<&ޡPHZCxMkrM26*\y#H`C`H oc Zuخѩ;p9zꂥZ%0|l-ŹB ʜfKLVizLi@tORhH&s*SUXZR 5VÅn<1 y20H&l)3E .?V-)aMήh0Cu򠖍EƯ9<F=Rk$+^wSMcRN2&?QEqc6j{' M&ae}>PJ+«wvQcqO6=OB M.AL+Hy3z3LL~;8h&\❗hd ^$ d"ucL_/^j!7Ymճ2A%. "U endstream endobj 113 0 obj << /Type /FontDescriptor /FontName /ZRAILW+LMMono10-Regular /Flags 4 /FontBBox [-451 -316 731 1016] /Ascent 599 /CapHeight 599 /Descent -222 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/S/a/colon/d/e/g/h/i/k/l/n/o/p/period/r/slash/t/underscore/w) /FontFile 112 0 R >> endobj 91 0 obj << /Type /Encoding /Differences [27/ff/fi 39/quoteright/parenleft/parenright 43/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven 57/nine/colon/semicolon/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P 82/R/S/T/U/V/W/X/Y 95/underscore 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p 114/r/s/t/u/v/w/x/y/z] >> endobj 95 0 obj << /Type /Encoding /Differences [0/minus] >> endobj 93 0 obj << /Type /Encoding /Differences [42/asteriskmath 136/bullet] >> endobj 50 0 obj << /Type /Font /Subtype /Type1 /BaseFont /VHDCAH+LMRoman10-Regular /FontDescriptor 103 0 R /FirstChar 27 /LastChar 122 /Widths 99 0 R /Encoding 91 0 R >> endobj 44 0 obj << /Type /Font /Subtype /Type1 /BaseFont /RINUZG+LMRoman12-Regular /FontDescriptor 105 0 R /FirstChar 44 /LastChar 121 /Widths 100 0 R /Encoding 91 0 R >> endobj 53 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZXKLBE+LMRoman8-Regular /FontDescriptor 107 0 R /FirstChar 40 /LastChar 121 /Widths 98 0 R /Encoding 91 0 R >> endobj 43 0 obj << /Type /Font /Subtype /Type1 /BaseFont /QMQOIS+LMSans10-Bold /FontDescriptor 109 0 R /FirstChar 27 /LastChar 121 /Widths 101 0 R /Encoding 91 0 R >> endobj 90 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZRAILW+LMMono10-Regular /FontDescriptor 113 0 R /FirstChar 46 /LastChar 119 /Widths 92 0 R /Encoding 91 0 R >> endobj 58 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ANTVSN+LMMathSymbols8-Regular /FontDescriptor 111 0 R /FirstChar 0 /LastChar 0 /Widths 96 0 R /Encoding 95 0 R >> endobj 80 0 obj << /Type /Font /Subtype /Type1 /BaseFont /VHDCAH+LMRoman10-Regular /FontDescriptor 103 0 R /FirstChar 136 /LastChar 136 /Widths 94 0 R /Encoding 93 0 R >> endobj 54 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZXKLBE+LMRoman8-Regular /FontDescriptor 107 0 R /FirstChar 42 /LastChar 42 /Widths 97 0 R /Encoding 93 0 R >> endobj 45 0 obj << /Type /Pages /Count 4 /Kids [38 0 R 47 0 R 82 0 R 87 0 R] >> endobj 114 0 obj << /Type /Outlines /First 3 0 R /Last 35 0 R /Count 3 >> endobj 35 0 obj << /Title 36 0 R /A 33 0 R /Parent 114 0 R /Prev 27 0 R >> endobj 31 0 obj << /Title 32 0 R /A 29 0 R /Parent 27 0 R >> endobj 27 0 obj << /Title 28 0 R /A 25 0 R /Parent 114 0 R /Prev 3 0 R /Next 35 0 R /First 31 0 R /Last 31 0 R /Count -1 >> endobj 23 0 obj << /Title 24 0 R /A 21 0 R /Parent 3 0 R /Prev 11 0 R >> endobj 19 0 obj << /Title 20 0 R /A 17 0 R /Parent 11 0 R /Prev 15 0 R >> endobj 15 0 obj << /Title 16 0 R /A 13 0 R /Parent 11 0 R /Next 19 0 R >> endobj 11 0 obj << /Title 12 0 R /A 9 0 R /Parent 3 0 R /Prev 7 0 R /Next 23 0 R /First 15 0 R /Last 19 0 R /Count -2 >> endobj 7 0 obj << /Title 8 0 R /A 5 0 R /Parent 3 0 R /Next 11 0 R >> endobj 3 0 obj << /Title 4 0 R /A 1 0 R /Parent 114 0 R /Next 27 0 R /First 7 0 R /Last 23 0 R /Count -3 >> endobj 115 0 obj << /Names [(Doc-Start) 42 0 R (lstlisting.-1) 51 0 R (lstnumber.-1.1) 52 0 R (lstnumber.-1.10) 64 0 R (lstnumber.-1.11) 65 0 R (lstnumber.-1.12) 66 0 R] /Limits [(Doc-Start) (lstnumber.-1.12)] >> endobj 116 0 obj << /Names [(lstnumber.-1.13) 67 0 R (lstnumber.-1.14) 68 0 R (lstnumber.-1.15) 69 0 R (lstnumber.-1.16) 70 0 R (lstnumber.-1.17) 71 0 R (lstnumber.-1.18) 72 0 R] /Limits [(lstnumber.-1.13) (lstnumber.-1.18)] >> endobj 117 0 obj << /Names [(lstnumber.-1.19) 73 0 R (lstnumber.-1.2) 55 0 R (lstnumber.-1.20) 74 0 R (lstnumber.-1.21) 75 0 R (lstnumber.-1.22) 76 0 R (lstnumber.-1.23) 77 0 R] /Limits [(lstnumber.-1.19) (lstnumber.-1.23)] >> endobj 118 0 obj << /Names [(lstnumber.-1.24) 78 0 R (lstnumber.-1.25) 79 0 R (lstnumber.-1.3) 56 0 R (lstnumber.-1.4) 57 0 R (lstnumber.-1.5) 59 0 R (lstnumber.-1.6) 60 0 R] /Limits [(lstnumber.-1.24) (lstnumber.-1.6)] >> endobj 119 0 obj << /Names [(lstnumber.-1.7) 61 0 R (lstnumber.-1.8) 62 0 R (lstnumber.-1.9) 63 0 R (page.1) 41 0 R (page.2) 49 0 R (page.3) 84 0 R] /Limits [(lstnumber.-1.7) (page.3)] >> endobj 120 0 obj << /Names [(page.4) 89 0 R (section.1) 2 0 R (section.2) 26 0 R (section.3) 34 0 R (subsection.1.1) 6 0 R (subsection.1.2) 10 0 R] /Limits [(page.4) (subsection.1.2)] >> endobj 121 0 obj << /Names [(subsection.1.3) 22 0 R (subsection.2.1) 30 0 R (subsubsection.1.2.1) 14 0 R (subsubsection.1.2.2) 18 0 R] /Limits [(subsection.1.3) (subsubsection.1.2.2)] >> endobj 122 0 obj << /Kids [115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R] /Limits [(Doc-Start) (subsection.1.2)] >> endobj 123 0 obj << /Kids [121 0 R] /Limits [(subsection.1.3) (subsubsection.1.2.2)] >> endobj 124 0 obj << /Kids [122 0 R 123 0 R] /Limits [(Doc-Start) (subsubsection.1.2.2)] >> endobj 125 0 obj << /Dests 124 0 R >> endobj 126 0 obj << /Type /Catalog /Pages 45 0 R /Outlines 114 0 R /Names 125 0 R /PageMode/UseOutlines /OpenAction 37 0 R >> endobj 127 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.13)/Keywords() /CreationDate (D:20120820131141+02'00') /ModDate (D:20120820131141+02'00') /Trapped /False /PTEX.Fullbanner (This is MiKTeX-pdfTeX 2.9.4535 (1.40.13)) >> endobj xref 0 128 0000000000 65535 f 0000000015 00000 n 0000005989 00000 n 0000156582 00000 n 0000000060 00000 n 0000000096 00000 n 0000007580 00000 n 0000156512 00000 n 0000000146 00000 n 0000000172 00000 n 0000007637 00000 n 0000156391 00000 n 0000000222 00000 n 0000000253 00000 n 0000007696 00000 n 0000156317 00000 n 0000000309 00000 n 0000000334 00000 n 0000009407 00000 n 0000156243 00000 n 0000000390 00000 n 0000000416 00000 n 0000009466 00000 n 0000156170 00000 n 0000000467 00000 n 0000000496 00000 n 0000010948 00000 n 0000156046 00000 n 0000000542 00000 n 0000000585 00000 n 0000011007 00000 n 0000155985 00000 n 0000000636 00000 n 0000000662 00000 n 0000011066 00000 n 0000155910 00000 n 0000000708 00000 n 0000000779 00000 n 0000001111 00000 n 0000001344 00000 n 0000000827 00000 n 0000001226 00000 n 0000001285 00000 n 0000154909 00000 n 0000154569 00000 n 0000155756 00000 n 0000007754 00000 n 0000005815 00000 n 0000001426 00000 n 0000005930 00000 n 0000154399 00000 n 0000006047 00000 n 0000006106 00000 n 0000154740 00000 n 0000155588 00000 n 0000006165 00000 n 0000006224 00000 n 0000006283 00000 n 0000155245 00000 n 0000006342 00000 n 0000006401 00000 n 0000006460 00000 n 0000006519 00000 n 0000006578 00000 n 0000006637 00000 n 0000006696 00000 n 0000006755 00000 n 0000006814 00000 n 0000006873 00000 n 0000006932 00000 n 0000006991 00000 n 0000007050 00000 n 0000007108 00000 n 0000007167 00000 n 0000007226 00000 n 0000007285 00000 n 0000007344 00000 n 0000007403 00000 n 0000007462 00000 n 0000007521 00000 n 0000155417 00000 n 0000009525 00000 n 0000009233 00000 n 0000007884 00000 n 0000009348 00000 n 0000010696 00000 n 0000011125 00000 n 0000010562 00000 n 0000009619 00000 n 0000010889 00000 n 0000155076 00000 n 0000153911 00000 n 0000011231 00000 n 0000154319 00000 n 0000011545 00000 n 0000154258 00000 n 0000011569 00000 n 0000011593 00000 n 0000011617 00000 n 0000012126 00000 n 0000012667 00000 n 0000013097 00000 n 0000013642 00000 n 0000046209 00000 n 0000046648 00000 n 0000069438 00000 n 0000069718 00000 n 0000102767 00000 n 0000103204 00000 n 0000128208 00000 n 0000128515 00000 n 0000131264 00000 n 0000131506 00000 n 0000153622 00000 n 0000155836 00000 n 0000156690 00000 n 0000156903 00000 n 0000157131 00000 n 0000157358 00000 n 0000157581 00000 n 0000157769 00000 n 0000157956 00000 n 0000158143 00000 n 0000158261 00000 n 0000158349 00000 n 0000158440 00000 n 0000158478 00000 n 0000158604 00000 n trailer << /Size 128 /Root 126 0 R /Info 127 0 R /ID [<06216D238B91C4DBA64FD7D9D8162C0D> <06216D238B91C4DBA64FD7D9D8162C0D>] >> startxref 158879 %%EOF blobby-1.0rc3/doc/codeconvention/codeconvention.tex0000644000175000017500000001203512042452365024070 0ustar danielknobedanielknobe\documentclass[a4paper]{scrartcl} \usepackage[latin1]{inputenc} \usepackage[T1]{fontenc} \usepackage{lmodern} \usepackage[english]{babel} \usepackage{hyperref} \usepackage{listings} \usepackage{color} \usepackage{textcomp} \definecolor{listinggray}{gray}{0.9} \definecolor{lbcolor}{rgb}{0.9,0.9,0.9} \definecolor{white}{rgb}{1.0,1.0,1.0} \lstset{ backgroundcolor=\color{white}, tabsize=4, rulecolor=, language=c++, basicstyle=\scriptsize, upquote=true, aboveskip={1.5\baselineskip}, columns=fixed, showstringspaces=false, extendedchars=true, breaklines=true, prebreak = \raisebox{0ex}[0ex][0ex]{\ensuremath{\hookleftarrow}}, frame=single, showtabs=false, showspaces=false, showstringspaces=false, keywordstyle=\color[rgb]{0,0,1}, commentstyle=\color[rgb]{0.133,0.545,0.133}, stringstyle=\color[rgb]{0.627,0.126,0.941}, linewidth=\textwidth } \title{codeconventions of blobby volley 2} \author{the blobby volley developers} \begin{document} \begin{titlepage} \maketitle \thispagestyle{empty} \end{titlepage} \section{Structure of files} Every .cpp .h file has the following format: \begin{lstlisting} /*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ /* includes */ /* implementation */ \end{lstlisting} \subsection{Includes} Includes are to be sorted by library. They should occur in the following order: \begin{itemize} \item Stdlib \item Boost \item Other 3rd party libraries we use (for Example: lua, tinyxml, raknet, SDL) \item Blobby \end{itemize} \subsection{Coding Style} This section describes the coding style. If you think something is missing just add it. \subsubsection{Common} \begin{itemize} \item Opening curly braces are placed in a new line \item Closing curly braces are followed by an empty line \item The left indention should be done by tabs, the rest by spaces \item Do not write more than one statement into one source line \item Break statements within switch are followed by an empty line \item Use smart pointers when ever possible \item Code should be gcc 4.6 compatible \item Code should compile in c++11 mode \item Don't use the deprecated exception handling of older c++ versions \item Binary/ternary operators are seperated from their operands by spaces (exception: long expression - for grouping purposes) \item Leaving out braces for one line code blocks after if/for is allowed when the following instruction takes only one line, but must be followed by an empty line \item Use c++ headers instead of c ones for stdlib includes (e.g. indstead of ) \item Use c++ strings instead of c strings if possible \item Prefer references over pointers \end{itemize} \subsubsection{Classes} \begin{itemize} \item Class names start with an upper case letter \item Member variables are to be prefixed with m \item Member functions start with a lower case letter \item Members occur in the following order: public, protected, private \item Access level specifiers (is it called that way? / public, protected, private) are indented one level \item Members are indented two levels \item For big classes, it might be a good idea to group member functions into different categories \item Initialize members by constructers initphase whenever possible \end{itemize} \subsection{Namespaces} TODO \section{Things you should not do} Every contribution with this attributes will be rejected. \subsection{Pattern} The following patterns are not allowed to use: \begin{itemize} \item Singleton (\url{http://en.wikipedia.org/wiki/Singleton_pattern}) \end{itemize} \section{Stuff that must be integrated in this file correctly} TODO: - Write a more detailed code-convention document - Detailed desription of every section needed Following files implement the convention: - RenderManagerSDL.cpp - RenderManagerGL2D.cpp - UserConfig.cpp - TextManager.cpp - SpeedController.cpp - SoundManager.cpp - ScriptedInputSource.cpp - ReplayRecorder.cpp - ReplayPlayer.cpp - RenderManagerGP2X.cpp - RenderManager.cpp - Player.cpp - PhysicWorld.cpp - NetworkMessage.cpp TODO: - all .cpp in subfolder - src folder a - NetworkGame.cpp - ReplayLoader.cpp \end{document}blobby-1.0rc3/src/raknet/RakPeer.h0000644000175000017500000011460712042452367020336 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief A RakPeer is the lower level Communication End Point. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_PEER_H #define __RAK_PEER_H #include "ReliabilityLayer.h" #include "RakPeerInterface.h" #include "RSACrypt.h" #include "BitStream.h" #include "SingleProducerConsumer.h" #include "PacketPool.h" class HuffmanEncodingTree; class MessageHandlerInterface; #ifdef _WIN32 // unsigned __stdcall RecvFromNetworkLoop(LPVOID arguments); void __stdcall ProcessPortUnreachable( unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ); void __stdcall ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ); unsigned __stdcall UpdateNetworkLoop( LPVOID arguments ); #else // void* RecvFromNetworkLoop( void* arguments ); void ProcessPortUnreachable( unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ); void ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ); void* UpdateNetworkLoop( void* arguments ); #endif /** * @brief The lowest communication end point in RakNet. * * This class provide the lowest communication end point in RakNet. * It is recommended that you use it if you are going to be at the * same time client and server. */ class RakPeer : public RakPeerInterface { public: /** * Constructor */ RakPeer(); /** * Destructor */ virtual ~RakPeer(); /* * -------------------------------------------------------------------------------------------- * Major Low Level Functions - Functions needed by most users * -------------------------------------------------------------------------------------------- */ /** * Starts the network threads, opens the listen port * You must call this before calling SetMaximumIncomingConnections or Connect * Multiple calls while already active are ignored. To call this function again with different settings, you must first call Disconnect() * To accept incoming connections, use SetMaximumIncomingConnections * * Parameters: * @param MaximumNumberOfPeers Required so the network can preallocate and for thread safety. * - A pure client would set this to 1. A pure server would set it to the number of allowed clients. * - A hybrid would set it to the sum of both types of connections * @param localPort The port to listen for connections on. * @param _threadSleepTimer >=0 for how many ms to Sleep each internal update cycle (recommended 30 for low performance, 0 for regular) * @param forceHostAddress Can force RakNet to use a particular IP to host on. Pass 0 to automatically pick an IP * * @return False on failure (can't create socket or thread), true on success. */ bool Initialize( unsigned short MaximumNumberOfPeers, unsigned short localPort, int _threadSleepTimer, const char *forceHostAddress=0 ); /** * Must be called while offline * Secures connections though a combination of SHA1, AES128, SYN Cookies, and RSA to prevent * connection spoofing, replay attacks, data eavesdropping, packet tampering, and MitM attacks. * There is a significant amount of processing and a slight amount of bandwidth * overhead for this feature. * * If you accept connections, you must call this or else secure connections will not be enabled * for incoming connections. * If you are connecting to another system, you can call this with values for the * (e and p,q) public keys before connecting to prevent MitM * * @param pubKeyE A pointer to the public keys from the RSACrypt class. * @param pubKeyN A pointer to the public keys from the RSACrypt class. * @param privKeyP Public key generated from the RSACrypt class. * @param privKeyQ Public key generated from the RSACrypt class. * If the private keys are 0, then a new key will be generated when this function is called * * @see the Encryption sample */ void InitializeSecurity(const char *pubKeyE, const char *pubKeyN, const char *privKeyP, const char *privKeyQ ); /** * Disables all security. * @note Must be called while offline * */ virtual void DisableSecurity( void ); /** * Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no * more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced * to the maximum number of peers allowed. Defaults to 0. * * @param numberAllowed Maximum number of incoming connections allowed. */ void SetMaximumIncomingConnections( unsigned short numberAllowed ); /** * Get the number of maximum incoming connection. * @return the maximum number of incoming connections, which is always <= MaximumNumberOfPeers */ unsigned short GetMaximumIncomingConnections( void ) const; /** * Sets the password incoming connections must match in the call to Connect (defaults to none) * Pass 0 to passwordData to specify no password * * @param passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. * Specify 0 for no password data * @param passwordDataLength The length in bytes of passwordData */ void SetIncomingPassword( const char* passwordData, int passwordDataLength ); /** * Get the password set by SetIncomingPassword in a BitStream * @return The password in a BitStream. */ RakNet::BitStream *GetIncomingPassword( void ); /** * Call this to connect to the specified host (ip or domain name) and server port. * Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. * This is a non-blocking connection. You know the connection is successful when IsConnected() returns true * or receive gets a packet with the type identifier ID_CONNECTION_ACCEPTED. If the connection is not * successful, such as rejected connection or no response then neither of these things will happen. * Requires that you first call Initialize * * @param host Either a dotted IP address or a domain name * @param remotePort Which port to connect to on the remote machine. * @param passwordData A data block that must match the data block on the server. This can be just a password, or can be a stream of data * @param passwordDataLength The length in bytes of passwordData * * @return True on successful initiation. False on incorrect parameters, internal error, or too many existing peers */ bool Connect( const char* host, unsigned short remotePort, char* passwordData, int passwordDataLength ); /** * Stops the network threads and close all connections. Multiple calls are ok. * * * @param blockDuration How long you should wait for all remaining packets to go out, per connected system * If you set it to 0 then the disconnection notification probably won't arrive */ virtual void Disconnect( unsigned int blockDuration ); /** * Returns true if the network threads are running */ bool IsActive( void ) const; /** * Fills the array remoteSystems with the playerID of all the systems we are connected to * * @param[out] remoteSystems An array of PlayerID structures to be filled with the PlayerIDs of the systems we are connected to * - pass 0 to remoteSystems to only get the number of systems we are connected to * @param numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array */ bool GetConnectionList( PlayerID *remoteSystems, unsigned short *numberOfSystems ) const; /** * Sends a block of data to the specified system that you are connected to. * This function only works while the client is connected (Use the Connect function). * * @param data The block of data to send * @param length The size in bytes of the data to send * @param priority What priority level to send on. * @param reliability How reliability to send this data * @param orderingChannel When using ordered or sequenced packets, what channel to order these on. * - Packets are only ordered relative to other packets on the same stream * @param playerId Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_PLAYER_ID to specify none * @param broadcast True to send this packet to all connected systems. If true, then playerId specifies who not to send the packet to. * @return * False if we are not connected to the specified recipient. True otherwise */ bool Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ); /** * Sends a block of data to the specified system that you are connected to. * This function only works while the client is connected (Use the Connect function). * * @param bitStream The bitstream to send * @param priority What priority level to send on. * @param reliability How reliability to send this data * @param orderingChannel When using ordered or sequenced packets, what channel to order these on. * - Packets are only ordered relative to other packets on the same stream * @param playerId Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_PLAYER_ID to specify none * @param broadcast True to send this packet to all connected systems. If true, then playerId specifies who not to send the packet to. * @return * False if we are not connected to the specified recipient. True otherwise */ bool Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ); /** * Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. * Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct * * @return * 0 if no packets are waiting to be handled, otherwise an allocated packet * If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected * This also updates all memory blocks associated with synchronized memory and distributed objects */ Packet* Receive( void ); /** * Call this to deallocate a packet returned by Receive when you are done handling it. * @param packet A packet to free */ void DeallocatePacket( Packet *packet ); /** * Return the total number of connections we are allowed */ unsigned short GetMaximumNumberOfPeers( void ) const; /* * -------------------------------------------------------------------------------------------- * Player Management Functions * -------------------------------------------------------------------------------------------- */ /** * Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). * * @param target Which connection to close * @param sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. */ void CloseConnection( PlayerID target, bool sendDisconnectionNotification ); /** * Given a playerID, returns an index from 0 to the maximum number of players allowed - 1. * * @param playerId The playerID to search for * * @return An integer from 0 to the maximum number of peers -1, or -1 if that player is not found */ int GetIndexFromPlayerID( PlayerID playerId ); /** * This function is only useful for looping through all players. * * @param index an integer between 0 and the maximum number of players allowed - 1. * * @return A valid playerID or UNASSIGNED_PLAYER_ID if no such player at that index */ PlayerID GetPlayerIDFromIndex( int index ); /** * Bans an IP from connecting. Banned IPs persist between connections. * * @param IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 * @param milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban */ void AddToBanList( const char *IP, unsigned int milliseconds=0 ); /** * Allows a previously banned IP to connect. * * @param IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will unban * All IP addresses starting with 128.0.0 */ void RemoveFromBanList( const char *IP ); /** * Allows all previously banned IPs to connect. */ void ClearBanList( void ); /** * Determines if a particular IP is banned. * * @param IP Complete dotted IP address * * @return * - True if IP matches any IPs in the ban list, accounting for any wildcards. * - False otherwise. */ bool IsBanned( const char *IP ); /* * -------------------------------------------------------------------------------------------- * Pinging Functions - Functions dealing with the automatic ping mechanism * -------------------------------------------------------------------------------------------- */ /** * Send a ping to the specified connected system. * * Requires: * The sender and recipient must already be started via a successful call to Initialize * * @param target who to ping */ void Ping( PlayerID target ); /** * Send a ping to the specified unconnected system. * The remote system, if it is Initialized, will respond with ID_PONG. * The final ping time will be encoded in the following 4 bytes (2-5) as an unsigned int * * Requires: * The sender and recipient must already be started via a successful call to Initialize * * * @param host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. * @param remotePort Which port to connect to on the remote machine. * @param onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections */ void Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections ); /** * Gets the average of all ping times read for a specified target * * @param target whose time to read * @return The average of all ping times read for a specified target. */ int GetAveragePing( PlayerID playerId ); /** * Gets the last ping time read for the specific player or -1 if none read yet * * @param target whose time to read * @return Just the last ping */ int GetLastPing( PlayerID playerId ) const; /** * Gets the lowest ping time read or -1 if none read yet * * @param target whose time to read * @return the lowest ping time */ int GetLowestPing( PlayerID playerId ) const; /** * Ping the remote systems every so often. This is off by default * This will work anytime * * @param doPing True to start occasional pings. False to stop them. */ void SetOccasionalPing( bool doPing ); /* * -------------------------------------------------------------------------------------------- * Static Data Functions - Functions dealing with API defined synchronized memory * -------------------------------------------------------------------------------------------- */ /** * All systems have a block of data associated with them, for user use. This block of data can be used to easily * specify typical system data that you want to know on connection, such as the player's name. * * @param playerId Which system you are referring to. Pass the value returned by GetInternalID to refer to yourself * * @return The data passed to SetRemoteStaticData stored as a bitstream */ RakNet::BitStream * GetRemoteStaticData( PlayerID playerId ); /** * All systems have a block of data associated with them, for user use. This block of data can be used to easily * specify typical system data that you want to know on connection, such as the player's name. * * @param playerId Whose static data to change. Use your own playerId to change your own static data * @param data a block of data to store * @param length The length of data in bytes */ void SetRemoteStaticData( PlayerID playerId, const char *data, const long length ); /** * Sends your static data to the specified system. This is automatically done on connection. * You should call this when you change your static data. * To send the static data of another system (such as relaying their data) you should do this normally with Send * * @param target Who to send your static data to. Specify UNASSIGNED_PLAYER_ID to broadcast to all */ void SendStaticData( PlayerID target ); /** * Sets the data to send with an (LAN server discovery) /(offline ping) response * Length should be under 400 bytes, as a security measure against flood attacks * See the Ping sample project for how this is used. * @param data a block of data to store, or 0 for none * @param length The length of data in bytes, or 0 for none */ void SetOfflinePingResponse( const char *data, const unsigned int length ); /* * -------------------------------------------------------------------------------------------- * Network Functions - Functions dealing with the network in general * -------------------------------------------------------------------------------------------- */ /** * Return the unique address identifier that represents you on the the network and is based on your local IP / port * Note that unlike in previous versions, this is a struct and is not sequential */ PlayerID GetInternalID( void ) const; /** * Return the unique address identifier that represents you on the the network and is based on your external * IP / port (the IP / port the specified player uses to communicate with you) * @note that unlike in previous versions, this is a struct and is not sequential * * @param target Which remote system you are referring to for your external ID */ PlayerID GetExternalID( PlayerID target ) const; /** * Change the MTU size in order to improve performance when sending large packets * This can only be called when not connected. * A too high of value will cause packets not to arrive at worst and be fragmented at best. * A too low of value will split packets unnecessarily. * * Parameters: * @param size: Set according to the following table: * - 1500. The largest Ethernet packet size; it is also the default value. * This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. * - 1492. The size PPPoE prefers. * - 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) * - 1468. The size DHCP prefers. * - 1460. Usable by AOL if you don't have large email attachments, etc. * - 1430. The size VPN and PPTP prefer. * - 1400. Maximum size for AOL DSL. * - 576. Typical value to connect to dial-up ISPs. (Default) * * @return False on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE */ bool SetMTUSize( int size ); /** * Returns the current MTU size * * @return The MTU sized specified in SetMTUSize */ int GetMTUSize( void ) const; /** * Returns the number of IP addresses we have */ unsigned GetNumberOfAddresses( void ); /** * Returns the dotted IP address for the specified playerId * * @param playerId Any player ID other than UNASSIGNED_PLAYER_ID, even if that player is not currently connected */ const char* PlayerIDToDottedIP( PlayerID playerId ) const; /** * Converts a dotted IP to a playerId * * @param[in] host Either a dotted IP address or a domain name * @param[in] remotePort Which port to connect to on the remote machine. * @param[out] playerId The result of this operation */ void IPToPlayerID( const char* host, unsigned short remotePort, PlayerID *playerId ); /** * Returns an IP address at index 0 to GetNumberOfAddresses-1 */ const char* GetLocalIP( unsigned int index ); /** * Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary * when connection to servers with multiple IP addresses. * * @param allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections */ void AllowConnectionResponseIPMigration( bool allow ); /** * Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. * This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through * * Requires: * The sender and recipient must already be started via a successful call to Initialize * * @param host Either a dotted IP address or a domain name * @param remotePort Which port to connect to on the remote machine. * @param data Optional data to append to the packet. * @param dataLength length of data in bytes. Use 0 if no data. */ void AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ); /* * -------------------------------------------------------------------------------------------- * Compression Functions - Functions related to the compression layer * -------------------------------------------------------------------------------------------- */ /** * Enables or disables our tracking of bytes input to and output from the network. * This is required to get a frequency table, which is used to generate a new compression layer. * You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only track * part of the values sent over the network. * This value persists between connect calls and defaults to false (no frequency tracking) * * @param doCompile - true to track bytes. Defaults to false */ void SetCompileFrequencyTable( bool doCompile ); /** * Returns the frequency of outgoing bytes into outputFrequencyTable * The purpose is to save to file as either a master frequency table from a sample game session for passing to * GenerateCompressionLayer(false); * You should only call this when disconnected. * Requires that you first enable data frequency tracking by calling SetCompileFrequencyTable(true) * * @param[out] outputFrequencyTable The frequency of each corresponding byte * * @return False (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success) */ bool GetOutgoingFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ); /** * Generates the compression layer from the input frequency table. * You should call this twice - once with inputLayer as true and once as false. * The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false. * Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true * Calling this function when there is an existing layer will overwrite the old layer * You should only call this when disconnected * * @param inputFrequencyTable The frequency table returned from GetSendFrequencyTable(...); * @param inputLayer Whether inputFrequencyTable represents incoming data from other systems (true) or outgoing data from this system (false) * * @return False on failure (we are connected). True otherwise */ bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ); /** * Deletes the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory * You should only call this when disconnected * * @param inputLayer Specifies the corresponding compression layer generated by GenerateCompressionLayer. * * @return False on failure (we are connected). True otherwise */ bool DeleteCompressionLayer( bool inputLayer ); /** * Get the compression ratio. A low compression ratio is good. Compression is for outgoing data * @return The compression ratio. */ float GetCompressionRatio( void ) const; /** * Get the decompression ratio. A high decompression ratio is good. Decompression is for incoming data * @return The decompression ratio. */ float GetDecompressionRatio( void ) const; /* * -------------------------------------------------------------------------------------------- * Message Handler Functions * -------------------------------------------------------------------------------------------- */ /** * Attatches a message handler interface to run code automatically on message receipt in the Receive call * * @param messageHandler Pointer to a message handler to attach */ void AttachMessageHandler( MessageHandlerInterface *messageHandler ); /** * Detatches a message handler interface to run code automatically on message receipt * * @param messageHandler Pointer to a message handler to detatch */ void DetachMessageHandler( MessageHandlerInterface *messageHandler ); /* * -------------------------------------------------------------------------------------------- * Micellaneous Functions * -------------------------------------------------------------------------------------------- */ /** * Retrieves the data you passed to the passwordData parameter in Connect * * @param[out] passwordData Should point to a block large enough to hold the password data you passed to Connect * @param passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written */ void GetPasswordData( char *passwordData, int *passwordDataLength ); /** * Put a packet back at the end of the receive queue in case you don't want to deal with it immediately * * @param packet The packet you want to push back. */ void PushBackPacket( Packet *packet ); /* * -------------------------------------------------------------------------------------------- * Statistical Functions - Functions dealing with API performance * -------------------------------------------------------------------------------------------- */ /** * Returns a structure containing a large set of network statistics for the specified system * You can map this data to a string using the C style StatisticsToString function * * @param playerId Which connected system to get statistics for * * @return 0 on can't find the specified system. A pointer to a set of data otherwise. */ RakNetStatisticsStruct * const GetStatistics( PlayerID playerId ); /** * @brief Used to unify time * * * This structure agregate the ping time and the clock differential. * both are used to synchronized time between peers */ struct PingAndClockDifferential { /** * ping time */ short pingTime; /** * clock differential */ unsigned int clockDifferential; }; /** * @brief Store Remote System Description. * * RakPeer need to maintain a set of information concerning all remote peer * This is the goal of this structure. */ struct RemoteSystemStruct { PlayerID playerId; /**< The remote system associated with this reliability layer*/ PlayerID myExternalPlayerId; /**< Your own IP, as reported by the remote system*/ ReliabilityLayer reliabilityLayer; /**< The reliability layer associated with this player*/ bool weInitiatedTheConnection; /**< True if we started this connection via Connect. False if someone else connected to us.*/ PingAndClockDifferential pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE ]; /**< last x ping times and calculated clock differentials with it*/ int pingAndClockDifferentialWriteIndex; /**< The index we are writing into the pingAndClockDifferential circular buffer*/ int lowestPing; /** requestedConnectionsList; /** * Data that both the client and the server needs */ unsigned int bytesSentPerSecond, bytesReceivedPerSecond; // bool isSocketLayerBlocking; // bool continualPing,isRecvfromThreadActive,isMainLoopThreadActive, endThreads, isSocketLayerBlocking; unsigned int validationInteger; #ifdef _WIN32 HANDLE #else pthread_t #endif processPacketsThreadHandle, recvfromThreadHandle; SimpleMutex incomingQueueMutex, banListMutex; //,synchronizedMemoryQueueMutex, automaticVariableSynchronizationMutex; BlobNet::ADT::Queue incomingPacketQueue; //, synchronizedMemoryPacketQueue; // BitStream enumerationData; struct BanStruct { char IP[16]; unsigned int timeout; // 0 for none }; struct RequestedConnectionStruct { PlayerID playerId; unsigned int nextRequestTime; unsigned char requestsMade; char *data; unsigned short dataLength; enum {CONNECT=1, PING=2, PING_OPEN_CONNECTIONS=4, ADVERTISE_SYSTEM=8} actionToTake; }; BasicDataStructures::List banList; BasicDataStructures::List messageHandlerList; BasicDataStructures::SingleProducerConsumer requestedConnectionList; /* * Compression stuff */ unsigned int frequencyTable[ 256 ]; HuffmanEncodingTree *inputTree, *outputTree; unsigned int rawBytesSent, rawBytesReceived, compressedBytesSent, compressedBytesReceived; // void DecompressInput(RakNet::BitStream *bitStream); // void UpdateOutgoingFrequencyTable(RakNet::BitStream * bitStream); void GenerateSYNCookieRandomNumber( void ); void SecuredConnectionResponse( PlayerID playerId ); void SecuredConnectionConfirmation( RakPeer::RemoteSystemStruct * remoteSystem, char* data ); bool RunUpdateCycle( void ); // void RunMutexedUpdateCycle(void); struct BufferedCommandStruct { char *data; int numberOfBitsToSend; PacketPriority priority; PacketReliability reliability; char orderingChannel; PlayerID playerId; bool broadcast; RemoteSystemStruct::ConnectMode connectionMode; ObjectID objectID; enum {BCS_SEND, BCS_CLOSE_CONNECTION, BCS_DO_NOTHING} command; }; // Single producer single consumer queue using a linked list BasicDataStructures::SingleProducerConsumer bufferedCommands; bool AllowIncomingConnections(void) const; // Sends static data using immediate send mode or not (if called from user thread, put false for performImmediate. If called from update thread, put true). // This is done for efficiency, so we don't buffer send calls that are from the network thread anyway void SendStaticDataInternal( PlayerID target, bool performImmediate ); void PingInternal( PlayerID target, bool performImmediate ); bool ValidSendTarget(PlayerID playerId, bool broadcast); // This stores the user send calls to be handled by the update thread. This way we don't have thread contention over playerIDs void CloseConnectionInternalBuffered( PlayerID target, bool sendDisconnectionNotification ); void CloseConnectionInternalImmediate( PlayerID target ); void SendBuffered( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode ); bool SendImmediate( char *data, int numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast, bool useCallerDataAllocation, unsigned int currentTime ); void ClearBufferedCommands(void); void ClearRequestedConnectionList(void); int MTUSize; bool trackFrequencyTable; int threadSleepTimer; SOCKET connectionSocket; // Histogram statistics //unsigned int nextReadBytesTime; //int lastSentBytes,lastReceivedBytes; /* * Encryption and security */ big::RSACrypt rsacrypt; big::u32 publicKeyE; RSA_BIT_SIZE publicKeyN; bool keysLocallyGenerated, usingSecurity; unsigned int randomNumberExpirationTime; unsigned char newRandomNumber[ 20 ], oldRandomNumber[ 20 ]; /** * How long it has been since things were updated by a call to receive * Update thread uses this to determine how long to sleep for */ unsigned int lastUserUpdateCycle; /* * True to allow connection accepted packets from anyone. False to only allow these packets from servers * we requested a connection to. */ bool allowConnectionResponseIPMigration; PacketPool packetPool; }; #endif blobby-1.0rc3/src/raknet/BitStream.h0000644000175000017500000004376112042452367020701 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief RakNet::BitStream: packet encoding and decoding * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __BITSTREAM_H #define __BITSTREAM_H // Arbitrary size, just picking something likely to be larger than most packets #define BITSTREAM_STACK_ALLOCATION_SIZE 256 /** * @brief This namespace only contains a few utility class used in * RakNet. * * RakNet namespace is the not really in use currently. */ /** \note If you want the default network byte stream to be in Network Byte Order (Big Endian) then #define __BITSTREAM_BIG_END otherwise the default is 'Little Endian'. If your CPU has the same Byte Order as your network stream, you can cut out some overheads using #define __BITSTREAM_NATIVE_END --- if this is defined, the __BITSTREAM_BIG_END flag becomes ineffective. */ namespace RakNet { /** * This macro transform a bit in byte * @param x Transform a bit to a byte */ #define BITS_TO_BYTES(x) (((x)+7)>>3) /** * @brief Packets encoding and decoding facilities * * Helper class to encode and decode packets. * */ class BitStream { public: /** * Default Constructor */ BitStream(); /** * Preallocate some memory for the construction of the packet * @param initialBytesToAllocate the amount of byte to pre-allocate. */ BitStream( int initialBytesToAllocate ); /** * Initialize the BitStream object using data from the network. * Set _copyData to true if you want to make an internal copy of * the data you are passing. You can then Write and do all other * operations Set it to false if you want to just use a pointer to * the data you are passing, in order to save memory and speed. * You should only then do read operations. * @param _data An array of bytes. * @param lengthInBytes Size of the @em _data. * @param _copyData Does a copy of the input data. */ BitStream( char* _data, unsigned int lengthInBytes, bool _copyData ); /** * Destructor */ ~BitStream(); /** * Reset the bitstream for reuse */ void Reset( void ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const bool input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const unsigned char input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const char input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const unsigned short input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const short input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const unsigned int input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const int input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const float input ); /** * Write the native types to the end of the buffer * without any compression mecanism. * @param input The data */ void Write( const double input ); /** * Write an array or casted stream. It is supossed to * be raw data. It is also not possible to deal with endian problem * @param input a byte buffer * @param numberOfBytes the size of the byte buffer */ void Write( const char* input, const int numberOfBytes ); /** * Write the native types with simple compression. * Best used with negatives and positives close to 0 * @param input The data. */ void WriteCompressed( const unsigned char input ); /** * Write the native types with simple compression. * Best used with negatives and positives close to 0 * @param input The data. */ void WriteCompressed( const char input ); /** * Write the native types with simple compression. * Best used with negatives and positives close to 0 * @param input The data. */ void WriteCompressed( const unsigned short input ); /** * Write the native types with simple compression. * Best used with negatives and positives close to 0 * @param input The data. */ void WriteCompressed( const short input ); /** * Write the native types with simple compression. * Best used with negatives and positives close to 0 * @param input The data. */ void WriteCompressed( const unsigned int input ); /** * Write the native types with simple compression. * Best used with negatives and positives close to 0 * @param input The data. */ void WriteCompressed( const int input ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( bool &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( unsigned char &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( char &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( unsigned short &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( short &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( unsigned int &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( int &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( float &output ); /** * Read the native types from the front of the buffer * @param output The readed value. * @return true on success false otherwise. The result of a reading * can only be wrong in the case we reach the end of the BitStream * with some missing bits. */ bool Read( double &output ); /** * Read an array or casted stream of byte. The array * is raw data. There is no automatic conversion on * big endian arch * @param output The result byte array. It should be larger than @em numberOfBytes. * @param numberOfBytes The number of byte to read * @return true on success false if there is some missing bytes. */ bool Read( char* output, const int numberOfBytes ); /** * Read the types you wrote with WriteCompressed * @param output The read value * @return true on success, false on not enough data to read */ bool ReadCompressed( unsigned char & output ); /** * Read the types you wrote with WriteCompressed * @param output The read value * @return true on success, false on not enough data to read */ bool ReadCompressed( char &output ); /** * Read the types you wrote with WriteCompressed * @param output The read value * @return true on success, false on not enough data to read */ bool ReadCompressed( unsigned short &output ); /** * Read the types you wrote with WriteCompressed * @param output The read value * @return true on success, false on not enough data to read */ bool ReadCompressed( short &output ); /** * Read the types you wrote with WriteCompressed * @param output The read value * @return true on success, false on not enough data to read */ bool ReadCompressed( unsigned int &output ); /** * Read the types you wrote with WriteCompressed * @param output The read value * @return true on success, false on not enough data to read */ bool ReadCompressed( int &output ); /** * Sets the read pointer back to the beginning of your data. */ void ResetReadPointer( void ); /** * Sets the write pointer back to the beginning of your data. */ void ResetWritePointer( void ); /** * This is good to call when you are done with the stream to make * sure you didn't leave any data left over void */ void AssertStreamEmpty( void ); /** * print to the standard output the state of the stream bit by bit */ void PrintBits( void ) const; /** * Ignore data we don't intend to read * @param numberOfBits The number of bits to ignore */ void IgnoreBits( const int numberOfBits ); /** * Ignore data we don't intend to read * @param numberOfBytes The number of bytes to ignore */ void IgnoreBytes( const int numberOfBytes ); /** * Move the write pointer to a position on the array. * @param offset the offset from the start of the array. * @attention * Dangerous if you don't know what you are doing! * */ void SetWriteOffset( const int offset ); /** * Returns the length in bits of the stream */ int GetNumberOfBitsUsed( void ) const; /** * Returns the length in bytes of the stream */ int GetNumberOfBytesUsed( void ) const; /** * Returns the number of bits into the stream that we have read */ int GetReadOffset( void ) const; /** * Returns the number of bits left in the stream that haven't been read */ int GetNumberOfUnreadBits( void ) const; /** * Makes a copy of the internal data for you Data will point to * the stream. Returns the length in bits of the stream. Partial * bytes are left aligned * @param _data the resulting byte copy of the internal state. */ int CopyData( unsigned char** _data ) const; /** * Set the stream to some initial data. For internal use * Partial bytes are left aligned * @param input The data * @param numberOfBits the number of bits set in the data buffer */ void SetData( const unsigned char* input, const int numberOfBits ); /** * Exposes the internal data. * Partial bytes are left aligned. * @return A pointer to the internal state */ unsigned char* GetData( void ) const; /** * Write numberToWrite bits from the input source Right aligned * data means in the case of a partial byte, the bits are aligned * from the right (bit 0) rather than the left (as in the normal * internal representation) You would set this to true when * writing user data, and false when copying bitstream data, such * as writing one bitstream to another * @param input The data * @param numberOfBitsToWrite The number of bits to write * @param rightAlignedBits if true data will be right aligned */ void WriteBits( const unsigned char* input, int numberOfBitsToWrite, const bool rightAlignedBits = true ); /** * Align the bitstream to the byte boundary and then write the * specified number of bits. This is faster than WriteBits but * wastes the bits to do the alignment and requires you to call * ReadAlignedBits at the corresponding read position. * @param input The data * @param numberOfBytesToWrite The size of data. */ void WriteAlignedBytes( const unsigned char* input, const int numberOfBytesToWrite ); /** * Read bits, starting at the next aligned bits. Note that the * modulus 8 starting offset of the sequence must be the same as * was used with WriteBits. This will be a problem with packet * coalescence unless you byte align the coalesced packets. * @param output The byte array larger than @em numberOfBytesToRead * @param numberOfBytesToRead The number of byte to read from the internal state * @return true if there is enough byte. */ bool ReadAlignedBytes( unsigned char* output, const int numberOfBytesToRead ); /** * Align the next write and/or read to a byte boundary. This can * be used to 'waste' bits to byte align for efficiency reasons It * can also be used to force coalesced bitstreams to start on byte * boundaries so so WriteAlignedBits and ReadAlignedBits both * calculate the same offset when aligning. */ void AlignWriteToByteBoundary( void ); /** * Align the next write and/or read to a byte boundary. This can * be used to 'waste' bits to byte align for efficiency reasons It * can also be used to force coalesced bitstreams to start on byte * boundaries so so WriteAlignedBits and ReadAlignedBits both * calculate the same offset when aligning. */ void AlignReadToByteBoundary( void ); /** * Read numberOfBitsToRead bits to the output source * alignBitsToRight should be set to true to convert internal * bitstream data to userdata It should be false if you used * WriteBits with rightAlignedBits false * @param output The resulting bits array * @param numberOfBitsToRead The number of bits to read * @param alignsBitsToRight if true bits will be right aligned. * @return true if there is enough bits to read */ bool ReadBits( unsigned char* output, int numberOfBitsToRead, const bool alignBitsToRight = true ); /** * --- Low level functions --- * These are for when you want to deal * with bits and don't care about type checking * Write a 0 */ void Write0( void ); /** * --- Low level functions --- * These are for when you want to deal * with bits and don't care about type checking * Write a 1 */ void Write1( void ); /** * --- Low level functions --- * These are for when you want to deal * with bits and don't care about type checking * Reads 1 bit and returns true if that bit is 1 and false if it is 0 */ bool ReadBit( void ); /** * If we used the constructor version with copy data off, this * makes sure it is set to on and the data pointed to is copied. */ void AssertCopyData( void ); /** * Use this if you pass a pointer copy to the constructor * (_copyData==false) and want to overallocate to prevent * reallocation */ void SetNumberOfBitsAllocated( const unsigned int lengthInBits ); private: /** * Assume the input source points to a native type, compress and write it. */ void WriteCompressed( const unsigned char* input, const int size, const bool unsignedData ); /** * Assume the input source points to a compressed native type. * Decompress and read it. */ bool ReadCompressed( unsigned char* output, const int size, const bool unsignedData ); /** * Reallocates (if necessary) in preparation of writing * numberOfBitsToWrite */ void AddBitsAndReallocate( const int numberOfBitsToWrite ); /** * Number of bits currently used */ int numberOfBitsUsed; /** * Number of bits currently allocated */ int numberOfBitsAllocated; /** * Current readOffset */ int readOffset; /** * array of byte storing the data. Points to stackData or if is bigger than that then is allocated */ unsigned char *data; /** * true if the internal buffer is copy of the data passed to the * constructor */ bool copyData; unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; }; } #endif blobby-1.0rc3/src/raknet/PacketPool.cpp0000644000175000017500000000601612042452367021373 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "PacketPool.h" #include PacketPool::PacketPool() { #ifdef _DEBUG packetsReleased = 0; #endif } PacketPool::~PacketPool() { #ifdef _DEBUG // If this assert hits then not all packets given through GetPointer have been returned to ReleasePointer. // Either // 1. You got a packet from Receive and didn't give it back to DeallocatePacket when you were done with it // 2. You didn't call Disconnect before shutdown, and the order of destructor calls happened to hit the PacketPool singleton before it hit the RakPeer class(es). assert( packetsReleased == 0 ); #endif ClearPool(); } void PacketPool::ClearPool( void ) { Packet * p; poolMutex.Lock(); while ( !pool.empty() ) { p = pool.top(); pool.pop(); delete [] p->data; delete p; } poolMutex.Unlock(); } Packet* PacketPool::GetPointer( void ) { Packet * p = 0; poolMutex.Lock(); #ifdef _DEBUG packetsReleased++; #endif if ( !pool.empty() ) { p = pool.top(); pool.pop(); } poolMutex.Unlock(); if ( p ) return p; p = new Packet; p->data = 0; return p; } void PacketPool::ReleasePointer( Packet *p ) { if ( p == 0 ) { // Releasing a null pointer? #ifdef _DEBUG assert( 0 ); #endif return ; } delete [] p->data; p->data = 0; poolMutex.Lock(); pool.push( p ); #ifdef _DEBUG // assert( packetsReleased > 0 ); packetsReleased--; #endif poolMutex.Unlock(); } blobby-1.0rc3/src/raknet/HuffmanEncodingTreeFactory.cpp0000644000175000017500000000573712042452367024546 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file HuffmanEncodingTreeFactory.cpp * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "HuffmanEncodingTreeFactory.h" #include "HuffmanEncodingTreeNode.h" #include "HuffmanEncodingTree.h" #include HuffmanEncodingTreeFactory::HuffmanEncodingTreeFactory() { Reset(); } // Reset the frequency table. You don't need to call this unless you want to reuse the class for a new tree void HuffmanEncodingTreeFactory::Reset( void ) { for ( int counter = 0; counter < 256; counter++ ) frequency[ counter ] = 0; } // Pass an array of bytes to this to add those elements to the frequency table void HuffmanEncodingTreeFactory::AddToFrequencyTable( unsigned char *array, int size ) { while ( size-- > 0 ) frequency[ array[ size ] ] ++; } // Copies the frequency table to the array passed void HuffmanEncodingTreeFactory::GetFrequencyTable( unsigned int _frequency[ 256 ] ) { memcpy( _frequency, frequency, sizeof( unsigned int ) * 256 ); } unsigned int * HuffmanEncodingTreeFactory::GetFrequencyTable( void ) { return frequency; } // Generate a HuffmanEncodingTree. // You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself HuffmanEncodingTree * HuffmanEncodingTreeFactory::GenerateTree( void ) { HuffmanEncodingTree * huffmanEncodingTree = new HuffmanEncodingTree; huffmanEncodingTree->GenerateFromFrequencyTable( frequency ); return huffmanEncodingTree; } blobby-1.0rc3/src/raknet/GetTime.h0000644000175000017500000000356612042452367020344 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file GetTime.h * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __GET_TIME_H #define __GET_TIME_H namespace RakNet { /** * This function return the current time * @return The number of seconds from 1970 january the 1st. */ unsigned int GetTime( void ); } #endif blobby-1.0rc3/src/raknet/RakNetStatistics.h0000644000175000017500000001263512042452367022242 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Store Statistics concerning Network Usage. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_NET_STATISTICS_H #define __RAK_NET_STATISTICS_H #include "PacketPriority.h" /** * @brief Network Statisics Usage * * Store Statistics information related to network usage */ struct RakNetStatisticsStruct { //! Number of Messages in the send Buffer (high, medium, low priority) unsigned messageSendBuffer[ NUMBER_OF_PRIORITIES ]; //! Number of messages sent (high, medium, low priority) unsigned messagesSent[ NUMBER_OF_PRIORITIES ]; //! Number of data bits used for user messages unsigned messageDataBitsSent[ NUMBER_OF_PRIORITIES ]; //! Number of total bits used for user messages, including headers unsigned messageTotalBitsSent[ NUMBER_OF_PRIORITIES ]; //! Number of packets sent containing only acknowledgements unsigned packetsContainingOnlyAcknowlegements; //! Number of acknowledgements sent unsigned acknowlegementsSent; //! Number of acknowledgements waiting to be sent unsigned acknowlegementsPending; //! Number of acknowledgements bits sent unsigned acknowlegementBitsSent; //! Number of packets containing only acknowledgements and resends unsigned packetsContainingOnlyAcknowlegementsAndResends; //! Number of messages resent unsigned messageResends; //! Number of bits resent of actual data unsigned messageDataBitsResent; //! Total number of bits resent, including headers unsigned messagesTotalBitsResent; //! Number of messages waiting for ack unsigned messagesOnResendQueue; //! Number of messages not split for sending unsigned numberOfUnsplitMessages; //! Number of messages split for sending unsigned numberOfSplitMessages; //! Total number of splits done for sending unsigned totalSplits; //! Total packets sent unsigned packetsSent; //! Number of bits added by encryption unsigned encryptionBitsSent; //! total bits sent unsigned totalBitsSent; //! Number of sequenced messages arrived out of order unsigned sequencedMessagesOutOfOrder; //! Number of sequenced messages arrived in order unsigned sequencedMessagesInOrder; //! Number of ordered messages arrived out of order unsigned orderedMessagesOutOfOrder; //! Number of ordered messages arrived in order unsigned orderedMessagesInOrder; //! Packets with a good CRC received unsigned packetsReceived; //! Packets with a bad CRC received unsigned packetsWithBadCRCReceived; //! Bits with a good CRC received unsigned bitsReceived; //! Bits with a bad CRC received unsigned bitsWithBadCRCReceived; //! Number of acknowledgement messages received for packets we are resending unsigned acknowlegementsReceived; //! Number of acknowledgement messages received for packets we are not resending unsigned duplicateAcknowlegementsReceived; //! Number of data messages (anything other than an ack) received that are valid and not duplicate unsigned messagesReceived; //! Number of data messages (anything other than an ack) received that are invalid unsigned invalidMessagesReceived; //! Number of data messages (anything other than an ack) received that are duplicate unsigned duplicateMessagesReceived; //! Number of messages waiting for reassembly unsigned messagesWaitingForReassembly; //! Number of messages in reliability output queue unsigned internalOutputQueueSize; //! Current window size unsigned windowSize; //! lossy window size unsigned lossySize; //! connection start time unsigned int connectionStartTime; }; /** * Verbosity level currently supports 0 (low), 1 (medium), 2 (high) * @param s The Statistical information to format out * @param buffer The buffer containing a formated report * @param verbosityLevel * - 0 low * - 1 medium * - 2 high */ void StatisticsToString( RakNetStatisticsStruct *s, char *buffer, int verbosityLevel ); #endif blobby-1.0rc3/src/raknet/MTUSize.h0000644000175000017500000000467412042452367020307 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Provide some consts concerning the size of network packet. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef DEFAULT_MTU_SIZE /** * 1500. The largest Ethernet packet size; it is also the default * value. This is the typical setting for non-PPPoE, non-VPN * connections. The default value for NETGEAR routers, adapters and * switches. 1492. The size PPPoE prefers. 1472. Maximum size to use * for pinging. (Bigger packets are fragmented.) 1468. The size DHCP * prefers. 1460. Usable by AOL if you don't have large email * attachments, etc. 1430. The size VPN and PPTP prefer. 1400. Maximum * size for AOL DSL. 576. Typical value to connect to dial-up ISPs. */ #define DEFAULT_MTU_SIZE 576 /** * This is the largest value for an UDP packet Fixe DEFAULT_MTU_SIZE * to a lower value. */ #define MAXIMUM_MTU_SIZE 8000 #endif blobby-1.0rc3/src/raknet/PacketEnumerations.h0000644000175000017500000002577512042452367022615 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @ingroup RALNET_MESSAGE_ID * @file * @brief Define RakNet internal packet identifier * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __PACKET_ENUMERATIONS_H #define __PACKET_ENUMERATIONS_H /** * @defgroup RAKNET_MESSAGE_ID Message Identifier * * You should not edit the file PacketEnumerations.h as it is a part of RakNet static library * To define your own message id, define an enum following the code example that follows. * * @code * enum { * ID_MYPROJECT_MSG_1 = ID_RESERVED_9 + 1, * ID_MYPROJECT_MSG_2, * ... * }; * @endcode * * @note RakNet define message ID as a 1 byte information. If you * require more than 256 message IDs do not reuse the function * Multiplayer::GetPacketIdentifier and replace the existing one by a * correct version of this function the version. In this case you can * integrate in the code of your project the file Multiplayer.h and * edit this file to fits your needs. */ enum { // // RESERVED TYPES - DO NOT CHANGE THESE // // Ignore these: ID_CONNECTED_PING, //!< 0: Ping from a connected system. Update timestamps (internal use only) ID_UNCONNECTED_PING, //!< 1: Ping from an unconnected system. Reply but do not update timestamps. (internal use only) ID_UNCONNECTED_PING_OPEN_CONNECTIONS, //!< 2: Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only) ID_CONNECTED_PONG, //!< 3: Pong from a connected system. Update timestamps (internal use only) ID_REQUEST_STATIC_DATA, //!< 4: Someone asked for our static data (internal use only) ID_CONNECTION_REQUEST, //!< 5: Asking for a new connection (internal use only) ID_SECURED_CONNECTION_RESPONSE, //!< 6: Connecting to a secured server/peer ID_SECURED_CONNECTION_CONFIRMATION, //!< 7: Connecting to a secured server/peer ID_RPC, //!< 8: Remote procedure call (internal use only) ID_BROADCAST_PINGS, //!< 9: Server / Client only - The server is broadcasting the pings of all players in the game (internal use only) ID_SET_RANDOM_NUMBER_SEED, //!< 10: Server / Client only - The server is broadcasting a random number seed (internal use only) ID_RPC_MAPPING, //!< 11: Packet that tells us the packet contains an integer ID to name mapping for the remote system ID_KEEPALIVE, //!< 12: Just a reliable keepalive ID_OPEN_CONNECTION_REQUEST, //!< 13: Guaranteed offline message so we know when to reset and start a new connection ID_OPEN_CONNECTION_REPLY, ///!< 14: Guaranteed offline message response so we know when to reset and start a new connection //Handle these below. Possible recipients in [...] ID_PONG, //!< [CLIENT|PEER] 15: Pong from an unconnected system. First byte is ID_PONG, second 4 bytes is the ping, following bytes is system specific enumeration data. ID_RSA_PUBLIC_KEY_MISMATCH, //!< [CLIENT|PEER] 16: We preset an RSA public key which does not match what the system we connected to is using. ID_REMOTE_DISCONNECTION_NOTIFICATION, //!< [CLIENT] 17: In a client/server environment, a client other than ourselves has disconnected gracefully. Packet::playerID is modified to reflect the playerID of this client. ID_REMOTE_CONNECTION_LOST, //!< [CLIENT] 18: In a client/server environment, a client other than ourselves has been forcefully dropped. Packet::playerID is modified to reflect the playerID of this client. ID_REMOTE_NEW_INCOMING_CONNECTION, //!< [CLIENT] 19: In a client/server environment, a client other than ourselves has connected. Packet::playerID is modified to reflect the playerID of this client. ID_REMOTE_EXISTING_CONNECTION, //!< [CLIENT] 20: On our initial connection to the server, we are told of every other client in the game. Packet::playerID is modified to reflect the playerID of this client. ID_REMOTE_STATIC_DATA, //!< [CLIENT] - 21: Got the data for another client ID_CONNECTION_BANNED, //!< [PEER|CLIENT] 22: We are banned from the system we attempted to connect to. ID_CONNECTION_REQUEST_ACCEPTED, //!< [PEER|CLIENT] 23: In a client/server environment, our connection request to the server has been accepted. ID_NEW_INCOMING_CONNECTION, //!< [PEER|SERVER] 24: A remote system has successfully connected. ID_NO_FREE_INCOMING_CONNECTIONS, //!< [PEER|CLIENT] 25: The system we attempted to connect to is not accepting new connections. ID_DISCONNECTION_NOTIFICATION, //!< [PEER|SERVER|CLIENT] 26: The system specified in Packet::playerID has disconnected from us. For the client, this would mean the server has shutdown. ID_CONNECTION_LOST, //!< [PEER|SERVER|CLIENT] 27: Reliable packets cannot be delivered to the system specifed in Packet::playerID. The connection to that system has been closed. ID_TIMESTAMP, //!< [PEER|SERVER|CLIENT] 28: The four bytes following this byte represent an unsigned int which is automatically modified by the difference in system times between the sender and the recipient. Requires that you call StartOccasionalPing. ID_RECEIVED_STATIC_DATA, //!< [PEER|SERVER|CLIENT] 29: We got a bitstream containing static data. You can now read this data. This packet is transmitted automatically on connections, and can also be manually sent. ID_INVALID_PASSWORD, //!< [PEER|CLIENT] 30: The remote system is using a password and has refused our connection because we did not set the correct password. ID_MODIFIED_PACKET, //!< [PEER|SERVER|CLIENT] 31: A packet has been tampered with in transit. The sender is contained in Packet::playerID. ID_REMOTE_PORT_REFUSED, //!< [PEER|SERVER|CLIENT] 32: [11/14/05 - DEPRECIATED: No longer returned] The remote host is not accepting data on this port. This only comes up when connecting to yourself on the same computer and there is no bound socket on that port. ID_VOICE_PACKET, //!< [PEER] 33: This packet contains voice data. You should pass it to the RakVoice system. ID_UPDATE_DISTRIBUTED_NETWORK_OBJECT, //!< [CLIENT|SERVER] 34: Indicates creation or update of a distributed network object. Pass to DistributedNetworkObjectManager::Instance()->HandleDistributedNetworkObjectPacket ID_DISTRIBUTED_NETWORK_OBJECT_CREATION_ACCEPTED, //!< [CLIENT] 35: Client creation of a distributed network object was accepted. Pass to DistributedNetworkObjectManager::Instance()->HandleDistributedNetworkObjectPacketCreationAccepted ID_DISTRIBUTED_NETWORK_OBJECT_CREATION_REJECTED, //!< [CLIENT] 36: Client creation of a distributed network object was rejected. Pass to DistributedNetworkObjectManager::Instance()->HandleDistributedNetworkObjectPacketCreationRejected ID_AUTOPATCHER_REQUEST_FILE_LIST, //!< [PEER|SERVER|CLIENT] 37: Request for a list of downloadable files. Pass to Autopatcher::SendDownloadableFileList ID_AUTOPATCHER_FILE_LIST, //!< [PEER|SERVER|CLIENT] 38: Got a list of downloadable files. Pass to Autopatcher::OnAutopatcherFileList ID_AUTOPATCHER_REQUEST_FILES, //!< [PEER|SERVER|CLIENT] 39: Request for a particular set of downloadable files. Pass to Autopatcher::OnAutopatcherRequestFiles ID_AUTOPATCHER_SET_DOWNLOAD_LIST, //!< [PEER|SERVER|CLIENT] 40: Set the list of files that were approved for download and are incoming. Pass to Autopatcher::OnAutopatcherSetDownloadList ID_AUTOPATCHER_WRITE_FILE, //!< [PEER|SERVER|CLIENT] 41: Got a file that we requested for download. Pass to Autopatcher::OnAutopatcherWriteFile ID_QUERY_MASTER_SERVER, //!< [MASTERSERVER] 42: Request to the master server for the list of servers that contain at least one of the specified keys ID_MASTER_SERVER_DELIST_SERVER, //!< [MASTERSERVER] 43: Remove a game server from the master server. ID_MASTER_SERVER_UPDATE_SERVER, //!< [MASTERSERVER|MASTERCLIENT] 44: Add or update the information for a server. ID_MASTER_SERVER_SET_SERVER, //!< [MASTERSERVER|MASTERCLIENT] 45: Add or set the information for a server. ID_RELAYED_CONNECTION_NOTIFICATION, //!< [MASTERSERVER|MASTERCLIENT] 46: This message indicates a game client is connecting to a game server, and is relayed through the master server. ID_ADVERTISE_SYSTEM, //!< [PEER|SERVER|CLIENT] 47: Inform a remote system of our IP/Port. ID_FULLY_CONNECTED_MESH_JOIN_RESPONSE, //!< [PEER via MessageHandlerInterface] 48: Used by FullyConnectedMesh packet handler to automatically connect to other peers and form a fully connected mesh topology ID_FULLY_CONNECTED_MESH_JOIN_REQUEST, //!< [PEER] 49: Used by FullyConnectedMesh packet handler to automatically connect to other peers and form a fully connected mesh topology ID_CONNECTION_ATTEMPT_FAILED, //!< [PEER|SERVER|CLIENT] 50: Sent to the player when a connection request cannot be completed due to inability to connect ID_REPLICATOR_DATA_PUSH_OBJECT, ID_REPLICATOR_DATA_SEND_MEMORY, ID_REPLICATOR_DATA_SEND_OBJECT_SCOPE, ID_REPLICATOR_MEMORY_START, ID_REPLICATOR_DATA_STOP, ID_REPLICATOR_OBJECT_CREATION_REQUEST, ID_REPLICATOR_OBJECT_CREATION_REQUEST_RESPONSE, ID_REPLICATOR_STR_MAP_INDEX, ID_RESERVED7, //!< For future versions ID_RESERVED8, //!< For future versions ID_RESERVED9, //!< For future versions //------------------------------------------------------------------------------------------------------------- // // YOUR TYPES HERE! // WARNING - By default it is assumed that the packet identifier is one byte (unsigned char) // In the unlikely event that you need more than 256 types, including the built-in types, then you'll need // to request a special edition with larger identifiers, or change it yourself // ID_MASTER_REGISTER_USER, ID_MASTER_SERVER_REGISTRATION_FAILED }; #endif blobby-1.0rc3/src/raknet/RakServer.h0000644000175000017500000006407212042452367020711 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief A RakServer provide Server facilities * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_SERVER_H #define __RAK_SERVER_H #include "RakPeer.h" #include "RakServerInterface.h" /** * @brief Server Facilities on top of RakPeer * * This class provide an increase level of services on top of * RakPeer. The communication end point can now only accept connexion * and exchange message with peer connected to him * *@see RakClient * */ class RakServer : public RakServerInterface, public RakPeer { public: /** * Constructor */ RakServer(); /** * Destructor */ virtual ~RakServer(); /** * Call this to initiate the server with the number of players you want to be allowed connected at once * @param AllowedPlayers Current maximum number of allowed players is 65535 * @param threadSleepTimer >=0 for how many ms to Sleep each internal update cycle (recommended 30 for low performance, 0 for regular) * @param port is the port you want the server to read and write on * Make sure this port is open for UDP * @param forceHostAddress Can force RakNet to use a particular IP to host on. Pass 0 to automatically pick an IP * @return true on successful initiation, false otherwise */ bool Start( unsigned short AllowedPlayers, int threadSleepTimer, unsigned short port, const char *forceHostAddress=0 ); /** * Must be called while offline * Secures connections though a combination of SHA1, AES128, SYN Cookies, and RSA to prevent * connection spoofing, replay attacks, data eavesdropping, packet tampering, and MitM attacks. * There is a significant amount of processing and a slight amount of bandwidth * overhead for this feature. * * If you accept connections, you must call this or else secure connections will not be enabled * for incoming connections. If the private keys are 0, then a new key will be generated when this function is called * * @see the Encryption sample * @param privateKeyE A pointer to the public keys from the RSACrypt class. * @param privateKeyN A pointer to the public keys from the RSACrypt class. * */ void InitializeSecurity( const char *privateKeyE, const char *privateKeyN ); /** * Must be called while offline * Disables all security. */ void DisableSecurity( void ); /** * Set the password clients have to use to connect to this server. The password persists between connections. * Pass 0 for no password. * You can call this anytime * @param _password The password name. */ void SetPassword( const char *_password ); /** * Returns true if a password was set, false otherwise */ bool HasPassword( void ); /** * Stops the server, stops synchronized data, and resets all internal data. This will drop all players currently connected, however * since the server is stopped packet reliability is not enforced so the Kick network message may not actually * arrive. Those players will disconnect due to timeout. If you want to end the server more gracefully, you * can manually Kick each player first. Does nothing if the server is not running to begin with * * @param blockDuration How long you should wait for all remaining packets to go out, per connected system * If you set it to 0 then the disconnection notifications probably won't arrive */ void Disconnect( unsigned int blockDuration ); /** * This function only works while the server is active (Use the Start function). Returns false on failure, true on success * Send the data stream of length length to whichever playerId you specify. Specify UNASSIGNED_PLAYER_ID for all players connected * If you aren't sure what to specify for priority and reliability, use HIGH_PRIORITY, RELIABLE, 0 for ordering channel * Set broadcast to true to broadcast to all connected clients EXCEPT the one specified in the playerId field. * To broadcast to everyone specify UNASSIGNED_PLAYER_ID for the playerId field. */ bool Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ); /** * This function only works while the server is active (Use the Start function). Returns false on failure, true on success * Send the bitstream to whichever playerId you specify. * You can set the first byte to a packet identifier, however you will need to have TYPE_CHECKING undefined or the internal type checking * will add extra data and make this not work. If you want TYPE_CHECKING on, you will need to use BitStream::WriteBits to avoid the type checking. * This interface will probably change to fix this in future versions. * If you aren't sure what to specify for priority and reliability, use HIGH_PRIORITY and RELIABLE, 0 for ordering channel * Set broadcast to true to broadcast to all connected clients EXCEPT the one specified in the playerId field. * To broadcast to everyone specify UNASSIGNED_PLAYER_ID for the playerId field. */ bool Send( const RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ); /** * Call this to get a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. * Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct * Returns 0 if no packets are waiting to be handled * If the server is not active this will also return 0, as all waiting packets are flushed when the server is Disconnected * This also updates all memory blocks associated with synchronized memory */ Packet* Receive( void ); /** * Kick out the specified player. */ void Kick( PlayerID playerId ); /** * Call this to deallocate a packet returned by Receive when you are done handling it. */ void DeallocatePacket( Packet *packet ); /** * Set how many players are allowed on the server. If more players are currently connected then are allowed then * No more players will be allowed to join until the number of players is less than the number of allowed players * The server must be active for this to have meaning */ void SetAllowedPlayers( unsigned short AllowedPlayers ); /** * Return how many players are allowed to connect. This value was set either from Start or from SetAllowedPlayers * The server must be active for this to have meaning */ unsigned short GetAllowedPlayers( void ) const; /** * Return how many players are currently connected to the server. * The server must be active for this to have meaning */ unsigned short GetConnectedPlayers( void ); /** * Returns a static string pointer containing the IP of the specified connected player. * Also returns the client port * This changes between calls so be sure to copy what is returned if you need to retain it * Useful for creating and maintaining ban lists * The server must be active for this to have meaning * If the specified id does not represent an active player the results are undefined (most likely returns 0) */ void GetPlayerIPFromID( PlayerID playerId, char returnValue[ 22 ], unsigned short *port ); /** * Send a ping request to the specified player */ void PingPlayer( PlayerID playerId ); /** * Returns the average of all ping times read for the specific player or -1 if none read yet */ int GetAveragePing( PlayerID playerId ); /** * Returns the last ping time read for the specific player or -1 if none read yet */ int GetLastPing( PlayerID playerId ); /** * Returns the lowest ping time read or -1 if none read yet */ int GetLowestPing( PlayerID playerId ); /** * Ping all players every so often. This is on by default. In games where you don't care about ping you can call * StopOccasionalPing to save the bandwidth * This will work anytime */ void StartOccasionalPing( void ); /** * Stop pinging players every so often. Players are pinged by default. In games where you don't care about ping * you can call this to save the bandwidth * This will work anytime */ void StopOccasionalPing( void ); /** * Returns true if the server is currently active */ bool IsActive( void ) const; /** * Returns a number automatically synchronized between the server and client which randomly changes every * 9 seconds. The time it changes is accurate to within a few ms and is best used to seed * random number generators that you want to usually return the same output on all systems. Keep in mind this * isn't perfectly accurate as there is always a very small chance the numbers will by out of synch during * changes so you should confine its use to visual effects or functionality that has a backup method to * maintain synchronization. If you don't need this functionality and want to save the bandwidth call * StopSynchronizedRandomInteger after starting the server */ unsigned int GetSynchronizedRandomInteger( void ) const; /** * Start or restart the synchronized random integer. This is on by default. Call StopSynchronizedRandomInteger * to stop it keeping the number in synch */ void StartSynchronizedRandomInteger( void ); /** * Stop the synchronized random integer. Call StartSynchronizedRandomInteger to start it again */ void StopSynchronizedRandomInteger( void ); /* * Call this to automatically synchronize a block of memory. * Unique identifier should be an integer corresponding to the same variable between clients and the server. This integer * should start at 0 and not surpass the range of UniqueIDType. It is recommended you set this from an enum * memoryBlock should point to the data you want to read from or write to with size of size in bytes * isAuthority should be true if all other computers should match their data in memory block to yours. This is triggered by * when the variable changes. So setting it to true on both the server and one client would make it so if the synchronized * variable on that client changed, the server would then relay it to all clients. * In the current implementation, setting isAuthority to true on the server will cause changes to that variable to be broadcast to * all connected clients. * synchronizationRules is an optional function pointer defined by you. It should * return true if the two passed memory blocks are sufficiently different to synchronize them. This is an optimization so * data that changes rapidly, such as per-frame, can be made to not update every frame * The first parameter to synchronizationRules is the new data, the second is the internal copy of the old data * secondaryUniqueIdentifier is optional and used when you have the same unique identifier and is intended for multiple instances of a class * that derives from NetworkObject. * You can call this anytime - however if you call it before the connection is complete initial data will not by synchronized void SynchronizeMemory(UniqueIDType uniqueIdentifier, char *memoryBlock, unsigned short size, bool isAuthority, bool (*synchronizationRules) (char*,char*)=0,ObjectID secondaryUniqueIdentifier=UNASSIGNED_OBJECT_ID); * Call this to stop synchronization of a block of memory previously defined by uniqueIdentifier and secondaryUniqueIdentifier * by the call to SynchronizeMemory * CALL THIS BEFORE SYNCHRONIZED MEMORY IS DEALLOCATED! * It is not necessary to call this before disconnecting, as all synchronized states will be released then. void DesynchronizeMemory(UniqueIDType uniqueIdentifier, ObjectID secondaryUniqueIdentifier=UNASSIGNED_OBJECT_ID); * Call this to Desynchronize all synchronized memory void DesynchronizeAllMemory(void); */ /** * This is an optional function to generate the compression layer from the input frequency table. * You should call this twice - once with inputLayer as true and once as false. * The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false. * Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true * Calling this function when there is an existing layer will overwrite the old layer * You should only call this when disconnected * @return false (failure) if connected. Otherwise true (success) */ bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ); /** * Delete the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory * You should only call this when disconnected * @return false (failure) if connected. Otherwise true (success) */ bool DeleteCompressionLayer( bool inputLayer ); /** * Enables or disables frequency table tracking. This is required to get a frequency table, which is used to generate * A new compression layer. * You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only track * part of the values sent over the network. * This value persists between connect calls and defaults to false (no frequency tracking) * @param b True to enable tracking */ void SetTrackFrequencyTable( bool b ); /** * Returns the frequency of outgoing bytes into outputFrequencyTable * The purpose is to save to file as either a master frequency table from a sample game session for passing to * GenerateCompressionLayer. * You should only call this when disconnected * Requires that you first enable data frequency tracking by calling SetTrackFrequencyTable(true) * @param[out] outputFrequencyTable The Frequency Table used in the compression layer * @return false (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success) */ bool GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ); /** * Returns the compression ratio. A low compression ratio is good. Compression is for outgoing data */ float GetCompressionRatio( void ) const; /** * Returns the decompression ratio. A high decompression ratio is good. Decompression is for incoming data */ float GetDecompressionRatio( void ) const; /** * Attatches a message handler interface to run code automatically on message receipt in the Receive call * * @param messageHandler Pointer to a message handler to attach */ void AttachMessageHandler( MessageHandlerInterface *messageHandler ); /** * Detatches a message handler interface to run code automatically on message receipt * * @param messageHandler Pointer to a message handler to detatch */ void DetachMessageHandler( MessageHandlerInterface *messageHandler ); /** * The server internally maintains a data struct that is automatically sent to clients when the connect. * This is useful to contain data such as the server name or message of the day. Access that struct with this * function. * @note * If you change any data in the struct remote clients won't reflect this change unless you manually update them * Do so by calling SendStaticServerDataToClient(UNASSIGNED_PLAYER_ID) (broadcast to all) * The data is entered as an array and stored and returned as a BitStream. * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters */ RakNet::BitStream * GetStaticServerData( void ); /** * The server internally maintains a data struct that is automatically sent to clients when the connect. * This is useful to contain data such as the server name or message of the day. Access that struct with this * function. * @note * If you change any data in the struct remote clients won't reflect this change unless you manually update them * Do so by calling SendStaticServerDataToClient(UNASSIGNED_PLAYER_ID) (broadcast to all) * The data is entered as an array and stored and returned as a BitStream. * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters */ void SetStaticServerData( const char *data, const long length ); /** * This sets to true or false whether we want to support relaying of static client data to other connected clients. * If set to false it saves some bandwdith, however other clients won't know the static client data and attempting * to read that data will return garbage. Default is true. This also only works for up to 32 players. Games * supporting more than 32 players will have this shut off automatically upon server start and must have it forced * back on with this function if you do indeed want it * This should be called after the server is started in case you want to override when it shuts off at 32 players */ void SetRelayStaticClientData( bool b ); /** * Send the static server data to the specified player. Pass UNASSIGNED_PLAYER_ID to broadcast to all players * The only time you need to call this function is to update clients that are already connected when you change the static * server data by calling GetStaticServerData and directly modifying the object pointed to. Obviously if the * connected clients don't need to know the new data you don't need to update them, so it's up to you * The server must be active for this to have meaning */ void SendStaticServerDataToClient( PlayerID playerId ); /** * Sets the data to send with an (LAN server discovery) /(offline ping) response * Length should be under 400 bytes, as a security measure against flood attacks * @see the Ping sample project for how this is used. * @param data a block of data to store, or 0 for none * @param length The length of data in bytes, or 0 for none */ void SetOfflinePingResponse( const char *data, const unsigned int length ); /** * Returns a pointer to an attached client's character name specified by the playerId * Returns 0 if no such player is connected * Note that you can modify the client data here. Changes won't be reflected on clients unless you force them to * update by calling ChangeStaticClientData * The server must be active for this to have meaning * The data is entered as an array and stored and returned as a BitStream. * Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in * RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the client may change at any time the * data contents and/or its length! * @param playerId The ID of the client * @return Statistical information of this client */ RakNet::BitStream * GetStaticClientData( PlayerID playerId ); /** * Returns a pointer to an attached client's character name specified by the playerId * Returns 0 if no such player is connected * Note that you can modify the client data here. Changes won't be reflected on clients unless you force them to * update by calling ChangeStaticClientData * The server must be active for this to have meaning * The data is entered as an array and stored and returned as a BitStream. * Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in * RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the client may change at any time the * data contents and/or its length! * @param playerId The ID of the client * @param data A buffer containing statistics * @param length The size of the buffer */ void SetStaticClientData( PlayerID playerId, const char *data, const long length ); /** * This function is used to update the information on connected clients when the server effects a change * of static client data * playerChangedId should be the playerId of the player whose data was changed. This is the parameter passed to * GetStaticClientData to get a pointer to that player's information * Note that a client that gets this new information for himself will update the data for his playerID but not his local data (which is the user's copy) * i.e. player 5 would have the data returned by GetStaticClientData(5) changed but his local information returned by * GetStaticClientData(UNASSIGNED_PLAYER_ID) would remain the same as it was previously. * playerToSendToId should be the player you want to update with the new information. This will almost always * be everybody, in which case you should pass UNASSIGNED_PLAYER_ID. * The server must be active for this to have meaning */ void ChangeStaticClientData( PlayerID playerChangedId, PlayerID playerToSendToId ); /** * Internally store the IP address(es) for the server and return how many it has. * This can easily be more than one, for example a system on both a LAN and with a net connection. * The server does not have to be active for this to work */ unsigned int GetNumberOfAddresses( void ); /** * Call this function where 0 <= index < x where x is the value returned by GetNumberOfAddresses * Returns a static string filled with the server IP of the specified index * Strings returned in no particular order. You'll have to check every index see which string you want * Returns 0 on invalid input * The server does not have to be active for this to work */ const char* GetLocalIP( unsigned int index ); /** * Put a packet back at the end of the receive queue in case you don't want to deal with it immediately */ void PushBackPacket( Packet *packet ); /** * Given a playerID, returns an index from 0 to the maximum number of players allowed - 1. */ int GetIndexFromPlayerID( PlayerID playerId ); /** * This function is only useful for looping through all players. * Index should range between 0 and the maximum number of players allowed - 1. */ PlayerID GetPlayerIDFromIndex( int index ); /** * Bans an IP from connecting. Banned IPs persist between connections. * * Parameters * IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 */ void AddToBanList( const char *IP ); /** * Allows a previously banned IP to connect. * * Parameters * IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 */ void RemoveFromBanList( const char *IP ); /** * Allows all previously banned IPs to connect. */ void ClearBanList( void ); /** * Determines if a particular IP is banned. * * Parameters * IP - Complete dotted IP address * * Returns * True if IP matches any IPs in the ban list, accounting for any wildcards. * False otherwise. */ bool IsBanned( const char *IP ); /** * Returns true if that player ID is currently used */ bool IsActivePlayerID( PlayerID playerId ); /** * Change the MTU size in order to improve performance when sending large packets * This can only be called when not connected. * Returns false on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE * A too high of value will cause packets not to arrive at worst and be fragmented at best. * A too low of value will split packets unnecessarily. * Set according to the following table: * 1500. The largest Ethernet packet size; it is also the default value. * This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. * 1492. The size PPPoE prefers. * 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) * 1468. The size DHCP prefers. * 1460. Usable by AOL if you don't have large email attachments, etc. * 1430. The size VPN and PPTP prefer. * 1400. Maximum size for AOL DSL. * 576. Typical value to connect to dial-up ISPs. (Default) */ bool SetMTUSize( int size ); /** * Returns the current MTU size */ int GetMTUSize( void ) const; /** * Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. * This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through * * host: Either a dotted IP address or a domain name * remotePort: Which port to connect to on the remote machine. */ void AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ); /** * Returns a structure containing a large set of network statistics for the specified system * You can map this data to a string using the C style StatisticsToString function * * Parameters * playerId: Which connected system to get statistics for * * Returns: * 0 on can't find the specified system. A pointer to a set of data otherwise. */ RakNetStatisticsStruct * const GetStatistics( PlayerID playerId ); private: /** * Synchronized random integer */ unsigned int seed, nextSeed, nextSeedUpdate, broadcastPingsTime; bool synchronizedRandomInteger, relayStaticClientData; }; #endif blobby-1.0rc3/src/raknet/HuffmanEncodingTree.cpp0000644000175000017500000002501012042452367023200 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file HuffmanEncodingTree.cpp * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "HuffmanEncodingTree.h" #include "../blobnet/adt/Queue.hpp" #include "BitStream.h" #include HuffmanEncodingTree::HuffmanEncodingTree() { root = 0; } HuffmanEncodingTree::~HuffmanEncodingTree() { FreeMemory(); } void HuffmanEncodingTree::FreeMemory( void ) { if ( root == 0 ) return ; // Use an in-order traversal to delete the tree BlobNet::ADT::Queue nodeQueue; HuffmanEncodingTreeNode *node; nodeQueue.push( root ); while ( nodeQueue.size() > 0 ) { node = nodeQueue.pop(); if ( node->left ) nodeQueue.push( node->left ); if ( node->right ) nodeQueue.push( node->right ); delete node; } // Delete the encoding table for ( int i = 0; i < 256; i++ ) delete [] encodingTable[ i ].encoding; root = 0; } ////#include // Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ) { int counter; HuffmanEncodingTreeNode * node; HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier // 1. Make 256 trees each with a weight equal to the frequency of the corresponding character BasicDataStructures::LinkedList huffmanEncodingTreeNodeList; FreeMemory(); for ( counter = 0; counter < 256; counter++ ) { node = new HuffmanEncodingTreeNode; node->left = 0; node->right = 0; node->value = (unsigned char) counter; node->weight = frequencyTable[ counter ]; if ( node->weight == 0 ) node->weight = 1; // 0 weights are illegal leafList[ counter ] = node; // Used later to generate the encryption table InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order. } // 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right // children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes. #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( 1 ) { huffmanEncodingTreeNodeList.beginning(); HuffmanEncodingTreeNode *lesser, *greater; lesser = huffmanEncodingTreeNodeList.pop(); greater = huffmanEncodingTreeNodeList.pop(); node = new HuffmanEncodingTreeNode; node->left = lesser; node->right = greater; node->weight = lesser->weight + greater->weight; lesser->parent = node; // This is done to make generating the encryption table easier greater->parent = node; // This is done to make generating the encryption table easier if ( huffmanEncodingTreeNodeList.size() == 0 ) { // 3. Assign the one remaining node in the list to the root node. root = node; root->parent = 0; break; } // Put the new node back into the list at the correct spot to maintain the sort. Linear search time InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); } bool tempPath[ 256 ]; // Maximum path length is 256 unsigned short tempPathLength; HuffmanEncodingTreeNode *currentNode; RakNet::BitStream bitStream; // Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents. // This can be done more efficiently but this isn't bad and it's way easier to program and debug for ( counter = 0; counter < 256; counter++ ) { // Already done at the end of the loop and before it! tempPathLength = 0; // Set the current node at the leaf currentNode = leafList[ counter ]; do { if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root tempPath[ tempPathLength++ ] = false; else tempPath[ tempPathLength++ ] = true; currentNode = currentNode->parent; } while ( currentNode != root ); // Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf while ( tempPathLength-- > 0 ) { if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want bitStream.Write1(); else bitStream.Write0(); } // Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer encodingTable[ counter ].bitLength = ( unsigned short ) bitStream.CopyData( &encodingTable[ counter ].encoding ); // Reset the bitstream for the next iteration bitStream.Reset(); } } // Pass an array of bytes to array and a preallocated BitStream to receive the output void HuffmanEncodingTree::EncodeArray( unsigned char *input, int sizeInBytes, RakNet::BitStream * output ) { if ( sizeInBytes <= 0 ) return ; int counter; // For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation for ( counter = 0; counter < sizeInBytes; counter++ ) { output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned } // Byte align the output so the unassigned remaining bits don't equate to some actual value if ( output->GetNumberOfBitsUsed() % 8 != 0 ) { // Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned. unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) ); for ( counter = 0; counter < 256; counter++ ) if ( encodingTable[ counter ].bitLength > remainingBits ) { output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned break; } #ifdef _DEBUG assert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits #endif } } int HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, int sizeInBits, int maxCharsToWrite, unsigned char *output ) { HuffmanEncodingTreeNode * currentNode; int outputWriteIndex; outputWriteIndex = 0; currentNode = root; // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root for ( int counter = 0; counter < sizeInBits; counter++ ) { if ( input->ReadBit() == false ) // left! currentNode = currentNode->left; else currentNode = currentNode->right; if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf { if ( outputWriteIndex < maxCharsToWrite ) output[ outputWriteIndex ] = currentNode->value; outputWriteIndex++; currentNode = root; } } return outputWriteIndex; } // Pass an array of encoded bytes to array and a preallocated BitStream to receive the output void HuffmanEncodingTree::DecodeArray( unsigned char *input, int sizeInBits, RakNet::BitStream * output ) { HuffmanEncodingTreeNode * currentNode; if ( sizeInBits <= 0 ) return ; RakNet::BitStream bitStream( BITS_TO_BYTES( sizeInBits ) ); bitStream.SetData( input, sizeInBits ); // Put the data in the BitStream class to make it easier to use currentNode = root; // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root for ( int counter = 0; counter < sizeInBits; counter++ ) { if ( bitStream.ReadBit() == false ) // left! currentNode = currentNode->left; else currentNode = currentNode->right; if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf { output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING currentNode = root; } } } // Insertion sort. Slow but easy to write in this case void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, BasicDataStructures::LinkedList *huffmanEncodingTreeNodeList ) const { if ( huffmanEncodingTreeNodeList->size() == 0 ) { huffmanEncodingTreeNodeList->insert( node ); return ; } huffmanEncodingTreeNodeList->beginning(); unsigned counter = 0; #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( 1 ) { if ( huffmanEncodingTreeNodeList->peek()->weight < node->weight ) ++( *huffmanEncodingTreeNodeList ); else { huffmanEncodingTreeNodeList->insert( node ); break; } // Didn't find a spot in the middle - add to the end if ( ++counter == huffmanEncodingTreeNodeList->size() ) { huffmanEncodingTreeNodeList->end(); huffmanEncodingTreeNodeList->add ( node ) ; // Add to the end break; } } } blobby-1.0rc3/src/raknet/QueueLinkedList.h0000644000175000017500000001106112042452367022042 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * QueueLinkedList ADT - By Kevin Jenkins (http://www.rakkar.org) * Initilize with the following structure * QueueLinkedList * * Has the following member functions * push - adds an element to the top of the queue * pop - returns the bottom element from the queue and removes it. Result undefined if queue empty * peek - returns the bottom element from the queue. Result undefined if queue empty * end_peek - Lets you see the item you most recently added to the queue * size - returns the size of the queue * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases * clear - empties the queue and returns storage * The assignment and copy constructors are defined * * EXAMPLE * QueueLinkedList A; * A.push(5); * A.push(10); * * A.peek(); // Returns 5 * A.pop(); // Returns 5 * A.peek(); // Returns 10 * A.pop(); // Returns 10 * // At this point the queue is empty * NOTES * The default initial allocation size is 1 * This queue uses a linked list to contain elements * */ #ifndef __QUEUE_LINKED_LIST_H #define __QUEUE_LINKED_LIST_H #include "LinkedList.h" ////#include "MemoryManager.h" namespace BasicDataStructures { template class QueueLinkedList { public: QueueLinkedList(); QueueLinkedList( const QueueLinkedList& original_copy ); bool operator= ( const QueueLinkedList& original_copy ); QueueType pop( void ); QueueType& peek( void ); QueueType& end_peek( void ); void push( const QueueType& input ); const unsigned int size( void ); void clear( void ); void compress( void ); private: LinkedList data; }; template QueueLinkedList::QueueLinkedList() { } template inline const unsigned int QueueLinkedList::size() { return data.size(); } template inline QueueType QueueLinkedList::pop( void ) { data.beginning(); return ( QueueType ) data.pop(); } template inline QueueType& QueueLinkedList::peek( void ) { data.beginning(); return ( QueueType ) data.peek(); } template inline QueueType& QueueLinkedList::end_peek( void ) { data.end(); return ( QueueType ) data.peek(); } template void QueueLinkedList::push( const QueueType& input ) { data.end(); data.add( input ); } template QueueLinkedList::QueueLinkedList( const QueueLinkedList& original_copy ) { data = original_copy.data; } template bool QueueLinkedList::operator= ( const QueueLinkedList& original_copy ) { if ( ( &original_copy ) == this ) return false; data = original_copy.data; } template void QueueLinkedList::clear ( void ) { data.clear(); } } // End namespace #endif blobby-1.0rc3/src/raknet/RakNetworkFactory.cpp0000644000175000017500000000531312042452367022750 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Implementation of RAkNetworkFactory class * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "RakNetworkFactory.h" #include "RakServerInterface.h" #include "RakClientInterface.h" #include "RakServer.h" #include "RakClient.h" #include "RakPeerInterface.h" #include "RakPeer.h" // Returns a new instance of the network client. RakClientInterface* RakNetworkFactory::GetRakClientInterface( void ) { return new RakClient; } // Returns a new instance of the network server. RakServerInterface* RakNetworkFactory::GetRakServerInterface( void ) { return new RakServer; } // Returns a new instance of the network peer. RakPeerInterface* RakNetworkFactory::GetRakPeerInterface( void ) { return new RakPeer; } // Destroys an instance of the network client; void RakNetworkFactory::DestroyRakClientInterface( RakClientInterface* i ) { delete ( RakClient* ) i; } // Destroys an instance of the network server; void RakNetworkFactory::DestroyRakServerInterface( RakServerInterface* i ) { delete ( RakServer* ) i; } void RakNetworkFactory::DestroyRakPeerInterface( RakPeerInterface* i ) { delete ( RakPeer* ) i; } blobby-1.0rc3/src/raknet/SimpleMutex.h0000644000175000017500000000416212042452367021253 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief SimpleMutex class implementation * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __SIMPLE_MUTEX_H #define __SIMPLE_MUTEX_H #ifdef _WIN32 #include #else #include #include #endif class SimpleMutex { public: SimpleMutex(); ~SimpleMutex(); void Lock(void); void Unlock(void); private: #ifdef _WIN32 //HANDLE hMutex; CRITICAL_SECTION criticalSection; // Docs say this is faster than a mutex for single process access #else pthread_mutex_t hMutex; #endif }; #endif blobby-1.0rc3/src/raknet/NetworkTypes.h0000644000175000017500000002076312042452367021462 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file NetworkTypes.h * @brief Define Network Common Class and Types. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __NETWORK_TYPES_H #define __NETWORK_TYPES_H // Needed for Serialize/Deserialize functions #include "BitStream.h" /** * Typename for Network Object Identifier */ typedef unsigned short ObjectID; /** * Typename for Unique Id */ typedef unsigned char UniqueIDType; /** * Typename for player index */ typedef unsigned short PlayerIndex; /** * @brief Player Identifier * * This define a Player Unique Identifier. * In fact, it corresponds to the peer address. */ struct PlayerID { /** * The peer address from inet_addr. */ unsigned int binaryAddress; /** * The port number associated to the connexion. */ unsigned short port; /** * Copy operator * @param input a player ID * @return a reference to the current object */ PlayerID& operator = ( const PlayerID& input ) { binaryAddress = input.binaryAddress; port = input.port; return *this; } /** * Test if two Player Unique Identifier are the same * @param left a Player Unique Identifier * @param right a Player Unique Identifier * @return 1 if left and right corresponds to the same player 0 otherwise */ friend int operator==( const PlayerID& left, const PlayerID& right ); /** * Test if two Player Unique Identifier differs * @param left a Player Unique Identifier * @param right a Player Unique Identifier * @return 0 if left and right corresponds to the same player 1 otherwise */ friend int operator!=( const PlayerID& left, const PlayerID& right ); /** * Test if left is greater than right * @param left a Player Unique Identifier * @param right a Player Unique Identifier * @return 1 if left is greater than right, 0 otherwise */ friend int operator > ( const PlayerID& left, const PlayerID& right ); /** * Test if left is lesser than right * @param left a Player Unique Identifier * @param right a Player Unique Identifier * @return 1 if left is lesser than right, 0 otherwise */ friend int operator < ( const PlayerID& left, const PlayerID& right ); }; /// Size of PlayerID data #define PlayerID_Size 6 /** * @brief Connection request handling * * This structure is used internally to store the connection request * @internal */ //struct RequestedConnectionStruct //{ /** * Who we wanted to connect to. */ // PlayerID playerId; /** * When will we requested this connection. */ // unsigned int time; /** * Security key */ // unsigned char AESKey[ 16 ]; /** * true if security policy are enabled */ // bool setAESKey; /** * Next time we will try to connect */ // unsigned int nextRequestTime; /** * How important this request is. This is used two systems try to connect to each other at the same time - so only one request goes through */ // unsigned int priority; //}; /** * @brief Network Packet * * This structure store information concerning * a packet going throught the network */ struct Packet { /** * Server only - this is the index into the player array that this playerId maps to */ PlayerIndex playerIndex; /** * The Player Unique Identifier that send this packet. */ PlayerID playerId; /** * The length of the data. * @deprecated You should use bitSize inplace. * */ unsigned int length; /** * The number of bits in used. * Same as length but represents bits. Length is obsolete and retained for backwards compatibility. */ unsigned int bitSize; /** * The byte array. * The standard behaviour in RakNet define the first byte of the data array as the packet class. * @see PacketEnumerations.h */ unsigned char* data; }; class RakPeerInterface; //#pragma pack(push,1) //#pragma pack(1) /** * @brief Store Accepted Connection * * Handle active connection. * @internal */ /* struct ConnectionAcceptStruct { unsigned char typeId; unsigned short remotePort; PlayerID externalID; PlayerIndex playerIndex; void Serialize(RakNet::BitStream &bstream) { bstream.Write(typeId); bstream.Write(remotePort); bstream.Write(externalID.binaryAddress); bstream.Write(externalID.port); bstream.Write(playerIndex); } void Deserialize(RakNet::BitStream &bstream) { bstream.Read(typeId); bstream.Read(remotePort); bstream.Read(externalID.binaryAddress); bstream.Read(externalID.port); bstream.Read(playerIndex); } }; */ /** Size of ConnectionAcceptStruct data. 1: unsigned char typeId; 2: unsigned short remotePort; PlayerID externalID 4: unsigned int binaryAddress 2: unsigned short port 2: PlayerIndex playerIndex Total: 11 */ //#define ConnectionAcceptStruct_Size 11 /* #pragma pack(1) struct PingStruct { unsigned char typeId; unsigned int sendPingTime; unsigned int sendPongTime; void Serialize(RakNet::BitStream &bstream) { bstream.Write(typeId); bstream.Write(sendPingTime); bstream.Write(sendPongTime); } void Deserialize(RakNet::BitStream &bstream) { bstream.Read(typeId); bstream.Read(sendPingTime); bstream.Read(sendPongTime); } }; */ /** Size of PingStruct data 1: unsigned char typeId; 4: unsigned int sendPingTime; 4: unsigned int sendPongTime; Total: 9 */ //#define PingStruct_Size 9 //#pragma pack(1) /** * @brief Store Unconnected ping informations * * @internal */ /* struct UnconnectedPingStruct { unsigned char typeId; unsigned int sendPingTime; void Serialize(RakNet::BitStream &bstream) { bstream.Write(typeId); bstream.Write(sendPingTime); } void Deserialize(RakNet::BitStream &bstream) { bstream.Read(typeId); bstream.Read(sendPingTime); } }; */ /// Size of UnconnectedPingStruct data //#define UnconnectedPingStruct_Size 5 //#pragma pack(1) /* struct SetRandomNumberSeedStruct { unsigned char ts; unsigned int timeStamp; unsigned char typeId; unsigned int seed; unsigned int nextSeed; void Serialize(RakNet::BitStream &bstream) { bstream.Write(ts); bstream.Write(timeStamp); bstream.Write(typeId); bstream.Write(seed); bstream.Write(nextSeed); } void Deserialize(RakNet::BitStream &bstream) { bstream.Write(ts); bstream.Write(timeStamp); bstream.Write(typeId); bstream.Write(seed); bstream.Write(nextSeed); } }; */ //#define SetRandomNumberSeedStruct_Size 14 //#pragma pack(1) /* struct NewIncomingConnectionStruct { unsigned char typeId; PlayerID externalID; void Serialize(RakNet::BitStream &bstream) { bstream.Write(typeId); bstream.Write(externalID.binaryAddress); bstream.Write(externalID.port); } void Deserialize(RakNet::BitStream &bstream) { bstream.Read(typeId); bstream.Read(externalID.binaryAddress); bstream.Read(externalID.port); } }; */ /* #define NewIncomingConnectionStruct_Size 7 #pragma pack(pop) */ /** * Index of an unassigned player */ const PlayerIndex UNASSIGNED_PLAYER_INDEX = 65535; /** * Index of an invalid Player Unique Id */ const PlayerID UNASSIGNED_PLAYER_ID = { 0xFFFFFFFF, 0xFFFF }; /** * Invalid Object Unique Id */ const ObjectID UNASSIGNED_OBJECT_ID = 65535; /** * Sizeof the Ping Array */ const int PING_TIMES_ARRAY_SIZE = 5; #endif blobby-1.0rc3/src/raknet/OrderedList.h0000644000175000017500000001520712042452367021221 0ustar danielknobedanielknobe/** * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __ORDERED_LIST_H #define __ORDERED_LIST_H #include "ArrayList.h" namespace BasicDataStructures { // comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 // If the data type has comparison operators already defined then you can just use defaultComparison template class OrderedList { public: static int defaultComparison(data_type a, key_type b) {if (b dataList; }; template OrderedList::OrderedList() { } template OrderedList::~OrderedList() { Clear(); } template bool OrderedList::HasData(key_type key, int (*comparisonFunction)(data_type, key_type)) { bool objectExists; unsigned index; index = GetIndexFromKey(key, &objectExists, comparisonFunction); return objectExists; } template data_type OrderedList::GetElementFromKey(key_type key, int (*comparisonFunction)(data_type, key_type)) { bool objectExists; unsigned index; index = GetIndexFromKey(key, &objectExists, comparisonFunction); assert(objectExists); return dataList[index]; } template unsigned OrderedList::GetIndexFromKey(key_type key, bool *objectExists, int (*comparisonFunction)(data_type, key_type)) { int index, upperBound, lowerBound; int res; if (dataList.size()==0) { *objectExists=false; return 0; } upperBound=(int)dataList.size()-1; lowerBound=0; index = (int)dataList.size()/2; #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while (1) { res = comparisonFunction(dataList[index],key); if (res==0) { *objectExists=true; return index; } else if (res<0) { upperBound=index-1; index=(upperBound-lowerBound)/2; } else// if (res>0) { lowerBound=index+1; index=lowerBound+(upperBound-lowerBound)/2; } if (lowerBound>upperBound) { *objectExists=false; return lowerBound; // No match } } } template bool OrderedList::InsertElement(data_type data, int (*comparisonFunction)(data_type, key_type)) { bool objectExists; unsigned index; index = GetIndexFromKey(data, &objectExists, comparisonFunction); // Duplicate insertion! Don't insert elements more than once assert(objectExists==false); if (objectExists) return false; if (index>=dataList.size()) dataList.insert(data); else dataList.insert(data,index); return true; } template bool OrderedList::InsertElement(data_type data, key_type key, int (*comparisonFunction)(data_type, key_type)) { bool objectExists; unsigned index; index = GetIndexFromKey(key, &objectExists, comparisonFunction); // Duplicate insertion! Don't insert elements more than once assert(objectExists==false); if (objectExists) return false; if (index>=dataList.size()) dataList.insert(data); else dataList.insert(data,index); return true; } template void OrderedList::RemoveElement(key_type key, int (*comparisonFunction)(data_type, key_type)) { bool objectExists; unsigned index; index = GetIndexFromKey(key, &objectExists, comparisonFunction); // Can't find the element to remove if this assert hits assert(objectExists==true); if (objectExists==false) return; dataList.del(index); } template void OrderedList::RemoveElementAtIndex(unsigned index) { dataList.del(index); } template void OrderedList::Clear(void) { dataList.clear(); } template data_type& OrderedList::operator[]( unsigned int position ) { return dataList[position]; } template unsigned OrderedList::Size(void) const { return dataList.size(); } } #endif blobby-1.0rc3/src/raknet/SocketLayer.cpp0000644000175000017500000002726712042452367021572 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief SocketLayer class implementation * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "../blobnet/Logger.hpp" #include "SocketLayer.h" #include #include "MTUSize.h" #ifdef _WIN32 #include typedef int socklen_t; #else #include // memcpy #include #endif #include "ExtendedOverlappedPool.h" #ifdef __USE_IO_COMPLETION_PORTS #include "AsynchronousFileIO.h" #endif int SocketLayer::socketLayerInstanceCount = 0; SocketLayer SocketLayer::I; #ifdef _WIN32 extern void __stdcall ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ); #else extern void ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ); #endif #ifdef _WIN32 extern void __stdcall ProcessPortUnreachable( unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ); #else extern void ProcessPortUnreachable( unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ); #endif #ifdef _DEBUG #include #endif /* #ifdef _DEBUG void Error(const char* message) { #ifdef WIN32 DWORD dwIOError = GetLastError(); LPVOID messageBuffer; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language ( LPTSTR ) &messageBuffer, 0, NULL); printf("%s:Error code - %d\n%s", message, dwIOError, messageBuffer); LocalFree(messageBuffer); #else printf("%s", message); #endif } #endif */ SocketLayer::SocketLayer() { // Check if the socketlayer is already started if (socketLayerInstanceCount == 0) { #ifdef _WIN32 WSADATA winsockInfo; // Initiate use of the Winsock DLL (Up to Verion 2.2) if (WSAStartup(MAKEWORD(2, 2), &winsockInfo ) != 0) { LOG("SocketLayer", "WSAStartup failed") } #endif } // increase usecount socketLayerInstanceCount++; } SocketLayer::~SocketLayer() { if (socketLayerInstanceCount == 1) { #ifdef _WIN32 // Terminate use of the Winsock DLL WSACleanup(); #endif } // decrease usecount socketLayerInstanceCount--; } SOCKET SocketLayer::Connect(SOCKET writeSocket, unsigned int binaryAddress, unsigned short port) { assert(writeSocket != INVALID_SOCKET); sockaddr_in connectSocketAddress; connectSocketAddress.sin_family = AF_INET; connectSocketAddress.sin_port = htons( port ); connectSocketAddress.sin_addr.s_addr = binaryAddress; if ( connect( writeSocket, ( struct sockaddr * ) & connectSocketAddress, sizeof( struct sockaddr ) ) != 0 ) { LOG("SocketLayer", "WSAConnect failed") } return writeSocket; } #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter SOCKET SocketLayer::CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress ) { SOCKET listenSocket; sockaddr_in listenerSocketAddress; int ret; #ifdef __USE_IO_COMPLETION_PORTS if ( blockingSocket == false ) listenSocket = WSASocket( AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED ); else #endif listenSocket = socket( AF_INET, SOCK_DGRAM, 0 ); if ( listenSocket == INVALID_SOCKET ) { LOG("SocketLayer", "socket(...) failed") return INVALID_SOCKET; } int sock_opt = 1; if ( setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, ( char * ) & sock_opt, sizeof ( sock_opt ) ) == -1 ) { LOG("SocketLayer", "setsockopt(SO_REUSEADDR) failed") } //Set non-blocking #ifdef _WIN32 unsigned long nonblocking = 1; if ( ioctlsocket( listenSocket, FIONBIO, &nonblocking ) != 0 ) { assert( 0 ); return INVALID_SOCKET; } #else if ( fcntl( listenSocket, F_SETFL, O_NONBLOCK ) != 0 ) { assert( 0 ); return INVALID_SOCKET; } #endif // Set broadcast capable if ( setsockopt( listenSocket, SOL_SOCKET, SO_BROADCAST, ( char * ) & sock_opt, sizeof( sock_opt ) ) == -1 ) { LOG("SocketLayer", "setsockopt(SO_BROADCAST) failed") } // Listen on our designated Port# listenerSocketAddress.sin_port = htons( port ); // Fill in the rest of the address structure listenerSocketAddress.sin_family = AF_INET; if (forceHostAddress) { listenerSocketAddress.sin_addr.s_addr = inet_addr( forceHostAddress ); } else { listenerSocketAddress.sin_addr.s_addr = INADDR_ANY; } // bind our name to the socket ret = bind( listenSocket, ( struct sockaddr * ) & listenerSocketAddress, sizeof( struct sockaddr ) ); if ( ret == SOCKET_ERROR ) { LOG("SocketLayer", "bind(...) failed") return INVALID_SOCKET; } return listenSocket; } const char* SocketLayer::DomainNameToIP( const char *domainName ) { struct hostent * phe = gethostbyname( domainName ); if ( phe == 0 || phe->h_addr_list[ 0 ] == 0 ) { //cerr << "Yow! Bad host lookup." << endl; return 0; } struct in_addr addr; memcpy( &addr, phe->h_addr_list[ 0 ], sizeof( struct in_addr ) ); return inet_ntoa( addr ); } int SocketLayer::Write( SOCKET writeSocket, const char* data, int length ) { #ifdef _DEBUG assert( writeSocket != INVALID_SOCKET ); #endif #ifdef __USE_IO_COMPLETION_PORTS ExtendedOverlappedStruct* eos = ExtendedOverlappedPool::Instance()->GetPointer(); memset( &( eos->overlapped ), 0, sizeof( OVERLAPPED ) ); memcpy( eos->data, data, length ); eos->length = length; //AsynchronousFileIO::Instance()->PostWriteCompletion(ccs); WriteAsynch( ( HANDLE ) writeSocket, eos ); return length; #else return send( writeSocket, data, length, 0 ); #endif } // Start an asynchronous read using the specified socket. #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter bool SocketLayer::AssociateSocketWithCompletionPortAndRead( SOCKET readSocket, unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ) { #ifdef __USE_IO_COMPLETION_PORTS assert( readSocket != INVALID_SOCKET ); ClientContextStruct* ccs = new ClientContextStruct; ccs->handle = ( HANDLE ) readSocket; ExtendedOverlappedStruct* eos = ExtendedOverlappedPool::Instance()->GetPointer(); memset( &( eos->overlapped ), 0, sizeof( OVERLAPPED ) ); eos->binaryAddress = binaryAddress; eos->port = port; eos->rakPeer = rakPeer; eos->length = MAXIMUM_MTU_SIZE; bool b = AsynchronousFileIO::Instance()->AssociateSocketWithCompletionPort( readSocket, ( DWORD ) ccs ); if ( !b ) { ExtendedOverlappedPool::Instance()->ReleasePointer( eos ); delete ccs; return false; } BOOL success = ReadAsynch( ( HANDLE ) readSocket, eos ); if ( success == FALSE ) return false; #endif return true; } int SocketLayer::RecvFrom( SOCKET s, RakPeer *rakPeer, int *errorCode ) { int len; char data[ MAXIMUM_MTU_SIZE ]; sockaddr_in sa; unsigned short portnum; socklen_t len2 = sizeof( struct sockaddr_in ); sa.sin_family = AF_INET; #ifdef _DEBUG portnum = 0; data[ 0 ] = 0; len = 0; sa.sin_addr.s_addr = 0; #endif if ( s == INVALID_SOCKET ) { *errorCode = SOCKET_ERROR; return SOCKET_ERROR; } len = recvfrom( s, data, MAXIMUM_MTU_SIZE, 0, ( sockaddr* ) & sa, ( socklen_t* ) & len2 ); // if (len>0) // printf("Got packet on port %i\n",ntohs(sa.sin_port)); if ( len == 0 ) { LOG("SocketLayer", "Recvfrom returned 0 on a connectionless blocking call\non port %i. This is a bug with Zone Alarm. Please turn off Zone Alarm.\n" << ntohs( sa.sin_port ) ) #ifdef _DEBUG assert( 0 ); #endif *errorCode = SOCKET_ERROR; return SOCKET_ERROR; } if ( len != SOCKET_ERROR ) { portnum = ntohs( sa.sin_port ); //strcpy(ip, inet_ntoa(sa.sin_addr)); //if (strcmp(ip, "0.0.0.0")==0) // strcpy(ip, "127.0.0.1"); ProcessNetworkPacket( sa.sin_addr.s_addr, portnum, data, len, rakPeer ); return 1; } else { *errorCode = 0; #if defined(_WIN32) && defined(_DEBUG) DWORD dwIOError = WSAGetLastError(); if ( dwIOError == WSAEWOULDBLOCK ) { return SOCKET_ERROR; } if ( dwIOError == WSAECONNRESET ) { ProcessPortUnreachable(sa.sin_addr.s_addr, portnum, rakPeer); // *errorCode = dwIOError; return SOCKET_ERROR; } else { if ( dwIOError != WSAEINTR ) { LOG("SocketLayer", "recvfrom failed") } } #endif } return 0; // no data } int SocketLayer::SendTo( SOCKET s, const char *data, int length, unsigned int binaryAddress, unsigned short port ) { if ( s == INVALID_SOCKET ) { return -1; } int len; sockaddr_in sa; sa.sin_port = htons( port ); sa.sin_addr.s_addr = binaryAddress; sa.sin_family = AF_INET; do { len = sendto( s, data, length, 0, ( const sockaddr* ) & sa, sizeof( struct sockaddr_in ) ); } while ( len == 0 ); if ( len != SOCKET_ERROR ) return 0; #if defined(_WIN32) DWORD dwIOError = WSAGetLastError(); if ( dwIOError == WSAECONNRESET ) { // LOG("A previous send operation resulted in an ICMP Port Unreachable message.\n" ) } else { LOG("SocketLayer", "recvfrom failed") } return dwIOError; #else return 1; #endif } int SocketLayer::SendTo( SOCKET s, const char *data, int length, char ip[ 16 ], unsigned short port ) { unsigned int binaryAddress; binaryAddress = inet_addr( ip ); return SendTo( s, data, length, binaryAddress, port ); } void SocketLayer::GetMyIP( char ipList[ 10 ][ 16 ] ) { // Longest possible Hostname char hostname[80]; // Get the hostname of the local maschine if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) { LOG("SocketLayer", "gethostname failed") return ; } LOG("SocketLayer", "Host name is " << hostname ) struct hostent *phe = gethostbyname( hostname ); if ( phe == 0 ) { LOG("SocketLayer", "gethostbyname failed") return ; } for ( int i = 0; phe->h_addr_list[ i ] != 0 && i < 10; ++i ) { struct in_addr addr; memcpy( &addr, phe->h_addr_list[ i ], sizeof( struct in_addr ) ); //cout << "Address " << i << ": " << inet_ntoa(addr) << endl; strcpy( ipList[ i ], inet_ntoa( addr ) ); LOG("SocketLayer", "My IP addresses "<< ipList[ i ]) } } const char* SocketLayer::nameToIP(const char* name) const { struct in_addr address; struct hostent * hostInformation = gethostbyname(name); if (hostInformation == 0 || hostInformation->h_addr_list[0] == 0) { LOG("SocketLayer", "Domainname of ip can't be resolved") return 0; } memcpy(&address, hostInformation->h_addr_list[0], sizeof(struct in_addr)); return inet_ntoa(address); } blobby-1.0rc3/src/raknet/RakPeer.cpp0000644000175000017500000043650012042452367020670 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief RakPeer Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "RakPeer.h" #include "NetworkTypes.h" #ifdef __USE_IO_COMPLETION_PORTS #include "AsynchronousFileIO.h" #endif #ifdef _WIN32 //#include #include #else #define closesocket close #include #include #endif #include // toupper #include "GetTime.h" #include "PacketEnumerations.h" #include "HuffmanEncodingTree.h" #include "PacketPool.h" #include "Rand.h" #include "MessageHandlerInterface.h" // alloca #ifdef _WIN32 #include #else #include #endif // On a Little-endian machine the RSA key and message are mangled, but we're // trying to be friendly to the little endians, so we do byte order // mangling on Big-Endian machines. Note that this mangling is independent // of the byte order used on the network (which also defaults to little-end). #ifdef HOST_ENDIAN_IS_BIG void __inline BSWAPCPY(unsigned char *dest, unsigned char *source, int bytesize) { #ifdef _DEBUG assert( (bytesize % 4 == 0)&&(bytesize)&& "Something is wrong with your exponent or modulus size."); #endif int i; for (i=0; i=0 for how many ms to Sleep each internal update cycle (recommended 30 for low performance, 0 for regular) // forceHostAddress Can force RakNet to use a particular IP to host on. Pass 0 to automatically pick an IP // Returns: // False on failure (can't create socket or thread), true on success. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::Initialize( unsigned short MaximumNumberOfPeers, unsigned short localPort, int _threadSleepTimer, const char *forceHostAddress ) { // Maximum number of peers must be > 0 assert(MaximumNumberOfPeers > 0); if (MaximumNumberOfPeers <= 0) { return false; } unsigned i; if ( connectionSocket == INVALID_SOCKET ) { connectionSocket = SocketLayer::Instance()->CreateBoundSocket( localPort, true, forceHostAddress ); if ( connectionSocket == INVALID_SOCKET ) return false; } if ( _threadSleepTimer < 0 ) return false; if ( maximumNumberOfPeers == 0 ) { // Don't allow more incoming connections than we have peers. if ( maximumIncomingConnections > MaximumNumberOfPeers ) maximumIncomingConnections = MaximumNumberOfPeers; maximumNumberOfPeers = MaximumNumberOfPeers; // Allocate a few extra remote systems to handle new connections from players trying to connect when the server is full remoteSystemListSize = MaximumNumberOfPeers + 1 + MaximumNumberOfPeers/8; // rakPeerMutexes[ RakPeer::remoteSystemList_Mutex ].Lock(); remoteSystemList = new RemoteSystemStruct[ remoteSystemListSize ]; // rakPeerMutexes[ RakPeer::remoteSystemList_Mutex ].Unlock(); for ( i = 0; i < remoteSystemListSize; i++ ) { remoteSystemList[ i ].playerId = UNASSIGNED_PLAYER_ID; // remoteSystemList[ i ].allowPlayerIdAssigment=true; } } // For histogram statistics // nextReadBytesTime=0; // lastSentBytes=lastReceivedBytes=0; if ( endThreads ) { lastUserUpdateCycle = 0; // Reset the frequency table that we use to save outgoing data memset( frequencyTable, 0, sizeof( unsigned int ) * 256 ); // Reset the statistical data rawBytesSent = rawBytesReceived = compressedBytesSent = compressedBytesReceived = 0; updateCycleIsRunning = false; endThreads = false; // Create the threads threadSleepTimer = _threadSleepTimer; ClearBufferedCommands(); char ipList[ 10 ][ 16 ]; SocketLayer::Instance()->GetMyIP( ipList ); myPlayerId.port = localPort; myPlayerId.binaryAddress = inet_addr( ipList[ 0 ] ); { #ifdef _WIN32 if ( isMainLoopThreadActive == false ) { unsigned ProcessPacketsThreadID = 0; processPacketsThreadHandle = ( HANDLE ) _beginthreadex( NULL, 0, UpdateNetworkLoop, this, 0, &ProcessPacketsThreadID ); if ( processPacketsThreadHandle == 0 ) { Disconnect( 0 ); return false; } // SetThreadPriority(processPacketsThreadHandle, THREAD_PRIORITY_HIGHEST); CloseHandle( processPacketsThreadHandle ); processPacketsThreadHandle = 0; } #else pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); // sched_param sp; // sp.sched_priority = sched_get_priority_max(SCHED_OTHER); // pthread_attr_setschedparam(&attr, &sp); int error; if ( isMainLoopThreadActive == false ) { error = pthread_create( &processPacketsThreadHandle, &attr, &UpdateNetworkLoop, this ); if ( error ) { Disconnect( 0 ); return false; } } processPacketsThreadHandle = 0; #endif // Wait for the threads to activate. When they are active they will set these variables to true while ( /*isRecvfromThreadActive==false || */isMainLoopThreadActive == false ) #ifdef _WIN32 Sleep( 10 ); #else usleep( 10 * 1000 ); #endif } /* else { #ifdef __USE_IO_COMPLETION_PORTS AsynchronousFileIO::Instance()->IncreaseUserCount(); #endif }*/ } return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Must be called while offline // Secures connections though a combination of SHA1, AES128, SYN Cookies, and RSA to prevent // connection spoofing, replay attacks, data eavesdropping, packet tampering, and MitM attacks. // There is a significant amount of processing and a slight amount of bandwidth // overhead for this feature. // // If you accept connections, you must call this or else secure connections will not be enabled // for incoming connections. // If you are connecting to another system, you can call this with values for the // (e and p,q) public keys before connecting to prevent MitM // // Parameters: // pubKeyE, pubKeyN - A pointer to the public keys from the RSACrypt class. See the Encryption sample // privKeyP, privKeyQ - Private keys generated from the RSACrypt class. See the Encryption sample // If the private keys are 0, then a new key will be generated when this function is called // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::InitializeSecurity(const char *pubKeyE, const char *pubKeyN, const char *privKeyP, const char *privKeyQ ) { if ( endThreads == false ) return ; // Setting the client key is e,n, // Setting the server key is p,q if ( //( privKeyP && privKeyQ && ( pubKeyE || pubKeyN ) ) || //( pubKeyE && pubKeyN && ( privKeyP || privKeyQ ) ) || ( privKeyP && privKeyQ == 0 ) || ( privKeyQ && privKeyP == 0 ) || ( pubKeyE && pubKeyN == 0 ) || ( pubKeyN && pubKeyE == 0 ) ) { // Invalid parameters assert( 0 ); } seedMT( RakNet::GetTime() ); GenerateSYNCookieRandomNumber(); usingSecurity = true; if ( privKeyP == 0 && privKeyQ == 0 && pubKeyE == 0 && pubKeyN == 0 ) { keysLocallyGenerated = true; rsacrypt.generateKeys(); } else { if ( pubKeyE && pubKeyN ) { // Save public keys memcpy( ( char* ) & publicKeyE, pubKeyE, sizeof( publicKeyE ) ); memcpy( publicKeyN, pubKeyN, sizeof( publicKeyN ) ); } if ( privKeyP && privKeyQ ) { BIGHALFSIZE( RSA_BIT_SIZE, p ); BIGHALFSIZE( RSA_BIT_SIZE, q ); memcpy( p, privKeyP, sizeof( p ) ); memcpy( q, privKeyQ, sizeof( q ) ); // Save private keys rsacrypt.setPrivateKey( p, q ); } keysLocallyGenerated = false; } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description // Must be called while offline // Disables all security. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::DisableSecurity( void ) { if ( endThreads == false ) return ; usingSecurity = false; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no // more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced // to the maximum number of peers allowed. Defaults to 0. // // Parameters: // numberAllowed - Maximum number of incoming connections allowed. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SetMaximumIncomingConnections( unsigned short numberAllowed ) { maximumIncomingConnections = numberAllowed; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the maximum number of incoming connections, which is always <= MaximumNumberOfPeers // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- unsigned short RakPeer::GetMaximumIncomingConnections( void ) const { return maximumIncomingConnections; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Sets the password incoming connections must match in the call to Connect (defaults to none) // Pass 0 to passwordData to specify no password // // Parameters: // passwordData: A data block that incoming connections must match. This can be just a password, or can be a stream of data. // - Specify 0 for no password data // passwordDataLength: The length in bytes of passwordData // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SetIncomingPassword( const char* passwordData, int passwordDataLength ) { if (passwordDataLength > MAX_OFFLINE_DATA_LENGTH) passwordDataLength=MAX_OFFLINE_DATA_LENGTH; // Set the incoming password data rakPeerMutexes[ incomingPasswordBitStream_Mutex ].Lock(); incomingPasswordBitStream.Reset(); if ( passwordData && passwordDataLength > 0 ) incomingPasswordBitStream.Write( passwordData, passwordDataLength ); rakPeerMutexes[ incomingPasswordBitStream_Mutex ].Unlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the password set by SetIncomingPassword in a BitStream // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- RakNet::BitStream *RakPeer::GetIncomingPassword( void ) { return & incomingPasswordBitStream; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Call this to connect to the specified host (ip or domain name) and server port. // Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. // This is a non-blocking connection. You know the connection is successful when IsConnected() returns true // or receive gets a packet with the type identifier ID_CONNECTION_ACCEPTED. If the connection is not // successful, such as rejected connection or no response then neither of these things will happen. // Requires that you first call Initialize // // Parameters: // host: Either a dotted IP address or a domain name // remotePort: Which port to connect to on the remote machine. // passwordData: A data block that must match the data block on the server. This can be just a password, or can be a stream of data // passwordDataLength: The length in bytes of passwordData // // Returns: // True on successful initiation. False on incorrect parameters, internal error, or too many existing peers // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::Connect( const char* host, unsigned short remotePort, char* passwordData, int passwordDataLength ) { // If endThreads is true here you didn't call Initialize() first. if ( host == 0 || endThreads || connectionSocket == INVALID_SOCKET ) return false; unsigned numberOfFreeSlots; numberOfFreeSlots = 0; /* for ( i = 0; i < maximumNumberOfPeers; i++ ) { if ( remoteSystemList[ i ].playerId == UNASSIGNED_PLAYER_ID ) numberOfFreeSlots++; } if ( numberOfFreeSlots < (unsigned short)(remoteSystemListSize-maximumNumberOfPeers)) return false; */ if (passwordDataLength>MAX_OFFLINE_DATA_LENGTH) passwordDataLength=MAX_OFFLINE_DATA_LENGTH; // Set the incoming password data rakPeerMutexes[ outgoingPasswordBitStream_Mutex ].Lock(); outgoingPasswordBitStream.Reset(); if ( passwordData && passwordDataLength > 0 ) outgoingPasswordBitStream.Write( passwordData, passwordDataLength ); rakPeerMutexes[ outgoingPasswordBitStream_Mutex ].Unlock(); // If the host starts with something other than 0, 1, or 2 it's (probably) a domain name. if ( host[ 0 ] < '0' || host[ 0 ] > '2' ) { host = ( char* ) SocketLayer::Instance()->DomainNameToIP( host ); if (host==0) return false; } // Connecting to ourselves in the same instance of the program? if ( ( strcmp( host, "127.0.0.1" ) == 0 || strcmp( host, "0.0.0.0" ) == 0 ) && remotePort == myPlayerId.port ) { // Feedback loop. if ( !AllowIncomingConnections() ) { // Tell the game that this person has connected Packet * p; p = packetPool.GetPointer(); p->data = new unsigned char [ 1 ]; p->data[ 0 ] = (unsigned char) ID_NO_FREE_INCOMING_CONNECTIONS; p->playerId = myPlayerId; p->playerIndex = ( PlayerIndex ) GetIndexFromPlayerID( myPlayerId ); p->length = 1; p->bitSize = 8; #ifdef _DEBUG assert( p->data ); #endif incomingQueueMutex.Lock(); incomingPacketQueue.push( p ); incomingQueueMutex.Unlock(); } else { // Just assume we are connected. This is really just for testing. // RemoteSystemStruct* remoteSystem = AssignPlayerIDToRemoteSystemList( myPlayerId, RemoteSystemStruct::CONNECTED ); // if ( remoteSystem != 0 ) // { RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)); bitStream.Write((unsigned char)ID_NEW_INCOMING_CONNECTION); bitStream.Write(myPlayerId.binaryAddress); bitStream.Write(myPlayerId.port); // Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, myPlayerId, false ); SendBuffered(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, myPlayerId, false, RemoteSystemStruct::CONNECTED); return true; // } // else // return false; } } return SendConnectionRequest( host, remotePort ); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Stops the network threads and close all connections. Multiple calls are ok. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::Disconnect( unsigned int blockDuration ) { unsigned i,j; bool anyActive; unsigned stopWaitingTime; // PlayerID playerId; unsigned int time; unsigned short systemListSize = remoteSystemListSize; // This is done for threading reasons for (i=0; i < messageHandlerList.size(); i++) { messageHandlerList[i]->OnDisconnect(this); } if ( blockDuration > 0 ) { for ( i = 0; i < systemListSize; i++ ) { NotifyAndFlagForDisconnect(remoteSystemList[i].playerId, false); } time = RakNet::GetTime(); stopWaitingTime = time + blockDuration; while ( time < stopWaitingTime ) { anyActive=false; for (j=0; j < systemListSize; j++) { if (remoteSystemList[j].playerId!=UNASSIGNED_PLAYER_ID) { anyActive=true; break; } } // If this system is out of packets to send, then stop waiting if ( anyActive==false ) break; // This will probably cause the update thread to run which will probably // send the disconnection notification #ifdef _WIN32 Sleep( 15 ); #else usleep( 15 * 1000 ); #endif time = RakNet::GetTime(); } } if ( endThreads == false ) { // Stop the threads endThreads = true; // Normally the thread will call DecreaseUserCount on termination but if we aren't using threads just do it // manually #ifdef __USE_IO_COMPLETION_PORTS AsynchronousFileIO::Instance()->DecreaseUserCount(); #endif } while ( isMainLoopThreadActive ) #ifdef _WIN32 Sleep( 15 ); #else usleep( 15 * 1000 ); #endif // Reset the remote system list after the threads are known to have stopped so threads do not add or update data to them after they are reset //rakPeerMutexes[ RakPeer::remoteSystemList_Mutex ].Lock(); for ( i = 0; i < systemListSize; i++ ) { // Reserve this reliability layer for ourselves remoteSystemList[ i ].playerId = UNASSIGNED_PLAYER_ID; // Remove any remaining packets remoteSystemList[ i ].reliabilityLayer.Reset(); } //rakPeerMutexes[ remoteSystemList_Mutex ].Unlock(); // Setting maximumNumberOfPeers to 0 allows remoteSystemList to be reallocated in Initialize. // Setting remoteSystemListSize prevents threads from accessing the reliability layer maximumNumberOfPeers = 0; remoteSystemListSize = 0; if ( connectionSocket != INVALID_SOCKET ) { closesocket( connectionSocket ); connectionSocket = INVALID_SOCKET; } // Clear out the queues while ( incomingPacketQueue.size() ) packetPool.ReleasePointer( incomingPacketQueue.pop() ); ClearBufferedCommands(); bytesSentPerSecond = bytesReceivedPerSecond = 0; ClearRequestedConnectionList(); // Clear out the reliabilty layer list in case we want to reallocate it in a successive call to Init. RemoteSystemStruct * temp = remoteSystemList; remoteSystemList = 0; delete [] temp; packetPool.ClearPool(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns true if the network threads are running // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::IsActive( void ) const { return endThreads == false; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Fills the array remoteSystems with the playerID of all the systems we are connected to // // Parameters: // remoteSystems (out): An array of PlayerID structures to be filled with the PlayerIDs of the systems we are connected to // - pass 0 to remoteSystems to only get the number of systems we are connected to // numberOfSystems (int, out): As input, the size of remoteSystems array. As output, the number of elements put into the array // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::GetConnectionList( PlayerID *remoteSystems, unsigned short *numberOfSystems ) const { int count, index; count=0; if ( remoteSystemList == 0 || endThreads == true ) { *numberOfSystems = 0; return false; } // This is called a lot so I unrolled the loop if ( remoteSystems ) { for ( count = 0, index = 0; index < remoteSystemListSize; ++index ) if ( remoteSystemList[ index ].playerId != UNASSIGNED_PLAYER_ID && remoteSystemList[ index ].connectMode==RemoteSystemStruct::CONNECTED) { if ( count < *numberOfSystems ) remoteSystems[ count ] = remoteSystemList[ index ].playerId; ++count; } } else { for ( count = 0, index = 0; index < remoteSystemListSize; ++index ) if ( remoteSystemList[ index ].playerId != UNASSIGNED_PLAYER_ID && remoteSystemList[ index ].connectMode==RemoteSystemStruct::CONNECTED) ++count; } *numberOfSystems = ( unsigned short ) count; return 0; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Sends a block of data to the specified system that you are connected to. // This function only works while the client is connected (Use the Connect function). // // Parameters: // data: The block of data to send // length: The size in bytes of the data to send // bitStream: The bitstream to send // priority: What priority level to send on. // reliability: How reliability to send this data // orderingChannel: When using ordered or sequenced packets, what channel to order these on. // - Packets are only ordered relative to other packets on the same stream // playerId: Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_PLAYER_ID to specify none // broadcast: True to send this packet to all connected systems. If true, then playerId specifies who not to send the packet to. // Returns: // False if we are not connected to the specified recipient. True otherwise // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) { #ifdef _DEBUG assert( data && length > 0 ); #endif if ( data == 0 || length < 0 ) return false; RakNet::BitStream temp( (char*)data, length, false ); return Send( &temp, priority, reliability, orderingChannel, playerId, broadcast ); } bool RakPeer::Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) { #ifdef _DEBUG assert( bitStream->GetNumberOfBytesUsed() > 0 ); #endif if ( bitStream->GetNumberOfBytesUsed() == 0 ) return false; if ( remoteSystemList == 0 || endThreads == true ) return false; if ( broadcast == false && playerId == UNASSIGNED_PLAYER_ID ) return false; if (ValidSendTarget(playerId, broadcast)) { // Sends need to be buffered and processed in the update thread because the playerID associated with the reliability layer can change, // from that thread, resulting in a send to the wrong player! While I could mutex the playerID, that is much slower than doing this SendBuffered(bitStream, priority, reliability, orderingChannel, playerId, broadcast, RemoteSystemStruct::NO_ACTION); return true; } return false; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. // Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct // // Returns: // 0 if no packets are waiting to be handled, otherwise an allocated packet // If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected // This also updates all memory blocks associated with synchronized memory and distributed objects // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Packet* RakPeer::Receive( void ) { if ( !( IsActive() ) ) return 0; Packet *val; int offset; unsigned int i; bool propagate; for (i=0; i < messageHandlerList.size(); i++) { messageHandlerList[i]->OnUpdate(this); } #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( true ) { if (incomingPacketQueue.size() > 0) // First fast unlocked check { incomingQueueMutex.Lock(); if ( incomingPacketQueue.size() > 0 ) // Second safe locked check { val = incomingPacketQueue.pop(); } else { incomingQueueMutex.Unlock(); return 0; } incomingQueueMutex.Unlock(); } else return 0; // Do RPC calls from the user thread, not the network update thread if ( val->data[ 0 ] == ID_RPC ) { // RPC_ASSERT assert(0); DeallocatePacket( val ); continue; } if ( ( val->length >= sizeof(unsigned char) + sizeof( int ) ) && ( (unsigned char) val->data[ 0 ] == ID_TIMESTAMP ) ) { offset = sizeof(unsigned char); ShiftIncomingTimestamp( ( char* ) val->data + offset, val->playerId ); } propagate=true; for (i=0; i < messageHandlerList.size(); i++) { if (messageHandlerList[i]->OnReceive(this, val)) { // If the message handler returns true, do no further processing propagate=false; break; } // Packets that are not propagated to the game are only processed by message handlers if (propagate==true && messageHandlerList[i]->PropagateToGame(val)==false) propagate=false; } if (propagate==false) { DeallocatePacket( val ); continue; } break; } #ifdef _DEBUG assert( val->data ); #endif return val; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Call this to deallocate a packet returned by Receive when you are done handling it. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::DeallocatePacket( Packet *packet ) { if ( packet == 0 ) return ; packetPool.ReleasePointer( packet ); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Return the total number of connections we are allowed // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- unsigned short RakPeer::GetMaximumNumberOfPeers( void ) const { return maximumNumberOfPeers; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). // // Parameters: // target: Which connection to close // sendDisconnectionNotification: True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::CloseConnection( PlayerID target, bool sendDisconnectionNotification ) { CloseConnectionInternalBuffered(target, sendDisconnectionNotification); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Given a playerID, returns an index from 0 to the maximum number of players allowed - 1. // // Parameters // playerId - The playerID to search for // // Returns // An integer from 0 to the maximum number of peers -1, or -1 if that player is not found // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- int RakPeer::GetIndexFromPlayerID( PlayerID playerId ) { unsigned i; if ( playerId == UNASSIGNED_PLAYER_ID ) return -1; for ( i = 0; i < maximumNumberOfPeers; i++ ) if ( remoteSystemList[ i ].playerId == playerId ) return i; return -1; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // This function is only useful for looping through all players. // // Parameters // index - an integer between 0 and the maximum number of players allowed - 1. // // Returns // A valid playerID or UNASSIGNED_PLAYER_ID if no such player at that index // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- PlayerID RakPeer::GetPlayerIDFromIndex( int index ) { if ( index >= 0 && index < remoteSystemListSize ) if (remoteSystemList[ index ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED) // Don't give the user players that aren't fully connected, since sends will fail return remoteSystemList[ index ].playerId; return UNASSIGNED_PLAYER_ID; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Bans an IP from connecting. Banned IPs persist between connections. // // Parameters // IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban // All IP addresses starting with 128.0.0 // milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::AddToBanList( const char *IP, unsigned int milliseconds ) { unsigned index; unsigned int time = RakNet::GetTime(); if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) return ; // If this guy is already in the ban list, do nothing index = 0; banListMutex.Lock(); for ( ; index < banList.size(); index++ ) { if ( strcmp( IP, banList[ index ]->IP ) == 0 ) { // Already in the ban list. Just update the time if (milliseconds==0) banList[ index ]->timeout=0; // Infinite else banList[ index ]->timeout=time+milliseconds; banListMutex.Unlock(); return; } } banListMutex.Unlock(); BanStruct *banStruct = new BanStruct; if (milliseconds==0) banStruct->timeout=0; // Infinite else banStruct->timeout=time+milliseconds; strcpy( banStruct->IP, IP ); banListMutex.Lock(); banList.insert( banStruct ); banListMutex.Unlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Allows a previously banned IP to connect. // // Parameters // IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban // All IP addresses starting with 128.0.0 // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::RemoveFromBanList( const char *IP ) { unsigned index; BanStruct *temp; if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) return ; index = 0; temp=0; banListMutex.Lock(); for ( ; index < banList.size(); index++ ) { if ( strcmp( IP, banList[ index ]->IP ) == 0 ) { temp = banList[ index ]; banList[ index ] = banList[ banList.size() - 1 ]; banList.del( banList.size() - 1 ); break; } } banListMutex.Unlock(); if (temp) { delete temp; } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Allows all previously banned IPs to connect. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::ClearBanList( void ) { unsigned index; index = 0; banListMutex.Lock(); for ( ; index < banList.size(); index++ ) { delete [] banList[ index ]->IP; delete [] banList[ index ]; } banList.clear(); banListMutex.Unlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Determines if a particular IP is banned. // // Parameters // IP - Complete dotted IP address // // Returns // True if IP matches any IPs in the ban list, accounting for any wildcards. // False otherwise. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::IsBanned( const char *IP ) { unsigned banListIndex, characterIndex; unsigned int time; BanStruct *temp; if ( IP == 0 || IP[ 0 ] == 0 || strlen( IP ) > 15 ) return false; banListIndex = 0; if ( banList.size() == 0 ) return false; // Skip the mutex if possible time = RakNet::GetTime(); banListMutex.Lock(); while ( banListIndex < banList.size() ) { if (banList[ banListIndex ]->timeout>0 && banList[ banListIndex ]->timeoutIP[ characterIndex ] == IP[ characterIndex ] ) { // Equal characters if ( IP[ characterIndex ] == 0 ) { banListMutex.Unlock(); // End of the string and the strings match return true; } characterIndex++; } else { if ( banList[ banListIndex ]->IP[ characterIndex ] == 0 || IP[ characterIndex ] == 0 ) { // End of one of the strings break; } // Characters do not match if ( banList[ banListIndex ]->IP[ characterIndex ] == '*' ) { banListMutex.Unlock(); // Domain is banned. return true; } // Characters do not match and it is not a * break; } } banListIndex++; } } banListMutex.Unlock(); // No match found. return false; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Send a ping to the specified connected system. // // Parameters: // target - who to ping // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::Ping( PlayerID target ) { PingInternal(target, false); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Send a ping to the specified unconnected system. // The remote system, if it is Initialized, will respond with ID_PONG. // The final ping time will be encoded in the following 4 bytes (2-5) as an unsigned int // // Requires: // The sender and recipient must already be started via a successful call to Initialize // // Parameters: // host: Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. // remotePort: Which port to connect to on the remote machine. // onlyReplyOnAcceptingConnections: Only request a reply if the remote system has open connections // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections ) { if ( host == 0 ) return; if ( IsActive() == false ) return; // If the host starts with something other than 0, 1, or 2 it's (probably) a domain name. if ( host[ 0 ] < '0' || host[ 0 ] > '2' ) { host = ( char* ) SocketLayer::Instance()->DomainNameToIP( host ); } if ( host == NULL ) return; PlayerID playerId; IPToPlayerID( host, remotePort, &playerId ); // disabled so can send while shutting down // if (GetRemoteSystemFromPlayerID(playerId)) // return; if (strcmp(host, "255.255.255.255")==0) { RakNet::BitStream bitStream( sizeof(unsigned char) + sizeof(unsigned int) ); if ( onlyReplyOnAcceptingConnections ) bitStream.Write((unsigned char)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); else bitStream.Write((unsigned char)ID_UNCONNECTED_PING); // No timestamp for 255.255.255.255 SocketLayer::Instance()->SendTo( connectionSocket, (const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), ( char* ) host, remotePort ); } else { RequestedConnectionStruct *rcs = requestedConnectionList.WriteLock(); rcs->playerId=playerId; rcs->nextRequestTime=RakNet::GetTime(); rcs->requestsMade=0; rcs->data=0; if (onlyReplyOnAcceptingConnections) rcs->actionToTake=RequestedConnectionStruct::PING_OPEN_CONNECTIONS; else rcs->actionToTake=RequestedConnectionStruct::PING; requestedConnectionList.WriteUnlock(); } /* RakNet::BitStream bitStream( sizeof(unsigned char) + sizeof(unsigned int) ); if ( onlyReplyOnAcceptingConnections ) bitStream.Write((unsigned char)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); else bitStream.Write((unsigned char)ID_UNCONNECTED_PING); bitStream.Write((unsigned int)RakNet::GetTime()); // SendBuffered(&bitStream, SYSTEM_PRIORITY, UNRELIABLE, 0, playerId, false, RemoteSystemStruct::DISCONNECT_ASAP); SocketLayer::Instance()->SendTo( connectionSocket, (const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), ( char* ) host, remotePort ); */ } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the average of all ping times read for a specified target // // Parameters: // target - whose time to read // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- int RakPeer::GetAveragePing( PlayerID playerId ) { int sum, quantity; RemoteSystemStruct *remoteSystem = GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem == 0 ) return -1; for ( sum = 0, quantity = 0; quantity < PING_TIMES_ARRAY_SIZE; quantity++ ) { if ( remoteSystem->pingAndClockDifferential[ quantity ].pingTime == -1 ) break; else sum += remoteSystem->pingAndClockDifferential[ quantity ].pingTime; } if ( quantity > 0 ) return sum / quantity; else return -1; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the last ping time read for the specific player or -1 if none read yet // // Parameters: // target - whose time to read // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- int RakPeer::GetLastPing( PlayerID playerId ) const { RemoteSystemStruct * remoteSystem = GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem == 0 ) return -1; if ( remoteSystem->pingAndClockDifferentialWriteIndex == 0 ) return remoteSystem->pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE - 1 ].pingTime; else return remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex - 1 ].pingTime; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the lowest ping time read or -1 if none read yet // // Parameters: // target - whose time to read // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- int RakPeer::GetLowestPing( PlayerID playerId ) const { RemoteSystemStruct * remoteSystem = GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem == 0 ) return -1; return remoteSystem->lowestPing; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Ping the remote systems every so often. This is off by default // This will work anytime // // Parameters: // doPing - True to start occasional pings. False to stop them. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SetOccasionalPing( bool doPing ) { occasionalPing = doPing; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // All systems have a block of data associated with them, for user use. This block of data can be used to easily // specify typical system data that you want to know on connection, such as the player's name. // // Parameters: // playerId: Which system you are referring to. Pass the value returned by GetInternalID to refer to yourself // // Returns: // The data passed to SetRemoteStaticData stored as a bitstream // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- RakNet::BitStream * RakPeer::GetRemoteStaticData( PlayerID playerId ) { if ( playerId == myPlayerId ) return & localStaticData; RemoteSystemStruct *remoteSystem = GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem ) return & ( remoteSystem->staticData ); else return 0; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // All systems have a block of data associated with them, for user use. This block of data can be used to easily // specify typical system data that you want to know on connection, such as the player's name. // // Parameters: // playerId: Whose static data to change. Use your own playerId to change your own static data // data: a block of data to store // length: The length of data in bytes // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SetRemoteStaticData( PlayerID playerId, const char *data, const long length ) { if ( playerId == myPlayerId ) { localStaticData.Reset(); if ( data && length > 0 ) localStaticData.Write( data, length ); } else { RemoteSystemStruct *remoteSystem = GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem == 0 ) return ; remoteSystem->staticData.Reset(); if ( data && length > 0 ) remoteSystem->staticData.Write( data, length ); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Sends your static data to the specified system. This is automatically done on connection. // You should call this when you change your static data. // To send the static data of another system (such as relaying their data) you should do this normally with Send // // Parameters: // target: Who to send your static data to. Specify UNASSIGNED_PLAYER_ID to broadcast to all // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SendStaticData( PlayerID target ) { SendStaticDataInternal(target, false); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Length should be under 400 bytes, as a security measure against flood attacks // Sets the data to send with an (LAN server discovery) /(offline ping) response // See the Ping sample project for how this is used. // data: a block of data to store, or 0 for none // length: The length of data in bytes, or 0 for none // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SetOfflinePingResponse( const char *data, const unsigned int length ) { assert(length < 400); rakPeerMutexes[ offlinePingResponse_Mutex ].Lock(); offlinePingResponse.Reset(); if ( data && length > 0 ) offlinePingResponse.Write( data, length ); rakPeerMutexes[ offlinePingResponse_Mutex ].Unlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Return the unique PlayerID that represents you on the the network // Note that unlike in previous versions, this is a struct and is not sequential // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- PlayerID RakPeer::GetInternalID( void ) const { return myPlayerId; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Return the unique address identifier that represents you on the the network and is based on your external // IP / port (the IP / port the specified player uses to communicate with you) // Note that unlike in previous versions, this is a struct and is not sequential // // Parameters: // target: Which remote system you are referring to for your external ID // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- PlayerID RakPeer::GetExternalID( PlayerID target ) const { RemoteSystemStruct * remoteSystem = GetRemoteSystemFromPlayerID( target ); if ( remoteSystem == 0 ) return UNASSIGNED_PLAYER_ID; else return remoteSystem->myExternalPlayerId; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Change the MTU size in order to improve performance when sending large packets // This can only be called when not connected. // A too high of value will cause packets not to arrive at worst and be fragmented at best. // A too low of value will split packets unnecessarily. // // Parameters: // size: Set according to the following table: // 1500. The largest Ethernet packet size // This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. // 1492. The size PPPoE prefers. // 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) // 1468. The size DHCP prefers. // 1460. Usable by AOL if you don't have large email attachments, etc. // 1430. The size VPN and PPTP prefer. // 1400. Maximum size for AOL DSL. // 576. Typical value to connect to dial-up ISPs. (Default) // // Returns: // False on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::SetMTUSize( int size ) { if ( IsActive() ) return false; if ( size < 512 ) size = 512; else if ( size > MAXIMUM_MTU_SIZE ) size = MAXIMUM_MTU_SIZE; MTUSize = size; return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the current MTU size // // Returns: // The MTU sized specified in SetMTUSize // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- int RakPeer::GetMTUSize( void ) const { return MTUSize; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the number of IP addresses we have // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- unsigned int RakPeer::GetNumberOfAddresses( void ) { char ipList[ 10 ][ 16 ]; memset( ipList, 0, sizeof( char ) * 16 * 10 ); SocketLayer::Instance()->GetMyIP( ipList ); int i = 0; while ( ipList[ i ][ 0 ] ) i++; return i; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Given a PlayerID struct, returns the dotted IP address string this binaryAddress field represents // // Returns: // Null terminated dotted IP address string. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- const char* RakPeer::PlayerIDToDottedIP( PlayerID playerId ) const { in_addr in; in.s_addr = playerId.binaryAddress; return inet_ntoa( in ); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns an IP address at index 0 to GetNumberOfAddresses-1 // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- const char* RakPeer::GetLocalIP( unsigned int index ) { static char ipList[ 10 ][ 16 ]; if ( index >= 10 ) index = 9; memset( ipList, 0, sizeof( char ) * 16 * 10 ); SocketLayer::Instance()->GetMyIP( ipList ); return ipList[ index ]; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary // when connection to servers with multiple IP addresses // // Parameters: // allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::AllowConnectionResponseIPMigration( bool allow ) { allowConnectionResponseIPMigration = allow; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. // This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through // // Requires: // The sender and recipient must already be started via a successful call to Initialize // // host: Either a dotted IP address or a domain name // remotePort: Which port to connect to on the remote machine. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ) { if ( IsActive() == false ) return ; // This is a security measure. Don't send data longer than this value assert(dataLength <= MAX_OFFLINE_DATA_LENGTH); assert(dataLength>=0); // If the host starts with something other than 0, 1, or 2 it's (probably) a domain name. if ( host[ 0 ] < '0' || host[ 0 ] > '2' ) { host = ( char* ) SocketLayer::Instance()->DomainNameToIP( host ); } PlayerID playerId; IPToPlayerID( host, remotePort, &playerId ); // disabled so can send while shutting down //if (GetRemoteSystemFromPlayerID(playerId)) // return; RequestedConnectionStruct *rcs = requestedConnectionList.WriteLock(); rcs->playerId=playerId; rcs->nextRequestTime=RakNet::GetTime(); rcs->requestsMade=0; if (data && dataLength>0) { rcs->data=new char [dataLength]; rcs->dataLength=dataLength; memcpy(rcs->data, data, dataLength); } else { rcs->data=0; rcs->dataLength=0; } rcs->actionToTake=RequestedConnectionStruct::ADVERTISE_SYSTEM; requestedConnectionList.WriteUnlock(); // unsigned char c = ID_ADVERTISE_SYSTEM; // RakNet::BitStream temp(sizeof(c)); // temp.Write((unsigned char)c); // if (data && dataLength>0) // temp.Write(data, dataLength); // Send(&temp, SYSTEM_PRIORITY, UNRELIABLE, 0, playerId, false); //SendBuffered(&temp, SYSTEM_PRIORITY, UNRELIABLE, 0, playerId, false, RemoteSystemStruct::DISCONNECT_ASAP); // SocketLayer::Instance()->SendTo( connectionSocket, (const char*)temp.GetData(), temp.GetNumberOfBytesUsed(), ( char* ) host, remotePort ); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Enables or disables our tracking of bytes input to and output from the network. // This is required to get a frequency table, which is used to generate a new compression layer. // You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only track // part of the values sent over the network. // This value persists between connect calls and defaults to false (no frequency tracking) // // Parameters: // doCompile - true to track bytes. Defaults to false // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SetCompileFrequencyTable( bool doCompile ) { trackFrequencyTable = doCompile; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the frequency of outgoing bytes into outputFrequencyTable // The purpose is to save to file as either a master frequency table from a sample game session for passing to // GenerateCompressionLayer(false) // You should only call this when disconnected. // Requires that you first enable data frequency tracking by calling SetCompileFrequencyTable(true) // // Parameters: // outputFrequencyTable (out): The frequency of each corresponding byte // // Returns: // Ffalse (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success) // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::GetOutgoingFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ) { if ( IsActive() ) return false; if ( trackFrequencyTable == false ) return false; memcpy( outputFrequencyTable, frequencyTable, sizeof( unsigned int ) * 256 ); return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Generates the compression layer from the input frequency table. // You should call this twice - once with inputLayer as true and once as false. // The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false. // Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true // Calling this function when there is an existing layer will overwrite the old layer // You should only call this when disconnected // // Parameters: // inputFrequencyTable: The frequency table returned from GetSendFrequencyTable(...) // inputLayer - Whether inputFrequencyTable represents incoming data from other systems (true) or outgoing data from this system (false) // // Returns: // False on failure (we are connected). True otherwise // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ) { if ( IsActive() ) return false; DeleteCompressionLayer( inputLayer ); if ( inputLayer ) { inputTree = new HuffmanEncodingTree; inputTree->GenerateFromFrequencyTable( inputFrequencyTable ); } else { outputTree = new HuffmanEncodingTree; outputTree->GenerateFromFrequencyTable( inputFrequencyTable ); } return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Deletes the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory // You should only call this when disconnected // // Parameters: // inputLayer - Specifies the corresponding compression layer generated by GenerateCompressionLayer. // // Returns: // False on failure (we are connected). True otherwise // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::DeleteCompressionLayer( bool inputLayer ) { if ( IsActive() ) return false; if ( inputLayer ) { if ( inputTree ) { delete inputTree; inputTree = 0; } } else { if ( outputTree ) { delete outputTree; outputTree = 0; } } return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Returns: // The compression ratio. A low compression ratio is good. Compression is for outgoing data // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- float RakPeer::GetCompressionRatio( void ) const { if ( rawBytesSent > 0 ) { return ( float ) compressedBytesSent / ( float ) rawBytesSent; } else return 0.0f; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Returns: // The decompression ratio. A high decompression ratio is good. Decompression is for incoming data // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- float RakPeer::GetDecompressionRatio( void ) const { if ( rawBytesReceived > 0 ) { return ( float ) compressedBytesReceived / ( float ) rawBytesReceived; } else return 0.0f; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Attatches a message handler interface to run code automatically on message receipt in the Receive call // // @param messageHandler Pointer to a message handler to attach // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::AttachMessageHandler( MessageHandlerInterface *messageHandler ) { if (messageHandlerList.getIndexOf(messageHandler)==MAX_UNSIGNED_LONG) { messageHandlerList.insert(messageHandler); messageHandler->OnAttach(this); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Detatches a message handler interface to run code automatically on message receipt // // @param messageHandler Pointer to a message handler to detatch // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::DetachMessageHandler( MessageHandlerInterface *messageHandler ) { unsigned int index; index = messageHandlerList.getIndexOf(messageHandler); if (index!=MAX_UNSIGNED_LONG) { // Unordered list so delete from end for speed messageHandlerList[index]=messageHandlerList[messageHandlerList.size()-1]; messageHandlerList.del(); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Returns the data you passed to the passwordData parameter in Connect // // Parameters // passwordData (out): Should point to a block large enough to hold the password data you passed to Connect // passwordDataLength (in, out): Maximum size of the array passwordData. Modified to hold the number of bytes actually written // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::GetPasswordData( char *passwordData, int *passwordDataLength ) { int length; if ( incomingPasswordBitStream.GetNumberOfBytesUsed() < *passwordDataLength ) length = incomingPasswordBitStream.GetNumberOfBytesUsed(); else length = *passwordDataLength; memcpy( passwordData, incomingPasswordBitStream.GetData(), length ); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Description: // Put a packet back at the end of the receive queue in case you don't want to deal with it immediately // // Parameters // packet: The packet you want to push back. // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::PushBackPacket( Packet *packet ) { if ( packet ) { #ifdef _DEBUG assert( packet->data ); #endif incomingPacketQueue.pushAtHead( packet ); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- RakNetStatisticsStruct * const RakPeer::GetStatistics( PlayerID playerId ) { RemoteSystemStruct * rss; rss = GetRemoteSystemFromPlayerID( playerId ); if ( rss && endThreads==false ) return rss->reliabilityLayer.GetStatistics(); return 0; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::SendConnectionRequest( const char* host, unsigned short remotePort ) { PlayerID playerId; // RemoteSystemStruct *rss; // bool success; IPToPlayerID( host, remotePort, &playerId ); // rss=AssignPlayerIDToRemoteSystemList(playerId, RemoteSystemStruct::REQUESTED_CONNECTION); // if (rss==0) // return false; // full or already connected // Already connected? if (GetRemoteSystemFromPlayerID(playerId)) return false; RequestedConnectionStruct *rcs = requestedConnectionList.WriteLock(); rcs->playerId=playerId; rcs->nextRequestTime=RakNet::GetTime(); rcs->requestsMade=0; rcs->data=0; rcs->actionToTake=RequestedConnectionStruct::CONNECT; requestedConnectionList.WriteUnlock(); // Request will be sent in the other thread //char c = ID_OPEN_CONNECTION_REQUEST; //SocketLayer::Instance()->SendTo( connectionSocket, (char*)&c, 1, ( char* ) host, remotePort ); /* RakNet::BitStream temp( sizeof(unsigned char) + outgoingPasswordBitStream.GetNumberOfBytesUsed() ); temp.Write( (unsigned char) ID_CONNECTION_REQUEST ); if ( outgoingPasswordBitStream.GetNumberOfBytesUsed() > 0 ) temp.Write( ( char* ) outgoingPasswordBitStream.GetData(), outgoingPasswordBitStream.GetNumberOfBytesUsed() ); // success=Send(&temp, SYSTEM_PRIORITY, RELIABLE, 0, playerId, false); SendBuffered(&temp, SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, RemoteSystemStruct::REQUESTED_CONNECTION); //#ifdef _DEBUG // assert(success); //#endif */ return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::IPToPlayerID( const char* host, unsigned short remotePort, PlayerID *playerId ) { if ( host == 0 ) return ; playerId->binaryAddress = inet_addr( host ); playerId->port = remotePort; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- RakPeer::RemoteSystemStruct *RakPeer::GetRemoteSystemFromPlayerID( PlayerID playerID ) const { unsigned i; if ( playerID == UNASSIGNED_PLAYER_ID ) return 0; for ( i = 0; i < remoteSystemListSize; i++ ) if ( remoteSystemList[ i ].playerId == playerID ) return remoteSystemList + i; return 0; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, PlayerID playerId, const char *data, int byteSize ) { // If we are full tell the sender. if ( !AllowIncomingConnections() ) { unsigned char c = ID_NO_FREE_INCOMING_CONNECTIONS; // SocketLayer::Instance()->SendTo( rakPeer->connectionSocket, ( char* ) & c, sizeof( char ), playerId.binaryAddress, playerId.port ); SendImmediate(( char* ) & c, sizeof( char )*8, SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, RakNet::GetTime()); remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; } else { const char *password = data + sizeof(unsigned char); int passwordLength = byteSize - sizeof(unsigned char); if ( incomingPasswordBitStream.GetNumberOfBytesUsed() == passwordLength && memcmp( password, incomingPasswordBitStream.GetData(), passwordLength ) == 0 ) { remoteSystem->connectMode=RemoteSystemStruct::HANDLING_CONNECTION_REQUEST; if ( usingSecurity == false ) { #ifdef _TEST_AES unsigned char AESKey[ 16 ]; // Save the AES key for ( i = 0; i < 16; i++ ) AESKey[ i ] = i; OnConnectionRequest( remoteSystem, AESKey, true ); #else // Connect this player assuming we have open slots OnConnectionRequest( remoteSystem, 0, false ); #endif } else SecuredConnectionResponse( playerId ); } else { // This one we only send once since we don't care if it arrives. unsigned char c = ID_INVALID_PASSWORD; // SocketLayer::Instance()->SendTo( rakPeer->connectionSocket, ( char* ) & c, sizeof( char ), playerId.binaryAddress, playerId.port ); SendImmediate(( char* ) & c, sizeof( char )*8, SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, RakNet::GetTime()); remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; } } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, unsigned char *AESKey, bool setAESKey ) { if ( AllowIncomingConnections() ) { #ifdef __USE_IO_COMPLETION_PORTS unsigned index; for ( index = 0; index < remoteSystemListSize; index++ ) if ( remoteSystemList + index == remoteSystem ) break; if ( SetupIOCompletionPortSocket( index ) == false ) { // Socket error assert( 0 ); return ; } #endif RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(unsigned short)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(PlayerIndex)); bitStream.Write((unsigned char)ID_CONNECTION_REQUEST_ACCEPTED); #ifdef __USE_IO_COMPLETION_PORTS bitStream.Write((unsigned short)myPlayerId.port + ( unsigned short ) index + ( unsigned short ) 1); #else bitStream.Write((unsigned short)myPlayerId.port); #endif bitStream.Write(remoteSystem->playerId.binaryAddress); bitStream.Write(remoteSystem->playerId.port); bitStream.Write(( PlayerIndex ) GetIndexFromPlayerID( remoteSystem->playerId )); /* ConnectionAcceptStruct ds; ds.typeId = ID_CONNECTION_REQUEST_ACCEPTED; #ifdef __USE_IO_COMPLETION_PORTS ds.remotePort = myPlayerId.port + ( unsigned short ) index + ( unsigned short ) 1; #else ds.remotePort = myPlayerId.port; #endif ds.externalID = remoteSystem->playerId; ds.playerIndex = ( PlayerIndex ) GetIndexFromPlayerID( remoteSystem->playerId ); RakNet::BitStream dsBitS( ConnectionAcceptStruct_Size ); ds.Serialize( dsBitS ); */ SendImmediate((char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, remoteSystem->playerId, false, false, RakNet::GetTime()); // Don't set secure connections immediately because we need the ack from the remote system to know ID_CONNECTION_REQUEST_ACCEPTED // As soon as a 16 byte packet arrives, we will turn on AES. This works because all encrypted packets are multiples of 16 and the // packets I happen to be sending are less than 16 bytes remoteSystem->setAESKey=setAESKey; if ( setAESKey ) { memcpy(remoteSystem->AESKey, AESKey, 16); remoteSystem->connectMode=RemoteSystemStruct::SET_ENCRYPTION_ON_MULTIPLE_16_BYTE_PACKET; } } else { unsigned char c = ID_NO_FREE_INCOMING_CONNECTIONS; //SocketLayer::Instance()->SendTo( connectionSocket, ( char* ) & c, sizeof( char ), playerId.binaryAddress, playerId.port ); SendImmediate((char*)&c, sizeof(c)*8, SYSTEM_PRIORITY, RELIABLE, 0, remoteSystem->playerId, false, false, RakNet::GetTime()); remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::NotifyAndFlagForDisconnect( PlayerID playerId, bool performImmediate ) { RakNet::BitStream temp( sizeof(unsigned char) + outgoingPasswordBitStream.GetNumberOfBytesUsed() ); temp.Write( (unsigned char) ID_DISCONNECTION_NOTIFICATION ); if ( outgoingPasswordBitStream.GetNumberOfBytesUsed() > 0 ) temp.Write( ( char* ) outgoingPasswordBitStream.GetData(), outgoingPasswordBitStream.GetNumberOfBytesUsed() ); if (performImmediate) { SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, RakNet::GetTime()); RemoteSystemStruct *rss=GetRemoteSystemFromPlayerID(playerId); rss->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; } else { SendBuffered(&temp, SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, RemoteSystemStruct::DISCONNECT_ASAP); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- unsigned short RakPeer::GetNumberOfRemoteInitiatedConnections( void ) const { unsigned short i, numberOfIncomingConnections; if ( remoteSystemList == 0 || endThreads == true ) return 0; numberOfIncomingConnections = 0; for ( i = 0; i < remoteSystemListSize; i++ ) { if ( remoteSystemList[ i ].playerId != UNASSIGNED_PLAYER_ID && remoteSystemList[ i ].weInitiatedTheConnection == false && remoteSystemList[i].connectMode==RemoteSystemStruct::CONNECTED) numberOfIncomingConnections++; } return numberOfIncomingConnections; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- RakPeer::RemoteSystemStruct * RakPeer::AssignPlayerIDToRemoteSystemList( PlayerID playerId, RemoteSystemStruct::ConnectMode connectionMode ) { RemoteSystemStruct * remoteSystem = 0; unsigned i,j; unsigned int time = RakNet::GetTime(); // If this guy is already connected, return 0. This needs to be checked inside the mutex // because threads may call the connection routine multiple times at the same time for ( i = 0; i < remoteSystemListSize; i++ ) { if ( remoteSystemList[ i ].playerId == playerId ) { return 0; } } for ( i = 0; i < remoteSystemListSize; i++ ) { if ( remoteSystemList[ i ].playerId == UNASSIGNED_PLAYER_ID) { remoteSystem=remoteSystemList+i; remoteSystem->playerId = playerId; // This one line causes future incoming packets to go through the reliability layer remoteSystem->reliabilityLayer.SetEncryptionKey( 0 ); for ( j = 0; j < PING_TIMES_ARRAY_SIZE; j++ ) { remoteSystem->pingAndClockDifferential[ j ].pingTime = -1; remoteSystem->pingAndClockDifferential[ j ].clockDifferential = 0; } remoteSystem->connectMode=connectionMode; remoteSystem->pingAndClockDifferentialWriteIndex = 0; remoteSystem->lowestPing = -1; remoteSystem->nextPingTime = 0; // Ping immediately remoteSystem->weInitiatedTheConnection = false; remoteSystem->staticData.Reset(); remoteSystem->connectionTime = time; remoteSystem->myExternalPlayerId = UNASSIGNED_PLAYER_ID; remoteSystem->setAESKey=false; remoteSystem->lastReliableSend=time; // Reserve this reliability layer for ourselves. remoteSystem->reliabilityLayer.Reset(); return remoteSystem; } } return remoteSystem; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Adjust the first four bytes (treated as unsigned int) of the pointer // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::ShiftIncomingTimestamp( char *data, PlayerID playerId ) const { #ifdef _DEBUG assert( IsActive() ); assert( data ); #endif RakNet::BitStream timeBS(data, 4, false); unsigned int encodedTimestamp; timeBS.Read(encodedTimestamp); encodedTimestamp = encodedTimestamp - GetBestClockDifferential( playerId ); timeBS.SetWriteOffset(0); timeBS.Write(encodedTimestamp); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm unsigned int RakPeer::GetBestClockDifferential( PlayerID playerId ) const { int counter, clockDifferential, lowestPingSoFar; RemoteSystemStruct *remoteSystem = GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem == 0 ) return 0; lowestPingSoFar = 65535; clockDifferential = 0; for ( counter = 0; counter < PING_TIMES_ARRAY_SIZE; counter++ ) { if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime == -1 ) break; if ( remoteSystem->pingAndClockDifferential[ counter ].pingTime < lowestPingSoFar ) { clockDifferential = remoteSystem->pingAndClockDifferential[ counter ].clockDifferential; lowestPingSoFar = remoteSystem->pingAndClockDifferential[ counter ].pingTime; } } return clockDifferential; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #ifdef __USE_IO_COMPLETION_PORTS bool RakPeer::SetupIOCompletionPortSocket( int index ) { SOCKET newSocket; if ( remoteSystemList[ index ].reliabilityLayer.GetSocket() != INVALID_SOCKET ) closesocket( remoteSystemList[ index ].reliabilityLayer.GetSocket() ); newSocket = SocketLayer::Instance()->CreateBoundSocket( myPlayerId.port + index + 1, false ); SocketLayer::Instance()->Connect( newSocket, remoteSystemList[ index ].playerId.binaryAddress, remoteSystemList[ index ].playerId.port ); // port is the port of the client remoteSystemList[ index ].reliabilityLayer.SetSocket( newSocket ); // Associate our new socket with a completion port and do the first read return SocketLayer::Instance()->AssociateSocketWithCompletionPortAndRead( newSocket, remoteSystemList[ index ].playerId.binaryAddress, remoteSystemList[ index ].playerId.port, this ); } #endif // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::GenerateSYNCookieRandomNumber( void ) { unsigned int number; int i; memcpy( oldRandomNumber, newRandomNumber, sizeof( newRandomNumber ) ); for ( i = 0; i < sizeof( newRandomNumber ); i += sizeof( number ) ) { number = randomMT(); memcpy( newRandomNumber + i, ( char* ) & number, sizeof( number ) ); } randomNumberExpirationTime = RakNet::GetTime() + SYN_COOKIE_OLD_RANDOM_NUMBER_DURATION; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SecuredConnectionResponse( PlayerID playerId ) { CSHA1 sha1; RSA_BIT_SIZE n; big::u32 e; unsigned char connectionRequestResponse[ 1 + sizeof( big::u32 ) + sizeof( RSA_BIT_SIZE ) + 20 ]; connectionRequestResponse[ 0 ] = ID_SECURED_CONNECTION_RESPONSE; // Hash the SYN-Cookie // s2c syn-cookie = SHA1_HASH(source ip address + source port + random number) sha1.Reset(); sha1.Update( ( unsigned char* ) & playerId.binaryAddress, sizeof( playerId.binaryAddress ) ); sha1.Update( ( unsigned char* ) & playerId.port, sizeof( playerId.port ) ); sha1.Update( ( unsigned char* ) & ( newRandomNumber ), 20 ); sha1.Final(); // Write the cookie memcpy( connectionRequestResponse + 1, sha1.GetHash(), 20 ); // Write the public keys rsacrypt.getPublicKey( e, n ); #ifdef HOST_ENDIAN_IS_BIG // Mangle the keys on a Big-endian machine before sending BSWAPCPY( (unsigned char *)(connectionRequestResponse + 1 + 20), (unsigned char *)&e, sizeof( big::u32 ) ); BSWAPCPY( (unsigned char *)(connectionRequestResponse + 1 + 20 + sizeof( big::u32 ) ), (unsigned char *)n, sizeof( RSA_BIT_SIZE ) ); #else memcpy( connectionRequestResponse + 1 + 20, ( char* ) & e, sizeof( big::u32 ) ); memcpy( connectionRequestResponse + 1 + 20 + sizeof( big::u32 ), n, sizeof( RSA_BIT_SIZE ) ); #endif // s2c public key, syn-cookie //SocketLayer::Instance()->SendTo( connectionSocket, ( char* ) connectionRequestResponse, 1 + sizeof( big::u32 ) + sizeof( RSA_BIT_SIZE ) + 20, playerId.binaryAddress, playerId.port ); // All secure connection requests are unreliable because the entire process needs to be restarted if any part fails. // Connection requests are resent periodically SendImmediate(( char* ) connectionRequestResponse, (1 + sizeof( big::u32 ) + sizeof( RSA_BIT_SIZE ) + 20) *8, SYSTEM_PRIORITY, UNRELIABLE, 0, playerId, false, false, RakNet::GetTime()); } void RakPeer::SecuredConnectionConfirmation( RakPeer::RemoteSystemStruct * remoteSystem, char* data ) { int i, j; unsigned char randomNumber[ 20 ]; unsigned int number; //bool doSend; Packet *packet; big::u32 e; RSA_BIT_SIZE n, message, encryptedMessage; big::RSACrypt privKeyPncrypt; // Make sure that we still want to connect if (remoteSystem->connectMode!=RemoteSystemStruct::REQUESTED_CONNECTION) return; /* // Make sure that we still want to connect bool requestedConnection = false; rakPeerMutexes[ RakPeer::requestedConnections_MUTEX ].Lock(); for ( i = 0; i < ( int ) requestedConnectionsList.size();i++ ) { if ( requestedConnectionsList[ i ]->playerId == playerId ) { // We did request this connection requestedConnection = true; break; } } rakPeerMutexes[ RakPeer::requestedConnections_MUTEX ].Unlock(); if ( requestedConnection == false ) return ; // Don't want to connect doSend = false; */ // Copy out e and n #ifdef HOST_ENDIAN_IS_BIG BSWAPCPY( (unsigned char *)&e, (unsigned char *)(data + 1 + 20), sizeof( big::u32 ) ); BSWAPCPY( (unsigned char *)n, (unsigned char *)(data + 1 + 20 + sizeof( big::u32 )), sizeof( RSA_BIT_SIZE ) ); #else memcpy( ( char* ) & e, data + 1 + 20, sizeof( big::u32 ) ); memcpy( n, data + 1 + 20 + sizeof( big::u32 ), sizeof( RSA_BIT_SIZE ) ); #endif // If we preset a size and it doesn't match, or the keys do not match, then tell the user if ( usingSecurity == true && keysLocallyGenerated == false ) { if ( memcmp( ( char* ) & e, ( char* ) & publicKeyE, sizeof( big::u32 ) ) != 0 || memcmp( n, publicKeyN, sizeof( RSA_BIT_SIZE ) ) != 0 ) { packet = packetPool.GetPointer(); packet->data = new unsigned char[ 1 ]; packet->data[ 0 ] = ID_RSA_PUBLIC_KEY_MISMATCH; packet->length = sizeof( char ); packet->bitSize = sizeof( char ) * 8; packet->playerId = remoteSystem->playerId; packet->playerIndex = ( PlayerIndex ) GetIndexFromPlayerID( packet->playerId ); incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; return; } } // Create a random number for ( i = 0; i < sizeof( randomNumber ); i += sizeof( number ) ) { number = randomMT(); memcpy( randomNumber + i, ( char* ) & number, sizeof( number ) ); } memset( message, 0, sizeof( message ) ); assert( sizeof( message ) >= sizeof( randomNumber ) ); #ifdef HOST_ENDIAN_IS_BIG // Scramble the plaintext message BSWAPCPY( (unsigned char *)message, randomNumber, sizeof(randomNumber) ); #else memcpy( message, randomNumber, sizeof( randomNumber ) ); #endif privKeyPncrypt.setPublicKey( e, n ); privKeyPncrypt.encrypt( message, encryptedMessage ); #ifdef HOST_ENDIAN_IS_BIG // A big-endian machine needs to scramble the byte order of an outgoing (encrypted) message BSWAPSELF( (unsigned char *)encryptedMessage, sizeof( RSA_BIT_SIZE ) ); #endif /* rakPeerMutexes[ RakPeer::requestedConnections_MUTEX ].Lock(); for ( i = 0; i < ( int ) requestedConnectionsList.size(); i++ ) { if ( requestedConnectionsList[ i ]->playerId == playerId ) { doSend = true; // Generate the AES key for ( j = 0; j < 16; j++ ) requestedConnectionsList[ i ]->AESKey[ j ] = data[ 1 + j ] ^ randomNumber[ j ]; requestedConnectionsList[ i ]->setAESKey = true; break; } } rakPeerMutexes[ RakPeer::requestedConnections_MUTEX ].Unlock(); */ // Take the remote system's AESKey and XOR with our random number. for ( j = 0; j < 16; j++ ) remoteSystem->AESKey[ j ] = data[ 1 + j ] ^ randomNumber[ j ]; remoteSystem->setAESKey = true; // if ( doSend ) // { char reply[ 1 + 20 + sizeof( RSA_BIT_SIZE ) ]; // c2s RSA(random number), same syn-cookie reply[ 0 ] = ID_SECURED_CONNECTION_CONFIRMATION; memcpy( reply + 1, data + 1, 20 ); // Copy the syn-cookie memcpy( reply + 1 + 20, encryptedMessage, sizeof( RSA_BIT_SIZE ) ); // Copy the encoded random number //SocketLayer::Instance()->SendTo( connectionSocket, reply, 1 + 20 + sizeof( RSA_BIT_SIZE ), playerId.binaryAddress, playerId.port ); // All secure connection requests are unreliable because the entire process needs to be restarted if any part fails. // Connection requests are resent periodically SendImmediate((char*)reply, (1 + 20 + sizeof( RSA_BIT_SIZE )) * 8, SYSTEM_PRIORITY, UNRELIABLE, 0, remoteSystem->playerId, false, false, RakNet::GetTime()); // } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::PushPortRefused( PlayerID target ) { // Tell the game we can't connect to this host Packet * p; p = packetPool.GetPointer(); p->data = new unsigned char[ 1 ]; p->data[ 0 ] = ID_REMOTE_PORT_REFUSED; p->length = sizeof( char ); p->playerId = target; // We don't know this! p->playerIndex = ( PlayerIndex ) GetIndexFromPlayerID( p->playerId ); #ifdef _DEBUG assert( p->data ); #endif // Relay this message to the game incomingQueueMutex.Lock(); incomingPacketQueue.push( p ); incomingQueueMutex.Unlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::AllowIncomingConnections(void) const { return GetNumberOfRemoteInitiatedConnections() < GetMaximumIncomingConnections(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SendStaticDataInternal( PlayerID target, bool performImmediate ) { RakNet::BitStream reply( sizeof(unsigned char) + localStaticData.GetNumberOfBytesUsed() ); reply.Write( (unsigned char) ID_RECEIVED_STATIC_DATA ); reply.Write( ( char* ) localStaticData.GetData(), localStaticData.GetNumberOfBytesUsed() ); if (performImmediate) { if ( target == UNASSIGNED_PLAYER_ID ) SendImmediate( (char*)reply.GetData(), reply.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, target, true, false, RakNet::GetTime() ); else SendImmediate( (char*)reply.GetData(), reply.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, target, false, false, RakNet::GetTime() ); } else { if ( target == UNASSIGNED_PLAYER_ID ) Send( &reply, SYSTEM_PRIORITY, RELIABLE, 0, target, true ); else Send( &reply, SYSTEM_PRIORITY, RELIABLE, 0, target, false ); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::PingInternal( PlayerID target, bool performImmediate ) { if ( IsActive() == false ) return ; RakNet::BitStream bitStream(sizeof(unsigned char)+sizeof(unsigned int)); bitStream.Write((unsigned char)ID_CONNECTED_PING); unsigned int currentTime = RakNet::GetTime(); bitStream.Write(currentTime); if (performImmediate) SendImmediate( (char*)bitStream.GetData(), bitStream.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, UNRELIABLE, 0, target, false, false, currentTime ); else Send( &bitStream, SYSTEM_PRIORITY, UNRELIABLE, 0, target, false ); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::CloseConnectionInternalBuffered( PlayerID target, bool sendDisconnectionNotification ) { if ( remoteSystemList == 0 || endThreads == true ) return; if (sendDisconnectionNotification) { NotifyAndFlagForDisconnect(target, false); } else { BufferedCommandStruct *bcs; bcs=bufferedCommands.WriteLock(); bcs->command=BufferedCommandStruct::BCS_CLOSE_CONNECTION; bcs->playerId=target; bcs->data=0; bufferedCommands.WriteUnlock(); } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::CloseConnectionInternalImmediate( PlayerID target) { if ( remoteSystemList == 0 || endThreads == true ) return; for (unsigned int i = 0 ; i < remoteSystemListSize; i++ ) { if ( remoteSystemList[ i ].playerId == target ) { // Reserve this reliability layer for ourselves remoteSystemList[ i ].playerId = UNASSIGNED_PLAYER_ID; // remoteSystemList[ i ].allowPlayerIdAssigment=false; // Remove any remaining packets. remoteSystemList[ i ].reliabilityLayer.Reset(); break; } } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::ValidSendTarget(PlayerID playerId, bool broadcast) { unsigned remoteSystemIndex; for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; remoteSystemIndex++ ) { if ( remoteSystemList[ remoteSystemIndex ].playerId != UNASSIGNED_PLAYER_ID && remoteSystemList[ remoteSystemIndex ].connectMode==RakPeer::RemoteSystemStruct::CONNECTED && // Not fully connected players are not valid user-send targets because the reliability layer wasn't reset yet ( ( broadcast == false && remoteSystemList[ remoteSystemIndex ].playerId == playerId ) || ( broadcast == true && remoteSystemList[ remoteSystemIndex ].playerId != playerId ) ) ) return true; } return false; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::SendBuffered( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode ) { BufferedCommandStruct *bcs; bcs=bufferedCommands.WriteLock(); bcs->data = new char[bitStream->GetNumberOfBytesUsed()]; // Making a copy doesn't lose efficiency because I tell the reliability layer to use this allocation for its own copy memcpy(bcs->data, bitStream->GetData(), bitStream->GetNumberOfBytesUsed()); bcs->numberOfBitsToSend=bitStream->GetNumberOfBitsUsed(); bcs->priority=priority; bcs->reliability=reliability; bcs->orderingChannel=orderingChannel; bcs->playerId=playerId; bcs->broadcast=broadcast; bcs->connectionMode=connectionMode; bcs->command=BufferedCommandStruct::BCS_SEND; bufferedCommands.WriteUnlock(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::SendImmediate( char *data, int numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast, bool useCallerDataAllocation, unsigned int currentTime ) { unsigned *sendList; unsigned sendListSize; bool callerDataAllocationUsed; unsigned remoteSystemIndex, sendListIndex; // Iterates into the list of remote systems unsigned numberOfBytesUsed = BITS_TO_BYTES(numberOfBitsToSend); callerDataAllocationUsed=false; sendList=(unsigned *)alloca(sizeof(unsigned)*remoteSystemListSize); sendListSize=0; for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; remoteSystemIndex++ ) { if ( remoteSystemList[ remoteSystemIndex ].playerId != UNASSIGNED_PLAYER_ID && ( ( broadcast == false && remoteSystemList[ remoteSystemIndex ].playerId == playerId ) || ( broadcast == true && remoteSystemList[ remoteSystemIndex ].playerId != playerId ) ) ) sendList[sendListSize++]=remoteSystemIndex; } if (sendListSize==0) return false; for (sendListIndex=0; sendListIndex < sendListSize; sendListIndex++) { if ( trackFrequencyTable ) { unsigned i; // Store output frequency for (i=0 ; i < numberOfBytesUsed; i++ ) frequencyTable[ (unsigned char)(data[i]) ]++; rawBytesSent += numberOfBytesUsed; } if ( outputTree ) { RakNet::BitStream bitStreamCopy( numberOfBytesUsed ); outputTree->EncodeArray( (unsigned char*) data, numberOfBytesUsed, &bitStreamCopy ); compressedBytesSent += bitStreamCopy.GetNumberOfBytesUsed(); remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( (char*) bitStreamCopy.GetData(), bitStreamCopy.GetNumberOfBitsUsed(), priority, reliability, orderingChannel, true, MTUSize, currentTime ); } else { // Send may split the packet and thus deallocate data. Don't assume data is valid if we use the callerAllocationData bool useData = useCallerDataAllocation && callerDataAllocationUsed==false && sendListIndex+1==sendListSize; remoteSystemList[sendList[sendListIndex]].reliabilityLayer.Send( data, numberOfBitsToSend, priority, reliability, orderingChannel, useData==false, MTUSize, currentTime ); if (useData) callerDataAllocationUsed=true; } if (reliability==RELIABLE || reliability==RELIABLE_ORDERED || reliability==RELIABLE_SEQUENCED) remoteSystemList[sendList[sendListIndex]].lastReliableSend=currentTime; } // Return value only meaningful if true was passed for useCallerDataAllocation. Means the reliability layer used that data copy, so the caller should not deallocate it return callerDataAllocationUsed; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::ClearBufferedCommands(void) { BufferedCommandStruct *bcs; while ((bcs=bufferedCommands.ReadLock())!=0) { if (bcs->data) delete [] bcs->data; bufferedCommands.ReadUnlock(); } bufferedCommands.Clear(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- void RakPeer::ClearRequestedConnectionList(void) { RequestedConnectionStruct *bcs; while ((bcs=requestedConnectionList.ReadLock())!=0) { if (bcs->data) delete [] bcs->data; requestedConnectionList.ReadUnlock(); } requestedConnectionList.Clear(); } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter #ifdef _WIN32 void __stdcall ProcessPortUnreachable( unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ) #else void ProcessPortUnreachable( unsigned int binaryAddress, unsigned short port, RakPeer *rakPeer ) #endif { // TODO - figure out how the hell to get the values for binaryAddress and port /* RakPeer::RemoteSystemStruct *remoteSystem; PlayerID playerId; playerId.binaryAddress = binaryAddress; playerId.port = port; remoteSystem = rakPeer->GetRemoteSystemFromPlayerID( playerId ); if (remoteSystem) remoteSystem->reliabilityLayer.KillConnection(); */ } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #ifdef _WIN32 void __stdcall ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ) #else void ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ) #endif { PlayerID playerId; // unsigned i; RakPeer::RemoteSystemStruct *remoteSystem; playerId.binaryAddress = binaryAddress; playerId.port = port; if (rakPeer->IsBanned( rakPeer->PlayerIDToDottedIP( playerId ) )) return; // UNCONNECTED MESSAGE to establish a connection if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REPLY && length == sizeof(unsigned char)) { // Verify that we were waiting for this bool acceptOpenConnection; int actionToTake=0; char data[MAX_OFFLINE_DATA_LENGTH]; unsigned short dataLength; RakPeer::RequestedConnectionStruct *rcsFirst, *rcs; rcsFirst = rakPeer->requestedConnectionList.ReadLock(); rcs=rcsFirst; acceptOpenConnection=false; while (rcs) { if (rcs->playerId==playerId) { acceptOpenConnection=true; actionToTake|=(int)rcs->actionToTake; if (rcs->data) { #ifdef _DEBUG assert(rcs->actionToTake==RakPeer::RequestedConnectionStruct::ADVERTISE_SYSTEM); assert(rcs->dataLength <= MAX_OFFLINE_DATA_LENGTH); #endif memcpy(data, rcs->data, rcs->dataLength); dataLength=rcs->dataLength; delete [] rcs->data; rcs->data=0; } if (rcs==rcsFirst) { rakPeer->requestedConnectionList.ReadUnlock(); rcsFirst=rakPeer->requestedConnectionList.ReadLock(); rcs=rcsFirst; continue; } else { // Duplicate call - cancel it rcs->playerId=UNASSIGNED_PLAYER_ID; } } rcs=rakPeer->requestedConnectionList.ReadLock(); } if (rcsFirst) rakPeer->requestedConnectionList.CancelReadLock(rcsFirst); if (acceptOpenConnection) { remoteSystem=rakPeer->AssignPlayerIDToRemoteSystemList(playerId, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER); // if (remoteSystem==0) // remoteSystem=rakPeer->GetRemoteSystemFromPlayerID(playerId); // Get the already connected guy if (remoteSystem) { unsigned int time = RakNet::GetTime(); if (actionToTake & RakPeer::RequestedConnectionStruct::CONNECT) { remoteSystem->connectMode=RakPeer::RemoteSystemStruct::REQUESTED_CONNECTION; remoteSystem->weInitiatedTheConnection=true; RakNet::BitStream temp( sizeof(unsigned char) + rakPeer->outgoingPasswordBitStream.GetNumberOfBytesUsed() ); temp.Write( (unsigned char) ID_CONNECTION_REQUEST ); if ( rakPeer->outgoingPasswordBitStream.GetNumberOfBytesUsed() > 0 ) temp.Write( ( char* ) rakPeer->outgoingPasswordBitStream.GetData(), rakPeer->outgoingPasswordBitStream.GetNumberOfBytesUsed() ); rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, time ); } if ((actionToTake & RakPeer::RequestedConnectionStruct::PING) || (actionToTake & RakPeer::RequestedConnectionStruct::PING_OPEN_CONNECTIONS)) { RakNet::BitStream temp( sizeof(unsigned char) + sizeof(unsigned int) ); if ( actionToTake & RakPeer::RequestedConnectionStruct::PING_OPEN_CONNECTIONS ) temp.Write((unsigned char)ID_UNCONNECTED_PING_OPEN_CONNECTIONS); else temp.Write((unsigned char)ID_UNCONNECTED_PING); temp.Write(time); // SocketLayer::Instance()->SendTo( connectionSocket, (const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed(), ( char* ) host, remotePort ); rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, time ); } if (actionToTake & RakPeer::RequestedConnectionStruct::ADVERTISE_SYSTEM) { RakNet::BitStream temp; temp.Write((unsigned char)ID_ADVERTISE_SYSTEM); if (dataLength>0) temp.Write(data, dataLength); rakPeer->SendImmediate((char*)temp.GetData(), temp.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, time ); remoteSystem->connectMode=RakPeer::RemoteSystemStruct::DISCONNECT_ASAP; } } } return; } // UNCONNECTED MESSAGE Broadcast ping with no data else if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && length == sizeof(unsigned char) ) { if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || rakPeer->AllowIncomingConnections() ) // Open connections with players { RakNet::BitStream outBitStream; outBitStream.Write((unsigned char)ID_PONG); // Should be named ID_UNCONNECTED_PONG eventually SocketLayer::Instance()->SendTo( rakPeer->connectionSocket, (const char*)outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), (char*)rakPeer->PlayerIDToDottedIP(playerId) , playerId.port ); } return; } // UNCONNECTED MESSAGE Pong with no data else if ((unsigned char) data[ 0 ] == ID_PONG && length == sizeof(unsigned char) ) { Packet * packet = rakPeer->packetPool.GetPointer(); packet->data = new unsigned char[ sizeof( char )+sizeof(unsigned int) ]; unsigned int zero=0; packet->data[ 0 ] = ID_PONG; memcpy(packet->data+sizeof( char ), (char*)&zero, sizeof(unsigned int)); packet->length = sizeof( char ); packet->bitSize = sizeof( char ) * 8; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) rakPeer->GetIndexFromPlayerID( playerId ); rakPeer->incomingQueueMutex.Lock(); rakPeer->incomingPacketQueue.push( packet ); rakPeer->incomingQueueMutex.Unlock(); return; } remoteSystem = rakPeer->GetRemoteSystemFromPlayerID( playerId ); if ( remoteSystem ) { if (remoteSystem->connectMode==RakPeer::RemoteSystemStruct::SET_ENCRYPTION_ON_MULTIPLE_16_BYTE_PACKET && (length%16)==0) remoteSystem->reliabilityLayer.SetEncryptionKey( remoteSystem->AESKey ); // Handle regular incoming data // HandleSocketReceiveFromConnectedPlayer is only safe to be called from the same thread as Update, which is this thread if ( remoteSystem->reliabilityLayer.HandleSocketReceiveFromConnectedPlayer( data, length ) == false ) { // These kinds of packets may have been duplicated and incorrectly determined to be // cheat packets. Anything else really is a cheat packet if ( !( ( (unsigned char)data[0] == ID_OPEN_CONNECTION_REQUEST && length == 1 ) || ( (unsigned char)data[0] == ID_OPEN_CONNECTION_REPLY && length == 1 ) || (((unsigned char)data[0] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && length == sizeof(unsigned char)+sizeof(unsigned int) ) || ( (unsigned char)data[0] == ID_PONG && length >= sizeof(unsigned char)+sizeof(unsigned int) ) || ( (unsigned char)data[0] == ID_ADVERTISE_SYSTEM && lengthpacketPool.GetPointer(); packet->data = new unsigned char[ 1 ]; packet->data[ 0 ] = ID_MODIFIED_PACKET; packet->length = sizeof( char ); packet->bitSize = sizeof( char ) * 8; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) rakPeer->GetIndexFromPlayerID( playerId ); rakPeer->incomingQueueMutex.Lock(); rakPeer->incomingPacketQueue.push( packet ); rakPeer->incomingQueueMutex.Unlock(); } } } else { if (length > 512) { // Flood attack? Unknown systems should never send more than a small amount of data rakPeer->AddToBanList(rakPeer->PlayerIDToDottedIP(playerId), TIMEOUT_TIME); return; } // The reason for ID_OPEN_CONNECTION_REQUEST and ID_OPEN_CONNECTION_REPLY is that they are only one byte so I can be sure // that they are offline messages and I know to reset the connections. This is because the smallest possible connected packet is 17 bits. // This is the only way I can tell for sure that a message is asking for a new connection. // This fixes bugs where I ignore a connection request from a connected player or handle a message that looks like a connection request but actually wasn't. if ((unsigned char)(data)[0] == ID_OPEN_CONNECTION_REQUEST && length == sizeof(unsigned char)) { remoteSystem=rakPeer->AssignPlayerIDToRemoteSystemList(playerId, RakPeer::RemoteSystemStruct::UNVERIFIED_SENDER); if (remoteSystem) // If this guy is already connected remote system will be 0 { char c = ID_OPEN_CONNECTION_REPLY; SocketLayer::Instance()->SendTo( rakPeer->connectionSocket, (char*)&c, 1, playerId.binaryAddress, playerId.port ); } } } } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bool RakPeer::RunUpdateCycle( void ) { RakPeer::RemoteSystemStruct * remoteSystem; unsigned remoteSystemIndex; Packet *packet; int ping, lastPing; // int currentSentBytes,currentReceivedBytes; // unsigned numberOfBytesUsed; unsigned numberOfBitsUsed; //PlayerID authoritativeClientPlayerId; int bitSize, byteSize; char *data; int errorCode; int gotData; unsigned int time; PlayerID playerId; BufferedCommandStruct *bcs; bool callerDataAllocationUsed; RakNetStatisticsStruct *rnss; do { // Read a packet gotData = SocketLayer::Instance()->RecvFrom( connectionSocket, this, &errorCode ); if ( gotData == SOCKET_ERROR ) { #ifdef _WIN32 if ( errorCode == WSAECONNRESET ) { gotData=false; // 11/14/05 - RecvFrom now calls HandlePortUnreachable rather than PushPortRefused //PushPortRefused( UNASSIGNED_PLAYER_ID ); //closesocket(peer->connectionSocket); //peer->connectionSocket = SocketLayer::Instance()->CreateBoundSocket(peer->myPlayerId.port, true); } else if ( errorCode != 0 && endThreads == false ) { #ifdef _DO_PRINTF printf( "Server RecvFrom critical failure!\n" ); #endif // Some kind of critical error // peer->isRecvfromThreadActive=false; endThreads = true; Disconnect( 0 ); return false; } #else if ( errorCode == -1 ) { // isRecvfromThreadActive=false; endThreads = true; Disconnect( 0 ); return false; } #endif } if ( endThreads ) return false; } while ( gotData>0 ); // Read until there is nothing left time=0; // Process all the deferred user thread Send and connect calls while ( ( bcs = bufferedCommands.ReadLock() ) != 0 ) // Don't immediately check mutex since it's so slow to activate it { if (bcs->command==BufferedCommandStruct::BCS_SEND) { // This will create a new connection if requested if (bcs->connectionMode!=RemoteSystemStruct::NO_ACTION) { remoteSystem=AssignPlayerIDToRemoteSystemList(bcs->playerId, bcs->connectionMode); if (!remoteSystem) { // Does this system already exist? remoteSystem=GetRemoteSystemFromPlayerID(bcs->playerId); if (remoteSystem) remoteSystem->connectMode=bcs->connectionMode; } } // GetTime is a very slow call so do it once and as late as possible if (time==0) time = RakNet::GetTime(); callerDataAllocationUsed=SendImmediate((char*)bcs->data, bcs->numberOfBitsToSend, bcs->priority, bcs->reliability, bcs->orderingChannel, bcs->playerId, bcs->broadcast, true, time); if ( callerDataAllocationUsed==false ) delete [] bcs->data; } else { #ifdef _DEBUG assert(bcs->command==BufferedCommandStruct::BCS_CLOSE_CONNECTION); #endif CloseConnectionInternalImmediate(bcs->playerId); } #ifdef _DEBUG bcs->data=0; #endif bufferedCommands.ReadUnlock(); } // Process connection attempts RequestedConnectionStruct *rcsFirst, *rcs; bool condition1, condition2; rcsFirst = requestedConnectionList.ReadLock(); rcs=rcsFirst; while (rcs) { if (time==0) time = RakNet::GetTime(); if (rcs->nextRequestTime < time) { condition1=rcs->requestsMade==3; condition2=(bool)((rcs->playerId==UNASSIGNED_PLAYER_ID)==1); // If too many requests made or a hole then remove this if possible, otherwise invalidate it if (condition1 || condition2) { if (rcs->data) { delete [] rcs->data; rcs->data=0; } if (condition1 && !condition2 && rcs->actionToTake==RequestedConnectionStruct::CONNECT) { // Tell user of connection attempt failed packet = packetPool.GetPointer(); packet->data = new unsigned char [ sizeof( char ) ]; packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't packet->length = sizeof( char ); packet->bitSize = ( sizeof( char ) * 8); packet->playerId = myPlayerId; packet->playerIndex = 65535; incomingQueueMutex.Lock(); ( incomingPacketQueue ).push( packet ); incomingQueueMutex.Unlock(); } // Remove this if possible if (rcs==rcsFirst) { requestedConnectionList.ReadUnlock(); rcsFirst = requestedConnectionList.ReadLock(); rcs=rcsFirst; } else { // Hole in the middle rcs->playerId=UNASSIGNED_PLAYER_ID; rcs=requestedConnectionList.ReadLock(); } continue; } rcs->requestsMade++; rcs->nextRequestTime=time+1000; char c = ID_OPEN_CONNECTION_REQUEST; SocketLayer::Instance()->SendTo( connectionSocket, (char*)&c, 1, ( char* ) PlayerIDToDottedIP(rcs->playerId), rcs->playerId.port ); } rcs=requestedConnectionList.ReadLock(); } if (rcsFirst) requestedConnectionList.CancelReadLock(rcsFirst); for ( remoteSystemIndex = 0; remoteSystemIndex < remoteSystemListSize; ++remoteSystemIndex ) { // I'm using playerId from remoteSystemList but am not locking it because this loop is called very frequently and it doesn't // matter if we miss or do an extra update. The reliability layers themselves never care which player they are associated with playerId = remoteSystemList[ remoteSystemIndex ].playerId; // Allow the playerID for this remote system list to change. We don't care if it changes now. // remoteSystemList[ remoteSystemIndex ].allowPlayerIdAssigment=true; if ( playerId != UNASSIGNED_PLAYER_ID ) { // Found an active remote system remoteSystem = remoteSystemList + remoteSystemIndex; // Update is only safe to call from the same thread that calls HandleSocketReceiveFromConnectedPlayer, // which is this thread if (time==0) time = RakNet::GetTime(); if (time > remoteSystem->lastReliableSend && time-remoteSystem->lastReliableSend > 5000 && remoteSystem->connectMode==RemoteSystemStruct::CONNECTED) { // If no reliable packets are waiting for an ack, do a one byte reliable send so that disconnections are noticed rnss=remoteSystem->reliabilityLayer.GetStatistics(); if (rnss->messagesOnResendQueue==0) { unsigned char keepAlive=ID_KEEPALIVE; SendImmediate((char*)&keepAlive,8,LOW_PRIORITY, RELIABLE, 0, remoteSystem->playerId, false, false, time); remoteSystem->lastReliableSend=time+TIMEOUT_TIME; } } remoteSystem->reliabilityLayer.Update( connectionSocket, playerId, MTUSize, time ); // playerId only used for the internet simulator test // Check for failure conditions if ( remoteSystem->reliabilityLayer.IsDeadConnection() || (remoteSystem->connectMode==RemoteSystemStruct::DISCONNECT_ASAP && remoteSystem->reliabilityLayer.IsDataWaiting()==false) || ((remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED && time > remoteSystem->connectionTime && time - remoteSystem->connectionTime > 10000)) ) { // Failed. Inform the user? if (remoteSystem->connectMode==RemoteSystemStruct::CONNECTED || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) { // Inform the user of the connection failure. packet = packetPool.GetPointer(); packet->data = new unsigned char [ sizeof( char ) + remoteSystem->staticData.GetNumberOfBytesUsed() ]; if (remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION) packet->data[ 0 ] = ID_CONNECTION_ATTEMPT_FAILED; // Attempted a connection and couldn't else packet->data[ 0 ] = ID_CONNECTION_LOST; // DeadConnection memcpy( packet->data + sizeof( char ), remoteSystem->staticData.GetData(), remoteSystem->staticData.GetNumberOfBytesUsed() ); packet->length = sizeof( char ) + remoteSystem->staticData.GetNumberOfBytesUsed(); packet->bitSize = ( sizeof( char ) + remoteSystem->staticData.GetNumberOfBytesUsed() ) * 8; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) remoteSystemIndex; incomingQueueMutex.Lock(); ( incomingPacketQueue ).push( packet ); incomingQueueMutex.Unlock(); } // else connection shutting down, don't bother telling the user #ifdef _DO_PRINTF printf("Connection dropped for player %i:%i\n", playerId.binaryAddress, playerId.port); #endif CloseConnectionInternalImmediate( playerId ); continue; } // Did the reliability layer detect a modified packet? if ( remoteSystem->reliabilityLayer.IsCheater() ) { packet = packetPool.GetPointer(); packet->length = 1; packet->data = new unsigned char [ 1 ]; packet->data[ 0 ] = (unsigned char) ID_MODIFIED_PACKET; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) remoteSystemIndex; incomingQueueMutex.Lock(); ( incomingPacketQueue ).push( packet ); incomingQueueMutex.Unlock(); continue; } // Ping this guy if it is time to do so if ( remoteSystem->connectMode==RemoteSystemStruct::CONNECTED && time > remoteSystem->nextPingTime && ( occasionalPing || remoteSystem->lowestPing == -1 ) ) { remoteSystem->nextPingTime = time + 5000; PingInternal( playerId, true ); } // Find whoever has the lowest player ID //if (playerId < authoritativeClientPlayerId) // authoritativeClientPlayerId=playerId; // Does the reliability layer have any packets waiting for us? // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer bitSize = remoteSystem->reliabilityLayer.Receive( &data ); while ( bitSize > 0 ) { // Put the input through compression if necessary if ( inputTree ) { RakNet::BitStream dataBitStream( MAXIMUM_MTU_SIZE ); // Since we are decompressing input, we need to copy to a bitstream, decompress, then copy back to a probably // larger data block. It's slow, but the user should have known that anyway dataBitStream.Reset(); dataBitStream.WriteAlignedBytes( ( unsigned char* ) data, BITS_TO_BYTES( bitSize ) ); rawBytesReceived += dataBitStream.GetNumberOfBytesUsed(); // numberOfBytesUsed = dataBitStream.GetNumberOfBytesUsed(); numberOfBitsUsed = dataBitStream.GetNumberOfBitsUsed(); //rawBytesReceived += numberOfBytesUsed; // Decompress the input data. if (numberOfBitsUsed>0) { unsigned char *dataCopy = new unsigned char[ dataBitStream.GetNumberOfBytesUsed() ]; memcpy( dataCopy, dataBitStream.GetData(), dataBitStream.GetNumberOfBytesUsed() ); dataBitStream.Reset(); inputTree->DecodeArray( dataCopy, numberOfBitsUsed, &dataBitStream ); compressedBytesReceived += dataBitStream.GetNumberOfBytesUsed(); delete [] dataCopy; byteSize = dataBitStream.GetNumberOfBytesUsed(); if ( byteSize > BITS_TO_BYTES( bitSize ) ) // Probably the case - otherwise why decompress? { delete [] data; data = new char [ byteSize ]; } memcpy( data, dataBitStream.GetData(), byteSize ); } else byteSize=0; } else // Fast and easy - just use the data that was returned byteSize = BITS_TO_BYTES( bitSize ); // For unknown senders we only accept a few specific packets if (remoteSystem->connectMode==RemoteSystemStruct::UNVERIFIED_SENDER) { if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) { ParseConnectionRequestPacket(remoteSystem, playerId, data, byteSize); delete [] data; } else if ( ((unsigned char) data[0] == ID_PONG && byteSize >= sizeof(unsigned char)+sizeof(unsigned int)) || ((unsigned char) data[0] == ID_ADVERTISE_SYSTEM && byteSize<=MAX_OFFLINE_DATA_LENGTH)) { // Push to the user Packet *packet = packetPool.GetPointer(); packet->data = ( unsigned char* ) data; packet->length = byteSize; packet->bitSize = byteSize*8; packet->playerId = playerId; packet->playerIndex=65535; incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); if (remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED) { remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; } } else if ( ( (unsigned char) data[ 0 ] == ID_UNCONNECTED_PING_OPEN_CONNECTIONS || (unsigned char)(data)[0] == ID_UNCONNECTED_PING) && byteSize == sizeof(unsigned char)+sizeof(unsigned int) ) { RakNet::BitStream inBitStream( data, byteSize, false ); inBitStream.IgnoreBits(8); unsigned int sendPingTime; inBitStream.Read(sendPingTime); if ( (unsigned char)(data)[0] == ID_UNCONNECTED_PING || AllowIncomingConnections() ) // Open connections with players { RakNet::BitStream outBitStream; outBitStream.Write((unsigned char)ID_PONG); // Should be named ID_UNCONNECTED_PONG eventually outBitStream.Write(sendPingTime); //tempBitStream.Write( data, UnconnectedPingStruct_Size ); rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Lock(); outBitStream.Write( ( char* ) offlinePingResponse.GetData(), offlinePingResponse.GetNumberOfBytesUsed() ); rakPeerMutexes[ RakPeer::offlinePingResponse_Mutex ].Unlock(); //SocketLayer::Instance()->SendTo( connectionSocket, ( char* ) outBitStream.GetData(), outBitStream.GetNumberOfBytesUsed(), playerId.binaryAddress, playerId.port ); SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, UNRELIABLE, 0, playerId, false, false, time ); } // else ID_UNCONNECTED_PING_OPEN_CONNECTIONS and we are full so don't send anything delete [] data; // Disconnect them after replying to their offline ping if (remoteSystem->connectMode!=RemoteSystemStruct::CONNECTED) remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; } else { CloseConnectionInternalImmediate( playerId ); #ifdef _DO_PRINTF printf("Temporarily banning %i:%i for sending nonsense data\n", playerId.binaryAddress, playerId.port); #endif AddToBanList(PlayerIDToDottedIP(playerId), TIMEOUT_TIME); delete [] data; } } else { // However, if we are connected we still take a connection request in case both systems are trying to connect to each other // at the same time if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST ) { if (remoteSystem->weInitiatedTheConnection==false) ParseConnectionRequestPacket(remoteSystem, playerId, data, byteSize); delete [] data; } else if ( (unsigned char) data[ 0 ] == ID_NEW_INCOMING_CONNECTION && byteSize == sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short) ) { #ifdef _DEBUG // This assert can be ignored since it could hit from duplicate packets. // It's just here for internal testing since it should only happen rarely and will mostly be from bugs // assert(remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST); #endif if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || remoteSystem->connectMode==RemoteSystemStruct::SET_ENCRYPTION_ON_MULTIPLE_16_BYTE_PACKET || playerId==myPlayerId) // local system connect { remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; PingInternal( playerId, true ); SendStaticDataInternal( playerId, true ); RakNet::BitStream inBitStream(data, byteSize, false); PlayerID bsPlayerId; inBitStream.IgnoreBits(8); inBitStream.Read(bsPlayerId.binaryAddress); inBitStream.Read(bsPlayerId.port); // Overwrite the data in the packet // NewIncomingConnectionStruct newIncomingConnectionStruct; // RakNet::BitStream nICS_BS( data, NewIncomingConnectionStruct_Size, false ); // newIncomingConnectionStruct.Deserialize( nICS_BS ); remoteSystem->myExternalPlayerId = bsPlayerId; // Send this info down to the game packet = packetPool.GetPointer(); packet->data = ( unsigned char* ) data; packet->length = byteSize; packet->bitSize = bitSize; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) remoteSystemIndex; #ifdef _DEBUG assert( packet->data ); #endif incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); } else delete [] data; } else if ( (unsigned char) data[ 0 ] == ID_CONNECTED_PONG && byteSize == sizeof(unsigned char)+sizeof(unsigned int)*2 ) { unsigned int sendPingTime, sendPongTime; // Copy into the ping times array the current time - the value returned // First extract the sent ping RakNet::BitStream inBitStream( data, byteSize, false ); //PingStruct ps; //ps.Deserialize(psBS); inBitStream.IgnoreBits(8); inBitStream.Read(sendPingTime); inBitStream.Read(sendPongTime); time = RakNet::GetTime(); // Update the time value to be accurate ping = time - sendPingTime; lastPing = remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime; // Ignore super high spikes in the average if ( lastPing <= 0 || ( ( ( int ) ping < ( lastPing * 3 ) ) && ping < 1200 ) ) { remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime = ( short ) ping; // Thanks to Chris Taylor (cat02e@fsu.edu) for the improved timestamping algorithm remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].clockDifferential = sendPongTime - ( time + sendPingTime ) / 2; if ( remoteSystem->lowestPing == -1 || remoteSystem->lowestPing > ping ) remoteSystem->lowestPing = ping; // Most packets should arrive by the ping time. remoteSystem->reliabilityLayer.SetLostPacketResendDelay( ping * 2 ); if ( ++( remoteSystem->pingAndClockDifferentialWriteIndex ) == PING_TIMES_ARRAY_SIZE ) remoteSystem->pingAndClockDifferentialWriteIndex = 0; } delete [] data; } else if ( (unsigned char)data[0] == ID_CONNECTED_PING && byteSize == sizeof(unsigned char)+sizeof(unsigned int) ) { RakNet::BitStream inBitStream( data, byteSize, false ); inBitStream.IgnoreBits(8); unsigned int sendPingTime; inBitStream.Read(sendPingTime); if ((unsigned char)(data)[0] == ID_CONNECTED_PING) { RakNet::BitStream outBitStream; outBitStream.Write((unsigned char)ID_CONNECTED_PONG); outBitStream.Write(sendPingTime); time = RakNet::GetTime(); outBitStream.Write((unsigned int)time); SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, UNRELIABLE, 0, playerId, false, false, time ); } delete [] data; } else if ( (unsigned char) data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) { packet = packetPool.GetPointer(); if ( remoteSystem->staticData.GetNumberOfBytesUsed() > 0 ) { packet->data = new unsigned char [ sizeof( char ) + remoteSystem->staticData.GetNumberOfBytesUsed() ]; packet->data[ 0 ] = ID_DISCONNECTION_NOTIFICATION; memcpy( packet->data + sizeof( char ), remoteSystem->staticData.GetData(), remoteSystem->staticData.GetNumberOfBytesUsed() ); packet->length = sizeof( char ) + remoteSystem->staticData.GetNumberOfBytesUsed(); packet->bitSize = sizeof( char ) * 8 + remoteSystem->staticData.GetNumberOfBitsUsed(); delete [] data; } else { packet->data = ( unsigned char* ) data; packet->bitSize = 8; packet->length = 1; } packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) remoteSystemIndex; // We shouldn't close the connection immediately because we need to ack the ID_DISCONNECTION_NOTIFICATION remoteSystem->connectMode=RemoteSystemStruct::DISCONNECT_ASAP; //CloseConnectionInternal( playerId, false, true ); #ifdef _DEBUG assert( packet->data ); #endif // Relay this message to the game incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); } else if ( (unsigned char) data[ 0 ] == ID_RPC_MAPPING ) { /// RPC ASSERT assert( 0 ); delete [] data; } else if ( (unsigned char) data[ 0 ] == ID_REQUEST_STATIC_DATA ) { SendStaticDataInternal( playerId, true ); delete [] data; } else if ( (unsigned char) data[ 0 ] == ID_RECEIVED_STATIC_DATA ) { remoteSystem->staticData.Reset(); remoteSystem->staticData.Write( ( char* ) data + sizeof(unsigned char), byteSize - 1 ); // Inform game server code that we got static data packet = packetPool.GetPointer(); packet->data = ( unsigned char* ) data; packet->length = byteSize; packet->bitSize = bitSize; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) remoteSystemIndex; #ifdef _DEBUG assert( packet->data ); #endif incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); } else if ( (unsigned char)(data)[0] == ID_SECURED_CONNECTION_RESPONSE && byteSize == 1 + sizeof( big::u32 ) + sizeof( RSA_BIT_SIZE ) + 20 ) { SecuredConnectionConfirmation( remoteSystem, data ); delete [] data; } else if ( (unsigned char)(data)[0] == ID_SECURED_CONNECTION_CONFIRMATION && byteSize == 1 + 20 + sizeof( RSA_BIT_SIZE ) ) { CSHA1 sha1; bool confirmedHash, newRandNumber; confirmedHash = false; // Hash the SYN-Cookie // s2c syn-cookie = SHA1_HASH(source ip address + source port + random number) sha1.Reset(); sha1.Update( ( unsigned char* ) & playerId.binaryAddress, sizeof( playerId.binaryAddress ) ); sha1.Update( ( unsigned char* ) & playerId.port, sizeof( playerId.port ) ); sha1.Update( ( unsigned char* ) & ( newRandomNumber ), 20 ); sha1.Final(); newRandNumber = false; // Confirm if //syn-cookie ?= HASH(source ip address + source port + last random number) //syn-cookie ?= HASH(source ip address + source port + current random number) if ( memcmp( sha1.GetHash(), data + 1, 20 ) == 0 ) { confirmedHash = true; newRandNumber = true; } else if ( randomNumberExpirationTime < RakNet::GetTime() ) { sha1.Reset(); sha1.Update( ( unsigned char* ) & playerId.binaryAddress, sizeof( playerId.binaryAddress ) ); sha1.Update( ( unsigned char* ) & playerId.port, sizeof( playerId.port ) ); sha1.Update( ( unsigned char* ) & ( oldRandomNumber ), 20 ); sha1.Final(); if ( memcmp( sha1.GetHash(), data + 1, 20 ) == 0 ) confirmedHash = true; } if ( confirmedHash ) { int i; unsigned char AESKey[ 16 ]; RSA_BIT_SIZE message, encryptedMessage; // On connection accept, AES key is c2s RSA_Decrypt(random number) XOR s2c syn-cookie // Get the random number first #ifdef HOST_ENDIAN_IS_BIG BSWAPCPY( (unsigned char *) encryptedMessage, (unsigned char *)(data + 1 + 20), sizeof( RSA_BIT_SIZE ) ); #else memcpy( encryptedMessage, data + 1 + 20, sizeof( RSA_BIT_SIZE ) ); #endif rsacrypt.decrypt( encryptedMessage, message ); #ifdef HOST_ENDIAN_IS_BIG BSWAPSELF( (unsigned char *) message, sizeof( RSA_BIT_SIZE ) ); #endif // Save the AES key for ( i = 0; i < 16; i++ ) AESKey[ i ] = data[ 1 + i ] ^ ( ( unsigned char* ) ( message ) ) [ i ]; // Connect this player assuming we have open slots OnConnectionRequest( remoteSystem, AESKey, true ); // Invalidate the new random number if ( newRandomNumber ) GenerateSYNCookieRandomNumber(); } delete [] data; } else if ( (unsigned char)(data)[0] == ID_KEEPALIVE && byteSize == sizeof(unsigned char) ) { // Do nothing delete [] data; } else if ( (unsigned char)(data)[0] == ID_CONNECTION_REQUEST_ACCEPTED && byteSize == sizeof(unsigned char)+sizeof(unsigned short)+sizeof(unsigned int)+sizeof(unsigned short)+sizeof(PlayerIndex) ) { // Make sure this connection accept is from someone we wanted to connect to bool allowConnection, alreadyConnected; if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST || remoteSystem->connectMode==RemoteSystemStruct::REQUESTED_CONNECTION || allowConnectionResponseIPMigration) allowConnection=true; else allowConnection=false; if (remoteSystem->connectMode==RemoteSystemStruct::HANDLING_CONNECTION_REQUEST) alreadyConnected=true; else alreadyConnected=false; if ( allowConnection ) { if (alreadyConnected==false) { unsigned short remotePort; PlayerID externalID; PlayerIndex playerIndex; RakNet::BitStream inBitStream(data, byteSize, false); inBitStream.IgnoreBits(8); // ID_CONNECTION_REQUEST_ACCEPTED inBitStream.Read(remotePort); inBitStream.Read(externalID.binaryAddress); inBitStream.Read(externalID.port); inBitStream.Read(playerIndex); // Find a free remote system struct to use // RakNet::BitStream casBitS(data, byteSize, false); // ConnectionAcceptStruct cas; // cas.Deserialize(casBitS); playerId.port = remotePort; remoteSystem->connectMode=RemoteSystemStruct::CONNECTED; // The remote system told us our external IP, so save it remoteSystem->myExternalPlayerId = externalID; #ifdef __USE_IO_COMPLETION_PORTS bool b; // Create a new nonblocking socket remoteSystem->reliabilityLayer.SetSocket( SocketLayer::Instance()->CreateBoundSocket( myPlayerId.port, false ) ); SocketLayer::Instance()->Connect( remoteSystem->reliabilityLayer.GetSocket(), playerId.binaryAddress, playerId.port ); // Associate our new socket with a completion port and do the first read b = SocketLayer::Instance()->AssociateSocketWithCompletionPortAndRead( remoteSystem->reliabilityLayer.GetSocket(), playerId.binaryAddress, playerId.port, rakPeer ); //client->//reliabilityLayerMutex.Unlock(); if ( b == false ) // Some damn completion port error... windows is so unreliable { #ifdef _DO_PRINTF printf( "RakClient - AssociateSocketWithCompletionPortAndRead failed" ); #endif return ; } #endif // Use the stored encryption key if (remoteSystem->setAESKey) remoteSystem->reliabilityLayer.SetEncryptionKey( remoteSystem->AESKey ); else remoteSystem->reliabilityLayer.SetEncryptionKey( 0 ); } // Send the connection request complete to the game Packet *packet = packetPool.GetPointer(); //packet->data = new unsigned char[ byteSize ]; //memcpy( packet->data, data, byteSize ); packet->data=(unsigned char*)data; // packet->data[0]=ID_CONNECTION_REQUEST_ACCEPTED; packet->length = byteSize; packet->bitSize = byteSize * 8; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) GetIndexFromPlayerID( playerId ); #ifdef _DEBUG assert( packet->data ); #endif incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); RakNet::BitStream outBitStream(sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned short)); outBitStream.Write((unsigned char)ID_NEW_INCOMING_CONNECTION); outBitStream.Write(playerId.binaryAddress); outBitStream.Write(playerId.port); // We turned on encryption with SetEncryptionKey. This pads packets to up to 16 bytes. // As soon as a 16 byte packet arrives on the remote system, we will turn on AES. This works because all encrypted packets are multiples of 16 and the // packets I happen to be sending before this are less than 16 bytes. Otherwise there is no way to know if a packet that arrived is // encrypted or not so the other side won't know to turn on encryption or not. SendImmediate( (char*)outBitStream.GetData(), outBitStream.GetNumberOfBitsUsed(), SYSTEM_PRIORITY, RELIABLE, 0, playerId, false, false, RakNet::GetTime() ); if (alreadyConnected==false) { PingInternal( playerId, true ); SendStaticDataInternal( playerId, true ); } } else { // Tell the remote system the connection failed NotifyAndFlagForDisconnect(playerId, true); #ifdef _DO_PRINTF printf( "Error: Got a connection accept when we didn't request the connection.\n" ); #endif delete [] data; } } else { packet = packetPool.GetPointer(); packet->data = ( unsigned char* ) data; packet->length = byteSize; packet->bitSize = bitSize; packet->playerId = playerId; packet->playerIndex = ( PlayerIndex ) remoteSystemIndex; #ifdef _DEBUG assert( packet->data ); #endif incomingQueueMutex.Lock(); incomingPacketQueue.push( packet ); incomingQueueMutex.Unlock(); } } // Does the reliability layer have any more packets waiting for us? // To be thread safe, this has to be called in the same thread as HandleSocketReceiveFromConnectedPlayer bitSize = remoteSystem->reliabilityLayer.Receive( &data ); } } } return true; } // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- #ifdef _WIN32 unsigned __stdcall UpdateNetworkLoop( LPVOID arguments ) #else void* UpdateNetworkLoop( void* arguments ) #endif { RakPeer * rakPeer = ( RakPeer * ) arguments; // unsigned int time; #ifdef __USE_IO_COMPLETION_PORTS AsynchronousFileIO::Instance()->IncreaseUserCount(); #endif // 11/15/05 - this is slower than Sleep() /* #ifdef _WIN32 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) // Lets see if these timers give better performance than Sleep HANDLE timerHandle; LARGE_INTEGER dueTime; if ( rakPeer->threadSleepTimer == 0 ) rakPeer->threadSleepTimer = 1; // 2nd parameter of false means synchronization timer instead of manual-reset timer timerHandle = CreateWaitableTimer( NULL, FALSE, 0 ); assert( timerHandle ); dueTime.QuadPart = -10000 * rakPeer->threadSleepTimer; // 10000 is 1 ms? BOOL success = SetWaitableTimer( timerHandle, &dueTime, rakPeer->threadSleepTimer, NULL, NULL, FALSE ); assert( success ); #endif #endif */ rakPeer->isMainLoopThreadActive = true; while ( rakPeer->endThreads == false ) { /* time=RakNet::GetTime(); // Dynamic threading - how long we sleep and if we update // depends on whether or not the user thread is updating if (time > rakPeer->lastUserUpdateCycle && time - rakPeer->lastUserUpdateCycle > UPDATE_THREAD_UPDATE_TIME) { // Only one thread should call RunUpdateCycle at a time. We don't need to delay calls so // a mutex on the function is not necessary - only on the variable that indicates if the function is // running rakPeer->RunMutexedUpdateCycle(); // User is not updating the network. Sleep a short time #ifdef _WIN32 Sleep(rakPeer->threadSleepTimer); #else usleep(rakPeer->threadSleepTimer * 1000); #endif } else { // User is actively updating the network. Only occasionally poll #ifdef _WIN32 Sleep(UPDATE_THREAD_POLL_TIME); #else usleep(UPDATE_THREAD_POLL_TIME * 1000); #endif } */ rakPeer->RunUpdateCycle(); #ifdef _WIN32 //#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) #if (0) // 08/05/05 This doesn't seem to work well at all! //#pragma message("-- RakNet:Using WaitForSingleObject --") if ( WaitForSingleObject( timerHandle, INFINITE ) != WAIT_OBJECT_0 ) { #ifdef _DEBUG assert( 0 ); #ifdef _DO_PRINTF printf( "WaitForSingleObject failed (%d)\n", GetLastError() ); #endif #endif } #else //#pragma message("-- RakNet:Using Sleep --") //#pragma message("-- Define _WIN32_WINNT as 0x0400 or higher to use WaitForSingleObject --") Sleep( rakPeer->threadSleepTimer ); #endif #else usleep( rakPeer->threadSleepTimer * 1000 ); #endif } rakPeer->isMainLoopThreadActive = false; #ifdef __USE_IO_COMPLETION_PORTS AsynchronousFileIO::Instance()->DecreaseUserCount(); #endif /* #ifdef _WIN32 #if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400) CloseHandle( timerHandle ); #endif #endif */ return 0; } blobby-1.0rc3/src/raknet/BigTypes.h0000644000175000017500000010561212042452367020527 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file BigTypes.h * @brief Provide Big Integer class * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * * (128)2^7-bit to (32768)2^14-bit signed 2's complement & unsigned extended arithmetic * * catid(cat02e@fsu.edu) * * 7/30/2004 Fixed VS6 compat * 7/28/2004 Fixed macros so they can be used outside of the big namespace * Now using pre-processor definitions from types.h for inline assembly * 7/26/2004 Removed a lot of assembly, made add/sub assembly optional * 7/25/2004 Merged the wrapper class Int from older code * 7/24/2004 Replaced trivial assembly code with std:: functions * Refined some assembly code with Art of Assembly chapter 9 * Added binary ops * 7/23/2004 Finished assembly coding * Removed Int class, for now * Added old C++ code back in with USEASSEMBLY * 7/22/2004 Signed arithmetic (needed for ext. Euclidean algo) * Cleaned up coding style * Began rewriting parts in assembly * 7/21/2004 Began writing * * Tabs: 4 spaces * Dist: public */ #ifndef BIGTYPES_H #define BIGTYPES_H #include "Types.h" //#define BIG_USES_STRINGS /* undefining this means you cannot convert bigs to strings or from strings */ #ifdef BIG_USES_STRINGS # include #endif /** * @brief Big Data Type Definition * * Provide Big Data Type functionnalities for the RakNet Libraries */ namespace big { using namespace cat; //// basic definitions //// // word size typedef u32 word; // assembly implementation is for 32-bit word size const u32 WORDBITS = sizeof( word ) * 8; const u32 HALFWORDBITS = sizeof( word ) * 8 / 2; const word WORDHIGHBIT = ( word ) 1 << ( WORDBITS - 1 ); const word WORDALLBITS = ( word ) 0 - 1; const word WORDLOBITS = ( ( word ) 1 << HALFWORDBITS ) - 1; const word WORDHIBITS = WORDALLBITS ^ WORDLOBITS; #define BIGHIGHBIT(n) ((n)[sizeof(n) / sizeof(big::word) - 1] & WORDHIGHBIT) // template operator parameter modes #define BIGONETYPE template /* supports only one class */ #define BIGTWOTYPES template /* sizeof Bigger >= sizeof T */ #define BIGSMALLTYPE template /* sizeof self >= sizeof Smaller */ //// big types //// #define BIGWORDCOUNT_FROMBITCOUNT(bits) ((bits) / 8 / sizeof(big::word)) #define BIGWORDCOUNT(T) (sizeof(T) / sizeof(big::word)) #define BIGBITCOUNT(T) (sizeof(T) * 8) // low words -- [0] < [1] < [2] < [3] -- high words typedef word u128[ BIGWORDCOUNT_FROMBITCOUNT( 128 ) ]; typedef word u256[ BIGWORDCOUNT_FROMBITCOUNT( 256 ) ]; typedef word u512[ BIGWORDCOUNT_FROMBITCOUNT( 512 ) ]; typedef word u1024[ BIGWORDCOUNT_FROMBITCOUNT( 1024 ) ]; typedef word u2048[ BIGWORDCOUNT_FROMBITCOUNT( 2048 ) ]; typedef word u4096[ BIGWORDCOUNT_FROMBITCOUNT( 4096 ) ]; typedef word u8192[ BIGWORDCOUNT_FROMBITCOUNT( 8192 ) ]; typedef word u16384[ BIGWORDCOUNT_FROMBITCOUNT( 16384 ) ]; typedef word u32768[ BIGWORDCOUNT_FROMBITCOUNT( 32768 ) ]; // use these macros to create temporary variables when // those variables are to be twice/half the size of another // variable of varying size. #define BIGDOUBLESIZE(T, var_name) big::word (var_name)[BIGWORDCOUNT(T) * 2] /* WARNING: invalid w/ u32768 */ #define BIGHALFSIZE(T, var_name) big::word (var_name)[BIGWORDCOUNT(T) / 2] /* WARNING: invalid w/ u128 */ //// library summary //// // assignment BIGONETYPE INLINE void zero( T &n ); // n = 0 BIGONETYPE INLINE void usetw( T &a, word b ); // a = b, zero-extend BIGONETYPE INLINE void ssetw( T &a, word b ); // a = b, sign-extend BIGONETYPE INLINE void set ( T &a, T &b ) ; // a = b BIGTWOTYPES INLINE void usetlow( Bigger &a, T &b ); // a_low = b (zero-extend) BIGTWOTYPES INLINE void ssetlow( Bigger &a, T &b ); // a_low = b (sign-extend) BIGTWOTYPES INLINE void sethigh( Bigger &a, T &b ); // a_high = b BIGTWOTYPES INLINE void takelow( T &a, Bigger &b ); // a = b_low BIGTWOTYPES INLINE void takehigh( T &a, Bigger &b ); // a = b_high // comparison BIGONETYPE bool ugreater( T &a, T &b ); // a > b (unsigned) BIGONETYPE bool ugreaterOrEqual( T &a, T &b ); // a >= b (unsigned) BIGONETYPE bool sgreater( T &a, T &b ); // a > b (signed) BIGONETYPE bool sgreaterOrEqual( T &a, T &b ); // a >= b (signed) BIGONETYPE INLINE bool equal( T &a, T &b ); // a == b BIGONETYPE INLINE bool isZero( T &n ); // a == 0 // binary BIGONETYPE void bAND( T &a, T &b ); // a &= b BIGONETYPE void bOR( T &a, T &b ); // a |= b BIGONETYPE void bXOR( T &a, T &b ); // a ^= b BIGONETYPE void bNOT( T &n ); // n = ~n // shifting BIGONETYPE void shiftLeft1( T &n ); // n <<= 1 BIGONETYPE void shiftLeft( T &n, u32 s ); // n <<= s (s <= WORDBITS) BIGONETYPE void ushiftRight1( T &n ); // n >>= 1 (unsigned) BIGONETYPE void ushiftRight( T &n, u32 s ); // n >>= s (unsigned) (s <= WORDBITS) BIGONETYPE void sshiftRight1( T &n ); // n >>= 1 (signed) BIGONETYPE void sshiftRight( T &n, u32 s ); // n >>= s (signed) (s <= WORDBITS) // addition/subtraction BIGONETYPE void add ( T &a, T &b ) ; // a += b BIGONETYPE void increment( T &n ); // ++n BIGONETYPE void subtract( T &a, T &b ); // a -= b BIGONETYPE void decrement( T &n ); // --n // negation BIGONETYPE void negate( T &n ); // n = -n // multiplication BIGONETYPE void usquare( T &a ); // a *= a, signed BIGTWOTYPES void umultiply( T &a, T &b, Bigger &m ); // m = a * b (&a != &b != &m), unsigned BIGTWOTYPES void umultiply( Bigger &a, T &b ); // a *= b (&a != &b), unsigned BIGONETYPE void ssquare( T &a ); // a *= a, signed BIGTWOTYPES void smultiply( T &a, T &b, Bigger &m ); // m = a * b (&a != &b != &m), signed BIGTWOTYPES void smultiply( Bigger &a, T &b ); // a *= b (&a != &b), signed // division/remainder BIGONETYPE void udivide( T &a, T &b, T &q, T &r ); // {q, r} = a / b (&q != &r), unsigned BIGONETYPE void umodulo( T &a, T &b, T &r ); // r = a Mod b, unsigned BIGONETYPE void sdivide( T &a, T &b, T &q, T &r ); // {q, r} = a / b (&q != &r), signed BIGONETYPE void smodulo( T &a, T &b, T &r ); // r = a Mod b, signed #ifdef BIG_USES_STRINGS // converting to/from strings BIGONETYPE std::string toString( T &n, bool sign, u16 radix ); // n -> string BIGONETYPE void fromString( std::string s, T &n, bool sign, u16 radix ); // s -> n #endif //////// wrapper class //////// #define BIGINTFAST INLINE Int & /* operation is done to self, returns itself */ #define BIGINTSLOW Int /* new object is created and returned */ BIGONETYPE class Int { protected: T raw; public: operator T &(); // automatic casting to T: you may use BigInt classes as parameters to the functions public: Int(); Int( word n ); #ifdef BIG_USES_STRINGS Int( std::string &s ); #endif Int( T &n ); public: BIGINTFAST zero(); BIGINTFAST operator=( word n ); BIGINTFAST operator=( T &n ); public: BIGINTFAST operator<<=( u32 s ); BIGINTSLOW operator<<( u32 s ); BIGINTFAST operator>>=( u32 s ); BIGINTSLOW operator>>( u32 s ); public: BIGINTFAST operator+=( T &n ); BIGINTSLOW operator+( T &n ); BIGINTFAST operator-=( T &n ); BIGINTSLOW operator-( T &n ); BIGINTFAST operator++(); // prefix BIGINTSLOW operator++( int ); // postfix BIGINTFAST operator--(); // prefix BIGINTSLOW operator--( int ); // postfix public: BIGINTSLOW operator-( int ); // negation public: BIGSMALLTYPE BIGINTFAST operator*=( Smaller &n ) { smultiply( raw, n ); return *this; } BIGINTSLOW operator*( T &n ); BIGINTFAST square(); public: BIGINTFAST operator/=( T &n ); BIGINTSLOW operator/( T &n ); BIGINTFAST operator%=( T &n ); BIGINTSLOW operator%( T &n ); public: /* fast */ bool operator>( T &n ); /* fast */ bool operator>=( T &n ); /* fast */ bool operator<( T &n ); /* fast */ bool operator<=( T &n ); /* fast */ bool operator==( T &n ); /* fast */ bool operator!=( T &n ); /* fast */ bool operator!(); public: #ifdef BIG_USES_STRINGS /* fast */ std::string str(); BIGINTFAST operator=( std::string &s ); BIGINTFAST operator=( const char *s ); #endif }; //////// assignment //////// // n = 0 BIGONETYPE INLINE void zero( T &n ) { memset( n, 0, sizeof( T ) ); } // a = b, zero-extend BIGONETYPE INLINE void usetw( T &a, word b ) { a[ 0 ] = b; memset( a + 1, 0, sizeof( T ) - sizeof( word ) ); } // a = b, sign-extend BIGONETYPE INLINE void ssetw( T &a, word b ) { a[ 0 ] = b; memset( a + 1, ( b & WORDHIGHBIT ) ? WORDALLBITS : 0, sizeof( T ) - sizeof( word ) ); } // a = b BIGONETYPE INLINE void set ( T &a, T &b ) { memcpy( a, b, sizeof( T ) ); } // a_low = b (zero-extend) BIGTWOTYPES INLINE void usetlow( Bigger &a, T &b ) { memcpy( a, b, sizeof( T ) ); memset( a + BIGWORDCOUNT( T ), 0, sizeof( Bigger ) - sizeof( T ) ); } // a_low = b (sign-extend) BIGTWOTYPES INLINE void ssetlow( Bigger &a, T &b ) { memcpy( a, b, sizeof( T ) ); memset( a + BIGWORDCOUNT( T ), BIGHIGHBIT( b ) ? WORDALLBITS : 0, sizeof( Bigger ) - sizeof( T ) ); } // a_high = b BIGTWOTYPES INLINE void sethigh( Bigger &a, T &b ) { memcpy( a + BIGWORDCOUNT( Bigger ) - BIGWORDCOUNT( T ), b, sizeof( T ) ); memset( a, 0, sizeof( Bigger ) - sizeof( T ) ); } // a = b_low BIGTWOTYPES INLINE void takelow( T &a, Bigger &b ) { memcpy( a, b, sizeof( T ) ); } // a = b_high BIGTWOTYPES INLINE void takehigh( T &a, Bigger &b ) { memcpy( a, b + BIGWORDCOUNT( Bigger ) - BIGWORDCOUNT( T ), sizeof( T ) ); } //////// comparison //////// // a > b BIGONETYPE bool ugreater( T &a, T &b ) { for ( s32 ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { if ( a[ ii ] > b[ ii ] ) return true; if ( a[ ii ] < b[ ii ] ) return false; } return false; } // a >= b BIGONETYPE bool ugreaterOrEqual( T &a, T &b ) { for ( s32 ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { if ( a[ ii ] > b[ ii ] ) return true; if ( a[ ii ] < b[ ii ] ) return false; } return true; } // a > b BIGONETYPE bool sgreater( T &a, T &b ) { for ( s32 ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { if ( a[ ii ] > b[ ii ] ) return BIGHIGHBIT( a ) == 0; if ( a[ ii ] < b[ ii ] ) return BIGHIGHBIT( b ) != 0; } return false; } // a >= b BIGONETYPE bool sgreaterOrEqual( T &a, T &b ) { for ( s32 ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { if ( a[ ii ] > b[ ii ] ) return BIGHIGHBIT( a ) == 0; if ( a[ ii ] < b[ ii ] ) return BIGHIGHBIT( b ) != 0; } return true; } // a == b BIGONETYPE INLINE bool equal( T &a, T &b ) { return memcmp( a, b, sizeof( T ) ) == 0; } // a == 0 BIGONETYPE INLINE bool isZero( T &n ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) if ( n[ ii ] ) return false; return true; } //////// binary //////// // a &= b BIGONETYPE void bAND( T &a, T &b ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) a[ ii ] &= b[ ii ]; } // a |= b BIGONETYPE void bOR( T &a, T &b ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) a[ ii ] |= b[ ii ]; } // a ^= b BIGONETYPE void bXOR( T &a, T &b ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) a[ ii ] ^= b[ ii ]; } // n = ~n BIGONETYPE void bNOT( T &n ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) n[ ii ] = ~n[ ii ]; } //////// shifting //////// // n <<= 1 BIGONETYPE void shiftLeft1( T &n ) { register word w_i, carry = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { w_i = n[ ii ]; n[ ii ] = ( w_i << 1 ) | carry; carry = w_i >> ( WORDBITS - 1 ); } } // n <<= s (s <= WORDBITS) BIGONETYPE void shiftLeft( T &n, u32 s ) { register s32 ii; register u32 bases = s / WORDBITS; u32 bits = s % WORDBITS; // move whole bases first if ( bases ) { // shift bases for ( ii = BIGWORDCOUNT( T ) - 1 - bases; ii >= 0; --ii ) n[ ii + bases ] = n[ ii ]; // clear the original locii of those bases memset( n, 0, bases * sizeof( word ) ); } if ( bits ) { register word w_i, carry = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { w_i = n[ ii ]; n[ ii ] = ( w_i << bits ) | carry; carry = w_i >> ( WORDBITS - bits ); } } } // n >>= 1 (unsigned) BIGONETYPE void ushiftRight1( T &n ) { register word w_i, carry = 0; for ( s32 ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { w_i = n[ ii ]; n[ ii ] = carry | ( w_i >> 1 ); carry = w_i << ( WORDBITS - 1 ); } } // n >>= s (unsigned) (s <= WORDBITS) BIGONETYPE void ushiftRight( T &n, u32 s ) { register s32 ii; register u32 bases = s / WORDBITS; register u32 bits = s % WORDBITS; // move whole bases first if ( bases ) { // shift bases for ( ii = bases; ii < BIGWORDCOUNT( T ); ++ii ) n[ ii - bases ] = n[ ii ]; // clear the original locii of those bases memset( n + BIGWORDCOUNT( T ) - bases, 0, bases * sizeof( word ) ); } if ( bits ) { register word w_i, carry = 0; for ( ii = BIGWORDCOUNT( T ) - 1 - bases; ii >= 0; --ii ) { w_i = n[ ii ]; n[ ii ] = carry | ( w_i >> bits ); carry = w_i << ( WORDBITS - bits ); } } } // n >>= 1 (signed) BIGONETYPE void sshiftRight1( T &n ) { register word w_i, carry = BIGHIGHBIT( n ) ? 1 : 0; for ( s32 ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { w_i = n[ ii ]; n[ ii ] = carry | ( w_i >> 1 ); carry = w_i << ( WORDBITS - 1 ); } } // n >>= s (signed) (s <= WORDBITS) BIGONETYPE void sshiftRight( T &n, u32 s ) { register s32 ii; register u32 bases = s / WORDBITS; register u32 bits = s % WORDBITS; word filler = BIGHIGHBIT( n ) ? WORDALLBITS : 0; // move whole bases first if ( bases ) { // shift bases for ( ii = bases; ii < BIGWORDCOUNT( T ); ++ii ) n[ ii - bases ] = n[ ii ]; // clear the original locii of those bases memset( n + BIGWORDCOUNT( T ) - bases, filler, bases * sizeof( word ) ); } if ( bits ) { register word w_i, carry = filler << ( WORDBITS - bits ); for ( ii = BIGWORDCOUNT( T ) - 1 - bases; ii >= 0; --ii ) { w_i = n[ ii ]; n[ ii ] = carry | ( w_i >> bits ); carry = w_i << ( WORDBITS - bits ); } } } //////// addition/subtraction //////// #if defined(NO_TEMPLATE_INLINE_ASSEMBLY) && defined(ASSEMBLY_INTEL_SYNTAX) void BorlandAdd( void *a, void *b, u32 c ) { ASSEMBLY_BLOCK // BorlandC, x86, 32-bit words { mov esi, b mov edi, a mov ecx, c // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi ] xor edx, edx // edx used later to index later words >= 4 add [ edi ] , eax // and now we can use ADD instead of ADC on the first addition mov eax, [ esi + 4 ] adc [ edi + 4 ], eax mov eax, [ esi + 8 ] adc [ edi + 8 ], eax mov eax, [ esi + 12 ] adc [ edi + 12 ], eax jecxz done_already next_word: inc edx inc edx // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi + edx * 8 ] adc [ edi + edx * 8 ], eax mov eax, [ esi + edx * 8 + 4 ] adc [ edi + edx * 8 + 4 ], eax mov eax, [ esi + edx * 8 + 8 ] adc [ edi + edx * 8 + 8 ], eax mov eax, [ esi + edx * 8 + 12 ] adc [ edi + edx * 8 + 12 ], eax loop next_word done_already: } } #endif // a += b BIGONETYPE void add ( T &a, T &b ) { #if defined(NO_TEMPLATE_INLINE_ASSEMBLY) && defined(ASSEMBLY_INTEL_SYNTAX) const u32 qc1 = BIGWORDCOUNT( T ) / 4 - 1; BorlandAdd( a, b, qc1 ); #elif defined(ASSEMBLY_INTEL_SYNTAX) const u32 qc1 = BIGWORDCOUNT( T ) / 4 - 1; ASSEMBLY_BLOCK // VS.NET, x86, 32-bit words { mov esi, b mov edi, a mov ecx, qc1 // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi ] xor edx, edx // edx used later to index later words >= 4 add [ edi ], eax // and now we can use ADD instead of ADC on the first addition mov eax, [ esi + 4 ] adc [ edi + 4 ], eax mov eax, [ esi + 8 ] adc [ edi + 8 ], eax mov eax, [ esi + 12 ] adc [ edi + 12 ], eax jecxz done_already next_word: inc edx inc edx // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi + edx * 8 ] adc [ edi + edx * 8 ], eax mov eax, [ esi + edx * 8 + 4 ] adc [ edi + edx * 8 + 4 ], eax mov eax, [ esi + edx * 8 + 8 ] adc [ edi + edx * 8 + 8 ], eax mov eax, [ esi + edx * 8 + 12 ] adc [ edi + edx * 8 + 12 ], eax loop next_word done_already: } #else register word carry = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word a_i = a[ ii ]; word b_i = b[ ii ]; a[ ii ] += b_i + carry; carry = ( ( a_i & ( WORDALLBITS >> 1 ) ) + ( b_i & ( WORDALLBITS >> 1 ) ) + carry ) >> ( WORDBITS - 1 ); carry += ( a_i >> ( WORDBITS - 1 ) ) + ( b_i >> ( WORDBITS - 1 ) ); carry >>= 1; } #endif } // ++n BIGONETYPE void increment( T &n ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) if ( ++n[ ii ] ) break; } #if defined(NO_TEMPLATE_INLINE_ASSEMBLY) && defined(ASSEMBLY_INTEL_SYNTAX) void BorlandSubtract( void *a, void *b, u32 c ) { ASSEMBLY_BLOCK // BorlandC, x86, 32-bit words { mov esi, b mov edi, a mov ecx, c // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi ] xor edx, edx // edx used later to index later words >= 4 add [ edi ] , eax // and now we can use ADD instead of ADC on the first addition mov eax, [ esi + 4 ] adc [ edi + 4 ], eax mov eax, [ esi + 8 ] adc [ edi + 8 ], eax mov eax, [ esi + 12 ] adc [ edi + 12 ], eax jecxz done_already next_word: inc edx inc edx // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi + edx * 8 ] adc [ edi + edx * 8 ], eax mov eax, [ esi + edx * 8 + 4 ] adc [ edi + edx * 8 + 4 ], eax mov eax, [ esi + edx * 8 + 8 ] adc [ edi + edx * 8 + 8 ], eax mov eax, [ esi + edx * 8 + 12 ] adc [ edi + edx * 8 + 12 ], eax loop next_word done_already: } } #endif // a -= b BIGONETYPE void subtract( T &a, T &b ) { #if defined(NO_TEMPLATE_INLINE_ASSEMBLY) && defined(ASSEMBLY_INTEL_SYNTAX) const u32 qc1 = BIGWORDCOUNT( T ) / 4 - 1; BorlandSubtract( a, b, qc1 ); #elif defined(ASSEMBLY_INTEL_SYNTAX) const u32 qc1 = BIGWORDCOUNT( T ) / 4 - 1; ASSEMBLY_BLOCK // VS.NET, x86, 32-bit words { mov esi, b mov edi, a mov ecx, qc1 // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi ] xor edx, edx // edx used later to index later words >= 4 sub [ edi ], eax // first subtraction doesn't need to borrow mov eax, [ esi + 4 ] sbb [ edi + 4 ], eax mov eax, [ esi + 8 ] sbb [ edi + 8 ], eax mov eax, [ esi + 12 ] sbb [ edi + 12 ], eax jecxz done_already next_word: inc edx inc edx // unrolled loop since word count is a multiple of 4 >= 4 mov eax, [ esi + edx * 8 ] sbb [ edi + edx * 8 ], eax mov eax, [ esi + edx * 8 + 4 ] sbb [ edi + edx * 8 + 4 ], eax mov eax, [ esi + edx * 8 + 8 ] sbb [ edi + edx * 8 + 8 ], eax mov eax, [ esi + edx * 8 + 12 ] sbb [ edi + edx * 8 + 12 ], eax loop next_word done_already: } #else register word borrow = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word a_i = a[ ii ]; word b_i = b[ ii ]; a[ ii ] -= b_i + borrow; borrow = ( ( a_i & ( WORDALLBITS >> 1 ) ) - ( b_i & ( WORDALLBITS >> 1 ) ) - borrow ) >> ( WORDBITS - 1 ); borrow += ( b_i >> ( WORDBITS - 1 ) ) - ( a_i >> ( WORDBITS - 1 ) ); ++borrow; borrow >>= 1; } #endif } // --n BIGONETYPE void decrement( T &n ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) if ( n[ ii ] -- ) break; } //////// negation //////// // n = -n BIGONETYPE void negate( T &n ) { for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) n[ ii ] = ~n[ ii ]; increment( n ); } //////// multiplication //////// // a *= a, unsigned BIGONETYPE void usquare( T &a ) { T a0, a1; set ( a0, a ) ; set ( a1, a ) ; zero( a ); u32 shifts = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word w_i = a0[ ii ]; u16 ctr = WORDBITS; while ( w_i ) { if ( w_i & 1 ) { if ( shifts ) { shiftLeft( a1, shifts ); shifts = 0; } add ( a, a1 ) ; } w_i >>= 1; ++shifts; --ctr; } shifts += ctr; } } // m = a * b (&a != &b != &m), unsigned BIGTWOTYPES void umultiply( T &a0, T &b0, Bigger &m ) { Bigger a; usetlow( a, a0 ); zero( m ); u32 shifts = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word w_i = b0[ ii ]; u16 ctr = WORDBITS; while ( w_i ) { if ( w_i & 1 ) { if ( shifts ) { shiftLeft( a, shifts ); shifts = 0; } add ( m, a ) ; } w_i >>= 1; ++shifts; --ctr; } shifts += ctr; } } // a *= b (&a != &b), unsigned BIGTWOTYPES void umultiply( Bigger &a0, T &b0 ) { Bigger a; set ( a, a0 ) ; zero( a0 ); u32 shifts = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word w_i = b0[ ii ]; u16 ctr = WORDBITS; while ( w_i ) { if ( w_i & 1 ) { if ( shifts ) { shiftLeft( a, shifts ); shifts = 0; } add ( a0, a ) ; } w_i >>= 1; ++shifts; --ctr; } shifts += ctr; } } // a *= a, signed BIGONETYPE void ssquare( T &a ) { T a0, a1; if ( BIGHIGHBIT( a ) ) negate( a ); set ( a0, a ) ; set ( a1, a ) ; zero( a ); u32 shifts = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word w_i = a0[ ii ]; u16 ctr = WORDBITS; while ( w_i ) { if ( w_i & 1 ) { if ( shifts ) { shiftLeft( a1, shifts ); shifts = 0; } add ( a, a1 ) ; } w_i >>= 1; ++shifts; --ctr; } shifts += ctr; } } // m = a * b (&a != &b != &m), signed BIGTWOTYPES void smultiply( T &a0, T &b0, Bigger &m ) { Bigger a; ssetlow( a, a0 ); word sign_a = BIGHIGHBIT( a ); if ( sign_a ) negate( a ); T b; set ( b, b0 ) ; word sign_b = BIGHIGHBIT( b ); if ( sign_b ) negate( b ); zero( m ); u32 shifts = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word w_i = b[ ii ]; u16 ctr = WORDBITS; while ( w_i ) { if ( w_i & 1 ) { if ( shifts ) { shiftLeft( a, shifts ); shifts = 0; } add ( m, a ) ; } w_i >>= 1; ++shifts; --ctr; } shifts += ctr; } if ( sign_a ^ sign_b ) negate( m ); } // a *= b (&a != &b), signed BIGTWOTYPES void smultiply( Bigger &a0, T &b0 ) { Bigger a; ssetlow( a, a0 ); word sign_a = BIGHIGHBIT( a ); if ( sign_a ) negate( a ); T b; set ( b, b0 ) ; word sign_b = BIGHIGHBIT( b ); if ( sign_b ) negate( b ); zero( a0 ); u32 shifts = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word w_i = b[ ii ]; u16 ctr = WORDBITS; while ( w_i ) { if ( w_i & 1 ) { if ( shifts ) { shiftLeft( a, shifts ); shifts = 0; } add ( a0, a ) ; } w_i >>= 1; ++shifts; --ctr; } shifts += ctr; } if ( sign_a ^ sign_b ) negate( a0 ); } //////// division/remainder //////// // {q, r} = a / b (&q != &r), unsigned BIGONETYPE void udivide( T &a, T &b0, T &q, T &r ) { T b; set ( b, b0 ) ; set ( r, a ) ; zero( q ); u32 shifts = 1; // sort of: shift b left until b > r, then shift back one if ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { s32 ii, jj; // shift by words if possible for ( ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) if ( r[ ii ] ) break; for ( jj = ii; jj >= 0; --jj ) if ( b[ jj ] ) break; if ( ii != jj ) { shifts = ( ii - jj ) * WORDBITS; shiftLeft( b, shifts ); ++shifts; } while ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { shiftLeft1( b ); ++shifts; } while ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } } else if ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } u32 qshifts = 0; while ( shifts-- ) { ++qshifts; if ( !ugreater( b, r ) ) { subtract( r, b ); shiftLeft( q, qshifts ); qshifts = 0; q[ 0 ] |= 1; } ushiftRight1( b ); } shiftLeft( q, qshifts ); } // r = a Mod b, unsigned BIGONETYPE void umodulo( T &a, T &b0, T &r ) { T b; u32 shifts = 1; set ( b, b0 ) ; set ( r, a ) ; if ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { s32 ii, jj; // shift by words if possible for ( ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) if ( r[ ii ] ) break; for ( jj = ii; jj >= 0; --jj ) if ( b[ jj ] ) break; if ( ii != jj ) { shifts = ( ii - jj ) * WORDBITS; shiftLeft( b, shifts ); ++shifts; } while ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { shiftLeft1( b ); ++shifts; } while ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } } else if ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } while ( shifts-- ) { if ( !ugreater( b, r ) ) subtract( r, b ); ushiftRight1( b ); } } // {q, r} = a / b (&q != &r), signed BIGONETYPE void sdivide( T &a, T &b0, T &q, T &r ) { T b; set ( b, b0 ) ; set ( r, a ) ; zero( q ); word sign_a = BIGHIGHBIT( a ); if ( sign_a ) negate( r ); word sign_b = BIGHIGHBIT( b ); if ( sign_b ) negate( b ); u32 shifts = 1; if ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { s32 ii, jj; // shift by words if possible for ( ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) if ( r[ ii ] ) break; for ( jj = ii; jj >= 0; --jj ) if ( b[ jj ] ) break; if ( ii != jj ) { shifts = ( ii - jj ) * WORDBITS; shiftLeft( b, shifts ); ++shifts; } while ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { shiftLeft1( b ); ++shifts; } while ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } } else if ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } u32 qshifts = 0; while ( shifts-- ) { ++qshifts; if ( !ugreater( b, r ) ) { subtract( r, b ); shiftLeft( q, qshifts ); qshifts = 0; q[ 0 ] |= 1; } ushiftRight1( b ); } shiftLeft( q, qshifts ); if ( sign_a ^ sign_b ) negate( q ); if ( sign_a ) negate( r ); } // r = a Mod b, signed BIGONETYPE void smodulo( T &a, T &b0, T &r ) { T b; u32 shifts = 1; set ( b, b0 ) ; set ( r, a ) ; word sign_a = BIGHIGHBIT( a ); if ( sign_a ) negate( r ); word sign_b = BIGHIGHBIT( b ); if ( sign_b ) negate( b ); if ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { s32 ii, jj; // shift by words if possible for ( ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) if ( r[ ii ] ) break; for ( jj = ii; jj >= 0; --jj ) if ( b[ jj ] ) break; if ( ii != jj ) { shifts = ( ii - jj ) * WORDBITS; shiftLeft( b, shifts ); ++shifts; } while ( !BIGHIGHBIT( b ) && ugreater( r, b ) ) { shiftLeft1( b ); ++shifts; } while ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } } else if ( ugreater( b, r ) ) { ushiftRight1( b ); --shifts; } while ( shifts-- ) { if ( !ugreater( b, r ) ) subtract( r, b ); ushiftRight1( b ); } if ( sign_a ) negate( r ); } //////// converting to/from strings //////// #ifdef BIG_USES_STRINGS // n -> string BIGONETYPE std::string toString( T &n0, bool sign = true, u16 radix = 10 ) { T n, base, r; std::string s; set ( n, n0 ) ; usetw( base, radix ); word sign_n = 0; if ( sign && ( sign_n = BIGHIGHBIT( n ) ) ) negate( n ); do // always allow first iteration for zero { // {q, r} = n / base udivide( n, base, n, r ); char ch = ( char ) r[ 0 ]; if ( ch >= 10 ) ch += 'a' - 10; else ch += '0'; // insert character s = ch + s; } while ( !isZero( n ) ); if ( sign_n ) s = '-' + s; return s; } // s -> n, signed BIGONETYPE void fromString( std::string s, T &n, bool sign = true, u16 radix = 10 ) { T acc, base, temp; usetw( acc, 1 ); usetw( base, radix ); zero( n ); u32 len = ( u32 ) s.length(); const char *citer = s.c_str() + len; while ( len-- ) { char ch = *( --citer ); if ( IS_ALPHA( ch ) ) // assumes alpha characters only up to radix ch = TO_LOWER( ch ) - 'a' + 10; else if ( sign && ch == '-' ) // '-' should be first character { negate( n ); break; } else // assumes it's alphanumeric/- ch -= '0'; usetw( temp, ch ); umultiply( temp, acc ); add ( n, temp ) ; umultiply( acc, base ); } } #endif // BIG_USES_STRINGS //////// class wrapper //////// BIGONETYPE INLINE Int::Int() { big::zero( raw ); } BIGONETYPE INLINE Int::Int( word n ) { ssetw( raw, n ); } #ifdef BIG_USES_STRINGS BIGONETYPE INLINE Int::Int( std::string &s ) { fromString( s, raw ); } #endif BIGONETYPE INLINE Int::Int( T &n ) { set ( raw, n ) ; } BIGONETYPE INLINE Int::operator T &() { return raw; } BIGONETYPE BIGINTFAST Int::zero() { big::zero( raw ); return *this; } BIGONETYPE BIGINTFAST Int::operator=( word n ) { ssetw( raw, n ); return *this; } BIGONETYPE BIGINTFAST Int::operator=( T &n ) { set ( raw, n ) ; return *this; } BIGONETYPE BIGINTFAST Int::operator<<=( u32 s ) { shiftLeft( raw, s ); return *this; } BIGONETYPE BIGINTSLOW Int::operator<<( u32 s ) { Int temp( raw ); return temp <<= s; } BIGONETYPE BIGINTFAST Int::operator>>=( u32 s ) { shiftRight( raw, s ); return *this; } BIGONETYPE BIGINTSLOW Int::operator>>( u32 s ) { Int temp( raw ); return temp >>= s; } BIGONETYPE BIGINTFAST Int::operator+=( T &n ) { add ( raw, n ) ; return *this; } BIGONETYPE BIGINTSLOW Int::operator+( T &n ) { Int temp( raw ); return temp += n; } BIGONETYPE BIGINTFAST Int::operator-=( T &n ) { subtract( raw, n ); return *this; } BIGONETYPE BIGINTSLOW Int::operator-( T &n ) { Int temp( raw ); return temp -= n; } BIGONETYPE BIGINTFAST Int::operator++() // prefix { increment( raw ); return *this; } BIGONETYPE BIGINTSLOW Int::operator++( int ) // postfix { Int temp( raw ); increment( raw ); return temp; } BIGONETYPE BIGINTFAST Int::operator--() // prefix { decrement( raw ); return *this; } BIGONETYPE BIGINTSLOW Int::operator--( int ) // postfix { Int temp( raw ); decrement( raw ); return temp; } BIGONETYPE BIGINTSLOW Int::operator-( int ) // negation { Int temp( raw ); negate( temp ); return temp; } BIGONETYPE BIGINTSLOW Int::operator*( T &n ) { Int temp( raw ); return temp *= n; } BIGONETYPE BIGINTFAST Int::square() { square( raw ); return *this; } BIGONETYPE BIGINTFAST Int::operator/=( T &n ) { T discard; divide( raw, n, raw, discard ); return *this; } BIGONETYPE BIGINTSLOW Int::operator/( T &n ) { Int temp( raw ); return temp /= n; } BIGONETYPE BIGINTFAST Int::operator%=( T &n ) { modulus( raw, n, raw ); return *this; } BIGONETYPE BIGINTSLOW Int::operator%( T &n ) { Int temp( raw ); return temp %= n; } BIGONETYPE INLINE /* fast */ bool Int::operator>( T &n ) { return sgreater( raw, n ); } BIGONETYPE INLINE /* fast */ bool Int::operator>=( T &n ) { return sgreaterOrEqual( raw, n ); } BIGONETYPE INLINE /* fast */ bool Int::operator<( T &n ) { return !sgreaterOrEqual( raw, n ); } BIGONETYPE INLINE /* fast */ bool Int::operator<=( T &n ) { return !sgreater( raw, n ); } BIGONETYPE INLINE /* fast */ bool Int::operator==( T &n ) { return equal( raw, n ); } BIGONETYPE INLINE /* fast */ bool Int::operator!=( T &n ) { return !equal( raw, n ); } BIGONETYPE INLINE /* fast */ bool Int::operator!() { return isZero( raw ); } #ifdef BIG_USES_STRINGS BIGONETYPE INLINE /* fast */ std::string Int::str() { return toString( raw ); } BIGONETYPE BIGINTFAST Int::operator=( std::string &s ) { fromString( s, raw ); return *this; } BIGONETYPE BIGINTFAST Int::operator=( const char *s ) { fromString( std::string( s ), raw ); return *this; } #endif // BIG_USES_STRINGS } #endif // BIGTYPES_H blobby-1.0rc3/src/raknet/RakNetStatistics.cpp0000644000175000017500000001773012042452367022576 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Statistical Information Formatting Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "RakNetStatistics.h" #include // sprintf #include "BitStream.h" // BITS_TO_BYTES // Verbosity level currently supports 0 (low), 1 (medium), 2 (high) // Buffer must be hold enough to hold the output string. See the source to get an idea of how many bytes will be output void StatisticsToString( RakNetStatisticsStruct *s, char *buffer, int verbosityLevel ) { if ( s == 0 ) { sprintf( buffer, "stats is a NULL pointer in statsToString\n" ); return ; } if ( verbosityLevel == 0 ) { // Verbosity level 0 sprintf( buffer, "Total bytes sent: %u\n" "Total bytes received: %u\n" "Packetloss: %.1f%%\n", BITS_TO_BYTES( s->totalBitsSent ), BITS_TO_BYTES( s->bitsReceived + s->bitsWithBadCRCReceived ), 100.0f * ( float ) s->messagesTotalBitsResent / ( float ) s->totalBitsSent ); } else if ( verbosityLevel == 1 ) { // Verbosity level 1 sprintf( buffer, "Messages in Send buffer: %u\n" "Messages sent: %u\n" "Bytes sent: %u\n" "Acks sent: %u\n" "Acks in send buffer: %u\n" "Messages waiting for ack: %u\n" "Messages resent: %u\n" "Bytes resent: %u\n" "Packetloss: %.1f%%\n" "Messages received: %u\n" "Bytes received: %u\n" "Acks received: %u\n" "Duplicate acks received: %u\n" "Window size: %u\n", s->messageSendBuffer[ SYSTEM_PRIORITY ] + s->messageSendBuffer[ HIGH_PRIORITY ] + s->messageSendBuffer[ MEDIUM_PRIORITY ] + s->messageSendBuffer[ LOW_PRIORITY ], s->messagesSent[ SYSTEM_PRIORITY ] + s->messagesSent[ HIGH_PRIORITY ] + s->messagesSent[ MEDIUM_PRIORITY ] + s->messagesSent[ LOW_PRIORITY ], BITS_TO_BYTES( s->totalBitsSent ), s->acknowlegementsSent, s->acknowlegementsPending, s->messagesOnResendQueue, s->messageResends, BITS_TO_BYTES( s->messagesTotalBitsResent ), 100.0f * ( float ) s->messagesTotalBitsResent / ( float ) s->totalBitsSent, s->duplicateMessagesReceived + s->invalidMessagesReceived + s->messagesReceived, BITS_TO_BYTES( s->bitsReceived + s->bitsWithBadCRCReceived ), s->acknowlegementsReceived, s->duplicateAcknowlegementsReceived, s->windowSize ); } else { // Verbosity level 2. sprintf( buffer, "Bytes sent:\t\t\t\t%u\n" "Messages in send buffer:\t\tSP:%u HP:%u MP:%u LP:%u\n" "Messages sent:\t\t\t\tSP:%u HP:%u MP:%u LP:%u\n" "Message data bytes sent:\t\tSP:%u HP:%u MP:%u LP:%u\n" "Message header bytes sent:\t\tSP:%u HP:%u MP:%u LP:%u\n" "Message total bytes sent:\t\tSP:%u HP:%u MP:%u LP:%u\n" "Bytes received:\t\t\t\tTtl:%u Good:%u Bad:%u\n" "Packets received:\t\t\tTtl:%u Good:%u Bad:%u\n" "Acks received:\t\t\t\tTtl:%u Good:%u Dup:%u\n" "Messages received:\t\t\tTotal:%u Valid:%u Invalid:%u Dup:%u\n" "Packetloss:\t\t\t\t%.1f%%\n" "Packets sent:\t\t\t\t%u\n" "Acks sent:\t\t\t\t%u\n" "Acks in send buffer:\t\t\t%u\n" "Messages waiting for ack:\t\t%u\n" "Ack bytes sent:\t\t\t\t%u\n" "Sent packets containing only acks:\t%u\n" "Sent packets w/only acks and resends:\t%u\n" "Reliable messages resent:\t\t%u\n" "Reliable message data bytes resent:\t%u\n" "Reliable message header bytes resent:\t%u\n" "Reliable message total bytes resent:\t%u\n" "Number of messages split:\t\t%u\n" "Number of messages unsplit:\t\t%u\n" "Message splits performed:\t\t%u\n" "Additional encryption bytes:\t\t%u\n" "Sequenced messages out of order:\t%u\n" "Sequenced messages in order:\t\t%u\n" "Ordered messages out of order:\t\t%u\n" "Ordered messages in of order:\t\t%u\n" "Split messages waiting for reassembly:\t%u\n" "Messages in internal output queue:\t%u\n" "Window size:\t\t\t\t%u\n" "Lossy window size\t\t\t%u\n" "Connection start time:\t\t\t%u\n", BITS_TO_BYTES( s->totalBitsSent ), s->messageSendBuffer[ SYSTEM_PRIORITY ], s->messageSendBuffer[ HIGH_PRIORITY ], s->messageSendBuffer[ MEDIUM_PRIORITY ], s->messageSendBuffer[ LOW_PRIORITY ], s->messagesSent[ SYSTEM_PRIORITY ], s->messagesSent[ HIGH_PRIORITY ], s->messagesSent[ MEDIUM_PRIORITY ], s->messagesSent[ LOW_PRIORITY ], BITS_TO_BYTES( s->messageDataBitsSent[ SYSTEM_PRIORITY ] ), BITS_TO_BYTES( s->messageDataBitsSent[ HIGH_PRIORITY ] ), BITS_TO_BYTES( s->messageDataBitsSent[ MEDIUM_PRIORITY ] ), BITS_TO_BYTES( s->messageDataBitsSent[ LOW_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ SYSTEM_PRIORITY ] - s->messageDataBitsSent[ SYSTEM_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ HIGH_PRIORITY ] - s->messageDataBitsSent[ HIGH_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ MEDIUM_PRIORITY ] - s->messageDataBitsSent[ MEDIUM_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ LOW_PRIORITY ] - s->messageDataBitsSent[ LOW_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ SYSTEM_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ HIGH_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ MEDIUM_PRIORITY ] ), BITS_TO_BYTES( s->messageTotalBitsSent[ LOW_PRIORITY ] ), BITS_TO_BYTES( s->bitsReceived + s->bitsWithBadCRCReceived ), BITS_TO_BYTES( s->bitsReceived ), BITS_TO_BYTES( s->bitsWithBadCRCReceived ), s->packetsReceived + s->packetsWithBadCRCReceived, s->packetsReceived, s->packetsWithBadCRCReceived, s->acknowlegementsReceived + s->duplicateAcknowlegementsReceived, s->acknowlegementsReceived, s->duplicateAcknowlegementsReceived, s->messagesReceived + s->invalidMessagesReceived + s->duplicateMessagesReceived, s->messagesReceived, s->invalidMessagesReceived, s->duplicateMessagesReceived, 100.0f * ( float ) s->messagesTotalBitsResent / ( float ) s->totalBitsSent, s->packetsSent, s->acknowlegementsSent, s->acknowlegementsPending, s->messagesOnResendQueue, BITS_TO_BYTES( s->acknowlegementBitsSent ), s->packetsContainingOnlyAcknowlegements, s->packetsContainingOnlyAcknowlegementsAndResends, s->messageResends, BITS_TO_BYTES( s->messageDataBitsResent ), BITS_TO_BYTES( s->messagesTotalBitsResent - s->messageDataBitsResent ), BITS_TO_BYTES( s->messagesTotalBitsResent ), s->numberOfSplitMessages, s->numberOfUnsplitMessages, s->totalSplits, BITS_TO_BYTES( s->encryptionBitsSent ), s->sequencedMessagesOutOfOrder, s->sequencedMessagesInOrder, s->orderedMessagesOutOfOrder, s->orderedMessagesInOrder, s->messagesWaitingForReassembly, s->internalOutputQueueSize, s->windowSize, s->lossySize, s->connectionStartTime ); } } blobby-1.0rc3/src/raknet/DataBlockEncryptor.h0000644000175000017500000000675312042452367022541 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief DataBlockEncryptor Class Declaration * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __DATA_BLOCK_ENCRYPTOR_H #define __DATA_BLOCK_ENCRYPTOR_H #include "rijndael.h" /** * Encrypt and Decrypt block of data * */ class DataBlockEncryptor { public: /** * Default Constructor */ DataBlockEncryptor(); /** * Destructor */ ~DataBlockEncryptor(); /** * Test if encryption/decryption key are set * @return true if SetKey has been called previously */ bool IsKeySet( void ) const; /** * Set the encryption key * @param key The new encryption key */ void SetKey( const unsigned char key[ 16 ] ); /** * Unset the encryption key */ void UnsetKey( void ); /** * Encrypt adds up to 15 bytes. Output should be large enough to hold this. * Output can be the same memory block as input * @param input the input buffer to encrypt * @param inputLength the size of the @em input buffer * @param output the output buffer to store encrypted data * @param outputLength the size of the output buffer */ void Encrypt( unsigned char *input, int inputLength, unsigned char *output, int *outputLength ); /** * Decrypt removes bytes, as few as 6. Output should be large enough to hold this. * Output can be the same memory block as input * @param input the input buffer to decrypt * @param inputLength the size of the @em input buffer * @param output the output buffer to store decrypted data * @param outputLength the size of the @em output buffer * @return False on bad checksum or input, true on success */ bool Decrypt( unsigned char *input, int inputLength, unsigned char *output, int *outputLength ); protected: /** * The encryption / decryption key */ //AES128 secretKeyAES128; // TODO keyInstance keyEncrypt; keyInstance keyDecrypt; cipherInstance cipherInst; /** * True if a key is set */ bool keySet; }; #endif blobby-1.0rc3/src/raknet/RakClientInterface.h0000644000175000017500000005225312042452367022500 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief User view of a RakClient object. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_CLIENT_INTERFACE_H #define __RAK_CLIENT_INTERFACE_H #include "NetworkTypes.h" #include "PacketPriority.h" #include "RakPeerInterface.h" #include "BitStream.h" #include "RakNetStatistics.h" /** * @brief Define user point of vue of a RakClient communication end point. * * This class define the user view of a RakClient communication * end-point. All accessible operation are available in this class. * You should only deal with RakClient object throught instance of * RakClientInterface. */ class RakClientInterface { public: /** * Destructor */ virtual ~RakClientInterface() {} /** * Call this to connect the client to the specified host (ip or domain name) and server port. * This is a non-blocking connection. You know the connection is successful when IsConnected() returns true * or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. * serverPort is which port to connect to on the remote machine. clientPort is the port you want the * client to use. Both ports must be open for UDP * * @param host a hostname * @param serverPort The port on which to contact @em host * @param clientPort The port to use localy * @param depreciated is legacy and unused * @param threadSleepTimer >=0 for how many ms to Sleep each internal update cycle * (recommended 30 for low performance, 0 for regular) * @return true on successful initiation, false otherwise */ virtual bool Connect( const char* host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer ) = 0; /** * Stops the client, stops synchronized data, and resets all internal data. * Does nothing if the client is not connected to begin with * blockDuration is how long you should wait for all remaining packets to go out * If you set it to 0 then the disconnection notification probably won't arrive * @param blockDuration The time to wait before truly close the communication and point */ virtual void Disconnect( unsigned int blockDuration ) = 0; /** * Can be called to use specific public RSA keys. (e and n) * In order to prevent altered keys. Will return ID_RSA_PUBLIC_KEY_MISMATCH in a packet * If a key has been altered. * * @param privKeyP Private keys generated from the RSACrypt class. Can be 0 * @param privKeyQ Private keys generated from the RSACrypt class. Can be 0 * @see Encryption sample. */ virtual void InitializeSecurity( const char *privKeyP, const char *privKeyQ ) = 0; /** * Set the password to use when connecting to a server. The password persists between connections. * Pass 0 for no password. * @param _password The password to use to connect to a server */ virtual void SetPassword( const char *_password ) = 0; /** * Returns true if a password was set, false otherwise * @return true if a password has previously been set using SetPassword */ virtual bool HasPassword( void ) const = 0; /** * This function only works while the client is connected (Use the * Connect function). Returns false on failure, true on success * Sends the data stream of length length If you aren't sure what to * specify for priority and reliability, use HIGH_PRIORITY and * RELIABLE, 0 for ordering channel * @param data a byte buffer * @param length the size of the byte buffer * @param priority the priority of the message * @param reliability the reliability policy required * @param orderingChannel the channel to send the message to. */ virtual bool Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel ) = 0; /** * This function only works while the client is connected (Use the * Connect function). Returns false on failure, true on success * Sends the BitStream If you aren't sure what to specify for * priority and reliability, use HIGH_PRIORITY and RELIABLE, 0 for * ordering channel * @param bitstream the data to send. * @param priority the priority of the message * @param reliability the reliability policy required * @param orderingChannel the channel to send the message to. */ virtual bool Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel ) = 0; /** * Call this to get a packet from the incoming packet queue. Use * DeallocatePacket to deallocate the packet after you are done with * it. Check the Packet struct at the top of * CoreNetworkStructures.h for the format of the struct Returns 0 if * no packets are waiting to be handled If the client is not active * this will also return 0, as all waiting packets are flushed when * the client is Disconnected This also updates all memory blocks * associated with synchronized memory * @return the last receive packet */ virtual Packet* Receive( void ) = 0; /** * Call this to deallocate a packet returned by Receive when you are done handling it. * Free the memory associated to a packet. It is not the same as using delete operator because * RakNet might decide not to delete right now the packet in order to use it later. * @param packet the packet to deallocate. */ virtual void DeallocatePacket( Packet *packet ) = 0; /** * Send a ping request to the server.Occasional pings are on by * default (see StartOccasionalPing and StopOccasionalPing) so * unless you turn them off it is not necessary to call this * function. It is here for completeness if you want it Does * nothing if the client is not connected to begin with */ virtual void PingServer( void ) = 0; /** * Sends a ping request to a server we are not connected to. This will also initialize the * networking system if it is not already initialized. You can stop the networking system * by calling Disconnect() * The final ping time will be encoded in the following 4 bytes (2-5) as an unsigned int * You can specify if the server should only reply if it has an open connection or not * This must be true for LAN broadcast server discovery on "255.255.255.255" * or you will get replies from clients as well. A broadcast will always have 0 for the ping time as only single * byte packets are allowed from unconnected systems. * @param host The host to contact * @param ServerPort the port used by the server * @param clientPort the port used to receive the answer * @param onlyReplyOnAcceptingConnections if true the server must be ready to accept incomming connection. */ virtual void PingServer( const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections ) = 0; /** * Returns the average of all ping times read * @return the average ping value to the server */ virtual int GetAveragePing( void ) = 0; /** * Returns the last ping time read for the specific player or -1 if none read yet * @return last ping value */ virtual int GetLastPing( void ) const = 0; /** * Returns the lowest ping time read or -1 if none read yet * @return lowest ping value */ virtual int GetLowestPing( void ) const = 0; /** * Returns the last ping for the specified player. This information * is broadcast by the server automatically In order to save * bandwidth this information is updated only infrequently and only * for the first 32 players * @param playerId The id of the player you want to have the ping (it might be your id) * @return the last ping for this player * @note You can read your own ping with * this method by passing your own playerId, however for more * up-to-date readings you should use one of the three functions * above * */ virtual int GetPlayerPing( PlayerID playerId ) = 0; /** * Ping the server every so often. This is on by default. In games * where you don't care about ping you can call StopOccasionalPing * to save the bandwidth This will work anytime */ virtual void StartOccasionalPing( void ) = 0; /** * Stop pinging the server every so often. The server is pinged by * default. In games where you don't care about ping you can call * this to save the bandwidth This will work anytime */ virtual void StopOccasionalPing( void ) = 0; /** * Returns true if the client is connected to a responsive server * @return true if connected to a server */ virtual bool IsConnected( void ) const = 0; /** * Returns a number automatically synchronized between the server * and client which randomly changes every 9 seconds. The time it * changes is accurate to within a few ms and is best used to seed * random number generators that you want to usually return the same * output on all systems. Keep in mind this isn't perfectly * accurate as there is always a very small chance the numbers will * by out of synch during changes so you should confine its use to * visual effects or functionality that has a backup method to * maintain synchronization. If you don't need this functionality * and want to save the bandwidth call StopSynchronizedRandomInteger * after starting the server * @return A random int common to all client and to the server. */ virtual unsigned int GetSynchronizedRandomInteger( void ) const = 0; /** * This is an optional function to generate the compression layer * from the input frequency table. You should call this twice - * once with inputLayer as true and once as false. The frequency * table passed here with inputLayer=true should match the frequency * table on the recipient with inputLayer=false. Likewise, the * frequency table passed here with inputLayer=false should match * the frequency table on the recipient with inputLayer=true Calling * this function when there is an existing layer will overwrite the * old layer You should only call this when disconnected * * @param inputFrenquencyTable the table to used for compression * @param inputLayer says if the @em inputFrequencyTable should be used for * sending or receiveing. * @return false (failure) if connected. Otherwise true (success) * * @note The server Sends should share the same inputFrequencyTable * as the client for receiving. It's also true for the client sending and the server receiving. */ virtual bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ) = 0; /** * Delete the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory * You should only call this when disconnected * @param inputLayer Delete the compression layer for sending or for receiving ? * @return false (failure) if connected. Otherwise true (success) */ virtual bool DeleteCompressionLayer( bool inputLayer ) = 0; /** * Enables or disables frequency table tracking. This is required * to get a frequency table, which is used to generate A new * compression layer. You can call this at any time - however you * SHOULD only call it when disconnected. Otherwise you will only * track part of the values sent over the network. This value * persists between connect calls and defaults to false (no * frequency tracking) * @param b true to unable tracking. */ virtual void SetTrackFrequencyTable( bool b ) = 0; /** * Returns the frequency of outgoing bytes into outputFrequencyTable * The purpose is to save to file as either a master frequency table * from a sample game session for passing to * GenerateCompressionLayer. You should only call this when * disconnected. Requires that you first enable data frequency * tracking by calling SetTrackFrequencyTable(true) * @param outputFrequencyTable The frequency table produce during the tracking time. * @return false (failure) if connected or if frequency table tracking is * not enabled. Otherwise true (success) */ virtual bool GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ) = 0; /** * Returns the compression ratio. A low compression ratio is good. Compression is for outgoing data * @return the current compression ratio */ virtual float GetCompressionRatio( void ) const = 0; /** * Returns the decompression ratio. A high decompression ratio is good. Decompression is for incoming data * @return the current decompression ratio */ virtual float GetDecompressionRatio( void ) const = 0; /** * Attatches a message handler interface to run code automatically on message receipt in the Receive call * * @param messageHandler Pointer to a message handler to attach */ virtual void AttachMessageHandler( MessageHandlerInterface *messageHandler )=0; /** * Detatches a message handler interface to run code automatically on message receipt * * @param messageHandler Pointer to a message handler to detatch */ virtual void DetachMessageHandler( MessageHandlerInterface *messageHandler )=0; /** * The server internally maintains a data struct that is * automatically sent to clients when the connect. This is useful * to contain data such as the server name or message of the day. * Access that struct with this function. The data is entered as an * array and stored and returned as a BitStream. Everytime you call * GetStaticServerData it resets the read pointer to the start of * the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the server may change at any time the * data contents and/or its length! * @return a bitstream containing statistics. */ virtual RakNet::BitStream * GetStaticServerData( void ) = 0; /** * The server internally maintains a data struct that is * automatically sent to clients when the connect. This is useful * to contain data such as the server name or message of the day. * Access that struct with this function. The data is entered as an * array and stored and returned as a BitStream. Everytime you call * GetStaticServerData it resets the read pointer to the start of * the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the server may change at any time the * data contents and/or its length! * @param data a byte buffer containing statistical information. * @param length the size of @em data */ virtual void SetStaticServerData( const char *data, const long length ) = 0; /** * The client internally maintains a data struct that is automatically sent to the server on connection * This is useful to contain data such as the player name. Access that struct with this * function. Pass UNASSIGNED_PLAYER_ID for playerId to reference your internal data. A playerId value to access the data of another player. * *** NOTE *** * If you change any data in the struct the server won't reflect this change unless you manually update it * Do so by calling SendStaticClientDataToServer * The data is entered as an array and stored and returned as a BitStream. * Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in * RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters */ virtual RakNet::BitStream * GetStaticClientData( PlayerID playerId ) = 0; /** * Set Local statistical information for playId. Call this * function when you receive statistical information from a * client. * * @param playerId the player ID * @param data the packet data * @param length the size of the data */ virtual void SetStaticClientData( PlayerID playerId, const char *data, const long length ) = 0; /** * Send the static server data to the server The only time you need * to call this function is to update clients that are already * connected when you change the static server data by calling * GetStaticServerData and directly modifying the object pointed to. * Obviously if the connected clients don't need to know the new * data you don't need to update them, so it's up to you The server * must be active for this to have meaning */ virtual void SendStaticClientDataToServer( void ) = 0; /** * Return the player number of the server. * @return the server playerID */ virtual PlayerID GetServerID( void ) const = 0; /** * Return the player number the server has assigned to you. * * @return our player ID * @note that unlike in previous versions, this is a struct and is not sequential * */ virtual PlayerID GetPlayerID( void ) const = 0; /** * Returns the dotted IP address for the specified playerId * * @param playerId Any player ID other than UNASSIGNED_PLAYER_ID, * even if that player is not currently connected * @return a dotted notation string representation of the address of playerId. */ virtual const char* PlayerIDToDottedIP( PlayerID playerId ) const = 0; /** * Put a packet back at the end of the receive queue in case you don't want to deal with it immediately * @param packet the packet to delayed */ virtual void PushBackPacket( Packet *packet ) = 0; /** * Change the MTU size in order to improve performance when sending large packets * This can only be called when not connected. * Returns false on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE * A too high of value will cause packets not to arrive at worst and be fragmented at best. * A too low of value will split packets unnecessarily. * Set according to the following table: * 1500. The largest Ethernet packet size; it is also the default value. * This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. * 1492. The size PPPoE prefers. * 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) * 1468. The size DHCP prefers. * 1460. Usable by AOL if you don't have large email attachments, etc. * 1430. The size VPN and PPTP prefer. * 1400. Maximum size for AOL DSL. * 576. Typical value to connect to dial-up ISPs. (Default) */ virtual bool SetMTUSize( int size ) = 0; /** * Returns the current MTU size */ virtual int GetMTUSize( void ) const = 0; /** * Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary * when connection to servers with multiple IP addresses. * * * @param allow True to allow this behavior, false to not allow. * Defaults to false. Value persists between connections */ virtual void AllowConnectionResponseIPMigration( bool allow ) = 0; /** * Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. * This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through * * @param host Either a dotted IP address or a domain name * @param remotePort Which port to connect to on the remote machine. * @param data Optional data to append to the packet. * @param dataLength length of data in bytes. Use 0 if no data. */ virtual void AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ) = 0; /** * Returns a structure containing a large set of network statistics for the server/client connection * You can map this data to a string using the C style StatisticsToString function * * @return 0 on can't find the specified system. A pointer to a set of data otherwise. */ virtual RakNetStatisticsStruct * const GetStatistics( void ) = 0; /** * @internal * Retrieve the player index corresponding to this client. */ virtual PlayerIndex GetPlayerIndex( void ) = 0; }; #endif blobby-1.0rc3/src/raknet/SingleProducerConsumer.h0000644000175000017500000001473212042452367023444 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief RakPeer Implementation * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __SINGLE_PRODUCER_CONSUMER_H #define __SINGLE_PRODUCER_CONSUMER_H #include static const int MINIMUM_LIST_SIZE=8; namespace BasicDataStructures { template class SingleProducerConsumer { public: SingleProducerConsumer(); ~SingleProducerConsumer(); // WriteLock must be immediately followed by WriteUnlock. These two functions must be called in the same thread. SingleProducerConsumerType* WriteLock(void); void WriteUnlock(void); // ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread. SingleProducerConsumerType* ReadLock(void); // Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored void CancelReadLock(SingleProducerConsumerType* cancelToLocation); void ReadUnlock(void); // Clear is not thread-safe and none of the lock or unlock functions should be called while it is running. void Clear(void); int Size(void) const; // An ESTIMATE of how many data elements are waiting to be read private: struct DataPlusPtr { SingleProducerConsumerType object; DataPlusPtr *next; }; DataPlusPtr *readPointer, *readAheadPointer, *writePointer, *writeAheadPointer; int listSize, dataSize; }; template SingleProducerConsumer::SingleProducerConsumer() { // Preallocate readPointer = new DataPlusPtr; writePointer=readPointer; readPointer->next = new DataPlusPtr; #ifdef _DEBUG assert(MINIMUM_LIST_SIZE>=3); #endif for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++) { readPointer=readPointer->next; readPointer->next = new DataPlusPtr; } listSize=MINIMUM_LIST_SIZE; readPointer->next->next=writePointer; // last to next = start readPointer=writePointer; readAheadPointer=readPointer; writeAheadPointer=writePointer; dataSize=0; } template SingleProducerConsumer::~SingleProducerConsumer() { DataPlusPtr *next; readPointer=writeAheadPointer->next; while (readPointer!=writeAheadPointer) { next=readPointer->next; delete readPointer; readPointer=next; } delete readPointer; } template SingleProducerConsumerType* SingleProducerConsumer::WriteLock( void ) { if (writeAheadPointer->next==readPointer) { DataPlusPtr *originalNext=writeAheadPointer->next; writeAheadPointer->next=new DataPlusPtr; writeAheadPointer->next->next=originalNext; ++listSize; } DataPlusPtr *last; last=writeAheadPointer; writeAheadPointer=writeAheadPointer->next; return (SingleProducerConsumerType*) last; } template void SingleProducerConsumer::WriteUnlock( void ) { // DataPlusPtr *dataContainer = (DataPlusPtr *)structure; #ifdef _DEBUG assert(writePointer->next!=readPointer); assert(writePointer!=writeAheadPointer); #endif ++dataSize; // User is done with the data, allow send by updating the write pointer writePointer=writePointer->next; } template SingleProducerConsumerType* SingleProducerConsumer::ReadLock( void ) { if (readAheadPointer==writePointer) return 0; DataPlusPtr *last; last=readAheadPointer; readAheadPointer=readAheadPointer->next; return (SingleProducerConsumerType*)last; } template void SingleProducerConsumer::CancelReadLock( SingleProducerConsumerType* cancelToLocation ) { #ifdef _DEBUG assert(readPointer!=writePointer); #endif readAheadPointer=(DataPlusPtr *)cancelToLocation; } template void SingleProducerConsumer::ReadUnlock( void ) { #ifdef _DEBUG assert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock assert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 #endif --dataSize; readPointer=readPointer->next; } template void SingleProducerConsumer::Clear( void ) { // Shrink the list down to MINIMUM_LIST_SIZE elements DataPlusPtr *next; writePointer=readPointer->next; while (listSize-- > MINIMUM_LIST_SIZE) { next=writePointer->next; delete writePointer; writePointer=next; } readPointer->next=writePointer; writePointer=readPointer; readAheadPointer=readPointer; writeAheadPointer=writePointer; dataSize=0; } template int SingleProducerConsumer::Size( void ) const { return dataSize; } } #endif blobby-1.0rc3/src/raknet/ReliabilityLayer.cpp0000644000175000017500000030666212042452367022612 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Reliability Layer Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ReliabilityLayer.h" #include #include "GetTime.h" #include "SocketLayer.h" // alloca #ifdef _WIN32 #include #else #include #endif // Defined in rand.cpp extern void seedMT( unsigned int seed ); extern inline unsigned int randomMT( void ); extern inline float frandomMT( void ); static const int ACK_BIT_LENGTH = sizeof( PacketNumberType ) *8 + 1; static const int MAXIMUM_WINDOW_SIZE = ( 8000 - UDP_HEADER_SIZE ) *8 / ACK_BIT_LENGTH; // Sanity check - the most ack packets that could ever (usually) fit into a frame. static const int MINIMUM_WINDOW_SIZE = 5; // how many packets can be sent unacknowledged before waiting for an ack static const int DEFAULT_RECEIVED_PACKETS_SIZE=128; // Divide by timeout time in seconds to get the max ave. packets per second before requiring reallocation //------------------------------------------------------------------------------------------------------- // Constructor //------------------------------------------------------------------------------------------------------- ReliabilityLayer::ReliabilityLayer() : updateBitStream( MAXIMUM_MTU_SIZE ) // preallocate the update bitstream so we can avoid a lot of reallocs at runtime { InitializeVariables(); #ifdef __USE_IO_COMPLETION_PORTS readWriteSocket = INVALID_SOCKET; #endif freeThreadedMemoryOnNextUpdate = false; } //------------------------------------------------------------------------------------------------------- // Destructor //------------------------------------------------------------------------------------------------------- ReliabilityLayer::~ReliabilityLayer() { FreeMemory( true ); // Free all memory immediately #ifdef __USE_IO_COMPLETION_PORTS if ( readWriteSocket != INVALID_SOCKET ) closesocket( readWriteSocket ); #endif } //------------------------------------------------------------------------------------------------------- // Resets the layer for reuse //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::Reset( void ) { FreeMemory( true ); // true because making a memory reset pending in the update cycle causes resets after reconnects. Instead, just call Reset from a single thread InitializeVariables(); } //------------------------------------------------------------------------------------------------------- // Sets up encryption //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::SetEncryptionKey( const unsigned char* key ) { if ( key ) encryptor.SetKey( key ); else encryptor.UnsetKey(); } //------------------------------------------------------------------------------------------------------- // Assign a socket for the reliability layer to use for writing //------------------------------------------------------------------------------------------------------- #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter void ReliabilityLayer::SetSocket( SOCKET s ) { #ifdef __USE_IO_COMPLETION_PORTS // If this hits I am probably using sequential ports while doing IO completion ports assert( s != INVALID_SOCKET ); readWriteSocket = s; #endif } //------------------------------------------------------------------------------------------------------- // Get the socket held by the reliability layer //------------------------------------------------------------------------------------------------------- SOCKET ReliabilityLayer::GetSocket( void ) { #ifdef __USE_IO_COMPLETION_PORTS return readWriteSocket; #else return INVALID_SOCKET; #endif } //------------------------------------------------------------------------------------------------------- // Initialize the variables //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::InitializeVariables( void ) { memset( waitingForOrderedPacketReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType)); memset( waitingForSequencedPacketReadIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); memset( waitingForOrderedPacketWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); memset( waitingForSequencedPacketWriteIndex, 0, NUMBER_OF_ORDERED_STREAMS * sizeof(OrderingIndexType) ); memset( &statistics, 0, sizeof( statistics ) ); statistics.connectionStartTime = RakNet::GetTime(); splitPacketId = 0; packetNumber = 0; // lastPacketSendTime=retransmittedFrames=sentPackets=sentFrames=receivedPacketsCount=bytesSent=bytesReceived=0; SetLostPacketResendDelay( 1000 ); deadConnection = cheater = false; lastAckTime = 0; blockWindowIncreaseUntilTime = 0; // Windowing windowSize = MINIMUM_WINDOW_SIZE; lossyWindowSize = MAXIMUM_WINDOW_SIZE + 1; // Infinite lastWindowIncreaseSizeTime = 0; // lastPacketReceivedTime=0; receivedPacketsBaseIndex=0; resetReceivedPackets=true; } //------------------------------------------------------------------------------------------------------- // Frees all allocated memory //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::FreeMemory( bool freeAllImmediately ) { if ( freeAllImmediately ) { FreeThreadedMemory(); FreeThreadSafeMemory(); } else { FreeThreadSafeMemory(); freeThreadedMemoryOnNextUpdate = true; } } void ReliabilityLayer::FreeThreadedMemory( void ) { } void ReliabilityLayer::FreeThreadSafeMemory( void ) { // unsigned i, j; // InternalPacket *internalPacket; // if (bytesSent > 0 || bytesReceived > 0) // { /* for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) { j = 0; reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Lock(); for ( ; j < sendQueue[ i ].size(); j++ ) { delete [] ( sendQueue[ i ] ) [ j ]->data; internalPacketPool.ReleasePointer( ( sendQueue[ i ] ) [ j ] ); } sendQueue[ i ].clearAndForceAllocation( 512 ); // Preallocate the send lists so we don't do a bunch of reallocations unnecessarily reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Unlock(); } */ // } unsigned i; InternalPacket *internalPacket; for ( i = 0; i < splitPacketList.size(); i++ ) { delete [] splitPacketList[ i ]->data; internalPacketPool.ReleasePointer( splitPacketList[ i ] ); } splitPacketList.clear(); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); // } // if (bytesSent > 0 || bytesReceived > 0) // { //reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); while ( outputQueue.size() > 0 ) { internalPacket = outputQueue.pop(); delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); } outputQueue.clearAndForceAllocation( 512 ); //reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); // } // if (bytesSent > 0 || bytesReceived > 0) // { //reliabilityLayerMutexes[orderingList_MUTEX].Lock(); for ( i = 0; i < orderingList.size(); i++ ) { if ( orderingList[ i ] ) { BasicDataStructures::LinkedList* theList = orderingList[ i ]; if ( theList ) { while ( theList->size() ) { internalPacket = orderingList[ i ]->pop(); delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); } delete theList; } } } orderingList.clear(); //reliabilityLayerMutexes[orderingList_MUTEX].Unlock(); // } // if (bytesSent > 0 || bytesReceived > 0) // { //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); while ( acknowledgementQueue.size() > 0 ) internalPacketPool.ReleasePointer( acknowledgementQueue.pop() ); acknowledgementQueue.clearAndForceAllocation( 64 ); //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); // } // if (bytesSent > 0 || bytesReceived > 0) // { //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); while ( resendQueue.size() ) { // The resend Queue can have NULL pointer holes. This is so we can deallocate blocks without having to compress the array internalPacket = resendQueue.pop(); if ( internalPacket ) { delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); } } resendQueue.clearAndForceAllocation( DEFAULT_RECEIVED_PACKETS_SIZE ); //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); // } unsigned j; for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) { j = 0; for ( ; j < sendPacketSet[ i ].size(); j++ ) { delete [] ( sendPacketSet[ i ] ) [ j ]->data; internalPacketPool.ReleasePointer( ( sendPacketSet[ i ] ) [ j ] ); } sendPacketSet[ i ].clearAndForceAllocation( 512 ); // Preallocate the send lists so we don't do a bunch of reallocations unnecessarily } #ifdef _INTERNET_SIMULATOR #pragma message("-- RakNet _INTERNET_SIMULATOR is defined!! --") for (unsigned i = 0; i < delayList.size(); i++ ) delete delayList[ i ]; delayList.clear(); #endif internalPacketPool.ClearPool(); } //------------------------------------------------------------------------------------------------------- // Packets are read directly from the socket layer and skip the reliability //layer because unconnected players do not use the reliability layer // This function takes packet data after a player has been confirmed as //connected. The game should not use that data directly // because some data is used internally, such as packet acknowledgement and //split packets //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::HandleSocketReceiveFromConnectedPlayer( const char *buffer, int length ) { #ifdef _DEBUG assert( !( length <= 0 || buffer == 0 ) ); #endif if ( length <= 1 || buffer == 0 ) // Length of 1 is a connection request resend that we just ignore return true; int numberOfAcksInFrame = 0; unsigned int time; bool indexFound; int count, size; PacketNumberType holeCount; // bool duplicatePacket; // bytesReceived+=length + UDP_HEADER_SIZE; UpdateThreadedMemory(); // decode this whole chunk if the decoder is defined. if ( encryptor.IsKeySet() ) { if ( encryptor.Decrypt( ( unsigned char* ) buffer, length, ( unsigned char* ) buffer, &length ) == false ) { statistics.bitsWithBadCRCReceived += length * 8; statistics.packetsWithBadCRCReceived++; return false; } } statistics.bitsReceived += length * 8; statistics.packetsReceived++; RakNet::BitStream socketData( (char*)buffer, length, false ); // Convert the incoming data to a bitstream for easy parsing // time = lastPacketReceivedTime = RakNet::GetTime(); time = RakNet::GetTime(); // Parse the bitstream to create an internal packet InternalPacket* internalPacket = CreateInternalPacketFromBitStream( &socketData, time ); while ( internalPacket ) { if ( internalPacket->isAcknowledgement ) { numberOfAcksInFrame++; //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); if ( resendQueue.size() == 0 ) { //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); //reliabilityLayerMutexes[lastAckTime_MUTEX].Lock(); lastAckTime = 0; // Not resending anything so clear this var so we don't drop the connection on not getting any more acks //reliabilityLayerMutexes[lastAckTime_MUTEX].Unlock(); } else { //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); //reliabilityLayerMutexes[lastAckTime_MUTEX].Lock(); lastAckTime = time; // Just got an ack. Record when we got it so we know the connection is alive //reliabilityLayerMutexes[lastAckTime_MUTEX].Unlock(); } // SHOW - ack received //printf("Got Ack for %i. resendQueue.size()=%i sendQueue[0].size() = %i\n",internalPacket->packetNumber, resendQueue.size(), sendQueue[0].size()); RemovePacketFromResendQueueAndDeleteOlderReliableSequenced( internalPacket->packetNumber ); internalPacketPool.ReleasePointer( internalPacket ); } else { // receivedPacketsCount++; if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED || internalPacket->reliability == RELIABLE ) { SendAcknowledgementPacket( internalPacket->packetNumber, time ); } // Show all arrived packets // printf("Got %i.\n", internalPacket->packetNumber); // resetReceivedPackets is set from a non-threadsafe function. // We do the actual reset in this function so the data is not modified by multiple threads if (resetReceivedPackets) { receivedPackets.clearAndForceAllocation(DEFAULT_RECEIVED_PACKETS_SIZE); receivedPacketsBaseIndex=0; resetReceivedPackets=false; } //printf("internalPacket->packetNumber=%i receivedPacketsBaseIndex=%i\n", internalPacket->packetNumber,receivedPacketsBaseIndex); // If the following conditional is true then this either a duplicate packet // or an older out of order packet // The subtraction unsigned overflow is intentional holeCount = (PacketNumberType)(internalPacket->packetNumber-receivedPacketsBaseIndex); const int typeRange = (PacketNumberType)-1; if (holeCount==0) { // Got what we were expecting if (receivedPackets.size()) receivedPackets.pop(); ++receivedPacketsBaseIndex; } else if (holeCount > typeRange-typeRange/2) { // Underflow - got a packet we have already counted past #ifdef _DEBUG //printf( "Warning: Got old duplicate packet (%i) with ID %i.\n", internalPacket->packetNumber, (unsigned char)internalPacket->data[0] ); #endif statistics.duplicateMessagesReceived++; // Duplicate packet delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } else if (holeCountpacketNumber, (unsigned char)internalPacket->data[0] ); #endif statistics.duplicateMessagesReceived++; // Duplicate packet delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } } else // holeCount>=receivedPackets.size() { // Got a higher count out of order packet whose packetNumber is higher than we have ever got // Add 0 times to the queue until (packetNumber - baseIndex) < queue size. while ((PacketNumberType)(holeCount) > receivedPackets.size()) receivedPackets.push(MakeReceivedPacketHole(time)); receivedPackets.push(time); #ifdef _DEBUG // If this assert hits then PacketNumberType has overflowed assert(receivedPackets.size() < (unsigned int)((PacketNumberType)(-1))); #endif } // Pop all expired times. For each expired time popped, increase the baseIndex // Each time is either the actual time a packet was received or a time that is between // (time+TIMEOUT_TIME) to (time+TIMEOUT_TIME*2) in the future. Future times are used as a flag // to indicate that we never got this packet so we don't mark it as a duplicate. while ( receivedPackets.size()>0 && IsExpiredTime(receivedPackets.peek(), time) ) { receivedPackets.pop(); ++receivedPacketsBaseIndex; } statistics.messagesReceived++; // If the allocated buffer is > DEFAULT_RECEIVED_PACKETS_SIZE and it is 3x greater than the number of elements actually being used //if (receivedPackets.AllocationSize() > DEFAULT_RECEIVED_PACKETS_SIZE && receivedPackets.AllocationSize() > receivedPackets.size() * 3) // receivedPackets.compress(); // Keep on top of deleting old unreliable split packets so they don't clog the list. if ( internalPacket->splitPacketCount > 0 ) DeleteOldUnreliableSplitPackets( time ); if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == UNRELIABLE_SEQUENCED ) { #ifdef _DEBUG assert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); #endif if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) { // Invalid packet #ifdef _DEBUG printf( "Got invalid packet\n" ); #endif delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } if ( IsOlderOrderedPacket( internalPacket->orderingIndex, waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] ) == false ) { statistics.sequencedMessagesInOrder++; // Check for older packets in the output list. Delete any found // UPDATE: // Disabled. We don't have enough info to consistently do this. Sometimes newer data does supercede // older data such as with constantly declining health, but not in all cases. // For example, with sequenced unreliable sound packets just because you send a newer one doesn't mean you // don't need the older ones because the odds are they will still arrive in order /* reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); DeleteSequencedPacketsInList(internalPacket->orderingChannel, outputQueue); reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); // Check for older packets in the split packet list. Delete any found reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); DeleteSequencedPacketsInList(internalPacket->orderingChannel, splitPacketList, internalPacket->splitPacketId); reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); */ // Is this a split packet? if ( internalPacket->splitPacketCount > 0 ) { // Generate the split // Verify some parameters to make sure we don't get junk data #ifdef _DEBUG assert( internalPacket->splitPacketIndex < internalPacket->splitPacketCount ); assert( internalPacket->dataBitLength < MAXIMUM_MTU_SIZE * 8 ); #endif //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); for ( unsigned cnt = 0; cnt < splitPacketList.size(); cnt++ ) { // Make sure this is not a duplicate insertion. // If this hits then most likely splitPacketId overflowed into existing waiting split packets (i.e. more than rangeof(splitPacketId) waiting) if ( splitPacketList[ cnt ]->splitPacketIndex == internalPacket->splitPacketIndex && splitPacketList[ cnt ]->splitPacketId == splitPacketId ) { #ifdef _DEBUG assert(0); #endif // Invalid packet #ifdef _DEBUG printf( "Error: Split packet duplicate insertion (1)\n" ); #endif delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } } #ifdef _DEBUG int splitPacketListSize = splitPacketList.size() + 1; #endif //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); // Check for a rebuilt packet InsertIntoSplitPacketList( internalPacket ); // Sequenced internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, time ); if ( internalPacket ) { #ifdef _DEBUG //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); assert( splitPacketList.size() == splitPacketListSize - internalPacket->splitPacketCount ); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); #endif // Update our index to the newest packet waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + 1; // If there is a rebuilt packet, add it to the output queue // reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); outputQueue.push( internalPacket ); // reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); internalPacket = 0; } #ifdef _DEBUG else { //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); assert( splitPacketList.size() == (unsigned int) splitPacketListSize ); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); } #endif // else don't have all the parts yet } else { // Update our index to the newest packet waitingForSequencedPacketReadIndex[ internalPacket->orderingChannel ] = internalPacket->orderingIndex + 1; // Not a split packet. Add the packet to the output queue // reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); outputQueue.push( internalPacket ); // reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); internalPacket = 0; } } else { statistics.sequencedMessagesOutOfOrder++; // Older sequenced packet. Discard it delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); } goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } // Is this an unsequenced split packet? if ( internalPacket->splitPacketCount > 0 ) { // An unsequenced split packet. May be ordered though. // Verify some parameters to make sure we don't get junk data #ifdef _DEBUG assert( internalPacket->splitPacketIndex < internalPacket->splitPacketCount ); assert( internalPacket->dataBitLength < MAXIMUM_MTU_SIZE * 8 ); #endif // Check for a rebuilt packet if ( internalPacket->reliability != RELIABLE_ORDERED ) internalPacket->orderingChannel = 255; // Use 255 to designate not sequenced and not ordered //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); // Make sure this is not a duplicate insertion. If this assert hits then splitPacketId overflowed into existing waiting split packets (i.e. more than rangeof(splitPacketId) waiting) for ( unsigned cnt = 0; cnt < splitPacketList.size(); cnt++ ) { if ( splitPacketList[ cnt ]->splitPacketIndex == internalPacket->splitPacketIndex && splitPacketList[ cnt ]->splitPacketId == internalPacket->splitPacketId ) { #ifdef _DEBUG assert(0); #endif // Invalid packet #ifdef _DEBUG printf( "Error: Split packet duplicate insertion (2)\n" ); #endif delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } } #ifdef _DEBUG int splitPacketListSize = splitPacketList.size() + 1; //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); #endif InsertIntoSplitPacketList( internalPacket ); internalPacket = BuildPacketFromSplitPacketList( internalPacket->splitPacketId, time ); if ( internalPacket == 0 ) { #ifdef _DEBUG //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); assert( splitPacketList.size() == (unsigned int) splitPacketListSize ); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); #endif // Don't have all the parts yet goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } #ifdef _DEBUG else { //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); assert( splitPacketList.size() == splitPacketListSize - internalPacket->splitPacketCount ); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); } #endif // else continue down to handle RELIABLE_ORDERED } if ( internalPacket->reliability == RELIABLE_ORDERED ) { #ifdef _DEBUG assert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); #endif if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) { #ifdef _DEBUG printf("Got invalid ordering channel %i from packet %i\n", internalPacket->orderingChannel, internalPacket->packetNumber); #endif // Invalid packet delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } if ( waitingForOrderedPacketReadIndex[ internalPacket->orderingChannel ] == internalPacket->orderingIndex ) { // Get the list to hold ordered packets for this stream BasicDataStructures::LinkedList *orderingListAtOrderingStream; unsigned char orderingChannelCopy = internalPacket->orderingChannel; statistics.orderedMessagesInOrder++; // Show ordering index increment //printf("Pushing immediate packet %i with ordering index %i\n", internalPacket->packetNumber, internalPacket->orderingIndex ); // Push the packet for the user to read // reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); outputQueue.push( internalPacket ); // reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); internalPacket = 0; // Don't reference this any longer since other threads access it // Wait for the next ordered packet in sequence waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ++; // This wraps //reliabilityLayerMutexes[orderingList_MUTEX].Lock(); orderingListAtOrderingStream = GetOrderingListAtOrderingStream( orderingChannelCopy ); if ( orderingListAtOrderingStream != 0) { while ( orderingListAtOrderingStream->size() > 0 ) { // Cycle through the list until nothing is found orderingListAtOrderingStream->beginning(); indexFound=false; size=orderingListAtOrderingStream->size(); count=0; while (count++ < size) { if ( orderingListAtOrderingStream->peek()->orderingIndex == waitingForOrderedPacketReadIndex[ orderingChannelCopy ] ) { //printf("Pushing delayed packet %i with ordering index %i. outputQueue.size()==%i\n", orderingListAtOrderingStream->peek()->packetNumber, orderingListAtOrderingStream->peek()->orderingIndex, outputQueue.size() ); outputQueue.push( orderingListAtOrderingStream->pop() ); waitingForOrderedPacketReadIndex[ orderingChannelCopy ]++; // This wraps at 255 indexFound=true; } else (*orderingListAtOrderingStream)++; } if (indexFound==false) break; } } internalPacket = 0; } else { // assert(waitingForOrderedPacketReadIndex[ internalPacket->orderingChannel ] < internalPacket->orderingIndex); statistics.orderedMessagesOutOfOrder++; // This is a newer ordered packet than we are waiting for. Store it for future use AddToOrderingList( internalPacket ); } goto CONTINUE_SOCKET_DATA_PARSE_LOOP; } // Nothing special about this packet. Add it to the output queue // reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); outputQueue.push( internalPacket ); // reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); // Output queue fill rate test // if (outputQueue.size()%50==0) // printf("outputQueue.size()=%i Time=%i\n", outputQueue.size(), RakNet::GetTime()); internalPacket = 0; } // Used for a goto to jump to the next packet immediately CONTINUE_SOCKET_DATA_PARSE_LOOP: // Parse the bitstream to create an internal packet internalPacket = CreateInternalPacketFromBitStream( &socketData, time ); } // numberOfAcksInFrame>=windowSize means that all the packets we last sent from the resendQueue are cleared out // 11/17/05 - the problem with numberOfAcksInFrame >= windowSize is that if the entire frame is filled with resends but not all resends filled the frame // then the sender is limited by how many resends can fit in one frame if ( numberOfAcksInFrame >= windowSize && ( sendPacketSet[ SYSTEM_PRIORITY ].size() > 0 || sendPacketSet[ HIGH_PRIORITY ].size() > 0 || sendPacketSet[ MEDIUM_PRIORITY ].size() > 0 ) ) { // reliabilityLayerMutexes[windowSize_MUTEX].Lock(); //printf("windowSize=%i lossyWindowSize=%i\n", windowSize, lossyWindowSize); if ( windowSize < lossyWindowSize || (time>lastWindowIncreaseSizeTime && time-lastWindowIncreaseSizeTime>lostPacketResendDelay*2) ) // Increases the window size slowly, testing for packetloss { // If we get a frame which clears out the resend queue after handling one or more acks, and we have packets waiting to go out, // and we didn't recently lose a packet then increase the window size by 1 windowSize++; if ( (time>lastWindowIncreaseSizeTime && time-lastWindowIncreaseSizeTime>lostPacketResendDelay*2) ) // The increase is to test for packetloss lastWindowIncreaseSizeTime = time; // If the window is so large that we couldn't possibly fit any more packets into the frame, then just leave it alone if ( windowSize > MAXIMUM_WINDOW_SIZE ) windowSize = MAXIMUM_WINDOW_SIZE; // SHOW - WINDOWING //else // printf("Increasing windowSize to %i. Lossy window size = %i\n", windowSize, lossyWindowSize); // If we are more than 5 over the lossy window size, increase the lossy window size by 1 if ( windowSize == MAXIMUM_WINDOW_SIZE || windowSize - lossyWindowSize > 5 ) lossyWindowSize++; } // reliabilityLayerMutexes[windowSize_MUTEX].Unlock(); } return true; } //------------------------------------------------------------------------------------------------------- // This gets an end-user packet already parsed out. Returns number of BITS put into the buffer //------------------------------------------------------------------------------------------------------- int ReliabilityLayer::Receive( char **data ) { // Wait until the clear occurs if (freeThreadedMemoryOnNextUpdate) return 0; InternalPacket * internalPacket; // reliabilityLayerMutexes[outputQueue_MUTEX].Lock(); if ( outputQueue.size() > 0 ) { // #ifdef _DEBUG // assert(bitStream->GetNumberOfBitsUsed()==0); // #endif internalPacket = outputQueue.pop(); // reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); int bitLength; *data = internalPacket->data; bitLength = internalPacket->dataBitLength; internalPacketPool.ReleasePointer( internalPacket ); return bitLength; } else { // reliabilityLayerMutexes[outputQueue_MUTEX].Unlock(); return 0; } } //------------------------------------------------------------------------------------------------------- // Puts data on the send queue // bitStream contains the data to send // priority is what priority to send the data at // reliability is what reliability to use // ordering channel is from 0 to 255 and specifies what stream to use //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::Send( char *data, int numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, unsigned int currentTime ) { #ifdef _DEBUG assert( !( reliability > RELIABLE_SEQUENCED || reliability < 0 ) ); assert( !( priority > NUMBER_OF_PRIORITIES || priority < 0 ) ); assert( !( orderingChannel < 0 || orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) ); assert( numberOfBitsToSend > 0 ); #endif #ifdef __USE_IO_COMPLETION_PORTS if ( readWriteSocket == INVALID_SOCKET ) return false; #endif // Fix any bad parameters if ( reliability > RELIABLE_SEQUENCED || reliability < 0 ) reliability = RELIABLE; if ( priority > NUMBER_OF_PRIORITIES || priority < 0 ) priority = HIGH_PRIORITY; if ( orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) orderingChannel = 0; int numberOfBytesToSend=BITS_TO_BYTES(numberOfBitsToSend); if ( numberOfBitsToSend == 0 ) { #ifdef _DEBUG printf( "Error!! ReliabilityLayer::Send bitStream->GetNumberOfBytesUsed()==0\n" ); #endif return false; } InternalPacket * internalPacket = internalPacketPool.GetPointer(); //InternalPacket * internalPacket = sendPacketSet[priority].WriteLock(); #ifdef _DEBUG // Remove accessing undefined memory warning memset( internalPacket, 255, sizeof( InternalPacket ) ); #endif internalPacket->creationTime = currentTime; if ( makeDataCopy ) { internalPacket->data = new char [ numberOfBytesToSend ]; memcpy( internalPacket->data, data, numberOfBytesToSend ); // printf("Allocated %i\n", internalPacket->data); } else { // Allocated the data elsewhere, delete it in here internalPacket->data = ( char* ) data; // printf("Using Pre-Allocated %i\n", internalPacket->data); } internalPacket->dataBitLength = numberOfBitsToSend; internalPacket->isAcknowledgement = false; internalPacket->nextActionTime = 0; //reliabilityLayerMutexes[ packetNumber_MUTEX ].Lock(); internalPacket->packetNumber = packetNumber; //reliabilityLayerMutexes[ packetNumber_MUTEX ].Unlock(); internalPacket->priority = priority; internalPacket->reliability = reliability; internalPacket->splitPacketCount = 0; // Calculate if I need to split the packet int headerLength = BITS_TO_BYTES( GetBitStreamHeaderLength( internalPacket ) ); int maxDataSize = MTUSize - UDP_HEADER_SIZE - headerLength; if ( encryptor.IsKeySet() ) maxDataSize -= 16; // Extra data for the encryptor bool splitPacket = numberOfBytesToSend > maxDataSize; // If a split packet, we might have to upgrade the reliability if ( splitPacket ) statistics.numberOfSplitMessages++; else statistics.numberOfUnsplitMessages++; // Increment the cyclical receivedPacketsIndex for use by the next packet. // This variable is used as the identifier of the packet on the remote machine. // When it cycles it will reuse older numbers but that is ok because by the time it // cycles those older packets will be pretty much guaranteed to arrive by then //reliabilityLayerMutexes[ packetNumber_MUTEX ].Lock(); // if ( ++packetNumber == RECEIVED_PACKET_LOG_LENGTH ) // packetNumber = 0; ++packetNumber; //reliabilityLayerMutexes[ packetNumber_MUTEX ].Unlock(); if ( internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == UNRELIABLE_SEQUENCED ) { // Assign the sequence stream and index internalPacket->orderingChannel = orderingChannel; //reliabilityLayerMutexes[ waitingForSequencedPacketWriteIndex_MUTEX ].Lock(); internalPacket->orderingIndex = waitingForSequencedPacketWriteIndex[ orderingChannel ] ++; //reliabilityLayerMutexes[ waitingForSequencedPacketWriteIndex_MUTEX ].Unlock(); // This packet supersedes all other sequenced packets on the same ordering channel // Delete all packets in all send lists that are sequenced and on the same ordering channel // UPDATE: // Disabled. We don't have enough info to consistently do this. Sometimes newer data does supercede // older data such as with constantly declining health, but not in all cases. // For example, with sequenced unreliable sound packets just because you send a newer one doesn't mean you // don't need the older ones because the odds are they will still arrive in order /* for (int i=0; i < NUMBER_OF_PRIORITIES; i++) { reliabilityLayerMutexes[sendQueue_MUTEX].Lock(); DeleteSequencedPacketsInList(orderingChannel, sendQueue[i]); reliabilityLayerMutexes[sendQueue_MUTEX].Unlock(); } */ } else if ( internalPacket->reliability == RELIABLE_ORDERED ) { // Assign the ordering channel and index internalPacket->orderingChannel = orderingChannel; //reliabilityLayerMutexes[ waitingForOrderedPacketWriteIndex_MUTEX ].Lock(); internalPacket->orderingIndex = waitingForOrderedPacketWriteIndex[ orderingChannel ] ++; //reliabilityLayerMutexes[ waitingForOrderedPacketWriteIndex_MUTEX ].Unlock(); } if ( splitPacket ) // If it uses a secure header it will be generated here { // Must split the packet. This will also generate the SHA1 if it is required. It also adds it to the send list. //InternalPacket packetCopy; //memcpy(&packetCopy, internalPacket, sizeof(InternalPacket)); //sendPacketSet[priority].CancelWriteLock(internalPacket); //SplitPacket( &packetCopy, MTUSize ); SplitPacket( internalPacket, MTUSize ); //delete [] packetCopy.data; return true; } // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + internalPacket->priority ].Lock(); sendPacketSet[ internalPacket->priority ].push( internalPacket ); // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + internalPacket->priority ].Unlock(); // sendPacketSet[priority].WriteUnlock(); return true; } //------------------------------------------------------------------------------------------------------- // Run this once per game cycle. Handles internal lists and actually does the send //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::Update( SOCKET s, PlayerID playerId, int MTUSize, unsigned int time ) { #ifdef __USE_IO_COMPLETION_PORTS if ( readWriteSocket == INVALID_SOCKET ) return; if (deadConnection) return; #endif // unsigned resendQueueSize; bool reliableDataSent; unsigned int lastAck; UpdateThreadedMemory(); // Accuracy isn't important on this value, and since this is called so often the mutex is sometimes causing deadlock problems. // So it is presently disabled // reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); // resendQueueSize=resendQueue.size(); // reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); // reliabilityLayerMutexes[lastAckTime_MUTEX].Lock(); lastAck = lastAckTime; // reliabilityLayerMutexes[lastAckTime_MUTEX].Unlock(); // Due to thread vagarities and the way I store the time to avoid slow calls to RakNet::GetTime // time may be less than lastAck if ( resendQueue.size() > 0 && time > lastAck && lastAck && time - lastAck > TIMEOUT_TIME ) { // SHOW - dead connection // printf("The connection has been lost.\n"); // We've waited a very long time for a reliable packet to get an ack and it never has deadConnection = true; return; } //if (outputWindowFullTime && RakNet::GetTime() > TIMEOUT_TIME + outputWindowFullTime) //{ // // We've waited a long time with no data from the other system. Assume the connection is lost // deadConnection=true; // return; //} // Not a frame but a packet actually. // However, in a sense it is a frame because we are filling multiple logical packets into one datagram //reliabilityLayerMutexes[updateBitStream_MUTEX].Lock(); // Keep sending to available bandwidth while ( IsFrameReady( time ) ) { updateBitStream.Reset(); GenerateFrame( &updateBitStream, MTUSize, &reliableDataSent, time ); if ( updateBitStream.GetNumberOfBitsUsed() > 0 ) { #ifndef _INTERNET_SIMULATOR SendBitStream( s, playerId, &updateBitStream ); #else // Delay the send to simulate lag DataAndTime *dt; dt = new DataAndTime; memcpy( dt->data, updateBitStream.GetData(), updateBitStream.GetNumberOfBytesUsed() ); dt->length = updateBitStream.GetNumberOfBytesUsed(); dt->sendTime = time + 1 + ( randomMT() % 100 ); delayList.insert( dt ); #endif } else break; } #ifdef _INTERNET_SIMULATOR // Do any lagged sends unsigned i = 0; while ( i < delayList.size() ) { if ( delayList[ i ]->sendTime < time ) { updateBitStream.Reset(); updateBitStream.Write( delayList[ i ]->data, delayList[ i ]->length ); // Send it now SendBitStream( s, playerId, &updateBitStream ); delete delayList[ i ]; if (i != delayList.size() - 1) delayList[ i ] = delayList[ delayList.size() - 1 ]; delayList.del(); } else i++; } #endif //reliabilityLayerMutexes[updateBitStream_MUTEX].Unlock(); } //------------------------------------------------------------------------------------------------------- // Writes a bitstream to the socket //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::SendBitStream( SOCKET s, PlayerID playerId, RakNet::BitStream *bitStream ) { // SHOW - showing reliable flow // if (bitStream->GetNumberOfBytesUsed()>50) // printf("Sending %i bytes. sendQueue[0].size()=%i, resendQueue.size()=%i\n", bitStream->GetNumberOfBytesUsed(), sendQueue[0].size(),resendQueue.size()); int oldLength, length; // sentFrames++; #ifdef _INTERNET_SIMULATOR // packetloss //if (windowSize>MINIMUM_WINDOW_SIZE && frandomMT() <= (float)(windowSize-MINIMUM_WINDOW_SIZE)/(float)(MAXIMUM_WINDOW_SIZE-MINIMUM_WINDOW_SIZE)) if (frandomMT() <= .05f) { // printf("Frame %i lost\n", sentFrames); return; } #endif // Encode the whole bitstream if the encoder is defined. if ( encryptor.IsKeySet() ) { length = bitStream->GetNumberOfBytesUsed(); oldLength = length; encryptor.Encrypt( ( unsigned char* ) bitStream->GetData(), length, ( unsigned char* ) bitStream->GetData(), &length ); statistics.encryptionBitsSent = ( length - oldLength ) * 8; assert( ( length % 16 ) == 0 ); } else { length = bitStream->GetNumberOfBytesUsed(); } #ifdef __USE_IO_COMPLETION_PORTS if ( readWriteSocket == INVALID_SOCKET ) { assert( 0 ); return ; } statistics.packetsSent++; statistics.totalBitsSent += length * 8; SocketLayer::Instance()->Write( readWriteSocket, ( const char* ) bitStream->GetData(), length ); #else statistics.packetsSent++; statistics.totalBitsSent += length * 8; //printf("total bits=%i length=%i\n", BITS_TO_BYTES(statistics.totalBitsSent), length); SocketLayer::Instance()->SendTo( s, ( char* ) bitStream->GetData(), length, playerId.binaryAddress, playerId.port ); #endif // __USE_IO_COMPLETION_PORTS // lastPacketSendTime=time; } //------------------------------------------------------------------------------------------------------- // Returns true if we can or should send a frame. False if we should not //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsFrameReady( unsigned int time ) { if ( IsSendThrottled() == false ) { // Show send throttled // printf("Send is throttled. resendQueue.size=%i windowSize=%i\n", resendQueue.size(), windowSize); return true; } //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); // Any acknowledgement packets waiting? We will send these even if the send is throttled. // Otherwise the throttle may never end if ( acknowledgementQueue.size() >= MINIMUM_WINDOW_SIZE // Try not waiting to send acks - will take more bandwidth but maybe less packetloss // || acknowledgementQueue.peek()->nextActionTime < time ) { //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); return true; } // reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); // Does the oldest packet need to be resent? If so, send it. // Otherwise the throttle may never end // reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); if ( resendQueue.size() > 0 && resendQueue.peek() && resendQueue.peek()->nextActionTime < time ) { // reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); return true; } // reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); // Send is throttled. Don't send. return false; } //------------------------------------------------------------------------------------------------------- // Generates a frame (coalesced packets) //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::GenerateFrame( RakNet::BitStream *output, int MTUSize, bool *reliableDataSent, unsigned int time ) { InternalPacket * internalPacket; int maxDataBitSize; int reliableBits = 0; int nextPacketBitLength; unsigned i; bool isReliable, onlySendUnreliable; bool acknowledgementPacketsSent; bool anyPacketsLost = false; maxDataBitSize = MTUSize - UDP_HEADER_SIZE; if ( encryptor.IsKeySet() ) maxDataBitSize -= 16; // Extra data for the encryptor maxDataBitSize <<= 3; acknowledgementPacketsSent = false; *reliableDataSent = false; // Packet acknowledgements always go out first if they are overdue or if there are a lot of them //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); // reliabilityLayerMutexes[remoteFramesAwaitingAck_MUTEX].Lock(); if ( acknowledgementQueue.size() > 0 && ( acknowledgementQueue.size() >= MINIMUM_WINDOW_SIZE || acknowledgementQueue.peek()->nextActionTime < time ) ) { do { // reliabilityLayerMutexes[remoteFramesAwaitingAck_MUTEX].Unlock(); internalPacket = acknowledgementQueue.pop(); //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); // Write the acknowledgement to the output bitstream statistics.acknowlegementsSent++; statistics.acknowlegementBitsSent += WriteToBitStreamFromInternalPacket( output, internalPacket ); acknowledgementPacketsSent = true; // Delete the acknowledgement internalPacketPool.ReleasePointer( internalPacket ); if ( output->GetNumberOfBitsUsed() + ACK_BIT_LENGTH > maxDataBitSize ) { // SHOW - show ack // printf("Sending FULL ack (%i) at time %i. acknowledgementQueue.size()=%i\n", output->GetNumberOfBytesUsed(), RakNet::GetTime(),acknowledgementQueue.size()); statistics.packetsContainingOnlyAcknowlegements++; // Show - Frame full // printf("Frame full in sending acks\n"); goto END_OF_GENERATE_FRAME; } // reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); } while ( acknowledgementQueue.size() > 0 ); } //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); // SHOW - show ack //if (output->GetNumberOfBitsUsed()>0) // printf("Sending ack (%i) at time %i. acknowledgementQueue.size()=%i\n", output->GetNumberOfBytesUsed(), RakNet::GetTime(),acknowledgementQueue.size()); //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); // The resend Queue can have NULL pointer holes. This is so we can deallocate blocks without having to compress the array while ( resendQueue.size() > 0 ) { if ( resendQueue.peek() == 0 ) { resendQueue.pop(); continue; // This was a hole } if ( resendQueue.peek()->nextActionTime < time ) { internalPacket = resendQueue.pop(); //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); // Testing //printf("Resending %i. queue size = %i\n", internalPacket->packetNumber, resendQueue.size()); nextPacketBitLength = GetBitStreamHeaderLength( internalPacket ) + internalPacket->dataBitLength; if ( output->GetNumberOfBitsUsed() + nextPacketBitLength > maxDataBitSize ) { //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); resendQueue.pushAtHead( internalPacket ); // Not enough room to use this packet after all! //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); if ( anyPacketsLost ) { UpdatePacketloss( time ); } // Show - Frame full //printf("Frame full in sending resends\n"); goto END_OF_GENERATE_FRAME; } #ifdef _DEBUG assert( internalPacket->priority >= 0 ); assert( internalPacket->reliability >= 0 ); #endif // SHOW - show resends //printf("Resending packet. resendQueue.size()=%i. Data=%s\n",resendQueue.size(), internalPacket->data); // Write to the output bitstream // sentPackets++; statistics.messageResends++; statistics.messageDataBitsResent += internalPacket->dataBitLength; statistics.messagesTotalBitsResent += WriteToBitStreamFromInternalPacket( output, internalPacket ); *reliableDataSent = true; // if (output->GetNumberOfBitsUsed() + ACK_BIT_LENGTH > maxDataBitSize) // printf("Frame full of just acks and resends at time %i.\n", RakNet::GetTime()); statistics.packetsContainingOnlyAcknowlegementsAndResends++; anyPacketsLost = true; internalPacket->nextActionTime = time + lostPacketResendDelay; // Put the packet back into the resend list at the correct spot // Don't make a copy since I'm reinserting an allocated struct InsertPacketIntoResendQueue( internalPacket, time, false, false ); //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); } else { break; } } //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); if ( anyPacketsLost ) { // Update packetloss UpdatePacketloss( time ); } onlySendUnreliable = false; if ( IsSendThrottled() ) return ; // Don't send regular data if we are supposed to be waiting on the window // From highest to lowest priority, fill up the output bitstream from the send lists for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) { // if (i==LOW_PRIORITY && sendQueue[LOW_PRIORITY].size() > 0 && (sendQueue[LOW_PRIORITY].size()%100)==0) // { // printf("%i\n", sendQueue[LOW_PRIORITY].size()); // } // Not mutexed - may give a wrong value if another thread is inserting something but it's ok // Because we can avoid a slow mutex call a lot of the time //if ( sendQueue[ i ].size() == 0 ) // continue; // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Lock(); while ( sendPacketSet[ i ].size() ) //while ( (internalPacket=sendPacketSet[i].ReadLock())!=0 ) { internalPacket = sendPacketSet[ i ].pop(); // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Unlock(); nextPacketBitLength = GetBitStreamHeaderLength( internalPacket ) + internalPacket->dataBitLength; if ( output->GetNumberOfBitsUsed() + nextPacketBitLength > maxDataBitSize ) { // This output won't fit. // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Lock(); sendPacketSet[ i ].pushAtHead( internalPacket ); // Push this back at the head so it is the next thing to go out // sendPacketSet[i].CancelReadLock(internalPacket); break; } if ( internalPacket->reliability == RELIABLE || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) isReliable = true; else isReliable = false; // Write to the output bitstream // sentPackets++; statistics.messagesSent[ i ] ++; statistics.messageDataBitsSent[ i ] += internalPacket->dataBitLength; // printf("Writing send packet %i to bitstream.\n", internalPacket->packetNumber); if (internalPacket->data[0]==49) { unsigned short packetPort; memcpy((char*)&packetPort, internalPacket->data+1, sizeof(unsigned short)); } statistics.messageTotalBitsSent[ i ] += WriteToBitStreamFromInternalPacket( output, internalPacket ); if ( isReliable ) { // Reliable packets are saved to resend later reliableBits += internalPacket->dataBitLength; internalPacket->nextActionTime = time + lostPacketResendDelay; // Third param is true to make a copy because this data is from a producer consumer pool and can't be stored out of order //InsertPacketIntoResendQueue( internalPacket, time, true, true ); InsertPacketIntoResendQueue( internalPacket, time, false, true ); *reliableDataSent = true; } else { // Unreliable packets are deleted delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); } //sendPacketSet[i].ReadUnlock(); // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Lock(); } // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + i ].Unlock(); } // Optimization - if we sent data but didn't send an acknowledgement packet previously then send them now if ( acknowledgementPacketsSent == false && output->GetNumberOfBitsUsed() > 0 ) { if ( acknowledgementQueue.size() > 0 ) { //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); while ( output->GetNumberOfBitsUsed() + ACK_BIT_LENGTH < maxDataBitSize && acknowledgementQueue.size() > 0 ) { internalPacket = acknowledgementQueue.pop(); #ifdef _DEBUG internalPacket->data=0; assert(internalPacket->isAcknowledgement==true); #endif //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); // Write the acknowledgement to the output bitstream WriteToBitStreamFromInternalPacket( output, internalPacket ); #ifdef _DEBUG assert(internalPacket->data==0); #endif // Delete the acknowledgement internalPacketPool.ReleasePointer( internalPacket ); //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); } //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); } } END_OF_GENERATE_FRAME: ; // if (output->GetNumberOfBitsUsed()>0) // { // Update the throttle with the header // bytesSent+=output->GetNumberOfBytesUsed() + UDP_HEADER_SIZE; //} } //------------------------------------------------------------------------------------------------------- // Are we waiting for any data to be sent out or be processed by the player? //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsDataWaiting(void) { unsigned i; for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) { if (sendPacketSet[ i ].size() > 0) return true; } return acknowledgementQueue.size() > 0 || resendQueue.size() > 0 || outputQueue.size() > 0 || orderingList.size() > 0 || splitPacketList.size() > 0; } //------------------------------------------------------------------------------------------------------- // This will return true if we should not send at this time //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsSendThrottled( void ) { return ( int ) GetResendQueueDataSize() >= windowSize; } //------------------------------------------------------------------------------------------------------- // We lost a packet //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::UpdatePacketloss( unsigned int time ) { // unsigned int time = RakNet::GetTime(); /* maximumWindowSize = (unsigned int)((double)maximumWindowSize * DECREASE_THROUGHPUT_DELTA); if (maximumWindowSize < MINIMUM_THROUGHPUT) { maximumWindowSize = MINIMUM_THROUGHPUT; } */ //printf("Lost packet. resendQueue.size()=%i sendQueue[0].size() = %i\n",resendQueue.size(), sendQueue[0].size()); // reliabilityLayerMutexes[windowSize_MUTEX].Lock(); // reliabilityLayerMutexes[windowSize_MUTEX].Unlock(); // retransmittedFrames++; // The window size will decrease everytime we have to retransmit a frame //reliabilityLayerMutexes[windowSize_MUTEX].Lock(); if ( --windowSize < MINIMUM_WINDOW_SIZE ) windowSize = MINIMUM_WINDOW_SIZE; //reliabilityLayerMutexes[windowSize_MUTEX].Unlock(); lossyWindowSize = windowSize; lastWindowIncreaseSizeTime = time; // This will block the window size from increasing immediately // SHOW - windowing //if (resendQueue.size()>0) //printf("Frame lost. New window size = %i. Lossy window size = %i. Time=%i. Next send time=%i\n", windowSize, lossyWindowSize, RakNet::GetTime(),resendQueue.peek()->nextActionTime); } //------------------------------------------------------------------------------------------------------- // Does what the function name says //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::RemovePacketFromResendQueueAndDeleteOlderReliableSequenced( PacketNumberType packetNumber ) { InternalPacket * internalPacket; PacketReliability reliability; // What type of reliability algorithm to use with this packet unsigned char orderingChannel; // What ordering channel this packet is on, if the reliability type uses ordering channels OrderingIndexType orderingIndex; // The ID used as identification for ordering channels // reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); for ( unsigned i = 0; i < resendQueue.size(); i ++ ) { if ( resendQueue[i] && packetNumber == resendQueue[i]->packetNumber ) { // Found what we wanted to ack statistics.acknowlegementsReceived++; if ( i == 0 ) internalPacket = resendQueue.pop(); else { // Generate a hole internalPacket = resendQueue[ i ]; // testing // printf("Removing packet %i from resend\n", internalPacket->packetNumber); resendQueue[ i ] = 0; } //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); // Save some of the data of the packet reliability = internalPacket->reliability; orderingChannel = internalPacket->orderingChannel; orderingIndex = internalPacket->orderingIndex; // Delete the packet //printf("Deleting %i\n", internalPacket->data); delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); // If the deleted packet was reliable sequenced, also delete all older reliable sequenced resends on the same ordering channel. // This is because we no longer need to send these. if ( reliability == RELIABLE_SEQUENCED ) { unsigned j = 0; //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); while ( j < resendQueue.size() ) { internalPacket = resendQueue[ j ]; if ( internalPacket && internalPacket->reliability == RELIABLE_SEQUENCED && internalPacket->orderingChannel == orderingChannel && IsOlderOrderedPacket( internalPacket->orderingIndex, orderingIndex ) ) { // Delete the packet delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); resendQueue[ j ] = 0; // Generate a hole } j++; } // reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); } //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); return ; } } //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); // Didn't find what we wanted to ack statistics.duplicateAcknowlegementsReceived++; } //------------------------------------------------------------------------------------------------------- // Acknowledge receipt of the packet with the specified packetNumber //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::SendAcknowledgementPacket( PacketNumberType packetNumber, unsigned int time ) { InternalPacket * internalPacket; // Disabled - never gets called anyway so just wastes CPU cycles /* // High load optimization - if there are over 100 acks waiting scan the list to make sure what we are adding isn't already scheduled to go out reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); size = acknowledgementQueue.size(); if (size>100) { for (i=0; i < size; i++) { internalPacket=acknowledgementQueue[i]; if (internalPacket && internalPacket->packetNumber==packetNumber) { reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); //printf("Eliminating duplicate ack. acknowledgementQueue.size()=%i\n",acknowledgementQueue.size()); return; // No need to add it - it is already here } } } reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); */ internalPacket = internalPacketPool.GetPointer(); #ifdef _DEBUG // Remove boundschecker accessing undefined memory error memset( internalPacket, 255, sizeof( InternalPacket ) ); #endif internalPacket->packetNumber = packetNumber; internalPacket->isAcknowledgement = true; internalPacket->creationTime = time; // We send this acknowledgement no later than 1/4 the time the remote //machine would send the original packet again // DEBUG internalPacket->nextActionTime = internalPacket->creationTime + ( lostPacketResendDelay >> 2 ); //internalPacket->nextActionTime = internalPacket->creationTime; //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Lock(); acknowledgementQueue.push( internalPacket ); // printf("Adding ack at time %i. acknowledgementQueue.size=%i\n",RakNet::GetTime(), acknowledgementQueue.size()); //reliabilityLayerMutexes[acknowledgementQueue_MUTEX].Unlock(); } //------------------------------------------------------------------------------------------------------- // Parse an internalPacket and figure out how many header bits would be // written. Returns that number //------------------------------------------------------------------------------------------------------- int ReliabilityLayer::GetBitStreamHeaderLength( const InternalPacket *const internalPacket ) { #ifdef _DEBUG assert( internalPacket ); #endif int bitLength; if ( internalPacket->isAcknowledgement ) return ACK_BIT_LENGTH; // Write if this packet has a security header (1 bit) //bitStream->Write(internalPacket->hasSecurityHeader); // -- bitLength+=1; bitLength = ACK_BIT_LENGTH; // Write the PacketReliability. This is encoded in 3 bits //bitStream->WriteBits((unsigned char*)&(internalPacket->reliability), 3, true); bitLength += 3; // If the reliability requires an ordering channel and ordering index, we Write those. if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) { // ordering channel encoded in 5 bits (from 0 to 31) //bitStream->WriteBits((unsigned char*)&(internalPacket->orderingChannel), 5, true); bitLength+=5; // ordering index is one byte //bitStream->WriteCompressed(internalPacket->orderingIndex); bitLength+=sizeof(OrderingIndexType)*8; } // Write if this is a split packet (1 bit) bool isSplitPacket = internalPacket->splitPacketCount > 0; //bitStream->Write(isSplitPacket); bitLength += 1; if ( isSplitPacket ) { // split packet indices are two bytes (so one packet can be split up to 65535 // times - maximum packet size would be about 500 * 65535) //bitStream->Write(internalPacket->splitPacketId); //bitStream->WriteCompressed(internalPacket->splitPacketIndex); //bitStream->WriteCompressed(internalPacket->splitPacketCount); bitLength += 3 * 8 * 2; } // Write how many bits the packet data is. Stored in an unsigned short and // read from 16 bits //bitStream->WriteBits((unsigned char*)&(internalPacket->dataBitLength), 16, true); // Read how many bits the packet data is. Stored in 16 bits bitLength += 16; // Byte alignment //bitLength += 8 - ((bitLength -1) %8 + 1); return bitLength; } //------------------------------------------------------------------------------------------------------- // Parse an internalPacket and create a bitstream to represent this data //------------------------------------------------------------------------------------------------------- int ReliabilityLayer::WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket ) { #ifdef _DEBUG assert( bitStream && internalPacket ); #endif int start = bitStream->GetNumberOfBitsUsed(); // testing //if (internalPacket->reliability==UNRELIABLE) // printf("Sending unreliable packet %i\n", internalPacket->packetNumber); //else if (internalPacket->reliability==RELIABLE_SEQUENCED || internalPacket->reliability==RELIABLE_ORDERED || internalPacket->reliability==RELIABLE) // printf("Sending reliable packet number %i\n", internalPacket->packetNumber); //bitStream->AlignWriteToByteBoundary(); // Write the packet number (2 bytes) bitStream->Write( internalPacket->packetNumber ); // Write if this packet is an acknowledgement (1 bit) bitStream->Write( internalPacket->isAcknowledgement ); // Acknowledgement packets have no more data than the packetnumber and whether it is an acknowledgement if ( internalPacket->isAcknowledgement ) { return bitStream->GetNumberOfBitsUsed() - start; } #ifdef _DEBUG assert( internalPacket->dataBitLength > 0 ); #endif // Write the PacketReliability. This is encoded in 3 bits unsigned char reliability = (unsigned char) internalPacket->reliability; bitStream->WriteBits( ( unsigned char* ) ( &( reliability ) ), 3, true ); // If the reliability requires an ordering channel and ordering index, we Write those. if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) { // ordering channel encoded in 5 bits (from 0 to 31) bitStream->WriteBits( ( unsigned char* ) & ( internalPacket->orderingChannel ), 5, true ); // One or two bytes bitStream->Write( internalPacket->orderingIndex ); } // Write if this is a split packet (1 bit) bool isSplitPacket = internalPacket->splitPacketCount > 0; bitStream->Write( isSplitPacket ); if ( isSplitPacket ) { // split packet indices are two bytes (so one packet can be split up to 65535 times - maximum packet size would be about 500 * 65535) bitStream->Write( internalPacket->splitPacketId ); bitStream->WriteCompressed( internalPacket->splitPacketIndex ); bitStream->WriteCompressed( internalPacket->splitPacketCount ); } // Write how many bits the packet data is. Stored in 13 bits #ifdef _DEBUG assert( BITS_TO_BYTES( internalPacket->dataBitLength ) < MAXIMUM_MTU_SIZE ); // I never send more than MTU_SIZE bytes #endif unsigned short length = ( unsigned short ) internalPacket->dataBitLength; // Ignore the 2 high bytes for WriteBits bitStream->WriteCompressed( length ); // Write the actual data. bitStream->WriteAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); //bitStream->WriteBits((unsigned char*)internalPacket->data, internalPacket->dataBitLength); return bitStream->GetNumberOfBitsUsed() - start; } //------------------------------------------------------------------------------------------------------- // Parse a bitstream and create an internal packet to represent this data //------------------------------------------------------------------------------------------------------- InternalPacket* ReliabilityLayer::CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, unsigned int time ) { bool bitStreamSucceeded; InternalPacket* internalPacket; if ( bitStream->GetNumberOfUnreadBits() < sizeof( internalPacket->packetNumber ) * 8 ) return 0; // leftover bits internalPacket = internalPacketPool.GetPointer(); #ifdef _DEBUG // Remove accessing undefined memory error memset( internalPacket, 255, sizeof( InternalPacket ) ); #endif internalPacket->creationTime = time; //bitStream->AlignReadToByteBoundary(); // Read the packet number (2 bytes) bitStreamSucceeded = bitStream->Read( internalPacket->packetNumber ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } // Read if this packet is an acknowledgement (1 bit) bitStreamSucceeded = bitStream->Read( internalPacket->isAcknowledgement ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } // Acknowledgement packets have no more data than the packetnumber and whether it is an acknowledgement if ( internalPacket->isAcknowledgement ) return internalPacket; // Read the PacketReliability. This is encoded in 3 bits unsigned char reliability; bitStreamSucceeded = bitStream->ReadBits( ( unsigned char* ) ( &( reliability ) ), 3 ); internalPacket->reliability = ( PacketReliability ) reliability; #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets // assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } // If the reliability requires an ordering channel and ordering index, we read those. if ( internalPacket->reliability == UNRELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_SEQUENCED || internalPacket->reliability == RELIABLE_ORDERED ) { // ordering channel encoded in 5 bits (from 0 to 31) bitStreamSucceeded = bitStream->ReadBits( ( unsigned char* ) & ( internalPacket->orderingChannel ), 5 ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } bitStreamSucceeded = bitStream->Read( internalPacket->orderingIndex ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } } // Read if this is a split packet (1 bit) bool isSplitPacket; bitStreamSucceeded = bitStream->Read( isSplitPacket ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } if ( isSplitPacket ) { // split packet indices are one byte (so one packet can be split up to 65535 times - maximum packet size would be about 500 * 65535) bitStreamSucceeded = bitStream->Read( internalPacket->splitPacketId ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets // assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } bitStreamSucceeded = bitStream->ReadCompressed( internalPacket->splitPacketIndex ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } bitStreamSucceeded = bitStream->ReadCompressed( internalPacket->splitPacketCount ); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } } else internalPacket->splitPacketIndex = internalPacket->splitPacketCount = 0; // Optimization - do byte alignment here //unsigned char zero; //bitStream->ReadBits(&zero, 8 - (bitStream->GetNumberOfBitsUsed() %8)); //assert(zero==0); unsigned short length; bitStreamSucceeded = bitStream->ReadCompressed( length ); // Read into an unsigned short. Otherwise the data would be offset too high by two bytes #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); #endif if ( bitStreamSucceeded == false ) { internalPacketPool.ReleasePointer( internalPacket ); return 0; } internalPacket->dataBitLength = length; #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets arriving when the sender does not know we just connected, which is an unavoidable condition sometimes // assert( internalPacket->dataBitLength > 0 && BITS_TO_BYTES( internalPacket->dataBitLength ) < MAXIMUM_MTU_SIZE ); #endif if ( ! ( internalPacket->dataBitLength > 0 && BITS_TO_BYTES( internalPacket->dataBitLength ) < MAXIMUM_MTU_SIZE ) ) { // 10/08/05 - internalPacket->data wasn't allocated yet // delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); return 0; } // Allocate memory to hold our data internalPacket->data = new char [ BITS_TO_BYTES( internalPacket->dataBitLength ) ]; //printf("Allocating %i\n", internalPacket->data); // Set the last byte to 0 so if ReadBits does not read a multiple of 8 the last bits are 0'ed out internalPacket->data[ BITS_TO_BYTES( internalPacket->dataBitLength ) - 1 ] = 0; // Read the data the packet holds bitStreamSucceeded = bitStream->ReadAlignedBytes( ( unsigned char* ) internalPacket->data, BITS_TO_BYTES( internalPacket->dataBitLength ) ); //bitStreamSucceeded = bitStream->ReadBits((unsigned char*)internalPacket->data, internalPacket->dataBitLength); #ifdef _DEBUG // 10/08/05 - Disabled assert since this hits from offline packets //assert( bitStreamSucceeded ); if ( bitStreamSucceeded == false ) { delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); return 0; } #endif // PRINTING UNRELIABLE STRINGS // if (internalPacket->data && internalPacket->dataBitLength>5*8) // printf("Received %s\n",internalPacket->data); return internalPacket; } //------------------------------------------------------------------------------------------------------- // Get the SHA1 code //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ) { CSHA1 sha1; sha1.Reset(); sha1.Update( ( unsigned char* ) buffer, nbytes ); sha1.Final(); memcpy( code, sha1.GetHash(), SHA1_LENGTH ); } //------------------------------------------------------------------------------------------------------- // Check the SHA1 code //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ) { char code2[ SHA1_LENGTH ]; GetSHA1( buffer, nbytes, code2 ); for ( int i = 0; i < SHA1_LENGTH; i++ ) if ( code[ i ] != code2[ i ] ) return false; return true; } //------------------------------------------------------------------------------------------------------- // Search the specified list for sequenced packets on the specified ordering // stream, optionally skipping those with splitPacketId, and delete them //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, BasicDataStructures::List&theList, int splitPacketId ) { unsigned i = 0; while ( i < theList.size() ) { if ( ( theList[ i ]->reliability == RELIABLE_SEQUENCED || theList[ i ]->reliability == UNRELIABLE_SEQUENCED ) && theList[ i ]->orderingChannel == orderingChannel && ( splitPacketId == -1 || theList[ i ]->splitPacketId != (unsigned int) splitPacketId ) ) { InternalPacket * internalPacket = theList[ i ]; theList.del( i ); delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); } else i++; } } //------------------------------------------------------------------------------------------------------- // Search the specified list for sequenced packets with a value less than orderingIndex and delete them // Note - I added functionality so you can use the Queue as a list (in this case for searching) but it is less efficient to do so than a regular list //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::DeleteSequencedPacketsInList( unsigned char orderingChannel, BlobNet::ADT::Queue&theList ) { InternalPacket * internalPacket; int listSize = theList.size(); int i = 0; while ( i < listSize ) { if ( ( theList[ i ]->reliability == RELIABLE_SEQUENCED || theList[ i ]->reliability == UNRELIABLE_SEQUENCED ) && theList[ i ]->orderingChannel == orderingChannel ) { internalPacket = theList[ i ]; theList.del( i ); delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); listSize--; } else i++; } } //------------------------------------------------------------------------------------------------------- // Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ) { // This should give me 255 or 65535 OrderingIndexType maxRange = (OrderingIndexType) -1; if ( waitingForPacketOrderingIndex > maxRange/2 ) { if ( newPacketOrderingIndex >= waitingForPacketOrderingIndex - maxRange/2+1 && newPacketOrderingIndex < waitingForPacketOrderingIndex ) { return true; } } else if ( newPacketOrderingIndex >= ( OrderingIndexType ) ( waitingForPacketOrderingIndex - (( OrderingIndexType ) maxRange/2+1) ) || newPacketOrderingIndex < waitingForPacketOrderingIndex ) { return true; } // Old packet return false; } //------------------------------------------------------------------------------------------------------- // Split the passed packet into chunks under MTU_SIZEbytes (including headers) and save those new chunks // Optimized version //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::SplitPacket( InternalPacket *internalPacket, int MTUSize ) { // Doing all sizes in bytes in this function so I don't write partial bytes with split packets internalPacket->splitPacketCount = 1; // This causes GetBitStreamHeaderLength to account for the split packet header int headerLength = BITS_TO_BYTES( GetBitStreamHeaderLength( internalPacket ) ); int dataByteLength = BITS_TO_BYTES( internalPacket->dataBitLength ); int maxDataSize; int maximumSendBlock, byteOffset, bytesToSend; unsigned short splitPacketIndex; int i; InternalPacket **internalPacketArray; maxDataSize = MTUSize - UDP_HEADER_SIZE; if ( encryptor.IsKeySet() ) maxDataSize -= 16; // Extra data for the encryptor #ifdef _DEBUG // Make sure we need to split the packet to begin with assert( dataByteLength > maxDataSize - headerLength ); // If this assert is hit the packet is so tremendous we need to widen the split packet types. You should never send something that big anyway assert( ( dataByteLength - 1 ) / ( maxDataSize - headerLength ) + 1 < 65535 ); #endif // How much to send in the largest block maximumSendBlock = maxDataSize - headerLength; // Calculate how many packets we need to create internalPacket->splitPacketCount = ( unsigned short ) ( ( dataByteLength - 1 ) / ( maximumSendBlock ) + 1 ); statistics.totalSplits += internalPacket->splitPacketCount; // Optimization // internalPacketArray = new InternalPacket*[internalPacket->splitPacketCount]; internalPacketArray = ( InternalPacket** ) alloca( sizeof( InternalPacket* ) * internalPacket->splitPacketCount ); for ( i = 0; i < ( int ) internalPacket->splitPacketCount; i++ ) { internalPacketArray[ i ] = internalPacketPool.GetPointer(); //internalPacketArray[ i ] = (InternalPacket*) alloca( sizeof( InternalPacket ) ); // internalPacketArray[ i ] = sendPacketSet[internalPacket->priority].WriteLock(); memcpy( internalPacketArray[ i ], internalPacket, sizeof( InternalPacket ) ); } // This identifies which packet this is in the set splitPacketIndex = 0; // Do a loop to send out all the packets do { byteOffset = splitPacketIndex * maximumSendBlock; bytesToSend = dataByteLength - byteOffset; if ( bytesToSend > maximumSendBlock ) bytesToSend = maximumSendBlock; // Copy over our chunk of data internalPacketArray[ splitPacketIndex ]->data = new char[ bytesToSend ]; memcpy( internalPacketArray[ splitPacketIndex ]->data, internalPacket->data + byteOffset, bytesToSend ); if ( bytesToSend != maximumSendBlock ) internalPacketArray[ splitPacketIndex ]->dataBitLength = internalPacket->dataBitLength - splitPacketIndex * ( maximumSendBlock << 3 ); else internalPacketArray[ splitPacketIndex ]->dataBitLength = bytesToSend << 3; internalPacketArray[ splitPacketIndex ]->splitPacketIndex = splitPacketIndex; internalPacketArray[ splitPacketIndex ]->splitPacketId = splitPacketId; internalPacketArray[ splitPacketIndex ]->splitPacketCount = internalPacket->splitPacketCount; if ( splitPacketIndex > 0 ) // For the first split packet index we keep the packetNumber already assigned { // For every further packet we use a new packetNumber. // Note that all split packets are reliable //reliabilityLayerMutexes[ packetNumber_MUTEX ].Lock(); internalPacketArray[ splitPacketIndex ]->packetNumber = packetNumber; //if ( ++packetNumber == RECEIVED_PACKET_LOG_LENGTH ) // packetNumber = 0; ++packetNumber; //reliabilityLayerMutexes[ packetNumber_MUTEX ].Unlock(); } // Add the new packet to send list at the correct priority // reliabilityLayerMutexes[sendQueue_MUTEX].Lock(); // sendQueue[internalPacket->priority].insert(newInternalPacket); // reliabilityLayerMutexes[sendQueue_MUTEX].Unlock(); // SHOW SPLIT PACKET GENERATION // if (splitPacketIndex % 100 == 0) // printf("splitPacketIndex=%i\n",splitPacketIndex); //} while(++splitPacketIndex < internalPacket->splitPacketCount); } while ( ++splitPacketIndex < internalPacket->splitPacketCount ); splitPacketId++; // It's ok if this wraps to 0 // InternalPacket *workingPacket; // Copy all the new packets into the split packet list // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + internalPacket->priority ].Lock(); for ( i = 0; i < ( int ) internalPacket->splitPacketCount; i++ ) { sendPacketSet[ internalPacket->priority ].push( internalPacketArray[ i ] ); // workingPacket=sendPacketSet[internalPacket->priority].WriteLock(); // memcpy(workingPacket, internalPacketArray[ i ], sizeof(InternalPacket)); // sendPacketSet[internalPacket->priority].WriteUnlock(); } // reliabilityLayerMutexes[ sendQueueSystemPriority_MUTEX + internalPacket->priority ].Unlock(); // Delete the original delete [] internalPacket->data; internalPacketPool.ReleasePointer( internalPacket ); //delete [] internalPacketArray; } //------------------------------------------------------------------------------------------------------- // Insert a packet into the split packet list //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::InsertIntoSplitPacketList( InternalPacket * internalPacket ) { //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); splitPacketList.insert( internalPacket ); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); } //------------------------------------------------------------------------------------------------------- // Take all split chunks with the specified splitPacketId and try to //reconstruct a packet. If we can, allocate and return it. Otherwise return 0 // Optimized version //------------------------------------------------------------------------------------------------------- InternalPacket * ReliabilityLayer::BuildPacketFromSplitPacketList( unsigned int splitPacketId, unsigned int time ) { int i, j, size; // How much data all blocks but the last hold int maxDataSize; int numParts; int bitlength; int *indexList; int indexListIndex; //reliabilityLayerMutexes[splitPacketList_MUTEX].Lock(); size = splitPacketList.size(); for ( i = 0; i < size; i++ ) { if ( splitPacketList[ i ]->splitPacketId == splitPacketId ) { // Is there enough elements in the list to have all the parts? if ( splitPacketList[ i ]->splitPacketCount > splitPacketList.size() - i ) { // if (splitPacketList.size() % 100 == 0 || splitPacketList[i]->splitPacketCount-splitPacketList.size()<100) // printf("%i out of %i\n", splitPacketList.size(), splitPacketList[i]->splitPacketCount); //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); return 0; } // printf("%i out of %i\n", splitPacketList.size(), splitPacketList[i]->splitPacketCount); // Keep track of the indices of the elements through our first scan so we don't have to rescan to find them indexListIndex = 0; numParts = 1; bitlength = splitPacketList[ i ]->dataBitLength; // indexList = new int[splitPacketList[i]->splitPacketCount]; indexList = ( int* ) alloca( sizeof( int ) * splitPacketList[ i ]->splitPacketCount ); indexList[ indexListIndex++ ] = i; maxDataSize = BITS_TO_BYTES( splitPacketList[ i ]->dataBitLength ); // Are all the parts there? for ( j = i + 1; j < size; j++ ) { if ( splitPacketList[ j ]->splitPacketId == splitPacketId ) { indexList[ indexListIndex++ ] = j; numParts++; bitlength += splitPacketList[ j ]->dataBitLength; // Verify that we are dealing with the same splitPacketId #ifdef _DEBUG assert(splitPacketList[ j ]->splitPacketCount==splitPacketList[ i ]->splitPacketCount); #endif if ( ( int ) BITS_TO_BYTES( splitPacketList[ j ]->dataBitLength ) > maxDataSize ) maxDataSize = BITS_TO_BYTES( splitPacketList[ j ]->dataBitLength ); } } if ( (unsigned int)numParts == splitPacketList[ i ]->splitPacketCount ) { int allocatedLength; // All the parts are here InternalPacket * internalPacket = CreateInternalPacketCopy( splitPacketList[ i ], 0, 0, time ); allocatedLength=BITS_TO_BYTES( bitlength ); internalPacket->data = new char[ allocatedLength ]; #ifdef _DEBUG internalPacket->splitPacketCount = splitPacketList[ i ]->splitPacketCount; #endif // Add each part to internalPacket j = 0; while ( j < indexListIndex ) { if ( splitPacketList[ indexList[ j ] ]->splitPacketCount-1 == splitPacketList[ indexList[ j ] ]->splitPacketIndex ) { // Last split packet // If this assert fails, // then the total bit length calculated by adding the last block to the maximum block size * the number of blocks that are not the last block // doesn't match the amount calculated from traversing the list #ifdef _DEBUG assert( BITS_TO_BYTES( splitPacketList[ indexList[ j ] ]->dataBitLength ) + splitPacketList[ indexList[ j ] ]->splitPacketIndex * (unsigned)maxDataSize == ( (unsigned)bitlength - 1 ) / 8 + 1 ); #endif if (splitPacketList[ indexList[ j ] ]->splitPacketIndex * (unsigned int) maxDataSize + (unsigned int) BITS_TO_BYTES( splitPacketList[ indexList[ j ] ]->dataBitLength ) > (unsigned int) allocatedLength ) { // Watch for buffer overruns #ifdef _DEBUG assert(0); #endif delete internalPacket->data; internalPacketPool.ReleasePointer(internalPacket); return 0; } memcpy( internalPacket->data + splitPacketList[ indexList[ j ] ]->splitPacketIndex * maxDataSize, splitPacketList[ indexList[ j ] ]->data, BITS_TO_BYTES( splitPacketList[ indexList[ j ] ]->dataBitLength ) ); } else { if (splitPacketList[ indexList[ j ] ]->splitPacketIndex * (unsigned int) maxDataSize + (unsigned int) maxDataSize > (unsigned int) allocatedLength ) { // Watch for buffer overruns #ifdef _DEBUG assert(0); #endif delete internalPacket->data; internalPacketPool.ReleasePointer(internalPacket); return 0; } // Not last split packet memcpy( internalPacket->data + splitPacketList[ indexList[ j ] ]->splitPacketIndex * maxDataSize, splitPacketList[ indexList[ j ] ]->data, maxDataSize ); } internalPacket->dataBitLength += splitPacketList[ indexList[ j ] ]->dataBitLength; InternalPacket *temp; temp = splitPacketList[ indexList[ j ] ]; delete [] temp->data; internalPacketPool.ReleasePointer( temp ); splitPacketList[ indexList[ j ] ] = 0; #ifdef _DEBUG numParts--; #endif j++; size--; } #ifdef _DEBUG assert( numParts == 0 ); // Make sure the correct # of elements was removed from the list #endif j = 0; while ( ( unsigned ) j < splitPacketList.size() ) if ( splitPacketList[ j ] == 0 ) { // Since order doesn't matter, swap from the tail to the current element. splitPacketList[ j ] = splitPacketList[ splitPacketList.size() - 1 ]; splitPacketList[ splitPacketList.size() - 1 ] = 0; // Then just delete the tail (just changes a counter) splitPacketList.del( splitPacketList.size() - 1 ); } else j++; //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); // delete [] indexList; return internalPacket; } // delete [] indexList; break; } } //reliabilityLayerMutexes[splitPacketList_MUTEX].Unlock(); return 0; } // Delete any unreliable split packets that have long since expired void ReliabilityLayer::DeleteOldUnreliableSplitPackets( unsigned int time ) { unsigned size, i, orderingIndexToDelete; unsigned int newestUnreliableSplitPacket; bool found; InternalPacket *temp; // Scan through the list for split packets that were sent unreliably. // If the newest unreliable split packet for a particular ID is more than 3000 ms old, then // delete all of them of that id size = splitPacketList.size(); newestUnreliableSplitPacket = 0; found = false; for ( i = 0; i < size; i++ ) { if ( ( splitPacketList[ i ]->reliability == UNRELIABLE || splitPacketList[ i ]->reliability == UNRELIABLE_SEQUENCED ) && splitPacketList[ i ]->creationTime >= newestUnreliableSplitPacket ) { orderingIndexToDelete = splitPacketList[ i ]->orderingIndex; newestUnreliableSplitPacket = splitPacketList[ i ]->creationTime; found = true; } } if ( found && time>newestUnreliableSplitPacket && time-newestUnreliableSplitPacket > 5000 ) { // Delete all split packets that use orderingIndexToDelete i = 0; while ( i < splitPacketList.size() ) { #pragma warning( disable : 4701 ) // warning C4701: local variable 'orderingIndexToDelete' may be used without having been initialized if ( splitPacketList[ i ]->orderingIndex == orderingIndexToDelete ) { temp = splitPacketList[ i ]; splitPacketList[ i ] = splitPacketList[ splitPacketList.size() - 1 ]; splitPacketList.del(); // Removes the last element delete [] temp->data; internalPacketPool.ReleasePointer( temp ); } else i++; } } } //------------------------------------------------------------------------------------------------------- // Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. // Does not copy any split data parameters as that information is always generated does not have any reason to be copied //------------------------------------------------------------------------------------------------------- InternalPacket * ReliabilityLayer::CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, unsigned int time ) { InternalPacket * copy = internalPacketPool.GetPointer(); #ifdef _DEBUG // Remove boundschecker accessing undefined memory error memset( copy, 255, sizeof( InternalPacket ) ); #endif // Copy over our chunk of data if ( dataByteLength > 0 ) { copy->data = new char[ dataByteLength ]; memcpy( copy->data, original->data + dataByteOffset, dataByteLength ); } else copy->data = 0; copy->dataBitLength = dataByteLength << 3; copy->creationTime = time; copy->isAcknowledgement = original->isAcknowledgement; copy->nextActionTime = 0; copy->orderingIndex = original->orderingIndex; copy->orderingChannel = original->orderingChannel; copy->packetNumber = original->packetNumber; copy->priority = original->priority; copy->reliability = original->reliability; return copy; } //------------------------------------------------------------------------------------------------------- // Get the specified ordering list // LOCK THIS WHOLE BLOCK WITH reliabilityLayerMutexes[orderingList_MUTEX].Unlock(); //------------------------------------------------------------------------------------------------------- BasicDataStructures::LinkedList *ReliabilityLayer::GetOrderingListAtOrderingStream( unsigned char orderingChannel ) { if ( orderingChannel >= orderingList.size() ) return 0; return orderingList[ orderingChannel ]; } //------------------------------------------------------------------------------------------------------- // Add the internal packet to the ordering list in order based on order index //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::AddToOrderingList( InternalPacket * internalPacket ) { #ifdef _DEBUG assert( internalPacket->orderingChannel < NUMBER_OF_ORDERED_STREAMS ); #endif if ( internalPacket->orderingChannel >= NUMBER_OF_ORDERED_STREAMS ) { return; } BasicDataStructures::LinkedList *theList; if ( internalPacket->orderingChannel >= orderingList.size() || orderingList[ internalPacket->orderingChannel ] == 0 ) { // Need a linked list in this index orderingList.replace( new BasicDataStructures::LinkedList, 0, internalPacket->orderingChannel ); theList=orderingList[ internalPacket->orderingChannel ]; } else { // Have a linked list in this index if ( orderingList[ internalPacket->orderingChannel ]->size() == 0 ) { theList=orderingList[ internalPacket->orderingChannel ]; } else { theList = GetOrderingListAtOrderingStream( internalPacket->orderingChannel ); } } theList->end(); theList->add(internalPacket); } //------------------------------------------------------------------------------------------------------- // Inserts a packet into the resend list in order // THIS WHOLE FUNCTION SHOULD BE LOCKED WITH // reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::InsertPacketIntoResendQueue( InternalPacket *internalPacket, unsigned int time, bool makeCopyOfInternalPacket, bool resetAckTimer ) { //reliabilityLayerMutexes[lastAckTime_MUTEX].Lock(); if ( lastAckTime == 0 || resetAckTimer ) lastAckTime = time; // Start the timer for the ack of this packet if we aren't already waiting for an ack //reliabilityLayerMutexes[lastAckTime_MUTEX].Unlock(); //reliabilityLayerMutexes[resendQueue_MUTEX].Lock(); if (makeCopyOfInternalPacket) { InternalPacket *pool=internalPacketPool.GetPointer(); //printf("Adding %i\n", internalPacket->data); memcpy(pool, internalPacket, sizeof(InternalPacket)); resendQueue.push( pool ); } else { resendQueue.push( internalPacket ); } //reliabilityLayerMutexes[resendQueue_MUTEX].Unlock(); } //------------------------------------------------------------------------------------------------------- // If Read returns -1 and this returns true then a modified packet was detected //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsCheater( void ) const { return cheater; } //------------------------------------------------------------------------------------------------------- // Were you ever unable to deliver a packet despite retries? //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsDeadConnection( void ) const { return deadConnection; } //------------------------------------------------------------------------------------------------------- // Causes IsDeadConnection to return true //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::KillConnection( void ) { deadConnection=true; } //------------------------------------------------------------------------------------------------------- // How long to wait between packet resends //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::SetLostPacketResendDelay( unsigned int i ) { if ( i > 0 ) lostPacketResendDelay = i; if ( lostPacketResendDelay < 150 ) // To avoid unnecessary packetloss, this value should be UPDATE_THREAD_UPDATE_TIME + UPDATE_THREAD_POLL_TIME at a minimum lostPacketResendDelay = 150; } //------------------------------------------------------------------------------------------------------- // Statistics //------------------------------------------------------------------------------------------------------- RakNetStatisticsStruct * const ReliabilityLayer::GetStatistics( void ) { int i; for ( i = 0; i < NUMBER_OF_PRIORITIES; i++ ) { statistics.messageSendBuffer[i] = sendPacketSet[i].size(); // statistics.messageSendBuffer[i] = sendPacketSet[i].Size(); } statistics.acknowlegementsPending = acknowledgementQueue.size(); statistics.messagesWaitingForReassembly = splitPacketList.size(); statistics.internalOutputQueueSize = outputQueue.size(); statistics.windowSize = windowSize; statistics.lossySize = lossyWindowSize == MAXIMUM_WINDOW_SIZE + 1 ? 0 : lossyWindowSize; statistics.messagesOnResendQueue = GetResendQueueDataSize(); return &statistics; } //------------------------------------------------------------------------------------------------------- // Decodes the time given and returns if that time should be removed from the recieved packets list //------------------------------------------------------------------------------------------------------- bool ReliabilityLayer::IsExpiredTime(unsigned int input, unsigned int currentTime) const { // A time in the future is just a flag that this was a packet we never got (a hole). We still expire these // after shifting the value to normal time if (IsReceivedPacketHole(input, currentTime)) input -= TIMEOUT_TIME*100; if (input < currentTime - TIMEOUT_TIME) return true; return false; } //------------------------------------------------------------------------------------------------------- // Returns if this packet time is encoded to mean we never got this packet //------------------------------------------------------------------------------------------------------- unsigned int ReliabilityLayer::IsReceivedPacketHole(unsigned int input, unsigned int currentTime) const { return input > currentTime+TIMEOUT_TIME; } //------------------------------------------------------------------------------------------------------- // Gets the time used to indicate that this received packet never arrived //------------------------------------------------------------------------------------------------------- unsigned int ReliabilityLayer::MakeReceivedPacketHole(unsigned int input) const { return input + TIMEOUT_TIME*100; // * 2 is enough but there is no reason to take that chance } //------------------------------------------------------------------------------------------------------- // Returns the number of packets in the resend queue, not counting holes //------------------------------------------------------------------------------------------------------- unsigned int ReliabilityLayer::GetResendQueueDataSize(void) const { /* unsigned int i, count; for (count=0, i=0; i < resendQueue.size(); i++) if (resendQueue[i]!=0) count++; return count; */ // Not accurate but thread-safe. The commented version might crash if the queue is cleared while we loop through it return resendQueue.size(); } //------------------------------------------------------------------------------------------------------- // Process threaded commands //------------------------------------------------------------------------------------------------------- void ReliabilityLayer::UpdateThreadedMemory(void) { if ( freeThreadedMemoryOnNextUpdate ) { freeThreadedMemoryOnNextUpdate = false; FreeThreadedMemory(); } } blobby-1.0rc3/src/raknet/Types.h0000644000175000017500000004035212042452367020104 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Compatibility Layer and fundamental tools and types * * @verbatim * Fundamental tools & types * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Catid(cat02e@fsu.edu) * * 8/9/2004 Added SINGLE/ARRAY_RELEASE * 8/5/2004 Added COMPILER_ preprocessors * class NoCopies * 8/1/2004 Removed mask stuff * 7/29/2004 Added swapLE, swapBE, getLE, getBE * 7/28/2004 Automatic and AutoArray now compile in dev-c++ * Added pre-processor conditions to support * other compilers * Removed GETWORD and GETDWORD * 7/15/2004 Now using COM_RELEASE throughout CatGL3 * 6/22/2004 Removed triple and pair * 6/12/2004 AutoDeallocate -> Automatic, AutoArray * 6/9/2004 OBJCLR * 5/2/2004 class AutoDeallocate * 5/1/2004 IS_POWER_OF_2, next_highest_power_of_2 * 4/30/2004 Merged character manip macros * 2/23/2004 CEIL* * Removed MEMCOPY32 and MEMCLEAR32, * memcpy and memset are now faster * MAKE_MASK * 2/10/2004 LITTLE_ENDIAN * COUNT1BITS32 * AT_LEAST_2_BITS * LEAST_SIGNIFICANT_BIT * X-mas/2003 [u/s]int?? -> [u/s]?? * 7/3/2003 Added template triple, point->pair * 6/15/2003 Added template rect, point * 3/30/2003 Added RO?8, RO?16 and ?int64 * Added MEMCOPY32 and MEMCLEAR32 * 3/12/2003 Added GETWORD and GETDWORD * 1/16/2003 Formalized this library. * * Tabs: 4 spaces * Dist: public * @endverbatim */ #ifndef TYPES_H #define TYPES_H /** * @brief Compatibility and Fundamental Tools and Types. This * namespace contains compatibility tools and types and also * fundamental class. * @section sec_compiler Compiler detection * Things to consider for each compiler: * * - BIG_ENDIAN / LITTLE_ENDIAN * - MULTITHREADED * - DEBUG * - HASINT64 * - Basic types {u8-u64, s8-s64, f32, f64} * - WIN32 * - ASSEMBLY_INTEL_SYNTAX / ASSEMBLY_ATT_SYNTAX, ASSEMBLY_BLOCK * - INLINE * - NO_TEMPLATE_INLINE_ASSEMBLY * - Fixes * * Set depending which compiler is being used: * * - COMPILER_MSVC * - COMPILER_GCC * - COMPILER_BORLANDC */ #include #if defined(__GLIBC__) && !defined(HOST_ENDIAN_IS_BIG) && !defined(HOST_ENDIAN_IS_LITTLE) #include #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define HOST_ENDIAN_IS_LITTLE #elif (__BYTE_ORDER == __BIG_ENDIAN) #define HOST_ENDIAN_IS_BIG #endif #endif // CB: Something strange happens with the system includes on // Linux and BIG_ENDIAN ends up defined at some point, so // we need to use HOST_ENDIAN_IS_BIG instead. This code is // a copy of code further down, but keeps the def outside // the cat namespace #if !defined(HOST_ENDIAN_IS_BIG) && !defined(HOST_ENDIAN_IS_LITTLE) #if defined(__sparc) || defined(__sparc__) || defined(__powerpc__) || \ defined(__ppc__) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || \ defined(_M_PPC) || defined(_M_MPPC) || defined(_M_MRX000) || \ defined(__POWERPC) || defined(m68k) || defined(powerpc) || \ defined(sel) || defined(pyr) || defined(mc68000) || defined(is68k) || \ defined(tahoe) || defined(ibm032) || defined(ibm370) || defined(MIPSEB) || \ defined(__convex__) || defined(DGUX) || defined(hppa) || defined(apollo) || \ defined(_CRAY) || defined(__hp9000) || defined(__hp9000s300) || defined(_AIX) || \ defined(__AIX) || defined(__pyr__) || defined(hp9000s700) || defined(_IBMR2) || defined(__ARMEB__) # define HOST_ENDIAN_IS_BIG #elif defined(__i386__) || defined(i386) || defined(intel) || defined(_M_IX86) || \ defined(__amd64) || defined(__amd64__) || \ defined(__alpha__) || defined(__alpha) || defined(__ia64) || defined(__ia64__) || \ defined(_M_ALPHA) || defined(ns32000) || defined(__ns32000__) || defined(sequent) || \ defined(MIPSEL) || defined(_MIPSEL) || defined(sun386) || defined(__sun386__) || defined(__ARMEL__) # define HOST_ENDIAN_IS_LITTLE #else # error "I can't tell what endian-ness to use for your architecture." #endif #endif namespace cat { //// endian-ness (ignoring NUXI) //// // Prevent an error message with gcc which define preprocessor symbol // LITTLE_ENDIAN or BIG_ENDIAN (endian.h) // dalfy #if !defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN) #if defined(HOST_ENDIAN_IS_BIG) # define BIG_ENDIAN #elif defined(HOST_ENDIAN_IS_LITTLE) # define LITTLE_ENDIAN #else # error "I can't tell what endian-ness to use for your architecture." #endif #endif #ifndef FALSE /** * Define an alias between FALSE and false */ #define FALSE false #endif #ifndef TRUE /** * Define an alias between TRUE and true */ #define TRUE true #endif //// compiler-specific //// #if defined(__COMO__) // Comeau C++ # error "Comeau C++ : I don't know your compiler" #elif defined(__DMC__) // Digital Mars C++ # error "Digital Mars C++ : I don't know your compiler" #elif defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) // Intel # error "Intel : I don't know your compiler" #elif defined(__GNUC__) // GNU C++ #define COMPILER_GCC # if defined(_MT) # define MULTITHREADED # endif # if defined(_DEBUG) # define DEBUG # endif # define INLINE inline # define HASINT64 # define ASSEMBLY_ATT_SYNTAX # define ASSEMBLY_BLOCK asm #elif defined(__DJGPP__) // DJGPP # error "DJGPP : I don't know your compiler" # define ASSEMBLY_ATT_SYNTAX #elif defined(__WATCOMC__) // WatcomC # error "WatcomC : I don't know your compiler" #elif defined(__KCC) // Kai C++ # error "Kai C++ : I don't know your compiler" #elif defined(__sgi) // SGI MIPSpro C++ # error "SGI MIPSpro C++ : I don't know your compiler" #elif defined(__SUNPRO_CC) // Sun Workshop Compiler C++ # error "Sun Workshop Compiler C++ : I don't know your compiler" #elif defined(__HP_aCC) // HP aCC # error "HP aCC : I don't know your compiler" #elif defined(__DECCXX) // Compaq Tru64 Unix cxx # error "Compaq Tru64 Unix cxx : I don't know your compiler" #elif defined(__ghs) // Greenhills C++ # error "Greenhills C++ : I don't know your compiler" #elif defined(__BORLANDC__) // Borland # if (__BORLANDC__ >= 0x561) # define HASINT64 # endif #define COMPILER_BORLANDC # if defined(__MT__) # define MULTITHREADED # endif # if defined(__DEBUG) # define DEBUG # endif # define INLINE inline # define ASSEMBLY_INTEL_SYNTAX # define ASSEMBLY_BLOCK _asm # define NO_TEMPLATE_INLINE_ASSEMBLY #elif defined(__MWERKS__) // Metrowerks CodeWarrior # error "Metrowerks CodeWarrior : I don't know your compiler" #elif defined(__MRC__) || defined(__SC__) // MPW MrCpp or SCpp # error "MPW MrCpp or SCpp : I don't know your compiler" #elif defined(__IBMCPP__) // IBM Visual Age # error "IBM Visual Age : I don't know your compiler" #elif defined(_MSC_VER) // Microsoft VC++ // must be last because many other compilers define this also #define COMPILER_MSVC # if (_MSC_VER >= 1200) /* 1200 == VC++ 6.0 */ # define HASINT64 # define INLINE __forceinline # endif # if defined(_MT) # define MULTITHREADED # endif # if defined(__DEBUG) # define DEBUG # endif # define BASIC_TYPES_ALREADY_DEFINED /** * Typename for 8 bits unsigned integer */ typedef unsigned __int8 u8; /** * Typename for 8 bits signed integer */ typedef signed __int8 s8; /** * Typename for 16 bits unsigned integer */ typedef unsigned __int16 u16; /** * Typename for 16 bits signed integer */ typedef signed __int16 s16; /** * Typename for 32 bits unsigned integer */ typedef unsigned __int32 u32; /** * Typename for 32 bits floatting point number */ typedef signed __int32 s32; /** * Typename for 32 bits floatting point number */ typedef float f32; /** * Typename for 64 bits floatting point number */ typedef double f64; # if defined(HASINT64) /** * Typename for 64 bits unsigned integer */ typedef unsigned __int64 u64; /** * Typename for 64 bits signed integer */ typedef signed __int64 s64; # endif # define ASSEMBLY_INTEL_SYNTAX # define ASSEMBLY_BLOCK __asm # if (_MSC_VER <= 1200) # pragma warning(disable : 4786) // truncation to 255 chars # endif #else # error "Unknown : I don't know your compiler" #endif // compilers // Generic basic types #if !defined(BASIC_TYPES_ALREADY_DEFINED) /** * Typename for 8 bits unsigned integer */ typedef unsigned char u8; /** * Typename for 8 bits signed integer */ typedef signed char s8; /** * Typename for 16 bits unsigned integer */ typedef unsigned short u16; /** * Typename for 16 bits signed integer */ typedef signed short s16; /** * Typename for 32 bits unsigned integer */ typedef unsigned int u32; /** * Typename for 32 bits signed integer */ typedef signed int s32; /** * Typename for 32 bits floatting point number */ typedef float f32; /** * Typename for 64 bits floatting point number */ typedef double f64; # if defined(HASINT64) /** * Typename for 64 bits unsigned integer */ typedef unsigned int long u64; /** * Typename for 64 bits signed integer */ typedef signed long long s64; # endif #endif /** * Fixed-point types * hi-Siiiiiiiiiiiiiiiiiiiii-lo | hi-ffffffffff-lo */ typedef s32 sfp22_10; /** * Fixed-point types * hi-iiiiiiiiiiiiiiiiiiiiii-lo | hi-ffffffffff-lo */ typedef u32 ufp22_10; /** * Fixed point types * hi-Siiiiiiiiiiiiiii-lo | hi-ffffffffffffffff-lo */ typedef s32 sfp16_16; /** * Fixed point types * hi-iiiiiiiiiiiiiiii-lo | hi-ffffffffffffffff-lo */ typedef u32 ufp16_16; /* COMmy macros */ #define COM_RELEASE(ref) if (ref) { (ref)->Release(); (ref) = 0; } #define SINGLE_RELEASE(ref) if (ref) { delete (ref); (ref) = 0; } #define ARRAY_RELEASE(ref) if (ref) { delete [](ref); (ref) = 0; } template class rect { public: rect() : x( 0 ), y( 0 ), w( 0 ), h( 0 ) {} rect( T xx, T yy, T ww, T hh ) : x( xx ), y( yy ), w( ww ), h( hh ) {} T x, y, w, h; }; template class AutoArray { T *ptr; public: AutoArray( T *c_ptr ) { ptr = c_ptr; } ~AutoArray() { ARRAY_RELEASE( ptr ); } INLINE void cancel() { ptr = 0; } }; template class Automatic { T *ptr; public: Automatic( T *c_ptr ) { ptr = c_ptr; } ~Automatic() { SINGLE_RELEASE( ptr ); } INLINE void cancel() { ptr = 0; } }; // Derive from NoCopies to disallow copies of derived class class NoCopies { protected: NoCopies() {} ~NoCopies() {} private: NoCopies( const NoCopies &cp ); NoCopies &operator=( const NoCopies &cp ); }; // Byte-order swapping #define BOSWAP32(n) ( ((n) << 24) | (((n) & 0x00ff0000) >> 8) | (((n) & 0x0000ff00) << 8) | ((n) >> 24) ) /* only works for u32 */ #define BOSWAP16(n) ( ((n) << 8) | ((n) >> 8) ) /* only works for u16 */ #ifdef LITTLE_ENDIAN # define swapLE(n) # define getLE(n) (n) INLINE void swapBE( u32 &n ) { n = BOSWAP32( n ); } INLINE void swapBE( u16 &n ) { n = BOSWAP16( n ); } INLINE u32 getBE( u32 n ) { return BOSWAP32( n ); } INLINE u16 getBE( u16 n ) { return BOSWAP16( n ); } INLINE void swapBE( s32 &n ) { n = BOSWAP32( ( u32 ) n ); } INLINE void swapBE( s16 &n ) { n = BOSWAP16( ( u16 ) n ); } INLINE s32 getBE( s32 n ) { return BOSWAP32( ( u32 ) n ); } INLINE s16 getBE( s16 n ) { return BOSWAP16( ( u16 ) n ); } #else # define swapBE(n) # define getBE(n) (n) INLINE void swapLE( u32 &n ) { n = BOSWAP32( n ); } INLINE void swapLE( u16 &n ) { n = BOSWAP16( n ); } INLINE u32 getLE( u32 n ) { return BOSWAP32( n ); } INLINE u16 getLE( u16 n ) { return BOSWAP16( n ); } INLINE void swapLE( s32 &n ) { n = BOSWAP32( ( u32 ) n ); } INLINE void swapLE( s16 &n ) { n = BOSWAP16( ( u16 ) n ); } INLINE s32 getLE( s32 n ) { return BOSWAP32( ( u32 ) n ); } INLINE s16 getLE( s16 n ) { return BOSWAP16( ( u16 ) n ); } #endif } // namespace cat // Rotation #define ROL8(n, r) ( ((n) << (r)) | ((n) >> ( 8 - (r))) ) /* only works for u8 */ #define ROR8(n, r) ( ((n) >> (r)) | ((n) << ( 8 - (r))) ) /* only works for u8 */ #define ROL16(n, r) ( ((n) << (r)) | ((n) >> (16 - (r))) ) /* only works for u16 */ #define ROR16(n, r) ( ((n) >> (r)) | ((n) << (16 - (r))) ) /* only works for u16 */ #define ROL32(n, r) ( ((n) << (r)) | ((n) >> (32 - (r))) ) /* only works for u32 */ #define ROR32(n, r) ( ((n) >> (r)) | ((n) << (32 - (r))) ) /* only works for u32 */ /* Add memory that is allocated on a 32-bit boundary and has at least one block. */ #define MEMADD32(ptr, len, val) { \ register u32 *__data = (u32*)(ptr); /* pointer to data to clear */ \ register s32 __length = (len); /* number of 32-bit blocks */ \ \ switch (__length % 8) \ { \ case 0: do { *__data++ += (val); \ case 7: *__data++ += (val); \ case 6: *__data++ += (val); \ case 5: *__data++ += (val); \ case 4: *__data++ += (val); \ case 3: *__data++ += (val); \ case 2: *__data++ += (val); \ case 1: *__data++ += (val); \ __length -= 8; \ } while(__length > 0); \ } \ } /** * Safe null-terminated string -> char buffer copy * @param dest the resulting string * @param src the string to copy * @param size the number of char to copy */ #define STRNCPY(dest, src, size) { \ strncpy(dest, src, size); \ dest[size-1] = 0; \ } /* Because memory clearing is a frequent operation */ #define MEMCLR(dest, size) memset(dest, 0, size) // Works for arrays, also #define OBJCLR(object) memset(&(object), 0, sizeof(object)) /* Fast binary method of counting bits MIT Hackmem from X11 Only works for 32-bit integers */ #define _C1B_INTERMED(n) ( (n) - (((n) >> 1) & 033333333333) - (((n) >> 2) & 011111111111) ) #define COUNT1BITS32(n) ( ((_C1B_INTERMED(n) + (_C1B_INTERMED(n) >> 3)) & 030707070707) % 63 ) /* Consider N an ordered set of 1/0 values. LESSTHAN2BITS(n) := there are less than 2 bits in set N Proof: (N - 1) will clear the least significant of the bits in N. Three cases exist concerning (N-1): N contains 0 bits An intersection with N would be trivially null. N contains 1 bit The least only existing bit was cleared, thus an intersection with the original set WOULD be null. N contains more than 1 bit A more significant bit remains in the set, thus an intersection with the original set WOULD NOT be null. */ #define AT_LEAST_2_BITS(n) ( (n) & ((n) - 1) ) #define LEAST_SIGNIFICANT_BIT(n) ( (n) & -(n) ) #define IS_POWER_OF_2(n) ( n && !AT_LEAST_2_BITS(n) ) INLINE cat::u32 next_highest_power_of_2( cat::u32 n ) { if ( IS_POWER_OF_2( n ) ) return n; cat::u16 b = 2; while ( n >>= 1 ) b <<= 1; return b; } /* CEIL* Bump 'n' to the next unit of 'width'. */ #define CEIL_UNIT(n, width) ( ( (n) + (width) - 1 ) / (width) ) #define CEIL(n, width) ( CEIL_UNIT(n, width) * (width) ) /* Quick character manipulation */ #define IS_ALPHA(ch) ( (((u8)(ch) & 0xc0) == 0x40) && ((((u8)(ch) - 1) & 0x1f) <= 0x19) ) #define IS_NUM(ch) ( ((u8)(ch) - 0x30) < 10 ) #define IS_ALPHANUM(ch) ( IS_ALPHA(ch) || IS_NUM(ch) ) #define TO_LOWER(ch) (char)( (ch) | 0x20 ) /* must be upper/lower-case alpha */ #define TO_UPPER(ch) (char)( (ch) & (~0x20) ) /* must be upper/lower-case alpha */ #endif // TYPES_H blobby-1.0rc3/src/raknet/InternalPacket.h0000644000175000017500000000704712042452367021710 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Define the Structure of an Internal Packet * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __INTERNAL_PACKET_H #define __INTERNAL_PACKET_H #include "PacketPriority.h" #ifdef _DEBUG #include "NetworkTypes.h" #endif /** * This must be able to hold the highest value of RECEIVED_PACKET_LOG_LENGTH. */ typedef unsigned short PacketNumberType; /** * Use unsigned char for most apps. Unsigned short if you send many packets at the same time on the same ordering stream * This has to be the same value on all systems */ // TODO - Change to unsigned short? typedef unsigned char OrderingIndexType; /** * @brief Structure of an Internal Packet * * Internal packets are used with the RakNet Network Library for internal * management only. */ struct InternalPacket { /** * True if this is an acknowledgment packet */ bool isAcknowledgement; /** * The number of this packet, used as an identifier */ PacketNumberType packetNumber; /** * The priority level of this packet */ PacketPriority priority; /** * What type of reliability algorithm to use with this packet */ PacketReliability reliability; /** * What ordering channel this packet is on, if the reliability type uses ordering channels */ unsigned char orderingChannel; /** * The ID used as identification for ordering channels */ OrderingIndexType orderingIndex; /** * The ID of the split packet, if we have split packets */ unsigned int splitPacketId; /** * If this is a split packet, the index into the array of subsplit packets */ unsigned int splitPacketIndex; /** * The size of the array of subsplit packets */ unsigned int splitPacketCount; /** * When this packet was created */ unsigned int creationTime; /** * The next time to take action on this packet */ unsigned int nextActionTime; /** * How many bits the data is */ unsigned int dataBitLength; /** * Buffer is a pointer to the actual data, assuming this packet has data at all */ char *data; }; #endif blobby-1.0rc3/src/raknet/PacketPriority.h0000644000175000017500000000513412042452367021750 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Defines Priority and Reliability Constants * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __PACKET_PRIORITY_H #define __PACKET_PRIORITY_H /** * This enum contains all level of priority that can be applyed to a packet. */ enum PacketPriority { SYSTEM_PRIORITY, //!< System priority is for system related messaging. Don't use it. HIGH_PRIORITY, //!< Those message are handle first MEDIUM_PRIORITY, //!< Message relativly important LOW_PRIORITY, //!< Not critical information NUMBER_OF_PRIORITIES }; /** * This define the reliability behaviour to apply to a packet * * @note Note to self: I write this with 3 bits in the stream! * */ enum PacketReliability { UNRELIABLE, //!< Send packet not reliable and not sequenced UNRELIABLE_SEQUENCED, //!< Send packet not reliable but sequenced RELIABLE, //!< Send packet reliable RELIABLE_ORDERED, //!< Send packet reliable respecting ordering RELIABLE_SEQUENCED //!< Send packet reliable respecting sequenced }; #endif blobby-1.0rc3/src/raknet/ArrayList.h0000644000175000017500000002013312042452367020705 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Provide an array based list container. * * An Array list container provide an o(1) access to its * elements. It's quite similar to the std::vector features. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __LIST_H #define __LIST_H #include #include /** * Define the max unsigned int value available * @todo Take this value from @em limits.h. * */ static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; namespace BasicDataStructures { /** * * List ADT (Array Style) - By Kevin Jenkins * Initialize with the following structure * List * * Has the following member functions * [x] - Overloaded: Returns element x (starting at 0) in the list between the brackets. If no x argument is specified it returns the last element on the list. If x is invalid it returns 0 * size - returns number of elements in the list * insert(item, x) - inserts at position x in the list. If no x argument is specified it adds item to the end of the list * replace(item, filler, x) - replaces the element at position x with . If x is greater than the current list size, the list is increased and the other values are assigned filler * del(OPTIONAL x) - deletes the element at position x. If no x argument is specified it deletes the last element on the list * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases * clear - empties the list and returns storage * The assignment and copy constructor operators are defined * * EXAMPLE * @code * List A; * A.size; // Returns 0 * A.insert(10); // Adds 10 to the end of the list * A[0]; // Returns 10 * A.insert(1,0); // Adds 1 to front of list so list reads 1,10 * A.replace(5,0, 1); // Replaces element 1 with a 5. List reads 1,5 * A.del(); // Deletes the last item on the list. List reads 1 * A.size; // Returns 1 * @endcode * * @note * The default initial allocation size is 1. * This function doubles the amount of memory allocated when the list is filled * This list is held in an array and is best used for random element selection * */ template class List { public: /** * Default constructor */ List(); /** * Destructor */ ~List(); /** * Copy constructor * @param original_copy The list to duplicate */ List( const List& original_copy ); /** * */ List& operator= ( const List& original_copy ); /** * Access an element by its index in the array * @param position The index in the array. * @return the element at position @em position. */ list_type& operator[] ( unsigned int position ); /** * Insert an element at position @em position in the list * @param input The new element. * @param position The position of the new element. */ void insert( list_type input, unsigned int position ); /** * Insert at the end of the list. * @param input The new element. */ void insert( list_type input ); /** * replace the value at @em position by @em input. If the size of * the list is less than @em position, it increase the capacity of * the list and fill slot with @em filler. * @param input The element to replace at position @em position. * @param filler The element use to fill new allocated capacity. * @param position The position of input in the list. */ void replace( list_type input, list_type filler, unsigned int position ); /** * replace the last element of the list by @em input * @param input the element used to replace the last element. */ void replace( list_type input ); /** * Delete the element at position @em position. * @param position the index of the element to delete */ void del( unsigned int position ); /** * Delete the element at the end of the list */ void del(); /** * Returns the index of the specified item or MAX_UNSIGNED_LONG if not found * @param input The element to check for * @return The index or position of @em input in the list. * If input is not in the list MAX_UNSIGNED_LONG is returned. */ unsigned int getIndexOf( list_type input ); /** * Get the size of the list */ const unsigned int size( void ) const; /** * Clear the list */ void clear( void ); private: /** * Store all values */ typedef std::vector container_type; container_type array; }; template List::List() { array.reserve(16); } template List::~List() { } template List::List( const List& original_copy ) { array = original_copy.array; } template List& List::operator= ( const List& original_copy ) { array = original_copy.array; return *this; } template inline list_type& List::operator[] ( unsigned int position ) { return array.at(position); } template void List::insert( list_type input, unsigned int position ) { array.insert(array.begin() + position, input); // Insert the new item at the correct spot assert( array[ position ] == input ); } template void List::insert( list_type input ) { array.push_back(input); } template inline void List::replace( list_type input, list_type filler, unsigned int position ) { if( position >= array.size() ) { // reserve new memory array.resize( position + 1, filler ); } array[ position ] = input; } template inline void List::replace( list_type input ) { if ( !array.empty() ) array.back() = input; } template void List::del( unsigned int position ) { array.erase( array.begin() + position ); } template inline void List::del() { // Delete the last element on the list. No compression needed array.pop_back(); } template unsigned int List::getIndexOf( list_type input ) { for ( unsigned int i = 0; i < array.size(); ++i ) if ( array[ i ] == input ) return i; return MAX_UNSIGNED_LONG; } template inline const unsigned int List::size( void ) const { return array.size(); } template void List::clear( void ) { array.clear(); } } // End namespace #endif blobby-1.0rc3/src/raknet/Rand.cpp0000644000175000017500000001564112042452367020222 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Random Value Generator Implementation * * Grabbed by Kevin from http://www.math.keio.ac.jp/~matumoto/cokus.c * * This is the ``Mersenne Twister'' random number generator MT19937, which * generates pseudorandom integers uniformly distributed in 0..(2^32 - 1) * starting from any odd seed in 0..(2^32 - 1). This version is a recode * by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by * Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in * July-August 1997). * * Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha * running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to * generate 300 million random numbers; after recoding: 24.0 sec. for the same * (i.e., 46.5% of original time), so speed is now about 12.5 million random * number generations per second on this machine. * * According to the URL * (and paraphrasing a bit in places), the Mersenne Twister is ``designed * with consideration of the flaws of various existing generators,'' has * a period of 2^19937 - 1, gives a sequence that is 623-dimensionally * equidistributed, and ``has passed many stringent tests, including the * die-hard test of G. Marsaglia and the load test of P. Hellekalek and * S. Wegenkittl.'' It is efficient in memory usage (typically using 2506 * to 5012 bytes of static data, depending on data type sizes, and the code * is quite short as well). It generates random numbers in batches of 624 * at a time, so the caching and pipelining of modern systems is exploited. * It is also divide- and mod-free. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Library General Public License as published by * the Free Software Foundation (either version 2 of the License or, at your * option, any later version). This library is distributed in the hope that * it will be useful, but WITHOUT ANY WARRANTY, without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Library General Public License for more details. You should have * received a copy of the GNU Library General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307, USA. * * It would be nice to CC: when you write. */ #include #include // // uint32 must be an unsigned integer type capable of holding at least 32 // bits; exactly 32 should be fastest, but 64 is better on an Alpha with // GCC at -O3 optimization so try your options and see what's best for you // //typedef unsigned int uint32; #define N (624) // length of state vector #define M (397) // a period parameter #define K (0x9908B0DFU) // a magic constant #define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u #define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u #define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u #define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v static unsigned int state[ N + 1 ]; // state vector + 1 extra to not violate ANSI C static unsigned int *next; // next random value is computed from here static int left = -1; // can *next++ this many times before reloading void seedMT( unsigned int seed ) // Defined in cokus_c.c { // // We initialize state[0..(N-1)] via the generator // // x_new = (69069 * x_old) mod 2^32 // // from Line 15 of Table 1, p. 106, Sec. 3.3.4 of Knuth's // _The Art of Computer Programming_, Volume 2, 3rd ed. // // Notes (SJC): I do not know what the initial state requirements // of the Mersenne Twister are, but it seems this seeding generator // could be better. It achieves the maximum period for its modulus // (2^30) iff x_initial is odd (p. 20-21, Sec. 3.2.1.2, Knuth); if // x_initial can be even, you have sequences like 0, 0, 0, ...; // 2^31, 2^31, 2^31, ...; 2^30, 2^30, 2^30, ...; 2^29, 2^29 + 2^31, // 2^29, 2^29 + 2^31, ..., etc. so I force seed to be odd below. // // Even if x_initial is odd, if x_initial is 1 mod 4 then // // the lowest bit of x is always 1, // the next-to-lowest bit of x is always 0, // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , // the 3rd-from-lowest bit of x 4-cycles ... 0 1 1 0 0 1 1 0 ... , // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 0 1 1 1 1 0 ... , // ... // // and if x_initial is 3 mod 4 then // // the lowest bit of x is always 1, // the next-to-lowest bit of x is always 1, // the 2nd-from-lowest bit of x alternates ... 0 1 0 1 0 1 0 1 ... , // the 3rd-from-lowest bit of x 4-cycles ... 0 0 1 1 0 0 1 1 ... , // the 4th-from-lowest bit of x has the 8-cycle ... 0 0 1 1 1 1 0 0 ... , // ... // // The generator's potency (min. s>=0 with (69069-1)^s = 0 mod 2^32) is // 16, which seems to be alright by p. 25, Sec. 3.2.1.3 of Knuth. It // also does well in the dimension 2..5 spectral tests, but it could be // better in dimension 6 (Line 15, Table 1, p. 106, Sec. 3.3.4, Knuth). // // Note that the random number user does not see the values generated // here directly since reloadMT() will always munge them first, so maybe // none of all of this matters. In fact, the seed values made here could // even be extra-special desirable if the Mersenne Twister theory says // so-- that's why the only change I made is to restrict to odd seeds. // register unsigned int x = ( seed | 1U ) & 0xFFFFFFFFU, *s = state; register int j; for ( left = 0, *s++ = x, j = N; --j; *s++ = ( x *= 69069U ) & 0xFFFFFFFFU ) ; } unsigned int reloadMT( void ) { register unsigned int * p0 = state, *p2 = state + 2, *pM = state + M, s0, s1; register int j; if ( left < -1 ) seedMT( 4357U ); left = N - 1, next = state + 1; for ( s0 = state[ 0 ], s1 = state[ 1 ], j = N - M + 1; --j; s0 = s1, s1 = *p2++ ) * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); for ( pM = state, j = M; --j; s0 = s1, s1 = *p2++ ) * p0++ = *pM++ ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); s1 = state[ 0 ], *p0 = *pM ^ ( mixBits( s0, s1 ) >> 1 ) ^ ( loBit( s1 ) ? K : 0U ); s1 ^= ( s1 >> 11 ); s1 ^= ( s1 << 7 ) & 0x9D2C5680U; s1 ^= ( s1 << 15 ) & 0xEFC60000U; return ( s1 ^ ( s1 >> 18 ) ); } unsigned int randomMT( void ) { unsigned int y; if ( --left < 0 ) return ( reloadMT() ); y = *next++; y ^= ( y >> 11 ); y ^= ( y << 7 ) & 0x9D2C5680U; y ^= ( y << 15 ) & 0xEFC60000U; return ( y ^ ( y >> 18 ) ); // This change made so the value returned is in the same range as what rand() returns // return(y ^ (y >> 18)) % 32767; } float frandomMT( void ) { return ( float ) ( ( double ) randomMT() / 4294967296.0 ); } blobby-1.0rc3/src/raknet/RakServerInterface.h0000644000175000017500000006105212042452367022525 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief User visible interface of RakServer * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_SERVER_INTERFACE_H #define __RAK_SERVER_INTERFACE_H #include "NetworkTypes.h" #include "PacketPriority.h" #include "RakPeerInterface.h" #include "BitStream.h" #include "RakNetStatistics.h" /** * @brief Visible Interface of RakServer * * This class define the features available to the user when he want * to use server interface. */ class RakServerInterface { public: /** * Destructor */ virtual ~RakServerInterface() {} /** * Call this to initiate the server with the number of players you want to be allowed connected at once * @param AllowedPlayers Current maximum number of allowed players is 65535 * @param threadSleepTimer >=0 for how many ms to Sleep each internal update cycle (recommended 30 for low performance, 0 for regular) * @param port is the port you want the server to read and write on * Make sure this port is open for UDP * @param forceHostAddress Can force RakNet to use a particular IP to host on. Pass 0 to automatically pick an IP * @return true on successful initiation, false otherwise */ virtual bool Start( unsigned short AllowedPlayers, int threadSleepTimer, unsigned short port, const char *forceHostAddress=0 ) = 0; /** * Must be called while offline * Secures connections though a combination of SHA1, AES128, SYN Cookies, and RSA to prevent * connection spoofing, replay attacks, data eavesdropping, packet tampering, and MitM attacks. * There is a significant amount of processing and a slight amount of bandwidth * overhead for this feature. * * If you accept connections, you must call this or else secure connections will not be enabled * for incoming connections. If the private keys are 0, then a new key will be generated when this function is called * * @param pubKeyE A pointer to the public keys from the RSACrypt class. * @param pubKeyN A pointer to the public keys from the RSACrypt class. * @see the Encryption sample * */ virtual void InitializeSecurity( const char *pubKeyE, const char *pubKeyN ) = 0; /** * Must be called while offline * Disables all security. */ virtual void DisableSecurity( void ) = 0; /** * Set the password clients have to use to connect to this server. The password persists between connections. * Pass 0 for no password. * You can call this anytime * @param _password The password name. */ virtual void SetPassword( const char *_password ) = 0; /** * Returns true if a password was set, false otherwise */ virtual bool HasPassword( void ) = 0; /** * Stops the server, stops synchronized data, and resets all internal data. This will drop all players currently connected, however * since the server is stopped packet reliability is not enforced so the Kick network message may not actually * arrive. Those players will disconnect due to timeout. If you want to end the server more gracefully, you * can manually Kick each player first. Does nothing if the server is not running to begin with * * @param blockDuration How long you should wait for all remaining packets to go out, per connected system * If you set it to 0 then the disconnection notifications probably won't arrive */ virtual void Disconnect( unsigned int blockDuration ) = 0; /** * This function only works while the server is active (Use the Start function). Returns false on failure, true on success * Send the data stream of length length to whichever playerId you specify. Specify UNASSIGNED_PLAYER_ID for all players connected * If you aren't sure what to specify for priority and reliability, use HIGH_PRIORITY, RELIABLE, 0 for ordering channel * Set broadcast to true to broadcast to all connected clients EXCEPT the one specified in the playerId field. * To broadcast to everyone specify UNASSIGNED_PLAYER_ID for the playerId field. */ virtual bool Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) = 0; /** * This function only works while the server is active (Use the Start function). Returns false on failure, true on success * Send the bitstream to whichever playerId you specify. * You can set the first byte to a packet identifier, however you will need to have TYPE_CHECKING undefined or the internal type checking * will add extra data and make this not work. If you want TYPE_CHECKING on, you will need to use BitStream::WriteBits to avoid the type checking. * This interface will probably change to fix this in future versions. * If you aren't sure what to specify for priority and reliability, use HIGH_PRIORITY and RELIABLE, 0 for ordering channel * Set broadcast to true to broadcast to all connected clients EXCEPT the one specified in the playerId field. * To broadcast to everyone specify UNASSIGNED_PLAYER_ID for the playerId field. */ virtual bool Send( const RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) = 0; /** * Call this to get a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. * Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct * Returns 0 if no packets are waiting to be handled * If the server is not active this will also return 0, as all waiting packets are flushed when the server is Disconnected * This also updates all memory blocks associated with synchronized memory */ virtual Packet* Receive( void ) = 0; /** * Kick out the specified player. */ virtual void Kick( PlayerID playerId ) = 0; /** * Call this to deallocate a packet returned by Receive when you are done handling it. */ virtual void DeallocatePacket( Packet *packet ) = 0; /** * Set how many players are allowed on the server. If more players are currently connected then are allowed then * No more players will be allowed to join until the number of players is less than the number of allowed players * The server must be active for this to have meaning */ virtual void SetAllowedPlayers( unsigned short AllowedPlayers ) = 0; /** * Return how many players are allowed to connect. This value was set either from Start or from SetAllowedPlayers * The server must be active for this to have meaning */ virtual unsigned short GetAllowedPlayers( void ) const = 0; /** * Return how many players are currently connected to the server. * The server must be active for this to have meaning */ virtual unsigned short GetConnectedPlayers( void ) = 0; /** * Returns a static string pointer containing the IP of the specified connected player. * Also returns the client port * This changes between calls so be sure to copy what is returned if you need to retain it * Useful for creating and maintaining ban lists * The server must be active for this to have meaning * If the specified id does not represent an active player the results are undefined (most likely returns 0) */ virtual void GetPlayerIPFromID( PlayerID playerId, char returnValue[ 22 ], unsigned short *port ) = 0; /** * Send a ping request to the specified player */ virtual void PingPlayer( PlayerID playerId ) = 0; /** * Returns the average of all ping times read for the specific player or -1 if none read yet */ virtual int GetAveragePing( PlayerID playerId ) = 0; /** * Returns the last ping time read for the specific player or -1 if none read yet */ virtual int GetLastPing( PlayerID playerId ) = 0; /** * Returns the lowest ping time read or -1 if none read yet */ virtual int GetLowestPing( PlayerID playerId ) = 0; /** * Ping all players every so often. This is on by default. In games where you don't care about ping you can call * StopOccasionalPing to save the bandwidth * This will work anytime */ virtual void StartOccasionalPing( void ) = 0; /** * Stop pinging players every so often. Players are pinged by default. In games where you don't care about ping * you can call this to save the bandwidth * This will work anytime */ virtual void StopOccasionalPing( void ) = 0; /** * Returns true if the server is currently active */ virtual bool IsActive( void ) const = 0; /** * Returns a number automatically synchronized between the server and client which randomly changes every * 9 seconds. The time it changes is accurate to within a few ms and is best used to seed * random number generators that you want to usually return the same output on all systems. Keep in mind this * isn't perfectly accurate as there is always a very small chance the numbers will by out of synch during * changes so you should confine its use to visual effects or functionality that has a backup method to * maintain synchronization. If you don't need this functionality and want to save the bandwidth call * StopSynchronizedRandomInteger after starting the server */ virtual unsigned int GetSynchronizedRandomInteger( void ) const = 0; /** * Start or restart the synchronized random integer. This is on by default. Call StopSynchronizedRandomInteger * to stop it keeping the number in synch */ virtual void StartSynchronizedRandomInteger( void ) = 0; /** * Stop the synchronized random integer. Call StartSynchronizedRandomInteger to start it again */ virtual void StopSynchronizedRandomInteger( void ) = 0; /** * This is an optional function to generate the compression layer from the input frequency table. * You should call this twice - once with inputLayer as true and once as false. * The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false. * Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true * Calling this function when there is an existing layer will overwrite the old layer * You should only call this when disconnected * @return false (failure) if connected. Otherwise true (success) */ virtual bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ) = 0; /** * Delete the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory * You should only call this when disconnected * @return false (failure) if connected. Otherwise true (success) */ virtual bool DeleteCompressionLayer( bool inputLayer ) = 0; /** * Enables or disables frequency table tracking. This is required to get a frequency table, which is used to generate * A new compression layer. * You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only track * part of the values sent over the network. * This value persists between connect calls and defaults to false (no frequency tracking) * @param b True to enable tracking */ virtual void SetTrackFrequencyTable( bool b ) = 0; /** * Returns the frequency of outgoing bytes into outputFrequencyTable * The purpose is to save to file as either a master frequency table from a sample game session for passing to * GenerateCompressionLayer. * You should only call this when disconnected * Requires that you first enable data frequency tracking by calling SetTrackFrequencyTable(true) * @param[out] outputFrequencyTable The Frequency Table used in the compression layer * @return false (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success) */ virtual bool GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ) = 0; /** * Returns the compression ratio. A low compression ratio is good. Compression is for outgoing data */ virtual float GetCompressionRatio( void ) const = 0; /** * Returns the decompression ratio. A high decompression ratio is good. Decompression is for incoming data */ virtual float GetDecompressionRatio( void ) const = 0; /** * Attatches a message handler interface to run code automatically on message receipt in the Receive call * * @param messageHandler Pointer to a message handler to attach */ virtual void AttachMessageHandler( MessageHandlerInterface *messageHandler )=0; /** * Detatches a message handler interface to run code automatically on message receipt * * @param messageHandler Pointer to a message handler to detatch */ virtual void DetachMessageHandler( MessageHandlerInterface *messageHandler )=0; /** * The server internally maintains a data struct that is automatically sent to clients when the connect. * This is useful to contain data such as the server name or message of the day. Access that struct with this * function. * @note * If you change any data in the struct remote clients won't reflect this change unless you manually update them * Do so by calling SendStaticServerDataToClient(UNASSIGNED_PLAYER_ID) (broadcast to all) * The data is entered as an array and stored and returned as a BitStream. * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters */ virtual RakNet::BitStream * GetStaticServerData( void ) = 0; /** * The server internally maintains a data struct that is automatically sent to clients when the connect. * This is useful to contain data such as the server name or message of the day. Access that struct with this * function. * @note * If you change any data in the struct remote clients won't reflect this change unless you manually update them * Do so by calling SendStaticServerDataToClient(UNASSIGNED_PLAYER_ID) (broadcast to all) * The data is entered as an array and stored and returned as a BitStream. * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters */ virtual void SetStaticServerData( const char *data, const long length ) = 0; /** * This sets to true or false whether we want to support relaying of static client data to other connected clients. * If set to false it saves some bandwdith, however other clients won't know the static client data and attempting * to read that data will return garbage. Default is true. This also only works for up to 32 players. Games * supporting more than 32 players will have this shut off automatically upon server start and must have it forced * back on with this function if you do indeed want it * This should be called after the server is started in case you want to override when it shuts off at 32 players */ virtual void SetRelayStaticClientData( bool b ) = 0; /** * Send the static server data to the specified player. Pass UNASSIGNED_PLAYER_ID to broadcast to all players * The only time you need to call this function is to update clients that are already connected when you change the static * server data by calling GetStaticServerData and directly modifying the object pointed to. Obviously if the * connected clients don't need to know the new data you don't need to update them, so it's up to you * The server must be active for this to have meaning */ virtual void SendStaticServerDataToClient( PlayerID playerId ) = 0; /** * Sets the data to send with an (LAN server discovery) /(offline ping) response * Length should be under 400 bytes, as a security measure against flood attacks * @see the Ping sample project for how this is used. * @param data a block of data to store, or 0 for none * @param length The length of data in bytes, or 0 for none */ virtual void SetOfflinePingResponse( const char *data, const unsigned int length ) = 0; /** * Returns a pointer to an attached client's character name specified by the playerId * Returns 0 if no such player is connected * Note that you can modify the client data here. Changes won't be reflected on clients unless you force them to * update by calling ChangeStaticClientData * The server must be active for this to have meaning * The data is entered as an array and stored and returned as a BitStream. * Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in * RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the client may change at any time the * data contents and/or its length! * @param playerId The ID of the client * @return Statistical information of this client */ virtual RakNet::BitStream * GetStaticClientData( PlayerID playerId ) = 0; /** * Returns a pointer to an attached client's character name specified by the playerId * Returns 0 if no such player is connected * Note that you can modify the client data here. Changes won't be reflected on clients unless you force them to * update by calling ChangeStaticClientData * The server must be active for this to have meaning * The data is entered as an array and stored and returned as a BitStream. * Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in * RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the client may change at any time the * data contents and/or its length! * @param playerId The ID of the client * @param data A buffer containing statistics * @param length The size of the buffer */ virtual void SetStaticClientData( PlayerID playerId, const char *data, const long length ) = 0; /** * This function is used to update the information on connected clients when the server effects a change * of static client data * playerChangedId should be the playerId of the player whose data was changed. This is the parameter passed to * GetStaticClientData to get a pointer to that player's information * Note that a client that gets this new information for himself will update the data for his playerID but not his local data (which is the user's copy) * i.e. player 5 would have the data returned by GetStaticClientData(5) changed but his local information returned by * GetStaticClientData(UNASSIGNED_PLAYER_ID) would remain the same as it was previously. * playerToSendToId should be the player you want to update with the new information. This will almost always * be everybody, in which case you should pass UNASSIGNED_PLAYER_ID. * The server must be active for this to have meaning */ virtual void ChangeStaticClientData( PlayerID playerChangedId, PlayerID playerToSendToId ) = 0; /** * Internally store the IP address(es) for the server and return how many it has. * This can easily be more than one, for example a system on both a LAN and with a net connection. * The server does not have to be active for this to work */ virtual unsigned int GetNumberOfAddresses( void ) = 0; /** * Call this function where 0 <= index < x where x is the value returned by GetNumberOfAddresses * Returns a static string filled with the server IP of the specified index * Strings returned in no particular order. You'll have to check every index see which string you want * Returns 0 on invalid input * The server does not have to be active for this to work */ virtual const char* GetLocalIP( unsigned int index ) = 0; /** * Put a packet back at the end of the receive queue in case you don't want to deal with it immediately */ virtual void PushBackPacket( Packet *packet ) = 0; /** * Given a playerID, returns an index from 0 to the maximum number of players allowed - 1. */ virtual int GetIndexFromPlayerID( PlayerID playerId ) = 0; /** * This function is only useful for looping through all players. * Index should range between 0 and the maximum number of players allowed - 1. */ virtual PlayerID GetPlayerIDFromIndex( int index ) = 0; /** * Bans an IP from connecting. Banned IPs persist between connections. * * Parameters * IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 */ virtual void AddToBanList( const char *IP ) = 0; /** * Allows a previously banned IP to connect. * * Parameters * IP - Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 */ virtual void RemoveFromBanList( const char *IP ) = 0; /** * Allows all previously banned IPs to connect. */ virtual void ClearBanList( void ) = 0; /** * Determines if a particular IP is banned. * * Parameters * IP - Complete dotted IP address * * Returns * True if IP matches any IPs in the ban list, accounting for any wildcards. * False otherwise. */ virtual bool IsBanned( const char *IP ) = 0; /** * Returns true if that player ID is currently used */ virtual bool IsActivePlayerID( PlayerID playerId ) = 0; /** * Change the MTU size in order to improve performance when sending large packets * This can only be called when not connected. * Returns false on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE * A too high of value will cause packets not to arrive at worst and be fragmented at best. * A too low of value will split packets unnecessarily. * Set according to the following table: * 1500. The largest Ethernet packet size; it is also the default value. * This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. * 1492. The size PPPoE prefers. * 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) * 1468. The size DHCP prefers. * 1460. Usable by AOL if you don't have large email attachments, etc. * 1430. The size VPN and PPTP prefer. * 1400. Maximum size for AOL DSL. * 576. Typical value to connect to dial-up ISPs. (Default) */ virtual bool SetMTUSize( int size ) = 0; /** * Returns the current MTU size */ virtual int GetMTUSize( void ) const = 0; /** * Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. * This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through * * host: Either a dotted IP address or a domain name * remotePort: Which port to connect to on the remote machine. */ virtual void AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ) = 0; /** * Returns a structure containing a large set of network statistics for the specified system * You can map this data to a string using the C style StatisticsToString function * * Parameters * playerId: Which connected system to get statistics for * * Returns: * 0 on can't find the specified system. A pointer to a set of data otherwise. */ virtual RakNetStatisticsStruct * const GetStatistics( PlayerID playerId ) = 0; }; #endif blobby-1.0rc3/src/raknet/SHA1.cpp0000644000175000017500000001525212042452367020030 0ustar danielknobedanielknobe /* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief SHA-1 Hash key computation implementation * * 100% free public domain implementation of the SHA-1 * algorithm by Dominik Reichl * * * === Test Vectors (from FIPS PUB 180-1) === * * "abc" * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D * * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 * * A million repetitions of "a" * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #include "SHA1.h" #include CSHA1::CSHA1() { Reset(); } CSHA1::~CSHA1() { Reset(); } void CSHA1::Reset() { // SHA1 initialization constants m_state[ 0 ] = 0x67452301; m_state[ 1 ] = 0xEFCDAB89; m_state[ 2 ] = 0x98BADCFE; m_state[ 3 ] = 0x10325476; m_state[ 4 ] = 0xC3D2E1F0; m_count[ 0 ] = 0; m_count[ 1 ] = 0; } void CSHA1::Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ) { unsigned int a = 0, b = 0, c = 0, d = 0, e = 0; SHA1_WORKSPACE_BLOCK* block; // static unsigned char workspace[64]; block = ( SHA1_WORKSPACE_BLOCK * ) workspace; memcpy( block, buffer, 64 ); // Copy state[] to working vars a = state[ 0 ]; b = state[ 1 ]; c = state[ 2 ]; d = state[ 3 ]; e = state[ 4 ]; // 4 rounds of 20 operations each. Loop unrolled. R0( a, b, c, d, e, 0 ); R0( e, a, b, c, d, 1 ); R0( d, e, a, b, c, 2 ); R0( c, d, e, a, b, 3 ); R0( b, c, d, e, a, 4 ); R0( a, b, c, d, e, 5 ); R0( e, a, b, c, d, 6 ); R0( d, e, a, b, c, 7 ); R0( c, d, e, a, b, 8 ); R0( b, c, d, e, a, 9 ); R0( a, b, c, d, e, 10 ); R0( e, a, b, c, d, 11 ); R0( d, e, a, b, c, 12 ); R0( c, d, e, a, b, 13 ); R0( b, c, d, e, a, 14 ); R0( a, b, c, d, e, 15 ); R1( e, a, b, c, d, 16 ); R1( d, e, a, b, c, 17 ); R1( c, d, e, a, b, 18 ); R1( b, c, d, e, a, 19 ); R2( a, b, c, d, e, 20 ); R2( e, a, b, c, d, 21 ); R2( d, e, a, b, c, 22 ); R2( c, d, e, a, b, 23 ); R2( b, c, d, e, a, 24 ); R2( a, b, c, d, e, 25 ); R2( e, a, b, c, d, 26 ); R2( d, e, a, b, c, 27 ); R2( c, d, e, a, b, 28 ); R2( b, c, d, e, a, 29 ); R2( a, b, c, d, e, 30 ); R2( e, a, b, c, d, 31 ); R2( d, e, a, b, c, 32 ); R2( c, d, e, a, b, 33 ); R2( b, c, d, e, a, 34 ); R2( a, b, c, d, e, 35 ); R2( e, a, b, c, d, 36 ); R2( d, e, a, b, c, 37 ); R2( c, d, e, a, b, 38 ); R2( b, c, d, e, a, 39 ); R3( a, b, c, d, e, 40 ); R3( e, a, b, c, d, 41 ); R3( d, e, a, b, c, 42 ); R3( c, d, e, a, b, 43 ); R3( b, c, d, e, a, 44 ); R3( a, b, c, d, e, 45 ); R3( e, a, b, c, d, 46 ); R3( d, e, a, b, c, 47 ); R3( c, d, e, a, b, 48 ); R3( b, c, d, e, a, 49 ); R3( a, b, c, d, e, 50 ); R3( e, a, b, c, d, 51 ); R3( d, e, a, b, c, 52 ); R3( c, d, e, a, b, 53 ); R3( b, c, d, e, a, 54 ); R3( a, b, c, d, e, 55 ); R3( e, a, b, c, d, 56 ); R3( d, e, a, b, c, 57 ); R3( c, d, e, a, b, 58 ); R3( b, c, d, e, a, 59 ); R4( a, b, c, d, e, 60 ); R4( e, a, b, c, d, 61 ); R4( d, e, a, b, c, 62 ); R4( c, d, e, a, b, 63 ); R4( b, c, d, e, a, 64 ); R4( a, b, c, d, e, 65 ); R4( e, a, b, c, d, 66 ); R4( d, e, a, b, c, 67 ); R4( c, d, e, a, b, 68 ); R4( b, c, d, e, a, 69 ); R4( a, b, c, d, e, 70 ); R4( e, a, b, c, d, 71 ); R4( d, e, a, b, c, 72 ); R4( c, d, e, a, b, 73 ); R4( b, c, d, e, a, 74 ); R4( a, b, c, d, e, 75 ); R4( e, a, b, c, d, 76 ); R4( d, e, a, b, c, 77 ); R4( c, d, e, a, b, 78 ); R4( b, c, d, e, a, 79 ); // Add the working vars back into state[] state[ 0 ] += a; state[ 1 ] += b; state[ 2 ] += c; state[ 3 ] += d; state[ 4 ] += e; // Wipe variables a = 0; b = 0; c = 0; d = 0; e = 0; } // Use this function to hash in binary data and strings void CSHA1::Update( unsigned char* data, unsigned int len ) { unsigned int i = 0, j = 0; j = ( m_count[ 0 ] >> 3 ) & 63; if ( ( m_count[ 0 ] += len << 3 ) < ( len << 3 ) ) m_count[ 1 ] ++; m_count[ 1 ] += ( len >> 29 ); if ( ( j + len ) > 63 ) { memcpy( &m_buffer[ j ], data, ( i = 64 - j ) ); Transform( m_state, m_buffer ); for ( ; i + 63 < len; i += 64 ) { Transform( m_state, &data[ i ] ); } j = 0; } else i = 0; memcpy( &m_buffer[ j ], &data[ i ], len - i ); } // Hash in file contents bool CSHA1::HashFile( char *szFileName ) { unsigned long ulFileSize = 0, ulRest = 0, ulBlocks = 0; unsigned long i = 0; unsigned char uData[ MAX_FILE_READ_BUFFER ]; FILE *fIn = NULL; if ( ( fIn = fopen( szFileName, "rb" ) ) == NULL ) return ( false ); fseek( fIn, 0, SEEK_END ); ulFileSize = ftell( fIn ); fseek( fIn, 0, SEEK_SET ); // This is faster div_t temp; temp = div( ulFileSize, MAX_FILE_READ_BUFFER ); ulRest = temp.rem; ulBlocks = temp.quot; // ulRest = ulFileSize % MAX_FILE_READ_BUFFER; // ulBlocks = ulFileSize / MAX_FILE_READ_BUFFER; for ( i = 0; i < ulBlocks; i++ ) { fread( uData, 1, MAX_FILE_READ_BUFFER, fIn ); Update( uData, MAX_FILE_READ_BUFFER ); } if ( ulRest != 0 ) { fread( uData, 1, ulRest, fIn ); Update( uData, ulRest ); } fclose( fIn ); fIn = NULL; return ( true ); } void CSHA1::Final() { unsigned int i = 0, j = 0; unsigned char finalcount[ 8 ] = { 0, 0, 0, 0, 0, 0, 0, 0 }; for ( i = 0; i < 8; i++ ) finalcount[ i ] = (unsigned char) ( ( m_count[ ( i >= 4 ? 0 : 1 ) ] >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); // Endian independent Update( ( unsigned char * ) "\200", 1 ); while ( ( m_count[ 0 ] & 504 ) != 448 ) Update( ( unsigned char * ) "\0", 1 ); Update( finalcount, 8 ); // Cause a SHA1Transform() for ( i = 0; i < 20; i++ ) { m_digest[ i ] = (unsigned char) ( ( m_state[ i >> 2 ] >> ( ( 3 - ( i & 3 ) ) * 8 ) ) & 255 ); } // Wipe variables for security reasons i = 0; j = 0; memset( m_buffer, 0, 64 ); memset( m_state, 0, 20 ); memset( m_count, 0, 8 ); memset( finalcount, 0, 8 ); Transform( m_state, m_buffer ); } // Get the final hash as a pre-formatted string void CSHA1::ReportHash( char *szReport, unsigned char uReportType ) { unsigned char i = 0; char szTemp[ 4 ]; if ( uReportType == REPORT_HEX ) { sprintf( szTemp, "%02X", m_digest[ 0 ] ); strcat( szReport, szTemp ); for ( i = 1; i < 20; i++ ) { sprintf( szTemp, " %02X", m_digest[ i ] ); strcat( szReport, szTemp ); } } else if ( uReportType == REPORT_DIGIT ) { sprintf( szTemp, "%u", m_digest[ 0 ] ); strcat( szReport, szTemp ); for ( i = 1; i < 20; i++ ) { sprintf( szTemp, " %u", m_digest[ i ] ); strcat( szReport, szTemp ); } } else strcpy( szReport, "Error: Unknown report type!" ); } // Get the raw message digest void CSHA1::GetHash( unsigned char *uDest ) { memcpy( uDest, m_digest, 20 ); } // Get the raw message digest // Added by Kevin to be quicker unsigned char * CSHA1::GetHash( void ) const { return ( unsigned char * ) m_digest; } blobby-1.0rc3/src/raknet/InternalPacketPool.cpp0000644000175000017500000000545012042452367023071 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Internal Packet Pool Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "InternalPacketPool.h" #include InternalPacketPool::InternalPacketPool() { #ifdef _DEBUG packetsReleased = 0; #endif unsigned i; for (i=0; i < 64; i++) pool.push(new InternalPacket); } InternalPacketPool::~InternalPacketPool() { #ifdef _DEBUG // If this assert hits then not all packets given through GetPointer have been returned to ReleasePointer assert( packetsReleased == 0 ); #endif ClearPool(); } void InternalPacketPool::ClearPool( void ) { InternalPacket * p; while ( !pool.empty() ) { p = pool.top(); pool.pop(); delete p; } } InternalPacket* InternalPacketPool::GetPointer( void ) { #ifdef _DEBUG packetsReleased++; #endif InternalPacket *p = 0; if ( !pool.empty() ) { p = pool.top(); pool.pop(); } if ( p ) return p; p = new InternalPacket; #ifdef _DEBUG p->data=0; #endif return p; } void InternalPacketPool::ReleasePointer( InternalPacket *p ) { if ( p == 0 ) { // Releasing a null pointer? #ifdef _DEBUG assert( 0 ); #endif return ; } #ifdef _DEBUG packetsReleased--; #endif #ifdef _DEBUG p->data=0; #endif pool.push( p ); } blobby-1.0rc3/src/raknet/CheckSum.cpp0000644000175000017500000000753312042452367021041 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Provide implementation of the CheckSum class * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * From http://www.flounder.com/checksum.htm * */ #include "CheckSum.h" /**************************************************************************** * CheckSum::add * Inputs: * unsigned int d: word to add * Result: void * * Effect: * Adds the bytes of the unsigned int to the CheckSum ****************************************************************************/ void CheckSum::add ( unsigned int value ) { union { unsigned int value; unsigned char bytes[ 4 ]; } data; data.value = value; for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) add ( data.bytes[ i ] ) ; } // CheckSum::add(unsigned int) /**************************************************************************** * CheckSum::add * Inputs: * unsigned short value: * Result: void * * Effect: * Adds the bytes of the unsigned short value to the CheckSum ****************************************************************************/ void CheckSum::add ( unsigned short value ) { union { unsigned short value; unsigned char bytes[ 2 ]; } data; data.value = value; for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ ) add ( data.bytes[ i ] ) ; } // CheckSum::add(unsigned short) /**************************************************************************** * CheckSum::add * Inputs: * unsigned char value: * Result: void * * Effect: * Adds the byte to the CheckSum ****************************************************************************/ void CheckSum::add ( unsigned char value ) { unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) ); r = ( cipher + r ) * c1 + c2; sum += cipher; } // CheckSum::add(unsigned char) /**************************************************************************** * CheckSum::add * Inputs: * LPunsigned char b: pointer to byte array * unsigned int length: count * Result: void * * Effect: * Adds the bytes to the CheckSum ****************************************************************************/ void CheckSum::add ( unsigned char *b, unsigned int length ) { for ( unsigned int i = 0; i < length; i++ ) add ( b[ i ] ) ; } // CheckSum::add(LPunsigned char, unsigned int) blobby-1.0rc3/src/raknet/LinkedList.h0000644000175000017500000010237112042452367021042 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * * @brief LinkedList and Circular Linked List * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * 9/04 Giblet - updated code to work with new compilers adhering more * closely to ISO standard * */ #ifndef __LINKED_LIST_H #define __LINKED_LIST_H /** * @brief Basic Data Structures (containers) * * This namespace contains containers used in RakNet. */ namespace BasicDataStructures { // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class template class LinkedList; /** * (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - * By Kevin Jenkins (http://www.rakkar.org) * Initilize with the following command * LinkedList * OR * CircularLinkedList * * Has the following member functions * - size: returns number of elements in the linked list * - insert(item): inserts @em item at the current position in * the LinkedList. * - add(item): inserts @em item after the current position in * the LinkedList. Does not increment the position * - replace(item): replaces the element at the current position @em item. * - peek: returns the element at the current position * - pop: returns the element at the current position and deletes it * - del: deletes the current element. Does nothing for an empty list. * - clear: empties the LinkedList and returns storage * - bool is_in(item): Does a linear search for @em item. Does not set * the position to it, only returns true on item found, false otherwise * - bool find(item): Does a linear search for @em item and sets the current * position to point to it if and only if the item is found. Returns true * on item found, false otherwise * - sort: Sorts the elements of the list with a mergesort and sets the * current pointer to the first element * - concatenate(list L): This appends L to the current list * - ++(prefix): moves the pointer one element up in the list and returns the * appropriate copy of the element in the list * - --(prefix): moves the pointer one element back in the list and returns * the appropriate copy of the element in the list * - beginning - moves the pointer to the start of the list. For circular * linked lists this is first 'position' created. You should call this * after the sort function to read the first value. * - end - moves the pointer to the end of the list. For circular linked * lists this is one less than the first 'position' created * The assignment and copy constructor operators are defined * * @note * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList * won't let you wrap around the root and lets you jump to two positions * relative to the root/ * 2. Postfix ++ and -- can be used but simply call the prefix versions. * * * EXAMPLE: * @code * LinkedList A; // Creates a Linked List of integers called A * CircularLinkedList B; // Creates a Circular Linked List of * // integers called B * * A.insert(20); // Adds 20 to A. A: 20 - current is 20 * A.insert(5); // Adds 5 to A. A: 5 20 - current is 5 * A.insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 * * A.is_in(1); // returns true * A.is_in(200); // returns false * A.find(5); // returns true and sets current to 5 * A.peek(); // returns 5 * A.find(1); // returns true and sets current to 1 * * (++A).peek(); // Returns 5 * A.peek(); // Returns 5 * * A.replace(10); // Replaces 5 with 10. * A.peek(); // Returns 10 * * A.beginning(); // Current points to the beginning of the list at 1 * * (++A).peek(); // Returns 5 * A.peek(); // Returns 10 * * A.del(); // Deletes 10. Current points to the next element, which is 20 * A.peek(); // Returns 20 * * A.beginning(); // Current points to the beginning of the list at 1 * * (++A).peek(); // Returns 5 * A.peek(); // Returns 20 * * A.clear(); // Deletes all nodes in A * * A.insert(5); // A: 5 - current is 5 * A.insert(6); // A: 6 5 - current is 6 * A.insert(7); // A: 7 6 5 - current is 7 * * A.clear(); * B.clear(); * * B.add(10); * B.add(20); * B.add(30); * B.add(5); * B.add(2); * B.add(25); * // Sorts the numbers in the list and sets the current pointer to the * // first element * B.sort(); * * // Postfix ++ just calls the prefix version and has no functional * // difference. * B.peek(); // Returns 2 * B++; * B.peek(); // Returns 5 * B++; * B.peek(); // Returns 10 * B++; * B.peek(); // Returns 20 * B++; * B.peek(); // Returns 25 * B++; * B.peek(); // Returns 30 * @endcode */ template class CircularLinkedList { public: struct node { CircularLinkedListType item; node* previous; node* next; }; CircularLinkedList(); ~CircularLinkedList(); CircularLinkedList( const CircularLinkedList& original_copy ); // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type bool operator= ( const CircularLinkedList& original_copy ); CircularLinkedList& operator++(); // CircularLinkedList A; ++A; CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; CircularLinkedList& operator--(); // CircularLinkedList A; --A; CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; bool is_in( const CircularLinkedListType& input ); bool find( const CircularLinkedListType& input ); void insert( const CircularLinkedListType& input ); CircularLinkedListType& add ( const CircularLinkedListType& input ) ; // Adds after the current position void replace( const CircularLinkedListType& input ); void del( void ); unsigned int size( void ); CircularLinkedListType& peek( void ); const CircularLinkedListType pop( void ); void clear( void ); void sort( void ); void beginning( void ); void end( void ); void concatenate( const CircularLinkedList& L ); protected: unsigned int list_size; node *root; node *position; node* find_pointer( const CircularLinkedListType& input ); private: CircularLinkedList merge( CircularLinkedList L1, CircularLinkedList L2 ); CircularLinkedList mergesort( const CircularLinkedList& L ); }; template class LinkedList : public CircularLinkedList { public: LinkedList() {} LinkedList( const LinkedList& original_copy ); ~LinkedList(); bool operator= ( const LinkedList& original_copy ); LinkedList& operator++(); // LinkedList A; ++A; LinkedList& operator++( int ); // Linked List A; A++; LinkedList& operator--(); // LinkedList A; --A; LinkedList& operator--( int ); // Linked List A; A--; private: LinkedList merge( LinkedList L1, LinkedList L2 ); LinkedList mergesort( const LinkedList& L ); }; template inline void CircularLinkedList::beginning( void ) { if ( this->root ) this->position = this->root; } template inline void CircularLinkedList::end( void ) { if ( this->root ) this->position = this->root->previous; } template bool LinkedList::operator= ( const LinkedList& original_copy ) { typename LinkedList::node * original_copy_pointer, *last, *save_position; if ( ( &original_copy ) != this ) { this->clear(); if ( original_copy.list_size == 0 ) { this->root = 0; this->position = 0; this->list_size = 0; } else if ( original_copy.list_size == 1 ) { this->root = new typename LinkedList::node; // root->item = new LinkedListType; this->root->next = this->root; this->root->previous = this->root; this->list_size = 1; this->position = this->root; // *(root->item)=*((original_copy.root)->item); this->root->item = original_copy.root->item; } else { // Setup the first part of the root node original_copy_pointer = original_copy.root; this->root = new typename LinkedList::node; // root->item = new LinkedListType; this->position = this->root; // *(root->item)=*((original_copy.root)->item); this->root->item = original_copy.root->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; do { // Save the current element this->last = this->position; // Point to the next node in the source list original_copy_pointer = original_copy_pointer->next; // Create a new node and point position to it this->position = new typename LinkedList::node; // position->item = new LinkedListType; // Copy the item to the new node // *(position->item)=*(original_copy_pointer->item); this->position->item = original_copy_pointer->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; // Set the previous pointer for the new node ( this->position->previous ) = this->last; // Set the next pointer for the old node to the new node ( this->last->next ) = this->position; } while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node this->position->next = this->root; this->root->previous = this->position; this->list_size = original_copy.list_size; this->position = save_position; } } return true; } template CircularLinkedList::CircularLinkedList() { this->root = 0; this->position = 0; this->list_size = 0; } template CircularLinkedList::~CircularLinkedList() { this->clear(); } template LinkedList::~LinkedList() { this->clear(); } template LinkedList::LinkedList( const LinkedList& original_copy ) { typename LinkedList::node * original_copy_pointer, *last, *save_position; if ( original_copy.list_size == 0 ) { this->root = 0; this->position = 0; this->list_size = 0; return ; } else if ( original_copy.list_size == 1 ) { this->root = new typename LinkedList::node; // root->item = new CircularLinkedListType; this->root->next = this->root; this->root->previous = this->root; this->list_size = 1; this->position = this->root; // *(root->item) = *((original_copy.root)->item); this->root->item = original_copy.root->item; } else { // Setup the first part of the root node original_copy_pointer = original_copy.root; this->root = new typename LinkedList::node; // root->item = new CircularLinkedListType; this->position = this->root; // *(root->item)=*((original_copy.root)->item); this->root->item = original_copy.root->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; do { // Save the current element this->last = this->position; // Point to the next node in the source list original_copy_pointer = original_copy_pointer->next; // Create a new node and point position to it this->position = new typename LinkedList::node; // position->item = new CircularLinkedListType; // Copy the item to the new node // *(position->item)=*(original_copy_pointer->item); this->position->item = original_copy_pointer->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; // Set the previous pointer for the new node ( this->position->previous ) = last; // Set the next pointer for the old node to the new node ( this->last->next ) = this->position; } while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node this->position->next = this->root; this->root->previous = this->position; this->list_size = original_copy.list_size; this->position = save_position; } } template CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) { node * original_copy_pointer; node *last; node *save_position; if ( original_copy.list_size == 0 ) { this->root = 0; this->position = 0; this->list_size = 0; return ; } else if ( original_copy.list_size == 1 ) { this->root = new typename CircularLinkedList::node; // root->item = new CircularLinkedListType; this->root->next = this->root; this->root->previous = this->root; this->list_size = 1; this->position = this->root; // *(root->item) = *((original_copy.root)->item); this->root->item = original_copy.root->item; } else { // Setup the first part of the root node original_copy_pointer = original_copy.root; this->root = new typename CircularLinkedList::node; // root->item = new CircularLinkedListType; this->position = this->root; // *(root->item)=*((original_copy.root)->item); this->root->item = original_copy.root->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; do { // Save the current element this->last = this->position; // Point to the next node in the source list original_copy_pointer = original_copy_pointer->next; // Create a new node and point position to it this->position = new typename CircularLinkedList::node; // position->item = new CircularLinkedListType; // Copy the item to the new node // *(position->item)=*(original_copy_pointer->item); this->position->item = original_copy_pointer->item; if ( original_copy_pointer == original_copy.position ) save_position = position; // Set the previous pointer for the new node ( this->position->previous ) = this->last; // Set the next pointer for the old node to the new node ( this->last->next ) = this->position; } while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node this->position->next = this->root; this->root->previous = position; this->list_size = original_copy.list_size; this->position = save_position; } } template bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) { node * original_copy_pointer; node *last; node *save_position; if ( ( &original_copy ) != this ) { this->clear(); if ( original_copy.list_size == 0 ) { this->root = 0; this->position = 0; this->list_size = 0; } else if ( original_copy.list_size == 1 ) { this->root = new typename CircularLinkedList::node; // root->item = new CircularLinkedListType; this->root->next = this->root; this->root->previous = this->root; this->list_size = 1; this->position = this->root; // *(root->item)=*((original_copy.root)->item); this->root->item = original_copy.root->item; } else { // Setup the first part of the root node original_copy_pointer = original_copy.root; this->root = new typename CircularLinkedList::node; // root->item = new CircularLinkedListType; this->position = this->root; // *(root->item)=*((original_copy.root)->item); this->root->item = original_copy.root->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; do { // Save the current element this->last = this->position; // Point to the next node in the source list original_copy_pointer = original_copy_pointer->next; // Create a new node and point position to it this->position = new typename CircularLinkedList::node; // position->item = new CircularLinkedListType; // Copy the item to the new node // *(position->item)=*(original_copy_pointer->item); this->position->item = original_copy_pointer->item; if ( original_copy_pointer == original_copy.position ) save_position = this->position; // Set the previous pointer for the new node ( this->position->previous ) = this->last; // Set the next pointer for the old node to the new node ( this->last->next ) = this->position; } while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node this->position->next = this->root; this->root->previous = this->position; this->list_size = original_copy.list_size; this->position = save_position; } } return true; } template void CircularLinkedList::insert( const CircularLinkedListType& input ) { node * new_node; if ( list_size == 0 ) { this->root = new typename CircularLinkedList::node; // root->item = new CircularLinkedListType; //*(root->item)=input; this->root->item = input; this->root->next = this->root; this->root->previous = this->root; this->list_size = 1; this->position = this->root; } else if ( list_size == 1 ) { this->position = new typename CircularLinkedList::node; // position->item = new CircularLinkedListType; this->root->next = this->position; this->root->previous = this->position; this->position->previous = this->root; this->position->next = this->root; // *(position->item)=input; this->position->item = input; this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item this->list_size = 2; } else { /* B | A --- C position->previous=A new_node=B position=C Note that the order of the following statements is important */ new_node = new typename CircularLinkedList::node; // new_node->item = new CircularLinkedListType; // *(new_node->item)=input; new_node->item = input; // Point next of A to B ( this->position->previous ) ->next = new_node; // Point last of B to A new_node->previous = this->position->previous; // Point last of C to B this->position->previous = new_node; // Point next of B to C new_node->next = this->position; // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root if ( this->position == this->root ) { this->root = new_node; this->position = this->root; } // Increase the recorded size of the list by one this->list_size++; } } template CircularLinkedListType& CircularLinkedList::add ( const CircularLinkedListType& input ) { node * new_node; if ( this->list_size == 0 ) { this->root = new typename CircularLinkedList::node; // root->item = new CircularLinkedListType; // *(root->item)=input; this->root->item = input; this->root->next = this->root; this->root->previous = this->root; this->list_size = 1; this->position = this->root; // return *(position->item); return this->position->item; } else if ( list_size == 1 ) { this->position = new typename CircularLinkedList::node; // position->item = new CircularLinkedListType; this->root->next = this->position; this->root->previous = this->position; this->position->previous = this->root; this->position->next = this->root; // *(position->item)=input; this->position->item = input; this->list_size = 2; this->position = this->root; // Don't move the position from the root // return *(position->item); return this->position->item; } else { /* B | A --- C new_node=B position=A position->next=C Note that the order of the following statements is important */ new_node = new typename CircularLinkedList::node; // new_node->item = new CircularLinkedListType; // *(new_node->item)=input; new_node->item = input; // Point last of B to A new_node->previous = this->position; // Point next of B to C new_node->next = ( this->position->next ); // Point last of C to B ( this->position->next ) ->previous = new_node; // Point next of A to B ( this->position->next ) = new_node; // Increase the recorded size of the list by one this->list_size++; // return *(new_node->item); return new_node->item; } } template inline void CircularLinkedList::replace( const CircularLinkedListType& input ) { if ( this->list_size > 0 ) // *(position->item)=input; this->position->item = input; } template void CircularLinkedList::del() { node * new_position; if ( this->list_size == 0 ) return ; else if ( this->list_size == 1 ) { // delete root->item; delete this->root; this->root = this->position = 0; this->list_size = 0; } else { ( this->position->previous ) ->next = this->position->next; ( this->position->next ) ->previous = this->position->previous; new_position = this->position->next; if ( this->position == this->root ) this->root = new_position; // delete position->item; delete this->position; this->position = new_position; this->list_size--; } } template bool CircularLinkedList::is_in( const CircularLinkedListType& input ) { node * return_value, *old_position; old_position = this->position; return_value = find_pointer( input ); this->position = old_position; if ( return_value != 0 ) return true; else return false; // Can't find the item don't do anything } template bool CircularLinkedList::find( const CircularLinkedListType& input ) { node * return_value; return_value = find_pointer( input ); if ( return_value != 0 ) { this->position = return_value; return true; } else return false; // Can't find the item don't do anything } template typename CircularLinkedList::node* CircularLinkedList::find_pointer( const CircularLinkedListType& input ) { node * current; if ( this->list_size == 0 ) return 0; current = this->root; // Search for the item starting from the root node and incrementing the pointer after every check // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 do { // if (*(current->item) == input) return current; if ( current->item == input ) return current; current = current->next; } while ( current != this->root ); return 0; } template inline unsigned int CircularLinkedList::size( void ) { return this->list_size; } template inline CircularLinkedListType& CircularLinkedList::peek( void ) { // return *(position->item); return this->position->item; } template const CircularLinkedListType CircularLinkedList::pop( void ) { CircularLinkedListType element; element = peek(); del(); return CircularLinkedListType( element ); // return temporary } // Prefix template CircularLinkedList& CircularLinkedList::operator++() { if ( this->list_size != 0 ) position = position->next; return *this; } /* // Postfix template CircularLinkedList& CircularLinkedList::operator++(int) { CircularLinkedList before; before=*this; operator++(); return before; } */ template CircularLinkedList& CircularLinkedList::operator++( int ) { return this->operator++(); } // Prefix template CircularLinkedList& CircularLinkedList::operator--() { if ( this->list_size != 0 ) this->position = this->position->previous; return *this; } /* // Postfix template CircularLinkedList& CircularLinkedList::operator--(int) { CircularLinkedList before; before=*this; operator--(); return before; } */ template CircularLinkedList& CircularLinkedList::operator--( int ) { return this->operator--(); } template void CircularLinkedList::clear( void ) { if ( this->list_size == 0 ) return ; else if ( this->list_size == 1 ) // {delete root->item; delete root;} { delete this->root; } else { node* current; node* temp; current = this->root; do { temp = current; current = current->next; // delete temp->item; delete temp; } while ( current != this->root ); } this->list_size = 0; this->root = 0; this->position = 0; } template inline void CircularLinkedList::concatenate( const CircularLinkedList& L ) { unsigned int counter; node* ptr; if ( L.list_size == 0 ) return ; if ( this->list_size == 0 ) * this = L; ptr = L.root; this->position = this->root->previous; // Cycle through each element in L and add it to the current list for ( counter = 0; counter < L.list_size; counter++ ) { // Add item after the current item pointed to // add(*(ptr->item)); add ( ptr->item ) ; // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer ptr = ptr->next; this->position = this->position->next; } } template inline void CircularLinkedList::sort( void ) { if ( this->list_size <= 1 ) return ; // Call equal operator to assign result of mergesort to current object *this = mergesort( *this ); this->position = this->root; } template CircularLinkedList CircularLinkedList::mergesort( const CircularLinkedList& L ) { unsigned int counter; node* location; CircularLinkedList L1; CircularLinkedList L2; location = L.root; // Split the list into two equal size sublists, L1 and L2 for ( counter = 0; counter < L.list_size / 2; counter++ ) { // L1.add (*(location->item)); L1.add ( location->item ); location = location->next; } for ( ;counter < L.list_size; counter++ ) { // L2.add(*(location->item)); L2.add ( location->item ); location = location->next; } // Recursively sort the sublists if ( L1.list_size > 1 ) L1 = mergesort( L1 ); if ( L2.list_size > 1 ) L2 = mergesort( L2 ); // Merge the two sublists return merge( L1, L2 ); } template CircularLinkedList CircularLinkedList::merge( CircularLinkedList L1, CircularLinkedList L2 ) { CircularLinkedList X; CircularLinkedListType element; L1.position = L1.root; L2.position = L2.root; // While neither list is empty while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) { // Compare the first items of L1 and L2 // Remove the smaller of the two items from the list if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) // if ((*((L1.root)->item)) < (*((L2.root)->item))) { // element = *((L1.root)->item); element = ( L1.root ) ->item; L1.del(); } else { // element = *((L2.root)->item); element = ( L2.root ) ->item; L2.del(); } // Add this item to the end of X X.add( element ); X++; } // Add the remaining list to X if ( L1.list_size != 0 ) X.concatenate( L1 ); else X.concatenate( L2 ); return X; } template LinkedList LinkedList::mergesort( const LinkedList& L ) { unsigned int counter; typename LinkedList::node* location; LinkedList L1; LinkedList L2; location = L.root; // Split the list into two equal size sublists, L1 and L2 for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) { // L1.add (*(location->item)); L1.add ( location->item ); location = location->next; } for ( ;counter < L.LinkedList_size; counter++ ) { // L2.add(*(location->item)); L2.add ( location->item ); location = location->next; } // Recursively sort the sublists if ( L1.list_size > 1 ) L1 = mergesort( L1 ); if ( L2.list_size > 1 ) L2 = mergesort( L2 ); // Merge the two sublists return merge( L1, L2 ); } template LinkedList LinkedList::merge( LinkedList L1, LinkedList L2 ) { LinkedList X; LinkedListType element; L1.position = L1.root; L2.position = L2.root; // While neither list is empty while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) { // Compare the first items of L1 and L2 // Remove the smaller of the two items from the list if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) // if ((*((L1.root)->item)) < (*((L2.root)->item))) { element = ( L1.root ) ->item; // element = *((L1.root)->item); L1.del(); } else { element = ( L2.root ) ->item; // element = *((L2.root)->item); L2.del(); } // Add this item to the end of X X.add( element ); } // Add the remaining list to X if ( L1.LinkedList_size != 0 ) X.concatenate( L1 ); else X.concatenate( L2 ); return X; } // Prefix template LinkedList& LinkedList::operator++() { if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) this->position = this->position->next; return *this; } /* // Postfix template LinkedList& LinkedList::operator++(int) { LinkedList before; before=*this; operator++(); return before; } */ // Postfix template LinkedList& LinkedList::operator++( int ) { return this->operator++(); } // Prefix template LinkedList& LinkedList::operator--() { if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) this->position = this->position->previous; return *this; } /* // Postfix template LinkedList& LinkedList::operator--(int) { LinkedList before; before=*this; operator--(); return before; } */ // Postfix template LinkedList& LinkedList::operator--( int ) { return this->operator--(); } } // End namespace #endif blobby-1.0rc3/src/raknet/MessageHandlerInterface.h0000644000175000017500000000712712042452367023506 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Message handler interface * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __MESSAGE_HANDLER_INTERFACE_H #define __MESSAGE_HANDLER_INTERFACE_H class RakPeerInterface; struct Packet; #include "NetworkTypes.h" // MessageHandlers should derive from MessageHandlerInterface and be attached to RakPeer using the function AttachMessageHandler // On a user call to Receive, OnReceive is called for every MessageHandlerInterface, which can then take action based on the packet // passed to it. This is used to transparently add game-independent functional modules, similar to browser plugins class MessageHandlerInterface { public: /** * OnUpdate is called everytime a packet is checked for . * * @param peer - the instance of RakPeer that is calling Receive * */ virtual void OnAttach(RakPeerInterface *peer); /** * OnUpdate is called everytime a packet is checked for . * * @param peer - the instance of RakPeer that is calling Receive * */ virtual void OnUpdate(RakPeerInterface *peer)=0; /** * OnReceive is called for every packet. * * @param peer - the instance of RakPeer that is calling Receive * @param packet - the packet that is being returned to the user * * @return true to absorb the packet, false to allow the packet to propagate to another handler, or to the game */ virtual bool OnReceive(RakPeerInterface *peer, Packet *packet)=0; /** * Called when RakPeer is shutdown * * @param playerId - the instance of RakPeer that is calling Receive * */ virtual void OnDisconnect(RakPeerInterface *peer)=0; /** * PropagateToGame tells RakPeer if a particular packet should be sent to the game or not * If you create a custom packet ID just for this handler you would not want to propagate it to the game, for example * * @param id - The first byte of the packet in question * * @return true (default) to allow a packet to propagate to the game. False to only let the packet be sent to message handlers */ virtual bool PropagateToGame(Packet *packet) const; }; #endif blobby-1.0rc3/src/raknet/ReliabilityLayer.h0000644000175000017500000004225312042452367022250 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Provide Reliability Communication * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RELIABILITY_LAYER_H #define __RELIABILITY_LAYER_H #include "MTUSize.h" #include "LinkedList.h" #include "ArrayList.h" #include "SocketLayer.h" #include "PacketPriority.h" #include "BitStream.h" #include "SimpleMutex.h" #include "InternalPacket.h" #include "InternalPacketPool.h" #include "DataBlockEncryptor.h" #include "RakNetStatistics.h" #include "SHA1.h" #include "../blobnet/adt/Queue.hpp" /** * Sizeof an UDP header in byte */ #define UDP_HEADER_SIZE 28 /** * Number of ordered streams available. * You can use up to 32 ordered streams */ #define NUMBER_OF_ORDERED_STREAMS 32 // 2^5 /** * Timeout before killing a connection. If no response to a reliable * packet for this long kill the connection */ #ifdef _DEBUG const unsigned int TIMEOUT_TIME = 30000; // In debug don't timeout for 30 seconds so the connection doesn't drop while debugging #else const unsigned int TIMEOUT_TIME = 10000; // In release timeout after the normal 10 seconds #endif /** * If you change MAX_AVERAGE_PACKETS_PER_SECOND or TIMEOUT_TIME, you * must make sure RECEIVED_PACKET_LOG_LENGTH < the range of * PacketNumberType (held in InternalPacket.h) * 6553.5 is the maximum for an unsigned short * @attention * take in account the value of RECEIVED_PACKET_LOG_LENGTH when changing this! * */ //const int MAX_AVERAGE_PACKETS_PER_SECOND = 6553; /** * This value must be less than the range of PacketNumberType. * PacketNumberType is in InternalPacket.h */ //const int RECEIVED_PACKET_LOG_LENGTH = ( TIMEOUT_TIME / 1000 ) * MAX_AVERAGE_PACKETS_PER_SECOND; #include "BitStream.h" /** * This class provide reliable communication services. * */ class ReliabilityLayer { public: /** * Default Constructor */ ReliabilityLayer(); /** * Destructor */ ~ReliabilityLayer(); /** * Resets the layer for reuse. * Callable from multiple threads */ void Reset( void ); /** * Sets up encryption * Callable from multiple threads * @param key the key used for encryption */ void SetEncryptionKey( const unsigned char *key ); /** * Assign a socket for the reliability layer to use for writing * Callable from multiple threads * @param s The socket to use for communication */ void SetSocket( SOCKET s ); /** * Get the socket held by the reliability layer * Callable from multiple threads * @return Retrieve the socket so that every body can used it */ SOCKET GetSocket( void ); /** * Must be called by the same thread as update Packets are read * directly from the socket layer and skip the reliability layer * because unconnected players do not use the reliability layer This * function takes packet data after a player has been confirmed as * connected. The game should not use that data directly because * some data is used internally, such as packet acknowledgement and * split packets * * @param buffer Store up to @em length byte from the incoming data * @param length The size of buffer * @param restrictToFirstPacket Set to true if this is a connection request. It will only allow packets with a packetNumber of 0 * @param firstPacketDataID If restrictToFirstPacket is true, then this is packetID type that is allowed. Other types are ignored * @param length The size of buffer * @return false on modified packets */ bool HandleSocketReceiveFromConnectedPlayer( const char *buffer, int length ); /** * This gets an end-user packet already parsed out. * * @param data The game data * @return Returns number of BITS put into the buffer * @note Callable from multiple threads * */ int Receive( char**data ); /** * Puts data on the send queue * @param bitStream contains the data to send * @param priority is what priority to send the data at * @param reliability is what reliability to use * @param orderingChannel is from 0 to 31 and specifies what stream to use * @param makeDataCopy if true @em bitStream will keep an internal copy of * the packet. * @param MTUSize * @note Callable from multiple threads * * @todo Document MTUSize parameter */ bool Send( char *data, int numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, unsigned int currentTime ); /** * Run this once per game cycle. Handles internal lists and * actually does the send Must be called by the same thread as * HandleSocketReceiveFromConnectedPlayer * * @param s the communication end point * @param playerId The Unique Player Identifier who should * have sent some packets * @param MTUSize * @param time * @todo * Document MTUSize and time parameter */ void Update( SOCKET s, PlayerID playerId, int MTUSize, unsigned int time ); /** * If Read returns -1 and this returns true then a modified packet * was detected * @return true when a modified packet is detected */ bool IsCheater( void ) const; /** * Were you ever unable to deliver a packet despite retries? * @return true if the connection no more responds */ bool IsDeadConnection( void ) const; /** * Causes IsDeadConnection to return true */ void KillConnection(void); /** * How long to wait between packet resends * @param i time to wait before resending a packet */ void SetLostPacketResendDelay( unsigned int i ); /** * Get Statistics * @return The object containing all stats */ RakNetStatisticsStruct * const GetStatistics( void ); /** * Are we waiting for any data to be sent out or be processed by the player? */ bool IsDataWaiting(void); private: /** * Returns true if we can or should send a frame. False if we should not * @param time The time to wait before sending a frame * @return true if we can or should send a frame. */ bool IsFrameReady( unsigned int time ); /** * Generates a frame (coalesced packets) * @param output The resulting BitStream * @param MTUSize * @param reliableDataSent True if we have to sent a reliable data * @param time Delay to send the packet or deadline * @todo * Check for time paramter * */ void GenerateFrame( RakNet::BitStream *output, int MTUSize, bool *reliableDataSent, unsigned int time ); /** * Writes a bitstream to the socket * @param s The socket used for sending data * @param playerId The target of the communication * @param bitStream The data to send. */ void SendBitStream( SOCKET s, PlayerID playerId, RakNet::BitStream *bitStream ); /** * Parse an internalPacket and create a bitstream to represent this data * Returns number of bits used */ int WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket ); // Parse a bitstream and create an internal packet to represent this data InternalPacket* CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, unsigned int time ); // Does what the function name says void RemovePacketFromResendQueueAndDeleteOlderReliableSequenced( PacketNumberType packetNumber ); // Acknowledge receipt of the packet with the specified packetNumber void SendAcknowledgementPacket( PacketNumberType packetNumber, unsigned int time ); // This will return true if we should not send at this time bool IsSendThrottled( void ); // We lost a packet void UpdatePacketloss( unsigned int time ); // Parse an internalPacket and figure out how many header bits would be written. Returns that number int GetBitStreamHeaderLength( const InternalPacket *const internalPacket ); // Get the SHA1 code void GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ); // Check the SHA1 code bool CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ); // Search the specified list for sequenced packets on the specified ordering channel, optionally skipping those with splitPacketId, and delete them void DeleteSequencedPacketsInList( unsigned char orderingChannel, BasicDataStructures::List&theList, int splitPacketId = -1 ); // Search the specified list for sequenced packets with a value less than orderingIndex and delete them void DeleteSequencedPacketsInList( unsigned char orderingChannel, BlobNet::ADT::Queue&theList ); // Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex bool IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ); // Split the passed packet into chunks under MTU_SIZE bytes (including headers) and save those new chunks void SplitPacket( InternalPacket *internalPacket, int MTUSize ); // Insert a packet into the split packet list void InsertIntoSplitPacketList( InternalPacket * internalPacket ); // Take all split chunks with the specified splitPacketId and try to reconstruct a packet. If we can, allocate and return it. Otherwise return 0 InternalPacket * BuildPacketFromSplitPacketList( unsigned int splitPacketId, unsigned int time ); // Delete any unreliable split packets that have long since expired void DeleteOldUnreliableSplitPackets( unsigned int time ); // Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. // Does not copy any split data parameters as that information is always generated does not have any reason to be copied InternalPacket * CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, unsigned int time ); // Get the specified ordering list // LOCK THIS WHOLE BLOCK WITH reliabilityLayerMutexes[orderingList_MUTEX].Unlock(); BasicDataStructures::LinkedList *GetOrderingListAtOrderingStream( unsigned char orderingChannel ); // Add the internal packet to the ordering list in order based on order index void AddToOrderingList( InternalPacket * internalPacket ); // Inserts a packet into the resend list in order void InsertPacketIntoResendQueue( InternalPacket *internalPacket, unsigned int time, bool makeCopyOfInternalPacket, bool resetAckTimer ); // Memory handling void FreeMemory( bool freeAllImmediately ); void FreeThreadedMemory( void ); void FreeThreadSafeMemory( void ); // Initialize the variables void InitializeVariables( void ); bool IsExpiredTime(unsigned int input, unsigned int currentTime) const; unsigned int IsReceivedPacketHole(unsigned int input, unsigned int currentTime) const; unsigned int MakeReceivedPacketHole(unsigned int input) const; unsigned int GetResendQueueDataSize(void) const; void UpdateThreadedMemory(void); // 10/17/05 - Don't need any of this now that all interactions are from the main network thread /* // STUFF TO MUTEX HERE enum { // splitPacketList_MUTEX, // We don't have to mutex this as long as Update and HandleSocketReceiveFromConnectedPlayer are called by the same thread //sendQueueSystemPriority_MUTEX, // Replaced with producer / consumer //sendQueueHighPriority_MUTEX, // Replaced with producer / consumer //sendQueueMediumPriority_MUTEX, // Replaced with producer / consumer //sendQueueLowPriority_MUTEX, // Replaced with producer / consumer //resendQueue_MUTEX,// We don't have to mutex this as long as Update and HandleSocketReceiveFromConnectedPlayer are called by the same thread //orderingList_MUTEX,// We don't have to mutex this as long as Update and HandleSocketReceiveFromConnectedPlayer are called by the same thread //acknowledgementQueue_MUTEX,// We don't have to mutex this as long as Update and HandleSocketReceiveFromConnectedPlayer are called by the same thread // outputQueue_MUTEX,// We don't have to mutex this as long as Recieve and HandleSocketReceiveFromConnectedPlayer are called by the same thread packetNumber_MUTEX, // windowSize_MUTEX, // Causes long delays for some reason //lastAckTime_MUTEX,// We don't have to mutex this as long as Update and HandleSocketReceiveFromConnectedPlayer are called by the same thread //updateBitStream_MUTEX,// We don't have to mutex this as long as Update and HandleSocketReceiveFromConnectedPlayer are called by the same thread waitingForOrderedPacketWriteIndex_MUTEX, waitingForSequencedPacketWriteIndex_MUTEX, NUMBER_OF_RELIABILITY_LAYER_MUTEXES }; SimpleMutex reliabilityLayerMutexes[ NUMBER_OF_RELIABILITY_LAYER_MUTEXES ]; */ BasicDataStructures::List splitPacketList; BasicDataStructures::List*> orderingList; BlobNet::ADT::Queue acknowledgementQueue, outputQueue; BlobNet::ADT::Queue resendQueue; BlobNet::ADT::Queue sendPacketSet[ NUMBER_OF_PRIORITIES ]; PacketNumberType packetNumber; //unsigned int windowSize; unsigned int lastAckTime; RakNet::BitStream updateBitStream; OrderingIndexType waitingForOrderedPacketWriteIndex[ NUMBER_OF_ORDERED_STREAMS ], waitingForSequencedPacketWriteIndex[ NUMBER_OF_ORDERED_STREAMS ]; // Used for flow control (changed to regular TCP sliding window) // unsigned int maximumWindowSize, bytesSentSinceAck; // unsigned int outputWindowFullTime; // under linux if this last variable is on the line above it the delete operator crashes deleting this class! // STUFF TO NOT MUTEX HERE (called from non-conflicting threads, or value is not important) OrderingIndexType waitingForOrderedPacketReadIndex[ NUMBER_OF_ORDERED_STREAMS ], waitingForSequencedPacketReadIndex[ NUMBER_OF_ORDERED_STREAMS ]; bool deadConnection, cheater; // unsigned int lastPacketSendTime,retransmittedFrames, sentPackets, sentFrames, receivedPacketsCount, bytesSent, bytesReceived,lastPacketReceivedTime; unsigned int lostPacketResendDelay; unsigned int splitPacketId; // int TIMEOUT_TIME; // How long to wait in MS before timing someone out //int MAX_AVERAGE_PACKETS_PER_SECOND; // Name says it all // int RECEIVED_PACKET_LOG_LENGTH, requestedReceivedPacketLogLength; // How big the receivedPackets array is // unsigned int *receivedPackets; unsigned int blockWindowIncreaseUntilTime; RakNetStatisticsStruct statistics; // New memory-efficient receivedPackets algorithm: // Store times in a queue. Store the base offset. // On time insert: // Pop all expired times. For each expired time popped, increase the baseIndex // If the packet number is >= the baseIndex and (packetNumber - baseIndex) < queue size // We got a duplicate packet. // else // Add 0 times to the queue until (packetNumber - baseIndex) < queue size. BlobNet::ADT::Queue receivedPackets; PacketNumberType receivedPacketsBaseIndex; bool resetReceivedPackets; // Windowing algorithm: // Start at a minimum size // Set the lossy window size to INFINITE // If the current window size is lower than the lossy window size, then increase the window size by 1 per frame if the number of acks is >= the window size and data is waiting to go out. // Otherwise, do the same, but also apply the limit of 1 frame per second. If we are more than 5 over the lossy window size, increase the lossy window size by 1 // If we lose a frame, decrease the window size by 1 per frame lost. Record the new window size as the lossy window size. int windowSize; int lossyWindowSize; unsigned int lastWindowIncreaseSizeTime; DataBlockEncryptor encryptor; #ifdef __USE_IO_COMPLETION_PORTS /** * @note Windows Port only * */ SOCKET readWriteSocket; #endif /** * This variable is so that free memory can be called by only the * update thread so we don't have to mutex things so much */ bool freeThreadedMemoryOnNextUpdate; #ifdef _INTERNET_SIMULATOR struct DataAndTime { char data[ 2000 ]; int length; unsigned int sendTime; }; BasicDataStructures::List delayList; #endif // This has to be a member because it's not threadsafe when I removed the mutexes InternalPacketPool internalPacketPool; }; #endif blobby-1.0rc3/src/raknet/ExtendedOverlappedPool.cpp0000644000175000017500000000473712042452367023756 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file ExtendedOverlappedPool.cpp * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef __USE_IO_COMPLETION_PORTS #include "ExtendedOverlappedPool.h" ExtendedOverlappedPool ExtendedOverlappedPool::I; ExtendedOverlappedPool::ExtendedOverlappedPool() {} ExtendedOverlappedPool::~ExtendedOverlappedPool() { // The caller better have returned all the packets! ExtendedOverlappedStruct * p; poolMutex.Lock(); while ( pool.size() ) { p = pool.pop(); delete p; } poolMutex.Unlock(); } ExtendedOverlappedStruct* ExtendedOverlappedPool::GetPointer( void ) { ExtendedOverlappedStruct * p = 0; poolMutex.Lock(); if ( pool.size() ) p = pool.pop(); poolMutex.Unlock(); if ( p ) return p; return new ExtendedOverlappedStruct; } void ExtendedOverlappedPool::ReleasePointer( ExtendedOverlappedStruct *p ) { poolMutex.Lock(); pool.push( p ); poolMutex.Unlock(); } #endif blobby-1.0rc3/src/raknet/RakClient.cpp0000644000175000017500000003530312042452367021207 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Client communication End Point Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "RakClient.h" #include "PacketEnumerations.h" #include "GetTime.h" // Constructor RakClient::RakClient() { unsigned i; for ( i = 0; i < 32; i++ ) otherClients[ i ].isActive = false; nextSeedUpdate = 0; } // Destructor RakClient::~RakClient() {} #pragma warning( disable : 4100 ) // warning C4100: 'depreciated' : unreferenced formal parameter bool RakClient::Connect( const char* host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer ) { RakPeer::Disconnect( 100 ); RakPeer::Initialize( 1, clientPort, threadSleepTimer ); if ( host[ 0 ] < '0' || host[ 0 ] > '2' ) { host = ( char* ) SocketLayer::Instance()->DomainNameToIP( host ); } unsigned i; for ( i = 0; i < 32; i++ ) { otherClients[ i ].isActive = false; otherClients[ i ].playerId = UNASSIGNED_PLAYER_ID; otherClients[ i ].staticData.Reset(); } // ignore depreciated. A pointless variable return RakPeer::Connect( host, serverPort, ( char* ) password.GetData(), password.GetNumberOfBytesUsed() ); } void RakClient::Disconnect( unsigned int blockDuration ) { RakPeer::Disconnect( blockDuration ); } void RakClient::InitializeSecurity( const char *privKeyP, const char *privKeyQ ) { RakPeer::InitializeSecurity( privKeyP, privKeyQ, 0, 0 ); } void RakClient::SetPassword( const char *_password ) { if ( _password == 0 || _password[ 0 ] == 0 ) password.Reset(); else { password.Reset(); password.Write( _password, ( int ) strlen( _password ) + 1 ); } } bool RakClient::HasPassword( void ) const { return password.GetNumberOfBytesUsed() > 0; } bool RakClient::Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel ) { if ( remoteSystemList == 0 ) return false; return RakPeer::Send( data, length, priority, reliability, orderingChannel, remoteSystemList[ 0 ].playerId, false ); } bool RakClient::Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel ) { if ( remoteSystemList == 0 ) return false; return RakPeer::Send( bitStream, priority, reliability, orderingChannel, remoteSystemList[ 0 ].playerId, false ); } Packet* RakClient::Receive( void ) { Packet * packet = RakPeer::Receive(); // Intercept specific client / server feature packets if ( packet ) { RakNet::BitStream bitStream( ( char* ) packet->data, packet->length, false ); int i; if ( packet->data[ 0 ] == ID_CONNECTION_REQUEST_ACCEPTED ) { // ConnectionAcceptStruct cas; // cas.Deserialize(bitStream); // unsigned short remotePort; // PlayerID externalID; PlayerIndex playerIndex; RakNet::BitStream inBitStream((char*)packet->data, packet->length, false); inBitStream.IgnoreBits(8); // ID_CONNECTION_REQUEST_ACCEPTED inBitStream.IgnoreBits(8 * sizeof(unsigned short)); //inBitStream.Read(remotePort); inBitStream.IgnoreBits(8 * sizeof(unsigned int)); //inBitStream.Read(externalID.binaryAddress); inBitStream.IgnoreBits(8 * sizeof(unsigned short)); //inBitStream.Read(externalID.port); inBitStream.Read(playerIndex); localPlayerIndex = playerIndex; packet->playerIndex = playerIndex; } else if ( packet->data[ 0 ] == ID_REMOTE_NEW_INCOMING_CONNECTION || packet->data[ 0 ] == ID_REMOTE_EXISTING_CONNECTION || packet->data[ 0 ] == ID_REMOTE_DISCONNECTION_NOTIFICATION || packet->data[ 0 ] == ID_REMOTE_CONNECTION_LOST ) { bitStream.IgnoreBits( 8 ); // Ignore identifier bitStream.Read( packet->playerId.binaryAddress ); bitStream.Read( packet->playerId.port ); if ( bitStream.Read( ( unsigned short& ) packet->playerIndex ) == false ) { DeallocatePacket( packet ); return 0; } if ( packet->data[ 0 ] == ID_REMOTE_DISCONNECTION_NOTIFICATION || packet->data[ 0 ] == ID_REMOTE_CONNECTION_LOST ) { i = GetOtherClientIndexByPlayerID( packet->playerId ); if ( i >= 0 ) otherClients[ i ].isActive = false; } } else if ( packet->data[ 0 ] == ID_REMOTE_STATIC_DATA ) { bitStream.IgnoreBits( 8 ); // Ignore identifier bitStream.Read( packet->playerId.binaryAddress ); bitStream.Read( packet->playerId.port ); bitStream.Read( packet->playerIndex ); // ADDED BY KURI i = GetOtherClientIndexByPlayerID( packet->playerId ); if ( i < 0 ) i = GetFreeOtherClientIndex(); if ( i >= 0 ) { otherClients[ i ].playerId = packet->playerId; otherClients[ i ].isActive = true; otherClients[ i ].staticData.Reset(); // The static data is what is left over in the stream otherClients[ i ].staticData.Write( ( char* ) bitStream.GetData() + BITS_TO_BYTES( bitStream.GetReadOffset() ), bitStream.GetNumberOfBytesUsed() - BITS_TO_BYTES( bitStream.GetReadOffset() ) ); } } else if ( packet->data[ 0 ] == ID_BROADCAST_PINGS ) { PlayerID playerId; int index; bitStream.IgnoreBits( 8 ); // Ignore identifier for ( i = 0; i < 32; i++ ) { if ( bitStream.Read( playerId.binaryAddress ) == false ) break; // No remaining data! bitStream.Read( playerId.port ); index = GetOtherClientIndexByPlayerID( playerId ); if ( index >= 0 ) bitStream.Read( otherClients[ index ].ping ); else { index = GetFreeOtherClientIndex(); if ( index >= 0 ) { otherClients[ index ].isActive = true; bitStream.Read( otherClients[ index ].ping ); otherClients[ index ].playerId = playerId; otherClients[ index ].staticData.Reset(); } else bitStream.IgnoreBits( sizeof( short ) * 8 ); } } DeallocatePacket( packet ); return 0; } else if ( packet->data[ 0 ] == ID_TIMESTAMP && packet->length == sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned int) ) { /* RakNet::BitStream s_BitS( (char *)packet->data, SetRandomNumberSeedStruct_Size, false ); SetRandomNumberSeedStruct s; s.Deserialize( s_BitS ); */ RakNet::BitStream inBitStream((char *)packet->data, packet->length, false); /* unsigned char ts; unsigned int timeStamp; unsigned char typeId; unsigned int seed; unsigned int nextSeed; */ unsigned int timeStamp; unsigned char typeId; unsigned int in_seed; unsigned int in_nextSeed; inBitStream.IgnoreBits(8); // ID_TIMESTAMP inBitStream.Read(timeStamp); inBitStream.Read(typeId); // ID_SET_RANDOM_NUMBER_SEED ? // Check to see if this is a user TIMESTAMP message which // accidentally has length SetRandomNumberSeedStruct_Size if ( typeId != ID_SET_RANDOM_NUMBER_SEED ) return packet; inBitStream.Read(in_seed); inBitStream.Read(in_nextSeed); seed = in_seed; nextSeed = in_nextSeed; nextSeedUpdate = timeStamp + 9000; // Seeds are updated every 9 seconds DeallocatePacket( packet ); return 0; } } return packet; } void RakClient::DeallocatePacket( Packet *packet ) { RakPeer::DeallocatePacket( packet ); } void RakClient::PingServer( void ) { if ( remoteSystemList == 0 ) return ; RakPeer::Ping( remoteSystemList[ 0 ].playerId ); } void RakClient::PingServer( const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections ) { // Must be 2 for linux systems RakPeer::Initialize( 2, clientPort, 10 ); RakPeer::Ping( host, serverPort, onlyReplyOnAcceptingConnections ); } int RakClient::GetAveragePing( void ) { if ( remoteSystemList == 0 ) return -1; return RakPeer::GetAveragePing( remoteSystemList[ 0 ].playerId ); } int RakClient::GetLastPing( void ) const { if ( remoteSystemList == 0 ) return -1; return RakPeer::GetLastPing( remoteSystemList[ 0 ].playerId ); } int RakClient::GetLowestPing( void ) const { if ( remoteSystemList == 0 ) return -1; return RakPeer::GetLowestPing( remoteSystemList[ 0 ].playerId ); } int RakClient::GetPlayerPing( PlayerID playerId ) { int i; for ( i = 0; i < 32; i++ ) if ( otherClients[ i ].playerId == playerId ) return otherClients[ i ].ping; return -1; } void RakClient::StartOccasionalPing( void ) { RakPeer::SetOccasionalPing( true ); } void RakClient::StopOccasionalPing( void ) { RakPeer::SetOccasionalPing( false ); } bool RakClient::IsConnected( void ) const { unsigned short numberOfSystems; RakPeer::GetConnectionList( 0, &numberOfSystems ); return numberOfSystems == 1; } unsigned int RakClient::GetSynchronizedRandomInteger( void ) const { if ( RakNet::GetTime() > nextSeedUpdate ) return nextSeed; else return seed; } bool RakClient::GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ) { return RakPeer::GenerateCompressionLayer( inputFrequencyTable, inputLayer ); } bool RakClient::DeleteCompressionLayer( bool inputLayer ) { return RakPeer::DeleteCompressionLayer( inputLayer ); } void RakClient::SetTrackFrequencyTable( bool b ) { RakPeer::SetCompileFrequencyTable( b ); } bool RakClient::GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ) { return RakPeer::GetOutgoingFrequencyTable( outputFrequencyTable ); } float RakClient::GetCompressionRatio( void ) const { return RakPeer::GetCompressionRatio(); } float RakClient::GetDecompressionRatio( void ) const { return RakPeer::GetDecompressionRatio(); } void RakClient::AttachMessageHandler( MessageHandlerInterface *messageHandler ) { RakPeer::AttachMessageHandler(messageHandler); } void RakClient::DetachMessageHandler( MessageHandlerInterface *messageHandler ) { RakPeer::DetachMessageHandler(messageHandler); } RakNet::BitStream * RakClient::GetStaticServerData( void ) { if ( remoteSystemList == 0 ) return 0; return RakPeer::GetRemoteStaticData( remoteSystemList[ 0 ].playerId ); } void RakClient::SetStaticServerData( const char *data, const long length ) { if ( remoteSystemList == 0 ) return ; RakPeer::SetRemoteStaticData( remoteSystemList[ 0 ].playerId, data, length ); } RakNet::BitStream * RakClient::GetStaticClientData( PlayerID playerId ) { int i; if ( playerId == UNASSIGNED_PLAYER_ID ) { return & localStaticData; } else { // KevinJ - No this is not correct. The client does not know about any system other than the server. // The caller needs to check for a NULL return value /// \todo Cirilo: Check that this is the CORRECT implementation /// An alternative is to fall through and return this expression /// rather than directly returning '0'. // return RakPeer::GetRemoteStaticData( playerId ); i = GetOtherClientIndexByPlayerID( playerId ); // printf("OtherClientIndexByPlayerID: %d\n", i); if ( i >= 0 ) { return & ( otherClients[ i ].staticData ); } } return 0; } void RakClient::SetStaticClientData( PlayerID playerId, const char *data, const long length ) { int i; if ( playerId == UNASSIGNED_PLAYER_ID ) { localStaticData.Reset(); localStaticData.Write( data, length ); } else { i = GetOtherClientIndexByPlayerID( playerId ); if ( i >= 0 ) { otherClients[ i ].staticData.Reset(); otherClients[ i ].staticData.Write( data, length ); } else RakPeer::SetRemoteStaticData( playerId, data, length ); } } void RakClient::SendStaticClientDataToServer( void ) { if ( remoteSystemList == 0 ) return ; RakPeer::SendStaticData( remoteSystemList[ 0 ].playerId ); } PlayerID RakClient::GetServerID( void ) const { if ( remoteSystemList == 0 ) return UNASSIGNED_PLAYER_ID; return remoteSystemList[ 0 ].playerId; } PlayerID RakClient::GetPlayerID( void ) const { if ( remoteSystemList == 0 ) return UNASSIGNED_PLAYER_ID; // GetExternalID is more accurate because it reflects our external IP and port to the server. // GetInternalID only matches the parameters we passed PlayerID myID = RakPeer::GetExternalID( remoteSystemList[ 0 ].playerId ); if ( myID == UNASSIGNED_PLAYER_ID ) return RakPeer::GetInternalID(); else return myID; } const char* RakClient::PlayerIDToDottedIP( PlayerID playerId ) const { return RakPeer::PlayerIDToDottedIP( playerId ); } void RakClient::PushBackPacket( Packet *packet ) { RakPeer::PushBackPacket( packet ); } bool RakClient::SetMTUSize( int size ) { return RakPeer::SetMTUSize( size ); } int RakClient::GetMTUSize( void ) const { return RakPeer::GetMTUSize(); } void RakClient::AllowConnectionResponseIPMigration( bool allow ) { RakPeer::AllowConnectionResponseIPMigration( allow ); } void RakClient::AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ) { RakPeer::AdvertiseSystem( host, remotePort, data, dataLength ); } RakNetStatisticsStruct* const RakClient::GetStatistics( void ) { return RakPeer::GetStatistics( remoteSystemList[ 0 ].playerId ); } int RakClient::GetOtherClientIndexByPlayerID( PlayerID playerId ) { unsigned i; for ( i = 0; i < 32; i++ ) { if ( otherClients[ i ].playerId == playerId ) return i; } return -1; } int RakClient::GetFreeOtherClientIndex( void ) { unsigned i; for ( i = 0; i < 32; i++ ) { if ( otherClients[ i ].isActive == false ) return i; } return -1; } PlayerIndex RakClient::GetPlayerIndex( void ) { return localPlayerIndex; } blobby-1.0rc3/src/raknet/RakPeerInterface.h0000644000175000017500000006607312042452367022162 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief User interface of RakPeer. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_PEER_INTERFACE_H #define __RAK_PEER_INTERFACE_H #include "PacketPriority.h" #include "NetworkTypes.h" #include "BitStream.h" #include "RakNetStatistics.h" /** * @brief Define the user view of a RakPeer instance. * * This class define the user point of view of a RakPeer instance. * */ class MessageHandlerInterface; class RakPeerInterface { public: /** * Destructor */ virtual ~RakPeerInterface() {} // -------------------------------------------------------------------------------------------- // Major Low Level Functions - Functions needed by most users // -------------------------------------------------------------------------------------------- /** * Starts the network threads, opens the listen port * You must call this before calling SetMaximumIncomingConnections or Connect * Multiple calls while already active are ignored. To call this function again with different settings, you must first call Disconnect() * To accept incoming connections, use SetMaximumIncomingConnections * * Parameters: * @param MaximumNumberOfPeers Required so RakNet can preallocate and for thread safety. * - A pure client would set this to 1. A pure server would set it to the number of allowed clients. * - A hybrid would set it to the sum of both types of connections * @param localPort The port to listen for connections on. * @param _threadSleepTimer >=0 for how many ms to Sleep each internal update cycle (recommended 30 for low performance, 0 for regular) * @param forceHostAddress Can force RakNet to use a particular IP to host on. Pass 0 to automatically pick an IP * * @return False on failure (can't create socket or thread), true on success. */ virtual bool Initialize( unsigned short MaximumNumberOfPeers, unsigned short localPort, int _threadSleepTimer, const char *forceHostAddress=0 ) = 0; /** * Must be called while offline * Secures connections though a combination of SHA1, AES128, SYN Cookies, and RSA to prevent * connection spoofing, replay attacks, data eavesdropping, packet tampering, and MitM attacks. * There is a significant amount of processing and a slight amount of bandwidth * overhead for this feature. * * If you accept connections, you must call this or else secure connections will not be enabled * for incoming connections. * If you are connecting to another system, you can call this with values for the * (e and p,q) public keys before connecting to prevent MitM * * @param pubKeyE A pointer to the public keys from the RSACrypt class. * @param pubKeyN A pointer to the public keys from the RSACrypt class. * @param privKeyP Public key generated from the RSACrypt class. * @param privKeyQ Public key generated from the RSACrypt class. * If the private keys are 0, then a new key will be generated when this function is called * * @see the Encryption sample */ virtual void InitializeSecurity(const char *pubKeyE, const char *pubKeyN, const char *privKeyP, const char *privKeyQ ) = 0; /** * Disables all security. * @note Must be called while offline * */ virtual void DisableSecurity( void ) = 0; /** * Sets how many incoming connections are allowed. If this is less than the number of players currently connected, no * more players will be allowed to connect. If this is greater than the maximum number of peers allowed, it will be reduced * to the maximum number of peers allowed. * * @param numberAllowed Maximum number of incoming connections allowed. */ virtual void SetMaximumIncomingConnections( unsigned short numberAllowed ) = 0; /** * Get the number of maximum incomming connection. * @return the maximum number of incoming connections, which is always <= MaximumNumberOfPeers */ virtual unsigned short GetMaximumIncomingConnections( void ) const = 0; /** * Sets the password incoming connections must match in the call to Connect (defaults to none) * Pass 0 to passwordData to specify no password * * @param passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. * Specify 0 for no password data * @param passwordDataLength The length in bytes of passwordData */ virtual void SetIncomingPassword( const char* passwordData, int passwordDataLength ) = 0; /** * Get the password set by SetIncomingPassword in a BitStream * @return The password in a BitStream. */ RakNet::BitStream *GetIncomingPassword( void ); /** * Call this to connect to the specified host (ip or domain name) and server port. * Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. Calling both acts as a true peer. * This is a non-blocking connection. You know the connection is successful when IsConnected() returns true * or receive gets a packet with the type identifier ID_CONNECTION_ACCEPTED. If the connection is not * successful, such as rejected connection or no response then neither of these things will happen. * Requires that you first call Initialize * * @param host Either a dotted IP address or a domain name * @param remotePort Which port to connect to on the remote machine. * @param passwordData A data block that must match the data block on the server. This can be just a password, or can be a stream of data * @param passwordDataLength The length in bytes of passwordData * * @return True on successful initiation. False on incorrect parameters, internal error, or too many existing peers */ virtual bool Connect( const char* host, unsigned short remotePort, char* passwordData, int passwordDataLength ) = 0; /** * Stops the network threads and close all connections. Multiple calls are ok. * * * @param blockDuration How long you should wait for all remaining packets to go out, per connected system * If you set it to 0 then the disconnection notification probably won't arrive */ virtual void Disconnect( unsigned int blockDuration ) = 0; /** * Returns true if the network threads are running */ virtual bool IsActive( void ) const = 0; /** * Fills the array remoteSystems with the playerID of all the systems we are connected to * * @param[out] remoteSystems An array of PlayerID structures to be filled with the PlayerIDs of the systems we are connected to * - pass 0 to remoteSystems to only get the number of systems we are connected to * @param numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array */ virtual bool GetConnectionList( PlayerID *remoteSystems, unsigned short *numberOfSystems ) const = 0; /** * Sends a block of data to the specified system that you are connected to. * This function only works while the client is connected (Use the Connect function). * * @param data The block of data to send * @param length The size in bytes of the data to send * @param priority What priority level to send on. * @param reliability How reliability to send this data * @param orderingChannel When using ordered or sequenced packets, what channel to order these on. * - Packets are only ordered relative to other packets on the same stream * @param playerId Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_PLAYER_ID to specify none * @param broadcast True to send this packet to all connected systems. If true, then playerId specifies who not to send the packet to. * @return * False if we are not connected to the specified recipient. True otherwise */ virtual bool Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) = 0; /** * Sends a block of data to the specified system that you are connected to. * This function only works while the client is connected (Use the Connect function). * * @param bitStream The bitstream to send * @param priority What priority level to send on. * @param reliability How reliability to send this data * @param orderingChannel When using ordered or sequenced packets, what channel to order these on. * - Packets are only ordered relative to other packets on the same stream * @param playerId Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_PLAYER_ID to specify none * @param broadcast True to send this packet to all connected systems. If true, then playerId specifies who not to send the packet to. * @return * False if we are not connected to the specified recipient. True otherwise */ virtual bool Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) = 0; /** * Gets a packet from the incoming packet queue. Use DeallocatePacket to deallocate the packet after you are done with it. * Check the Packet struct at the top of CoreNetworkStructures.h for the format of the struct * * @return * 0 if no packets are waiting to be handled, otherwise an allocated packet * If the client is not active this will also return 0, as all waiting packets are flushed when the client is Disconnected * This also updates all memory blocks associated with synchronized memory and distributed objects */ virtual Packet* Receive( void ) = 0; /** * Call this to deallocate a packet returned by Receive when you are done handling it. * @param packet A packet to free */ virtual void DeallocatePacket( Packet *packet ) = 0; /** * Return the total number of connections we are allowed */ virtual unsigned short GetMaximumNumberOfPeers( void ) const = 0; // -------------------------------------------------------------------------------------------- // Player Management Functions // -------------------------------------------------------------------------------------------- /** * Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). * * @param target Which connection to immediately close * @param sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. */ virtual void CloseConnection( PlayerID target, bool sendDisconnectionNotification ) = 0; /** * Given a playerID, returns an index from 0 to the maximum number of players allowed - 1. * * @param playerId The playerID to search for * * @return An integer from 0 to the maximum number of peers -1, or -1 if that player is not found */ virtual int GetIndexFromPlayerID( PlayerID playerId ) = 0; /** * This function is only useful for looping through all players. * * @param index an integer between 0 and the maximum number of players allowed - 1. * * @return A valid playerID or UNASSIGNED_PLAYER_ID if no such player at that index */ virtual PlayerID GetPlayerIDFromIndex( int index ) = 0; /** * Bans an IP from connecting. Banned IPs persist between connections. * * @param IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 * @param milliseconds - how many ms for a temporary ban. Use 0 for a permanent ban */ virtual void AddToBanList( const char *IP, unsigned int milliseconds=0 ) = 0; /** * Bans an IP from connecting. Banned IPs persist between connections. * * @param IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban * All IP addresses starting with 128.0.0 */ virtual void RemoveFromBanList( const char *IP ) = 0; /** * Allows all previously banned IPs to connect. */ virtual void ClearBanList( void ) = 0; /** * Determines if a particular IP is banned. * * @param IP Complete dotted IP address * * @return * - True if IP matches any IPs in the ban list, accounting for any wildcards. * - False otherwise. */ virtual bool IsBanned( const char *IP ) = 0; // -------------------------------------------------------------------------------------------- // Pinging Functions - Functions dealing with the automatic ping mechanism // -------------------------------------------------------------------------------------------- /** * Send a ping to the specified connected system. * * @param target who to ping */ virtual void Ping( PlayerID target ) = 0; /** * Send a ping to the specified unconnected system. * The remote system, if it is Initialized, will respond with ID_PONG. * The final ping time will be encoded in the following 4 bytes (2-5) as an unsigned int * * Requires: * The sender and recipient must already be started via a successful call to Initialize * * * @param host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. * @param remotePort Which port to connect to on the remote machine. * @param onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections */ virtual void Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections ) = 0; /** * Gets the average of all ping times read for a specified target * * @param target whose time to read * @return The average of all ping times read for a specified target. */ virtual int GetAveragePing( PlayerID playerId ) = 0; /** * Gets the last ping time read for the specific player or -1 if none read yet * * @param target whose time to read * @return Just the last ping */ virtual int GetLastPing( PlayerID playerId ) const = 0; /** * Gets the lowest ping time read or -1 if none read yet * * @param target whose time to read * @return the lowest ping time */ virtual int GetLowestPing( PlayerID playerId ) const = 0; /** * Ping the remote systems every so often. This is off by default * This will work anytime * * @param doPing True to start occasional pings. False to stop them. */ virtual void SetOccasionalPing( bool doPing ) = 0; // -------------------------------------------------------------------------------------------- // Static Data Functions - Functions dealing with API defined synchronized memory // -------------------------------------------------------------------------------------------- /** * All systems have a block of data associated with them, for user use. This block of data can be used to easily * specify typical system data that you want to know on connection, such as the player's name. * * @param playerId Which system you are referring to. Pass the value returned by GetInternalID to refer to yourself * * @return The data passed to SetRemoteStaticData stored as a bitstream */ virtual RakNet::BitStream * GetRemoteStaticData( PlayerID playerId ) = 0; /** * All systems have a block of data associated with them, for user use. This block of data can be used to easily * specify typical system data that you want to know on connection, such as the player's name. * * @param playerId Whose static data to change. Use your own playerId to change your own static data * @param data a block of data to store * @param length The length of data in bytes */ virtual void SetRemoteStaticData( PlayerID playerId, const char *data, const long length ) = 0; /** * Sends your static data to the specified system. This is automatically done on connection. * You should call this when you change your static data. * To send the static data of another system (such as relaying their data) you should do this normally with Send * * @param target Who to send your static data to. Specify UNASSIGNED_PLAYER_ID to broadcast to all */ virtual void SendStaticData( PlayerID target ) = 0; /** * Sets the data to send with an (LAN server discovery) /(offline ping) response * Length should be under 400 bytes, as a security measure against flood attacks * See the Ping sample project for how this is used. * @param data a block of data to store, or 0 for none * @param length The length of data in bytes, or 0 for none */ virtual void SetOfflinePingResponse( const char *data, const unsigned int length ) = 0; // -------------------------------------------------------------------------------------------- // Network Functions - Functions dealing with the network in general // -------------------------------------------------------------------------------------------- /** * Return the unique address identifier that represents you on the the network and is based on your local IP / port * Note that unlike in previous versions, this is a struct and is not sequential */ virtual PlayerID GetInternalID( void ) const = 0; /** * Return the unique address identifier that represents you on the the network and is based on your external * IP / port (the IP / port the specified player uses to communicate with you) * @note that unlike in previous versions, this is a struct and is not sequential * * @param target Which remote system you are referring to for your external ID */ virtual PlayerID GetExternalID( PlayerID target ) const = 0; /** * Change the MTU size in order to improve performance when sending large packets * This can only be called when not connected. * A too high of value will cause packets not to arrive at worst and be fragmented at best. * A too low of value will split packets unnecessarily. * * Parameters: * @param size: Set according to the following table: * - 1500. The largest Ethernet packet size; it is also the default value. * This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. * - 1492. The size PPPoE prefers. * - 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) * - 1468. The size DHCP prefers. * - 1460. Usable by AOL if you don't have large email attachments, etc. * - 1430. The size VPN and PPTP prefer. * - 1400. Maximum size for AOL DSL. * - 576. Typical value to connect to dial-up ISPs. (Default) * * @return False on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE */ virtual bool SetMTUSize( int size ) = 0; /** * Returns the current MTU size * * @return The MTU sized specified in SetMTUSize */ virtual int GetMTUSize( void ) const = 0; /** * Returns the number of IP addresses we have */ virtual unsigned GetNumberOfAddresses( void ) = 0; /** * Returns the dotted IP address for the specified playerId * * @param playerId Any player ID other than UNASSIGNED_PLAYER_ID, even if that player is not currently connected */ virtual const char* PlayerIDToDottedIP( PlayerID playerId ) const = 0; /** * Converts a dotted IP to a playerId * * @param[in] host Either a dotted IP address or a domain name * @param[in] remotePort Which port to connect to on the remote machine. * @param[out] playerId The result of this operation */ virtual void IPToPlayerID( const char* host, unsigned short remotePort, PlayerID *playerId ) = 0; /** * Returns an IP address at index 0 to GetNumberOfAddresses-1 */ virtual const char* GetLocalIP( unsigned int index ) = 0; /** * Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary * when connection to servers with multiple IP addresses. * * @param allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections */ void AllowConnectionResponseIPMigration( bool allow ); /** * Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. * This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through * * Requires: * The sender and recipient must already be started via a successful call to Initialize * * @param host Either a dotted IP address or a domain name * @param remotePort Which port to connect to on the remote machine. * @param data Optional data to append to the packet. * @param dataLength length of data in bytes. Use 0 if no data. */ virtual void AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ) = 0; // -------------------------------------------------------------------------------------------- // Compression Functions - Functions related to the compression layer // -------------------------------------------------------------------------------------------- /** * Enables or disables our tracking of bytes input to and output from the network. * This is required to get a frequency table, which is used to generate a new compression layer. * You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only track * part of the values sent over the network. * This value persists between connect calls and defaults to false (no frequency tracking) * * @param doCompile - true to track bytes. Defaults to false */ virtual void SetCompileFrequencyTable( bool doCompile ) = 0; /** * Returns the frequency of outgoing bytes into outputFrequencyTable * The purpose is to save to file as either a master frequency table from a sample game session for passing to * GenerateCompressionLayer(false); * You should only call this when disconnected. * Requires that you first enable data frequency tracking by calling SetCompileFrequencyTable(true) * * @param[out] outputFrequencyTable The frequency of each corresponding byte * * @return False (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success) */ virtual bool GetOutgoingFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ) = 0; /** * Generates the compression layer from the input frequency table. * You should call this twice - once with inputLayer as true and once as false. * The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false. * Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true * Calling this function when there is an existing layer will overwrite the old layer * You should only call this when disconnected * * @param inputFrequencyTable The frequency table returned from GetSendFrequencyTable(...); * @param inputLayer Whether inputFrequencyTable represents incoming data from other systems (true) or outgoing data from this system (false) * * @return False on failure (we are connected). True otherwise */ virtual bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ) = 0; /** * Deletes the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory * You should only call this when disconnected * * @param inputLayer Specifies the corresponding compression layer generated by GenerateCompressionLayer. * * @return False on failure (we are connected). True otherwise */ virtual bool DeleteCompressionLayer( bool inputLayer ) = 0; /** * Get the compression ratio. A low compression ratio is good. Compression is for outgoing data * @return The compression ratio. */ virtual float GetCompressionRatio( void ) const = 0; /** * Get the decompression ratio. A high decompression ratio is good. Decompression is for incoming data * @return The decompression ratio. */ virtual float GetDecompressionRatio( void ) const = 0; /* * -------------------------------------------------------------------------------------------- * Message Handler Functions * -------------------------------------------------------------------------------------------- */ /** * Attatches a message handler interface to run code automatically on message receipt in the Receive call * * @param messageHandler Pointer to a message handler to attach */ virtual void AttachMessageHandler( MessageHandlerInterface *messageHandler )=0; /** * Detatches a message handler interface to run code automatically on message receipt * * @param messageHandler Pointer to a message handler to detatch */ virtual void DetachMessageHandler( MessageHandlerInterface *messageHandler )=0; // -------------------------------------------------------------------------------------------- // Micellaneous Functions // -------------------------------------------------------------------------------------------- /** * Retrieves the data you passed to the passwordData parameter in Connect * * @param[out] passwordData Should point to a block large enough to hold the password data you passed to Connect * @param passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written */ virtual void GetPasswordData( char *passwordData, int *passwordDataLength ) = 0; /** * Put a packet back at the end of the receive queue in case you don't want to deal with it immediately * * @param packet The packet you want to push back. */ virtual void PushBackPacket( Packet *packet ) = 0; // -------------------------------------------------------------------------------------------- // Statistical Functions - Functions dealing with API performance // -------------------------------------------------------------------------------------------- /** * Returns a structure containing a large set of network statistics for the specified system * You can map this data to a string using the C style StatisticsToString function * * @param playerId Which connected system to get statistics for * * @return 0 on can't find the specified system. A pointer to a set of data otherwise. */ virtual RakNetStatisticsStruct * const GetStatistics( PlayerID playerId ) = 0; }; #endif blobby-1.0rc3/src/raknet/rijndael.cpp0000644000175000017500000005336412042452367021132 0ustar danielknobedanielknobe/* rijndael-alg-fst.c v2.0 August '99 * Optimised ANSI C code * authors: v1.0: Antoon Bosselaers * v2.0: Vincent Rijmen */ // This code is public, take a look in the LICENSE File /* * taken from the 'aescrypt' project: www.sf.net/projects/aescrypt * See LICENSE-EST for the license applicable to this file */ /* 14.Dec.2005 Cirilo: Removed silly hex keys; keys are now effectively unsigned char. */ // KevinJ - TODO - What the hell is this? It causes DevCPP not to compile. I don't know what the compiler flag is for // DEVCPP so I'm taking it out entirely /* #ifdef __GNUC__ #define __UNUS __attribute__((unused)) #else */ #define __UNUS //#endif #include #include #include #include "rijndael.h" /* KevinJ - Added this to just generate a random initialization vector */ #include "Rand.h" #define SC ((BC - 4) >> 1) #include "rijndael-boxes.h" int ROUNDS; static word8 shifts[3][4][2] = { { {0, 0}, {1, 3}, {2, 2}, {3, 1} }, { {0, 0}, {1, 5}, {2, 4}, {3, 3} }, { {0, 0}, {1, 7}, {3, 5}, {4, 4} } }; word8 mul(word8 a, word8 b) { /* multiply two elements of GF(2^m) * needed for MixColumn and InvMixColumn */ if (a && b) return Alogtable[(Logtable[a] + Logtable[b])%255]; else return 0; } void KeyAddition(word8 a[4][4], word8 rk[4][4], word8 BC) { /* Exor corresponding text input and round key input bytes */ int i, j; for(i = 0; i < BC; i++) for(j = 0; j < 4; j++) a[i][j] ^= rk[i][j]; } void ShiftRow(word8 a[4][4], word8 d, word8 BC) { /* Row 0 remains unchanged * The other three rows are shifted a variable amount */ word8 tmp[4]; int i, j; for(i = 1; i < 4; i++) { for(j = 0; j < BC; j++) tmp[j] = a[(j + shifts[SC][i][d]) % BC][i]; for(j = 0; j < BC; j++) a[j][i] = tmp[j]; } } void Substitution(word8 a[4][4], word8 box[256], word8 BC) { /* Replace every byte of the input by the byte at that place * in the nonlinear S-box */ int i, j; for(i = 0; i < BC; i++) for(j = 0; j < 4; j++) a[i][j] = box[a[i][j]] ; } void MixColumn(word8 a[4][4], word8 BC) { /* Mix the four bytes of every column in a linear way */ word8 b[4][4]; int i, j; for(j = 0; j < BC; j++) for(i = 0; i < 4; i++) b[j][i] = mul(2,a[j][i]) ^ mul(3,a[j][(i + 1) % 4]) ^ a[j][(i + 2) % 4] ^ a[j][(i + 3) % 4]; for(i = 0; i < 4; i++) for(j = 0; j < BC; j++) a[j][i] = b[j][i]; } void InvMixColumn(word8 a[4][4], word8 BC) { /* Mix the four bytes of every column in a linear way * This is the opposite operation of Mixcolumn */ int j; for(j = 0; j < BC; j++) *((word32*)a[j]) = *((word32*)U1[a[j][0]]) ^ *((word32*)U2[a[j][1]]) ^ *((word32*)U3[a[j][2]]) ^ *((word32*)U4[a[j][3]]); } int rijndaelKeySched (word8 k[MAXKC][4], int keyBits __UNUS, word8 W[MAXROUNDS+1][4][4]) { /* Calculate the necessary round keys * The number of calculations depends on keyBits and blockBits */ int j, r, t, rconpointer = 0; word8 tk[MAXKC][4]; int KC = ROUNDS - 6; for(j = KC-1; j >= 0; j--) *((word32*)tk[j]) = *((word32*)k[j]); r = 0; t = 0; /* copy values into round key array */ for(j = 0; (j < KC) && (r < (ROUNDS+1)); ) { for (; (j < KC) && (t < 4); j++, t++) *((word32*)W[r][t]) = *((word32*)tk[j]); if (t == 4) { r++; t = 0; } } while (r < (ROUNDS+1)) { /* while not enough round key material calculated */ /* calculate new values */ tk[0][0] ^= S[tk[KC-1][1]]; tk[0][1] ^= S[tk[KC-1][2]]; tk[0][2] ^= S[tk[KC-1][3]]; tk[0][3] ^= S[tk[KC-1][0]]; tk[0][0] ^= rcon[rconpointer++]; if (KC != 8) for(j = 1; j < KC; j++) *((word32*)tk[j]) ^= *((word32*)tk[j-1]); else { for(j = 1; j < KC/2; j++) *((word32*)tk[j]) ^= *((word32*)tk[j-1]); tk[KC/2][0] ^= S[tk[KC/2 - 1][0]]; tk[KC/2][1] ^= S[tk[KC/2 - 1][1]]; tk[KC/2][2] ^= S[tk[KC/2 - 1][2]]; tk[KC/2][3] ^= S[tk[KC/2 - 1][3]]; for(j = KC/2 + 1; j < KC; j++) *((word32*)tk[j]) ^= *((word32*)tk[j-1]); } /* copy values into round key array */ for(j = 0; (j < KC) && (r < (ROUNDS+1)); ) { for (; (j < KC) && (t < 4); j++, t++) *((word32*)W[r][t]) = *((word32*)tk[j]); if (t == 4) { r++; t = 0; } } } return 0; } int rijndaelKeyEnctoDec (int keyBits __UNUS, word8 W[MAXROUNDS+1][4][4]) { int r; for (r = 1; r < ROUNDS; r++) { InvMixColumn(W[r], 4); } return 0; } int rijndaelEncrypt (word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4]) { /* Encryption of one block. */ int r; word8 temp[4][4]; *((word32*)temp[0]) = *((word32*)a) ^ *((word32*)rk[0][0]); *((word32*)temp[1]) = *((word32*)(a+4)) ^ *((word32*)rk[0][1]); *((word32*)temp[2]) = *((word32*)(a+8)) ^ *((word32*)rk[0][2]); *((word32*)temp[3]) = *((word32*)(a+12)) ^ *((word32*)rk[0][3]); *((word32*)b) = *((word32*)T1[temp[0][0]]) ^ *((word32*)T2[temp[1][1]]) ^ *((word32*)T3[temp[2][2]]) ^ *((word32*)T4[temp[3][3]]); *((word32*)(b+4)) = *((word32*)T1[temp[1][0]]) ^ *((word32*)T2[temp[2][1]]) ^ *((word32*)T3[temp[3][2]]) ^ *((word32*)T4[temp[0][3]]); *((word32*)(b+8)) = *((word32*)T1[temp[2][0]]) ^ *((word32*)T2[temp[3][1]]) ^ *((word32*)T3[temp[0][2]]) ^ *((word32*)T4[temp[1][3]]); *((word32*)(b+12)) = *((word32*)T1[temp[3][0]]) ^ *((word32*)T2[temp[0][1]]) ^ *((word32*)T3[temp[1][2]]) ^ *((word32*)T4[temp[2][3]]); for(r = 1; r < ROUNDS-1; r++) { *((word32*)temp[0]) = *((word32*)b) ^ *((word32*)rk[r][0]); *((word32*)temp[1]) = *((word32*)(b+4)) ^ *((word32*)rk[r][1]); *((word32*)temp[2]) = *((word32*)(b+8)) ^ *((word32*)rk[r][2]); *((word32*)temp[3]) = *((word32*)(b+12)) ^ *((word32*)rk[r][3]); *((word32*)b) = *((word32*)T1[temp[0][0]]) ^ *((word32*)T2[temp[1][1]]) ^ *((word32*)T3[temp[2][2]]) ^ *((word32*)T4[temp[3][3]]); *((word32*)(b+4)) = *((word32*)T1[temp[1][0]]) ^ *((word32*)T2[temp[2][1]]) ^ *((word32*)T3[temp[3][2]]) ^ *((word32*)T4[temp[0][3]]); *((word32*)(b+8)) = *((word32*)T1[temp[2][0]]) ^ *((word32*)T2[temp[3][1]]) ^ *((word32*)T3[temp[0][2]]) ^ *((word32*)T4[temp[1][3]]); *((word32*)(b+12)) = *((word32*)T1[temp[3][0]]) ^ *((word32*)T2[temp[0][1]]) ^ *((word32*)T3[temp[1][2]]) ^ *((word32*)T4[temp[2][3]]); } /* last round is special */ *((word32*)temp[0]) = *((word32*)b) ^ *((word32*)rk[ROUNDS-1][0]); *((word32*)temp[1]) = *((word32*)(b+4)) ^ *((word32*)rk[ROUNDS-1][1]); *((word32*)temp[2]) = *((word32*)(b+8)) ^ *((word32*)rk[ROUNDS-1][2]); *((word32*)temp[3]) = *((word32*)(b+12)) ^ *((word32*)rk[ROUNDS-1][3]); b[0] = T1[temp[0][0]][1]; b[1] = T1[temp[1][1]][1]; b[2] = T1[temp[2][2]][1]; b[3] = T1[temp[3][3]][1]; b[4] = T1[temp[1][0]][1]; b[5] = T1[temp[2][1]][1]; b[6] = T1[temp[3][2]][1]; b[7] = T1[temp[0][3]][1]; b[8] = T1[temp[2][0]][1]; b[9] = T1[temp[3][1]][1]; b[10] = T1[temp[0][2]][1]; b[11] = T1[temp[1][3]][1]; b[12] = T1[temp[3][0]][1]; b[13] = T1[temp[0][1]][1]; b[14] = T1[temp[1][2]][1]; b[15] = T1[temp[2][3]][1]; *((word32*)b) ^= *((word32*)rk[ROUNDS][0]); *((word32*)(b+4)) ^= *((word32*)rk[ROUNDS][1]); *((word32*)(b+8)) ^= *((word32*)rk[ROUNDS][2]); *((word32*)(b+12)) ^= *((word32*)rk[ROUNDS][3]); return 0; } int rijndaelEncryptRound (word8 a[4][4], word8 rk[MAXROUNDS+1][4][4], int rounds) /* Encrypt only a certain number of rounds. * Only used in the Intermediate Value Known Answer Test. */ { int r; word8 temp[4][4]; /* make number of rounds sane */ if (rounds > ROUNDS) rounds = ROUNDS; *((word32*)a[0]) = *((word32*)a[0]) ^ *((word32*)rk[0][0]); *((word32*)a[1]) = *((word32*)a[1]) ^ *((word32*)rk[0][1]); *((word32*)a[2]) = *((word32*)a[2]) ^ *((word32*)rk[0][2]); *((word32*)a[3]) = *((word32*)a[3]) ^ *((word32*)rk[0][3]); for(r = 1; (r <= rounds) && (r < ROUNDS); r++) { *((word32*)temp[0]) = *((word32*)T1[a[0][0]]) ^ *((word32*)T2[a[1][1]]) ^ *((word32*)T3[a[2][2]]) ^ *((word32*)T4[a[3][3]]); *((word32*)temp[1]) = *((word32*)T1[a[1][0]]) ^ *((word32*)T2[a[2][1]]) ^ *((word32*)T3[a[3][2]]) ^ *((word32*)T4[a[0][3]]); *((word32*)temp[2]) = *((word32*)T1[a[2][0]]) ^ *((word32*)T2[a[3][1]]) ^ *((word32*)T3[a[0][2]]) ^ *((word32*)T4[a[1][3]]); *((word32*)temp[3]) = *((word32*)T1[a[3][0]]) ^ *((word32*)T2[a[0][1]]) ^ *((word32*)T3[a[1][2]]) ^ *((word32*)T4[a[2][3]]); *((word32*)a[0]) = *((word32*)temp[0]) ^ *((word32*)rk[r][0]); *((word32*)a[1]) = *((word32*)temp[1]) ^ *((word32*)rk[r][1]); *((word32*)a[2]) = *((word32*)temp[2]) ^ *((word32*)rk[r][2]); *((word32*)a[3]) = *((word32*)temp[3]) ^ *((word32*)rk[r][3]); } if (rounds == ROUNDS) { /* last round is special */ temp[0][0] = T1[a[0][0]][1]; temp[0][1] = T1[a[1][1]][1]; temp[0][2] = T1[a[2][2]][1]; temp[0][3] = T1[a[3][3]][1]; temp[1][0] = T1[a[1][0]][1]; temp[1][1] = T1[a[2][1]][1]; temp[1][2] = T1[a[3][2]][1]; temp[1][3] = T1[a[0][3]][1]; temp[2][0] = T1[a[2][0]][1]; temp[2][1] = T1[a[3][1]][1]; temp[2][2] = T1[a[0][2]][1]; temp[2][3] = T1[a[1][3]][1]; temp[3][0] = T1[a[3][0]][1]; temp[3][1] = T1[a[0][1]][1]; temp[3][2] = T1[a[1][2]][1]; temp[3][3] = T1[a[2][3]][1]; *((word32*)a[0]) = *((word32*)temp[0]) ^ *((word32*)rk[ROUNDS][0]); *((word32*)a[1]) = *((word32*)temp[1]) ^ *((word32*)rk[ROUNDS][1]); *((word32*)a[2]) = *((word32*)temp[2]) ^ *((word32*)rk[ROUNDS][2]); *((word32*)a[3]) = *((word32*)temp[3]) ^ *((word32*)rk[ROUNDS][3]); } return 0; } int rijndaelDecrypt (word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4]) { int r; word8 temp[4][4]; *((word32*)temp[0]) = *((word32*)a) ^ *((word32*)rk[ROUNDS][0]); *((word32*)temp[1]) = *((word32*)(a+4)) ^ *((word32*)rk[ROUNDS][1]); *((word32*)temp[2]) = *((word32*)(a+8)) ^ *((word32*)rk[ROUNDS][2]); *((word32*)temp[3]) = *((word32*)(a+12)) ^ *((word32*)rk[ROUNDS][3]); *((word32*)b) = *((word32*)T5[temp[0][0]]) ^ *((word32*)T6[temp[3][1]]) ^ *((word32*)T7[temp[2][2]]) ^ *((word32*)T8[temp[1][3]]); *((word32*)(b+4)) = *((word32*)T5[temp[1][0]]) ^ *((word32*)T6[temp[0][1]]) ^ *((word32*)T7[temp[3][2]]) ^ *((word32*)T8[temp[2][3]]); *((word32*)(b+8)) = *((word32*)T5[temp[2][0]]) ^ *((word32*)T6[temp[1][1]]) ^ *((word32*)T7[temp[0][2]]) ^ *((word32*)T8[temp[3][3]]); *((word32*)(b+12)) = *((word32*)T5[temp[3][0]]) ^ *((word32*)T6[temp[2][1]]) ^ *((word32*)T7[temp[1][2]]) ^ *((word32*)T8[temp[0][3]]); for(r = ROUNDS-1; r > 1; r--) { *((word32*)temp[0]) = *((word32*)b) ^ *((word32*)rk[r][0]); *((word32*)temp[1]) = *((word32*)(b+4)) ^ *((word32*)rk[r][1]); *((word32*)temp[2]) = *((word32*)(b+8)) ^ *((word32*)rk[r][2]); *((word32*)temp[3]) = *((word32*)(b+12)) ^ *((word32*)rk[r][3]); *((word32*)b) = *((word32*)T5[temp[0][0]]) ^ *((word32*)T6[temp[3][1]]) ^ *((word32*)T7[temp[2][2]]) ^ *((word32*)T8[temp[1][3]]); *((word32*)(b+4)) = *((word32*)T5[temp[1][0]]) ^ *((word32*)T6[temp[0][1]]) ^ *((word32*)T7[temp[3][2]]) ^ *((word32*)T8[temp[2][3]]); *((word32*)(b+8)) = *((word32*)T5[temp[2][0]]) ^ *((word32*)T6[temp[1][1]]) ^ *((word32*)T7[temp[0][2]]) ^ *((word32*)T8[temp[3][3]]); *((word32*)(b+12)) = *((word32*)T5[temp[3][0]]) ^ *((word32*)T6[temp[2][1]]) ^ *((word32*)T7[temp[1][2]]) ^ *((word32*)T8[temp[0][3]]); } /* last round is special */ *((word32*)temp[0]) = *((word32*)b) ^ *((word32*)rk[1][0]); *((word32*)temp[1]) = *((word32*)(b+4)) ^ *((word32*)rk[1][1]); *((word32*)temp[2]) = *((word32*)(b+8)) ^ *((word32*)rk[1][2]); *((word32*)temp[3]) = *((word32*)(b+12)) ^ *((word32*)rk[1][3]); b[0] = S5[temp[0][0]]; b[1] = S5[temp[3][1]]; b[2] = S5[temp[2][2]]; b[3] = S5[temp[1][3]]; b[4] = S5[temp[1][0]]; b[5] = S5[temp[0][1]]; b[6] = S5[temp[3][2]]; b[7] = S5[temp[2][3]]; b[8] = S5[temp[2][0]]; b[9] = S5[temp[1][1]]; b[10] = S5[temp[0][2]]; b[11] = S5[temp[3][3]]; b[12] = S5[temp[3][0]]; b[13] = S5[temp[2][1]]; b[14] = S5[temp[1][2]]; b[15] = S5[temp[0][3]]; *((word32*)b) ^= *((word32*)rk[0][0]); *((word32*)(b+4)) ^= *((word32*)rk[0][1]); *((word32*)(b+8)) ^= *((word32*)rk[0][2]); *((word32*)(b+12)) ^= *((word32*)rk[0][3]); return 0; } int rijndaelDecryptRound (word8 a[4][4], word8 rk[MAXROUNDS+1][4][4], int rounds) /* Decrypt only a certain number of rounds. * Only used in the Intermediate Value Known Answer Test. * Operations rearranged such that the intermediate values * of decryption correspond with the intermediate values * of encryption. */ { int r; /* make number of rounds sane */ if (rounds > ROUNDS) rounds = ROUNDS; /* First the special round: * without InvMixColumn * with extra KeyAddition */ KeyAddition(a,rk[ROUNDS],4); Substitution(a,Si,4); ShiftRow(a,1,4); /* ROUNDS-1 ordinary rounds */ for(r = ROUNDS-1; r > rounds; r--) { KeyAddition(a,rk[r],4); InvMixColumn(a,4); Substitution(a,Si,4); ShiftRow(a,1,4); } if (rounds == 0) { /* End with the extra key addition */ KeyAddition(a,rk[0],4); } return 0; } /*** End Rijndael algorithm, Begin the AES Interface ***/ int makeKey(keyInstance *key, BYTE direction, int keyByteLen, char *keyMaterial) { word8 k[MAXKC][4]; int i, j, t; int keyLen = keyByteLen*8; if (key == NULL) { return BAD_KEY_INSTANCE; } if ((direction == DIR_ENCRYPT) || (direction == DIR_DECRYPT)) { key->direction = direction; } else { return BAD_KEY_DIR; } if ((keyLen == 128) || (keyLen == 192) || (keyLen == 256)) { key->keyLen = keyLen; } else { return BAD_KEY_MAT; } if ( keyMaterial ) { strncpy(key->keyMaterial, keyMaterial, keyByteLen); } else { return BAD_KEY_MAT; } ROUNDS = keyLen/32 + 6; /* initialize key schedule: */ for(i = 0; i < key->keyLen/8; i++) { k[i / 4][i % 4] = (word8) key->keyMaterial[i]; } rijndaelKeySched (k, key->keyLen, key->keySched); if (direction == DIR_DECRYPT) rijndaelKeyEnctoDec (key->keyLen, key->keySched); return TRUE; } int cipherInit(cipherInstance *cipher, BYTE mode, char *IV) { int i, j, t; if ((mode == MODE_ECB) || (mode == MODE_CBC) || (mode == MODE_CFB1)) { cipher->mode = mode; } else { return BAD_CIPHER_MODE; } if (IV != NULL) { for(i = 0; i < 16; i++) cipher->IV[i] = IV[i]; } else { /* KevinJ - Added this to just generate a random initialization vector */ for(i = 0; i < 16; i++) cipher->IV[i]=(BYTE)randomMT(); } return TRUE; } int blockEncrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputByteLen, BYTE *outBuffer) { int i, k, numBlocks; word8 block[16], iv[4][4]; int inputLen = inputByteLen*8; if (cipher == NULL || key == NULL || key->direction == DIR_DECRYPT) { return BAD_CIPHER_STATE; } numBlocks = inputLen/128; switch (cipher->mode) { case MODE_ECB: for (i = numBlocks; i > 0; i--) { rijndaelEncrypt (input, outBuffer, key->keySched); input += 16; outBuffer += 16; } break; case MODE_CBC: #if STRICT_ALIGN memcpy(block,cipher->IV,16); #else *((word32*)block) = *((word32*)(cipher->IV)); *((word32*)(block+4)) = *((word32*)(cipher->IV+4)); *((word32*)(block+8)) = *((word32*)(cipher->IV+8)); *((word32*)(block+12)) = *((word32*)(cipher->IV+12)); #endif for (i = numBlocks; i > 0; i--) { *((word32*)block) ^= *((word32*)(input)); *((word32*)(block+4)) ^= *((word32*)(input+4)); *((word32*)(block+8)) ^= *((word32*)(input+8)); *((word32*)(block+12)) ^= *((word32*)(input+12)); rijndaelEncrypt (block, outBuffer, key->keySched); input += 16; outBuffer += 16; } break; case MODE_CFB1: #if STRICT_ALIGN memcpy(iv,cipher->IV,16); #else *((word32*)iv[0]) = *((word32*)(cipher->IV)); *((word32*)iv[1]) = *((word32*)(cipher->IV+4)); *((word32*)iv[2]) = *((word32*)(cipher->IV+8)); *((word32*)iv[3]) = *((word32*)(cipher->IV+12)); #endif for (i = numBlocks; i > 0; i--) { for (k = 0; k < 128; k++) { *((word32*)block) = *((word32*)iv[0]); *((word32*)(block+4)) = *((word32*)iv[1]); *((word32*)(block+8)) = *((word32*)iv[2]); *((word32*)(block+12)) = *((word32*)iv[3]); rijndaelEncrypt (block, block, key->keySched); outBuffer[k/8] ^= (block[0] & 0x80) >> (k & 7); iv[0][0] = (iv[0][0] << 1) | (iv[0][1] >> 7); iv[0][1] = (iv[0][1] << 1) | (iv[0][2] >> 7); iv[0][2] = (iv[0][2] << 1) | (iv[0][3] >> 7); iv[0][3] = (iv[0][3] << 1) | (iv[1][0] >> 7); iv[1][0] = (iv[1][0] << 1) | (iv[1][1] >> 7); iv[1][1] = (iv[1][1] << 1) | (iv[1][2] >> 7); iv[1][2] = (iv[1][2] << 1) | (iv[1][3] >> 7); iv[1][3] = (iv[1][3] << 1) | (iv[2][0] >> 7); iv[2][0] = (iv[2][0] << 1) | (iv[2][1] >> 7); iv[2][1] = (iv[2][1] << 1) | (iv[2][2] >> 7); iv[2][2] = (iv[2][2] << 1) | (iv[2][3] >> 7); iv[2][3] = (iv[2][3] << 1) | (iv[3][0] >> 7); iv[3][0] = (iv[3][0] << 1) | (iv[3][1] >> 7); iv[3][1] = (iv[3][1] << 1) | (iv[3][2] >> 7); iv[3][2] = (iv[3][2] << 1) | (iv[3][3] >> 7); iv[3][3] = (iv[3][3] << 1) | (outBuffer[k/8] >> (7-(k&7))) & 1; } } break; default: return BAD_CIPHER_STATE; } return numBlocks*128; } int blockDecrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputByteLen, BYTE *outBuffer) { int i, k, numBlocks; word8 block[16], iv[4][4]; int inputLen = inputByteLen*8; if (cipher == NULL || key == NULL || cipher->mode != MODE_CFB1 && key->direction == DIR_ENCRYPT) { return BAD_CIPHER_STATE; } numBlocks = inputLen/128; switch (cipher->mode) { case MODE_ECB: for (i = numBlocks; i > 0; i--) { rijndaelDecrypt (input, outBuffer, key->keySched); input += 16; outBuffer += 16; } break; case MODE_CBC: /* first block */ rijndaelDecrypt (input, block, key->keySched); #if STRICT_ALIGN memcpy(outBuffer,cipher->IV,16); *((word32*)(outBuffer)) ^= *((word32*)block); *((word32*)(outBuffer+4)) ^= *((word32*)(block+4)); *((word32*)(outBuffer+8)) ^= *((word32*)(block+8)); *((word32*)(outBuffer+12)) ^= *((word32*)(block+12)); #else *((word32*)(outBuffer)) = *((word32*)block) ^ *((word32*)(cipher->IV)); *((word32*)(outBuffer+4)) = *((word32*)(block+4)) ^ *((word32*)(cipher->IV+4)); *((word32*)(outBuffer+8)) = *((word32*)(block+8)) ^ *((word32*)(cipher->IV+8)); *((word32*)(outBuffer+12)) = *((word32*)(block+12)) ^ *((word32*)(cipher->IV+12)); #endif /* next blocks */ for (i = numBlocks-1; i > 0; i--) { rijndaelDecrypt (input, block, key->keySched); *((word32*)(outBuffer+16)) = *((word32*)block) ^ *((word32*)(input-16)); *((word32*)(outBuffer+20)) = *((word32*)(block+4)) ^ *((word32*)(input-12)); *((word32*)(outBuffer+24)) = *((word32*)(block+8)) ^ *((word32*)(input-8)); *((word32*)(outBuffer+28)) = *((word32*)(block+12)) ^ *((word32*)(input-4)); input += 16; outBuffer += 16; } break; case MODE_CFB1: #if STRICT_ALIGN memcpy(iv,cipher->IV,16); #else *((word32*)iv[0]) = *((word32*)(cipher->IV)); *((word32*)iv[1]) = *((word32*)(cipher->IV+4)); *((word32*)iv[2]) = *((word32*)(cipher->IV+8)); *((word32*)iv[3]) = *((word32*)(cipher->IV+12)); #endif for (i = numBlocks; i > 0; i--) { for (k = 0; k < 128; k++) { *((word32*)block) = *((word32*)iv[0]); *((word32*)(block+4)) = *((word32*)iv[1]); *((word32*)(block+8)) = *((word32*)iv[2]); *((word32*)(block+12)) = *((word32*)iv[3]); rijndaelEncrypt (block, block, key->keySched); iv[0][0] = (iv[0][0] << 1) | (iv[0][1] >> 7); iv[0][1] = (iv[0][1] << 1) | (iv[0][2] >> 7); iv[0][2] = (iv[0][2] << 1) | (iv[0][3] >> 7); iv[0][3] = (iv[0][3] << 1) | (iv[1][0] >> 7); iv[1][0] = (iv[1][0] << 1) | (iv[1][1] >> 7); iv[1][1] = (iv[1][1] << 1) | (iv[1][2] >> 7); iv[1][2] = (iv[1][2] << 1) | (iv[1][3] >> 7); iv[1][3] = (iv[1][3] << 1) | (iv[2][0] >> 7); iv[2][0] = (iv[2][0] << 1) | (iv[2][1] >> 7); iv[2][1] = (iv[2][1] << 1) | (iv[2][2] >> 7); iv[2][2] = (iv[2][2] << 1) | (iv[2][3] >> 7); iv[2][3] = (iv[2][3] << 1) | (iv[3][0] >> 7); iv[3][0] = (iv[3][0] << 1) | (iv[3][1] >> 7); iv[3][1] = (iv[3][1] << 1) | (iv[3][2] >> 7); iv[3][2] = (iv[3][2] << 1) | (iv[3][3] >> 7); iv[3][3] = (iv[3][3] << 1) | (input[k/8] >> (7-(k&7))) & 1; outBuffer[k/8] ^= (block[0] & 0x80) >> (k & 7); } } break; default: return BAD_CIPHER_STATE; } return numBlocks*128; } /** * cipherUpdateRounds: * * Encrypts/Decrypts exactly one full block a specified number of rounds. * Only used in the Intermediate Value Known Answer Test. * * Returns: * TRUE - on success * BAD_CIPHER_STATE - cipher in bad state (e.g., not initialized) */ int cipherUpdateRounds(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen __UNUS, BYTE *outBuffer, int rounds) { int j; word8 block[4][4]; if (cipher == NULL || key == NULL) { return BAD_CIPHER_STATE; } for (j = 3; j >= 0; j--) { /* parse input stream into rectangular array */ *((word32*)block[j]) = *((word32*)(input+4*j)); } switch (key->direction) { case DIR_ENCRYPT: rijndaelEncryptRound (block, key->keySched, rounds); break; case DIR_DECRYPT: rijndaelDecryptRound (block, key->keySched, rounds); break; default: return BAD_KEY_DIR; } for (j = 3; j >= 0; j--) { /* parse rectangular array into output ciphertext bytes */ *((word32*)(outBuffer+4*j)) = *((word32*)block[j]); } return TRUE; } blobby-1.0rc3/src/raknet/AsynchronousFileIO.h0000644000175000017500000000671412042452367022527 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * * @brief This file is used on win32 ports to benefits the use of IO Completion ports. * * @bug Currently IO Completion ports does not work correctly. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef __USE_IO_COMPLETION_PORTS #ifndef __ASYNCHRONOUS_FILE_IO_H #define __ASYNCHRONOUS_FILE_IO_H #ifdef _WIN32 #ifdef __USE_IO_COMPLETION_PORTS #include #else #include #endif #include #endif #include "SimpleMutex.h" struct ExtendedOverlappedStruct; /** * This class provide Asynchronous IO mecanism */ class AsynchronousFileIO { public: /** * Default Constructor */ AsynchronousFileIO(); /** * Destructor */ ~AsynchronousFileIO(); /** * Associate a socket to a port * @param socket the socket used for communication * @param dwCompletionKey the completion port key */ bool AssociateSocketWithCompletionPort( SOCKET socket, DWORD dwCompletionKey ); #endif /** * Singleton pattern. * Retrieve the unique instance */ static inline AsynchronousFileIO* Instance() { return & I; } /** * Increase the number of user of the instance */ void IncreaseUserCount( void ); /** * Decrease the number of user of the instance */ void DecreaseUserCount( void ); /** * Stop using asynchronous IO */ void Shutdown( void ); /** * Get the number of user of the instance */ int GetUserCount( void ); /** * @todo Document this member. * * */ unsigned threadCount; /** * @todo Document this member. * * */ bool killThreads; private: HANDLE completionPort; SimpleMutex userCountMutex; SYSTEM_INFO systemInfo; int userCount; static AsynchronousFileIO I; }; unsigned __stdcall ThreadPoolFunc( LPVOID arguments ); void WriteAsynch( HANDLE handle, ExtendedOverlappedStruct *extended ); BOOL ReadAsynch( HANDLE handle, ExtendedOverlappedStruct *extended ); #endif blobby-1.0rc3/src/raknet/Rand.h0000644000175000017500000000433612042452367017666 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file Rand.h * @brief Random Number Generator * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAND_H #define __RAND_H /** * Initialise seed for Random Generator * @param seed The initial value of the pseudo random suite. */ extern void seedMT( unsigned int seed ); // Defined in cokus_c.c /** * @todo Document this function */ extern unsigned int reloadMT( void ); /** * Get next random value from the generator * @return an integer random value. */ extern unsigned int randomMT( void ); /** * Get next random value form the generator * @return a real random value. */ extern float frandomMT( void ); #endif blobby-1.0rc3/src/raknet/BitStream.cpp0000644000175000017500000010322612042452367021225 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * * @brief Implementation of BitStream class * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Uncomment this to check that read and writes match with the same * type and in the case of streams, size. Useful during debugging */ //#define TYPE_CHECKING #ifdef TYPE_CHECKING #ifndef _DEBUG #ifdef _WIN32 #pragma message("Warning: TYPE_CHECKING is defined in BitStream.cpp when not in _DEBUG mode" ) #endif #endif #endif // This should be on by default for speed. Turn it off if you actually need endian swapping #define __BITSTREAM_NATIVE_END #include "BitStream.h" #include #include #include #include #include #include /*if defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) || defined ( __MACOSX__ ) #include #endif*/ #ifdef __BITSTREAM_BIG_END // Set up the read/write routines to produce Big-End network streams. #define B16_1 0 #define B16_0 1 #define B32_3 0 #define B32_2 1 #define B32_1 2 #define B32_0 3 #define B64_7 0 #define B64_6 1 #define B64_5 2 #define B64_4 3 #define B64_3 4 #define B64_2 5 #define B64_1 6 #define B64_0 7 #else // Default to producing Little-End network streams. #define B16_1 1 #define B16_0 0 #define B32_3 3 #define B32_2 2 #define B32_1 1 #define B32_0 0 #define B64_7 7 #define B64_6 6 #define B64_5 5 #define B64_4 4 #define B64_3 3 #define B64_2 2 #define B64_1 1 #define B64_0 0 #endif using namespace RakNet; BitStream::BitStream() { numberOfBitsUsed = 0; numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; readOffset = 0; data = stackData; copyData = true; } BitStream::BitStream( int initialBytesToAllocate ) { numberOfBitsUsed = 0; readOffset = 0; if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) { data = ( unsigned char* ) stackData; numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; } else { data = ( unsigned char* ) malloc( initialBytesToAllocate ); numberOfBitsAllocated = initialBytesToAllocate << 3; } #ifdef _DEBUG assert( data ); #endif // memset(data, 0, initialBytesToAllocate); copyData = true; } BitStream::BitStream( char* _data, unsigned int lengthInBytes, bool _copyData ) { numberOfBitsUsed = lengthInBytes << 3; readOffset = 0; copyData = _copyData; numberOfBitsAllocated = lengthInBytes << 3; if ( copyData ) { if ( lengthInBytes > 0 ) { if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) { data = ( unsigned char* ) stackData; numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; } else { data = ( unsigned char* ) malloc( lengthInBytes ); } #ifdef _DEBUG assert( data ); #endif memcpy( data, _data, lengthInBytes ); } else data = 0; } else data = ( unsigned char* ) _data; } // Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation void BitStream::SetNumberOfBitsAllocated( const unsigned int lengthInBits ) { #ifdef _DEBUG assert( lengthInBits >= ( unsigned int ) numberOfBitsAllocated ); #endif numberOfBitsAllocated = lengthInBits; } BitStream::~BitStream() { if ( copyData && numberOfBitsAllocated > BITSTREAM_STACK_ALLOCATION_SIZE << 3) free( data ); // Use realloc and free so we are more efficient than delete and new for resizing } void BitStream::Reset( void ) { // Note: Do NOT reallocate memory because BitStream is used // in places to serialize/deserialize a buffer. Reallocation // is a dangerous operation (may result in leaks). if ( numberOfBitsUsed > 0 ) { // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); } // Don't free memory here for speed efficiency //free(data); // Use realloc and free so we are more efficient than delete and new for resizing numberOfBitsUsed = 0; //numberOfBitsAllocated=8; readOffset = 0; //data=(unsigned char*)malloc(1); // if (numberOfBitsAllocated>0) // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); } // Write the native types to the end of the buffer void BitStream::Write( const bool input ) { #ifdef TYPE_CHECKING unsigned char ID = 0; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif if ( input ) Write1(); else Write0(); } void BitStream::Write( const unsigned char input ) { #ifdef TYPE_CHECKING unsigned char ID = 1; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); } void BitStream::Write( const char input ) { #ifdef TYPE_CHECKING unsigned char ID = 2; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); } void BitStream::Write( const unsigned short input ) { #ifdef TYPE_CHECKING unsigned char ID = 3; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char uint16w[2]; uint16w[B16_1] = (input >> 8)&(0xff); uint16w[B16_0] = input&(0xff); WriteBits( uint16w, sizeof( input ) * 8, true ); #endif } void BitStream::Write( const short input ) { #ifdef TYPE_CHECKING unsigned char ID = 4; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char int16w[2]; int16w[B16_1] = (input >> 8)&(0xff); int16w[B16_0] = input&(0xff); WriteBits( int16w, sizeof( input ) * 8, true ); #endif } void BitStream::Write( const unsigned int input ) { #ifdef TYPE_CHECKING unsigned char ID = 5; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char uint32w[4]; uint32w[B32_3] = (input >> 24)&(0x000000ff); uint32w[B32_2] = (input >> 16)&(0x000000ff); uint32w[B32_1] = (input >> 8)&(0x000000ff); uint32w[B32_0] = (input)&(0x000000ff); WriteBits( uint32w, sizeof( input ) * 8, true ); #endif } void BitStream::Write( const int input ) { #ifdef TYPE_CHECKING unsigned char ID = 6; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char int32w[4]; int32w[B32_3] = (input >> 24)&(0x000000ff); int32w[B32_2] = (input >> 16)&(0x000000ff); int32w[B32_1] = (input >> 8)&(0x000000ff); int32w[B32_0] = (input)&(0x000000ff); WriteBits( int32w, sizeof( input ) * 8, true ); #endif } void BitStream::Write( const float input ) { #ifdef TYPE_CHECKING unsigned char ID = 9; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifndef __BITSTREAM_NATIVE_END unsigned int intval = *((unsigned int *)(&input)); Write(intval); #else WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #endif } void BitStream::Write( const double input ) { #ifdef TYPE_CHECKING unsigned char ID = 10; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif WriteBits( ( unsigned char* ) & input, sizeof( input ) * 8, true ); } // Write an array or casted stream void BitStream::Write( const char* input, const int numberOfBytes ) { #ifdef TYPE_CHECKING unsigned char ID = 11; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); WriteBits( ( unsigned char* ) & numberOfBytes, sizeof( int ) * 8, true ); #endif WriteBits( ( unsigned char* ) input, numberOfBytes * 8, true ); } // Write the native types with simple compression. // Best used with negatives and positives close to 0 void BitStream::WriteCompressed( const unsigned char input ) { #ifdef TYPE_CHECKING unsigned char ID = 12; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif WriteCompressed( ( unsigned char* ) & input, sizeof( input ) * 8, true ); } void BitStream::WriteCompressed( const char input ) { #ifdef TYPE_CHECKING unsigned char ID = 13; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif WriteCompressed( ( unsigned char* ) & input, sizeof( input ) * 8, false ); } void BitStream::WriteCompressed( const unsigned short input ) { #ifdef TYPE_CHECKING unsigned char ID = 14; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteCompressed( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char uint16wc[2]; uint16wc[B16_1] = (input >> 8)&(0xff); uint16wc[B16_0] = input&(0xff); WriteCompressed( uint16wc, sizeof( input ) * 8, true ); #endif } void BitStream::WriteCompressed( const short input ) { #ifdef TYPE_CHECKING unsigned char ID = 15; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteCompressed( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char int16wc[2]; int16wc[B16_1] = (input >> 8)&(0xff); int16wc[B16_0] = input&(0xff); WriteCompressed( int16wc, sizeof( input ) * 8, false ); #endif } void BitStream::WriteCompressed( const unsigned int input ) { #ifdef TYPE_CHECKING unsigned char ID = 16; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteCompressed( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char uint32wc[4]; uint32wc[B32_3] = (input >> 24)&(0x000000ff); uint32wc[B32_2] = (input >> 16)&(0x000000ff); uint32wc[B32_1] = (input >> 8)&(0x000000ff); uint32wc[B32_0] = (input)&(0x000000ff); WriteCompressed( uint32wc, sizeof( input ) * 8, true ); #endif } void BitStream::WriteCompressed( const int input ) { #ifdef TYPE_CHECKING unsigned char ID = 17; WriteBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8, true ); #endif #ifdef __BITSTREAM_NATIVE_END WriteCompressed( ( unsigned char* ) & input, sizeof( input ) * 8, true ); #else static unsigned char int32wc[4]; int32wc[B32_3] = (input >> 24)&(0x000000ff); int32wc[B32_2] = (input >> 16)&(0x000000ff); int32wc[B32_1] = (input >> 8)&(0x000000ff); int32wc[B32_0] = (input)&(0x000000ff); WriteCompressed( int32wc, sizeof( input ) * 8, false ); #endif } // Read the native types from the front of the buffer // Write the native types to the end of the buffer bool BitStream::Read( bool& output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; #ifdef _DEBUG assert( ID == 0 ); #endif #endif //assert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from if ( readOffset + 1 > numberOfBitsUsed ) return false; //if (ReadBit()) // Check that bit if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset % 8 ) ) ) // Is it faster to just write it out here? output = true; else output = false; readOffset++; return true; } bool BitStream::Read( unsigned char &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 1 ); #endif return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); } bool BitStream::Read( char &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 2 ); #endif return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); } bool BitStream::Read( unsigned short &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 3 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); #else static unsigned char uint16r[2]; if (ReadBits( uint16r, sizeof( output ) * 8 ) != true) return false; output = (((unsigned short) uint16r[B16_1])<<8)|((unsigned short)uint16r[B16_0]); return true; #endif } bool BitStream::Read( short &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 4 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); #else static unsigned char int16r[2]; if (ReadBits( int16r, sizeof( output ) * 8 ) != true) return false; output = (((unsigned short) int16r[B16_1])<<8)|((unsigned short)int16r[B16_0]); return true; #endif } bool BitStream::Read( unsigned int &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 5 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); #else static unsigned char uint32r[4]; if(ReadBits( uint32r, sizeof( output ) * 8 ) != true) return false; output = (((unsigned int) uint32r[B32_3])<<24)| (((unsigned int) uint32r[B32_2])<<16)| (((unsigned int) uint32r[B32_1])<<8)| ((unsigned int) uint32r[B32_0]); return true; #endif } bool BitStream::Read( int &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 6 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); #else static unsigned char int32r[4]; if(ReadBits( int32r, sizeof( output ) * 8 ) != true) return false; output = (((unsigned int) int32r[B32_3])<<24)| (((unsigned int) int32r[B32_2])<<16)| (((unsigned int) int32r[B32_1])<<8)| ((unsigned int) int32r[B32_0]); return true; #endif } bool BitStream::Read( float &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 9 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); #else unsigned int val; if (Read(val) == false) return false; output = *((float *)(&val)); return true; #endif } bool BitStream::Read( double &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 10 ); #endif return ReadBits( ( unsigned char* ) & output, sizeof( output ) * 8 ); } // Read an array or casted stream bool BitStream::Read( char* output, const int numberOfBytes ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 11 ); int NOB; ReadBits( ( unsigned char* ) & NOB, sizeof( int ) * 8 ); assert( NOB == numberOfBytes ); #endif return ReadBits( ( unsigned char* ) output, numberOfBytes * 8 ); } // Read the types you wrote with WriteCompressed bool BitStream::ReadCompressed( unsigned char & output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 12 ); #endif return ReadCompressed( ( unsigned char* ) & output, sizeof( output ) * 8, true ); } bool BitStream::ReadCompressed( char &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 13 ); #endif return ReadCompressed( ( unsigned char* ) & output, sizeof( output ) * 8, false ); } bool BitStream::ReadCompressed( unsigned short &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 14 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadCompressed( ( unsigned char* ) & output, sizeof( output ) * 8, true ); #else static unsigned char uint16rc[2]; if (ReadCompressed( uint16rc, sizeof( output ) * 8, true ) != true) return false; output = (((unsigned short) uint16rc[B16_1])<<8)| ((unsigned short)uint16rc[B16_0]); return true; #endif } bool BitStream::ReadCompressed( short &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 15 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadCompressed( ( unsigned char* ) & output, sizeof( output ) * 8, true ); #else static unsigned char int16rc[2]; if (ReadCompressed( int16rc, sizeof( output ) * 8, false ) != true) return false; output = (((unsigned short) int16rc[B16_1])<<8)|((unsigned short)int16rc[B16_0]); return true; #endif } bool BitStream::ReadCompressed( unsigned int &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 16 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadCompressed( ( unsigned char* ) & output, sizeof( output ) * 8, true ); #else static unsigned char uint32rc[4]; if(ReadCompressed( uint32rc, sizeof( output ) * 8, true ) != true) return false; output = (((unsigned int) uint32rc[B32_3])<<24)| (((unsigned int) uint32rc[B32_2])<<16)| (((unsigned int) uint32rc[B32_1])<<8)| ((unsigned int) uint32rc[B32_0]); return true; #endif } bool BitStream::ReadCompressed( int &output ) { #ifdef TYPE_CHECKING unsigned char ID; if ( ReadBits( ( unsigned char* ) & ID, sizeof(unsigned char) * 8 ) == false ) return false; assert( ID == 17 ); #endif #ifdef __BITSTREAM_NATIVE_END return ReadCompressed( ( unsigned char* ) & output, sizeof( output ) * 8, true ); #else static unsigned char int32rc[4]; if(ReadCompressed( int32rc, sizeof( output ) * 8, false ) != true) return false; output = (((unsigned int) int32rc[B32_3])<<24)| (((unsigned int) int32rc[B32_2])<<16)| (((unsigned int) int32rc[B32_1])<<8)| ((unsigned int) int32rc[B32_0]); return true; #endif } // Sets the read pointer back to the beginning of your data. void BitStream::ResetReadPointer( void ) { readOffset = 0; } // Sets the write pointer back to the beginning of your data. void BitStream::ResetWritePointer( void ) { numberOfBitsUsed = 0; } // Write a 0 void BitStream::Write0( void ) { AddBitsAndReallocate( 1 ); // New bytes need to be zeroed if ( ( numberOfBitsUsed % 8 ) == 0 ) data[ numberOfBitsUsed >> 3 ] = 0; numberOfBitsUsed++; // This ++ was in the line above - but boundschecker didn't like that for some reason. } // Write a 1 void BitStream::Write1( void ) { AddBitsAndReallocate( 1 ); int numberOfBitsMod8 = numberOfBitsUsed % 8; if ( numberOfBitsMod8 == 0 ) data[ numberOfBitsUsed >> 3 ] = 0x80; else data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1 numberOfBitsUsed++; // This ++ was in the line above - but boundschecker didn't like that for some reason. } // Returns true if the next data read is a 1, false if it is a 0 bool BitStream::ReadBit( void ) { #pragma warning( disable : 4800 ) readOffset++; return ( bool ) ( data[ readOffset-1 >> 3 ] & ( 0x80 >> ( readOffset-1 % 8 ) ) ); #pragma warning( default : 4800 ) } // Align the bitstream to the byte boundary and then write the specified number of bits. // This is faster than WriteBits but wastes the bits to do the alignment and requires you to call // SetReadToByteAlignment at the corresponding read position void BitStream::WriteAlignedBytes( const unsigned char* input, const int numberOfBytesToWrite ) { #ifdef _DEBUG assert( numberOfBytesToWrite > 0 ); #endif AlignWriteToByteBoundary(); // Allocate enough memory to hold everything AddBitsAndReallocate( numberOfBytesToWrite << 3 ); // Write the data memcpy( data + ( numberOfBitsUsed >> 3 ), input, numberOfBytesToWrite ); numberOfBitsUsed += numberOfBytesToWrite << 3; } // Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the // sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence // unless you byte align the coalesced packets. bool BitStream::ReadAlignedBytes( unsigned char* output, const int numberOfBytesToRead ) { #ifdef _DEBUG assert( numberOfBytesToRead > 0 ); #endif if ( numberOfBytesToRead <= 0 ) return false; // Byte align AlignReadToByteBoundary(); if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed ) return false; // Write the data memcpy( output, data + ( readOffset >> 3 ), numberOfBytesToRead ); readOffset += numberOfBytesToRead << 3; return true; } // Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons void BitStream::AlignWriteToByteBoundary( void ) { if ( numberOfBitsUsed ) numberOfBitsUsed += 8 - ( ( numberOfBitsUsed - 1 ) % 8 + 1 ); } // Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons void BitStream::AlignReadToByteBoundary( void ) { if ( readOffset ) readOffset += 8 - ( ( readOffset - 1 ) % 8 + 1 ); } // Write numberToWrite bits from the input source void BitStream::WriteBits( const unsigned char *input, int numberOfBitsToWrite, const bool rightAlignedBits ) { // if (numberOfBitsToWrite<=0) // return; AddBitsAndReallocate( numberOfBitsToWrite ); int offset = 0; unsigned char dataByte; int numberOfBitsUsedMod8; numberOfBitsUsedMod8 = numberOfBitsUsed % 8; // Faster to put the while at the top surprisingly enough while ( numberOfBitsToWrite > 0 ) //do { dataByte = *( input + offset ); if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation) dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation // Writing to a new byte each time if ( numberOfBitsUsedMod8 == 0 ) * ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte; else { // Copy over the new data. *( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half) { *( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary) } } if ( numberOfBitsToWrite >= 8 ) numberOfBitsUsed += 8; else numberOfBitsUsed += numberOfBitsToWrite; numberOfBitsToWrite -= 8; offset++; } // } while(numberOfBitsToWrite>0); } // Set the stream to some initial data. For internal use void BitStream::SetData( const unsigned char* input, const int numberOfBits ) { #ifdef _DEBUG assert( numberOfBitsUsed == 0 ); // Make sure the stream is clear #endif if ( numberOfBits <= 0 ) return ; AddBitsAndReallocate( numberOfBits ); memcpy( data, input, BITS_TO_BYTES( numberOfBits ) ); numberOfBitsUsed = numberOfBits; } // Assume the input source points to a native type, compress and write it void BitStream::WriteCompressed( const unsigned char* input, const int size, const bool unsignedData ) { int currentByte = ( size >> 3 ) - 1; // PCs unsigned char byteMatch; if ( unsignedData ) { byteMatch = 0; } else { byteMatch = 0xFF; } // Write upper bytes with a single 1 // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes while ( currentByte > 0 ) { if ( input[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted { bool b = true; Write( b ); } else { // Write the remainder of the data after writing 0 bool b = false; Write( b ); WriteBits( input, ( currentByte + 1 ) << 3, true ); // currentByte--; return ; } currentByte--; } // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites. if ( ( unsignedData && ( ( *( input + currentByte ) ) & 0xF0 ) == 0x00 ) || ( unsignedData == false && ( ( *( input + currentByte ) ) & 0xF0 ) == 0xF0 ) ) { bool b = true; Write( b ); WriteBits( input + currentByte, 4, true ); } else { bool b = false; Write( b ); WriteBits( input + currentByte, 8, true ); } } // Read numberOfBitsToRead bits to the output source // alignBitsToRight should be set to true to convert internal bitstream data to userdata // It should be false if you used WriteBits with rightAlignedBits false bool BitStream::ReadBits( unsigned char* output, int numberOfBitsToRead, const bool alignBitsToRight ) { #ifdef _DEBUG assert( numberOfBitsToRead > 0 ); #endif // if (numberOfBitsToRead<=0) // return false; if ( readOffset + numberOfBitsToRead > numberOfBitsUsed ) return false; int readOffsetMod8; int offset = 0; memset( output, 0, BITS_TO_BYTES( numberOfBitsToRead ) ); readOffsetMod8 = readOffset % 8; // do // Faster to put the while at the top surprisingly enough while ( numberOfBitsToRead > 0 ) { *( output + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half *( output + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary) numberOfBitsToRead -= 8; if ( numberOfBitsToRead < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right { if ( alignBitsToRight ) * ( output + offset ) >>= -numberOfBitsToRead; readOffset += 8 + numberOfBitsToRead; } else readOffset += 8; offset++; } //} while(numberOfBitsToRead>0); return true; } // Assume the input source points to a compressed native type. Decompress and read it bool BitStream::ReadCompressed( unsigned char* output, const int size, const bool unsignedData ) { int currentByte = ( size >> 3 ) - 1; unsigned char byteMatch, halfByteMatch; if ( unsignedData ) { byteMatch = 0; halfByteMatch = 0; } else { byteMatch = 0xFF; halfByteMatch = 0xF0; } // Upper bytes are specified with a single 1 if they match byteMatch // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes while ( currentByte > 0 ) { // If we read a 1 then the data is byteMatch. bool b; if ( Read( b ) == false ) return false; if ( b ) // Check that bit { output[ currentByte ] = byteMatch; currentByte--; } else { // Read the rest of the bytes if ( ReadBits( output, ( currentByte + 1 ) << 3 ) == false ) return false; return true; } } // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits. // Otherwise we read a 0 and the 8 bytes //assert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from if ( readOffset + 1 > numberOfBitsUsed ) return false; bool b; if ( Read( b ) == false ) return false; if ( b ) // Check that bit { if ( ReadBits( output + currentByte, 4 ) == false ) return false; output[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits } else { if ( ReadBits( output + currentByte, 8 ) == false ) return false; } return true; } // Reallocates (if necessary) in preparation of writing numberOfBitsToWrite void BitStream::AddBitsAndReallocate( const int numberOfBitsToWrite ) { if ( numberOfBitsToWrite <= 0 ) return; int newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes { #ifdef _DEBUG // If this assert hits then we need to specify true for the third parameter in the constructor // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with assert( copyData == true ); #endif // Less memory efficient but saves on news and deletes newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2; // int newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); // Use realloc and free so we are more efficient than delete and new for resizing int amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated ); if (data==(unsigned char*)stackData) { if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) { data = ( unsigned char* ) malloc( amountToAllocate ); // need to copy the stack data over to our new memory area too memcpy ((void *)data, (void *)stackData, BITS_TO_BYTES( numberOfBitsAllocated )); } } else { data = ( unsigned char* ) realloc( data, amountToAllocate ); } #ifdef _DEBUG assert( data ); // Make sure realloc succeeded #endif // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 } if ( newNumberOfBitsAllocated > numberOfBitsAllocated ) numberOfBitsAllocated = newNumberOfBitsAllocated; } // Should hit if reads didn't match writes void BitStream::AssertStreamEmpty( void ) { assert( readOffset == numberOfBitsUsed ); } void BitStream::PrintBits( void ) const { if ( numberOfBitsUsed <= 0 ) { printf( "No bits\n" ); return ; } for ( int counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ); counter++ ) { int stop; if ( counter == ( numberOfBitsUsed - 1 ) >> 3 ) stop = 8 - ( ( ( numberOfBitsUsed - 1 ) % 8 ) + 1 ); else stop = 0; for ( int counter2 = 7; counter2 >= stop; counter2-- ) { if ( ( data[ counter ] >> counter2 ) & 1 ) putchar( '1' ); else putchar( '0' ); } putchar( ' ' ); } putchar( '\n' ); } // Exposes the data for you to look at, like PrintBits does. // Data will point to the stream. Returns the length in bits of the stream. int BitStream::CopyData( unsigned char** _data ) const { #ifdef _DEBUG assert( numberOfBitsUsed > 0 ); #endif *_data = new unsigned char [ BITS_TO_BYTES( numberOfBitsUsed ) ]; memcpy( *_data, data, sizeof(unsigned char) * ( BITS_TO_BYTES( numberOfBitsUsed ) ) ); return numberOfBitsUsed; } // Ignore data we don't intend to read void BitStream::IgnoreBits( const int numberOfBits ) { readOffset += numberOfBits; } void BitStream::IgnoreBytes( const int numberOfBytes ) { IgnoreBits( 8 * numberOfBytes ); } // Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! void BitStream::SetWriteOffset( const int offset ) { numberOfBitsUsed = offset; } // Returns the length in bits of the stream int BitStream::GetNumberOfBitsUsed( void ) const { return numberOfBitsUsed; } // Returns the length in bytes of the stream int BitStream::GetNumberOfBytesUsed( void ) const { return BITS_TO_BYTES( numberOfBitsUsed ); } // Returns the number of bits into the stream that we have read int BitStream::GetReadOffset( void ) const { return readOffset; } // Returns the number of bits left in the stream that haven't been read int BitStream::GetNumberOfUnreadBits( void ) const { return numberOfBitsUsed - readOffset; } // Exposes the internal data unsigned char* BitStream::GetData( void ) const { return data; } // If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied. void BitStream::AssertCopyData( void ) { if ( copyData == false ) { copyData = true; if ( numberOfBitsAllocated > 0 ) { unsigned char * newdata = ( unsigned char* ) malloc( BITS_TO_BYTES( numberOfBitsAllocated ) ); #ifdef _DEBUG assert( data ); #endif memcpy( newdata, data, BITS_TO_BYTES( numberOfBitsAllocated ) ); data = newdata; } else data = 0; } } blobby-1.0rc3/src/raknet/CMakeLists.txt0000644000175000017500000000227612042452367021372 0ustar danielknobedanielknobeset (raknet_SRC ArrayList.h AsynchronousFileIO.cpp AsynchronousFileIO.h BigTypes.h BinarySearchTree.h BitStream.cpp BitStream.h CheckSum.cpp CheckSum.h ClientContextStruct.h DataBlockEncryptor.cpp DataBlockEncryptor.h ExtendedOverlappedPool.cpp ExtendedOverlappedPool.h GetTime.cpp GetTime.h HuffmanEncodingTree.cpp HuffmanEncodingTree.h HuffmanEncodingTreeFactory.cpp HuffmanEncodingTreeFactory.h HuffmanEncodingTreeNode.h InternalPacket.h InternalPacketPool.cpp InternalPacketPool.h LinkedList.h MessageHandlerInterface.cpp MessageHandlerInterface.h MTUSize.h NetworkTypes.cpp NetworkTypes.h OrderedList.h PacketEnumerations.h PacketPool.cpp PacketPool.h PacketPriority.h QueueLinkedList.h RakClient.cpp RakClient.h RakClientInterface.h RakNetStatistics.cpp RakNetStatistics.h RakNetworkFactory.cpp RakNetworkFactory.h RakPeer.cpp RakPeer.h RakPeerInterface.h RakServer.cpp RakServer.h RakServerInterface.h Rand.cpp Rand.h ReliabilityLayer.cpp ReliabilityLayer.h rijndael-boxes.h rijndael.cpp rijndael.h RSACrypt.h SHA1.cpp SHA1.h SimpleMutex.cpp SimpleMutex.h SingleProducerConsumer.h SocketLayer.cpp SocketLayer.h Types.h ) add_library(raknet STATIC ${raknet_SRC}) blobby-1.0rc3/src/raknet/ClientContextStruct.h0000644000175000017500000000453712042452367022775 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Client Context Internal Structure * @note Windows Port Only * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __CLIENT_CONTEXT_STRUCT_H #define __CLIENT_CONTEXT_STRUCT_H #ifdef _WIN32 #include #endif #include "NetworkTypes.h" #include "MTUSize.h" class RakPeer; #ifdef __USE_IO_COMPLETION_PORTS struct ClientContextStruct { HANDLE handle; // The socket, also used as a file handle }; struct ExtendedOverlappedStruct { OVERLAPPED overlapped; char data[ MAXIMUM_MTU_SIZE ]; // Used to hold data to send int length; // Length of the actual data to send, always under MAXIMUM_MTU_SIZE unsigned int binaryAddress; unsigned short port; RakPeer *rakPeer; bool read; // Set to true for reads, false for writes }; #endif #endif blobby-1.0rc3/src/raknet/GetTime.cpp0000644000175000017500000000507512042452367020674 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file GetTime.cpp * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "GetTime.h" #ifdef _WIN32 #include #else #include #endif unsigned int RakNet::GetTime( void ) { #ifdef _WIN32 static LARGE_INTEGER yo; static LONGLONG counts; #else static timeval tp, initialTime; #endif static bool initialized = false; if ( initialized == false ) { #ifdef _WIN32 QueryPerformanceFrequency( &yo ); // The original code shifted right 10 bits //counts = yo.QuadPart >> 10; // It gives the wrong value since 2^10 is not 1000 counts = yo.QuadPart / 1000; #else gettimeofday( &initialTime, 0 ); #endif initialized = true; } #ifdef _WIN32 LARGE_INTEGER PerfVal; QueryPerformanceCounter( &PerfVal ); return ( unsigned int ) ( PerfVal.QuadPart / counts ); #else gettimeofday( &tp, 0 ); // Seconds to ms and microseconds to ms return ( tp.tv_sec - initialTime.tv_sec ) * 1000 + ( tp.tv_usec - initialTime.tv_usec ) / 1000; #endif } blobby-1.0rc3/src/raknet/BinarySearchTree.h0000644000175000017500000010714212042452367022173 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Binary Seach Tree * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Binary Search Tree ADT - By Kevin Jenkins (http://www.rakkar.org) * 9/04 Giblet - updated code to work with new compilers adhering more closely to ISO standard */ #ifndef __BINARY_SEARCH_TREE_H #define __BINARY_SEARCH_TREE_H #include "QueueLinkedList.h" namespace BasicDataStructures { /** * Initilize with the following structure * * BinarySearchTree * * OR * * AVLBalancedBinarySearchTree * * Use the AVL balanced tree if you want the tree to be balanced after every deletion and addition. This avoids the potential * worst case scenario where ordered input to a binary search tree gives linear search time results. It's not needed * if input will be evenly distributed, in which case the search time is O (log n). The search time for the AVL * balanced binary tree is O (log n) irregardless of input. * * Has the following member functions * unsigned int height() - Returns the height of the tree at the optional specified starting index. Default is the root * add(element) - adds an element to the BinarySearchTree * bool del(element) - deletes the node containing element if the element is in the tree as defined by a comparison with the == operator. Returns true on success, false if the element is not found * bool is_in(element) - returns true if element is in the tree as defined by a comparison with the == operator. Otherwise returns false * display_inorder(array) - Fills an array with an inorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. * display_preorder(array) - Fills an array with an preorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. * display_postorder(array) - Fills an array with an postorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. * display_breadth_first_search(array) - Fills an array with a breadth first search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. * clear - Destroys the tree. Same as calling the destructor * unsigned int height() - Returns the height of the tree * unsigned int size() - returns the size of the BinarySearchTree * get_pointer_to_node(element) - returns a pointer to the comparision element in the tree, allowing for direct modification when necessary with complex data types. * Be warned, it is possible to corrupt the tree if the element used for comparisons is modified. Returns NULL if the item is not found * * * EXAMPLE * @code * BinarySearchTree A; * A.add(10); * A.add(15); * A.add(5); * int* array = new int [A.size()]; * A.display_inorder(array); * array[0]; // returns 5 * array[1]; // returns 10 * array[2]; // returns 15 * @endcode * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases * * clear - empties the BinarySearchTree and returns storage * The assignment and copy constructors are defined * * @note The template type must have the copy constructor and * assignment operator defined and must work with >, <, and == All * elements in the tree MUST be distinct The assignment operator is * defined between BinarySearchTree and AVLBalancedBinarySearchTree * as long as they are of the same template type. However, passing a * BinarySearchTree to an AVLBalancedBinarySearchTree will lose its * structure unless it happened to be AVL balanced to begin with * Requires queue_linked_list.cpp for the breadth first search used * in the copy constructor, overloaded assignment operator, and * display_breadth_first_search. * * */ template class BinarySearchTree { public: /** * Describe a Binary Search Tree Node */ struct node { /** * The element stored in this node */ BinarySearchTreeType* item; /** * The left child node */ node* left; /** * The right child node */ node* right; }; /** * Default Constructor */ BinarySearchTree(); /** * Destructor */ virtual ~BinarySearchTree(); /** * Copy constructor * @param original_type The tree to duplicate */ BinarySearchTree( const BinarySearchTree& original_type ); /** * Assignment operator * @param original_copy The object to copy * @return a reference to the current object. */ BinarySearchTree& operator= ( const BinarySearchTree& original_copy ); /** * Retrieve the number of element of the tree. */ const unsigned int size( void ); /** * Clear the tree removind all values. */ void clear( void ); /** * Get the height of the tree or one of its subtree. * @param starting_node compute the height of the tree starting at node. * if @em starting_node is 0 then the height is computed from the root node. * @return the height of the tree. */ const unsigned int height( node* starting_node = 0 ); /** * Add @em input to the tree and return its node. * @param input the value to add to the tree. * @return node a pointer to the node of the tree corresponding to @em input. */ node* add ( const BinarySearchTreeType& input ) ; /** * Remove @em input from the tree and return a pointer to the node * previously containing @em input. * @param input The value to remove of the tree * @return A pointer to the node previously containing @em input. */ node* del( const BinarySearchTreeType& input ); /** * Test if the tree contains @em input. * @param input The element to search for. * @return true if the element is in the tree false otherwise. */ bool is_in( const BinarySearchTreeType& input ); /** * Store all element of the tree in @em return_array. The element * of the resulting arrayare in order. * @param return_array The resulting array of elements. */ void display_inorder( BinarySearchTreeType* return_array ); /** * Store all element of the tree in @em return_array. The element * of the resulting array follow a prefix order walk throught the nodes of the tree. * @param return_array The resulting array of elements. */ void display_preorder( BinarySearchTreeType* return_array ); /** * Store all element of the tree in @em return_array. The element * of the resulting array follow a postfix order walk throught the nodes of the tree. * @param return_array The resulting array of elements. */ void display_postorder( BinarySearchTreeType* return_array ); /** * Display the tree using a breadth first search. * Put the children of the current node into the queue. * Pop the queue, put its children into the queue, repeat until queue is empty. * @param return_array The resulting array of elements. */ void display_breadth_first_search( BinarySearchTreeType* return_array ); /** * Get a pointer to the node containing @em element. * @param element The element to find * @return a pointer to the element if found, 0 otherwise. */ BinarySearchTreeType*& get_pointer_to_node( const BinarySearchTreeType& element ); protected: /** * Root of the tree. */ node* root; enum Direction_Types { NOT_FOUND, LEFT, RIGHT, ROOT } direction; const unsigned int height_recursive( node* current ); /** * Store the number of node composing the tree. */ unsigned int BinarySearchTree_size; /** * Find recursively a node of the tree. * @param element The value to search for * @param[out] parent a pointer to the parent node * @return The node storing the @em element. */ node*& find( const BinarySearchTreeType& element, node** parent ); /** * Find the parent node of an element * @param element The element to search for. * @return The node parent of the node containing element */ node*& find_parent( const BinarySearchTreeType& element ); /** * Does the recursive operation of filling the array. Used by display_postorder. * @param current The node to start the postfix order walk throught of the tree. * @param return_array The resulting array storing elements. * @param index The index of the next element in the array. */ void display_postorder_recursive( node* current, BinarySearchTreeType* return_array, unsigned int& index ); /** * Reorganized the nodes of the tree following an insertion or a deletion of a node. * @param current the node to fix. */ void fix_tree( node* current ); }; /** * This class is similar to the BinarySearchTree one in its interface. However it provided Balanced Tree. The tree * storing values has a minimum height at all time. Searching a value in such a tree is more efficient than in standard * BinarySearchTree. . * * @note * for more information consult the base class documentation. * */ template class AVLBalancedBinarySearchTree : public BinarySearchTree { public: /** * Default constructor */ AVLBalancedBinarySearchTree() {} /** * Destructor */ virtual ~AVLBalancedBinarySearchTree(); /** * Add an element to the tree and balanced the tree. * @param input the new element */ void add ( const BinarySearchTreeType& input ) ; /** * Remove an element of the tree and balanced the tree. * @param input the element to remove */ void del( const BinarySearchTreeType& input ); /** * Assignement operator * @param original_copy The tree to copy * @return a reference to the current object. */ BinarySearchTree& operator= ( BinarySearchTree& original_copy ) { return BinarySearchTree::operator= ( original_copy ); } private: void balance_tree( typename BinarySearchTree::node* current, bool rotateOnce ); void rotate_right( typename BinarySearchTree::node *C ); void rotate_left( typename BinarySearchTree::node* C ); void double_rotate_right( typename BinarySearchTree::node *A ); void double_rotate_left( typename BinarySearchTree::node* A ); bool right_higher( typename BinarySearchTree::node* A ); bool left_higher( typename BinarySearchTree::node* A ); }; template void AVLBalancedBinarySearchTree::balance_tree( typename BinarySearchTree::node* current, bool rotateOnce ) { unsigned int left_height, right_height; while ( current ) { if ( current->left == 0 ) left_height = 0; else left_height = this->height( current->left ); if ( current->right == 0 ) right_height = 0; else right_height = this->height( current->right ); if ( right_height - left_height == 2 ) { if ( right_higher( current->right ) ) rotate_left( current->right ); else double_rotate_left( current ); if ( rotateOnce ) break; } else if ( right_height - left_height == -2 ) { if ( left_higher( current->left ) ) rotate_right( current->left ); else double_rotate_right( current ); if ( rotateOnce ) break; } if ( current == this->root ) break; current = this->find_parent( *( current->item ) ); } } template void AVLBalancedBinarySearchTree::add ( const BinarySearchTreeType& input ) { typename BinarySearchTree::node * current = BinarySearchTree::add ( input ) ; balance_tree( current, true ); } template void AVLBalancedBinarySearchTree::del( const BinarySearchTreeType& input ) { typename BinarySearchTree::node * current = BinarySearchTree::del( input ); balance_tree( current, false ); } template bool AVLBalancedBinarySearchTree::right_higher( typename BinarySearchTree::node *A ) { if ( A == 0 ) return false; return this->height( A->right ) > this->height( A->left ); } template bool AVLBalancedBinarySearchTree::left_higher( typename BinarySearchTree::node *A ) { if ( A == 0 ) return false; return this->height( A->left ) > this->height( A->right ); } template void AVLBalancedBinarySearchTree::rotate_right( typename BinarySearchTree::node *C ) { typename BinarySearchTree::node * A, *B, *D; /* RIGHT ROTATION A = parent(b) b= parent(c) c = node to rotate around A | // Either direction B / \ C / \ D TO A | // Either Direction C / \ B / \ D */ B = this->find_parent( *( C->item ) ); A = this->find_parent( *( B->item ) ); D = C->right; if ( A ) { // Direction was set by the last find_parent call if ( this->direction == this->LEFT ) A->left = C; else A->right = C; } else this->root = C; // If B has no parent parent then B must have been the root node B->left = D; C->right = B; } template void AVLBalancedBinarySearchTree::double_rotate_right( typename BinarySearchTree::node *A ) { // The left side of the left child must be higher for the tree to balance with a right rotation. If it isn't, rotate it left before the normal rotation so it is. rotate_left( A->left->right ); rotate_right( A->left ); } template void AVLBalancedBinarySearchTree::rotate_left( typename BinarySearchTree::node *C ) { typename BinarySearchTree::node * A, *B, *D; /* RIGHT ROTATION A = parent(b) b= parent(c) c = node to rotate around A | // Either direction B / \ C / \ D TO A | // Either Direction C / \ B / \ D */ B = this->find_parent( *( C->item ) ); A = this->find_parent( *( B->item ) ); D = C->left; if ( A ) { // Direction was set by the last find_parent call if ( this->direction == this->LEFT ) A->left = C; else A->right = C; } else this->root = C; // If B has no parent parent then B must have been the root node B->right = D; C->left = B; } template void AVLBalancedBinarySearchTree::double_rotate_left( typename BinarySearchTree::node *A ) { // The left side of the right child must be higher for the tree to balance with a left rotation. If it isn't, rotate it right before the normal rotation so it is. rotate_right( A->right->left ); rotate_left( A->right ); } template AVLBalancedBinarySearchTree::~AVLBalancedBinarySearchTree() { this->clear(); } template const unsigned int BinarySearchTree::size( void ) { return BinarySearchTree_size; } template const unsigned int BinarySearchTree::height( typename BinarySearchTree::node* starting_node ) { if ( BinarySearchTree_size == 0 || starting_node == 0 ) return 0; else return height_recursive( starting_node ); } // Recursively return the height of a binary tree template const unsigned int BinarySearchTree::height_recursive( typename BinarySearchTree::node* current ) { unsigned int left_height = 0, right_height = 0; if ( ( current->left == 0 ) && ( current->right == 0 ) ) return 1; // Leaf if ( current->left != 0 ) left_height = 1 + height_recursive( current->left ); if ( current->right != 0 ) right_height = 1 + height_recursive( current->right ); if ( left_height > right_height ) return left_height; else return right_height; } template BinarySearchTree::BinarySearchTree() { BinarySearchTree_size = 0; root = 0; } template BinarySearchTree::~BinarySearchTree() { this->clear(); } template BinarySearchTreeType*& BinarySearchTree::get_pointer_to_node( const BinarySearchTreeType& element ) { static typename BinarySearchTree::node * tempnode; static BinarySearchTreeType* dummyptr = 0; tempnode = find ( element, &tempnode ); if ( this->direction == this->NOT_FOUND ) return dummyptr; return tempnode->item; } template typename BinarySearchTree::node*& BinarySearchTree::find( const BinarySearchTreeType& element, typename BinarySearchTree::node** parent ) { static typename BinarySearchTree::node * current; current = this->root; *parent = 0; this->direction = this->ROOT; if ( BinarySearchTree_size == 0 ) { this->direction = this->NOT_FOUND; return current = 0; } // Check if the item is at the root if ( element == *( current->item ) ) { this->direction = this->ROOT; return current; } #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( true ) { // Move pointer if ( element < *( current->item ) ) { *parent = current; this->direction = this->LEFT; current = current->left; } else if ( element > *( current->item ) ) { *parent = current; this->direction = this->RIGHT; current = current->right; } if ( current == 0 ) break; // Check if new position holds the item if ( element == *( current->item ) ) { return current; } } this->direction = this->NOT_FOUND; return current = 0; } template typename BinarySearchTree::node*& BinarySearchTree::find_parent( const BinarySearchTreeType& element ) { static typename BinarySearchTree::node * parent; find ( element, &parent ); return parent; } // Performs a series of value swaps starting with current to fix the tree if needed template void BinarySearchTree::fix_tree( typename BinarySearchTree::node* current ) { BinarySearchTreeType temp; while ( 1 ) { if ( ( ( current->left ) != 0 ) && ( *( current->item ) < *( current->left->item ) ) ) { // Swap the current value with the one to the left temp = *( current->left->item ); *( current->left->item ) = *( current->item ); *( current->item ) = temp; current = current->left; } else if ( ( ( current->right ) != 0 ) && ( *( current->item ) > *( current->right->item ) ) ) { // Swap the current value with the one to the right temp = *( current->right->item ); *( current->right->item ) = *( current->item ); *( current->item ) = temp; current = current->right; } else break; // current points to the right place so quit } } template typename BinarySearchTree::node* BinarySearchTree::del( const BinarySearchTreeType& input ) { typename BinarySearchTree::node * node_to_delete, *current, *parent; if ( BinarySearchTree_size == 0 ) return 0; if ( BinarySearchTree_size == 1 ) { clear(); return 0; } node_to_delete = find( input, &parent ); if ( direction == NOT_FOUND ) return 0; // Couldn't find the element current = node_to_delete; // Replace the deleted node with the appropriate value if ( ( current->right ) == 0 && ( current->left ) == 0 ) // Leaf node, just remove it { if ( parent ) { if ( direction == LEFT ) parent->left = 0; else parent->right = 0; } delete node_to_delete->item; delete node_to_delete; BinarySearchTree_size--; return parent; } else if ( ( current->right ) != 0 && ( current->left ) == 0 ) // Node has only one child, delete it and cause the parent to point to that child { if ( parent ) { if ( direction == RIGHT ) parent->right = current->right; else parent->left = current->right; } else root = current->right; // Without a parent this must be the root node delete node_to_delete->item; delete node_to_delete; BinarySearchTree_size--; return parent; } else if ( ( current->right ) == 0 && ( current->left ) != 0 ) // Node has only one child, delete it and cause the parent to point to that child { if ( parent ) { if ( direction == RIGHT ) parent->right = current->left; else parent->left = current->left; } else root = current->left; // Without a parent this must be the root node delete node_to_delete->item; delete node_to_delete; BinarySearchTree_size--; return parent; } else // Go right, then as left as far as you can { parent = current; direction = RIGHT; current = current->right; // Must have a right branch because the if statements above indicated that it has 2 branches while ( current->left ) { direction = LEFT; parent = current; current = current->left; } // Replace the value held by the node to delete with the value pointed to by current; *( node_to_delete->item ) = *( current->item ); // Delete current. // If it is a leaf node just delete it if ( current->right == 0 ) { if ( direction == RIGHT ) parent->right = 0; else parent->left = 0; delete current->item; delete current; BinarySearchTree_size--; return parent; } else { // Skip this node and make its parent point to its right branch if ( direction == RIGHT ) parent->right = current->right; else parent->left = current->right; delete current->item; delete current; BinarySearchTree_size--; return parent; } } } template typename BinarySearchTree::node* BinarySearchTree::add ( const BinarySearchTreeType& input ) { typename BinarySearchTree::node * current, *parent; // Add the new element to the tree according to the following alogrithm: // 1. If the current node is empty add the new leaf // 2. If the element is less than the current node then go down the left branch // 3. If the element is greater than the current node then go down the right branch if ( BinarySearchTree_size == 0 ) { BinarySearchTree_size = 1; root = new typename BinarySearchTree::node; root->item = new BinarySearchTreeType; *( root->item ) = input; root->left = 0; root->right = 0; return root; } else { // start at the root current = parent = root; #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( true ) // This loop traverses the tree to find a spot for insertion { if ( input < *( current->item ) ) { if ( current->left == 0 ) { current->left = new typename BinarySearchTree::node; current->left->item = new BinarySearchTreeType; current = current->left; current->left = 0; current->right = 0; *( current->item ) = input; BinarySearchTree_size++; return current; } else { parent = current; current = current->left; } } else if ( input > *( current->item ) ) { if ( current->right == 0 ) { current->right = new typename BinarySearchTree::node; current->right->item = new BinarySearchTreeType; current = current->right; current->left = 0; current->right = 0; *( current->item ) = input; BinarySearchTree_size++; return current; } else { parent = current; current = current->right; } } else return 0; // ((input == current->item) == true) which is not allowed since the tree only takes discrete values. Do nothing } } } template bool BinarySearchTree::is_in( const BinarySearchTreeType& input ) { typename BinarySearchTree::node * parent; find( input, &parent ); if ( direction != NOT_FOUND ) return true; else return false; } template void BinarySearchTree::display_inorder( BinarySearchTreeType* return_array ) { typename BinarySearchTree::node * current, *parent; bool just_printed = false; unsigned int index = 0; current = root; if ( BinarySearchTree_size == 0 ) return ; // Do nothing for an empty tree else if ( BinarySearchTree_size == 1 ) { return_array[ 0 ] = *( root->item ); return ; } direction = ROOT; // Reset the direction while ( index != BinarySearchTree_size ) { // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) { // Go left if the following 2 conditions are true // I can go left // I did not just move up from a right child // I did not just move up from a left child current = current->left; direction = ROOT; // Reset the direction } else if ( ( direction != RIGHT ) && ( just_printed == false ) ) { // Otherwise, print the current node if the following 3 conditions are true: // I did not just move up from a right child // I did not print this ndoe last cycle return_array[ index++ ] = *( current->item ); just_printed = true; } else if ( ( current->right != 0 ) && ( direction != RIGHT ) ) { // Otherwise, go right if the following 2 conditions are true // I did not just move up from a right child // I can go right current = current->right; direction = ROOT; // Reset the direction just_printed = false; } else { // Otherwise I've done everything I can. Move up the tree one node parent = find_parent( *( current->item ) ); current = parent; just_printed = false; } } } template void BinarySearchTree::display_preorder( BinarySearchTreeType* return_array ) { typename BinarySearchTree::node * current, *parent; unsigned int index = 0; current = root; if ( BinarySearchTree_size == 0 ) return ; // Do nothing for an empty tree else if ( BinarySearchTree_size == 1 ) { return_array[ 0 ] = *( root->item ); return ; } direction = ROOT; // Reset the direction return_array[ index++ ] = *( current->item ); while ( index != BinarySearchTree_size ) { // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) { current = current->left; direction = ROOT; // Everytime you move a node print it return_array[ index++ ] = *( current->item ); } else if ( ( current->right != 0 ) && ( direction != RIGHT ) ) { current = current->right; direction = ROOT; // Everytime you move a node print it return_array[ index++ ] = *( current->item ); } else { // Otherwise I've done everything I can. Move up the tree one node parent = find_parent( *( current->item ) ); current = parent; } } } template inline void BinarySearchTree::display_postorder( BinarySearchTreeType* return_array ) { unsigned int index = 0; if ( BinarySearchTree_size == 0 ) return ; // Do nothing for an empty tree else if ( BinarySearchTree_size == 1 ) { return_array[ 0 ] = *( root->item ); return ; } display_postorder_recursive( root, return_array, index ); } // Recursively do a postorder traversal template void BinarySearchTree::display_postorder_recursive( typename BinarySearchTree::node* current, BinarySearchTreeType* return_array, unsigned int& index ) { if ( current->left != 0 ) display_postorder_recursive( current->left, return_array, index ); if ( current->right != 0 ) display_postorder_recursive( current->right, return_array, index ); return_array[ index++ ] = *( current->item ); } template void BinarySearchTree::display_breadth_first_search( BinarySearchTreeType* return_array ) { typename BinarySearchTree::node * current; unsigned int index = 0; // Display the tree using a breadth first search // Put the children of the current node into the queue // Pop the queue, put its children into the queue, repeat until queue is empty if ( BinarySearchTree_size == 0 ) return ; // Do nothing for an empty tree else if ( BinarySearchTree_size == 1 ) { return_array[ 0 ] = *( root->item ); return ; } else { BasicDataStructures::QueueLinkedList tree_queue; // Add the root of the tree I am copying from tree_queue.push( root ); do { current = tree_queue.pop(); return_array[ index++ ] = *( current->item ); // Add the child or children of the tree I am copying from to the queue if ( current->left != 0 ) tree_queue.push( current->left ); if ( current->right != 0 ) tree_queue.push( current->right ); } while ( tree_queue.size() > 0 ); } } template BinarySearchTree::BinarySearchTree( const BinarySearchTree& original_copy ) { typename BinarySearchTree::node * current; // Copy the tree using a breadth first search // Put the children of the current node into the queue // Pop the queue, put its children into the queue, repeat until queue is empty // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. BinarySearchTree_size = 0; root = 0; if ( original_copy.BinarySearchTree_size == 0 ) { BinarySearchTree_size = 0; } else { BasicDataStructures::QueueLinkedList tree_queue; // Add the root of the tree I am copying from tree_queue.push( original_copy.root ); do { current = tree_queue.pop(); add ( *( current->item ) ) ; // Add the child or children of the tree I am copying from to the queue if ( current->left != 0 ) tree_queue.push( current->left ); if ( current->right != 0 ) tree_queue.push( current->right ); } while ( tree_queue.size() > 0 ); } } template BinarySearchTree& BinarySearchTree::operator= ( const BinarySearchTree& original_copy ) { typename BinarySearchTree::node * current; if ( ( &original_copy ) == this ) return this; clear(); // Remove the current tree // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. BinarySearchTree_size = 0; root = 0; // Copy the tree using a breadth first search // Put the children of the current node into the queue // Pop the queue, put its children into the queue, repeat until queue is empty if ( original_copy.BinarySearchTree_size == 0 ) { BinarySearchTree_size = 0; } else { BasicDataStructures::QueueLinkedList tree_queue; // Add the root of the tree I am copying from tree_queue.push( original_copy.root ); do { current = tree_queue.pop(); add ( *( current->item ) ) ; // Add the child or children of the tree I am copying from to the queue if ( current->left != 0 ) tree_queue.push( current->left ); if ( current->right != 0 ) tree_queue.push( current->right ); } while ( tree_queue.size() > 0 ); } return this; } template inline void BinarySearchTree::clear ( void ) { typename BinarySearchTree::node * current, *parent; current = root; while ( BinarySearchTree_size > 0 ) { if ( BinarySearchTree_size == 1 ) { delete root->item; delete root; root = 0; BinarySearchTree_size = 0; } else { if ( current->left != 0 ) { current = current->left; } else if ( current->right != 0 ) { current = current->right; } else // leaf { // Not root node so must have a parent parent = find_parent( *( current->item ) ); if ( ( parent->left ) == current ) parent->left = 0; else parent->right = 0; delete current->item; delete current; current = parent; BinarySearchTree_size--; } } } } } // End namespace #endif blobby-1.0rc3/src/raknet/LICENSE0000644000175000017500000001166512042452367017641 0ustar danielknobedanielknobeRAKNET EMAIL: We are allowed to change every file to BSD-License: From - Tue Feb 16 17:22:10 2010 X-Account-Key: account2 X-UIDL: 743025276 X-Mozilla-Status: 0011 X-Mozilla-Status2: 00000000 X-Mozilla-Keys: Received: from [68.230.241.43] (helo=fed1rmmtao103.cox.net) by mx33.web.de with esmtp (WEB.DE 4.110 #314) id 1NhQB5-0007j2-00 for daniel-knobe@web.de; Tue, 16 Feb 2010 17:21:36 +0100 Received: from fed1rmimpo03.cox.net ([70.169.32.75]) by fed1rmmtao103.cox.net (InterMail vM.8.00.01.00 201-2244-105-20090324) with ESMTP id <20100216161827.WTHU19579.fed1rmmtao103.cox.net@fed1rmimpo03.cox.net> for ; Tue, 16 Feb 2010 11:18:27 -0500 Received: from [192.168.1.4] ([98.189.238.122]) by fed1rmimpo03.cox.net with bizsmtp id igJT1d0082f8TL204gJTL2; Tue, 16 Feb 2010 11:18:27 -0500 X-VR-Score: -210.00 X-Authority-Analysis: v=1.1 cv=3OYv9oSQMPl2mzQJct9Z0gr0/14v7p3aTRAVsPGESI0= c=1 sm=1 a=Iz04VOSJxDsDrARI3wmEZA==:17 a=MxczCRmKqDDVT_otjAYA:9 a=PMfHed8KGfxFHxPCxhMjB1GEX18A:4 a=Iz04VOSJxDsDrARI3wmEZA==:117 X-CM-Score: 0.00 Message-ID: <4B7AC558.60108@jenkinssoftware.com> Date: Tue, 16 Feb 2010 08:18:32 -0800 From: Kevin Jenkins User-Agent: Thunderbird 2.0.0.23 (Windows/20090812) MIME-Version: 1.0 To: Daniel Knobe Subject: Re: a second small question References: <4B7ABDAE.7010005@web.de> In-Reply-To: <4B7ABDAE.7010005@web.de> Content-Type: text/plain; charset=ISO-8859-15; format=flowed Content-Transfer-Encoding: 7bit Return-Path: rakkar@jenkinssoftware.com Just change everything to the BSD Daniel Knobe wrote: > Hello again, > we have one more problem. > The old raknet has the licensetext I send u before, but some > headerfiles have no licensetext: > ArrayList.h BigTypes.h BinarySearchTree.h CheckSum.cpp CheckSum.h > DataReplicator.cpp FullyConnectedMesh.cpp LinkedList.h > MessageHandlerInterface.cpp OrderedList.h Queue.h QueueLinkedList.h > RakNetQueue.h RPCMap.cpp RSACrypt.h Types.h > > Can we add the licensetext of the other files to it or can you tell me > the license of this files? > > Greets > Daniel Knobe > Blobby Volley 2 > RIJNDAEL EMAIL: From - Thu Mar 25 17:58:29 2010 X-Account-Key: account2 X-UIDL: 743025543 X-Mozilla-Status: 0011 X-Mozilla-Status2: 00000000 X-Mozilla-Keys: Received: from [134.58.240.44] (helo=cavuit02.kulnet.kuleuven.be) by mx36.web.de with esmtp (WEB.DE 4.110 #4) id 1NuoF1-0002hE-00 for daniel-knobe@web.de; Thu, 25 Mar 2010 15:40:59 +0100 Received: from smtps01.kuleuven.be (smtpshost01.kulnet.kuleuven.be [134.58.240.74]) by cavuit02.kulnet.kuleuven.be (Postfix) with ESMTP id C557E51C004 for ; Thu, 25 Mar 2010 15:40:38 +0100 (CET) Received: from hydrogen.esat.kuleuven.be (hydrogen.esat.kuleuven.be [134.58.56.153]) by smtps01.kuleuven.be (Postfix) with ESMTP id 7D9F031E702 for ; Thu, 25 Mar 2010 15:40:38 +0100 (CET) Received: from [10.33.137.107] (boguspomp.esat.kuleuven.be [10.33.137.107]) by hydrogen.esat.kuleuven.be (Postfix) with ESMTP id 8D91148002 for ; Thu, 25 Mar 2010 15:40:38 +0100 (CET) Message-ID: <4BAB75E6.2020408@esat.kuleuven.be> Date: Thu, 25 Mar 2010 15:40:38 +0100 X-Kuleuven: This mail passed the K.U.Leuven mailcluster From: Vincent Rijmen Organization: K.U. Leuven User-Agent: Thunderbird 2.0.0.22 (X11/20090625) MIME-Version: 1.0 To: Daniel Knobe Subject: Re: rijndael license problems References: <4BA93621.3060605@web.de> <4BA9C2D2.4060400@esat.kuleuven.be> <4BAA3650.30505@web.de> In-Reply-To: <4BAA3650.30505@web.de> Content-Type: text/plain; charset=ISO-8859-15; format=flowed Content-Transfer-Encoding: 7bit X-KULeuven-Information: Katholieke Universiteit Leuven X-KULeuven-Scanned: Found to be clean X-Spam-Status: not spam, SpamAssassin (not cached, score=-50, required 5, autolearn=disabled, KUL_SMTPS -50.00) X-KULeuven-Envelope-From: vincent.rijmen@esat.kuleuven.be Return-Path: vincent.rijmen@esat.kuleuven.be This code is public. There is no license. Vincent Daniel Knobe wrote: > I'm sorry, it was my mistake. > Here are the files (hope you use a free license). > > We need this version of the files (or a newer with 100% compatible API) > > Thanks for your support. > > Best regards, > Daniel Knobe > > On 24.03.2010 08:44, Vincent Rijmen wrote: >> >> >> Daniel Knobe wrote: >>> Hi, >>> we are using the 3 files below in our game blobby volley 2 >>> (blobby.sf.net) >> I did not see any files or file names. >>> >>> The problem is, that we can not find the lisensetext :(. We need it, >>> because we have problems to add our game to debian and ubuntu lucid. >>> Which version of lisence and version are you using? GPLv2? GPLv3, BSD? >> None. >>> >>> Thanks for your support :). >>> >>> Greetz >>> Daniel >>> >> > blobby-1.0rc3/src/raknet/SHA1.h0000644000175000017500000000702212042452367017471 0ustar danielknobedanielknobe /* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief SHA-1 computation class * * 100% free public domain implementation of the SHA-1 * algorithm by Dominik Reichl * * * === Test Vectors (from FIPS PUB 180-1) === * * "abc" * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D * * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 * * A million repetitions of "a" * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #ifndef ___SHA1_H___ #define ___SHA1_H___ #include // Needed for file access #include // Needed for memset and memcpy #include // Needed for strcat and strcpy #include "Types.h" /** * Size of the read buffer when computing SHA-1 sum on a file */ #define MAX_FILE_READ_BUFFER 8000 /** * Size of an SHA-1 key in byte */ #define SHA1_LENGTH 20 /** * Computes SHA-1 hashing class */ class CSHA1 { public: // Rotate x bits to the left // #define ROL32(value, bits) (((value)<<(bits))|((value)>>(32-(bits)))) #ifdef LITTLE_ENDIAN #define SHABLK0(i) (block->l[i] = (ROL32(block->l[i],24) & 0xFF00FF00) \ | (ROL32(block->l[i],8) & 0x00FF00FF)) #else #define SHABLK0(i) (block->l[i]) #endif #define SHABLK(i) (block->l[i&15] = ROL32(block->l[(i+13)&15] ^ block->l[(i+8)&15] \ ^ block->l[(i+2)&15] ^ block->l[i&15],1)) // SHA-1 rounds #define R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } #define R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } #define R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); } #define R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); } #define R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); } typedef union { unsigned char c[ 64 ]; unsigned int l[ 16 ]; } SHA1_WORKSPACE_BLOCK; /* * Two different formats for ReportHash(...) */ enum { REPORT_HEX = 0, /**< Report in hexa */ REPORT_DIGIT = 1 /**< Report in digit */}; /** * Default Constructor */ CSHA1(); /** * Destructor */ virtual ~CSHA1(); unsigned int m_state[ 5 ]; unsigned int m_count[ 2 ]; unsigned char m_buffer[ 64 ]; unsigned char m_digest[ 20 ]; /** * Reset the internal state of the SHA-1 computation * to compute a new SHA-1 hash key */ void Reset(); /** * Update the hash value from a byte buffer * @param data the byte buffer * @param len the number of byte available in the byte buffer */ void Update( unsigned char* data, unsigned int len ); /** * Compute the SHA-1 hash key of a file * @param szFileName The filename * @return true on success false otherwise */ bool HashFile( char *szFileName ); /** * Finalize hash and report */ void Final(); /** * Retrieve the hash key * @param szReport an array of byte containing the hash key * @param uReportType the format of the report might be REPORT_HEX or REPORT_DIGIT */ void ReportHash( char *szReport, unsigned char uReportType = REPORT_HEX ); /** * Retrieve the Hash in a previously allocated array * @param uDest the destination array */ void GetHash( unsigned char *uDest ); /** * Get a pointer to the hash key data. */ unsigned char * GetHash( void ) const; private: /** * Private SHA-1 transformation */ void Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ); /** * Moved here for thread and multiple instance safety */ unsigned char workspace[ 64 ]; }; #endif // ___SHA1_H___ blobby-1.0rc3/src/raknet/MessageHandlerInterface.cpp0000644000175000017500000000371012042452367024033 0ustar danielknobedanielknobe/** * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "MessageHandlerInterface.h" #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter bool MessageHandlerInterface::PropagateToGame(Packet *packet) const { return false; } #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter void MessageHandlerInterface::OnAttach(RakPeerInterface *peer) { } blobby-1.0rc3/src/raknet/InternalPacketPool.h0000644000175000017500000000535512042452367022542 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Internal Packet Pool Class Declaration. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __INTERNAL_PACKET_POOL #define __INTERNAL_PACKET_POOL #include #include "InternalPacket.h" /** * @brief Manage Internal Packet using pools. * * This class provide memory management for packets used internally in RakNet. * @see PacketPool * * @note Implement Singleton Pattern * */ class InternalPacketPool { public: /** * Constructor */ InternalPacketPool(); /** * Destructor */ ~InternalPacketPool(); /** * Retrieve a new InternalPacket instance. * @return a pointer to an InternalPacket structure. */ InternalPacket* GetPointer( void ); /** * Free am InternalPacket instance * @param p a pointer to the InternalPacket instance. */ void ReleasePointer( InternalPacket *p ); /** * Clear the pool */ void ClearPool( void ); private: /** * InternalPacket pool */ std::stack pool; /** * Multithread access management */ #ifdef _DEBUG /** * Used in debugging stage to monitor the number of internal packet released. */ int packetsReleased; #endif }; #endif blobby-1.0rc3/src/raknet/RakServer.cpp0000644000175000017500000003651312042452367021243 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief RakServer Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "RakServer.h" #include "PacketEnumerations.h" #include "GetTime.h" #include "Rand.h" // Defined in rand.cpp //extern void seedMT(unsigned int seed);//exported in Rand.h //extern unsigned int randomMT(void); //exported in Rand.h RakServer::RakServer() { nextSeedUpdate = 0; synchronizedRandomInteger = false; relayStaticClientData = false; broadcastPingsTime = 0; } RakServer::~RakServer() {} void RakServer::InitializeSecurity( const char *pubKeyE, const char *pubKeyN ) { RakPeer::InitializeSecurity( 0, 0, pubKeyE, pubKeyN ); } void RakServer::DisableSecurity( void ) { RakPeer::DisableSecurity(); } bool RakServer::Start( unsigned short AllowedPlayers, int threadSleepTimer, unsigned short port, const char *forceHostAddress ) { bool init; RakPeer::Disconnect( 30 ); init = RakPeer::Initialize( AllowedPlayers, port, threadSleepTimer, forceHostAddress ); RakPeer::SetMaximumIncomingConnections( AllowedPlayers ); // Random number seed long time = RakNet::GetTime(); seedMT( time ); seed = randomMT(); if ( seed % 2 == 0 ) // Even seed--; // make odd nextSeed = randomMT(); if ( nextSeed % 2 == 0 ) // Even nextSeed--; // make odd return init; } void RakServer::SetPassword( const char *_password ) { if ( _password ) { RakPeer::SetIncomingPassword( _password, ( int ) strlen( _password ) + 1 ); } else { RakPeer::SetIncomingPassword( 0, 0 ); } } bool RakServer::HasPassword( void ) { return GetIncomingPassword()->GetNumberOfBytesUsed() > 0; } void RakServer::Disconnect( unsigned int blockDuration ) { RakPeer::Disconnect( blockDuration ); } bool RakServer::Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) { return RakPeer::Send( data, length, priority, reliability, orderingChannel, playerId, broadcast ); } bool RakServer::Send( const RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, PlayerID playerId, bool broadcast ) { return RakPeer::Send( bitStream, priority, reliability, orderingChannel, playerId, broadcast ); } Packet* RakServer::Receive( void ) { Packet * packet = RakPeer::Receive(); // This is just a regular time based update. Nowhere else good to put it if ( RakPeer::IsActive() && occasionalPing ) { unsigned int time = RakNet::GetTime(); if ( time > broadcastPingsTime || ( packet && packet->data[ 0 ] == ID_RECEIVED_STATIC_DATA ) ) { if ( time > broadcastPingsTime ) broadcastPingsTime = time + 30000; // Broadcast pings every 30 seconds unsigned i, count; RemoteSystemStruct *remoteSystem; RakNet::BitStream bitStream( ( PlayerID_Size + sizeof( short ) ) * 32 + sizeof(unsigned char) ); unsigned char typeId = ID_BROADCAST_PINGS; bitStream.Write( typeId ); for ( i = 0, count = 0; count < 32 && i < remoteSystemListSize; i++ ) { remoteSystem = remoteSystemList + i; if ( remoteSystem->playerId != UNASSIGNED_PLAYER_ID ) { bitStream.Write( remoteSystem->playerId.binaryAddress ); bitStream.Write( remoteSystem->playerId.port ); bitStream.Write( remoteSystem->pingAndClockDifferential[ remoteSystem->pingAndClockDifferentialWriteIndex ].pingTime ); count++; } } if ( count > 0 ) // If we wrote anything { if ( packet && packet->data[ 0 ] == ID_NEW_INCOMING_CONNECTION ) // If this was a new connection Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, packet->playerId, false ); // Send to the new connection else Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, UNASSIGNED_PLAYER_ID, true ); // Send to everyone } } } // This is just a regular time based update. Nowhere else good to put it if ( RakPeer::IsActive() && synchronizedRandomInteger ) { unsigned int time = RakNet::GetTime(); if ( time > nextSeedUpdate || ( packet && packet->data[ 0 ] == ID_NEW_INCOMING_CONNECTION ) ) { if ( time > nextSeedUpdate ) nextSeedUpdate = time + 9000; // Seeds are updated every 9 seconds seed = nextSeed; nextSeed = randomMT(); if ( nextSeed % 2 == 0 ) // Even nextSeed--; // make odd /* SetRandomNumberSeedStruct s; s.ts = ID_TIMESTAMP; s.timeStamp = RakNet::GetTime(); s.typeId = ID_SET_RANDOM_NUMBER_SEED; s.seed = seed; s.nextSeed = nextSeed; RakNet::BitStream s_BitS( SetRandomNumberSeedStruct_Size ); s.Serialize( s_BitS ); */ RakNet::BitStream outBitStream(sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned int)); outBitStream.Write((unsigned char) ID_TIMESTAMP); outBitStream.Write((unsigned int) RakNet::GetTime()); outBitStream.Write((unsigned char) ID_SET_RANDOM_NUMBER_SEED); outBitStream.Write(seed); outBitStream.Write(nextSeed); if ( packet && packet->data[ 0 ] == ID_NEW_INCOMING_CONNECTION ) Send( &outBitStream, SYSTEM_PRIORITY, RELIABLE, 0, packet->playerId, false ); else Send( &outBitStream, SYSTEM_PRIORITY, RELIABLE, 0, UNASSIGNED_PLAYER_ID, true ); } } if ( packet ) { // Intercept specific client / server feature packets. This will do an extra send and still pass on the data to the user if ( packet->data[ 0 ] == ID_RECEIVED_STATIC_DATA ) { if ( relayStaticClientData ) { // Relay static data to the other systems but the sender RakNet::BitStream bitStream( packet->length + PlayerID_Size ); unsigned char typeId = ID_REMOTE_STATIC_DATA; bitStream.Write( typeId ); bitStream.Write( packet->playerId.binaryAddress ); bitStream.Write( packet->playerId.port ); bitStream.Write( packet->playerIndex ); bitStream.Write( ( char* ) packet->data + sizeof(unsigned char), packet->length - sizeof(unsigned char) ); Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, packet->playerId, true ); } } else if ( packet->data[ 0 ] == ID_DISCONNECTION_NOTIFICATION || packet->data[ 0 ] == ID_CONNECTION_LOST || packet->data[ 0 ] == ID_NEW_INCOMING_CONNECTION ) { // Relay the disconnection RakNet::BitStream bitStream( packet->length + PlayerID_Size ); unsigned char typeId; if ( packet->data[ 0 ] == ID_DISCONNECTION_NOTIFICATION ) typeId = ID_REMOTE_DISCONNECTION_NOTIFICATION; else if ( packet->data[ 0 ] == ID_CONNECTION_LOST ) typeId = ID_REMOTE_CONNECTION_LOST; else typeId = ID_REMOTE_NEW_INCOMING_CONNECTION; bitStream.Write( typeId ); bitStream.Write( packet->playerId.binaryAddress ); bitStream.Write( packet->playerId.port ); bitStream.Write( ( unsigned short& ) packet->playerIndex ); Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, packet->playerId, true ); if ( packet->data[ 0 ] == ID_NEW_INCOMING_CONNECTION ) { unsigned i; for ( i = 0; i < remoteSystemListSize; i++ ) { if ( remoteSystemList[ i ].playerId != UNASSIGNED_PLAYER_ID && packet->playerId != remoteSystemList[ i ].playerId ) { bitStream.Reset(); typeId = ID_REMOTE_EXISTING_CONNECTION; bitStream.Write( typeId ); bitStream.Write( remoteSystemList[ i ].playerId.binaryAddress ); bitStream.Write( remoteSystemList[ i ].playerId.port ); bitStream.Write( ( unsigned short ) i ); // One send to tell them of the connection Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, packet->playerId, false ); if ( relayStaticClientData ) { bitStream.Reset(); typeId = ID_REMOTE_STATIC_DATA; bitStream.Write( typeId ); bitStream.Write( remoteSystemList[ i ].playerId.binaryAddress ); bitStream.Write( remoteSystemList[ i ].playerId.port ); bitStream.Write( (unsigned short) i ); bitStream.Write( ( char* ) remoteSystemList[ i ].staticData.GetData(), remoteSystemList[ i ].staticData.GetNumberOfBytesUsed() ); // Another send to tell them of the static data Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, packet->playerId, false ); } } } } } } return packet; } void RakServer::Kick( PlayerID playerId ) { RakPeer::NotifyAndFlagForDisconnect(playerId, false); } void RakServer::DeallocatePacket( Packet *packet ) { RakPeer::DeallocatePacket( packet ); } void RakServer::SetAllowedPlayers( unsigned short AllowedPlayers ) { RakPeer::SetMaximumIncomingConnections( AllowedPlayers ); } unsigned short RakServer::GetAllowedPlayers( void ) const { return RakPeer::GetMaximumIncomingConnections(); } unsigned short RakServer::GetConnectedPlayers( void ) { unsigned short numberOfSystems; RakPeer::GetConnectionList( 0, &numberOfSystems ); return numberOfSystems; } void RakServer::GetPlayerIPFromID( PlayerID playerId, char returnValue[ 22 ], unsigned short *port ) { *port = playerId.port; strcpy( returnValue, RakPeer::PlayerIDToDottedIP( playerId ) ); } void RakServer::PingPlayer( PlayerID playerId ) { RakPeer::Ping( playerId ); } int RakServer::GetAveragePing( PlayerID playerId ) { return RakPeer::GetAveragePing( playerId ); } int RakServer::GetLastPing( PlayerID playerId ) { return RakPeer::GetLastPing( playerId ); } int RakServer::GetLowestPing( PlayerID playerId ) { return RakPeer::GetLowestPing( playerId ); } void RakServer::StartOccasionalPing( void ) { RakPeer::SetOccasionalPing( true ); } void RakServer::StopOccasionalPing( void ) { RakPeer::SetOccasionalPing( false ); } bool RakServer::IsActive( void ) const { return RakPeer::IsActive(); } unsigned int RakServer::GetSynchronizedRandomInteger( void ) const { return seed; } void RakServer::StartSynchronizedRandomInteger( void ) { synchronizedRandomInteger = true; } void RakServer::StopSynchronizedRandomInteger( void ) { synchronizedRandomInteger = false; } bool RakServer::GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ) { return RakPeer::GenerateCompressionLayer( inputFrequencyTable, inputLayer ); } bool RakServer::DeleteCompressionLayer( bool inputLayer ) { return RakPeer::DeleteCompressionLayer( inputLayer ); } void RakServer::SetTrackFrequencyTable( bool b ) { RakPeer::SetCompileFrequencyTable( b ); } bool RakServer::GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ) { return RakPeer::GetOutgoingFrequencyTable( outputFrequencyTable ); } float RakServer::GetCompressionRatio( void ) const { return RakPeer::GetCompressionRatio(); } float RakServer::GetDecompressionRatio( void ) const { return RakPeer::GetDecompressionRatio(); } void RakServer::AttachMessageHandler( MessageHandlerInterface *messageHandler ) { RakPeer::AttachMessageHandler(messageHandler); } void RakServer::DetachMessageHandler( MessageHandlerInterface *messageHandler ) { RakPeer::DetachMessageHandler(messageHandler); } RakNet::BitStream * RakServer::GetStaticServerData( void ) { return RakPeer::GetRemoteStaticData( myPlayerId ); } void RakServer::SetStaticServerData( const char *data, const long length ) { RakPeer::SetRemoteStaticData( myPlayerId, data, length ); } void RakServer::SetRelayStaticClientData( bool b ) { relayStaticClientData = b; } void RakServer::SendStaticServerDataToClient( PlayerID playerId ) { RakPeer::SendStaticData( playerId ); } void RakServer::SetOfflinePingResponse( const char *data, const unsigned int length ) { RakPeer::SetOfflinePingResponse( data, length ); } RakNet::BitStream * RakServer::GetStaticClientData( PlayerID playerId ) { return RakPeer::GetRemoteStaticData( playerId ); } void RakServer::SetStaticClientData( PlayerID playerId, const char *data, const long length ) { RakPeer::SetRemoteStaticData( playerId, data, length ); } // This will read the data from playerChangedId and send it to playerToSendToId void RakServer::ChangeStaticClientData( PlayerID playerChangedId, PlayerID playerToSendToId ) { RemoteSystemStruct * remoteSystem = GetRemoteSystemFromPlayerID( playerChangedId ); if ( remoteSystem == 0 ) return ; // No such playerChangedId // Relay static data to the other systems but the sender RakNet::BitStream bitStream( remoteSystem->staticData.GetNumberOfBytesUsed() + PlayerID_Size + sizeof(unsigned char) ); unsigned char typeId = ID_REMOTE_STATIC_DATA; bitStream.Write( typeId ); bitStream.Write( playerChangedId.binaryAddress ); bitStream.Write( playerChangedId.port ); bitStream.Write( ( char* ) remoteSystem->staticData.GetData(), remoteSystem->staticData.GetNumberOfBytesUsed() ); Send( &bitStream, SYSTEM_PRIORITY, RELIABLE, 0, playerToSendToId, true ); } unsigned int RakServer::GetNumberOfAddresses( void ) { return RakPeer::GetNumberOfAddresses(); } const char* RakServer::GetLocalIP( unsigned int index ) { return RakPeer::GetLocalIP( index ); } void RakServer::PushBackPacket( Packet *packet ) { RakPeer::PushBackPacket( packet ); } int RakServer::GetIndexFromPlayerID( PlayerID playerId ) { return RakPeer::GetIndexFromPlayerID( playerId ); } PlayerID RakServer::GetPlayerIDFromIndex( int index ) { return RakPeer::GetPlayerIDFromIndex( index ); } void RakServer::AddToBanList( const char *IP ) { RakPeer::AddToBanList( IP ); } void RakServer::RemoveFromBanList( const char *IP ) { RakPeer::RemoveFromBanList( IP ); } void RakServer::ClearBanList( void ) { RakPeer::ClearBanList(); } bool RakServer::IsBanned( const char *IP ) { return RakPeer::IsBanned( IP ); } bool RakServer::IsActivePlayerID( PlayerID playerId ) { return RakPeer::GetRemoteSystemFromPlayerID( playerId ) != 0; } bool RakServer::SetMTUSize( int size ) { return RakPeer::SetMTUSize( size ); } int RakServer::GetMTUSize( void ) const { return RakPeer::GetMTUSize(); } void RakServer::AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ) { RakPeer::AdvertiseSystem( host, remotePort, data, dataLength ); } RakNetStatisticsStruct * const RakServer::GetStatistics( PlayerID playerId ) { return RakPeer::GetStatistics( playerId ); } blobby-1.0rc3/src/raknet/PacketPool.h0000644000175000017500000000513012042452367021034 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Manage memory for packet. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __PACKET_POOL #define __PACKET_POOL #include "SimpleMutex.h" #include "NetworkTypes.h" #include /** * @brief Manage memory for packet. * * * The PacketPool class as multiple roles : * - Managing memory associated to packets * - Reuse memory of old packet to increase performances. * */ class PacketPool { public: /** * Constructor */ PacketPool(); /** * Destructor */ ~PacketPool(); /** * Get Memory for a packet * @return a Packet object */ Packet* GetPointer( void ); /** * Free Memory for a packet * @param p The packet to free */ void ReleasePointer( Packet *p ); /** * Clear the Packet Pool */ void ClearPool( void ); private: /** * Store packets */ std::stack pool; /** * Exclusive access to the pool */ SimpleMutex poolMutex; #ifdef _DEBUG /** * In debugging stage, stores the number of packet released */ int packetsReleased; #endif }; #endif blobby-1.0rc3/src/raknet/CheckSum.h0000644000175000017500000000534212042452367020502 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief CheckSum class declaration * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * From http://www.flounder.com/checksum.htm */ #ifndef __CHECKSUM_H #define __CHECKSUM_H /** * This class provide checksuming service. * */ class CheckSum { public: /** * Default constructor */ CheckSum() { clear(); } /** * Reset to an initial state. */ void clear() { sum = 0; r = 55665; c1 = 52845; c2 = 22719; } /** * add data to the checksum * @param w add a dword of data */ void add ( unsigned int w ) ; /** * add data to the checksum * @param w a word of data. */ void add ( unsigned short w ) ; /** * add an array of byte to the checksum. * @param b a pointer to the buffer. * @param length the size of the buffer. */ void add ( unsigned char* b, unsigned int length ) ; /** * Add one byte of data for checksuming * @param b a byte of data. */ void add ( unsigned char b ) ; /** * Get the checksum of the data. */ unsigned int get () { return sum; } protected: unsigned short r; unsigned short c1; unsigned short c2; unsigned int sum; }; #endif blobby-1.0rc3/src/raknet/HuffmanEncodingTreeFactory.h0000644000175000017500000000574112042452367024206 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file HuffmanEncodingTreeFactory.h * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __HUFFMAN_ENCODING_TREE_FACTORY #define __HUFFMAN_ENCODING_TREE_FACTORY class HuffmanEncodingTree; /** * This generates a special case of the huffman encoding tree with 8 bit keys */ class HuffmanEncodingTreeFactory { public: /** * Default Constructor */ HuffmanEncodingTreeFactory(); /** * Reset the frequency table. You don't need to call this unless you want to reuse the class for a new tree */ void Reset( void ); /** * Pass an array of bytes to this to add those elements to the frequency table * @param array the data to insert into the frequency table * @param size the size of the data to insert */ void AddToFrequencyTable( unsigned char *array, int size ); /** * Copies the frequency table to the array passed * Retrieve the frequency table * @param _frequency The frequency table used currently */ void GetFrequencyTable( unsigned int _frequency[ 256 ] ); /** * Returns the frequency table as a pointer * @return the address of the frenquency table */ unsigned int * GetFrequencyTable( void ); /** * Generate a HuffmanEncodingTree. * You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself */ HuffmanEncodingTree * GenerateTree( void ); private: /** * 256 frequencies. */ unsigned int frequency[ 256 ]; }; #endif blobby-1.0rc3/src/raknet/rijndael-boxes.h0000644000175000017500000023141212042452367021705 0ustar danielknobedanielknobe// This code is public, take a look in the LICENSE File word8 Logtable[256] = { 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142, 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, 102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87, 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, 44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, 151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7 }; word8 Alogtable[256] = { 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, 131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, 195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1 }; word8 S[256] = { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 }; word8 Si[256] = { 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125 }; word8 T1[256][4] = { {0xc6,0x63,0x63,0xa5}, {0xf8,0x7c,0x7c,0x84}, {0xee,0x77,0x77,0x99}, {0xf6,0x7b,0x7b,0x8d}, {0xff,0xf2,0xf2,0x0d}, {0xd6,0x6b,0x6b,0xbd}, {0xde,0x6f,0x6f,0xb1}, {0x91,0xc5,0xc5,0x54}, {0x60,0x30,0x30,0x50}, {0x02,0x01,0x01,0x03}, {0xce,0x67,0x67,0xa9}, {0x56,0x2b,0x2b,0x7d}, {0xe7,0xfe,0xfe,0x19}, {0xb5,0xd7,0xd7,0x62}, {0x4d,0xab,0xab,0xe6}, {0xec,0x76,0x76,0x9a}, {0x8f,0xca,0xca,0x45}, {0x1f,0x82,0x82,0x9d}, {0x89,0xc9,0xc9,0x40}, {0xfa,0x7d,0x7d,0x87}, {0xef,0xfa,0xfa,0x15}, {0xb2,0x59,0x59,0xeb}, {0x8e,0x47,0x47,0xc9}, {0xfb,0xf0,0xf0,0x0b}, {0x41,0xad,0xad,0xec}, {0xb3,0xd4,0xd4,0x67}, {0x5f,0xa2,0xa2,0xfd}, {0x45,0xaf,0xaf,0xea}, {0x23,0x9c,0x9c,0xbf}, {0x53,0xa4,0xa4,0xf7}, {0xe4,0x72,0x72,0x96}, {0x9b,0xc0,0xc0,0x5b}, {0x75,0xb7,0xb7,0xc2}, {0xe1,0xfd,0xfd,0x1c}, {0x3d,0x93,0x93,0xae}, {0x4c,0x26,0x26,0x6a}, {0x6c,0x36,0x36,0x5a}, {0x7e,0x3f,0x3f,0x41}, {0xf5,0xf7,0xf7,0x02}, {0x83,0xcc,0xcc,0x4f}, {0x68,0x34,0x34,0x5c}, {0x51,0xa5,0xa5,0xf4}, {0xd1,0xe5,0xe5,0x34}, {0xf9,0xf1,0xf1,0x08}, {0xe2,0x71,0x71,0x93}, {0xab,0xd8,0xd8,0x73}, {0x62,0x31,0x31,0x53}, {0x2a,0x15,0x15,0x3f}, {0x08,0x04,0x04,0x0c}, {0x95,0xc7,0xc7,0x52}, {0x46,0x23,0x23,0x65}, {0x9d,0xc3,0xc3,0x5e}, {0x30,0x18,0x18,0x28}, {0x37,0x96,0x96,0xa1}, {0x0a,0x05,0x05,0x0f}, {0x2f,0x9a,0x9a,0xb5}, {0x0e,0x07,0x07,0x09}, {0x24,0x12,0x12,0x36}, {0x1b,0x80,0x80,0x9b}, {0xdf,0xe2,0xe2,0x3d}, {0xcd,0xeb,0xeb,0x26}, {0x4e,0x27,0x27,0x69}, {0x7f,0xb2,0xb2,0xcd}, {0xea,0x75,0x75,0x9f}, {0x12,0x09,0x09,0x1b}, {0x1d,0x83,0x83,0x9e}, {0x58,0x2c,0x2c,0x74}, {0x34,0x1a,0x1a,0x2e}, {0x36,0x1b,0x1b,0x2d}, {0xdc,0x6e,0x6e,0xb2}, {0xb4,0x5a,0x5a,0xee}, {0x5b,0xa0,0xa0,0xfb}, {0xa4,0x52,0x52,0xf6}, {0x76,0x3b,0x3b,0x4d}, {0xb7,0xd6,0xd6,0x61}, {0x7d,0xb3,0xb3,0xce}, {0x52,0x29,0x29,0x7b}, {0xdd,0xe3,0xe3,0x3e}, {0x5e,0x2f,0x2f,0x71}, {0x13,0x84,0x84,0x97}, {0xa6,0x53,0x53,0xf5}, {0xb9,0xd1,0xd1,0x68}, {0x00,0x00,0x00,0x00}, {0xc1,0xed,0xed,0x2c}, {0x40,0x20,0x20,0x60}, {0xe3,0xfc,0xfc,0x1f}, {0x79,0xb1,0xb1,0xc8}, {0xb6,0x5b,0x5b,0xed}, {0xd4,0x6a,0x6a,0xbe}, {0x8d,0xcb,0xcb,0x46}, {0x67,0xbe,0xbe,0xd9}, {0x72,0x39,0x39,0x4b}, {0x94,0x4a,0x4a,0xde}, {0x98,0x4c,0x4c,0xd4}, {0xb0,0x58,0x58,0xe8}, {0x85,0xcf,0xcf,0x4a}, {0xbb,0xd0,0xd0,0x6b}, {0xc5,0xef,0xef,0x2a}, {0x4f,0xaa,0xaa,0xe5}, {0xed,0xfb,0xfb,0x16}, {0x86,0x43,0x43,0xc5}, {0x9a,0x4d,0x4d,0xd7}, {0x66,0x33,0x33,0x55}, {0x11,0x85,0x85,0x94}, {0x8a,0x45,0x45,0xcf}, {0xe9,0xf9,0xf9,0x10}, {0x04,0x02,0x02,0x06}, {0xfe,0x7f,0x7f,0x81}, {0xa0,0x50,0x50,0xf0}, {0x78,0x3c,0x3c,0x44}, {0x25,0x9f,0x9f,0xba}, {0x4b,0xa8,0xa8,0xe3}, {0xa2,0x51,0x51,0xf3}, {0x5d,0xa3,0xa3,0xfe}, {0x80,0x40,0x40,0xc0}, {0x05,0x8f,0x8f,0x8a}, {0x3f,0x92,0x92,0xad}, {0x21,0x9d,0x9d,0xbc}, {0x70,0x38,0x38,0x48}, {0xf1,0xf5,0xf5,0x04}, {0x63,0xbc,0xbc,0xdf}, {0x77,0xb6,0xb6,0xc1}, {0xaf,0xda,0xda,0x75}, {0x42,0x21,0x21,0x63}, {0x20,0x10,0x10,0x30}, {0xe5,0xff,0xff,0x1a}, {0xfd,0xf3,0xf3,0x0e}, {0xbf,0xd2,0xd2,0x6d}, {0x81,0xcd,0xcd,0x4c}, {0x18,0x0c,0x0c,0x14}, {0x26,0x13,0x13,0x35}, {0xc3,0xec,0xec,0x2f}, {0xbe,0x5f,0x5f,0xe1}, {0x35,0x97,0x97,0xa2}, {0x88,0x44,0x44,0xcc}, {0x2e,0x17,0x17,0x39}, {0x93,0xc4,0xc4,0x57}, {0x55,0xa7,0xa7,0xf2}, {0xfc,0x7e,0x7e,0x82}, {0x7a,0x3d,0x3d,0x47}, {0xc8,0x64,0x64,0xac}, {0xba,0x5d,0x5d,0xe7}, {0x32,0x19,0x19,0x2b}, {0xe6,0x73,0x73,0x95}, {0xc0,0x60,0x60,0xa0}, {0x19,0x81,0x81,0x98}, {0x9e,0x4f,0x4f,0xd1}, {0xa3,0xdc,0xdc,0x7f}, {0x44,0x22,0x22,0x66}, {0x54,0x2a,0x2a,0x7e}, {0x3b,0x90,0x90,0xab}, {0x0b,0x88,0x88,0x83}, {0x8c,0x46,0x46,0xca}, {0xc7,0xee,0xee,0x29}, {0x6b,0xb8,0xb8,0xd3}, {0x28,0x14,0x14,0x3c}, {0xa7,0xde,0xde,0x79}, {0xbc,0x5e,0x5e,0xe2}, {0x16,0x0b,0x0b,0x1d}, {0xad,0xdb,0xdb,0x76}, {0xdb,0xe0,0xe0,0x3b}, {0x64,0x32,0x32,0x56}, {0x74,0x3a,0x3a,0x4e}, {0x14,0x0a,0x0a,0x1e}, {0x92,0x49,0x49,0xdb}, {0x0c,0x06,0x06,0x0a}, {0x48,0x24,0x24,0x6c}, {0xb8,0x5c,0x5c,0xe4}, {0x9f,0xc2,0xc2,0x5d}, {0xbd,0xd3,0xd3,0x6e}, {0x43,0xac,0xac,0xef}, {0xc4,0x62,0x62,0xa6}, {0x39,0x91,0x91,0xa8}, {0x31,0x95,0x95,0xa4}, {0xd3,0xe4,0xe4,0x37}, {0xf2,0x79,0x79,0x8b}, {0xd5,0xe7,0xe7,0x32}, {0x8b,0xc8,0xc8,0x43}, {0x6e,0x37,0x37,0x59}, {0xda,0x6d,0x6d,0xb7}, {0x01,0x8d,0x8d,0x8c}, {0xb1,0xd5,0xd5,0x64}, {0x9c,0x4e,0x4e,0xd2}, {0x49,0xa9,0xa9,0xe0}, {0xd8,0x6c,0x6c,0xb4}, {0xac,0x56,0x56,0xfa}, {0xf3,0xf4,0xf4,0x07}, {0xcf,0xea,0xea,0x25}, {0xca,0x65,0x65,0xaf}, {0xf4,0x7a,0x7a,0x8e}, {0x47,0xae,0xae,0xe9}, {0x10,0x08,0x08,0x18}, {0x6f,0xba,0xba,0xd5}, {0xf0,0x78,0x78,0x88}, {0x4a,0x25,0x25,0x6f}, {0x5c,0x2e,0x2e,0x72}, {0x38,0x1c,0x1c,0x24}, {0x57,0xa6,0xa6,0xf1}, {0x73,0xb4,0xb4,0xc7}, {0x97,0xc6,0xc6,0x51}, {0xcb,0xe8,0xe8,0x23}, {0xa1,0xdd,0xdd,0x7c}, {0xe8,0x74,0x74,0x9c}, {0x3e,0x1f,0x1f,0x21}, {0x96,0x4b,0x4b,0xdd}, {0x61,0xbd,0xbd,0xdc}, {0x0d,0x8b,0x8b,0x86}, {0x0f,0x8a,0x8a,0x85}, {0xe0,0x70,0x70,0x90}, {0x7c,0x3e,0x3e,0x42}, {0x71,0xb5,0xb5,0xc4}, {0xcc,0x66,0x66,0xaa}, {0x90,0x48,0x48,0xd8}, {0x06,0x03,0x03,0x05}, {0xf7,0xf6,0xf6,0x01}, {0x1c,0x0e,0x0e,0x12}, {0xc2,0x61,0x61,0xa3}, {0x6a,0x35,0x35,0x5f}, {0xae,0x57,0x57,0xf9}, {0x69,0xb9,0xb9,0xd0}, {0x17,0x86,0x86,0x91}, {0x99,0xc1,0xc1,0x58}, {0x3a,0x1d,0x1d,0x27}, {0x27,0x9e,0x9e,0xb9}, {0xd9,0xe1,0xe1,0x38}, {0xeb,0xf8,0xf8,0x13}, {0x2b,0x98,0x98,0xb3}, {0x22,0x11,0x11,0x33}, {0xd2,0x69,0x69,0xbb}, {0xa9,0xd9,0xd9,0x70}, {0x07,0x8e,0x8e,0x89}, {0x33,0x94,0x94,0xa7}, {0x2d,0x9b,0x9b,0xb6}, {0x3c,0x1e,0x1e,0x22}, {0x15,0x87,0x87,0x92}, {0xc9,0xe9,0xe9,0x20}, {0x87,0xce,0xce,0x49}, {0xaa,0x55,0x55,0xff}, {0x50,0x28,0x28,0x78}, {0xa5,0xdf,0xdf,0x7a}, {0x03,0x8c,0x8c,0x8f}, {0x59,0xa1,0xa1,0xf8}, {0x09,0x89,0x89,0x80}, {0x1a,0x0d,0x0d,0x17}, {0x65,0xbf,0xbf,0xda}, {0xd7,0xe6,0xe6,0x31}, {0x84,0x42,0x42,0xc6}, {0xd0,0x68,0x68,0xb8}, {0x82,0x41,0x41,0xc3}, {0x29,0x99,0x99,0xb0}, {0x5a,0x2d,0x2d,0x77}, {0x1e,0x0f,0x0f,0x11}, {0x7b,0xb0,0xb0,0xcb}, {0xa8,0x54,0x54,0xfc}, {0x6d,0xbb,0xbb,0xd6}, {0x2c,0x16,0x16,0x3a} }; word8 T2[256][4] = { {0xa5,0xc6,0x63,0x63}, {0x84,0xf8,0x7c,0x7c}, {0x99,0xee,0x77,0x77}, {0x8d,0xf6,0x7b,0x7b}, {0x0d,0xff,0xf2,0xf2}, {0xbd,0xd6,0x6b,0x6b}, {0xb1,0xde,0x6f,0x6f}, {0x54,0x91,0xc5,0xc5}, {0x50,0x60,0x30,0x30}, {0x03,0x02,0x01,0x01}, {0xa9,0xce,0x67,0x67}, {0x7d,0x56,0x2b,0x2b}, {0x19,0xe7,0xfe,0xfe}, {0x62,0xb5,0xd7,0xd7}, {0xe6,0x4d,0xab,0xab}, {0x9a,0xec,0x76,0x76}, {0x45,0x8f,0xca,0xca}, {0x9d,0x1f,0x82,0x82}, {0x40,0x89,0xc9,0xc9}, {0x87,0xfa,0x7d,0x7d}, {0x15,0xef,0xfa,0xfa}, {0xeb,0xb2,0x59,0x59}, {0xc9,0x8e,0x47,0x47}, {0x0b,0xfb,0xf0,0xf0}, {0xec,0x41,0xad,0xad}, {0x67,0xb3,0xd4,0xd4}, {0xfd,0x5f,0xa2,0xa2}, {0xea,0x45,0xaf,0xaf}, {0xbf,0x23,0x9c,0x9c}, {0xf7,0x53,0xa4,0xa4}, {0x96,0xe4,0x72,0x72}, {0x5b,0x9b,0xc0,0xc0}, {0xc2,0x75,0xb7,0xb7}, {0x1c,0xe1,0xfd,0xfd}, {0xae,0x3d,0x93,0x93}, {0x6a,0x4c,0x26,0x26}, {0x5a,0x6c,0x36,0x36}, {0x41,0x7e,0x3f,0x3f}, {0x02,0xf5,0xf7,0xf7}, {0x4f,0x83,0xcc,0xcc}, {0x5c,0x68,0x34,0x34}, {0xf4,0x51,0xa5,0xa5}, {0x34,0xd1,0xe5,0xe5}, {0x08,0xf9,0xf1,0xf1}, {0x93,0xe2,0x71,0x71}, {0x73,0xab,0xd8,0xd8}, {0x53,0x62,0x31,0x31}, {0x3f,0x2a,0x15,0x15}, {0x0c,0x08,0x04,0x04}, {0x52,0x95,0xc7,0xc7}, {0x65,0x46,0x23,0x23}, {0x5e,0x9d,0xc3,0xc3}, {0x28,0x30,0x18,0x18}, {0xa1,0x37,0x96,0x96}, {0x0f,0x0a,0x05,0x05}, {0xb5,0x2f,0x9a,0x9a}, {0x09,0x0e,0x07,0x07}, {0x36,0x24,0x12,0x12}, {0x9b,0x1b,0x80,0x80}, {0x3d,0xdf,0xe2,0xe2}, {0x26,0xcd,0xeb,0xeb}, {0x69,0x4e,0x27,0x27}, {0xcd,0x7f,0xb2,0xb2}, {0x9f,0xea,0x75,0x75}, {0x1b,0x12,0x09,0x09}, {0x9e,0x1d,0x83,0x83}, {0x74,0x58,0x2c,0x2c}, {0x2e,0x34,0x1a,0x1a}, {0x2d,0x36,0x1b,0x1b}, {0xb2,0xdc,0x6e,0x6e}, {0xee,0xb4,0x5a,0x5a}, {0xfb,0x5b,0xa0,0xa0}, {0xf6,0xa4,0x52,0x52}, {0x4d,0x76,0x3b,0x3b}, {0x61,0xb7,0xd6,0xd6}, {0xce,0x7d,0xb3,0xb3}, {0x7b,0x52,0x29,0x29}, {0x3e,0xdd,0xe3,0xe3}, {0x71,0x5e,0x2f,0x2f}, {0x97,0x13,0x84,0x84}, {0xf5,0xa6,0x53,0x53}, {0x68,0xb9,0xd1,0xd1}, {0x00,0x00,0x00,0x00}, {0x2c,0xc1,0xed,0xed}, {0x60,0x40,0x20,0x20}, {0x1f,0xe3,0xfc,0xfc}, {0xc8,0x79,0xb1,0xb1}, {0xed,0xb6,0x5b,0x5b}, {0xbe,0xd4,0x6a,0x6a}, {0x46,0x8d,0xcb,0xcb}, {0xd9,0x67,0xbe,0xbe}, {0x4b,0x72,0x39,0x39}, {0xde,0x94,0x4a,0x4a}, {0xd4,0x98,0x4c,0x4c}, {0xe8,0xb0,0x58,0x58}, {0x4a,0x85,0xcf,0xcf}, {0x6b,0xbb,0xd0,0xd0}, {0x2a,0xc5,0xef,0xef}, {0xe5,0x4f,0xaa,0xaa}, {0x16,0xed,0xfb,0xfb}, {0xc5,0x86,0x43,0x43}, {0xd7,0x9a,0x4d,0x4d}, {0x55,0x66,0x33,0x33}, {0x94,0x11,0x85,0x85}, {0xcf,0x8a,0x45,0x45}, {0x10,0xe9,0xf9,0xf9}, {0x06,0x04,0x02,0x02}, {0x81,0xfe,0x7f,0x7f}, {0xf0,0xa0,0x50,0x50}, {0x44,0x78,0x3c,0x3c}, {0xba,0x25,0x9f,0x9f}, {0xe3,0x4b,0xa8,0xa8}, {0xf3,0xa2,0x51,0x51}, {0xfe,0x5d,0xa3,0xa3}, {0xc0,0x80,0x40,0x40}, {0x8a,0x05,0x8f,0x8f}, {0xad,0x3f,0x92,0x92}, {0xbc,0x21,0x9d,0x9d}, {0x48,0x70,0x38,0x38}, {0x04,0xf1,0xf5,0xf5}, {0xdf,0x63,0xbc,0xbc}, {0xc1,0x77,0xb6,0xb6}, {0x75,0xaf,0xda,0xda}, {0x63,0x42,0x21,0x21}, {0x30,0x20,0x10,0x10}, {0x1a,0xe5,0xff,0xff}, {0x0e,0xfd,0xf3,0xf3}, {0x6d,0xbf,0xd2,0xd2}, {0x4c,0x81,0xcd,0xcd}, {0x14,0x18,0x0c,0x0c}, {0x35,0x26,0x13,0x13}, {0x2f,0xc3,0xec,0xec}, {0xe1,0xbe,0x5f,0x5f}, {0xa2,0x35,0x97,0x97}, {0xcc,0x88,0x44,0x44}, {0x39,0x2e,0x17,0x17}, {0x57,0x93,0xc4,0xc4}, {0xf2,0x55,0xa7,0xa7}, {0x82,0xfc,0x7e,0x7e}, {0x47,0x7a,0x3d,0x3d}, {0xac,0xc8,0x64,0x64}, {0xe7,0xba,0x5d,0x5d}, {0x2b,0x32,0x19,0x19}, {0x95,0xe6,0x73,0x73}, {0xa0,0xc0,0x60,0x60}, {0x98,0x19,0x81,0x81}, {0xd1,0x9e,0x4f,0x4f}, {0x7f,0xa3,0xdc,0xdc}, {0x66,0x44,0x22,0x22}, {0x7e,0x54,0x2a,0x2a}, {0xab,0x3b,0x90,0x90}, {0x83,0x0b,0x88,0x88}, {0xca,0x8c,0x46,0x46}, {0x29,0xc7,0xee,0xee}, {0xd3,0x6b,0xb8,0xb8}, {0x3c,0x28,0x14,0x14}, {0x79,0xa7,0xde,0xde}, {0xe2,0xbc,0x5e,0x5e}, {0x1d,0x16,0x0b,0x0b}, {0x76,0xad,0xdb,0xdb}, {0x3b,0xdb,0xe0,0xe0}, {0x56,0x64,0x32,0x32}, {0x4e,0x74,0x3a,0x3a}, {0x1e,0x14,0x0a,0x0a}, {0xdb,0x92,0x49,0x49}, {0x0a,0x0c,0x06,0x06}, {0x6c,0x48,0x24,0x24}, {0xe4,0xb8,0x5c,0x5c}, {0x5d,0x9f,0xc2,0xc2}, {0x6e,0xbd,0xd3,0xd3}, {0xef,0x43,0xac,0xac}, {0xa6,0xc4,0x62,0x62}, {0xa8,0x39,0x91,0x91}, {0xa4,0x31,0x95,0x95}, {0x37,0xd3,0xe4,0xe4}, {0x8b,0xf2,0x79,0x79}, {0x32,0xd5,0xe7,0xe7}, {0x43,0x8b,0xc8,0xc8}, {0x59,0x6e,0x37,0x37}, {0xb7,0xda,0x6d,0x6d}, {0x8c,0x01,0x8d,0x8d}, {0x64,0xb1,0xd5,0xd5}, {0xd2,0x9c,0x4e,0x4e}, {0xe0,0x49,0xa9,0xa9}, {0xb4,0xd8,0x6c,0x6c}, {0xfa,0xac,0x56,0x56}, {0x07,0xf3,0xf4,0xf4}, {0x25,0xcf,0xea,0xea}, {0xaf,0xca,0x65,0x65}, {0x8e,0xf4,0x7a,0x7a}, {0xe9,0x47,0xae,0xae}, {0x18,0x10,0x08,0x08}, {0xd5,0x6f,0xba,0xba}, {0x88,0xf0,0x78,0x78}, {0x6f,0x4a,0x25,0x25}, {0x72,0x5c,0x2e,0x2e}, {0x24,0x38,0x1c,0x1c}, {0xf1,0x57,0xa6,0xa6}, {0xc7,0x73,0xb4,0xb4}, {0x51,0x97,0xc6,0xc6}, {0x23,0xcb,0xe8,0xe8}, {0x7c,0xa1,0xdd,0xdd}, {0x9c,0xe8,0x74,0x74}, {0x21,0x3e,0x1f,0x1f}, {0xdd,0x96,0x4b,0x4b}, {0xdc,0x61,0xbd,0xbd}, {0x86,0x0d,0x8b,0x8b}, {0x85,0x0f,0x8a,0x8a}, {0x90,0xe0,0x70,0x70}, {0x42,0x7c,0x3e,0x3e}, {0xc4,0x71,0xb5,0xb5}, {0xaa,0xcc,0x66,0x66}, {0xd8,0x90,0x48,0x48}, {0x05,0x06,0x03,0x03}, {0x01,0xf7,0xf6,0xf6}, {0x12,0x1c,0x0e,0x0e}, {0xa3,0xc2,0x61,0x61}, {0x5f,0x6a,0x35,0x35}, {0xf9,0xae,0x57,0x57}, {0xd0,0x69,0xb9,0xb9}, {0x91,0x17,0x86,0x86}, {0x58,0x99,0xc1,0xc1}, {0x27,0x3a,0x1d,0x1d}, {0xb9,0x27,0x9e,0x9e}, {0x38,0xd9,0xe1,0xe1}, {0x13,0xeb,0xf8,0xf8}, {0xb3,0x2b,0x98,0x98}, {0x33,0x22,0x11,0x11}, {0xbb,0xd2,0x69,0x69}, {0x70,0xa9,0xd9,0xd9}, {0x89,0x07,0x8e,0x8e}, {0xa7,0x33,0x94,0x94}, {0xb6,0x2d,0x9b,0x9b}, {0x22,0x3c,0x1e,0x1e}, {0x92,0x15,0x87,0x87}, {0x20,0xc9,0xe9,0xe9}, {0x49,0x87,0xce,0xce}, {0xff,0xaa,0x55,0x55}, {0x78,0x50,0x28,0x28}, {0x7a,0xa5,0xdf,0xdf}, {0x8f,0x03,0x8c,0x8c}, {0xf8,0x59,0xa1,0xa1}, {0x80,0x09,0x89,0x89}, {0x17,0x1a,0x0d,0x0d}, {0xda,0x65,0xbf,0xbf}, {0x31,0xd7,0xe6,0xe6}, {0xc6,0x84,0x42,0x42}, {0xb8,0xd0,0x68,0x68}, {0xc3,0x82,0x41,0x41}, {0xb0,0x29,0x99,0x99}, {0x77,0x5a,0x2d,0x2d}, {0x11,0x1e,0x0f,0x0f}, {0xcb,0x7b,0xb0,0xb0}, {0xfc,0xa8,0x54,0x54}, {0xd6,0x6d,0xbb,0xbb}, {0x3a,0x2c,0x16,0x16} }; word8 T3[256][4] = { {0x63,0xa5,0xc6,0x63}, {0x7c,0x84,0xf8,0x7c}, {0x77,0x99,0xee,0x77}, {0x7b,0x8d,0xf6,0x7b}, {0xf2,0x0d,0xff,0xf2}, {0x6b,0xbd,0xd6,0x6b}, {0x6f,0xb1,0xde,0x6f}, {0xc5,0x54,0x91,0xc5}, {0x30,0x50,0x60,0x30}, {0x01,0x03,0x02,0x01}, {0x67,0xa9,0xce,0x67}, {0x2b,0x7d,0x56,0x2b}, {0xfe,0x19,0xe7,0xfe}, {0xd7,0x62,0xb5,0xd7}, {0xab,0xe6,0x4d,0xab}, {0x76,0x9a,0xec,0x76}, {0xca,0x45,0x8f,0xca}, {0x82,0x9d,0x1f,0x82}, {0xc9,0x40,0x89,0xc9}, {0x7d,0x87,0xfa,0x7d}, {0xfa,0x15,0xef,0xfa}, {0x59,0xeb,0xb2,0x59}, {0x47,0xc9,0x8e,0x47}, {0xf0,0x0b,0xfb,0xf0}, {0xad,0xec,0x41,0xad}, {0xd4,0x67,0xb3,0xd4}, {0xa2,0xfd,0x5f,0xa2}, {0xaf,0xea,0x45,0xaf}, {0x9c,0xbf,0x23,0x9c}, {0xa4,0xf7,0x53,0xa4}, {0x72,0x96,0xe4,0x72}, {0xc0,0x5b,0x9b,0xc0}, {0xb7,0xc2,0x75,0xb7}, {0xfd,0x1c,0xe1,0xfd}, {0x93,0xae,0x3d,0x93}, {0x26,0x6a,0x4c,0x26}, {0x36,0x5a,0x6c,0x36}, {0x3f,0x41,0x7e,0x3f}, {0xf7,0x02,0xf5,0xf7}, {0xcc,0x4f,0x83,0xcc}, {0x34,0x5c,0x68,0x34}, {0xa5,0xf4,0x51,0xa5}, {0xe5,0x34,0xd1,0xe5}, {0xf1,0x08,0xf9,0xf1}, {0x71,0x93,0xe2,0x71}, {0xd8,0x73,0xab,0xd8}, {0x31,0x53,0x62,0x31}, {0x15,0x3f,0x2a,0x15}, {0x04,0x0c,0x08,0x04}, {0xc7,0x52,0x95,0xc7}, {0x23,0x65,0x46,0x23}, {0xc3,0x5e,0x9d,0xc3}, {0x18,0x28,0x30,0x18}, {0x96,0xa1,0x37,0x96}, {0x05,0x0f,0x0a,0x05}, {0x9a,0xb5,0x2f,0x9a}, {0x07,0x09,0x0e,0x07}, {0x12,0x36,0x24,0x12}, {0x80,0x9b,0x1b,0x80}, {0xe2,0x3d,0xdf,0xe2}, {0xeb,0x26,0xcd,0xeb}, {0x27,0x69,0x4e,0x27}, {0xb2,0xcd,0x7f,0xb2}, {0x75,0x9f,0xea,0x75}, {0x09,0x1b,0x12,0x09}, {0x83,0x9e,0x1d,0x83}, {0x2c,0x74,0x58,0x2c}, {0x1a,0x2e,0x34,0x1a}, {0x1b,0x2d,0x36,0x1b}, {0x6e,0xb2,0xdc,0x6e}, {0x5a,0xee,0xb4,0x5a}, {0xa0,0xfb,0x5b,0xa0}, {0x52,0xf6,0xa4,0x52}, {0x3b,0x4d,0x76,0x3b}, {0xd6,0x61,0xb7,0xd6}, {0xb3,0xce,0x7d,0xb3}, {0x29,0x7b,0x52,0x29}, {0xe3,0x3e,0xdd,0xe3}, {0x2f,0x71,0x5e,0x2f}, {0x84,0x97,0x13,0x84}, {0x53,0xf5,0xa6,0x53}, {0xd1,0x68,0xb9,0xd1}, {0x00,0x00,0x00,0x00}, {0xed,0x2c,0xc1,0xed}, {0x20,0x60,0x40,0x20}, {0xfc,0x1f,0xe3,0xfc}, {0xb1,0xc8,0x79,0xb1}, {0x5b,0xed,0xb6,0x5b}, {0x6a,0xbe,0xd4,0x6a}, {0xcb,0x46,0x8d,0xcb}, {0xbe,0xd9,0x67,0xbe}, {0x39,0x4b,0x72,0x39}, {0x4a,0xde,0x94,0x4a}, {0x4c,0xd4,0x98,0x4c}, {0x58,0xe8,0xb0,0x58}, {0xcf,0x4a,0x85,0xcf}, {0xd0,0x6b,0xbb,0xd0}, {0xef,0x2a,0xc5,0xef}, {0xaa,0xe5,0x4f,0xaa}, {0xfb,0x16,0xed,0xfb}, {0x43,0xc5,0x86,0x43}, {0x4d,0xd7,0x9a,0x4d}, {0x33,0x55,0x66,0x33}, {0x85,0x94,0x11,0x85}, {0x45,0xcf,0x8a,0x45}, {0xf9,0x10,0xe9,0xf9}, {0x02,0x06,0x04,0x02}, {0x7f,0x81,0xfe,0x7f}, {0x50,0xf0,0xa0,0x50}, {0x3c,0x44,0x78,0x3c}, {0x9f,0xba,0x25,0x9f}, {0xa8,0xe3,0x4b,0xa8}, {0x51,0xf3,0xa2,0x51}, {0xa3,0xfe,0x5d,0xa3}, {0x40,0xc0,0x80,0x40}, {0x8f,0x8a,0x05,0x8f}, {0x92,0xad,0x3f,0x92}, {0x9d,0xbc,0x21,0x9d}, {0x38,0x48,0x70,0x38}, {0xf5,0x04,0xf1,0xf5}, {0xbc,0xdf,0x63,0xbc}, {0xb6,0xc1,0x77,0xb6}, {0xda,0x75,0xaf,0xda}, {0x21,0x63,0x42,0x21}, {0x10,0x30,0x20,0x10}, {0xff,0x1a,0xe5,0xff}, {0xf3,0x0e,0xfd,0xf3}, {0xd2,0x6d,0xbf,0xd2}, {0xcd,0x4c,0x81,0xcd}, {0x0c,0x14,0x18,0x0c}, {0x13,0x35,0x26,0x13}, {0xec,0x2f,0xc3,0xec}, {0x5f,0xe1,0xbe,0x5f}, {0x97,0xa2,0x35,0x97}, {0x44,0xcc,0x88,0x44}, {0x17,0x39,0x2e,0x17}, {0xc4,0x57,0x93,0xc4}, {0xa7,0xf2,0x55,0xa7}, {0x7e,0x82,0xfc,0x7e}, {0x3d,0x47,0x7a,0x3d}, {0x64,0xac,0xc8,0x64}, {0x5d,0xe7,0xba,0x5d}, {0x19,0x2b,0x32,0x19}, {0x73,0x95,0xe6,0x73}, {0x60,0xa0,0xc0,0x60}, {0x81,0x98,0x19,0x81}, {0x4f,0xd1,0x9e,0x4f}, {0xdc,0x7f,0xa3,0xdc}, {0x22,0x66,0x44,0x22}, {0x2a,0x7e,0x54,0x2a}, {0x90,0xab,0x3b,0x90}, {0x88,0x83,0x0b,0x88}, {0x46,0xca,0x8c,0x46}, {0xee,0x29,0xc7,0xee}, {0xb8,0xd3,0x6b,0xb8}, {0x14,0x3c,0x28,0x14}, {0xde,0x79,0xa7,0xde}, {0x5e,0xe2,0xbc,0x5e}, {0x0b,0x1d,0x16,0x0b}, {0xdb,0x76,0xad,0xdb}, {0xe0,0x3b,0xdb,0xe0}, {0x32,0x56,0x64,0x32}, {0x3a,0x4e,0x74,0x3a}, {0x0a,0x1e,0x14,0x0a}, {0x49,0xdb,0x92,0x49}, {0x06,0x0a,0x0c,0x06}, {0x24,0x6c,0x48,0x24}, {0x5c,0xe4,0xb8,0x5c}, {0xc2,0x5d,0x9f,0xc2}, {0xd3,0x6e,0xbd,0xd3}, {0xac,0xef,0x43,0xac}, {0x62,0xa6,0xc4,0x62}, {0x91,0xa8,0x39,0x91}, {0x95,0xa4,0x31,0x95}, {0xe4,0x37,0xd3,0xe4}, {0x79,0x8b,0xf2,0x79}, {0xe7,0x32,0xd5,0xe7}, {0xc8,0x43,0x8b,0xc8}, {0x37,0x59,0x6e,0x37}, {0x6d,0xb7,0xda,0x6d}, {0x8d,0x8c,0x01,0x8d}, {0xd5,0x64,0xb1,0xd5}, {0x4e,0xd2,0x9c,0x4e}, {0xa9,0xe0,0x49,0xa9}, {0x6c,0xb4,0xd8,0x6c}, {0x56,0xfa,0xac,0x56}, {0xf4,0x07,0xf3,0xf4}, {0xea,0x25,0xcf,0xea}, {0x65,0xaf,0xca,0x65}, {0x7a,0x8e,0xf4,0x7a}, {0xae,0xe9,0x47,0xae}, {0x08,0x18,0x10,0x08}, {0xba,0xd5,0x6f,0xba}, {0x78,0x88,0xf0,0x78}, {0x25,0x6f,0x4a,0x25}, {0x2e,0x72,0x5c,0x2e}, {0x1c,0x24,0x38,0x1c}, {0xa6,0xf1,0x57,0xa6}, {0xb4,0xc7,0x73,0xb4}, {0xc6,0x51,0x97,0xc6}, {0xe8,0x23,0xcb,0xe8}, {0xdd,0x7c,0xa1,0xdd}, {0x74,0x9c,0xe8,0x74}, {0x1f,0x21,0x3e,0x1f}, {0x4b,0xdd,0x96,0x4b}, {0xbd,0xdc,0x61,0xbd}, {0x8b,0x86,0x0d,0x8b}, {0x8a,0x85,0x0f,0x8a}, {0x70,0x90,0xe0,0x70}, {0x3e,0x42,0x7c,0x3e}, {0xb5,0xc4,0x71,0xb5}, {0x66,0xaa,0xcc,0x66}, {0x48,0xd8,0x90,0x48}, {0x03,0x05,0x06,0x03}, {0xf6,0x01,0xf7,0xf6}, {0x0e,0x12,0x1c,0x0e}, {0x61,0xa3,0xc2,0x61}, {0x35,0x5f,0x6a,0x35}, {0x57,0xf9,0xae,0x57}, {0xb9,0xd0,0x69,0xb9}, {0x86,0x91,0x17,0x86}, {0xc1,0x58,0x99,0xc1}, {0x1d,0x27,0x3a,0x1d}, {0x9e,0xb9,0x27,0x9e}, {0xe1,0x38,0xd9,0xe1}, {0xf8,0x13,0xeb,0xf8}, {0x98,0xb3,0x2b,0x98}, {0x11,0x33,0x22,0x11}, {0x69,0xbb,0xd2,0x69}, {0xd9,0x70,0xa9,0xd9}, {0x8e,0x89,0x07,0x8e}, {0x94,0xa7,0x33,0x94}, {0x9b,0xb6,0x2d,0x9b}, {0x1e,0x22,0x3c,0x1e}, {0x87,0x92,0x15,0x87}, {0xe9,0x20,0xc9,0xe9}, {0xce,0x49,0x87,0xce}, {0x55,0xff,0xaa,0x55}, {0x28,0x78,0x50,0x28}, {0xdf,0x7a,0xa5,0xdf}, {0x8c,0x8f,0x03,0x8c}, {0xa1,0xf8,0x59,0xa1}, {0x89,0x80,0x09,0x89}, {0x0d,0x17,0x1a,0x0d}, {0xbf,0xda,0x65,0xbf}, {0xe6,0x31,0xd7,0xe6}, {0x42,0xc6,0x84,0x42}, {0x68,0xb8,0xd0,0x68}, {0x41,0xc3,0x82,0x41}, {0x99,0xb0,0x29,0x99}, {0x2d,0x77,0x5a,0x2d}, {0x0f,0x11,0x1e,0x0f}, {0xb0,0xcb,0x7b,0xb0}, {0x54,0xfc,0xa8,0x54}, {0xbb,0xd6,0x6d,0xbb}, {0x16,0x3a,0x2c,0x16} }; word8 T4[256][4] = { {0x63,0x63,0xa5,0xc6}, {0x7c,0x7c,0x84,0xf8}, {0x77,0x77,0x99,0xee}, {0x7b,0x7b,0x8d,0xf6}, {0xf2,0xf2,0x0d,0xff}, {0x6b,0x6b,0xbd,0xd6}, {0x6f,0x6f,0xb1,0xde}, {0xc5,0xc5,0x54,0x91}, {0x30,0x30,0x50,0x60}, {0x01,0x01,0x03,0x02}, {0x67,0x67,0xa9,0xce}, {0x2b,0x2b,0x7d,0x56}, {0xfe,0xfe,0x19,0xe7}, {0xd7,0xd7,0x62,0xb5}, {0xab,0xab,0xe6,0x4d}, {0x76,0x76,0x9a,0xec}, {0xca,0xca,0x45,0x8f}, {0x82,0x82,0x9d,0x1f}, {0xc9,0xc9,0x40,0x89}, {0x7d,0x7d,0x87,0xfa}, {0xfa,0xfa,0x15,0xef}, {0x59,0x59,0xeb,0xb2}, {0x47,0x47,0xc9,0x8e}, {0xf0,0xf0,0x0b,0xfb}, {0xad,0xad,0xec,0x41}, {0xd4,0xd4,0x67,0xb3}, {0xa2,0xa2,0xfd,0x5f}, {0xaf,0xaf,0xea,0x45}, {0x9c,0x9c,0xbf,0x23}, {0xa4,0xa4,0xf7,0x53}, {0x72,0x72,0x96,0xe4}, {0xc0,0xc0,0x5b,0x9b}, {0xb7,0xb7,0xc2,0x75}, {0xfd,0xfd,0x1c,0xe1}, {0x93,0x93,0xae,0x3d}, {0x26,0x26,0x6a,0x4c}, {0x36,0x36,0x5a,0x6c}, {0x3f,0x3f,0x41,0x7e}, {0xf7,0xf7,0x02,0xf5}, {0xcc,0xcc,0x4f,0x83}, {0x34,0x34,0x5c,0x68}, {0xa5,0xa5,0xf4,0x51}, {0xe5,0xe5,0x34,0xd1}, {0xf1,0xf1,0x08,0xf9}, {0x71,0x71,0x93,0xe2}, {0xd8,0xd8,0x73,0xab}, {0x31,0x31,0x53,0x62}, {0x15,0x15,0x3f,0x2a}, {0x04,0x04,0x0c,0x08}, {0xc7,0xc7,0x52,0x95}, {0x23,0x23,0x65,0x46}, {0xc3,0xc3,0x5e,0x9d}, {0x18,0x18,0x28,0x30}, {0x96,0x96,0xa1,0x37}, {0x05,0x05,0x0f,0x0a}, {0x9a,0x9a,0xb5,0x2f}, {0x07,0x07,0x09,0x0e}, {0x12,0x12,0x36,0x24}, {0x80,0x80,0x9b,0x1b}, {0xe2,0xe2,0x3d,0xdf}, {0xeb,0xeb,0x26,0xcd}, {0x27,0x27,0x69,0x4e}, {0xb2,0xb2,0xcd,0x7f}, {0x75,0x75,0x9f,0xea}, {0x09,0x09,0x1b,0x12}, {0x83,0x83,0x9e,0x1d}, {0x2c,0x2c,0x74,0x58}, {0x1a,0x1a,0x2e,0x34}, {0x1b,0x1b,0x2d,0x36}, {0x6e,0x6e,0xb2,0xdc}, {0x5a,0x5a,0xee,0xb4}, {0xa0,0xa0,0xfb,0x5b}, {0x52,0x52,0xf6,0xa4}, {0x3b,0x3b,0x4d,0x76}, {0xd6,0xd6,0x61,0xb7}, {0xb3,0xb3,0xce,0x7d}, {0x29,0x29,0x7b,0x52}, {0xe3,0xe3,0x3e,0xdd}, {0x2f,0x2f,0x71,0x5e}, {0x84,0x84,0x97,0x13}, {0x53,0x53,0xf5,0xa6}, {0xd1,0xd1,0x68,0xb9}, {0x00,0x00,0x00,0x00}, {0xed,0xed,0x2c,0xc1}, {0x20,0x20,0x60,0x40}, {0xfc,0xfc,0x1f,0xe3}, {0xb1,0xb1,0xc8,0x79}, {0x5b,0x5b,0xed,0xb6}, {0x6a,0x6a,0xbe,0xd4}, {0xcb,0xcb,0x46,0x8d}, {0xbe,0xbe,0xd9,0x67}, {0x39,0x39,0x4b,0x72}, {0x4a,0x4a,0xde,0x94}, {0x4c,0x4c,0xd4,0x98}, {0x58,0x58,0xe8,0xb0}, {0xcf,0xcf,0x4a,0x85}, {0xd0,0xd0,0x6b,0xbb}, {0xef,0xef,0x2a,0xc5}, {0xaa,0xaa,0xe5,0x4f}, {0xfb,0xfb,0x16,0xed}, {0x43,0x43,0xc5,0x86}, {0x4d,0x4d,0xd7,0x9a}, {0x33,0x33,0x55,0x66}, {0x85,0x85,0x94,0x11}, {0x45,0x45,0xcf,0x8a}, {0xf9,0xf9,0x10,0xe9}, {0x02,0x02,0x06,0x04}, {0x7f,0x7f,0x81,0xfe}, {0x50,0x50,0xf0,0xa0}, {0x3c,0x3c,0x44,0x78}, {0x9f,0x9f,0xba,0x25}, {0xa8,0xa8,0xe3,0x4b}, {0x51,0x51,0xf3,0xa2}, {0xa3,0xa3,0xfe,0x5d}, {0x40,0x40,0xc0,0x80}, {0x8f,0x8f,0x8a,0x05}, {0x92,0x92,0xad,0x3f}, {0x9d,0x9d,0xbc,0x21}, {0x38,0x38,0x48,0x70}, {0xf5,0xf5,0x04,0xf1}, {0xbc,0xbc,0xdf,0x63}, {0xb6,0xb6,0xc1,0x77}, {0xda,0xda,0x75,0xaf}, {0x21,0x21,0x63,0x42}, {0x10,0x10,0x30,0x20}, {0xff,0xff,0x1a,0xe5}, {0xf3,0xf3,0x0e,0xfd}, {0xd2,0xd2,0x6d,0xbf}, {0xcd,0xcd,0x4c,0x81}, {0x0c,0x0c,0x14,0x18}, {0x13,0x13,0x35,0x26}, {0xec,0xec,0x2f,0xc3}, {0x5f,0x5f,0xe1,0xbe}, {0x97,0x97,0xa2,0x35}, {0x44,0x44,0xcc,0x88}, {0x17,0x17,0x39,0x2e}, {0xc4,0xc4,0x57,0x93}, {0xa7,0xa7,0xf2,0x55}, {0x7e,0x7e,0x82,0xfc}, {0x3d,0x3d,0x47,0x7a}, {0x64,0x64,0xac,0xc8}, {0x5d,0x5d,0xe7,0xba}, {0x19,0x19,0x2b,0x32}, {0x73,0x73,0x95,0xe6}, {0x60,0x60,0xa0,0xc0}, {0x81,0x81,0x98,0x19}, {0x4f,0x4f,0xd1,0x9e}, {0xdc,0xdc,0x7f,0xa3}, {0x22,0x22,0x66,0x44}, {0x2a,0x2a,0x7e,0x54}, {0x90,0x90,0xab,0x3b}, {0x88,0x88,0x83,0x0b}, {0x46,0x46,0xca,0x8c}, {0xee,0xee,0x29,0xc7}, {0xb8,0xb8,0xd3,0x6b}, {0x14,0x14,0x3c,0x28}, {0xde,0xde,0x79,0xa7}, {0x5e,0x5e,0xe2,0xbc}, {0x0b,0x0b,0x1d,0x16}, {0xdb,0xdb,0x76,0xad}, {0xe0,0xe0,0x3b,0xdb}, {0x32,0x32,0x56,0x64}, {0x3a,0x3a,0x4e,0x74}, {0x0a,0x0a,0x1e,0x14}, {0x49,0x49,0xdb,0x92}, {0x06,0x06,0x0a,0x0c}, {0x24,0x24,0x6c,0x48}, {0x5c,0x5c,0xe4,0xb8}, {0xc2,0xc2,0x5d,0x9f}, {0xd3,0xd3,0x6e,0xbd}, {0xac,0xac,0xef,0x43}, {0x62,0x62,0xa6,0xc4}, {0x91,0x91,0xa8,0x39}, {0x95,0x95,0xa4,0x31}, {0xe4,0xe4,0x37,0xd3}, {0x79,0x79,0x8b,0xf2}, {0xe7,0xe7,0x32,0xd5}, {0xc8,0xc8,0x43,0x8b}, {0x37,0x37,0x59,0x6e}, {0x6d,0x6d,0xb7,0xda}, {0x8d,0x8d,0x8c,0x01}, {0xd5,0xd5,0x64,0xb1}, {0x4e,0x4e,0xd2,0x9c}, {0xa9,0xa9,0xe0,0x49}, {0x6c,0x6c,0xb4,0xd8}, {0x56,0x56,0xfa,0xac}, {0xf4,0xf4,0x07,0xf3}, {0xea,0xea,0x25,0xcf}, {0x65,0x65,0xaf,0xca}, {0x7a,0x7a,0x8e,0xf4}, {0xae,0xae,0xe9,0x47}, {0x08,0x08,0x18,0x10}, {0xba,0xba,0xd5,0x6f}, {0x78,0x78,0x88,0xf0}, {0x25,0x25,0x6f,0x4a}, {0x2e,0x2e,0x72,0x5c}, {0x1c,0x1c,0x24,0x38}, {0xa6,0xa6,0xf1,0x57}, {0xb4,0xb4,0xc7,0x73}, {0xc6,0xc6,0x51,0x97}, {0xe8,0xe8,0x23,0xcb}, {0xdd,0xdd,0x7c,0xa1}, {0x74,0x74,0x9c,0xe8}, {0x1f,0x1f,0x21,0x3e}, {0x4b,0x4b,0xdd,0x96}, {0xbd,0xbd,0xdc,0x61}, {0x8b,0x8b,0x86,0x0d}, {0x8a,0x8a,0x85,0x0f}, {0x70,0x70,0x90,0xe0}, {0x3e,0x3e,0x42,0x7c}, {0xb5,0xb5,0xc4,0x71}, {0x66,0x66,0xaa,0xcc}, {0x48,0x48,0xd8,0x90}, {0x03,0x03,0x05,0x06}, {0xf6,0xf6,0x01,0xf7}, {0x0e,0x0e,0x12,0x1c}, {0x61,0x61,0xa3,0xc2}, {0x35,0x35,0x5f,0x6a}, {0x57,0x57,0xf9,0xae}, {0xb9,0xb9,0xd0,0x69}, {0x86,0x86,0x91,0x17}, {0xc1,0xc1,0x58,0x99}, {0x1d,0x1d,0x27,0x3a}, {0x9e,0x9e,0xb9,0x27}, {0xe1,0xe1,0x38,0xd9}, {0xf8,0xf8,0x13,0xeb}, {0x98,0x98,0xb3,0x2b}, {0x11,0x11,0x33,0x22}, {0x69,0x69,0xbb,0xd2}, {0xd9,0xd9,0x70,0xa9}, {0x8e,0x8e,0x89,0x07}, {0x94,0x94,0xa7,0x33}, {0x9b,0x9b,0xb6,0x2d}, {0x1e,0x1e,0x22,0x3c}, {0x87,0x87,0x92,0x15}, {0xe9,0xe9,0x20,0xc9}, {0xce,0xce,0x49,0x87}, {0x55,0x55,0xff,0xaa}, {0x28,0x28,0x78,0x50}, {0xdf,0xdf,0x7a,0xa5}, {0x8c,0x8c,0x8f,0x03}, {0xa1,0xa1,0xf8,0x59}, {0x89,0x89,0x80,0x09}, {0x0d,0x0d,0x17,0x1a}, {0xbf,0xbf,0xda,0x65}, {0xe6,0xe6,0x31,0xd7}, {0x42,0x42,0xc6,0x84}, {0x68,0x68,0xb8,0xd0}, {0x41,0x41,0xc3,0x82}, {0x99,0x99,0xb0,0x29}, {0x2d,0x2d,0x77,0x5a}, {0x0f,0x0f,0x11,0x1e}, {0xb0,0xb0,0xcb,0x7b}, {0x54,0x54,0xfc,0xa8}, {0xbb,0xbb,0xd6,0x6d}, {0x16,0x16,0x3a,0x2c} }; word8 T5[256][4] = { {0x51,0xf4,0xa7,0x50}, {0x7e,0x41,0x65,0x53}, {0x1a,0x17,0xa4,0xc3}, {0x3a,0x27,0x5e,0x96}, {0x3b,0xab,0x6b,0xcb}, {0x1f,0x9d,0x45,0xf1}, {0xac,0xfa,0x58,0xab}, {0x4b,0xe3,0x03,0x93}, {0x20,0x30,0xfa,0x55}, {0xad,0x76,0x6d,0xf6}, {0x88,0xcc,0x76,0x91}, {0xf5,0x02,0x4c,0x25}, {0x4f,0xe5,0xd7,0xfc}, {0xc5,0x2a,0xcb,0xd7}, {0x26,0x35,0x44,0x80}, {0xb5,0x62,0xa3,0x8f}, {0xde,0xb1,0x5a,0x49}, {0x25,0xba,0x1b,0x67}, {0x45,0xea,0x0e,0x98}, {0x5d,0xfe,0xc0,0xe1}, {0xc3,0x2f,0x75,0x02}, {0x81,0x4c,0xf0,0x12}, {0x8d,0x46,0x97,0xa3}, {0x6b,0xd3,0xf9,0xc6}, {0x03,0x8f,0x5f,0xe7}, {0x15,0x92,0x9c,0x95}, {0xbf,0x6d,0x7a,0xeb}, {0x95,0x52,0x59,0xda}, {0xd4,0xbe,0x83,0x2d}, {0x58,0x74,0x21,0xd3}, {0x49,0xe0,0x69,0x29}, {0x8e,0xc9,0xc8,0x44}, {0x75,0xc2,0x89,0x6a}, {0xf4,0x8e,0x79,0x78}, {0x99,0x58,0x3e,0x6b}, {0x27,0xb9,0x71,0xdd}, {0xbe,0xe1,0x4f,0xb6}, {0xf0,0x88,0xad,0x17}, {0xc9,0x20,0xac,0x66}, {0x7d,0xce,0x3a,0xb4}, {0x63,0xdf,0x4a,0x18}, {0xe5,0x1a,0x31,0x82}, {0x97,0x51,0x33,0x60}, {0x62,0x53,0x7f,0x45}, {0xb1,0x64,0x77,0xe0}, {0xbb,0x6b,0xae,0x84}, {0xfe,0x81,0xa0,0x1c}, {0xf9,0x08,0x2b,0x94}, {0x70,0x48,0x68,0x58}, {0x8f,0x45,0xfd,0x19}, {0x94,0xde,0x6c,0x87}, {0x52,0x7b,0xf8,0xb7}, {0xab,0x73,0xd3,0x23}, {0x72,0x4b,0x02,0xe2}, {0xe3,0x1f,0x8f,0x57}, {0x66,0x55,0xab,0x2a}, {0xb2,0xeb,0x28,0x07}, {0x2f,0xb5,0xc2,0x03}, {0x86,0xc5,0x7b,0x9a}, {0xd3,0x37,0x08,0xa5}, {0x30,0x28,0x87,0xf2}, {0x23,0xbf,0xa5,0xb2}, {0x02,0x03,0x6a,0xba}, {0xed,0x16,0x82,0x5c}, {0x8a,0xcf,0x1c,0x2b}, {0xa7,0x79,0xb4,0x92}, {0xf3,0x07,0xf2,0xf0}, {0x4e,0x69,0xe2,0xa1}, {0x65,0xda,0xf4,0xcd}, {0x06,0x05,0xbe,0xd5}, {0xd1,0x34,0x62,0x1f}, {0xc4,0xa6,0xfe,0x8a}, {0x34,0x2e,0x53,0x9d}, {0xa2,0xf3,0x55,0xa0}, {0x05,0x8a,0xe1,0x32}, {0xa4,0xf6,0xeb,0x75}, {0x0b,0x83,0xec,0x39}, {0x40,0x60,0xef,0xaa}, {0x5e,0x71,0x9f,0x06}, {0xbd,0x6e,0x10,0x51}, {0x3e,0x21,0x8a,0xf9}, {0x96,0xdd,0x06,0x3d}, {0xdd,0x3e,0x05,0xae}, {0x4d,0xe6,0xbd,0x46}, {0x91,0x54,0x8d,0xb5}, {0x71,0xc4,0x5d,0x05}, {0x04,0x06,0xd4,0x6f}, {0x60,0x50,0x15,0xff}, {0x19,0x98,0xfb,0x24}, {0xd6,0xbd,0xe9,0x97}, {0x89,0x40,0x43,0xcc}, {0x67,0xd9,0x9e,0x77}, {0xb0,0xe8,0x42,0xbd}, {0x07,0x89,0x8b,0x88}, {0xe7,0x19,0x5b,0x38}, {0x79,0xc8,0xee,0xdb}, {0xa1,0x7c,0x0a,0x47}, {0x7c,0x42,0x0f,0xe9}, {0xf8,0x84,0x1e,0xc9}, {0x00,0x00,0x00,0x00}, {0x09,0x80,0x86,0x83}, {0x32,0x2b,0xed,0x48}, {0x1e,0x11,0x70,0xac}, {0x6c,0x5a,0x72,0x4e}, {0xfd,0x0e,0xff,0xfb}, {0x0f,0x85,0x38,0x56}, {0x3d,0xae,0xd5,0x1e}, {0x36,0x2d,0x39,0x27}, {0x0a,0x0f,0xd9,0x64}, {0x68,0x5c,0xa6,0x21}, {0x9b,0x5b,0x54,0xd1}, {0x24,0x36,0x2e,0x3a}, {0x0c,0x0a,0x67,0xb1}, {0x93,0x57,0xe7,0x0f}, {0xb4,0xee,0x96,0xd2}, {0x1b,0x9b,0x91,0x9e}, {0x80,0xc0,0xc5,0x4f}, {0x61,0xdc,0x20,0xa2}, {0x5a,0x77,0x4b,0x69}, {0x1c,0x12,0x1a,0x16}, {0xe2,0x93,0xba,0x0a}, {0xc0,0xa0,0x2a,0xe5}, {0x3c,0x22,0xe0,0x43}, {0x12,0x1b,0x17,0x1d}, {0x0e,0x09,0x0d,0x0b}, {0xf2,0x8b,0xc7,0xad}, {0x2d,0xb6,0xa8,0xb9}, {0x14,0x1e,0xa9,0xc8}, {0x57,0xf1,0x19,0x85}, {0xaf,0x75,0x07,0x4c}, {0xee,0x99,0xdd,0xbb}, {0xa3,0x7f,0x60,0xfd}, {0xf7,0x01,0x26,0x9f}, {0x5c,0x72,0xf5,0xbc}, {0x44,0x66,0x3b,0xc5}, {0x5b,0xfb,0x7e,0x34}, {0x8b,0x43,0x29,0x76}, {0xcb,0x23,0xc6,0xdc}, {0xb6,0xed,0xfc,0x68}, {0xb8,0xe4,0xf1,0x63}, {0xd7,0x31,0xdc,0xca}, {0x42,0x63,0x85,0x10}, {0x13,0x97,0x22,0x40}, {0x84,0xc6,0x11,0x20}, {0x85,0x4a,0x24,0x7d}, {0xd2,0xbb,0x3d,0xf8}, {0xae,0xf9,0x32,0x11}, {0xc7,0x29,0xa1,0x6d}, {0x1d,0x9e,0x2f,0x4b}, {0xdc,0xb2,0x30,0xf3}, {0x0d,0x86,0x52,0xec}, {0x77,0xc1,0xe3,0xd0}, {0x2b,0xb3,0x16,0x6c}, {0xa9,0x70,0xb9,0x99}, {0x11,0x94,0x48,0xfa}, {0x47,0xe9,0x64,0x22}, {0xa8,0xfc,0x8c,0xc4}, {0xa0,0xf0,0x3f,0x1a}, {0x56,0x7d,0x2c,0xd8}, {0x22,0x33,0x90,0xef}, {0x87,0x49,0x4e,0xc7}, {0xd9,0x38,0xd1,0xc1}, {0x8c,0xca,0xa2,0xfe}, {0x98,0xd4,0x0b,0x36}, {0xa6,0xf5,0x81,0xcf}, {0xa5,0x7a,0xde,0x28}, {0xda,0xb7,0x8e,0x26}, {0x3f,0xad,0xbf,0xa4}, {0x2c,0x3a,0x9d,0xe4}, {0x50,0x78,0x92,0x0d}, {0x6a,0x5f,0xcc,0x9b}, {0x54,0x7e,0x46,0x62}, {0xf6,0x8d,0x13,0xc2}, {0x90,0xd8,0xb8,0xe8}, {0x2e,0x39,0xf7,0x5e}, {0x82,0xc3,0xaf,0xf5}, {0x9f,0x5d,0x80,0xbe}, {0x69,0xd0,0x93,0x7c}, {0x6f,0xd5,0x2d,0xa9}, {0xcf,0x25,0x12,0xb3}, {0xc8,0xac,0x99,0x3b}, {0x10,0x18,0x7d,0xa7}, {0xe8,0x9c,0x63,0x6e}, {0xdb,0x3b,0xbb,0x7b}, {0xcd,0x26,0x78,0x09}, {0x6e,0x59,0x18,0xf4}, {0xec,0x9a,0xb7,0x01}, {0x83,0x4f,0x9a,0xa8}, {0xe6,0x95,0x6e,0x65}, {0xaa,0xff,0xe6,0x7e}, {0x21,0xbc,0xcf,0x08}, {0xef,0x15,0xe8,0xe6}, {0xba,0xe7,0x9b,0xd9}, {0x4a,0x6f,0x36,0xce}, {0xea,0x9f,0x09,0xd4}, {0x29,0xb0,0x7c,0xd6}, {0x31,0xa4,0xb2,0xaf}, {0x2a,0x3f,0x23,0x31}, {0xc6,0xa5,0x94,0x30}, {0x35,0xa2,0x66,0xc0}, {0x74,0x4e,0xbc,0x37}, {0xfc,0x82,0xca,0xa6}, {0xe0,0x90,0xd0,0xb0}, {0x33,0xa7,0xd8,0x15}, {0xf1,0x04,0x98,0x4a}, {0x41,0xec,0xda,0xf7}, {0x7f,0xcd,0x50,0x0e}, {0x17,0x91,0xf6,0x2f}, {0x76,0x4d,0xd6,0x8d}, {0x43,0xef,0xb0,0x4d}, {0xcc,0xaa,0x4d,0x54}, {0xe4,0x96,0x04,0xdf}, {0x9e,0xd1,0xb5,0xe3}, {0x4c,0x6a,0x88,0x1b}, {0xc1,0x2c,0x1f,0xb8}, {0x46,0x65,0x51,0x7f}, {0x9d,0x5e,0xea,0x04}, {0x01,0x8c,0x35,0x5d}, {0xfa,0x87,0x74,0x73}, {0xfb,0x0b,0x41,0x2e}, {0xb3,0x67,0x1d,0x5a}, {0x92,0xdb,0xd2,0x52}, {0xe9,0x10,0x56,0x33}, {0x6d,0xd6,0x47,0x13}, {0x9a,0xd7,0x61,0x8c}, {0x37,0xa1,0x0c,0x7a}, {0x59,0xf8,0x14,0x8e}, {0xeb,0x13,0x3c,0x89}, {0xce,0xa9,0x27,0xee}, {0xb7,0x61,0xc9,0x35}, {0xe1,0x1c,0xe5,0xed}, {0x7a,0x47,0xb1,0x3c}, {0x9c,0xd2,0xdf,0x59}, {0x55,0xf2,0x73,0x3f}, {0x18,0x14,0xce,0x79}, {0x73,0xc7,0x37,0xbf}, {0x53,0xf7,0xcd,0xea}, {0x5f,0xfd,0xaa,0x5b}, {0xdf,0x3d,0x6f,0x14}, {0x78,0x44,0xdb,0x86}, {0xca,0xaf,0xf3,0x81}, {0xb9,0x68,0xc4,0x3e}, {0x38,0x24,0x34,0x2c}, {0xc2,0xa3,0x40,0x5f}, {0x16,0x1d,0xc3,0x72}, {0xbc,0xe2,0x25,0x0c}, {0x28,0x3c,0x49,0x8b}, {0xff,0x0d,0x95,0x41}, {0x39,0xa8,0x01,0x71}, {0x08,0x0c,0xb3,0xde}, {0xd8,0xb4,0xe4,0x9c}, {0x64,0x56,0xc1,0x90}, {0x7b,0xcb,0x84,0x61}, {0xd5,0x32,0xb6,0x70}, {0x48,0x6c,0x5c,0x74}, {0xd0,0xb8,0x57,0x42} }; word8 T6[256][4] = { {0x50,0x51,0xf4,0xa7}, {0x53,0x7e,0x41,0x65}, {0xc3,0x1a,0x17,0xa4}, {0x96,0x3a,0x27,0x5e}, {0xcb,0x3b,0xab,0x6b}, {0xf1,0x1f,0x9d,0x45}, {0xab,0xac,0xfa,0x58}, {0x93,0x4b,0xe3,0x03}, {0x55,0x20,0x30,0xfa}, {0xf6,0xad,0x76,0x6d}, {0x91,0x88,0xcc,0x76}, {0x25,0xf5,0x02,0x4c}, {0xfc,0x4f,0xe5,0xd7}, {0xd7,0xc5,0x2a,0xcb}, {0x80,0x26,0x35,0x44}, {0x8f,0xb5,0x62,0xa3}, {0x49,0xde,0xb1,0x5a}, {0x67,0x25,0xba,0x1b}, {0x98,0x45,0xea,0x0e}, {0xe1,0x5d,0xfe,0xc0}, {0x02,0xc3,0x2f,0x75}, {0x12,0x81,0x4c,0xf0}, {0xa3,0x8d,0x46,0x97}, {0xc6,0x6b,0xd3,0xf9}, {0xe7,0x03,0x8f,0x5f}, {0x95,0x15,0x92,0x9c}, {0xeb,0xbf,0x6d,0x7a}, {0xda,0x95,0x52,0x59}, {0x2d,0xd4,0xbe,0x83}, {0xd3,0x58,0x74,0x21}, {0x29,0x49,0xe0,0x69}, {0x44,0x8e,0xc9,0xc8}, {0x6a,0x75,0xc2,0x89}, {0x78,0xf4,0x8e,0x79}, {0x6b,0x99,0x58,0x3e}, {0xdd,0x27,0xb9,0x71}, {0xb6,0xbe,0xe1,0x4f}, {0x17,0xf0,0x88,0xad}, {0x66,0xc9,0x20,0xac}, {0xb4,0x7d,0xce,0x3a}, {0x18,0x63,0xdf,0x4a}, {0x82,0xe5,0x1a,0x31}, {0x60,0x97,0x51,0x33}, {0x45,0x62,0x53,0x7f}, {0xe0,0xb1,0x64,0x77}, {0x84,0xbb,0x6b,0xae}, {0x1c,0xfe,0x81,0xa0}, {0x94,0xf9,0x08,0x2b}, {0x58,0x70,0x48,0x68}, {0x19,0x8f,0x45,0xfd}, {0x87,0x94,0xde,0x6c}, {0xb7,0x52,0x7b,0xf8}, {0x23,0xab,0x73,0xd3}, {0xe2,0x72,0x4b,0x02}, {0x57,0xe3,0x1f,0x8f}, {0x2a,0x66,0x55,0xab}, {0x07,0xb2,0xeb,0x28}, {0x03,0x2f,0xb5,0xc2}, {0x9a,0x86,0xc5,0x7b}, {0xa5,0xd3,0x37,0x08}, {0xf2,0x30,0x28,0x87}, {0xb2,0x23,0xbf,0xa5}, {0xba,0x02,0x03,0x6a}, {0x5c,0xed,0x16,0x82}, {0x2b,0x8a,0xcf,0x1c}, {0x92,0xa7,0x79,0xb4}, {0xf0,0xf3,0x07,0xf2}, {0xa1,0x4e,0x69,0xe2}, {0xcd,0x65,0xda,0xf4}, {0xd5,0x06,0x05,0xbe}, {0x1f,0xd1,0x34,0x62}, {0x8a,0xc4,0xa6,0xfe}, {0x9d,0x34,0x2e,0x53}, {0xa0,0xa2,0xf3,0x55}, {0x32,0x05,0x8a,0xe1}, {0x75,0xa4,0xf6,0xeb}, {0x39,0x0b,0x83,0xec}, {0xaa,0x40,0x60,0xef}, {0x06,0x5e,0x71,0x9f}, {0x51,0xbd,0x6e,0x10}, {0xf9,0x3e,0x21,0x8a}, {0x3d,0x96,0xdd,0x06}, {0xae,0xdd,0x3e,0x05}, {0x46,0x4d,0xe6,0xbd}, {0xb5,0x91,0x54,0x8d}, {0x05,0x71,0xc4,0x5d}, {0x6f,0x04,0x06,0xd4}, {0xff,0x60,0x50,0x15}, {0x24,0x19,0x98,0xfb}, {0x97,0xd6,0xbd,0xe9}, {0xcc,0x89,0x40,0x43}, {0x77,0x67,0xd9,0x9e}, {0xbd,0xb0,0xe8,0x42}, {0x88,0x07,0x89,0x8b}, {0x38,0xe7,0x19,0x5b}, {0xdb,0x79,0xc8,0xee}, {0x47,0xa1,0x7c,0x0a}, {0xe9,0x7c,0x42,0x0f}, {0xc9,0xf8,0x84,0x1e}, {0x00,0x00,0x00,0x00}, {0x83,0x09,0x80,0x86}, {0x48,0x32,0x2b,0xed}, {0xac,0x1e,0x11,0x70}, {0x4e,0x6c,0x5a,0x72}, {0xfb,0xfd,0x0e,0xff}, {0x56,0x0f,0x85,0x38}, {0x1e,0x3d,0xae,0xd5}, {0x27,0x36,0x2d,0x39}, {0x64,0x0a,0x0f,0xd9}, {0x21,0x68,0x5c,0xa6}, {0xd1,0x9b,0x5b,0x54}, {0x3a,0x24,0x36,0x2e}, {0xb1,0x0c,0x0a,0x67}, {0x0f,0x93,0x57,0xe7}, {0xd2,0xb4,0xee,0x96}, {0x9e,0x1b,0x9b,0x91}, {0x4f,0x80,0xc0,0xc5}, {0xa2,0x61,0xdc,0x20}, {0x69,0x5a,0x77,0x4b}, {0x16,0x1c,0x12,0x1a}, {0x0a,0xe2,0x93,0xba}, {0xe5,0xc0,0xa0,0x2a}, {0x43,0x3c,0x22,0xe0}, {0x1d,0x12,0x1b,0x17}, {0x0b,0x0e,0x09,0x0d}, {0xad,0xf2,0x8b,0xc7}, {0xb9,0x2d,0xb6,0xa8}, {0xc8,0x14,0x1e,0xa9}, {0x85,0x57,0xf1,0x19}, {0x4c,0xaf,0x75,0x07}, {0xbb,0xee,0x99,0xdd}, {0xfd,0xa3,0x7f,0x60}, {0x9f,0xf7,0x01,0x26}, {0xbc,0x5c,0x72,0xf5}, {0xc5,0x44,0x66,0x3b}, {0x34,0x5b,0xfb,0x7e}, {0x76,0x8b,0x43,0x29}, {0xdc,0xcb,0x23,0xc6}, {0x68,0xb6,0xed,0xfc}, {0x63,0xb8,0xe4,0xf1}, {0xca,0xd7,0x31,0xdc}, {0x10,0x42,0x63,0x85}, {0x40,0x13,0x97,0x22}, {0x20,0x84,0xc6,0x11}, {0x7d,0x85,0x4a,0x24}, {0xf8,0xd2,0xbb,0x3d}, {0x11,0xae,0xf9,0x32}, {0x6d,0xc7,0x29,0xa1}, {0x4b,0x1d,0x9e,0x2f}, {0xf3,0xdc,0xb2,0x30}, {0xec,0x0d,0x86,0x52}, {0xd0,0x77,0xc1,0xe3}, {0x6c,0x2b,0xb3,0x16}, {0x99,0xa9,0x70,0xb9}, {0xfa,0x11,0x94,0x48}, {0x22,0x47,0xe9,0x64}, {0xc4,0xa8,0xfc,0x8c}, {0x1a,0xa0,0xf0,0x3f}, {0xd8,0x56,0x7d,0x2c}, {0xef,0x22,0x33,0x90}, {0xc7,0x87,0x49,0x4e}, {0xc1,0xd9,0x38,0xd1}, {0xfe,0x8c,0xca,0xa2}, {0x36,0x98,0xd4,0x0b}, {0xcf,0xa6,0xf5,0x81}, {0x28,0xa5,0x7a,0xde}, {0x26,0xda,0xb7,0x8e}, {0xa4,0x3f,0xad,0xbf}, {0xe4,0x2c,0x3a,0x9d}, {0x0d,0x50,0x78,0x92}, {0x9b,0x6a,0x5f,0xcc}, {0x62,0x54,0x7e,0x46}, {0xc2,0xf6,0x8d,0x13}, {0xe8,0x90,0xd8,0xb8}, {0x5e,0x2e,0x39,0xf7}, {0xf5,0x82,0xc3,0xaf}, {0xbe,0x9f,0x5d,0x80}, {0x7c,0x69,0xd0,0x93}, {0xa9,0x6f,0xd5,0x2d}, {0xb3,0xcf,0x25,0x12}, {0x3b,0xc8,0xac,0x99}, {0xa7,0x10,0x18,0x7d}, {0x6e,0xe8,0x9c,0x63}, {0x7b,0xdb,0x3b,0xbb}, {0x09,0xcd,0x26,0x78}, {0xf4,0x6e,0x59,0x18}, {0x01,0xec,0x9a,0xb7}, {0xa8,0x83,0x4f,0x9a}, {0x65,0xe6,0x95,0x6e}, {0x7e,0xaa,0xff,0xe6}, {0x08,0x21,0xbc,0xcf}, {0xe6,0xef,0x15,0xe8}, {0xd9,0xba,0xe7,0x9b}, {0xce,0x4a,0x6f,0x36}, {0xd4,0xea,0x9f,0x09}, {0xd6,0x29,0xb0,0x7c}, {0xaf,0x31,0xa4,0xb2}, {0x31,0x2a,0x3f,0x23}, {0x30,0xc6,0xa5,0x94}, {0xc0,0x35,0xa2,0x66}, {0x37,0x74,0x4e,0xbc}, {0xa6,0xfc,0x82,0xca}, {0xb0,0xe0,0x90,0xd0}, {0x15,0x33,0xa7,0xd8}, {0x4a,0xf1,0x04,0x98}, {0xf7,0x41,0xec,0xda}, {0x0e,0x7f,0xcd,0x50}, {0x2f,0x17,0x91,0xf6}, {0x8d,0x76,0x4d,0xd6}, {0x4d,0x43,0xef,0xb0}, {0x54,0xcc,0xaa,0x4d}, {0xdf,0xe4,0x96,0x04}, {0xe3,0x9e,0xd1,0xb5}, {0x1b,0x4c,0x6a,0x88}, {0xb8,0xc1,0x2c,0x1f}, {0x7f,0x46,0x65,0x51}, {0x04,0x9d,0x5e,0xea}, {0x5d,0x01,0x8c,0x35}, {0x73,0xfa,0x87,0x74}, {0x2e,0xfb,0x0b,0x41}, {0x5a,0xb3,0x67,0x1d}, {0x52,0x92,0xdb,0xd2}, {0x33,0xe9,0x10,0x56}, {0x13,0x6d,0xd6,0x47}, {0x8c,0x9a,0xd7,0x61}, {0x7a,0x37,0xa1,0x0c}, {0x8e,0x59,0xf8,0x14}, {0x89,0xeb,0x13,0x3c}, {0xee,0xce,0xa9,0x27}, {0x35,0xb7,0x61,0xc9}, {0xed,0xe1,0x1c,0xe5}, {0x3c,0x7a,0x47,0xb1}, {0x59,0x9c,0xd2,0xdf}, {0x3f,0x55,0xf2,0x73}, {0x79,0x18,0x14,0xce}, {0xbf,0x73,0xc7,0x37}, {0xea,0x53,0xf7,0xcd}, {0x5b,0x5f,0xfd,0xaa}, {0x14,0xdf,0x3d,0x6f}, {0x86,0x78,0x44,0xdb}, {0x81,0xca,0xaf,0xf3}, {0x3e,0xb9,0x68,0xc4}, {0x2c,0x38,0x24,0x34}, {0x5f,0xc2,0xa3,0x40}, {0x72,0x16,0x1d,0xc3}, {0x0c,0xbc,0xe2,0x25}, {0x8b,0x28,0x3c,0x49}, {0x41,0xff,0x0d,0x95}, {0x71,0x39,0xa8,0x01}, {0xde,0x08,0x0c,0xb3}, {0x9c,0xd8,0xb4,0xe4}, {0x90,0x64,0x56,0xc1}, {0x61,0x7b,0xcb,0x84}, {0x70,0xd5,0x32,0xb6}, {0x74,0x48,0x6c,0x5c}, {0x42,0xd0,0xb8,0x57} }; word8 T7[256][4] = { {0xa7,0x50,0x51,0xf4}, {0x65,0x53,0x7e,0x41}, {0xa4,0xc3,0x1a,0x17}, {0x5e,0x96,0x3a,0x27}, {0x6b,0xcb,0x3b,0xab}, {0x45,0xf1,0x1f,0x9d}, {0x58,0xab,0xac,0xfa}, {0x03,0x93,0x4b,0xe3}, {0xfa,0x55,0x20,0x30}, {0x6d,0xf6,0xad,0x76}, {0x76,0x91,0x88,0xcc}, {0x4c,0x25,0xf5,0x02}, {0xd7,0xfc,0x4f,0xe5}, {0xcb,0xd7,0xc5,0x2a}, {0x44,0x80,0x26,0x35}, {0xa3,0x8f,0xb5,0x62}, {0x5a,0x49,0xde,0xb1}, {0x1b,0x67,0x25,0xba}, {0x0e,0x98,0x45,0xea}, {0xc0,0xe1,0x5d,0xfe}, {0x75,0x02,0xc3,0x2f}, {0xf0,0x12,0x81,0x4c}, {0x97,0xa3,0x8d,0x46}, {0xf9,0xc6,0x6b,0xd3}, {0x5f,0xe7,0x03,0x8f}, {0x9c,0x95,0x15,0x92}, {0x7a,0xeb,0xbf,0x6d}, {0x59,0xda,0x95,0x52}, {0x83,0x2d,0xd4,0xbe}, {0x21,0xd3,0x58,0x74}, {0x69,0x29,0x49,0xe0}, {0xc8,0x44,0x8e,0xc9}, {0x89,0x6a,0x75,0xc2}, {0x79,0x78,0xf4,0x8e}, {0x3e,0x6b,0x99,0x58}, {0x71,0xdd,0x27,0xb9}, {0x4f,0xb6,0xbe,0xe1}, {0xad,0x17,0xf0,0x88}, {0xac,0x66,0xc9,0x20}, {0x3a,0xb4,0x7d,0xce}, {0x4a,0x18,0x63,0xdf}, {0x31,0x82,0xe5,0x1a}, {0x33,0x60,0x97,0x51}, {0x7f,0x45,0x62,0x53}, {0x77,0xe0,0xb1,0x64}, {0xae,0x84,0xbb,0x6b}, {0xa0,0x1c,0xfe,0x81}, {0x2b,0x94,0xf9,0x08}, {0x68,0x58,0x70,0x48}, {0xfd,0x19,0x8f,0x45}, {0x6c,0x87,0x94,0xde}, {0xf8,0xb7,0x52,0x7b}, {0xd3,0x23,0xab,0x73}, {0x02,0xe2,0x72,0x4b}, {0x8f,0x57,0xe3,0x1f}, {0xab,0x2a,0x66,0x55}, {0x28,0x07,0xb2,0xeb}, {0xc2,0x03,0x2f,0xb5}, {0x7b,0x9a,0x86,0xc5}, {0x08,0xa5,0xd3,0x37}, {0x87,0xf2,0x30,0x28}, {0xa5,0xb2,0x23,0xbf}, {0x6a,0xba,0x02,0x03}, {0x82,0x5c,0xed,0x16}, {0x1c,0x2b,0x8a,0xcf}, {0xb4,0x92,0xa7,0x79}, {0xf2,0xf0,0xf3,0x07}, {0xe2,0xa1,0x4e,0x69}, {0xf4,0xcd,0x65,0xda}, {0xbe,0xd5,0x06,0x05}, {0x62,0x1f,0xd1,0x34}, {0xfe,0x8a,0xc4,0xa6}, {0x53,0x9d,0x34,0x2e}, {0x55,0xa0,0xa2,0xf3}, {0xe1,0x32,0x05,0x8a}, {0xeb,0x75,0xa4,0xf6}, {0xec,0x39,0x0b,0x83}, {0xef,0xaa,0x40,0x60}, {0x9f,0x06,0x5e,0x71}, {0x10,0x51,0xbd,0x6e}, {0x8a,0xf9,0x3e,0x21}, {0x06,0x3d,0x96,0xdd}, {0x05,0xae,0xdd,0x3e}, {0xbd,0x46,0x4d,0xe6}, {0x8d,0xb5,0x91,0x54}, {0x5d,0x05,0x71,0xc4}, {0xd4,0x6f,0x04,0x06}, {0x15,0xff,0x60,0x50}, {0xfb,0x24,0x19,0x98}, {0xe9,0x97,0xd6,0xbd}, {0x43,0xcc,0x89,0x40}, {0x9e,0x77,0x67,0xd9}, {0x42,0xbd,0xb0,0xe8}, {0x8b,0x88,0x07,0x89}, {0x5b,0x38,0xe7,0x19}, {0xee,0xdb,0x79,0xc8}, {0x0a,0x47,0xa1,0x7c}, {0x0f,0xe9,0x7c,0x42}, {0x1e,0xc9,0xf8,0x84}, {0x00,0x00,0x00,0x00}, {0x86,0x83,0x09,0x80}, {0xed,0x48,0x32,0x2b}, {0x70,0xac,0x1e,0x11}, {0x72,0x4e,0x6c,0x5a}, {0xff,0xfb,0xfd,0x0e}, {0x38,0x56,0x0f,0x85}, {0xd5,0x1e,0x3d,0xae}, {0x39,0x27,0x36,0x2d}, {0xd9,0x64,0x0a,0x0f}, {0xa6,0x21,0x68,0x5c}, {0x54,0xd1,0x9b,0x5b}, {0x2e,0x3a,0x24,0x36}, {0x67,0xb1,0x0c,0x0a}, {0xe7,0x0f,0x93,0x57}, {0x96,0xd2,0xb4,0xee}, {0x91,0x9e,0x1b,0x9b}, {0xc5,0x4f,0x80,0xc0}, {0x20,0xa2,0x61,0xdc}, {0x4b,0x69,0x5a,0x77}, {0x1a,0x16,0x1c,0x12}, {0xba,0x0a,0xe2,0x93}, {0x2a,0xe5,0xc0,0xa0}, {0xe0,0x43,0x3c,0x22}, {0x17,0x1d,0x12,0x1b}, {0x0d,0x0b,0x0e,0x09}, {0xc7,0xad,0xf2,0x8b}, {0xa8,0xb9,0x2d,0xb6}, {0xa9,0xc8,0x14,0x1e}, {0x19,0x85,0x57,0xf1}, {0x07,0x4c,0xaf,0x75}, {0xdd,0xbb,0xee,0x99}, {0x60,0xfd,0xa3,0x7f}, {0x26,0x9f,0xf7,0x01}, {0xf5,0xbc,0x5c,0x72}, {0x3b,0xc5,0x44,0x66}, {0x7e,0x34,0x5b,0xfb}, {0x29,0x76,0x8b,0x43}, {0xc6,0xdc,0xcb,0x23}, {0xfc,0x68,0xb6,0xed}, {0xf1,0x63,0xb8,0xe4}, {0xdc,0xca,0xd7,0x31}, {0x85,0x10,0x42,0x63}, {0x22,0x40,0x13,0x97}, {0x11,0x20,0x84,0xc6}, {0x24,0x7d,0x85,0x4a}, {0x3d,0xf8,0xd2,0xbb}, {0x32,0x11,0xae,0xf9}, {0xa1,0x6d,0xc7,0x29}, {0x2f,0x4b,0x1d,0x9e}, {0x30,0xf3,0xdc,0xb2}, {0x52,0xec,0x0d,0x86}, {0xe3,0xd0,0x77,0xc1}, {0x16,0x6c,0x2b,0xb3}, {0xb9,0x99,0xa9,0x70}, {0x48,0xfa,0x11,0x94}, {0x64,0x22,0x47,0xe9}, {0x8c,0xc4,0xa8,0xfc}, {0x3f,0x1a,0xa0,0xf0}, {0x2c,0xd8,0x56,0x7d}, {0x90,0xef,0x22,0x33}, {0x4e,0xc7,0x87,0x49}, {0xd1,0xc1,0xd9,0x38}, {0xa2,0xfe,0x8c,0xca}, {0x0b,0x36,0x98,0xd4}, {0x81,0xcf,0xa6,0xf5}, {0xde,0x28,0xa5,0x7a}, {0x8e,0x26,0xda,0xb7}, {0xbf,0xa4,0x3f,0xad}, {0x9d,0xe4,0x2c,0x3a}, {0x92,0x0d,0x50,0x78}, {0xcc,0x9b,0x6a,0x5f}, {0x46,0x62,0x54,0x7e}, {0x13,0xc2,0xf6,0x8d}, {0xb8,0xe8,0x90,0xd8}, {0xf7,0x5e,0x2e,0x39}, {0xaf,0xf5,0x82,0xc3}, {0x80,0xbe,0x9f,0x5d}, {0x93,0x7c,0x69,0xd0}, {0x2d,0xa9,0x6f,0xd5}, {0x12,0xb3,0xcf,0x25}, {0x99,0x3b,0xc8,0xac}, {0x7d,0xa7,0x10,0x18}, {0x63,0x6e,0xe8,0x9c}, {0xbb,0x7b,0xdb,0x3b}, {0x78,0x09,0xcd,0x26}, {0x18,0xf4,0x6e,0x59}, {0xb7,0x01,0xec,0x9a}, {0x9a,0xa8,0x83,0x4f}, {0x6e,0x65,0xe6,0x95}, {0xe6,0x7e,0xaa,0xff}, {0xcf,0x08,0x21,0xbc}, {0xe8,0xe6,0xef,0x15}, {0x9b,0xd9,0xba,0xe7}, {0x36,0xce,0x4a,0x6f}, {0x09,0xd4,0xea,0x9f}, {0x7c,0xd6,0x29,0xb0}, {0xb2,0xaf,0x31,0xa4}, {0x23,0x31,0x2a,0x3f}, {0x94,0x30,0xc6,0xa5}, {0x66,0xc0,0x35,0xa2}, {0xbc,0x37,0x74,0x4e}, {0xca,0xa6,0xfc,0x82}, {0xd0,0xb0,0xe0,0x90}, {0xd8,0x15,0x33,0xa7}, {0x98,0x4a,0xf1,0x04}, {0xda,0xf7,0x41,0xec}, {0x50,0x0e,0x7f,0xcd}, {0xf6,0x2f,0x17,0x91}, {0xd6,0x8d,0x76,0x4d}, {0xb0,0x4d,0x43,0xef}, {0x4d,0x54,0xcc,0xaa}, {0x04,0xdf,0xe4,0x96}, {0xb5,0xe3,0x9e,0xd1}, {0x88,0x1b,0x4c,0x6a}, {0x1f,0xb8,0xc1,0x2c}, {0x51,0x7f,0x46,0x65}, {0xea,0x04,0x9d,0x5e}, {0x35,0x5d,0x01,0x8c}, {0x74,0x73,0xfa,0x87}, {0x41,0x2e,0xfb,0x0b}, {0x1d,0x5a,0xb3,0x67}, {0xd2,0x52,0x92,0xdb}, {0x56,0x33,0xe9,0x10}, {0x47,0x13,0x6d,0xd6}, {0x61,0x8c,0x9a,0xd7}, {0x0c,0x7a,0x37,0xa1}, {0x14,0x8e,0x59,0xf8}, {0x3c,0x89,0xeb,0x13}, {0x27,0xee,0xce,0xa9}, {0xc9,0x35,0xb7,0x61}, {0xe5,0xed,0xe1,0x1c}, {0xb1,0x3c,0x7a,0x47}, {0xdf,0x59,0x9c,0xd2}, {0x73,0x3f,0x55,0xf2}, {0xce,0x79,0x18,0x14}, {0x37,0xbf,0x73,0xc7}, {0xcd,0xea,0x53,0xf7}, {0xaa,0x5b,0x5f,0xfd}, {0x6f,0x14,0xdf,0x3d}, {0xdb,0x86,0x78,0x44}, {0xf3,0x81,0xca,0xaf}, {0xc4,0x3e,0xb9,0x68}, {0x34,0x2c,0x38,0x24}, {0x40,0x5f,0xc2,0xa3}, {0xc3,0x72,0x16,0x1d}, {0x25,0x0c,0xbc,0xe2}, {0x49,0x8b,0x28,0x3c}, {0x95,0x41,0xff,0x0d}, {0x01,0x71,0x39,0xa8}, {0xb3,0xde,0x08,0x0c}, {0xe4,0x9c,0xd8,0xb4}, {0xc1,0x90,0x64,0x56}, {0x84,0x61,0x7b,0xcb}, {0xb6,0x70,0xd5,0x32}, {0x5c,0x74,0x48,0x6c}, {0x57,0x42,0xd0,0xb8} }; word8 T8[256][4] = { {0xf4,0xa7,0x50,0x51}, {0x41,0x65,0x53,0x7e}, {0x17,0xa4,0xc3,0x1a}, {0x27,0x5e,0x96,0x3a}, {0xab,0x6b,0xcb,0x3b}, {0x9d,0x45,0xf1,0x1f}, {0xfa,0x58,0xab,0xac}, {0xe3,0x03,0x93,0x4b}, {0x30,0xfa,0x55,0x20}, {0x76,0x6d,0xf6,0xad}, {0xcc,0x76,0x91,0x88}, {0x02,0x4c,0x25,0xf5}, {0xe5,0xd7,0xfc,0x4f}, {0x2a,0xcb,0xd7,0xc5}, {0x35,0x44,0x80,0x26}, {0x62,0xa3,0x8f,0xb5}, {0xb1,0x5a,0x49,0xde}, {0xba,0x1b,0x67,0x25}, {0xea,0x0e,0x98,0x45}, {0xfe,0xc0,0xe1,0x5d}, {0x2f,0x75,0x02,0xc3}, {0x4c,0xf0,0x12,0x81}, {0x46,0x97,0xa3,0x8d}, {0xd3,0xf9,0xc6,0x6b}, {0x8f,0x5f,0xe7,0x03}, {0x92,0x9c,0x95,0x15}, {0x6d,0x7a,0xeb,0xbf}, {0x52,0x59,0xda,0x95}, {0xbe,0x83,0x2d,0xd4}, {0x74,0x21,0xd3,0x58}, {0xe0,0x69,0x29,0x49}, {0xc9,0xc8,0x44,0x8e}, {0xc2,0x89,0x6a,0x75}, {0x8e,0x79,0x78,0xf4}, {0x58,0x3e,0x6b,0x99}, {0xb9,0x71,0xdd,0x27}, {0xe1,0x4f,0xb6,0xbe}, {0x88,0xad,0x17,0xf0}, {0x20,0xac,0x66,0xc9}, {0xce,0x3a,0xb4,0x7d}, {0xdf,0x4a,0x18,0x63}, {0x1a,0x31,0x82,0xe5}, {0x51,0x33,0x60,0x97}, {0x53,0x7f,0x45,0x62}, {0x64,0x77,0xe0,0xb1}, {0x6b,0xae,0x84,0xbb}, {0x81,0xa0,0x1c,0xfe}, {0x08,0x2b,0x94,0xf9}, {0x48,0x68,0x58,0x70}, {0x45,0xfd,0x19,0x8f}, {0xde,0x6c,0x87,0x94}, {0x7b,0xf8,0xb7,0x52}, {0x73,0xd3,0x23,0xab}, {0x4b,0x02,0xe2,0x72}, {0x1f,0x8f,0x57,0xe3}, {0x55,0xab,0x2a,0x66}, {0xeb,0x28,0x07,0xb2}, {0xb5,0xc2,0x03,0x2f}, {0xc5,0x7b,0x9a,0x86}, {0x37,0x08,0xa5,0xd3}, {0x28,0x87,0xf2,0x30}, {0xbf,0xa5,0xb2,0x23}, {0x03,0x6a,0xba,0x02}, {0x16,0x82,0x5c,0xed}, {0xcf,0x1c,0x2b,0x8a}, {0x79,0xb4,0x92,0xa7}, {0x07,0xf2,0xf0,0xf3}, {0x69,0xe2,0xa1,0x4e}, {0xda,0xf4,0xcd,0x65}, {0x05,0xbe,0xd5,0x06}, {0x34,0x62,0x1f,0xd1}, {0xa6,0xfe,0x8a,0xc4}, {0x2e,0x53,0x9d,0x34}, {0xf3,0x55,0xa0,0xa2}, {0x8a,0xe1,0x32,0x05}, {0xf6,0xeb,0x75,0xa4}, {0x83,0xec,0x39,0x0b}, {0x60,0xef,0xaa,0x40}, {0x71,0x9f,0x06,0x5e}, {0x6e,0x10,0x51,0xbd}, {0x21,0x8a,0xf9,0x3e}, {0xdd,0x06,0x3d,0x96}, {0x3e,0x05,0xae,0xdd}, {0xe6,0xbd,0x46,0x4d}, {0x54,0x8d,0xb5,0x91}, {0xc4,0x5d,0x05,0x71}, {0x06,0xd4,0x6f,0x04}, {0x50,0x15,0xff,0x60}, {0x98,0xfb,0x24,0x19}, {0xbd,0xe9,0x97,0xd6}, {0x40,0x43,0xcc,0x89}, {0xd9,0x9e,0x77,0x67}, {0xe8,0x42,0xbd,0xb0}, {0x89,0x8b,0x88,0x07}, {0x19,0x5b,0x38,0xe7}, {0xc8,0xee,0xdb,0x79}, {0x7c,0x0a,0x47,0xa1}, {0x42,0x0f,0xe9,0x7c}, {0x84,0x1e,0xc9,0xf8}, {0x00,0x00,0x00,0x00}, {0x80,0x86,0x83,0x09}, {0x2b,0xed,0x48,0x32}, {0x11,0x70,0xac,0x1e}, {0x5a,0x72,0x4e,0x6c}, {0x0e,0xff,0xfb,0xfd}, {0x85,0x38,0x56,0x0f}, {0xae,0xd5,0x1e,0x3d}, {0x2d,0x39,0x27,0x36}, {0x0f,0xd9,0x64,0x0a}, {0x5c,0xa6,0x21,0x68}, {0x5b,0x54,0xd1,0x9b}, {0x36,0x2e,0x3a,0x24}, {0x0a,0x67,0xb1,0x0c}, {0x57,0xe7,0x0f,0x93}, {0xee,0x96,0xd2,0xb4}, {0x9b,0x91,0x9e,0x1b}, {0xc0,0xc5,0x4f,0x80}, {0xdc,0x20,0xa2,0x61}, {0x77,0x4b,0x69,0x5a}, {0x12,0x1a,0x16,0x1c}, {0x93,0xba,0x0a,0xe2}, {0xa0,0x2a,0xe5,0xc0}, {0x22,0xe0,0x43,0x3c}, {0x1b,0x17,0x1d,0x12}, {0x09,0x0d,0x0b,0x0e}, {0x8b,0xc7,0xad,0xf2}, {0xb6,0xa8,0xb9,0x2d}, {0x1e,0xa9,0xc8,0x14}, {0xf1,0x19,0x85,0x57}, {0x75,0x07,0x4c,0xaf}, {0x99,0xdd,0xbb,0xee}, {0x7f,0x60,0xfd,0xa3}, {0x01,0x26,0x9f,0xf7}, {0x72,0xf5,0xbc,0x5c}, {0x66,0x3b,0xc5,0x44}, {0xfb,0x7e,0x34,0x5b}, {0x43,0x29,0x76,0x8b}, {0x23,0xc6,0xdc,0xcb}, {0xed,0xfc,0x68,0xb6}, {0xe4,0xf1,0x63,0xb8}, {0x31,0xdc,0xca,0xd7}, {0x63,0x85,0x10,0x42}, {0x97,0x22,0x40,0x13}, {0xc6,0x11,0x20,0x84}, {0x4a,0x24,0x7d,0x85}, {0xbb,0x3d,0xf8,0xd2}, {0xf9,0x32,0x11,0xae}, {0x29,0xa1,0x6d,0xc7}, {0x9e,0x2f,0x4b,0x1d}, {0xb2,0x30,0xf3,0xdc}, {0x86,0x52,0xec,0x0d}, {0xc1,0xe3,0xd0,0x77}, {0xb3,0x16,0x6c,0x2b}, {0x70,0xb9,0x99,0xa9}, {0x94,0x48,0xfa,0x11}, {0xe9,0x64,0x22,0x47}, {0xfc,0x8c,0xc4,0xa8}, {0xf0,0x3f,0x1a,0xa0}, {0x7d,0x2c,0xd8,0x56}, {0x33,0x90,0xef,0x22}, {0x49,0x4e,0xc7,0x87}, {0x38,0xd1,0xc1,0xd9}, {0xca,0xa2,0xfe,0x8c}, {0xd4,0x0b,0x36,0x98}, {0xf5,0x81,0xcf,0xa6}, {0x7a,0xde,0x28,0xa5}, {0xb7,0x8e,0x26,0xda}, {0xad,0xbf,0xa4,0x3f}, {0x3a,0x9d,0xe4,0x2c}, {0x78,0x92,0x0d,0x50}, {0x5f,0xcc,0x9b,0x6a}, {0x7e,0x46,0x62,0x54}, {0x8d,0x13,0xc2,0xf6}, {0xd8,0xb8,0xe8,0x90}, {0x39,0xf7,0x5e,0x2e}, {0xc3,0xaf,0xf5,0x82}, {0x5d,0x80,0xbe,0x9f}, {0xd0,0x93,0x7c,0x69}, {0xd5,0x2d,0xa9,0x6f}, {0x25,0x12,0xb3,0xcf}, {0xac,0x99,0x3b,0xc8}, {0x18,0x7d,0xa7,0x10}, {0x9c,0x63,0x6e,0xe8}, {0x3b,0xbb,0x7b,0xdb}, {0x26,0x78,0x09,0xcd}, {0x59,0x18,0xf4,0x6e}, {0x9a,0xb7,0x01,0xec}, {0x4f,0x9a,0xa8,0x83}, {0x95,0x6e,0x65,0xe6}, {0xff,0xe6,0x7e,0xaa}, {0xbc,0xcf,0x08,0x21}, {0x15,0xe8,0xe6,0xef}, {0xe7,0x9b,0xd9,0xba}, {0x6f,0x36,0xce,0x4a}, {0x9f,0x09,0xd4,0xea}, {0xb0,0x7c,0xd6,0x29}, {0xa4,0xb2,0xaf,0x31}, {0x3f,0x23,0x31,0x2a}, {0xa5,0x94,0x30,0xc6}, {0xa2,0x66,0xc0,0x35}, {0x4e,0xbc,0x37,0x74}, {0x82,0xca,0xa6,0xfc}, {0x90,0xd0,0xb0,0xe0}, {0xa7,0xd8,0x15,0x33}, {0x04,0x98,0x4a,0xf1}, {0xec,0xda,0xf7,0x41}, {0xcd,0x50,0x0e,0x7f}, {0x91,0xf6,0x2f,0x17}, {0x4d,0xd6,0x8d,0x76}, {0xef,0xb0,0x4d,0x43}, {0xaa,0x4d,0x54,0xcc}, {0x96,0x04,0xdf,0xe4}, {0xd1,0xb5,0xe3,0x9e}, {0x6a,0x88,0x1b,0x4c}, {0x2c,0x1f,0xb8,0xc1}, {0x65,0x51,0x7f,0x46}, {0x5e,0xea,0x04,0x9d}, {0x8c,0x35,0x5d,0x01}, {0x87,0x74,0x73,0xfa}, {0x0b,0x41,0x2e,0xfb}, {0x67,0x1d,0x5a,0xb3}, {0xdb,0xd2,0x52,0x92}, {0x10,0x56,0x33,0xe9}, {0xd6,0x47,0x13,0x6d}, {0xd7,0x61,0x8c,0x9a}, {0xa1,0x0c,0x7a,0x37}, {0xf8,0x14,0x8e,0x59}, {0x13,0x3c,0x89,0xeb}, {0xa9,0x27,0xee,0xce}, {0x61,0xc9,0x35,0xb7}, {0x1c,0xe5,0xed,0xe1}, {0x47,0xb1,0x3c,0x7a}, {0xd2,0xdf,0x59,0x9c}, {0xf2,0x73,0x3f,0x55}, {0x14,0xce,0x79,0x18}, {0xc7,0x37,0xbf,0x73}, {0xf7,0xcd,0xea,0x53}, {0xfd,0xaa,0x5b,0x5f}, {0x3d,0x6f,0x14,0xdf}, {0x44,0xdb,0x86,0x78}, {0xaf,0xf3,0x81,0xca}, {0x68,0xc4,0x3e,0xb9}, {0x24,0x34,0x2c,0x38}, {0xa3,0x40,0x5f,0xc2}, {0x1d,0xc3,0x72,0x16}, {0xe2,0x25,0x0c,0xbc}, {0x3c,0x49,0x8b,0x28}, {0x0d,0x95,0x41,0xff}, {0xa8,0x01,0x71,0x39}, {0x0c,0xb3,0xde,0x08}, {0xb4,0xe4,0x9c,0xd8}, {0x56,0xc1,0x90,0x64}, {0xcb,0x84,0x61,0x7b}, {0x32,0xb6,0x70,0xd5}, {0x6c,0x5c,0x74,0x48}, {0xb8,0x57,0x42,0xd0} }; word8 S5[256] = { 0x52,0x09,0x6a,0xd5, 0x30,0x36,0xa5,0x38, 0xbf,0x40,0xa3,0x9e, 0x81,0xf3,0xd7,0xfb, 0x7c,0xe3,0x39,0x82, 0x9b,0x2f,0xff,0x87, 0x34,0x8e,0x43,0x44, 0xc4,0xde,0xe9,0xcb, 0x54,0x7b,0x94,0x32, 0xa6,0xc2,0x23,0x3d, 0xee,0x4c,0x95,0x0b, 0x42,0xfa,0xc3,0x4e, 0x08,0x2e,0xa1,0x66, 0x28,0xd9,0x24,0xb2, 0x76,0x5b,0xa2,0x49, 0x6d,0x8b,0xd1,0x25, 0x72,0xf8,0xf6,0x64, 0x86,0x68,0x98,0x16, 0xd4,0xa4,0x5c,0xcc, 0x5d,0x65,0xb6,0x92, 0x6c,0x70,0x48,0x50, 0xfd,0xed,0xb9,0xda, 0x5e,0x15,0x46,0x57, 0xa7,0x8d,0x9d,0x84, 0x90,0xd8,0xab,0x00, 0x8c,0xbc,0xd3,0x0a, 0xf7,0xe4,0x58,0x05, 0xb8,0xb3,0x45,0x06, 0xd0,0x2c,0x1e,0x8f, 0xca,0x3f,0x0f,0x02, 0xc1,0xaf,0xbd,0x03, 0x01,0x13,0x8a,0x6b, 0x3a,0x91,0x11,0x41, 0x4f,0x67,0xdc,0xea, 0x97,0xf2,0xcf,0xce, 0xf0,0xb4,0xe6,0x73, 0x96,0xac,0x74,0x22, 0xe7,0xad,0x35,0x85, 0xe2,0xf9,0x37,0xe8, 0x1c,0x75,0xdf,0x6e, 0x47,0xf1,0x1a,0x71, 0x1d,0x29,0xc5,0x89, 0x6f,0xb7,0x62,0x0e, 0xaa,0x18,0xbe,0x1b, 0xfc,0x56,0x3e,0x4b, 0xc6,0xd2,0x79,0x20, 0x9a,0xdb,0xc0,0xfe, 0x78,0xcd,0x5a,0xf4, 0x1f,0xdd,0xa8,0x33, 0x88,0x07,0xc7,0x31, 0xb1,0x12,0x10,0x59, 0x27,0x80,0xec,0x5f, 0x60,0x51,0x7f,0xa9, 0x19,0xb5,0x4a,0x0d, 0x2d,0xe5,0x7a,0x9f, 0x93,0xc9,0x9c,0xef, 0xa0,0xe0,0x3b,0x4d, 0xae,0x2a,0xf5,0xb0, 0xc8,0xeb,0xbb,0x3c, 0x83,0x53,0x99,0x61, 0x17,0x2b,0x04,0x7e, 0xba,0x77,0xd6,0x26, 0xe1,0x69,0x14,0x63, 0x55,0x21,0x0c,0x7d }; word8 U1[256][4] = { {0x00,0x00,0x00,0x00}, {0x0e,0x09,0x0d,0x0b}, {0x1c,0x12,0x1a,0x16}, {0x12,0x1b,0x17,0x1d}, {0x38,0x24,0x34,0x2c}, {0x36,0x2d,0x39,0x27}, {0x24,0x36,0x2e,0x3a}, {0x2a,0x3f,0x23,0x31}, {0x70,0x48,0x68,0x58}, {0x7e,0x41,0x65,0x53}, {0x6c,0x5a,0x72,0x4e}, {0x62,0x53,0x7f,0x45}, {0x48,0x6c,0x5c,0x74}, {0x46,0x65,0x51,0x7f}, {0x54,0x7e,0x46,0x62}, {0x5a,0x77,0x4b,0x69}, {0xe0,0x90,0xd0,0xb0}, {0xee,0x99,0xdd,0xbb}, {0xfc,0x82,0xca,0xa6}, {0xf2,0x8b,0xc7,0xad}, {0xd8,0xb4,0xe4,0x9c}, {0xd6,0xbd,0xe9,0x97}, {0xc4,0xa6,0xfe,0x8a}, {0xca,0xaf,0xf3,0x81}, {0x90,0xd8,0xb8,0xe8}, {0x9e,0xd1,0xb5,0xe3}, {0x8c,0xca,0xa2,0xfe}, {0x82,0xc3,0xaf,0xf5}, {0xa8,0xfc,0x8c,0xc4}, {0xa6,0xf5,0x81,0xcf}, {0xb4,0xee,0x96,0xd2}, {0xba,0xe7,0x9b,0xd9}, {0xdb,0x3b,0xbb,0x7b}, {0xd5,0x32,0xb6,0x70}, {0xc7,0x29,0xa1,0x6d}, {0xc9,0x20,0xac,0x66}, {0xe3,0x1f,0x8f,0x57}, {0xed,0x16,0x82,0x5c}, {0xff,0x0d,0x95,0x41}, {0xf1,0x04,0x98,0x4a}, {0xab,0x73,0xd3,0x23}, {0xa5,0x7a,0xde,0x28}, {0xb7,0x61,0xc9,0x35}, {0xb9,0x68,0xc4,0x3e}, {0x93,0x57,0xe7,0x0f}, {0x9d,0x5e,0xea,0x04}, {0x8f,0x45,0xfd,0x19}, {0x81,0x4c,0xf0,0x12}, {0x3b,0xab,0x6b,0xcb}, {0x35,0xa2,0x66,0xc0}, {0x27,0xb9,0x71,0xdd}, {0x29,0xb0,0x7c,0xd6}, {0x03,0x8f,0x5f,0xe7}, {0x0d,0x86,0x52,0xec}, {0x1f,0x9d,0x45,0xf1}, {0x11,0x94,0x48,0xfa}, {0x4b,0xe3,0x03,0x93}, {0x45,0xea,0x0e,0x98}, {0x57,0xf1,0x19,0x85}, {0x59,0xf8,0x14,0x8e}, {0x73,0xc7,0x37,0xbf}, {0x7d,0xce,0x3a,0xb4}, {0x6f,0xd5,0x2d,0xa9}, {0x61,0xdc,0x20,0xa2}, {0xad,0x76,0x6d,0xf6}, {0xa3,0x7f,0x60,0xfd}, {0xb1,0x64,0x77,0xe0}, {0xbf,0x6d,0x7a,0xeb}, {0x95,0x52,0x59,0xda}, {0x9b,0x5b,0x54,0xd1}, {0x89,0x40,0x43,0xcc}, {0x87,0x49,0x4e,0xc7}, {0xdd,0x3e,0x05,0xae}, {0xd3,0x37,0x08,0xa5}, {0xc1,0x2c,0x1f,0xb8}, {0xcf,0x25,0x12,0xb3}, {0xe5,0x1a,0x31,0x82}, {0xeb,0x13,0x3c,0x89}, {0xf9,0x08,0x2b,0x94}, {0xf7,0x01,0x26,0x9f}, {0x4d,0xe6,0xbd,0x46}, {0x43,0xef,0xb0,0x4d}, {0x51,0xf4,0xa7,0x50}, {0x5f,0xfd,0xaa,0x5b}, {0x75,0xc2,0x89,0x6a}, {0x7b,0xcb,0x84,0x61}, {0x69,0xd0,0x93,0x7c}, {0x67,0xd9,0x9e,0x77}, {0x3d,0xae,0xd5,0x1e}, {0x33,0xa7,0xd8,0x15}, {0x21,0xbc,0xcf,0x08}, {0x2f,0xb5,0xc2,0x03}, {0x05,0x8a,0xe1,0x32}, {0x0b,0x83,0xec,0x39}, {0x19,0x98,0xfb,0x24}, {0x17,0x91,0xf6,0x2f}, {0x76,0x4d,0xd6,0x8d}, {0x78,0x44,0xdb,0x86}, {0x6a,0x5f,0xcc,0x9b}, {0x64,0x56,0xc1,0x90}, {0x4e,0x69,0xe2,0xa1}, {0x40,0x60,0xef,0xaa}, {0x52,0x7b,0xf8,0xb7}, {0x5c,0x72,0xf5,0xbc}, {0x06,0x05,0xbe,0xd5}, {0x08,0x0c,0xb3,0xde}, {0x1a,0x17,0xa4,0xc3}, {0x14,0x1e,0xa9,0xc8}, {0x3e,0x21,0x8a,0xf9}, {0x30,0x28,0x87,0xf2}, {0x22,0x33,0x90,0xef}, {0x2c,0x3a,0x9d,0xe4}, {0x96,0xdd,0x06,0x3d}, {0x98,0xd4,0x0b,0x36}, {0x8a,0xcf,0x1c,0x2b}, {0x84,0xc6,0x11,0x20}, {0xae,0xf9,0x32,0x11}, {0xa0,0xf0,0x3f,0x1a}, {0xb2,0xeb,0x28,0x07}, {0xbc,0xe2,0x25,0x0c}, {0xe6,0x95,0x6e,0x65}, {0xe8,0x9c,0x63,0x6e}, {0xfa,0x87,0x74,0x73}, {0xf4,0x8e,0x79,0x78}, {0xde,0xb1,0x5a,0x49}, {0xd0,0xb8,0x57,0x42}, {0xc2,0xa3,0x40,0x5f}, {0xcc,0xaa,0x4d,0x54}, {0x41,0xec,0xda,0xf7}, {0x4f,0xe5,0xd7,0xfc}, {0x5d,0xfe,0xc0,0xe1}, {0x53,0xf7,0xcd,0xea}, {0x79,0xc8,0xee,0xdb}, {0x77,0xc1,0xe3,0xd0}, {0x65,0xda,0xf4,0xcd}, {0x6b,0xd3,0xf9,0xc6}, {0x31,0xa4,0xb2,0xaf}, {0x3f,0xad,0xbf,0xa4}, {0x2d,0xb6,0xa8,0xb9}, {0x23,0xbf,0xa5,0xb2}, {0x09,0x80,0x86,0x83}, {0x07,0x89,0x8b,0x88}, {0x15,0x92,0x9c,0x95}, {0x1b,0x9b,0x91,0x9e}, {0xa1,0x7c,0x0a,0x47}, {0xaf,0x75,0x07,0x4c}, {0xbd,0x6e,0x10,0x51}, {0xb3,0x67,0x1d,0x5a}, {0x99,0x58,0x3e,0x6b}, {0x97,0x51,0x33,0x60}, {0x85,0x4a,0x24,0x7d}, {0x8b,0x43,0x29,0x76}, {0xd1,0x34,0x62,0x1f}, {0xdf,0x3d,0x6f,0x14}, {0xcd,0x26,0x78,0x09}, {0xc3,0x2f,0x75,0x02}, {0xe9,0x10,0x56,0x33}, {0xe7,0x19,0x5b,0x38}, {0xf5,0x02,0x4c,0x25}, {0xfb,0x0b,0x41,0x2e}, {0x9a,0xd7,0x61,0x8c}, {0x94,0xde,0x6c,0x87}, {0x86,0xc5,0x7b,0x9a}, {0x88,0xcc,0x76,0x91}, {0xa2,0xf3,0x55,0xa0}, {0xac,0xfa,0x58,0xab}, {0xbe,0xe1,0x4f,0xb6}, {0xb0,0xe8,0x42,0xbd}, {0xea,0x9f,0x09,0xd4}, {0xe4,0x96,0x04,0xdf}, {0xf6,0x8d,0x13,0xc2}, {0xf8,0x84,0x1e,0xc9}, {0xd2,0xbb,0x3d,0xf8}, {0xdc,0xb2,0x30,0xf3}, {0xce,0xa9,0x27,0xee}, {0xc0,0xa0,0x2a,0xe5}, {0x7a,0x47,0xb1,0x3c}, {0x74,0x4e,0xbc,0x37}, {0x66,0x55,0xab,0x2a}, {0x68,0x5c,0xa6,0x21}, {0x42,0x63,0x85,0x10}, {0x4c,0x6a,0x88,0x1b}, {0x5e,0x71,0x9f,0x06}, {0x50,0x78,0x92,0x0d}, {0x0a,0x0f,0xd9,0x64}, {0x04,0x06,0xd4,0x6f}, {0x16,0x1d,0xc3,0x72}, {0x18,0x14,0xce,0x79}, {0x32,0x2b,0xed,0x48}, {0x3c,0x22,0xe0,0x43}, {0x2e,0x39,0xf7,0x5e}, {0x20,0x30,0xfa,0x55}, {0xec,0x9a,0xb7,0x01}, {0xe2,0x93,0xba,0x0a}, {0xf0,0x88,0xad,0x17}, {0xfe,0x81,0xa0,0x1c}, {0xd4,0xbe,0x83,0x2d}, {0xda,0xb7,0x8e,0x26}, {0xc8,0xac,0x99,0x3b}, {0xc6,0xa5,0x94,0x30}, {0x9c,0xd2,0xdf,0x59}, {0x92,0xdb,0xd2,0x52}, {0x80,0xc0,0xc5,0x4f}, {0x8e,0xc9,0xc8,0x44}, {0xa4,0xf6,0xeb,0x75}, {0xaa,0xff,0xe6,0x7e}, {0xb8,0xe4,0xf1,0x63}, {0xb6,0xed,0xfc,0x68}, {0x0c,0x0a,0x67,0xb1}, {0x02,0x03,0x6a,0xba}, {0x10,0x18,0x7d,0xa7}, {0x1e,0x11,0x70,0xac}, {0x34,0x2e,0x53,0x9d}, {0x3a,0x27,0x5e,0x96}, {0x28,0x3c,0x49,0x8b}, {0x26,0x35,0x44,0x80}, {0x7c,0x42,0x0f,0xe9}, {0x72,0x4b,0x02,0xe2}, {0x60,0x50,0x15,0xff}, {0x6e,0x59,0x18,0xf4}, {0x44,0x66,0x3b,0xc5}, {0x4a,0x6f,0x36,0xce}, {0x58,0x74,0x21,0xd3}, {0x56,0x7d,0x2c,0xd8}, {0x37,0xa1,0x0c,0x7a}, {0x39,0xa8,0x01,0x71}, {0x2b,0xb3,0x16,0x6c}, {0x25,0xba,0x1b,0x67}, {0x0f,0x85,0x38,0x56}, {0x01,0x8c,0x35,0x5d}, {0x13,0x97,0x22,0x40}, {0x1d,0x9e,0x2f,0x4b}, {0x47,0xe9,0x64,0x22}, {0x49,0xe0,0x69,0x29}, {0x5b,0xfb,0x7e,0x34}, {0x55,0xf2,0x73,0x3f}, {0x7f,0xcd,0x50,0x0e}, {0x71,0xc4,0x5d,0x05}, {0x63,0xdf,0x4a,0x18}, {0x6d,0xd6,0x47,0x13}, {0xd7,0x31,0xdc,0xca}, {0xd9,0x38,0xd1,0xc1}, {0xcb,0x23,0xc6,0xdc}, {0xc5,0x2a,0xcb,0xd7}, {0xef,0x15,0xe8,0xe6}, {0xe1,0x1c,0xe5,0xed}, {0xf3,0x07,0xf2,0xf0}, {0xfd,0x0e,0xff,0xfb}, {0xa7,0x79,0xb4,0x92}, {0xa9,0x70,0xb9,0x99}, {0xbb,0x6b,0xae,0x84}, {0xb5,0x62,0xa3,0x8f}, {0x9f,0x5d,0x80,0xbe}, {0x91,0x54,0x8d,0xb5}, {0x83,0x4f,0x9a,0xa8}, {0x8d,0x46,0x97,0xa3} }; word8 U2[256][4] = { {0x00,0x00,0x00,0x00}, {0x0b,0x0e,0x09,0x0d}, {0x16,0x1c,0x12,0x1a}, {0x1d,0x12,0x1b,0x17}, {0x2c,0x38,0x24,0x34}, {0x27,0x36,0x2d,0x39}, {0x3a,0x24,0x36,0x2e}, {0x31,0x2a,0x3f,0x23}, {0x58,0x70,0x48,0x68}, {0x53,0x7e,0x41,0x65}, {0x4e,0x6c,0x5a,0x72}, {0x45,0x62,0x53,0x7f}, {0x74,0x48,0x6c,0x5c}, {0x7f,0x46,0x65,0x51}, {0x62,0x54,0x7e,0x46}, {0x69,0x5a,0x77,0x4b}, {0xb0,0xe0,0x90,0xd0}, {0xbb,0xee,0x99,0xdd}, {0xa6,0xfc,0x82,0xca}, {0xad,0xf2,0x8b,0xc7}, {0x9c,0xd8,0xb4,0xe4}, {0x97,0xd6,0xbd,0xe9}, {0x8a,0xc4,0xa6,0xfe}, {0x81,0xca,0xaf,0xf3}, {0xe8,0x90,0xd8,0xb8}, {0xe3,0x9e,0xd1,0xb5}, {0xfe,0x8c,0xca,0xa2}, {0xf5,0x82,0xc3,0xaf}, {0xc4,0xa8,0xfc,0x8c}, {0xcf,0xa6,0xf5,0x81}, {0xd2,0xb4,0xee,0x96}, {0xd9,0xba,0xe7,0x9b}, {0x7b,0xdb,0x3b,0xbb}, {0x70,0xd5,0x32,0xb6}, {0x6d,0xc7,0x29,0xa1}, {0x66,0xc9,0x20,0xac}, {0x57,0xe3,0x1f,0x8f}, {0x5c,0xed,0x16,0x82}, {0x41,0xff,0x0d,0x95}, {0x4a,0xf1,0x04,0x98}, {0x23,0xab,0x73,0xd3}, {0x28,0xa5,0x7a,0xde}, {0x35,0xb7,0x61,0xc9}, {0x3e,0xb9,0x68,0xc4}, {0x0f,0x93,0x57,0xe7}, {0x04,0x9d,0x5e,0xea}, {0x19,0x8f,0x45,0xfd}, {0x12,0x81,0x4c,0xf0}, {0xcb,0x3b,0xab,0x6b}, {0xc0,0x35,0xa2,0x66}, {0xdd,0x27,0xb9,0x71}, {0xd6,0x29,0xb0,0x7c}, {0xe7,0x03,0x8f,0x5f}, {0xec,0x0d,0x86,0x52}, {0xf1,0x1f,0x9d,0x45}, {0xfa,0x11,0x94,0x48}, {0x93,0x4b,0xe3,0x03}, {0x98,0x45,0xea,0x0e}, {0x85,0x57,0xf1,0x19}, {0x8e,0x59,0xf8,0x14}, {0xbf,0x73,0xc7,0x37}, {0xb4,0x7d,0xce,0x3a}, {0xa9,0x6f,0xd5,0x2d}, {0xa2,0x61,0xdc,0x20}, {0xf6,0xad,0x76,0x6d}, {0xfd,0xa3,0x7f,0x60}, {0xe0,0xb1,0x64,0x77}, {0xeb,0xbf,0x6d,0x7a}, {0xda,0x95,0x52,0x59}, {0xd1,0x9b,0x5b,0x54}, {0xcc,0x89,0x40,0x43}, {0xc7,0x87,0x49,0x4e}, {0xae,0xdd,0x3e,0x05}, {0xa5,0xd3,0x37,0x08}, {0xb8,0xc1,0x2c,0x1f}, {0xb3,0xcf,0x25,0x12}, {0x82,0xe5,0x1a,0x31}, {0x89,0xeb,0x13,0x3c}, {0x94,0xf9,0x08,0x2b}, {0x9f,0xf7,0x01,0x26}, {0x46,0x4d,0xe6,0xbd}, {0x4d,0x43,0xef,0xb0}, {0x50,0x51,0xf4,0xa7}, {0x5b,0x5f,0xfd,0xaa}, {0x6a,0x75,0xc2,0x89}, {0x61,0x7b,0xcb,0x84}, {0x7c,0x69,0xd0,0x93}, {0x77,0x67,0xd9,0x9e}, {0x1e,0x3d,0xae,0xd5}, {0x15,0x33,0xa7,0xd8}, {0x08,0x21,0xbc,0xcf}, {0x03,0x2f,0xb5,0xc2}, {0x32,0x05,0x8a,0xe1}, {0x39,0x0b,0x83,0xec}, {0x24,0x19,0x98,0xfb}, {0x2f,0x17,0x91,0xf6}, {0x8d,0x76,0x4d,0xd6}, {0x86,0x78,0x44,0xdb}, {0x9b,0x6a,0x5f,0xcc}, {0x90,0x64,0x56,0xc1}, {0xa1,0x4e,0x69,0xe2}, {0xaa,0x40,0x60,0xef}, {0xb7,0x52,0x7b,0xf8}, {0xbc,0x5c,0x72,0xf5}, {0xd5,0x06,0x05,0xbe}, {0xde,0x08,0x0c,0xb3}, {0xc3,0x1a,0x17,0xa4}, {0xc8,0x14,0x1e,0xa9}, {0xf9,0x3e,0x21,0x8a}, {0xf2,0x30,0x28,0x87}, {0xef,0x22,0x33,0x90}, {0xe4,0x2c,0x3a,0x9d}, {0x3d,0x96,0xdd,0x06}, {0x36,0x98,0xd4,0x0b}, {0x2b,0x8a,0xcf,0x1c}, {0x20,0x84,0xc6,0x11}, {0x11,0xae,0xf9,0x32}, {0x1a,0xa0,0xf0,0x3f}, {0x07,0xb2,0xeb,0x28}, {0x0c,0xbc,0xe2,0x25}, {0x65,0xe6,0x95,0x6e}, {0x6e,0xe8,0x9c,0x63}, {0x73,0xfa,0x87,0x74}, {0x78,0xf4,0x8e,0x79}, {0x49,0xde,0xb1,0x5a}, {0x42,0xd0,0xb8,0x57}, {0x5f,0xc2,0xa3,0x40}, {0x54,0xcc,0xaa,0x4d}, {0xf7,0x41,0xec,0xda}, {0xfc,0x4f,0xe5,0xd7}, {0xe1,0x5d,0xfe,0xc0}, {0xea,0x53,0xf7,0xcd}, {0xdb,0x79,0xc8,0xee}, {0xd0,0x77,0xc1,0xe3}, {0xcd,0x65,0xda,0xf4}, {0xc6,0x6b,0xd3,0xf9}, {0xaf,0x31,0xa4,0xb2}, {0xa4,0x3f,0xad,0xbf}, {0xb9,0x2d,0xb6,0xa8}, {0xb2,0x23,0xbf,0xa5}, {0x83,0x09,0x80,0x86}, {0x88,0x07,0x89,0x8b}, {0x95,0x15,0x92,0x9c}, {0x9e,0x1b,0x9b,0x91}, {0x47,0xa1,0x7c,0x0a}, {0x4c,0xaf,0x75,0x07}, {0x51,0xbd,0x6e,0x10}, {0x5a,0xb3,0x67,0x1d}, {0x6b,0x99,0x58,0x3e}, {0x60,0x97,0x51,0x33}, {0x7d,0x85,0x4a,0x24}, {0x76,0x8b,0x43,0x29}, {0x1f,0xd1,0x34,0x62}, {0x14,0xdf,0x3d,0x6f}, {0x09,0xcd,0x26,0x78}, {0x02,0xc3,0x2f,0x75}, {0x33,0xe9,0x10,0x56}, {0x38,0xe7,0x19,0x5b}, {0x25,0xf5,0x02,0x4c}, {0x2e,0xfb,0x0b,0x41}, {0x8c,0x9a,0xd7,0x61}, {0x87,0x94,0xde,0x6c}, {0x9a,0x86,0xc5,0x7b}, {0x91,0x88,0xcc,0x76}, {0xa0,0xa2,0xf3,0x55}, {0xab,0xac,0xfa,0x58}, {0xb6,0xbe,0xe1,0x4f}, {0xbd,0xb0,0xe8,0x42}, {0xd4,0xea,0x9f,0x09}, {0xdf,0xe4,0x96,0x04}, {0xc2,0xf6,0x8d,0x13}, {0xc9,0xf8,0x84,0x1e}, {0xf8,0xd2,0xbb,0x3d}, {0xf3,0xdc,0xb2,0x30}, {0xee,0xce,0xa9,0x27}, {0xe5,0xc0,0xa0,0x2a}, {0x3c,0x7a,0x47,0xb1}, {0x37,0x74,0x4e,0xbc}, {0x2a,0x66,0x55,0xab}, {0x21,0x68,0x5c,0xa6}, {0x10,0x42,0x63,0x85}, {0x1b,0x4c,0x6a,0x88}, {0x06,0x5e,0x71,0x9f}, {0x0d,0x50,0x78,0x92}, {0x64,0x0a,0x0f,0xd9}, {0x6f,0x04,0x06,0xd4}, {0x72,0x16,0x1d,0xc3}, {0x79,0x18,0x14,0xce}, {0x48,0x32,0x2b,0xed}, {0x43,0x3c,0x22,0xe0}, {0x5e,0x2e,0x39,0xf7}, {0x55,0x20,0x30,0xfa}, {0x01,0xec,0x9a,0xb7}, {0x0a,0xe2,0x93,0xba}, {0x17,0xf0,0x88,0xad}, {0x1c,0xfe,0x81,0xa0}, {0x2d,0xd4,0xbe,0x83}, {0x26,0xda,0xb7,0x8e}, {0x3b,0xc8,0xac,0x99}, {0x30,0xc6,0xa5,0x94}, {0x59,0x9c,0xd2,0xdf}, {0x52,0x92,0xdb,0xd2}, {0x4f,0x80,0xc0,0xc5}, {0x44,0x8e,0xc9,0xc8}, {0x75,0xa4,0xf6,0xeb}, {0x7e,0xaa,0xff,0xe6}, {0x63,0xb8,0xe4,0xf1}, {0x68,0xb6,0xed,0xfc}, {0xb1,0x0c,0x0a,0x67}, {0xba,0x02,0x03,0x6a}, {0xa7,0x10,0x18,0x7d}, {0xac,0x1e,0x11,0x70}, {0x9d,0x34,0x2e,0x53}, {0x96,0x3a,0x27,0x5e}, {0x8b,0x28,0x3c,0x49}, {0x80,0x26,0x35,0x44}, {0xe9,0x7c,0x42,0x0f}, {0xe2,0x72,0x4b,0x02}, {0xff,0x60,0x50,0x15}, {0xf4,0x6e,0x59,0x18}, {0xc5,0x44,0x66,0x3b}, {0xce,0x4a,0x6f,0x36}, {0xd3,0x58,0x74,0x21}, {0xd8,0x56,0x7d,0x2c}, {0x7a,0x37,0xa1,0x0c}, {0x71,0x39,0xa8,0x01}, {0x6c,0x2b,0xb3,0x16}, {0x67,0x25,0xba,0x1b}, {0x56,0x0f,0x85,0x38}, {0x5d,0x01,0x8c,0x35}, {0x40,0x13,0x97,0x22}, {0x4b,0x1d,0x9e,0x2f}, {0x22,0x47,0xe9,0x64}, {0x29,0x49,0xe0,0x69}, {0x34,0x5b,0xfb,0x7e}, {0x3f,0x55,0xf2,0x73}, {0x0e,0x7f,0xcd,0x50}, {0x05,0x71,0xc4,0x5d}, {0x18,0x63,0xdf,0x4a}, {0x13,0x6d,0xd6,0x47}, {0xca,0xd7,0x31,0xdc}, {0xc1,0xd9,0x38,0xd1}, {0xdc,0xcb,0x23,0xc6}, {0xd7,0xc5,0x2a,0xcb}, {0xe6,0xef,0x15,0xe8}, {0xed,0xe1,0x1c,0xe5}, {0xf0,0xf3,0x07,0xf2}, {0xfb,0xfd,0x0e,0xff}, {0x92,0xa7,0x79,0xb4}, {0x99,0xa9,0x70,0xb9}, {0x84,0xbb,0x6b,0xae}, {0x8f,0xb5,0x62,0xa3}, {0xbe,0x9f,0x5d,0x80}, {0xb5,0x91,0x54,0x8d}, {0xa8,0x83,0x4f,0x9a}, {0xa3,0x8d,0x46,0x97} }; word8 U3[256][4] = { {0x00,0x00,0x00,0x00}, {0x0d,0x0b,0x0e,0x09}, {0x1a,0x16,0x1c,0x12}, {0x17,0x1d,0x12,0x1b}, {0x34,0x2c,0x38,0x24}, {0x39,0x27,0x36,0x2d}, {0x2e,0x3a,0x24,0x36}, {0x23,0x31,0x2a,0x3f}, {0x68,0x58,0x70,0x48}, {0x65,0x53,0x7e,0x41}, {0x72,0x4e,0x6c,0x5a}, {0x7f,0x45,0x62,0x53}, {0x5c,0x74,0x48,0x6c}, {0x51,0x7f,0x46,0x65}, {0x46,0x62,0x54,0x7e}, {0x4b,0x69,0x5a,0x77}, {0xd0,0xb0,0xe0,0x90}, {0xdd,0xbb,0xee,0x99}, {0xca,0xa6,0xfc,0x82}, {0xc7,0xad,0xf2,0x8b}, {0xe4,0x9c,0xd8,0xb4}, {0xe9,0x97,0xd6,0xbd}, {0xfe,0x8a,0xc4,0xa6}, {0xf3,0x81,0xca,0xaf}, {0xb8,0xe8,0x90,0xd8}, {0xb5,0xe3,0x9e,0xd1}, {0xa2,0xfe,0x8c,0xca}, {0xaf,0xf5,0x82,0xc3}, {0x8c,0xc4,0xa8,0xfc}, {0x81,0xcf,0xa6,0xf5}, {0x96,0xd2,0xb4,0xee}, {0x9b,0xd9,0xba,0xe7}, {0xbb,0x7b,0xdb,0x3b}, {0xb6,0x70,0xd5,0x32}, {0xa1,0x6d,0xc7,0x29}, {0xac,0x66,0xc9,0x20}, {0x8f,0x57,0xe3,0x1f}, {0x82,0x5c,0xed,0x16}, {0x95,0x41,0xff,0x0d}, {0x98,0x4a,0xf1,0x04}, {0xd3,0x23,0xab,0x73}, {0xde,0x28,0xa5,0x7a}, {0xc9,0x35,0xb7,0x61}, {0xc4,0x3e,0xb9,0x68}, {0xe7,0x0f,0x93,0x57}, {0xea,0x04,0x9d,0x5e}, {0xfd,0x19,0x8f,0x45}, {0xf0,0x12,0x81,0x4c}, {0x6b,0xcb,0x3b,0xab}, {0x66,0xc0,0x35,0xa2}, {0x71,0xdd,0x27,0xb9}, {0x7c,0xd6,0x29,0xb0}, {0x5f,0xe7,0x03,0x8f}, {0x52,0xec,0x0d,0x86}, {0x45,0xf1,0x1f,0x9d}, {0x48,0xfa,0x11,0x94}, {0x03,0x93,0x4b,0xe3}, {0x0e,0x98,0x45,0xea}, {0x19,0x85,0x57,0xf1}, {0x14,0x8e,0x59,0xf8}, {0x37,0xbf,0x73,0xc7}, {0x3a,0xb4,0x7d,0xce}, {0x2d,0xa9,0x6f,0xd5}, {0x20,0xa2,0x61,0xdc}, {0x6d,0xf6,0xad,0x76}, {0x60,0xfd,0xa3,0x7f}, {0x77,0xe0,0xb1,0x64}, {0x7a,0xeb,0xbf,0x6d}, {0x59,0xda,0x95,0x52}, {0x54,0xd1,0x9b,0x5b}, {0x43,0xcc,0x89,0x40}, {0x4e,0xc7,0x87,0x49}, {0x05,0xae,0xdd,0x3e}, {0x08,0xa5,0xd3,0x37}, {0x1f,0xb8,0xc1,0x2c}, {0x12,0xb3,0xcf,0x25}, {0x31,0x82,0xe5,0x1a}, {0x3c,0x89,0xeb,0x13}, {0x2b,0x94,0xf9,0x08}, {0x26,0x9f,0xf7,0x01}, {0xbd,0x46,0x4d,0xe6}, {0xb0,0x4d,0x43,0xef}, {0xa7,0x50,0x51,0xf4}, {0xaa,0x5b,0x5f,0xfd}, {0x89,0x6a,0x75,0xc2}, {0x84,0x61,0x7b,0xcb}, {0x93,0x7c,0x69,0xd0}, {0x9e,0x77,0x67,0xd9}, {0xd5,0x1e,0x3d,0xae}, {0xd8,0x15,0x33,0xa7}, {0xcf,0x08,0x21,0xbc}, {0xc2,0x03,0x2f,0xb5}, {0xe1,0x32,0x05,0x8a}, {0xec,0x39,0x0b,0x83}, {0xfb,0x24,0x19,0x98}, {0xf6,0x2f,0x17,0x91}, {0xd6,0x8d,0x76,0x4d}, {0xdb,0x86,0x78,0x44}, {0xcc,0x9b,0x6a,0x5f}, {0xc1,0x90,0x64,0x56}, {0xe2,0xa1,0x4e,0x69}, {0xef,0xaa,0x40,0x60}, {0xf8,0xb7,0x52,0x7b}, {0xf5,0xbc,0x5c,0x72}, {0xbe,0xd5,0x06,0x05}, {0xb3,0xde,0x08,0x0c}, {0xa4,0xc3,0x1a,0x17}, {0xa9,0xc8,0x14,0x1e}, {0x8a,0xf9,0x3e,0x21}, {0x87,0xf2,0x30,0x28}, {0x90,0xef,0x22,0x33}, {0x9d,0xe4,0x2c,0x3a}, {0x06,0x3d,0x96,0xdd}, {0x0b,0x36,0x98,0xd4}, {0x1c,0x2b,0x8a,0xcf}, {0x11,0x20,0x84,0xc6}, {0x32,0x11,0xae,0xf9}, {0x3f,0x1a,0xa0,0xf0}, {0x28,0x07,0xb2,0xeb}, {0x25,0x0c,0xbc,0xe2}, {0x6e,0x65,0xe6,0x95}, {0x63,0x6e,0xe8,0x9c}, {0x74,0x73,0xfa,0x87}, {0x79,0x78,0xf4,0x8e}, {0x5a,0x49,0xde,0xb1}, {0x57,0x42,0xd0,0xb8}, {0x40,0x5f,0xc2,0xa3}, {0x4d,0x54,0xcc,0xaa}, {0xda,0xf7,0x41,0xec}, {0xd7,0xfc,0x4f,0xe5}, {0xc0,0xe1,0x5d,0xfe}, {0xcd,0xea,0x53,0xf7}, {0xee,0xdb,0x79,0xc8}, {0xe3,0xd0,0x77,0xc1}, {0xf4,0xcd,0x65,0xda}, {0xf9,0xc6,0x6b,0xd3}, {0xb2,0xaf,0x31,0xa4}, {0xbf,0xa4,0x3f,0xad}, {0xa8,0xb9,0x2d,0xb6}, {0xa5,0xb2,0x23,0xbf}, {0x86,0x83,0x09,0x80}, {0x8b,0x88,0x07,0x89}, {0x9c,0x95,0x15,0x92}, {0x91,0x9e,0x1b,0x9b}, {0x0a,0x47,0xa1,0x7c}, {0x07,0x4c,0xaf,0x75}, {0x10,0x51,0xbd,0x6e}, {0x1d,0x5a,0xb3,0x67}, {0x3e,0x6b,0x99,0x58}, {0x33,0x60,0x97,0x51}, {0x24,0x7d,0x85,0x4a}, {0x29,0x76,0x8b,0x43}, {0x62,0x1f,0xd1,0x34}, {0x6f,0x14,0xdf,0x3d}, {0x78,0x09,0xcd,0x26}, {0x75,0x02,0xc3,0x2f}, {0x56,0x33,0xe9,0x10}, {0x5b,0x38,0xe7,0x19}, {0x4c,0x25,0xf5,0x02}, {0x41,0x2e,0xfb,0x0b}, {0x61,0x8c,0x9a,0xd7}, {0x6c,0x87,0x94,0xde}, {0x7b,0x9a,0x86,0xc5}, {0x76,0x91,0x88,0xcc}, {0x55,0xa0,0xa2,0xf3}, {0x58,0xab,0xac,0xfa}, {0x4f,0xb6,0xbe,0xe1}, {0x42,0xbd,0xb0,0xe8}, {0x09,0xd4,0xea,0x9f}, {0x04,0xdf,0xe4,0x96}, {0x13,0xc2,0xf6,0x8d}, {0x1e,0xc9,0xf8,0x84}, {0x3d,0xf8,0xd2,0xbb}, {0x30,0xf3,0xdc,0xb2}, {0x27,0xee,0xce,0xa9}, {0x2a,0xe5,0xc0,0xa0}, {0xb1,0x3c,0x7a,0x47}, {0xbc,0x37,0x74,0x4e}, {0xab,0x2a,0x66,0x55}, {0xa6,0x21,0x68,0x5c}, {0x85,0x10,0x42,0x63}, {0x88,0x1b,0x4c,0x6a}, {0x9f,0x06,0x5e,0x71}, {0x92,0x0d,0x50,0x78}, {0xd9,0x64,0x0a,0x0f}, {0xd4,0x6f,0x04,0x06}, {0xc3,0x72,0x16,0x1d}, {0xce,0x79,0x18,0x14}, {0xed,0x48,0x32,0x2b}, {0xe0,0x43,0x3c,0x22}, {0xf7,0x5e,0x2e,0x39}, {0xfa,0x55,0x20,0x30}, {0xb7,0x01,0xec,0x9a}, {0xba,0x0a,0xe2,0x93}, {0xad,0x17,0xf0,0x88}, {0xa0,0x1c,0xfe,0x81}, {0x83,0x2d,0xd4,0xbe}, {0x8e,0x26,0xda,0xb7}, {0x99,0x3b,0xc8,0xac}, {0x94,0x30,0xc6,0xa5}, {0xdf,0x59,0x9c,0xd2}, {0xd2,0x52,0x92,0xdb}, {0xc5,0x4f,0x80,0xc0}, {0xc8,0x44,0x8e,0xc9}, {0xeb,0x75,0xa4,0xf6}, {0xe6,0x7e,0xaa,0xff}, {0xf1,0x63,0xb8,0xe4}, {0xfc,0x68,0xb6,0xed}, {0x67,0xb1,0x0c,0x0a}, {0x6a,0xba,0x02,0x03}, {0x7d,0xa7,0x10,0x18}, {0x70,0xac,0x1e,0x11}, {0x53,0x9d,0x34,0x2e}, {0x5e,0x96,0x3a,0x27}, {0x49,0x8b,0x28,0x3c}, {0x44,0x80,0x26,0x35}, {0x0f,0xe9,0x7c,0x42}, {0x02,0xe2,0x72,0x4b}, {0x15,0xff,0x60,0x50}, {0x18,0xf4,0x6e,0x59}, {0x3b,0xc5,0x44,0x66}, {0x36,0xce,0x4a,0x6f}, {0x21,0xd3,0x58,0x74}, {0x2c,0xd8,0x56,0x7d}, {0x0c,0x7a,0x37,0xa1}, {0x01,0x71,0x39,0xa8}, {0x16,0x6c,0x2b,0xb3}, {0x1b,0x67,0x25,0xba}, {0x38,0x56,0x0f,0x85}, {0x35,0x5d,0x01,0x8c}, {0x22,0x40,0x13,0x97}, {0x2f,0x4b,0x1d,0x9e}, {0x64,0x22,0x47,0xe9}, {0x69,0x29,0x49,0xe0}, {0x7e,0x34,0x5b,0xfb}, {0x73,0x3f,0x55,0xf2}, {0x50,0x0e,0x7f,0xcd}, {0x5d,0x05,0x71,0xc4}, {0x4a,0x18,0x63,0xdf}, {0x47,0x13,0x6d,0xd6}, {0xdc,0xca,0xd7,0x31}, {0xd1,0xc1,0xd9,0x38}, {0xc6,0xdc,0xcb,0x23}, {0xcb,0xd7,0xc5,0x2a}, {0xe8,0xe6,0xef,0x15}, {0xe5,0xed,0xe1,0x1c}, {0xf2,0xf0,0xf3,0x07}, {0xff,0xfb,0xfd,0x0e}, {0xb4,0x92,0xa7,0x79}, {0xb9,0x99,0xa9,0x70}, {0xae,0x84,0xbb,0x6b}, {0xa3,0x8f,0xb5,0x62}, {0x80,0xbe,0x9f,0x5d}, {0x8d,0xb5,0x91,0x54}, {0x9a,0xa8,0x83,0x4f}, {0x97,0xa3,0x8d,0x46} }; word8 U4[256][4] = { {0x00,0x00,0x00,0x00}, {0x09,0x0d,0x0b,0x0e}, {0x12,0x1a,0x16,0x1c}, {0x1b,0x17,0x1d,0x12}, {0x24,0x34,0x2c,0x38}, {0x2d,0x39,0x27,0x36}, {0x36,0x2e,0x3a,0x24}, {0x3f,0x23,0x31,0x2a}, {0x48,0x68,0x58,0x70}, {0x41,0x65,0x53,0x7e}, {0x5a,0x72,0x4e,0x6c}, {0x53,0x7f,0x45,0x62}, {0x6c,0x5c,0x74,0x48}, {0x65,0x51,0x7f,0x46}, {0x7e,0x46,0x62,0x54}, {0x77,0x4b,0x69,0x5a}, {0x90,0xd0,0xb0,0xe0}, {0x99,0xdd,0xbb,0xee}, {0x82,0xca,0xa6,0xfc}, {0x8b,0xc7,0xad,0xf2}, {0xb4,0xe4,0x9c,0xd8}, {0xbd,0xe9,0x97,0xd6}, {0xa6,0xfe,0x8a,0xc4}, {0xaf,0xf3,0x81,0xca}, {0xd8,0xb8,0xe8,0x90}, {0xd1,0xb5,0xe3,0x9e}, {0xca,0xa2,0xfe,0x8c}, {0xc3,0xaf,0xf5,0x82}, {0xfc,0x8c,0xc4,0xa8}, {0xf5,0x81,0xcf,0xa6}, {0xee,0x96,0xd2,0xb4}, {0xe7,0x9b,0xd9,0xba}, {0x3b,0xbb,0x7b,0xdb}, {0x32,0xb6,0x70,0xd5}, {0x29,0xa1,0x6d,0xc7}, {0x20,0xac,0x66,0xc9}, {0x1f,0x8f,0x57,0xe3}, {0x16,0x82,0x5c,0xed}, {0x0d,0x95,0x41,0xff}, {0x04,0x98,0x4a,0xf1}, {0x73,0xd3,0x23,0xab}, {0x7a,0xde,0x28,0xa5}, {0x61,0xc9,0x35,0xb7}, {0x68,0xc4,0x3e,0xb9}, {0x57,0xe7,0x0f,0x93}, {0x5e,0xea,0x04,0x9d}, {0x45,0xfd,0x19,0x8f}, {0x4c,0xf0,0x12,0x81}, {0xab,0x6b,0xcb,0x3b}, {0xa2,0x66,0xc0,0x35}, {0xb9,0x71,0xdd,0x27}, {0xb0,0x7c,0xd6,0x29}, {0x8f,0x5f,0xe7,0x03}, {0x86,0x52,0xec,0x0d}, {0x9d,0x45,0xf1,0x1f}, {0x94,0x48,0xfa,0x11}, {0xe3,0x03,0x93,0x4b}, {0xea,0x0e,0x98,0x45}, {0xf1,0x19,0x85,0x57}, {0xf8,0x14,0x8e,0x59}, {0xc7,0x37,0xbf,0x73}, {0xce,0x3a,0xb4,0x7d}, {0xd5,0x2d,0xa9,0x6f}, {0xdc,0x20,0xa2,0x61}, {0x76,0x6d,0xf6,0xad}, {0x7f,0x60,0xfd,0xa3}, {0x64,0x77,0xe0,0xb1}, {0x6d,0x7a,0xeb,0xbf}, {0x52,0x59,0xda,0x95}, {0x5b,0x54,0xd1,0x9b}, {0x40,0x43,0xcc,0x89}, {0x49,0x4e,0xc7,0x87}, {0x3e,0x05,0xae,0xdd}, {0x37,0x08,0xa5,0xd3}, {0x2c,0x1f,0xb8,0xc1}, {0x25,0x12,0xb3,0xcf}, {0x1a,0x31,0x82,0xe5}, {0x13,0x3c,0x89,0xeb}, {0x08,0x2b,0x94,0xf9}, {0x01,0x26,0x9f,0xf7}, {0xe6,0xbd,0x46,0x4d}, {0xef,0xb0,0x4d,0x43}, {0xf4,0xa7,0x50,0x51}, {0xfd,0xaa,0x5b,0x5f}, {0xc2,0x89,0x6a,0x75}, {0xcb,0x84,0x61,0x7b}, {0xd0,0x93,0x7c,0x69}, {0xd9,0x9e,0x77,0x67}, {0xae,0xd5,0x1e,0x3d}, {0xa7,0xd8,0x15,0x33}, {0xbc,0xcf,0x08,0x21}, {0xb5,0xc2,0x03,0x2f}, {0x8a,0xe1,0x32,0x05}, {0x83,0xec,0x39,0x0b}, {0x98,0xfb,0x24,0x19}, {0x91,0xf6,0x2f,0x17}, {0x4d,0xd6,0x8d,0x76}, {0x44,0xdb,0x86,0x78}, {0x5f,0xcc,0x9b,0x6a}, {0x56,0xc1,0x90,0x64}, {0x69,0xe2,0xa1,0x4e}, {0x60,0xef,0xaa,0x40}, {0x7b,0xf8,0xb7,0x52}, {0x72,0xf5,0xbc,0x5c}, {0x05,0xbe,0xd5,0x06}, {0x0c,0xb3,0xde,0x08}, {0x17,0xa4,0xc3,0x1a}, {0x1e,0xa9,0xc8,0x14}, {0x21,0x8a,0xf9,0x3e}, {0x28,0x87,0xf2,0x30}, {0x33,0x90,0xef,0x22}, {0x3a,0x9d,0xe4,0x2c}, {0xdd,0x06,0x3d,0x96}, {0xd4,0x0b,0x36,0x98}, {0xcf,0x1c,0x2b,0x8a}, {0xc6,0x11,0x20,0x84}, {0xf9,0x32,0x11,0xae}, {0xf0,0x3f,0x1a,0xa0}, {0xeb,0x28,0x07,0xb2}, {0xe2,0x25,0x0c,0xbc}, {0x95,0x6e,0x65,0xe6}, {0x9c,0x63,0x6e,0xe8}, {0x87,0x74,0x73,0xfa}, {0x8e,0x79,0x78,0xf4}, {0xb1,0x5a,0x49,0xde}, {0xb8,0x57,0x42,0xd0}, {0xa3,0x40,0x5f,0xc2}, {0xaa,0x4d,0x54,0xcc}, {0xec,0xda,0xf7,0x41}, {0xe5,0xd7,0xfc,0x4f}, {0xfe,0xc0,0xe1,0x5d}, {0xf7,0xcd,0xea,0x53}, {0xc8,0xee,0xdb,0x79}, {0xc1,0xe3,0xd0,0x77}, {0xda,0xf4,0xcd,0x65}, {0xd3,0xf9,0xc6,0x6b}, {0xa4,0xb2,0xaf,0x31}, {0xad,0xbf,0xa4,0x3f}, {0xb6,0xa8,0xb9,0x2d}, {0xbf,0xa5,0xb2,0x23}, {0x80,0x86,0x83,0x09}, {0x89,0x8b,0x88,0x07}, {0x92,0x9c,0x95,0x15}, {0x9b,0x91,0x9e,0x1b}, {0x7c,0x0a,0x47,0xa1}, {0x75,0x07,0x4c,0xaf}, {0x6e,0x10,0x51,0xbd}, {0x67,0x1d,0x5a,0xb3}, {0x58,0x3e,0x6b,0x99}, {0x51,0x33,0x60,0x97}, {0x4a,0x24,0x7d,0x85}, {0x43,0x29,0x76,0x8b}, {0x34,0x62,0x1f,0xd1}, {0x3d,0x6f,0x14,0xdf}, {0x26,0x78,0x09,0xcd}, {0x2f,0x75,0x02,0xc3}, {0x10,0x56,0x33,0xe9}, {0x19,0x5b,0x38,0xe7}, {0x02,0x4c,0x25,0xf5}, {0x0b,0x41,0x2e,0xfb}, {0xd7,0x61,0x8c,0x9a}, {0xde,0x6c,0x87,0x94}, {0xc5,0x7b,0x9a,0x86}, {0xcc,0x76,0x91,0x88}, {0xf3,0x55,0xa0,0xa2}, {0xfa,0x58,0xab,0xac}, {0xe1,0x4f,0xb6,0xbe}, {0xe8,0x42,0xbd,0xb0}, {0x9f,0x09,0xd4,0xea}, {0x96,0x04,0xdf,0xe4}, {0x8d,0x13,0xc2,0xf6}, {0x84,0x1e,0xc9,0xf8}, {0xbb,0x3d,0xf8,0xd2}, {0xb2,0x30,0xf3,0xdc}, {0xa9,0x27,0xee,0xce}, {0xa0,0x2a,0xe5,0xc0}, {0x47,0xb1,0x3c,0x7a}, {0x4e,0xbc,0x37,0x74}, {0x55,0xab,0x2a,0x66}, {0x5c,0xa6,0x21,0x68}, {0x63,0x85,0x10,0x42}, {0x6a,0x88,0x1b,0x4c}, {0x71,0x9f,0x06,0x5e}, {0x78,0x92,0x0d,0x50}, {0x0f,0xd9,0x64,0x0a}, {0x06,0xd4,0x6f,0x04}, {0x1d,0xc3,0x72,0x16}, {0x14,0xce,0x79,0x18}, {0x2b,0xed,0x48,0x32}, {0x22,0xe0,0x43,0x3c}, {0x39,0xf7,0x5e,0x2e}, {0x30,0xfa,0x55,0x20}, {0x9a,0xb7,0x01,0xec}, {0x93,0xba,0x0a,0xe2}, {0x88,0xad,0x17,0xf0}, {0x81,0xa0,0x1c,0xfe}, {0xbe,0x83,0x2d,0xd4}, {0xb7,0x8e,0x26,0xda}, {0xac,0x99,0x3b,0xc8}, {0xa5,0x94,0x30,0xc6}, {0xd2,0xdf,0x59,0x9c}, {0xdb,0xd2,0x52,0x92}, {0xc0,0xc5,0x4f,0x80}, {0xc9,0xc8,0x44,0x8e}, {0xf6,0xeb,0x75,0xa4}, {0xff,0xe6,0x7e,0xaa}, {0xe4,0xf1,0x63,0xb8}, {0xed,0xfc,0x68,0xb6}, {0x0a,0x67,0xb1,0x0c}, {0x03,0x6a,0xba,0x02}, {0x18,0x7d,0xa7,0x10}, {0x11,0x70,0xac,0x1e}, {0x2e,0x53,0x9d,0x34}, {0x27,0x5e,0x96,0x3a}, {0x3c,0x49,0x8b,0x28}, {0x35,0x44,0x80,0x26}, {0x42,0x0f,0xe9,0x7c}, {0x4b,0x02,0xe2,0x72}, {0x50,0x15,0xff,0x60}, {0x59,0x18,0xf4,0x6e}, {0x66,0x3b,0xc5,0x44}, {0x6f,0x36,0xce,0x4a}, {0x74,0x21,0xd3,0x58}, {0x7d,0x2c,0xd8,0x56}, {0xa1,0x0c,0x7a,0x37}, {0xa8,0x01,0x71,0x39}, {0xb3,0x16,0x6c,0x2b}, {0xba,0x1b,0x67,0x25}, {0x85,0x38,0x56,0x0f}, {0x8c,0x35,0x5d,0x01}, {0x97,0x22,0x40,0x13}, {0x9e,0x2f,0x4b,0x1d}, {0xe9,0x64,0x22,0x47}, {0xe0,0x69,0x29,0x49}, {0xfb,0x7e,0x34,0x5b}, {0xf2,0x73,0x3f,0x55}, {0xcd,0x50,0x0e,0x7f}, {0xc4,0x5d,0x05,0x71}, {0xdf,0x4a,0x18,0x63}, {0xd6,0x47,0x13,0x6d}, {0x31,0xdc,0xca,0xd7}, {0x38,0xd1,0xc1,0xd9}, {0x23,0xc6,0xdc,0xcb}, {0x2a,0xcb,0xd7,0xc5}, {0x15,0xe8,0xe6,0xef}, {0x1c,0xe5,0xed,0xe1}, {0x07,0xf2,0xf0,0xf3}, {0x0e,0xff,0xfb,0xfd}, {0x79,0xb4,0x92,0xa7}, {0x70,0xb9,0x99,0xa9}, {0x6b,0xae,0x84,0xbb}, {0x62,0xa3,0x8f,0xb5}, {0x5d,0x80,0xbe,0x9f}, {0x54,0x8d,0xb5,0x91}, {0x4f,0x9a,0xa8,0x83}, {0x46,0x97,0xa3,0x8d} }; word32 rcon[30] = { 0x01,0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; blobby-1.0rc3/src/raknet/RSACrypt.h0000644000175000017500000005731512042452367020456 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * Performant RSA en/decryption with 256-bit to 16384-bit modulus * catid(cat02e@fsu.edu) * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * 7/30/2004 Fixed VS6 compat * 7/26/2004 Now internally generates private keys * simpleModExp() is faster for encryption than MontyModExp * CRT-MontyModExp is faster for decryption than CRT-SimpleModExp * 7/25/2004 Implemented Montgomery modular exponentation * Implemented CRT modular exponentation optimization * 7/21/2004 Did some pre-lim coding * Best performance on my 1.8 GHz P4 (mobile): * 1024-bit generate key : 30 seconds * 1024-bit set private key : 100 ms (pre-compute this step) * 1024-bit encryption : 200 usec * 1024-bit decryption : 400 ms * * TODO * There's a bug in MonModExp() that restricts us to k-1 bits * * Tabs: 4 spaces * Dist: public */ #ifndef RSACRYPT_H #define RSACRYPT_H #define RSASUPPORTGENPRIME // Can't go under 256 or you'll need to disable the USEASSEMBLY macro in bigtypes.h // That's because the assembly assumes at least 128-bit data to work on // #define RSA_BIT_SIZE big::u512 #define RSA_BIT_SIZE big::u256 #include "BigTypes.h" #include "Rand.h" //Giblet - added missing include for randomMT() namespace big { using namespace cat; // r = x^y Mod n (fast for small y) BIGONETYPE void simpleModExp( T &x0, T &y0, T &n0, T &r0 ) { BIGDOUBLESIZE( T, x ); BIGDOUBLESIZE( T, y ); BIGDOUBLESIZE( T, n ); BIGDOUBLESIZE( T, r ); usetlow( x, x0 ); usetlow( y, y0 ); usetlow( n, n0 ); usetw( r, 1 ); umodulo( x, n, x ); u32 squares = 0; for ( u32 ii = 0; ii < BIGWORDCOUNT( T ); ++ii ) { word y_i = y[ ii ]; u32 ctr = WORDBITS; while ( y_i ) { if ( y_i & 1 ) { if ( squares ) do { usquare( x ); umodulo( x, n, x ); } while ( --squares ); umultiply( r, x, r ); umodulo( r, n, r ); } y_i >>= 1; ++squares; --ctr; } squares += ctr; } takelow( r0, r ); } // computes Rn = 2^k (mod n), n < 2^k BIGONETYPE void rModn( T &n, T &Rn ) { BIGDOUBLESIZE( T, dR ); BIGDOUBLESIZE( T, dn ); BIGDOUBLESIZE( T, dRn ); T one; // dR = 2^k usetw( one, 1 ); sethigh( dR, one ); // Rn = 2^k (mod n) usetlow( dn, n ); umodulo( dR, dn, dRn ); takelow( Rn, dRn ); } // computes c = GCD(a, b) BIGONETYPE void GCD( T &a0, T &b0, T &c ) { T a; umodulo( a0, b0, c ); if ( isZero( c ) ) { set ( c, b0 ) ; return ; } umodulo( b0, c, a ); if ( isZero( a ) ) return ; #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( true ) { umodulo( c, a, c ); if ( isZero( c ) ) { set ( c, a ) ; return ; } umodulo( a, c, a ); if ( isZero( a ) ) return ; } } // directly computes x = c - a * b (mod n) > 0, c < n BIGONETYPE void SubMulMod( T &a, T &b, T &c, T &n, T &x ) { BIGDOUBLESIZE( T, da ); BIGDOUBLESIZE( T, dn ); T y; // y = a b (mod n) usetlow( da, a ); umultiply( da, b ); usetlow( dn, n ); umodulo( da, dn, da ); takelow( y, da ); // x = (c - y) (mod n) > 0 set ( x, c ) ; if ( ugreater( c, y ) ) { subtract( x, y ); } else { subtract( x, y ); add ( x, n ) ; } } /* directly compute a' s.t. a' a - b' b = 1 b = b0 = n0 rp = a' a = 2^k a > b > 0 GCD(a, b) = 1 (b odd) Trying to keep everything positive */ BIGONETYPE void computeRinverse( T &n0, T &rp ) { T x0, x1, x2, a, b, q; //x[0] = 1 usetw( x0, 1 ); // a = 2^k (mod b0) rModn( n0, a ); // {q, b} = b0 / a udivide( n0, a, q, b ); // if b = 0, return x[0] if ( isZero( b ) ) { set ( rp, x0 ) ; return ; } // x[1] = -q (mod b0) = b0 - q, q <= b0 set ( x1, n0 ) ; subtract( x1, q ); // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[1] if ( isZero( a ) ) { set ( rp, x1 ) ; return ; } #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( true ) { // x[2] = x[0] - x[1] * q (mod b0) SubMulMod( q, x1, x0, n0, x2 ); // {q, b} = b / a udivide( b, a, q, b ); // if b = 0, return x[2] if ( isZero( b ) ) { set ( rp, x2 ) ; return ; } // x[0] = x[1] - x[2] * q (mod b0) SubMulMod( q, x2, x1, n0, x0 ); // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[0] if ( isZero( a ) ) { set ( rp, x0 ) ; return ; } // x[1] = x[2] - x[0] * q (mod b0) SubMulMod( q, x0, x2, n0, x1 ); // {q, b} = b / a udivide( b, a, q, b ); // if b = 0, return x[1] if ( isZero( b ) ) { set ( rp, x1 ) ; return ; } // x[2] = x[0] - x[1] * q (mod b0) SubMulMod( q, x1, x0, n0, x2 ); // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[2] if ( isZero( a ) ) { set ( rp, x2 ) ; return ; } // x[0] = x[1] - x[2] * q (mod b0) SubMulMod( q, x2, x1, n0, x0 ); // {q, b} = b / a udivide( b, a, q, b ); // if b = 0, return x[0] if ( isZero( b ) ) { set ( rp, x0 ) ; return ; } // x[1] = x[2] - x[0] * q (mod b0) SubMulMod( q, x0, x2, n0, x1 ); // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[1] if ( isZero( a ) ) { set ( rp, x1 ) ; return ; } } } /* BIGONETYPE void computeRinverse2(T &_n0, T &_rp) { //T x0, x1, x2, a, b, q; BIGDOUBLESIZE(T, x0); BIGDOUBLESIZE(T, x1); BIGDOUBLESIZE(T, x2); BIGDOUBLESIZE(T, a); BIGDOUBLESIZE(T, b); BIGDOUBLESIZE(T, q); BIGDOUBLESIZE(T, n0); BIGDOUBLESIZE(T, rp); usetlow(n0, _n0); usetlow(rp, _rp); std::string old; //x[0] = 1 usetw(x0, 1); T _a; // a = 2^k (mod b0) rModn(_n0, _a); RECORD("TEST") << "a=" << toString(a, false) << " = 2^k (mod " << toString(n0, false) << ")"; usetlow(a, _a); // {q, b} = b0 / a udivide(n0, a, q, b); RECORD("TEST") << "{q=" << toString(q, false) << ", b=" << toString(b, false) << "} = n0=" << toString(n0, false) << " / a=" << toString(a, false); // if b = 0, return x[0] if (isZero(b)) { RECORD("TEST") << "b == 0, Returning x[0]"; set(rp, x0); takelow(_rp, rp); return; } // x[1] = -q (mod b0) negate(q); smodulo(q, n0, x1); if (BIGHIGHBIT(x1)) add(x1, n0); // q > 0 RECORD("TEST") << "x1=" << toString(x1, false) << " = q=" << toString(q, false) << " (mod n0=" << toString(n0, false) << ")"; // {q, a} = a / b old = toString(a, false); udivide(a, b, q, a); RECORD("TEST") << "{q=" << toString(q, false) << ", a=" << toString(a, false) << "} = a=" << old << " / b=" << toString(b); // if a = 0, return x[1] if (isZero(a)) { RECORD("TEST") << "a == 0, Returning x[1]"; set(rp, x1); takelow(_rp, rp); return; } RECORD("TEST") << "Entering loop..."; while (true) { // x[2] = x[0] - x[1] * q (mod b0) SubMulMod(q, x1, x0, n0, x2); RECORD("TEST") << "x[0] = " << toString(x0, false); RECORD("TEST") << "x[1] = " << toString(x1, false); RECORD("TEST") << "x[2] = " << toString(x2, false); // {q, b} = b / a old = toString(b); udivide(b, a, q, b); RECORD("TEST") << "{q=" << toString(q, false) << ", b=" << toString(b) << "} = b=" << old << " / a=" << toString(a, false); // if b = 0, return x[2] if (isZero(b)) { RECORD("TEST") << "b == 0, Returning x[2]"; set(rp, x2); takelow(_rp, rp); return; } // x[0] = x[1] - x[2] * q (mod b0) SubMulMod(q, x2, x1, n0, x0); RECORD("TEST") << "x[0] = " << toString(x0, false); RECORD("TEST") << "x[1] = " << toString(x1, false); RECORD("TEST") << "x[2] = " << toString(x2, false); // {q, a} = a / b old = toString(a, false); udivide(a, b, q, a); RECORD("TEST") << "{q=" << toString(q, false) << ", a=" << toString(a, false) << "} = a=" << old << " / b=" << toString(b); // if a = 0, return x[0] if (isZero(a)) { RECORD("TEST") << "a == 0, Returning x[0]"; set(rp, x0); takelow(_rp, rp); return; } // x[1] = x[2] - x[0] * q (mod b0) SubMulMod(q, x0, x2, n0, x1); RECORD("TEST") << "x[0] = " << toString(x0, false); RECORD("TEST") << "x[1] = " << toString(x1, false); RECORD("TEST") << "x[2] = " << toString(x2, false); // {q, b} = b / a old = toString(b); udivide(b, a, q, b); RECORD("TEST") << "{q=" << toString(q, false) << ", b=" << toString(b) << "} = b=" << old << " / a=" << toString(a, false); // if b = 0, return x[1] if (isZero(b)) { RECORD("TEST") << "b == 0, Returning x[1]"; set(rp, x1); takelow(_rp, rp); return; } // x[2] = x[0] - x[1] * q (mod b0) SubMulMod(q, x1, x0, n0, x2); RECORD("TEST") << "x[0] = " << toString(x0, false); RECORD("TEST") << "x[1] = " << toString(x1, false); RECORD("TEST") << "x[2] = " << toString(x2, false); // {q, a} = a / b old = toString(a, false); udivide(a, b, q, a); RECORD("TEST") << "{q=" << toString(q, false) << ", a=" << toString(a, false) << "} = a=" << old << " / b=" << toString(b); // if a = 0, return x[2] if (isZero(a)) { RECORD("TEST") << "a == 0, Returning x[2]"; set(rp, x2); takelow(_rp, rp); return; } // x[0] = x[1] - x[2] * q (mod b0) SubMulMod(q, x2, x1, n0, x0); RECORD("TEST") << "x[0] = " << toString(x0, false); RECORD("TEST") << "x[1] = " << toString(x1, false); RECORD("TEST") << "x[2] = " << toString(x2, false); // {q, b} = b / a old = toString(b); udivide(b, a, q, b); RECORD("TEST") << "{q=" << toString(q, false) << ", b=" << toString(b) << "} = b=" << old << " / a=" << toString(a, false); // if b = 0, return x[0] if (isZero(b)) { RECORD("TEST") << "b == 0, Returning x[0]"; set(rp, x0); takelow(_rp, rp); return; } // x[1] = x[2] - x[0] * q (mod b0) SubMulMod(q, x0, x2, n0, x1); RECORD("TEST") << "x[0] = " << toString(x0, false); RECORD("TEST") << "x[1] = " << toString(x1, false); RECORD("TEST") << "x[2] = " << toString(x2, false); // {q, a} = a / b old = toString(a, false); udivide(a, b, q, a); RECORD("TEST") << "{q=" << toString(q, false) << ", a=" << toString(a, false) << "} = a=" << old << " / b=" << toString(b); // if a = 0, return x[1] if (isZero(a)) { RECORD("TEST") << "a == 0, Returning x[1]"; set(rp, x1); takelow(_rp, rp); return; } } } */ // directly compute a^-1 s.t. a^-1 a (mod b) = 1, a < b, GCD(a, b) BIGONETYPE void computeModularInverse( T &a0, T &b0, T &ap ) { T x0, x1, x2; T a, b, q; // x[2] = 1 usetw( x2, 1 ); // {q, b} = b0 / a0 udivide( b0, a0, q, b ); // x[0] = -q (mod b0) = b0 - q, q <= b0 set ( x0, b0 ) ; subtract( x0, q ); set ( a, a0 ) ; #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant while ( true ) { // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[0] if ( isZero( a ) ) { set ( ap, x0 ) ; return ; } // x[1] = x[2] - x[0] * q (mod b0) SubMulMod( x0, q, x2, b0, x1 ); // {q, b} = b / a udivide( b, a, q, b ); // if b = 0, return x[1] if ( isZero( b ) ) { set ( ap, x1 ) ; return ; } // x[2] = x[0] - x[1] * q (mod b0) SubMulMod( x1, q, x0, b0, x2 ); // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[2] if ( isZero( a ) ) { set ( ap, x2 ) ; return ; } // x[0] = x[1] - x[2] * q (mod b0) SubMulMod( x2, q, x1, b0, x0 ); // {q, b} = b / a udivide( b, a, q, b ); // if b = 0, return x[0] if ( isZero( b ) ) { set ( ap, x0 ) ; return ; } // x[1] = x[2] - x[0] * q (mod b0) SubMulMod( x0, q, x2, b0, x1 ); // {q, a} = a / b udivide( a, b, q, a ); // if a = 0, return x[1] if ( isZero( a ) ) { set ( ap, x1 ) ; return ; } // x[2] = x[0] - x[1] * q (mod b0) SubMulMod( x1, q, x0, b0, x2 ); // {q, b} = b / a udivide( b, a, q, b ); // if b = 0, return x[2] if ( isZero( b ) ) { set ( ap, x2 ) ; return ; } // x[0] = x[1] - x[2] * q (mod b0) SubMulMod( x2, q, x1, b0, x0 ); } } // indirectly computes n' s.t. 1 = r' r - n' n = GCD(r, n) BIGONETYPE void computeNRinverse( T &n0, T &np ) { BIGDOUBLESIZE( T, r ); BIGDOUBLESIZE( T, n ); // r' = (1 + n' n) / r computeRinverse( n0, np ); // n' = (r' r - 1) / n sethigh( r, np ); // special case of r = 2^k decrement( r ); usetlow( n, n0 ); udivide( r, n, n, r ); takelow( np, n ); } /* // indirectly computes n' s.t. 1 = r' r - n' n = GCD(r, n) BIGONETYPE void computeNRinverse2(T &n0, T &np) { BIGDOUBLESIZE(T, r); BIGDOUBLESIZE(T, n); // r' = (1 + n' n) / r computeRinverse2(n0, np); // n' = (r' r - 1) / n sethigh(r, np); // special case of r = 2^k decrement(r); usetlow(n, n0); udivide(r, n, n, r); takelow(np, n); } */ // Montgomery product u = a * b (mod n) BIGONETYPE void MonPro( T &ap, T &bp, T &n, T &np, T &u_out ) { BIGDOUBLESIZE( T, t ); BIGDOUBLESIZE( T, u ); T m; // t = a' b' umultiply( ap, bp, t ); // m = (low half of t)*np (mod r) takelow( m, t ); umultiply( m, np ); // u = (t + m*n), u_out = u / r = high half of u umultiply( m, n, u ); add ( u, t ) ; takehigh( u_out, u ); // if u >= n, return u - n, else u if ( ugreaterOrEqual( u_out, n ) ) subtract( u_out, n ); } // indirectly calculates x = M^e (mod n) BIGONETYPE void MonModExp( T &x, T &M, T &e, T &n, T &np, T &xp0 ) { // x' = xp0 set ( x, xp0 ) ; // find M' = M r (mod n) BIGDOUBLESIZE( T, dM ); BIGDOUBLESIZE( T, dn ); T Mp; sethigh( dM, M ); // dM = M r usetlow( dn, n ); umodulo( dM, dn, dM ); // dM = dM (mod n) takelow( Mp, dM ); // M' = M r (mod n) /* i may be wrong, but it seems to me that the squaring results in a constant until we hit the first set bit this could save a lot of time, but it needs to be proven */ s32 ii, bc; word e_i; // for i = k - 1 down to 0 do for ( ii = BIGWORDCOUNT( T ) - 1; ii >= 0; --ii ) { e_i = e[ ii ]; bc = WORDBITS; while ( bc-- ) { // if e_i = 1, x = MonPro(M', x') if ( e_i & WORDHIGHBIT ) goto start_squaring; e_i <<= 1; } } for ( ; ii >= 0; --ii ) { e_i = e[ ii ]; bc = WORDBITS; while ( bc-- ) { // x' = MonPro(x', x') MonPro( x, x, n, np, x ); // if e_i = 1, x = MonPro(M', x') if ( e_i & WORDHIGHBIT ) { start_squaring: MonPro( Mp, x, n, np, x ); } e_i <<= 1; } } // x = MonPro(x', 1) T one; usetw( one, 1 ); MonPro( x, one, n, np, x ); } // indirectly calculates x = C ^ d (mod n) using the Chinese Remainder Thm #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter BIGTWOTYPES void CRTModExp( Bigger &x, Bigger &C, Bigger &d, T &p, T &q, T &pInverse, T &pnp, T &pxp, T &qnp, T &qxp ) { // d1 = d mod (p - 1) Bigger dd1; T d1; usetlow( dd1, p ); decrement( dd1 ); umodulo( d, dd1, dd1 ); takelow( d1, dd1 ); // M1 = C1^d1 (mod p) Bigger dp, dC1; T M1, C1; usetlow( dp, p ); umodulo( C, dp, dC1 ); takelow( C1, dC1 ); simpleModExp( C1, d1, p, M1 ); //MonModExp(M1, C1, d1, p, pnp, pxp); // d2 = d mod (q - 1) Bigger dd2; T d2; usetlow( dd2, q ); decrement( dd2 ); umodulo( d, dd2, dd2 ); takelow( d2, dd2 ); // M2 = C2^d2 (mod q) Bigger dq, dC2; T M2, C2; usetlow( dq, q ); umodulo( C, dq, dC2 ); takelow( C2, dC2 ); simpleModExp( C2, d2, q, M2 ); //MonModExp(M2, C2, d2, q, qnp, qxp); // x = M1 + p * ((M2 - M1)(p^-1 mod q) mod q) if ( ugreater( M2, M1 ) ) { subtract( M2, M1 ); } else { subtract( M2, M1 ); add ( M2, q ) ; } // x = M1 + p * (( M2 )(p^-1 mod q) mod q) umultiply( M2, pInverse, x ); // x = M1 + p * (( x ) mod q) umodulo( x, dq, x ); // x = M1 + p * ( x ) umultiply( x, dp ); // x = M1 + ( x ) Bigger dM1; usetlow( dM1, M1 ); // x = ( dM1 ) + ( x ) add ( x, dM1 ) ; } // generates a suitable public exponent s.t. 4 < e << phi, GCD(e, phi) = 1 BIGONETYPE void computePublicExponent( T &phi, T &e ) { T r, one, two; usetw( one, 1 ); usetw( two, 2 ); usetw( e, 65537 - 2 ); if ( ugreater( e, phi ) ) usetw( e, 5 - 2 ); do { add ( e, two ) ; GCD( phi, e, r ); } while ( !equal( r, one ) ); } // directly computes private exponent BIGONETYPE void computePrivateExponent( T &e, T &phi, T &d ) { // d = e^-1 (mod phi), 1 < e << phi computeModularInverse( e, phi, d ); } #ifdef RSASUPPORTGENPRIME static const u16 PRIME_TABLE[ 256 ] = { 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621 }; /* modified Rabin-Miller primality test (added small primes) When picking a value for insurance, note that the probability of failure of the test to detect a composite number is at most 4^(-insurance), so: insurance max. probability of failure 3 1.56% 4 0.39% 5 0.098% <-- default 6 0.024% ... */ BIGONETYPE bool RabinMillerPrimalityTest( T &n, u32 insurance ) { // check divisibility by small primes <= 1621 (speeds up computation) T temp; for ( u32 ii = 0; ii < 256; ++ii ) { usetw( temp, PRIME_TABLE[ ii++ ] ); umodulo( n, temp, temp ); if ( isZero( temp ) ) return false; } // n1 = n - 1 T n1; set ( n1, n ) ; decrement( n1 ); // write r 2^s = n - 1, r is odd T r; u32 s = 0; set ( r, n1 ) ; while ( !( r[ 0 ] & 1 ) ) { ushiftRight1( r ); ++s; } // one = 1 T one; usetw( one, 1 ); // cache n -> dn BIGDOUBLESIZE( T, dy ); BIGDOUBLESIZE( T, dn ); usetlow( dn, n ); while ( insurance-- ) { // choose random integer a s.t. 1 < a < n - 1 T a; int index; for ( index = 0; index < sizeof( a ) / sizeof( a[ 0 ] ); index++ ) a[ index ] = randomMT(); umodulo( a, n1, a ); // compute y = a ^ r (mod n) T y; simpleModExp( a, r, n, y ); if ( !equal( y, one ) && !equal( y, n1 ) ) { u32 j = s; while ( ( j-- > 1 ) && !equal( y, n1 ) ) { umultiply( y, y, dy ); umodulo( dy, dn, dy ); takelow( y, dy ); if ( equal( y, one ) ) return false; } if ( !equal( y, n1 ) ) return false; } } return true; } // generates a strong pseudo-prime BIGONETYPE void generateStrongPseudoPrime( T &n ) { do { int index; for ( index = 0; index < sizeof( n ) / sizeof( n[ 0 ] ); index++ ) n[ index ] = randomMT(); n[ BIGWORDCOUNT( T ) - 1 ] |= WORDHIGHBIT; //n[BIGWORDCOUNT(T) - 1] &= ~WORDHIGHBIT; n[BIGWORDCOUNT(T) - 1] |= WORDHIGHBIT >> 1; n[ 0 ] |= 1; } while ( !RabinMillerPrimalityTest( n, 5 ) ); } #endif // RSASUPPORTGENPRIME //////// RSACrypt class //////// BIGONETYPE class RSACrypt { // public key T e, n; T np, xp; // private key bool factorsAvailable; T d, phi; BIGHALFSIZE( T, p ); BIGHALFSIZE( T, pnp ); BIGHALFSIZE( T, pxp ); BIGHALFSIZE( T, q ); BIGHALFSIZE( T, qnp ); BIGHALFSIZE( T, qxp ); BIGHALFSIZE( T, pInverse ); public: RSACrypt() { reset(); } ~RSACrypt() { reset(); } public: void reset() { zero( d ); zero( p ); zero( q ); zero( pInverse ); factorsAvailable = false; } #ifdef RSASUPPORTGENPRIME void generateKeys() { BIGHALFSIZE( T, p0 ); BIGHALFSIZE( T, q0 ); generateStrongPseudoPrime( p0 ); generateStrongPseudoPrime( q0 ); setPrivateKey( p0, q0 ); } #endif // RSASUPPORTGENPRIME BIGSMALLTYPE void setPrivateKey( Smaller &c_p, Smaller &c_q ) { factorsAvailable = true; // re-order factors s.t. q > p if ( ugreater( c_p, c_q ) ) { set ( q, c_p ) ; set ( p, c_q ) ; } else { set ( p, c_p ) ; set ( q, c_q ) ; } // phi = (p - 1)(q - 1) BIGHALFSIZE( T, p1 ); BIGHALFSIZE( T, q1 ); set ( p1, p ) ; decrement( p1 ); set ( q1, q ) ; decrement( q1 ); umultiply( p1, q1, phi ); // compute e computePublicExponent( phi, e ); // compute d computePrivateExponent( e, phi, d ); // compute p^-1 mod q computeModularInverse( p, q, pInverse ); // compute n = pq umultiply( p, q, n ); // find n' computeNRinverse( n, np ); // x' = 1*r (mod n) rModn( n, xp ); // find pn' computeNRinverse( p, pnp ); // computeNRinverse2(p, pnp); // px' = 1*r (mod p) rModn( p, pxp ); // find qn' computeNRinverse( q, qnp ); // qx' = 1*r (mod q) rModn( q, qxp ); } void setPublicKey( u32 c_e, T &c_n ) { reset(); // in case we knew a private key usetw( e, c_e ); set ( n, c_n ) ; // find n' computeNRinverse( n, np ); // x' = 1*r (mod n) rModn( n, xp ); } public: void getPublicKey( u32 &c_e, T &c_n ) { c_e = e[ 0 ]; set ( c_n, n ) ; } BIGSMALLTYPE void getPrivateKey( Smaller &c_p, Smaller &c_q ) { set ( c_p, p ) ; set ( c_q, q ) ; } public: void encrypt( T &M, T &x ) { if ( factorsAvailable ) CRTModExp( x, M, e, p, q, pInverse, pnp, pxp, qnp, qxp ); else simpleModExp( M, e, n, x ); } void decrypt( T &C, T &x ) { if ( factorsAvailable ) CRTModExp( x, C, d, p, q, pInverse, pnp, pxp, qnp, qxp ); } }; } #endif // RSACRYPT_H blobby-1.0rc3/src/raknet/SimpleMutex.cpp0000644000175000017500000000625212042452367021610 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Simple Mutex * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "SimpleMutex.h" #include ////#include "MemoryManager.h" SimpleMutex::SimpleMutex() { #ifdef _WIN32 // hMutex = CreateMutex(NULL, FALSE, 0); // assert(hMutex); InitializeCriticalSection(&criticalSection); #else int error = pthread_mutex_init(&hMutex, 0); assert(error==0); #endif } SimpleMutex::~SimpleMutex() { #ifdef _WIN32 // CloseHandle(hMutex); DeleteCriticalSection(&criticalSection); #else pthread_mutex_destroy(&hMutex); #endif } #ifdef _WIN32 #ifdef _DEBUG #include #endif #endif void SimpleMutex::Lock(void) { #ifdef _WIN32 /* DWORD d = WaitForSingleObject(hMutex, INFINITE); #ifdef _DEBUG if (d==WAIT_FAILED) { LPVOID messageBuffer; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &messageBuffer, 0, NULL ); // Process any inserts in messageBuffer. // ... // Display the string. //MessageBox( NULL, (LPCTSTR)messageBuffer, "Error", MB_OK | MB_ICONINFORMATION ); printf("SimpleMutex error: %s", messageBuffer); // Free the buffer. LocalFree( messageBuffer ); } assert(d==WAIT_OBJECT_0); */ EnterCriticalSection(&criticalSection); #else int error = pthread_mutex_lock(&hMutex); assert(error==0); #endif } void SimpleMutex::Unlock(void) { #ifdef _WIN32 // ReleaseMutex(hMutex); LeaveCriticalSection(&criticalSection); #else int error = pthread_mutex_unlock(&hMutex); assert(error==0); #endif } blobby-1.0rc3/src/raknet/NetworkTypes.cpp0000644000175000017500000000464312042452367022014 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Unique Player Identifier Class implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "NetworkTypes.h" int operator==( const PlayerID& left, const PlayerID& right ) { return left.binaryAddress == right.binaryAddress && left.port == right.port; } int operator!=( const PlayerID& left, const PlayerID& right ) { return left.binaryAddress != right.binaryAddress || left.port != right.port; } int operator>( const PlayerID& left, const PlayerID& right ) { return ( ( left.binaryAddress > right.binaryAddress ) || ( ( left.binaryAddress == right.binaryAddress ) && ( left.port > right.port ) ) ); } int operator<( const PlayerID& left, const PlayerID& right ) { return ( ( left.binaryAddress < right.binaryAddress ) || ( ( left.binaryAddress == right.binaryAddress ) && ( left.port < right.port ) ) ); } blobby-1.0rc3/src/raknet/HuffmanEncodingTree.h0000644000175000017500000000646012042452367022655 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file HuffmanEncodingTree.h * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __HUFFMAN_ENCODING_TREE #define __HUFFMAN_ENCODING_TREE #include "HuffmanEncodingTreeNode.h" #include "BitStream.h" #include "LinkedList.h" /** * This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 */ class HuffmanEncodingTree { public: HuffmanEncodingTree(); ~HuffmanEncodingTree(); /** * Pass an array of bytes to array and a preallocated BitStream to receive the output */ void EncodeArray( unsigned char *input, int sizeInBytes, RakNet::BitStream * output ); /** * Two versions that perform the same operation. * The second version also the number of bytes in the stream, which may be greater than the max chars to write */ int DecodeArray( RakNet::BitStream * input, int sizeInBits, int maxCharsToWrite, unsigned char *output ); void DecodeArray( unsigned char *input, int sizeInBits, RakNet::BitStream * output ); /** * Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree */ void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); /** * Free the memory used by the tree */ void FreeMemory( void ); private: /** * The root node of the tree */ HuffmanEncodingTreeNode *root; /** * Used to hold bit encoding for one character */ struct CharacterEncoding { unsigned char* encoding; unsigned short bitLength; }; CharacterEncoding encodingTable[ 256 ]; void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, BasicDataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; }; #endif blobby-1.0rc3/src/raknet/ExtendedOverlappedPool.h0000644000175000017500000000447012042452367023415 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file ExtendedOverlappedPool.h * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef __USE_IO_COMPLETION_PORTS #ifndef __EXTENDED_OVERLAPPED_POOL #define __EXTENDED_OVERLAPPED_POOL #include "SimpleMutex.h" #include "ClientContextStruct.h" #include "RakNetQueue.h" /** * @internal * @todo Document this class */ class ExtendedOverlappedPool { public: ExtendedOverlappedPool(); ~ExtendedOverlappedPool(); ExtendedOverlappedStruct* GetPointer( void ); void ReleasePointer( ExtendedOverlappedStruct *p ); static inline ExtendedOverlappedPool* Instance() { return & I; } private: BasicDataStructures::Queue pool; SimpleMutex poolMutex; static ExtendedOverlappedPool I; }; #endif #endif blobby-1.0rc3/src/raknet/RakClient.h0000644000175000017500000006031612042452367020656 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Client communication End Point Declaration * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_CLIENT_H #define __RAK_CLIENT_H #include "RakPeer.h" #include "RakClientInterface.h" /** * @brief Client Peer A client peer is used to contact a server. It * can connect to one server a a time. If you need to connect to * multiple server at the same time think of using a RakPeer instead * of a RakClient. * * @see RakServer */ class RakClient : public RakPeer, public RakClientInterface { public: /** * Constructor */ RakClient(); /** * Destructor */ virtual ~RakClient(); /** * Call this to connect the client to the specified host (ip or domain name) and server port. * This is a non-blocking connection. You know the connection is successful when IsConnected() returns true * or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED. * serverPort is which port to connect to on the remote machine. clientPort is the port you want the * client to use. Both ports must be open for UDP * * @param host a hostname * @param serverPort The port on which to contact @em host * @param clientPort The port to use localy * @param depreciated is legacy and unused * @param threadSleepTimer >=0 for how many ms to Sleep each internal update cycle * (recommended 30 for low performance, 0 for regular) * @return true on successful initiation, false otherwise */ bool Connect( const char* host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer ); /** * Stops the client, stops synchronized data, and resets all internal data. * Does nothing if the client is not connected to begin with * blockDuration is how long you should wait for all remaining packets to go out * If you set it to 0 then the disconnection notification probably won't arrive * @param blockDuration The time to wait before truly close the communication and point */ void Disconnect( unsigned int blockDuration ); /** * Can be called to use specific public RSA keys. (e and n) * In order to prevent altered keys. Will return ID_RSA_PUBLIC_KEY_MISMATCH in a packet * If a key has been altered. * * @param privKeyP Private keys generated from the RSACrypt class. Can be 0 * @param privKeyQ Private keys generated from the RSACrypt class. Can be 0 * @see Encryption sample. */ void InitializeSecurity( const char *privKeyP, const char *privKeyQ ); /** * Set the password to use when connecting to a server. The password persists between connections. * Pass 0 for no password. * @param _password The password to use to connect to a server */ void SetPassword( const char *_password ); /** * Returns true if a password was set, false otherwise * @return true if a password has previously been set using SetPassword */ bool HasPassword( void ) const; /** * This function only works while the client is connected (Use the * Connect function). Returns false on failure, true on success * Sends the data stream of length length If you aren't sure what to * specify for priority and reliability, use HIGH_PRIORITY and * RELIABLE, 0 for ordering channel * @param data a byte buffer * @param length the size of the byte buffer * @param priority the priority of the message * @param reliability the reliability policy required * @param orderingChannel the channel to send the message to. */ bool Send( const char *data, const long length, PacketPriority priority, PacketReliability reliability, char orderingChannel ); /** * This function only works while the client is connected (Use the * Connect function). Returns false on failure, true on success * Sends the BitStream If you aren't sure what to specify for * priority and reliability, use HIGH_PRIORITY and RELIABLE, 0 for * ordering channel * @param bitstream the data to send. * @param priority the priority of the message * @param reliability the reliability policy required * @param orderingChannel the channel to send the message to. */ bool Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel ); /** * Call this to get a packet from the incoming packet queue. Use * DeallocatePacket to deallocate the packet after you are done with * it. Check the Packet struct at the top of * CoreNetworkStructures.h for the format of the struct Returns 0 if * no packets are waiting to be handled If the client is not active * this will also return 0, as all waiting packets are flushed when * the client is Disconnected This also updates all memory blocks * associated with synchronized memory * @return the last receive packet */ Packet* Receive( void ); /** * Call this to deallocate a packet returned by Receive when you are done handling it. * Free the memory associated to a packet. It is not the same as using delete operator because * RakNet might decide not to delete right now the packet in order to use it later. * @param packet the packet to deallocate. */ void DeallocatePacket( Packet *packet ); /** * Send a ping request to the server.Occasional pings are on by * default (see StartOccasionalPing and StopOccasionalPing) so * unless you turn them off it is not necessary to call this * function. It is here for completeness if you want it Does * nothing if the client is not connected to begin with */ void PingServer( void ); /** * Sends a ping request to a server we are not connected to. This will also initialize the * networking system if it is not already initialized. You can stop the networking system * by calling Disconnect() * The final ping time will be encoded in the following 4 bytes (2-5) as an unsigned int * You can specify if the server should only reply if it has an open connection or not * This must be true for LAN broadcast server discovery on "255.255.255.255" * or you will get replies from clients as well. * @param host The host to contact * @param ServerPort the port used by the server * @param clientPort the port used to receive the answer * @param onlyReplyOnAcceptingConnections if true the server must be ready to accept incomming connection. */ void PingServer( const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections ); /** * Returns the average of all ping times read * @return the average ping value to the server */ int GetAveragePing( void ); /** * Returns the last ping time read for the specific player or -1 if none read yet * @return last ping value */ int GetLastPing( void ) const; /** * Returns the lowest ping time read or -1 if none read yet * @return lowest ping value */ int GetLowestPing( void ) const; /** * Returns the last ping for the specified player. This information * is broadcast by the server automatically In order to save * bandwidth this information is updated only infrequently and only * for the first 32 players * @param playerId The id of the player you want to have the ping (it might be your id) * @return the last ping for this player * @note You can read your own ping with * this method by passing your own playerId, however for more * up-to-date readings you should use one of the three functions * above * */ int GetPlayerPing( PlayerID playerId ); /** * Ping the server every so often. This is on by default. In games * where you don't care about ping you can call StopOccasionalPing * to save the bandwidth This will work anytime */ void StartOccasionalPing( void ); /** * Stop pinging the server every so often. The server is pinged by * default. In games where you don't care about ping you can call * this to save the bandwidth This will work anytime */ void StopOccasionalPing( void ); /** * Returns true if the client is connected to a responsive server * @return true if connected to a server */ bool IsConnected( void ) const; /** * Returns a number automatically synchronized between the server * and client which randomly changes every 9 seconds. The time it * changes is accurate to within a few ms and is best used to seed * random number generators that you want to usually return the same * output on all systems. Keep in mind this isn't perfectly * accurate as there is always a very small chance the numbers will * by out of synch during changes so you should confine its use to * visual effects or functionality that has a backup method to * maintain synchronization. If you don't need this functionality * and want to save the bandwidth call StopSynchronizedRandomInteger * after starting the server * @return A random int common to all client and to the server. */ unsigned int GetSynchronizedRandomInteger( void ) const; /* * Call this to automatically synchronize a block of memory. * Unique identifier should be an integer corresponding to the same variable between clients and the server. This integer * should start at 0 and not surpass the range of UniqueIDType. It is recommended you set this from an enum * memoryBlock should point to the data you want to read from or write to with size of size in bytes * isAuthority should be true if all other computers should match their data in memory block to yours. This is triggered by * when the variable changes. So setting it to true on both the server and one client would make it so if the synchronized * variable on that client changed, the server would then relay it to all clients. * In the current implementation, setting isAuthority to true on the server will cause changes to that variable to be broadcast to * all connected clients. * synchronizationRules is an optional function pointer defined by you. It should * return true if the two passed memory blocks are sufficiently different to synchronize them. This is an optimization so * data that changes rapidly, such as per-frame, can be made to not update every frame * The first parameter to synchronizationRules is the new data, the second is the internal copy of the old data * secondaryUniqueIdentifier is optional and used when you have the same unique identifier and is intended for multiple instances of a class * that derives from NetworkObject. * You can call this anytime - however if you call it before the connection is complete initial data will not by synchronized void SynchronizeMemory(UniqueIDType uniqueIdentifier, char *memoryBlock, unsigned short size, bool isAuthority, bool (*synchronizationRules) (char*,char*)=0,ObjectID secondaryUniqueIdentifier=UNASSIGNED_OBJECT_ID); * Call this to stop synchronization of a block of memory previously defined by uniqueIdentifier and secondaryUniqueIdentifier * by the call to SynchronizeMemory * CALL THIS BEFORE SYNCHRONIZED MEMORY IS DEALLOCATED! * It is not necessary to call this before disconnecting, as all synchronized states will be released then. void DesynchronizeMemory(UniqueIDType uniqueIdentifier, ObjectID secondaryUniqueIdentifier=UNASSIGNED_OBJECT_ID); * Call this to Desynchronize all synchronized memory void DesynchronizeAllMemory(void); */ /** * This is an optional function to generate the compression layer * from the input frequency table. You should call this twice - * once with inputLayer as true and once as false. The frequency * table passed here with inputLayer=true should match the frequency * table on the recipient with inputLayer=false. Likewise, the * frequency table passed here with inputLayer=false should match * the frequency table on the recipient with inputLayer=true Calling * this function when there is an existing layer will overwrite the * old layer You should only call this when disconnected * * @param inputFrenquencyTable the table to used for compression * @param inputLayer says if the @em inputFrequencyTable should be used for * sending or receiveing. * @return false (failure) if connected. Otherwise true (success) * * @note The server Sends should share the same inputFrequencyTable * as the client for receiving. It's also true for the client sending and the server receiving. */ bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ); /** * Delete the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory * You should only call this when disconnected * @param inputLayer Delete the compression layer for sending or for receiving ? * @return false (failure) if connected. Otherwise true (success) */ bool DeleteCompressionLayer( bool inputLayer ); /** * Enables or disables frequency table tracking. This is required * to get a frequency table, which is used to generate A new * compression layer. You can call this at any time - however you * SHOULD only call it when disconnected. Otherwise you will only * track part of the values sent over the network. This value * persists between connect calls and defaults to false (no * frequency tracking) * @param b true to unable tracking. */ void SetTrackFrequencyTable( bool b ); /** * Returns the frequency of outgoing bytes into outputFrequencyTable * The purpose is to save to file as either a master frequency table * from a sample game session for passing to * GenerateCompressionLayer. You should only call this when * disconnected. Requires that you first enable data frequency * tracking by calling SetTrackFrequencyTable(true) * @param outputFrequencyTable The frequency table produce during the tracking time. * @return false (failure) if connected or if frequency table tracking is * not enabled. Otherwise true (success) */ bool GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ); /** * Returns the compression ratio. A low compression ratio is good. Compression is for outgoing data * @return the current compression ratio */ float GetCompressionRatio( void ) const; /** * Returns the decompression ratio. A high decompression ratio is good. Decompression is for incoming data * @return the current decompression ratio */ float GetDecompressionRatio( void ) const; /** * Attatches a message handler interface to run code automatically on message receipt in the Receive call * * @param messageHandler Pointer to a message handler to attach */ void AttachMessageHandler( MessageHandlerInterface *messageHandler ); /** * Detatches a message handler interface to run code automatically on message receipt * * @param messageHandler Pointer to a message handler to detatch */ void DetachMessageHandler( MessageHandlerInterface *messageHandler ); /** * The server internally maintains a data struct that is * automatically sent to clients when the connect. This is useful * to contain data such as the server name or message of the day. * Access that struct with this function. The data is entered as an * array and stored and returned as a BitStream. Everytime you call * GetStaticServerData it resets the read pointer to the start of * the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the server may change at any time the * data contents and/or its length! * @return a bitstream containing statistics. */ RakNet::BitStream * GetStaticServerData( void ); /** * The server internally maintains a data struct that is * automatically sent to clients when the connect. This is useful * to contain data such as the server name or message of the day. * Access that struct with this function. The data is entered as an * array and stored and returned as a BitStream. Everytime you call * GetStaticServerData it resets the read pointer to the start of * the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters * Note that the server may change at any time the * data contents and/or its length! * @param data a byte buffer containing statistical information. * @param length the size of @em data */ void SetStaticServerData( const char *data, const long length ); /** * The client internally maintains a data struct that is automatically sent to the server on connection * This is useful to contain data such as the player name. Access that struct with this * function. Pass UNASSIGNED_PLAYER_ID for playerId to reference your internal data. A playerId value to access the data of another player. * *** NOTE *** * If you change any data in the struct the server won't reflect this change unless you manually update it * Do so by calling SendStaticClientDataToServer * The data is entered as an array and stored and returned as a BitStream. * Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer * Maintain a pointer copy to the bitstream as in * RakNet::BitStream *copy = ...->GetStaticServerData(...); * To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods * of the bitstream for the 2nd and 3rd parameters */ RakNet::BitStream * GetStaticClientData( PlayerID playerId ); /** * Set Local statistical information for playId. Call this * function when you receive statistical information from a * client. * * @param playerId the player ID * @param data the packet data * @param length the size of the data */ void SetStaticClientData( PlayerID playerId, const char *data, const long length ); /** * Send the static server data to the server The only time you need * to call this function is to update clients that are already * connected when you change the static server data by calling * GetStaticServerData and directly modifying the object pointed to. * Obviously if the connected clients don't need to know the new * data you don't need to update them, so it's up to you The server * must be active for this to have meaning */ void SendStaticClientDataToServer( void ); /** * Return the player number of the server. * @return the server playerID */ PlayerID GetServerID( void ) const; /** * Return the player number the server has assigned to you. * * @return our player ID * @note that unlike in previous versions, this is a struct and is not sequential * */ PlayerID GetPlayerID( void ) const; /** * Returns the dotted IP address for the specified playerId * * @param playerId Any player ID other than UNASSIGNED_PLAYER_ID, * even if that player is not currently connected * @return a dotted notation string representation of the address of playerId. */ const char* PlayerIDToDottedIP( PlayerID playerId ) const; /** * Put a packet back at the end of the receive queue in case you don't want to deal with it immediately * @param packet the packet to delayed */ void PushBackPacket( Packet *packet ); /** * Change the MTU size in order to improve performance when sending large packets * This can only be called when not connected. * Returns false on failure (we are connected). True on success. Maximum allowed size is MAXIMUM_MTU_SIZE * A too high of value will cause packets not to arrive at worst and be fragmented at best. * A too low of value will split packets unnecessarily. * Set according to the following table: * 1500. The largest Ethernet packet size; it is also the default value. * This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. * 1492. The size PPPoE prefers. * 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) * 1468. The size DHCP prefers. * 1460. Usable by AOL if you don't have large email attachments, etc. * 1430. The size VPN and PPTP prefer. * 1400. Maximum size for AOL DSL. * 576. Typical value to connect to dial-up ISPs. (Default) */ bool SetMTUSize( int size ); /** * Returns the current MTU size */ int GetMTUSize( void ) const; /** * Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary * when connection to servers with multiple IP addresses. * * * @param allow True to allow this behavior, false to not allow. * Defaults to false. Value persists between connections */ void AllowConnectionResponseIPMigration( bool allow ); /** * Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. * This will tell the remote system our external IP outside the LAN, and can be used for NAT punch through * * @param host Either a dotted IP address or a domain name * @param remotePort Which port to connect to on the remote machine. * @param data Optional data to append to the packet. * @param dataLength length of data in bytes. Use 0 if no data. */ void AdvertiseSystem( char *host, unsigned short remotePort, const char *data, int dataLength ); /** * Returns a structure containing a large set of network statistics for the server/client connection * You can map this data to a string using the C style StatisticsToString function * * @return 0 on can't find the specified system. A pointer to a set of data otherwise. */ RakNetStatisticsStruct * const GetStatistics( void ); /** * @internal * Retrieve the player index corresponding to this client. */ PlayerIndex GetPlayerIndex( void ); private: /** * Get the player index of another client * @param playerId the id of a client * @return the index */ int GetOtherClientIndexByPlayerID( PlayerID playerId ); /** * Get one free client index. * @return an unsued yet index */ int GetFreeOtherClientIndex( void ); /** * Store the password for this client */ RakNet::BitStream password; /** * Store other client information */ struct OtherClientsStruct { /** * The id of the other player */ PlayerID playerId; /** * The average ping time */ short ping; /** * Other client's Static Client Data */ RakNet::BitStream staticData; /** * Tel whether the remote client is active or not */ bool isActive; } otherClients[ 32 ]; /** * Synchronized random integer */ unsigned int seed; /** * Synchronized random integer */ unsigned int nextSeed; /** * Synchronized random integer */ unsigned int nextSeedUpdate; /** * Our local index */ PlayerIndex localPlayerIndex; /** * This is our external ID (and also IP) (returned from the server) */ PlayerID externalPlayerID; }; #endif blobby-1.0rc3/src/raknet/AsynchronousFileIO.cpp0000644000175000017500000002241212042452367023053 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Asynchronous IO mecanisms implementation. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef __USE_IO_COMPLETION_PORTS #include "AsynchronousFileIO.h" #include "ClientContextStruct.h" #include #include "ExtendedOverlappedPool.h" #include #include // All these are used for the Read callback. For general Asynch file IO you would change these #include "NetworkTypes.h" class RakPeer; #ifdef _WIN32 extern void __stdcall ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ); #else extern void ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer ); #endif AsynchronousFileIO AsynchronousFileIO::I; AsynchronousFileIO::AsynchronousFileIO() { userCount = 0; threadCount = 0; completionPort = NULL; // Determine how many processors are on the system. GetSystemInfo( &systemInfo ); } void AsynchronousFileIO::IncreaseUserCount() { userCountMutex.Lock(); ++userCount; if ( userCount == 1 ) { // Create the completion port that will be used by all the worker // threads. completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, systemInfo.dwNumberOfProcessors * 2 ); if ( completionPort == NULL ) { userCount = 0; userCountMutex.Unlock(); return ; } UINT nThreadID; HANDLE workerHandle; // Create worker threads // One worker thread per processor for ( DWORD i = 0; i < systemInfo.dwNumberOfProcessors * 2; i++ ) // In debug just make one worker thread so it's easier to trace //for ( i = 0; i < systemInfo.dwNumberOfProcessors * 1; i++ ) { workerHandle = ( HANDLE ) _beginthreadex( NULL, // Security 0, // Stack size - use default ThreadPoolFunc, // Thread fn entry point ( void* ) completionPort, // Param for thread 0, // Init flag &nThreadID ); // Thread address // Feel free to comment this out for regular thread priority SetThreadPriority( workerHandle, THREAD_PRIORITY_HIGHEST ); CloseHandle( workerHandle ); } // Wait for the threads to start while ( threadCount < systemInfo.dwNumberOfProcessors * 2 ) Sleep( 0 ); } userCountMutex.Unlock(); } void AsynchronousFileIO::DecreaseUserCount() { userCountMutex.Lock(); assert( userCount > 0 ); if ( userCount == 0 ) return ; userCount--; if ( userCount == 0 ) Shutdown(); userCountMutex.Unlock(); } void AsynchronousFileIO::Shutdown( void ) { killThreads = true; if ( completionPort != NULL ) for ( DWORD i = 0; i < systemInfo.dwNumberOfProcessors * 2; i++ ) PostQueuedCompletionStatus( completionPort, 0, 0 , 0 ); // Kill worker threads while ( threadCount > 0 ) Sleep( 0 ); if ( completionPort != NULL ) CloseHandle( completionPort ); } int AsynchronousFileIO::GetUserCount( void ) { return userCount; } AsynchronousFileIO::~AsynchronousFileIO() { if ( threadCount > 0 ) Shutdown(); } bool AsynchronousFileIO::AssociateSocketWithCompletionPort( SOCKET socket, DWORD dwCompletionKey ) { HANDLE h = CreateIoCompletionPort( ( HANDLE ) socket, completionPort, dwCompletionKey, 0 ); return h == completionPort; } BOOL ReadAsynch( HANDLE handle, ExtendedOverlappedStruct *extended ) { BOOL success; extended->read = true; success = ReadFile( handle, extended->data, extended->length, 0, ( LPOVERLAPPED ) extended ); if ( !success ) { DWORD dwErrCode = GetLastError(); if ( dwErrCode != ERROR_IO_PENDING ) { #if defined(_WIN32) && defined(_DEBUG) LPVOID messageBuffer; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwErrCode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language ( LPTSTR ) & messageBuffer, 0, NULL ); // something has gone wrong here... printf( "ReadFile failed:Error code - %d\n%s", dwErrCode, messageBuffer ); //Free the buffer. LocalFree( messageBuffer ); #endif return FALSE; } } return TRUE; } void WriteAsynch( HANDLE handle, ExtendedOverlappedStruct *extended ) { //printf("Beginning asynch write of %i bytes.\n",extended->length); //for (int i=0; i < extended->length && i < 10; i++) // printf("%i ", extended->data[i]); //printf("\n\n"); BOOL success; extended->read = false; success = WriteFile( handle, extended->data, extended->length, 0, ( LPOVERLAPPED ) extended ); if ( !success ) { DWORD dwErrCode = GetLastError(); if ( dwErrCode != ERROR_IO_PENDING ) { #if defined(_WIN32) && defined(_DEBUG) LPVOID messageBuffer; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwErrCode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language ( LPTSTR ) & messageBuffer, 0, NULL ); // something has gone wrong here... printf( "WriteFile failed:Error code - %d\n%s", dwErrCode, messageBuffer ); //Free the buffer. LocalFree( messageBuffer ); #endif } } } unsigned __stdcall ThreadPoolFunc( LPVOID arguments ) { DWORD dwIoSize; ClientContextStruct* lpClientContext; ExtendedOverlappedStruct* lpOverlapped; LPOVERLAPPED temp; BOOL bError; HANDLE *completionPort = ( HANDLE * ) arguments; AsynchronousFileIO::Instance()->threadCount++; while ( 1 ) { // Get a completed IO request. BOOL returnValue = GetQueuedCompletionStatus( completionPort, &dwIoSize, ( LPDWORD ) & lpClientContext, &temp, INFINITE ); lpOverlapped = ( ExtendedOverlappedStruct* ) temp; DWORD dwIOError = GetLastError(); if ( lpOverlapped == 0 ) break; // Cancelled thread if ( !returnValue && dwIOError != WAIT_TIMEOUT ) { if ( dwIOError != ERROR_OPERATION_ABORTED ) { // Print all but this very common error message #if defined(_WIN32) && defined(_DEBUG) LPVOID messageBuffer; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language ( LPTSTR ) & messageBuffer, 0, NULL ); // something has gone wrong here... printf( "GetQueuedCompletionStatus failed:Error code - %d\n%s", dwIOError, messageBuffer ); //Free the buffer. LocalFree( messageBuffer ); #endif } HANDLE_ERROR: // Some kind of error. Erase the data for this call bError = true; // This socket is no longer used if ( lpOverlapped ) delete lpOverlapped; if ( lpClientContext ) delete lpClientContext; // If we are killing the threads, then we keep posting fake completion statuses until we get a fake one through the queue (i.e. lpOverlapped==0 as above) // This way we delete all the data from the real calls before exiting the thread if ( AsynchronousFileIO::Instance()->killThreads ) { PostQueuedCompletionStatus( completionPort, 0, 0, 0 ); } } else bError = false; if ( !bError ) { if ( returnValue && NULL != lpOverlapped && NULL != lpClientContext ) { if ( lpOverlapped->read == true ) { assert( dwIoSize > 0 ); ProcessNetworkPacket( lpOverlapped->binaryAddress, lpOverlapped->port, lpOverlapped->data, dwIoSize, lpOverlapped->rakPeer ); // Issue a new read so we always have one outstanding read per socket // Finished a read. Reuse the overlapped pointer bError = ReadAsynch( lpClientContext->handle, lpOverlapped ); if ( !bError ) goto HANDLE_ERROR; // Windows is super unreliable! } else { // AsynchronousFileIO::Instance()->Write(lpClientContext); // Finished a write ExtendedOverlappedPool::Instance()->ReleasePointer( lpOverlapped ); } } else assert( 0 ); } } AsynchronousFileIO::Instance()->threadCount--; return 0; } #endif blobby-1.0rc3/src/raknet/RakNetworkFactory.h0000644000175000017500000000630412042452367022416 0ustar danielknobedanielknobe /* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief RakNetworkFactory class is a factory for communication End Point. * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __RAK_NETWORK_FACTORY_H #define __RAK_NETWORK_FACTORY_H class RakClientInterface; class RakServerInterface; class RakPeerInterface; #ifdef _WIN32 #define RAK_DLL_EXPORT __declspec(dllexport) #else // Unix needs no export, but for name mangling, keep the function name // clean. If you omit the 'extern "C"', the .so names will be // compiler dependent. #define RAK_DLL_EXPORT extern "C" #endif #if defined(DLL_EXPORTS) || defined(_USRDLL) class RAK_DLL_EXPORT RakNetworkFactory #else //class __declspec( dllimport ) RakNetworkFactor /** * @brief Communication End Point Provider * * This class is in charge of creating and managing Peers. You should always * pass throught this class to get a communication End Point. */ class RakNetworkFactory #endif { public: /** * Returns a new instance of the network client. */ static RakClientInterface* GetRakClientInterface( void ); /** * Returns a new instance of the network server. */ static RakServerInterface* GetRakServerInterface( void ); /** * Returns a new instance of the network server. */ static RakPeerInterface* GetRakPeerInterface( void ); /** * Destroys an instance of the network client. */ static void DestroyRakClientInterface( RakClientInterface* i ); /** * Destroys an instance of the network server. */ static void DestroyRakServerInterface( RakServerInterface* i ); /** * Destroys an instance of the network server. */ static void DestroyRakPeerInterface( RakPeerInterface* i ); }; #endif blobby-1.0rc3/src/raknet/HuffmanEncodingTreeNode.h0000644000175000017500000000425212042452367023460 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file HuffmanEncodingTreeNode.h * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __HUFFMAN_ENCODING_TREE_NODE #define __HUFFMAN_ENCODING_TREE_NODE /** * @todo BasicDataStructure namespace maybe * This structure define a node eof an Huffman Tree * */ struct HuffmanEncodingTreeNode { /** * Node value */ unsigned char value; /** * Node weight */ unsigned weight; /** * Left Child Node */ HuffmanEncodingTreeNode *left; /** * Right Child Node */ HuffmanEncodingTreeNode *right; /** * Parent Node */ HuffmanEncodingTreeNode *parent; }; #endif blobby-1.0rc3/src/raknet/SocketLayer.h0000644000175000017500000001430612042452367021225 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief Socket Layer Abstraction * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __SOCKET_LAYER_H #define __SOCKET_LAYER_H #ifdef _WIN32 #ifdef __USE_IO_COMPLETION_PORTS #include // DON'T FORGET TO INLCLUDE Ws2_32.lib to your project! #else #include #endif #else #include #include #include #include #include #include /** * typename for communication endpoint */ typedef int SOCKET; /** * Invalid socket */ static const SOCKET INVALID_SOCKET = -1; /** * Socket error */ #define SOCKET_ERROR -1 #endif #include "ClientContextStruct.h" class RakPeer; /** * the SocketLayer provide platform independent Socket implementation */ class SocketLayer { public: /** * Default Constructor */ SocketLayer(); /** * Destructor */ ~SocketLayer(); /** * Get Singleton Instance of the Socket Layer unique object. * @return unique instance */ static inline SocketLayer* Instance() { return & I; } /** * Create a socket connected to a remote host * @param writeSocket The local socket * @param binaryAddress The address of the remote host * @param port the remote port * @return A new socket used for communication * @todo * Check for the binary address byte order * */ SOCKET Connect( SOCKET writeSocket, unsigned int binaryAddress, unsigned short port ); /** * Creates a socket to listen for incoming connections on the specified port * @param port the port number * @param blockingSocket * @return A new socket used for accepting clients */ SOCKET CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress ); const char* DomainNameToIP( const char *domainName ); #ifdef __USE_IO_COMPLETION_PORTS /** * Associate a socket to the completion port mecanism. * @param socket the socket * @param completionKey the completion port group identifier * @note Windows version only */ void AssociateSocketWithCompletionPort( SOCKET socket, ClientContextStruct* completionKey ); #endif /** * Start an asynchronous read using the specified socket. The * callback will use the specified PlayerID (associated with this * socket) and call either the client or the server callback (one or * the other should be 0) */ bool AssociateSocketWithCompletionPortAndRead( SOCKET readSocket, unsigned int binaryAddress, unsigned short port, RakPeer* rakPeer ); /** * Does a writing operation on a socket. * It Send a packet to a peer throught the network. * The socket must be connected * @param writeSocket the socket to use to do the communication * @param data a byte buffer containing the data * @param length the size of the byte buffer * return written bytes */ int Write( SOCKET writeSocket, const char* data, int length ); /** * Read data from a socket * @param s the socket * @param rakPeer * @param errorCode An error code if an error occured * @return Returns true if you successfully read data * @todo check the role of RakPeer * */ int RecvFrom( SOCKET s, RakPeer *rakPeer, int *errorCode ); /** * Retrieve all local IP address in a printable format * @param ipList An array of ip address in dot format. */ void GetMyIP( char ipList[ 10 ][ 16 ] ); /** * Send data to a peer. The socket should not be connected to a remote host. * @param s the socket * @param data the byte buffer to send * @param length The length of the @em data * @param ip The address of the remote host in dot format * @param port The port number used by the remote host * @return 0 on success. * * @todo check return value */ int SendTo( SOCKET s, const char *data, int length, char ip[ 16 ], unsigned short port ); /** * Send data to a peer. The socket should not be connected to a remote host. * @param s the socket * @param data the byte buffer to send * @param length The length of the @em data * @param binaryAddress The peer address in binary format. * @param port The port number used by the remote host * @return 0 on success. * * @todo check return value */ int SendTo( SOCKET s, const char *data, int length, unsigned int binaryAddress, unsigned short port ); /// @brief Get the Ip address of an domain /// @param name Name of the domain /// @return Ip address /// @todo This is only for IPv4. IPv6 is not available jet const char* nameToIP(const char* name) const; private: /** * Tell whether or not the socket layer is already active */ static int socketLayerInstanceCount; /** * Singleton instance */ static SocketLayer I; }; #endif blobby-1.0rc3/src/raknet/DataBlockEncryptor.cpp0000644000175000017500000001641012042452367023063 0ustar danielknobedanielknobe/* -*- mode: c++; c-file-style: raknet; tab-always-indent: nil; -*- */ /** * @file * @brief DataBlockEncryptor Class Implementation * * Copyright (c) 2003, Rakkarsoft LLC and Kevin Jenkins * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "DataBlockEncryptor.h" #include "CheckSum.h" #include "GetTime.h" #include "Rand.h" #include #include #include "rijndael.h" #include "Types.h" DataBlockEncryptor::DataBlockEncryptor() { keySet = false; } DataBlockEncryptor::~DataBlockEncryptor() {} bool DataBlockEncryptor::IsKeySet( void ) const { return keySet; } void DataBlockEncryptor::SetKey( const unsigned char key[ 16 ] ) { keySet = true; //secretKeyAES128.set_key( key ); makeKey(&keyEncrypt, DIR_ENCRYPT, 16, (char*)key); makeKey(&keyDecrypt, DIR_DECRYPT, 16, (char*)key); cipherInit(&cipherInst, MODE_ECB, 0); // ECB is not secure except that I chain manually farther down. } void DataBlockEncryptor::UnsetKey( void ) { keySet = false; } void DataBlockEncryptor::Encrypt( unsigned char *input, int inputLength, unsigned char *output, int *outputLength ) { unsigned index, byteIndex, lastBlock; unsigned int checkSum; unsigned char paddingBytes; unsigned char encodedPad; unsigned char randomChar; CheckSum checkSumCalculator; #ifdef _DEBUG assert( keySet ); #endif assert( input && inputLength ); // randomChar will randomize the data so the same data sent twice will not look the same randomChar = (unsigned char) randomMT(); // 16-(((x-1) % 16)+1) // # of padding bytes is 16 -(((input_length + extra_data -1) % 16)+1) paddingBytes = (unsigned char) ( 16 - ( ( ( inputLength + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) - 1 ) % 16 ) + 1 ) ); // Randomize the pad size variable encodedPad = (unsigned char) randomMT(); encodedPad <<= 4; encodedPad |= paddingBytes; *outputLength = inputLength + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes; // Write the data first, in case we are overwriting ourselves if ( input == output ) memmove( output + sizeof( checkSum ) + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes, input, inputLength ); else memcpy( output + sizeof( checkSum ) + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes, input, inputLength ); // Write the random char memcpy( output + sizeof( checkSum ), ( char* ) & randomChar, sizeof( randomChar ) ); // Write the pad size variable memcpy( output + sizeof( checkSum ) + sizeof( randomChar ), ( char* ) & encodedPad, sizeof( encodedPad ) ); // Write the padding for ( index = 0; index < paddingBytes; index++ ) *( output + sizeof( checkSum ) + sizeof( randomChar ) + sizeof( encodedPad ) + index ) = (unsigned char) randomMT(); // Calculate the checksum on the data checkSumCalculator.add( output + sizeof( checkSum ), inputLength + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes ); checkSum = checkSumCalculator.get(); // Write checksum #ifdef HOST_ENDIAN_IS_BIG output[0] = checkSum&0xFF; output[1] = (checkSum>>8)&0xFF; output[2] = (checkSum>>16)&0xFF; output[3] = (checkSum>>24)&0xFF; #else memcpy( output, ( char* ) & checkSum, sizeof( checkSum ) ); #endif // AES on the first block // secretKeyAES128.encrypt16( output ); blockEncrypt(&cipherInst, &keyEncrypt, output, 16, output); lastBlock = 0; // Now do AES on every other block from back to front for ( index = *outputLength - 16; index >= 16; index -= 16 ) { for ( byteIndex = 0; byteIndex < 16; byteIndex++ ) output[ index + byteIndex ] ^= output[ lastBlock + byteIndex ]; //secretKeyAES128.encrypt16( output + index ); blockEncrypt(&cipherInst, &keyEncrypt, output+index, 16, output+index); lastBlock = index; } } bool DataBlockEncryptor::Decrypt( unsigned char *input, int inputLength, unsigned char *output, int *outputLength ) { unsigned index, byteIndex, lastBlock; unsigned int checkSum; unsigned char paddingBytes; unsigned char encodedPad; unsigned char randomChar; CheckSum checkSumCalculator; #ifdef _DEBUG assert( keySet ); #endif if ( input == 0 || inputLength < 16 || ( inputLength % 16 ) != 0 ) { return false; } // Unchain in reverse order for ( index = 16; ( int ) index <= inputLength - 16;index += 16 ) { // secretKeyAES128.decrypt16( input + index ); blockDecrypt(&cipherInst, &keyDecrypt, input + index, 16, input + index); for ( byteIndex = 0; byteIndex < 16; byteIndex++ ) { if ( index + 16 == ( unsigned ) inputLength ) input[ index + byteIndex ] ^= input[ byteIndex ]; else input[ index + byteIndex ] ^= input[ index + 16 + byteIndex ]; } lastBlock = index; }; // Decrypt the first block //secretKeyAES128.decrypt16( input ); blockDecrypt(&cipherInst, &keyDecrypt, input, 16, input); // Read checksum #ifdef HOST_ENDIAN_IS_BIG checkSum = (unsigned int)input[0] | (unsigned int)(input[1]<<8) | (unsigned int)(input[2]<<16)|(unsigned int)(input[3]<<24); #else memcpy( ( char* ) & checkSum, input, sizeof( checkSum ) ); #endif // Read the pad size variable memcpy( ( char* ) & encodedPad, input + sizeof( randomChar ) + sizeof( checkSum ), sizeof( encodedPad ) ); // Ignore the high 4 bytes paddingBytes = encodedPad & 0x0F; // Get the data length *outputLength = inputLength - sizeof( randomChar ) - sizeof( checkSum ) - sizeof( encodedPad ) - paddingBytes; // Calculate the checksum on the data. checkSumCalculator.add( input + sizeof( checkSum ), *outputLength + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes ); if ( checkSum != checkSumCalculator.get() ) return false; // Read the data if ( input == output ) memmove( output, input + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes, *outputLength ); else memcpy( output, input + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes, *outputLength ); return true; } blobby-1.0rc3/src/raknet/rijndael.h0000644000175000017500000001137212042452367020570 0ustar danielknobedanielknobe/* rijndael-alg-fst.h v2.0 August '99 * Optimised ANSI C code */ // This code is public, take a look in the LICENSE File /* * taken from the 'aescrypt' project: www.sf.net/projects/aescrypt * See LICENSE-EST for the license applicable to this file */ /* * Note: Although the routines claim to support 192 and 256 bit blocks, * don't take your chances - stick to the 128 bit (16 byte) blocks unless * you've run tests to prove that 192 and 256 are correctly supported. * - Cirilo */ #include #ifndef __RIJNDAEL_ALG_H #define __RIJNDAEL_ALG_H #define MAXKC (256/32) #define MAXROUNDS 14 typedef unsigned char word8; typedef unsigned short word16; typedef unsigned int word32; int rijndaelKeySched (word8 k[MAXKC][4], int keyBits, word8 rk[MAXROUNDS+1][4][4]); int rijndaelKeyEnctoDec (int keyBits, word8 W[MAXROUNDS+1][4][4]); int rijndaelEncrypt (word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4]); int rijndaelEncryptRound (word8 a[4][4], word8 rk[MAXROUNDS+1][4][4], int rounds); int rijndaelDecrypt (word8 a[16], word8 b[16], word8 rk[MAXROUNDS+1][4][4]); int rijndaelDecryptRound (word8 a[4][4], word8 rk[MAXROUNDS+1][4][4], int rounds); #endif /* __RIJNDAEL_ALG_H */ /* End of algorithm headers. begin the AES API header defs */ #ifndef __RIJNDAEL_API_H #define __RIJNDAEL_API_H /* rijndael-api-fst.h v2.0 August '99 * Optimised ANSI C code */ /* Defines: Add any additional defines you need */ #define DIR_ENCRYPT 0 /* Are we encrpyting? */ #define DIR_DECRYPT 1 /* Are we decrpyting? */ #define MODE_ECB 1 /* Are we ciphering in ECB mode? */ #define MODE_CBC 2 /* Are we ciphering in CBC mode? */ #define MODE_CFB1 3 /* Are we ciphering in 1-bit CFB mode? */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define BITSPERBLOCK 128 /* Default number of bits in a cipher block */ /* Error Codes - CHANGE POSSIBLE: inclusion of additional error codes */ #define BAD_KEY_DIR -1 /* Key direction is invalid, e.g., unknown value */ #define BAD_KEY_MAT -2 /* Key material not of correct length */ #define BAD_KEY_INSTANCE -3 /* Key passed is not valid */ #define BAD_CIPHER_MODE -4 /* Params struct passed to cipherInit invalid */ #define BAD_CIPHER_STATE -5 /* Cipher in wrong state (e.g., not initialized) */ #define BAD_BLOCK_LENGTH -6 #define BAD_CIPHER_INSTANCE -7 /* CHANGE POSSIBLE: inclusion of algorithm specific defines */ /* 14.Dec.2005 Cirilo: keys are now unsigned char rather than hex (ASCII) */ #define MAX_KEY_SIZE 32 /* # of unsigned char's needed to represent a key */ #define MAX_IV_SIZE 16 /* # bytes needed to represent an IV */ /* Typedefs: Typedef'ed data storage elements. Add any algorithm specific parameters at the bottom of the structs as appropriate. */ typedef unsigned char BYTE; /* The structure for key information */ typedef struct { BYTE direction; /* Key used for encrypting or decrypting? */ int keyLen; /* Length of the key */ char keyMaterial[MAX_KEY_SIZE+1]; /* Raw key data in ASCII, e.g., user input or KAT values */ /* The following parameters are algorithm dependent, replace or add as necessary */ int blockLen; /* block length */ word8 keySched[MAXROUNDS+1][4][4]; /* key schedule */ } keyInstance; /* The structure for cipher information */ typedef struct { /* changed order of the components */ BYTE mode; /* MODE_ECB, MODE_CBC, or MODE_CFB1 */ BYTE IV[MAX_IV_SIZE]; /* A possible Initialization Vector for ciphering */ /* Add any algorithm specific parameters needed here */ int blockLen; /* Sample: Handles non-128 bit block sizes (if available) */ } cipherInstance; /* Function protoypes */ /* CHANGED: makeKey(): parameter blockLen added this parameter is absolutely necessary if you want to setup the round keys in a variable block length setting cipherInit(): parameter blockLen added (for obvious reasons) */ int makeKey(keyInstance *key, BYTE direction, int keyLen, char *keyMaterial); int cipherInit(cipherInstance *cipher, BYTE mode, char *IV); int blockEncrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer); int blockDecrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer); int cipherUpdateRounds(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer, int Rounds); #endif /* __RIJNDAEL_API_H */ blobby-1.0rc3/src/tinyxml/tinyxmlerror.cpp0000644000175000017500000000337712042452371022332 0ustar danielknobedanielknobe/* www.sourceforge.net/projects/tinyxml Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "tinyxml.h" // The goal of the seperate error file is to make the first // step towards localization. tinyxml (currently) only supports // english error messages, but the could now be translated. // // It also cleans up the code a bit. // const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] = { "No error", "Error", "Failed to open file", "Error parsing Element.", "Failed to read Element name", "Error reading Element value.", "Error reading Attributes.", "Error: empty tag.", "Error reading end tag.", "Error parsing Unknown.", "Error parsing Comment.", "Error parsing Declaration.", "Error document empty.", "Error null (0) or unexpected EOF found in input stream.", "Error parsing CDATA.", "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", }; blobby-1.0rc3/src/tinyxml/tinystr.h0000644000175000017500000002000612042452371020721 0ustar danielknobedanielknobe/* www.sourceforge.net/projects/tinyxml This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TIXML_USE_STL #ifndef TIXML_STRING_INCLUDED #define TIXML_STRING_INCLUDED #include #include /* The support for explicit isn't that universal, and it isn't really required - it is used to check that the TiXmlString class isn't incorrectly used. Be nice to old compilers and macro it here: */ #if defined(_MSC_VER) && (_MSC_VER >= 1200 ) // Microsoft visual studio, version 6 and higher. #define TIXML_EXPLICIT explicit #elif defined(__GNUC__) && (__GNUC__ >= 3 ) // GCC version 3 and higher.s #define TIXML_EXPLICIT explicit #else #define TIXML_EXPLICIT #endif /* TiXmlString is an emulation of a subset of the std::string template. Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. Only the member functions relevant to the TinyXML project have been implemented. The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase a string and there's no more room, we allocate a buffer twice as big as we need. */ class TiXmlString { public : // The size type used typedef size_t size_type; // Error value for find primitive static const size_type npos; // = -1; // TiXmlString empty constructor TiXmlString () : rep_(&nullrep_) { } // TiXmlString copy constructor TiXmlString ( const TiXmlString & copy) : rep_(0) { init(copy.length()); memcpy(start(), copy.data(), length()); } // TiXmlString constructor, based on a string TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) { init( static_cast( strlen(copy) )); memcpy(start(), copy, length()); } // TiXmlString constructor, based on a string TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) { init(len); memcpy(start(), str, len); } // TiXmlString destructor ~TiXmlString () { quit(); } TiXmlString& operator = (const char * copy) { return assign( copy, (size_type)strlen(copy)); } TiXmlString& operator = (const TiXmlString & copy) { return assign(copy.start(), copy.length()); } // += operator. Maps to append TiXmlString& operator += (const char * suffix) { return append(suffix, static_cast( strlen(suffix) )); } // += operator. Maps to append TiXmlString& operator += (char single) { return append(&single, 1); } // += operator. Maps to append TiXmlString& operator += (const TiXmlString & suffix) { return append(suffix.data(), suffix.length()); } // Convert a TiXmlString into a null-terminated char * const char * c_str () const { return rep_->str; } // Convert a TiXmlString into a char * (need not be null terminated). const char * data () const { return rep_->str; } // Return the length of a TiXmlString size_type length () const { return rep_->size; } // Alias for length() size_type size () const { return rep_->size; } // Checks if a TiXmlString is empty bool empty () const { return rep_->size == 0; } // Return capacity of string size_type capacity () const { return rep_->capacity; } // single char extraction const char& at (size_type index) const { assert( index < length() ); return rep_->str[ index ]; } // [] operator char& operator [] (size_type index) const { assert( index < length() ); return rep_->str[ index ]; } // find a char in a string. Return TiXmlString::npos if not found size_type find (char lookup) const { return find(lookup, 0); } // find a char in a string from an offset. Return TiXmlString::npos if not found size_type find (char tofind, size_type offset) const { if (offset >= length()) return npos; for (const char* p = c_str() + offset; *p != '\0'; ++p) { if (*p == tofind) return static_cast< size_type >( p - c_str() ); } return npos; } void clear () { //Lee: //The original was just too strange, though correct: // TiXmlString().swap(*this); //Instead use the quit & re-init: quit(); init(0,0); } /* Function to reserve a big amount of data when we know we'll need it. Be aware that this function DOES NOT clear the content of the TiXmlString if any exists. */ void reserve (size_type cap); TiXmlString& assign (const char* str, size_type len); TiXmlString& append (const char* str, size_type len); void swap (TiXmlString& other) { Rep* r = rep_; rep_ = other.rep_; other.rep_ = r; } private: void init(size_type sz) { init(sz, sz); } void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } char* start() const { return rep_->str; } char* finish() const { return rep_->str + rep_->size; } struct Rep { size_type size, capacity; char str[1]; }; void init(size_type sz, size_type cap) { if (cap) { // Lee: the original form: // rep_ = static_cast(operator new(sizeof(Rep) + cap)); // doesn't work in some cases of new being overloaded. Switching // to the normal allocation, although use an 'int' for systems // that are overly picky about structure alignment. const size_type bytesNeeded = sizeof(Rep) + cap; const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); rep_ = reinterpret_cast( new int[ intsNeeded ] ); rep_->str[ rep_->size = sz ] = '\0'; rep_->capacity = cap; } else { rep_ = &nullrep_; } } void quit() { if (rep_ != &nullrep_) { // The rep_ is really an array of ints. (see the allocator, above). // Cast it back before delete, so the compiler won't incorrectly call destructors. delete [] ( reinterpret_cast( rep_ ) ); } } Rep * rep_; static Rep nullrep_; } ; inline bool operator == (const TiXmlString & a, const TiXmlString & b) { return ( a.length() == b.length() ) // optimization on some platforms && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare } inline bool operator < (const TiXmlString & a, const TiXmlString & b) { return strcmp(a.c_str(), b.c_str()) < 0; } inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); TiXmlString operator + (const TiXmlString & a, const char* b); TiXmlString operator + (const char* a, const TiXmlString & b); /* TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. Only the operators that we need for TinyXML have been developped. */ class TiXmlOutStream : public TiXmlString { public : // TiXmlOutStream << operator. TiXmlOutStream & operator << (const TiXmlString & in) { *this += in; return *this; } // TiXmlOutStream << operator. TiXmlOutStream & operator << (const char * in) { *this += in; return *this; } } ; #endif // TIXML_STRING_INCLUDED #endif // TIXML_USE_STL blobby-1.0rc3/src/tinyxml/tinyxmlparser.cpp0000644000175000017500000011057212042452371022471 0ustar danielknobedanielknobe/* www.sourceforge.net/projects/tinyxml Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include #include #include "tinyxml.h" //#define DEBUG_PARSER #if defined( DEBUG_PARSER ) # if defined( DEBUG ) && defined( _MSC_VER ) # include # define TIXML_LOG OutputDebugString # else # define TIXML_LOG printf # endif #endif // Note tha "PutString" hardcodes the same list. This // is less flexible than it appears. Changing the entries // or order will break putstring. TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = { { "&", 5, '&' }, { "<", 4, '<' }, { ">", 4, '>' }, { """, 6, '\"' }, { "'", 6, '\'' } }; // Bunch of unicode info at: // http://www.unicode.org/faq/utf_bom.html // Including the basic of this table, which determines the #bytes in the // sequence from the lead byte. 1 placed for invalid sequences -- // although the result will be junk, pass it through as much as possible. // Beware of the non-characters in UTF-8: // ef bb bf (Microsoft "lead bytes") // ef bf be // ef bf bf const unsigned char TIXML_UTF_LEAD_0 = 0xefU; const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; const int TiXmlBase::utf8ByteTable[256] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid }; void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) { const unsigned long BYTE_MASK = 0xBF; const unsigned long BYTE_MARK = 0x80; const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; if (input < 0x80) *length = 1; else if ( input < 0x800 ) *length = 2; else if ( input < 0x10000 ) *length = 3; else if ( input < 0x200000 ) *length = 4; else { *length = 0; return; } // This code won't covert this correctly anyway. output += *length; // Scary scary fall throughs. switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); } } /*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) { // This will only work for low-ascii, everything else is assumed to be a valid // letter. I'm not sure this is the best approach, but it is quite tricky trying // to figure out alhabetical vs. not across encoding. So take a very // conservative approach. // if ( encoding == TIXML_ENCODING_UTF8 ) // { if ( anyByte < 127 ) return isalpha( anyByte ); else return 1; // What else to do? The unicode set is huge...get the english ones right. // } // else // { // return isalpha( anyByte ); // } } /*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) { // This will only work for low-ascii, everything else is assumed to be a valid // letter. I'm not sure this is the best approach, but it is quite tricky trying // to figure out alhabetical vs. not across encoding. So take a very // conservative approach. // if ( encoding == TIXML_ENCODING_UTF8 ) // { if ( anyByte < 127 ) return isalnum( anyByte ); else return 1; // What else to do? The unicode set is huge...get the english ones right. // } // else // { // return isalnum( anyByte ); // } } class TiXmlParsingData { friend class TiXmlDocument; public: void Stamp( const char* now, TiXmlEncoding encoding ); const TiXmlCursor& Cursor() const { return cursor; } private: // Only used by the document! TiXmlParsingData( const char* start, int _tabsize, int row, int col ) { assert( start ); stamp = start; tabsize = _tabsize; cursor.row = row; cursor.col = col; } TiXmlCursor cursor; const char* stamp; int tabsize; }; void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) { assert( now ); // Do nothing if the tabsize is 0. if ( tabsize < 1 ) { return; } // Get the current row, column. int row = cursor.row; int col = cursor.col; const char* p = stamp; assert( p ); while ( p < now ) { // Treat p as unsigned, so we have a happy compiler. const unsigned char* pU = (const unsigned char*)p; // Code contributed by Fletcher Dunn: (modified by lee) switch (*pU) { case 0: // We *should* never get here, but in case we do, don't // advance past the terminating null character, ever return; case '\r': // bump down to the next line ++row; col = 0; // Eat the character ++p; // Check for \r\n sequence, and treat this as a single character if (*p == '\n') { ++p; } break; case '\n': // bump down to the next line ++row; col = 0; // Eat the character ++p; // Check for \n\r sequence, and treat this as a single // character. (Yes, this bizarre thing does occur still // on some arcane platforms...) if (*p == '\r') { ++p; } break; case '\t': // Eat the character ++p; // Skip to next tab stop col = (col / tabsize + 1) * tabsize; break; case TIXML_UTF_LEAD_0: if ( encoding == TIXML_ENCODING_UTF8 ) { if ( *(p+1) && *(p+2) ) { // In these cases, don't advance the column. These are // 0-width spaces. if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) p += 3; else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) p += 3; else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) p += 3; else { p +=3; ++col; } // A normal character. } } else { ++p; ++col; } break; default: if ( encoding == TIXML_ENCODING_UTF8 ) { // Eat the 1 to 4 byte utf8 character. int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; if ( step == 0 ) step = 1; // Error case from bad encoding, but handle gracefully. p += step; // Just advance one column, of course. ++col; } else { ++p; ++col; } break; } } cursor.row = row; cursor.col = col; assert( cursor.row >= -1 ); assert( cursor.col >= -1 ); stamp = p; assert( stamp ); } const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) { if ( !p || !*p ) { return 0; } if ( encoding == TIXML_ENCODING_UTF8 ) { while ( *p ) { const unsigned char* pU = (const unsigned char*)p; // Skip the stupid Microsoft UTF-8 Byte order marks if ( *(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) { p += 3; continue; } else if(*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==0xbfU && *(pU+2)==0xbeU ) { p += 3; continue; } else if(*(pU+0)==TIXML_UTF_LEAD_0 && *(pU+1)==0xbfU && *(pU+2)==0xbfU ) { p += 3; continue; } if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. ++p; else break; } } else { while ( *p && IsWhiteSpace( *p ) ) ++p; } return p; } #ifdef TIXML_USE_STL /*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) { for( ;; ) { if ( !in->good() ) return false; int c = in->peek(); // At this scope, we can't get to a document. So fail silently. if ( !IsWhiteSpace( c ) || c <= 0 ) return true; *tag += (char) in->get(); } } /*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) { //assert( character > 0 && character < 128 ); // else it won't work in utf-8 while ( in->good() ) { int c = in->peek(); if ( c == character ) return true; if ( c <= 0 ) // Silent failure: can't get document at this scope return false; in->get(); *tag += (char) c; } return false; } #endif // One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The // "assign" optimization removes over 10% of the execution time. // const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) { // Oddly, not supported on some comilers, //name->clear(); // So use this: *name = ""; assert( p ); // Names start with letters or underscores. // Of course, in unicode, tinyxml has no idea what a letter *is*. The // algorithm is generous. // // After that, they can be letters, underscores, numbers, // hyphens, or colons. (Colons are valid ony for namespaces, // but tinyxml can't tell namespaces from names.) if ( p && *p && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) { const char* start = p; while( p && *p && ( IsAlphaNum( (unsigned char ) *p, encoding ) || *p == '_' || *p == '-' || *p == '.' || *p == ':' ) ) { //(*name) += *p; // expensive ++p; } if ( p-start > 0 ) { name->assign( start, p-start ); } return p; } return 0; } const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) { // Presume an entity, and pull it out. TIXML_STRING ent; int i; *length = 0; if ( *(p+1) && *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; ptrdiff_t delta = 0; unsigned mult = 1; if ( *(p+2) == 'x' ) { // Hexadecimal. if ( !*(p+3) ) return 0; const char* q = p+3; q = strchr( q, ';' ); if ( !q || !*q ) return 0; delta = q-p; --q; while ( *q != 'x' ) { if ( *q >= '0' && *q <= '9' ) ucs += mult * (*q - '0'); else if ( *q >= 'a' && *q <= 'f' ) ucs += mult * (*q - 'a' + 10); else if ( *q >= 'A' && *q <= 'F' ) ucs += mult * (*q - 'A' + 10 ); else return 0; mult *= 16; --q; } } else { // Decimal. if ( !*(p+2) ) return 0; const char* q = p+2; q = strchr( q, ';' ); if ( !q || !*q ) return 0; delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) ucs += mult * (*q - '0'); else return 0; mult *= 10; --q; } } if ( encoding == TIXML_ENCODING_UTF8 ) { // convert the UCS to UTF-8 ConvertUTF32ToUTF8( ucs, value, length ); } else { *value = (char)ucs; *length = 1; } return p + delta + 1; } // Now try to match it. for( i=0; iappend( cArr, len ); } } else { bool whitespace = false; // Remove leading white space: p = SkipWhiteSpace( p, encoding ); while ( p && *p && !StringEqual( p, endTag, caseInsensitive, encoding ) ) { if ( *p == '\r' || *p == '\n' ) { whitespace = true; ++p; } else if ( IsWhiteSpace( *p ) ) { whitespace = true; ++p; } else { // If we've found whitespace, add it before the // new character. Any whitespace just becomes a space. if ( whitespace ) { (*text) += ' '; whitespace = false; } int len; char cArr[4] = { 0, 0, 0, 0 }; p = GetChar( p, cArr, &len, encoding ); if ( len == 1 ) (*text) += cArr[0]; // more efficient else text->append( cArr, len ); } } } if ( p && *p ) p += strlen( endTag ); return ( p && *p ) ? p : 0; } #ifdef TIXML_USE_STL void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) { // The basic issue with a document is that we don't know what we're // streaming. Read something presumed to be a tag (and hope), then // identify it, and call the appropriate stream method on the tag. // // This "pre-streaming" will never read the closing ">" so the // sub-tag can orient itself. if ( !StreamTo( in, '<', tag ) ) { SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } while ( in->good() ) { int tagIndex = (int) tag->length(); while ( in->good() && in->peek() != '>' ) { int c = in->get(); if ( c <= 0 ) { SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); break; } (*tag) += (char) c; } if ( in->good() ) { // We now have something we presume to be a node of // some sort. Identify it, and call the node to // continue streaming. TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); if ( node ) { node->StreamIn( in, tag ); bool isElement = node->ToElement() != 0; delete node; node = 0; // If this is the root element, we're done. Parsing will be // done by the >> operator. if ( isElement ) { return; } } else { SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } } } // We should have returned sooner. SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); } #endif const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) { ClearError(); // Parse away, at the document level. Since a document // contains nothing but other tags, most of what happens // here is skipping white space. if ( !p || !*p ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } // Note that, for a document, this needs to come // before the while space skip, so that parsing // starts from the pointer we are given. location.Clear(); if ( prevData ) { location.row = prevData->cursor.row; location.col = prevData->cursor.col; } else { location.row = 0; location.col = 0; } TiXmlParsingData data( p, TabSize(), location.row, location.col ); location = data.Cursor(); if ( encoding == TIXML_ENCODING_UNKNOWN ) { // Check for the Microsoft UTF-8 lead bytes. const unsigned char* pU = (const unsigned char*)p; if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) { encoding = TIXML_ENCODING_UTF8; useMicrosoftBOM = true; } } p = SkipWhiteSpace( p, encoding ); if ( !p ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } while ( p && *p ) { TiXmlNode* node = Identify( p, encoding ); if ( node ) { p = node->Parse( p, &data, encoding ); LinkEndChild( node ); } else { break; } // Did we get encoding info? if ( encoding == TIXML_ENCODING_UNKNOWN && node->ToDeclaration() ) { TiXmlDeclaration* dec = node->ToDeclaration(); const char* enc = dec->Encoding(); assert( enc ); if ( *enc == 0 ) encoding = TIXML_ENCODING_UTF8; else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) encoding = TIXML_ENCODING_UTF8; else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice else encoding = TIXML_ENCODING_LEGACY; } p = SkipWhiteSpace( p, encoding ); } // Was this empty? if ( !firstChild ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); return 0; } // All is well. return p; } void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) { // The first error in a chain is more accurate - don't set again! if ( error ) return; assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); error = true; errorId = err; errorDesc = errorString[ errorId ]; errorLocation.Clear(); if ( pError && data ) { data->Stamp( pError, encoding ); errorLocation = data->Cursor(); } } TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) { TiXmlNode* returnNode = 0; p = SkipWhiteSpace( p, encoding ); if( !p || !*p || *p != '<' ) { return 0; } p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) { return 0; } // What is this thing? // - Elements start with a letter or underscore, but xml is reserved. // - Comments: "; if ( !StringEqual( p, startTag, false, encoding ) ) { if ( document ) document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); return 0; } p += strlen( startTag ); // [ 1475201 ] TinyXML parses entities in comments // Oops - ReadText doesn't work, because we don't want to parse the entities. // p = ReadText( p, &value, false, endTag, false, encoding ); // // from the XML spec: /* [Definition: Comments may appear anywhere in a document outside other markup; in addition, they may appear within the document type declaration at places allowed by the grammar. They are not part of the document's character data; an XML processor MAY, but need not, make it possible for an application to retrieve the text of comments. For compatibility, the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity references MUST NOT be recognized within comments. An example of a comment: */ value = ""; // Keep all the white space. while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) { value.append( p, 1 ); ++p; } if ( p && *p ) p += strlen( endTag ); return p; } const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) { p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) return 0; if ( data ) { data->Stamp( p, encoding ); location = data->Cursor(); } // Read the name, the '=' and the value. const char* pErr = p; p = ReadName( p, &name, encoding ); if ( !p || !*p ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); return 0; } p = SkipWhiteSpace( p, encoding ); if ( !p || !*p || *p != '=' ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } ++p; // skip '=' p = SkipWhiteSpace( p, encoding ); if ( !p || !*p ) { if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } const char* end; const char SINGLE_QUOTE = '\''; const char DOUBLE_QUOTE = '\"'; if ( *p == SINGLE_QUOTE ) { ++p; end = "\'"; // single quote in string p = ReadText( p, &value, false, end, false, encoding ); } else if ( *p == DOUBLE_QUOTE ) { ++p; end = "\""; // double quote in string p = ReadText( p, &value, false, end, false, encoding ); } else { // All attribute values should be in single or double quotes. // But this is such a common error that the parser will try // its best, even without them. value = ""; while ( p && *p // existence && !IsWhiteSpace( *p ) // whitespace && *p != '/' && *p != '>' ) // tag end { if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { // [ 1451649 ] Attribute values with trailing quotes not handled correctly // We did not have an opening quote but seem to have a // closing one. Give up and throw an error. if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); return 0; } value += *p; ++p; } } return p; } #ifdef TIXML_USE_STL void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) { while ( in->good() ) { int c = in->peek(); if ( !cdata && (c == '<' ) ) { return; } if ( c <= 0 ) { TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } (*tag) += (char) c; in->get(); // "commits" the peek made above if ( cdata && c == '>' && tag->size() >= 3 ) { size_t len = tag->size(); if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { // terminator of cdata. return; } } } } #endif const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) { value = ""; TiXmlDocument* document = GetDocument(); if ( data ) { data->Stamp( p, encoding ); location = data->Cursor(); } const char* const startTag = ""; if ( cdata || StringEqual( p, startTag, false, encoding ) ) { cdata = true; if ( !StringEqual( p, startTag, false, encoding ) ) { if ( document ) document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); return 0; } p += strlen( startTag ); // Keep all the white space, ignore the encoding, etc. while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) { value += *p; ++p; } TIXML_STRING dummy; p = ReadText( p, &dummy, false, endTag, false, encoding ); return p; } else { bool ignoreWhite = true; const char* end = "<"; p = ReadText( p, &value, ignoreWhite, end, false, encoding ); if ( p && *p ) return p-1; // don't truncate the '<' return 0; } } #ifdef TIXML_USE_STL void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) { while ( in->good() ) { int c = in->get(); if ( c <= 0 ) { TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); return; } (*tag) += (char) c; if ( c == '>' ) { // All is well. return; } } } #endif const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) { p = SkipWhiteSpace( p, _encoding ); // Find the beginning, find the end, and look for // the stuff in-between. TiXmlDocument* document = GetDocument(); if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); return 0; } if ( data ) { data->Stamp( p, _encoding ); location = data->Cursor(); } p += 5; version = ""; encoding = ""; standalone = ""; while ( p && *p ) { if ( *p == '>' ) { ++p; return p; } p = SkipWhiteSpace( p, _encoding ); if ( StringEqual( p, "version", true, _encoding ) ) { TiXmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); version = attrib.Value(); } else if ( StringEqual( p, "encoding", true, _encoding ) ) { TiXmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); encoding = attrib.Value(); } else if ( StringEqual( p, "standalone", true, _encoding ) ) { TiXmlAttribute attrib; p = attrib.Parse( p, data, _encoding ); standalone = attrib.Value(); } else { // Read over whatever it is. while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) ++p; } } return 0; } bool TiXmlText::Blank() const { for ( unsigned i=0; i #ifdef TIXML_USE_STL #include #include #endif #include "tinyxml.h" FILE* TiXmlFOpen( const char* filename, const char* mode ); bool TiXmlBase::condenseWhiteSpace = true; // Microsoft compiler security FILE* TiXmlFOpen( const char* filename, const char* mode ) { #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) FILE* fp = 0; errno_t err = fopen_s( &fp, filename, mode ); if ( !err && fp ) return fp; return 0; #else return fopen( filename, mode ); #endif } void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) { int i=0; while( i<(int)str.length() ) { unsigned char c = (unsigned char) str[i]; if ( c == '&' && i < ( (int)str.length() - 2 ) && str[i+1] == '#' && str[i+2] == 'x' ) { // Hexadecimal character reference. // Pass through unchanged. // © -- copyright symbol, for example. // // The -1 is a bug fix from Rob Laveaux. It keeps // an overflow from happening if there is no ';'. // There are actually 2 ways to exit this loop - // while fails (error case) and break (semicolon found). // However, there is no mechanism (currently) for // this function to return an error. while ( i<(int)str.length()-1 ) { outString->append( str.c_str() + i, 1 ); ++i; if ( str[i] == ';' ) break; } } else if ( c == '&' ) { outString->append( entity[0].str, entity[0].strLength ); ++i; } else if ( c == '<' ) { outString->append( entity[1].str, entity[1].strLength ); ++i; } else if ( c == '>' ) { outString->append( entity[2].str, entity[2].strLength ); ++i; } else if ( c == '\"' ) { outString->append( entity[3].str, entity[3].strLength ); ++i; } else if ( c == '\'' ) { outString->append( entity[4].str, entity[4].strLength ); ++i; } else if ( c < 32 ) { // Easy pass at non-alpha/numeric/symbol // Below 32 is symbolic. char buf[ 32 ]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); #else sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); #endif //*ME: warning C4267: convert 'size_t' to 'int' //*ME: Int-Cast to make compiler happy ... outString->append( buf, (int)strlen( buf ) ); ++i; } else { //char realc = (char) c; //outString->append( &realc, 1 ); *outString += (char) c; // somewhat more efficient function call. ++i; } } } TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() { parent = 0; type = _type; firstChild = 0; lastChild = 0; prev = 0; next = 0; } TiXmlNode::~TiXmlNode() { TiXmlNode* node = firstChild; TiXmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } } void TiXmlNode::CopyTo( TiXmlNode* target ) const { target->SetValue (value.c_str() ); target->userData = userData; target->location = location; } void TiXmlNode::Clear() { TiXmlNode* node = firstChild; TiXmlNode* temp = 0; while ( node ) { temp = node; node = node->next; delete temp; } firstChild = 0; lastChild = 0; } TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) { assert( node->parent == 0 || node->parent == this ); assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) { delete node; if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } node->parent = this; node->prev = lastChild; node->next = 0; if ( lastChild ) lastChild->next = node; else firstChild = node; // it was an empty list. lastChild = node; return node; } TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) { if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; return LinkEndChild( node ); } TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) { if ( !beforeThis || beforeThis->parent != this ) { return 0; } if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->next = beforeThis; node->prev = beforeThis->prev; if ( beforeThis->prev ) { beforeThis->prev->next = node; } else { assert( firstChild == beforeThis ); firstChild = node; } beforeThis->prev = node; return node; } TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) { if ( !afterThis || afterThis->parent != this ) { return 0; } if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) { if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = addThis.Clone(); if ( !node ) return 0; node->parent = this; node->prev = afterThis; node->next = afterThis->next; if ( afterThis->next ) { afterThis->next->prev = node; } else { assert( lastChild == afterThis ); lastChild = node; } afterThis->next = node; return node; } TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) { if ( !replaceThis ) return 0; if ( replaceThis->parent != this ) return 0; if ( withThis.ToDocument() ) { // A document can never be a child. Thanks to Noam. TiXmlDocument* document = GetDocument(); if ( document ) document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); return 0; } TiXmlNode* node = withThis.Clone(); if ( !node ) return 0; node->next = replaceThis->next; node->prev = replaceThis->prev; if ( replaceThis->next ) replaceThis->next->prev = node; else lastChild = node; if ( replaceThis->prev ) replaceThis->prev->next = node; else firstChild = node; delete replaceThis; node->parent = this; return node; } bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) { if ( !removeThis ) { return false; } if ( removeThis->parent != this ) { assert( 0 ); return false; } if ( removeThis->next ) removeThis->next->prev = removeThis->prev; else lastChild = removeThis->prev; if ( removeThis->prev ) removeThis->prev->next = removeThis->next; else firstChild = removeThis->next; delete removeThis; return true; } const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const { const TiXmlNode* node; for ( node = firstChild; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const { const TiXmlNode* node; for ( node = lastChild; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const { if ( !previous ) { return FirstChild(); } else { assert( previous->parent == this ); return previous->NextSibling(); } } const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const { if ( !previous ) { return FirstChild( val ); } else { assert( previous->parent == this ); return previous->NextSibling( val ); } } const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const { const TiXmlNode* node; for ( node = next; node; node = node->next ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const { const TiXmlNode* node; for ( node = prev; node; node = node->prev ) { if ( strcmp( node->Value(), _value ) == 0 ) return node; } return 0; } void TiXmlElement::RemoveAttribute( const char * name ) { #ifdef TIXML_USE_STL TIXML_STRING str( name ); TiXmlAttribute* node = attributeSet.Find( str ); #else TiXmlAttribute* node = attributeSet.Find( name ); #endif if ( node ) { attributeSet.Remove( node ); delete node; } } const TiXmlElement* TiXmlNode::FirstChildElement() const { const TiXmlNode* node; for ( node = FirstChild(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const { const TiXmlNode* node; for ( node = FirstChild( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::NextSiblingElement() const { const TiXmlNode* node; for ( node = NextSibling(); node; node = node->NextSibling() ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const { const TiXmlNode* node; for ( node = NextSibling( _value ); node; node = node->NextSibling( _value ) ) { if ( node->ToElement() ) return node->ToElement(); } return 0; } const TiXmlDocument* TiXmlNode::GetDocument() const { const TiXmlNode* node; for( node = this; node; node = node->parent ) { if ( node->ToDocument() ) return node->ToDocument(); } return 0; } TiXmlElement::TiXmlElement (const char * _value) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; value = _value; } #ifdef TIXML_USE_STL TiXmlElement::TiXmlElement( const std::string& _value ) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; value = _value; } #endif TiXmlElement::TiXmlElement( const TiXmlElement& copy) : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) { firstChild = lastChild = 0; copy.CopyTo( this ); } TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) { ClearThis(); base.CopyTo( this ); return *this; } TiXmlElement::~TiXmlElement() { ClearThis(); } void TiXmlElement::ClearThis() { Clear(); while( attributeSet.First() ) { TiXmlAttribute* node = attributeSet.First(); attributeSet.Remove( node ); delete node; } } const char* TiXmlElement::Attribute( const char* name ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( node ) return node->Value(); return 0; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( attrib ) return &attrib->ValueStr(); return 0; } #endif const char* TiXmlElement::Attribute( const char* name, int* i ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const char* result = 0; if ( attrib ) { result = attrib->Value(); if ( i ) { attrib->QueryIntValue( i ); } } return result; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const std::string* result = 0; if ( attrib ) { result = &attrib->ValueStr(); if ( i ) { attrib->QueryIntValue( i ); } } return result; } #endif const char* TiXmlElement::Attribute( const char* name, double* d ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const char* result = 0; if ( attrib ) { result = attrib->Value(); if ( d ) { attrib->QueryDoubleValue( d ); } } return result; } #ifdef TIXML_USE_STL const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); const std::string* result = 0; if ( attrib ) { result = &attrib->ValueStr(); if ( d ) { attrib->QueryDoubleValue( d ); } } return result; } #endif int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryIntValue( ival ); } int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; int ival = 0; int result = node->QueryIntValue( &ival ); *value = (unsigned)ival; return result; } int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; int result = TIXML_WRONG_TYPE; if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) { *bval = true; result = TIXML_SUCCESS; } else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) { *bval = false; result = TIXML_SUCCESS; } return result; } #ifdef TIXML_USE_STL int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryIntValue( ival ); } #endif int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryDoubleValue( dval ); } #ifdef TIXML_USE_STL int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const { const TiXmlAttribute* attrib = attributeSet.Find( name ); if ( !attrib ) return TIXML_NO_ATTRIBUTE; return attrib->QueryDoubleValue( dval ); } #endif void TiXmlElement::SetAttribute( const char * name, int val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetIntValue( val ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetAttribute( const std::string& name, int val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetIntValue( val ); } } #endif void TiXmlElement::SetDoubleAttribute( const char * name, double val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetDoubleValue( val ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); if ( attrib ) { attrib->SetDoubleValue( val ); } } #endif void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); if ( attrib ) { attrib->SetValue( cvalue ); } } #ifdef TIXML_USE_STL void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) { TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); if ( attrib ) { attrib->SetValue( _value ); } } #endif void TiXmlElement::Print( FILE* cfile, int depth ) const { int i; assert( cfile ); for ( i=0; iNext() ) { fprintf( cfile, " " ); attrib->Print( cfile, depth ); } // There are 3 different formatting approaches: // 1) An element without children is printed as a node // 2) An element with only a text child is printed as text // 3) An element with children is printed on multiple lines. TiXmlNode* node; if ( !firstChild ) { fprintf( cfile, " />" ); } else if ( firstChild == lastChild && firstChild->ToText() ) { fprintf( cfile, ">" ); firstChild->Print( cfile, depth + 1 ); fprintf( cfile, "", value.c_str() ); } else { fprintf( cfile, ">" ); for ( node = firstChild; node; node=node->NextSibling() ) { if ( !node->ToText() ) { fprintf( cfile, "\n" ); } node->Print( cfile, depth+1 ); } fprintf( cfile, "\n" ); for( i=0; i", value.c_str() ); } } void TiXmlElement::CopyTo( TiXmlElement* target ) const { // superclass: TiXmlNode::CopyTo( target ); // Element class: // Clone the attributes, then clone the children. const TiXmlAttribute* attribute = 0; for( attribute = attributeSet.First(); attribute; attribute = attribute->Next() ) { target->SetAttribute( attribute->Name(), attribute->Value() ); } TiXmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this, attributeSet.First() ) ) { for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } TiXmlNode* TiXmlElement::Clone() const { TiXmlElement* clone = new TiXmlElement( Value() ); if ( !clone ) return 0; CopyTo( clone ); return clone; } const char* TiXmlElement::GetText() const { const TiXmlNode* child = this->FirstChild(); if ( child ) { const TiXmlText* childText = child->ToText(); if ( childText ) { return childText->Value(); } } return 0; } TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; ClearError(); } TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } #ifdef TIXML_USE_STL TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { tabsize = 4; useMicrosoftBOM = false; value = documentName; ClearError(); } #endif TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) { copy.CopyTo( this ); } TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) { Clear(); copy.CopyTo( this ); return *this; } bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) { return LoadFile( Value(), encoding ); } bool TiXmlDocument::SaveFile() const { return SaveFile( Value() ); } bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) { TIXML_STRING filename( _filename ); value = filename; // reading in binary mode so that tinyxml can normalize the EOL FILE* file = TiXmlFOpen( value.c_str (), "rb" ); if ( file ) { bool result = LoadFile( file, encoding ); fclose( file ); return result; } else { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } } bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) { if ( !file ) { SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Delete the existing data: Clear(); location.Clear(); // Get the file size, so we can pre-allocate the string. HUGE speed impact. long length = 0; fseek( file, 0, SEEK_END ); length = ftell( file ); fseek( file, 0, SEEK_SET ); // Strange case, but good to handle up front. if ( length <= 0 ) { SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Subtle bug here. TinyXml did use fgets. But from the XML spec: // 2.11 End-of-Line Handling // // // ...the XML processor MUST behave as if it normalized all line breaks in external // parsed entities (including the document entity) on input, before parsing, by translating // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to // a single #xA character. // // // It is not clear fgets does that, and certainly isn't clear it works cross platform. // Generally, you expect fgets to translate from the convention of the OS to the c/unix // convention, and not work generally. /* while( fgets( buf, sizeof(buf), file ) ) { data += buf; } */ char* buf = new char[ length+1 ]; buf[0] = 0; if ( fread( buf, length, 1, file ) != 1 ) { delete [] buf; SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); return false; } // Process the buffer in place to normalize new lines. (See comment above.) // Copies from the 'p' to 'q' pointer, where p can advance faster if // a newline-carriage return is hit. // // Wikipedia: // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 const char* p = buf; // the read head char* q = buf; // the write head const char CR = 0x0d; const char LF = 0x0a; buf[length] = 0; while( *p ) { assert( p < (buf+length) ); assert( q <= (buf+length) ); assert( q <= p ); if ( *p == CR ) { *q++ = LF; p++; if ( *p == LF ) { // check for CR+LF (and skip LF) p++; } } else { *q++ = *p++; } } assert( q <= (buf+length) ); *q = 0; Parse( buf, 0, encoding ); delete [] buf; return !Error(); } bool TiXmlDocument::SaveFile( const char * filename ) const { // The old c stuff lives on... FILE* fp = TiXmlFOpen( filename, "w" ); if ( fp ) { bool result = SaveFile( fp ); fclose( fp ); return result; } return false; } bool TiXmlDocument::SaveFile( FILE* fp ) const { if ( useMicrosoftBOM ) { const unsigned char TIXML_UTF_LEAD_0 = 0xefU; const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; fputc( TIXML_UTF_LEAD_0, fp ); fputc( TIXML_UTF_LEAD_1, fp ); fputc( TIXML_UTF_LEAD_2, fp ); } Print( fp, 0 ); return (ferror(fp) == 0); } void TiXmlDocument::CopyTo( TiXmlDocument* target ) const { TiXmlNode::CopyTo( target ); target->error = error; target->errorId = errorId; target->errorDesc = errorDesc; target->tabsize = tabsize; target->errorLocation = errorLocation; target->useMicrosoftBOM = useMicrosoftBOM; TiXmlNode* node = 0; for ( node = firstChild; node; node = node->NextSibling() ) { target->LinkEndChild( node->Clone() ); } } TiXmlNode* TiXmlDocument::Clone() const { TiXmlDocument* clone = new TiXmlDocument(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlDocument::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { node->Print( cfile, depth ); fprintf( cfile, "\n" ); } } bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const { if ( visitor->VisitEnter( *this ) ) { for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) break; } } return visitor->VisitExit( *this ); } const TiXmlAttribute* TiXmlAttribute::Next() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } /* TiXmlAttribute* TiXmlAttribute::Next() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( next->value.empty() && next->name.empty() ) return 0; return next; } */ const TiXmlAttribute* TiXmlAttribute::Previous() const { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } /* TiXmlAttribute* TiXmlAttribute::Previous() { // We are using knowledge of the sentinel. The sentinel // have a value or name. if ( prev->value.empty() && prev->name.empty() ) return 0; return prev; } */ void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const { TIXML_STRING n, v; EncodeString( name, &n ); EncodeString( value, &v ); if (value.find ('\"') == TIXML_STRING::npos) { if ( cfile ) { fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; } } else { if ( cfile ) { fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); } if ( str ) { (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; } } } int TiXmlAttribute::QueryIntValue( int* ival ) const { if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } int TiXmlAttribute::QueryDoubleValue( double* dval ) const { if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } void TiXmlAttribute::SetIntValue( int _value ) { char buf [64]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); #else sprintf (buf, "%d", _value); #endif SetValue (buf); } void TiXmlAttribute::SetDoubleValue( double _value ) { char buf [256]; #if defined(TIXML_SNPRINTF) TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); #else sprintf (buf, "%g", _value); #endif SetValue (buf); } int TiXmlAttribute::IntValue() const { return atoi (value.c_str ()); } double TiXmlAttribute::DoubleValue() const { return atof (value.c_str ()); } TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { copy.CopyTo( this ); } TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) { Clear(); base.CopyTo( this ); return *this; } void TiXmlComment::Print( FILE* cfile, int depth ) const { assert( cfile ); for ( int i=0; i", value.c_str() ); } void TiXmlComment::CopyTo( TiXmlComment* target ) const { TiXmlNode::CopyTo( target ); } bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlComment::Clone() const { TiXmlComment* clone = new TiXmlComment(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlText::Print( FILE* cfile, int depth ) const { assert( cfile ); if ( cdata ) { int i; fprintf( cfile, "\n" ); for ( i=0; i\n", value.c_str() ); // unformatted output } else { TIXML_STRING buffer; EncodeString( value, &buffer ); fprintf( cfile, "%s", buffer.c_str() ); } } void TiXmlText::CopyTo( TiXmlText* target ) const { TiXmlNode::CopyTo( target ); target->cdata = cdata; } bool TiXmlText::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlText::Clone() const { TiXmlText* clone = 0; clone = new TiXmlText( "" ); if ( !clone ) return 0; CopyTo( clone ); return clone; } TiXmlDeclaration::TiXmlDeclaration( const char * _version, const char * _encoding, const char * _standalone ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } #ifdef TIXML_USE_STL TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, const std::string& _encoding, const std::string& _standalone ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { version = _version; encoding = _encoding; standalone = _standalone; } #endif TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) { copy.CopyTo( this ); } TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) { Clear(); copy.CopyTo( this ); return *this; } void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const { if ( cfile ) fprintf( cfile, "" ); if ( str ) (*str) += "?>"; } void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const { TiXmlNode::CopyTo( target ); target->version = version; target->encoding = encoding; target->standalone = standalone; } bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlDeclaration::Clone() const { TiXmlDeclaration* clone = new TiXmlDeclaration(); if ( !clone ) return 0; CopyTo( clone ); return clone; } void TiXmlUnknown::Print( FILE* cfile, int depth ) const { for ( int i=0; i", value.c_str() ); } void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const { TiXmlNode::CopyTo( target ); } bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const { return visitor->Visit( *this ); } TiXmlNode* TiXmlUnknown::Clone() const { TiXmlUnknown* clone = new TiXmlUnknown(); if ( !clone ) return 0; CopyTo( clone ); return clone; } TiXmlAttributeSet::TiXmlAttributeSet() { sentinel.next = &sentinel; sentinel.prev = &sentinel; } TiXmlAttributeSet::~TiXmlAttributeSet() { assert( sentinel.next == &sentinel ); assert( sentinel.prev == &sentinel ); } void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) { #ifdef TIXML_USE_STL assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. #else assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. #endif addMe->next = &sentinel; addMe->prev = sentinel.prev; sentinel.prev->next = addMe; sentinel.prev = addMe; } void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) { TiXmlAttribute* node; for( node = sentinel.next; node != &sentinel; node = node->next ) { if ( node == removeMe ) { node->prev->next = node->next; node->next->prev = node->prev; node->next = 0; node->prev = 0; return; } } assert( 0 ); // we tried to remove a non-linked attribute. } #ifdef TIXML_USE_STL TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const { for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( node->name == name ) return node; } return 0; } TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) { TiXmlAttribute* attrib = Find( _name ); if ( !attrib ) { attrib = new TiXmlAttribute(); Add( attrib ); attrib->SetName( _name ); } return attrib; } #endif TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const { for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) { if ( strcmp( node->name.c_str(), name ) == 0 ) return node; } return 0; } TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) { TiXmlAttribute* attrib = Find( _name ); if ( !attrib ) { attrib = new TiXmlAttribute(); Add( attrib ); attrib->SetName( _name ); } return attrib; } #ifdef TIXML_USE_STL std::istream& operator>> (std::istream & in, TiXmlNode & base) { TIXML_STRING tag; tag.reserve( 8 * 1000 ); base.StreamIn( &in, &tag ); base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); return in; } #endif #ifdef TIXML_USE_STL std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) { TiXmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out << printer.Str(); return out; } std::string& operator<< (std::string& out, const TiXmlNode& base ) { TiXmlPrinter printer; printer.SetStreamPrinting(); base.Accept( &printer ); out.append( printer.Str() ); return out; } #endif TiXmlHandle TiXmlHandle::FirstChild() const { if ( node ) { TiXmlNode* child = node->FirstChild(); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const { if ( node ) { TiXmlNode* child = node->FirstChild( value ); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChildElement() const { if ( node ) { TiXmlElement* child = node->FirstChildElement(); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const { if ( node ) { TiXmlElement* child = node->FirstChildElement( value ); if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::Child( int count ) const { if ( node ) { int i; TiXmlNode* child = node->FirstChild(); for ( i=0; child && iNextSibling(), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const { if ( node ) { int i; TiXmlNode* child = node->FirstChild( value ); for ( i=0; child && iNextSibling( value ), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::ChildElement( int count ) const { if ( node ) { int i; TiXmlElement* child = node->FirstChildElement(); for ( i=0; child && iNextSiblingElement(), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const { if ( node ) { int i; TiXmlElement* child = node->FirstChildElement( value ); for ( i=0; child && iNextSiblingElement( value ), ++i ) { // nothing } if ( child ) return TiXmlHandle( child ); } return TiXmlHandle( 0 ); } bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) { return true; } bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) { return true; } bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) { DoIndent(); buffer += "<"; buffer += element.Value(); for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) { buffer += " "; attrib->Print( 0, 0, &buffer ); } if ( !element.FirstChild() ) { buffer += " />"; DoLineBreak(); } else { buffer += ">"; if ( element.FirstChild()->ToText() && element.LastChild() == element.FirstChild() && element.FirstChild()->ToText()->CDATA() == false ) { simpleTextPrint = true; // no DoLineBreak()! } else { DoLineBreak(); } } ++depth; return true; } bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) { --depth; if ( !element.FirstChild() ) { // nothing. } else { if ( simpleTextPrint ) { simpleTextPrint = false; } else { DoIndent(); } buffer += ""; DoLineBreak(); } return true; } bool TiXmlPrinter::Visit( const TiXmlText& text ) { if ( text.CDATA() ) { DoIndent(); buffer += ""; DoLineBreak(); } else if ( simpleTextPrint ) { TIXML_STRING str; TiXmlBase::EncodeString( text.ValueTStr(), &str ); buffer += str; } else { DoIndent(); TIXML_STRING str; TiXmlBase::EncodeString( text.ValueTStr(), &str ); buffer += str; DoLineBreak(); } return true; } bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) { DoIndent(); declaration.Print( 0, 0, &buffer ); DoLineBreak(); return true; } bool TiXmlPrinter::Visit( const TiXmlComment& comment ) { DoIndent(); buffer += ""; DoLineBreak(); return true; } bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) { DoIndent(); buffer += "<"; buffer += unknown.Value(); buffer += ">"; DoLineBreak(); return true; } blobby-1.0rc3/src/tinyxml/tinyxml.h0000644000175000017500000017650212042452371020726 0ustar danielknobedanielknobe/* www.sourceforge.net/projects/tinyxml Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYXML_INCLUDED #define TINYXML_INCLUDED #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4530 ) #pragma warning( disable : 4786 ) #endif #include #include #include #include #include // Help out windows: #if defined( _DEBUG ) && !defined( DEBUG ) #define DEBUG #endif #ifdef TIXML_USE_STL #include #include #include #define TIXML_STRING std::string #else #include "tinystr.h" #define TIXML_STRING TiXmlString #endif // Deprecated library function hell. Compilers want to use the // new safe versions. This probably doesn't fully address the problem, // but it gets closer. There are too many compilers for me to fully // test. If you get compilation troubles, undefine TIXML_SAFE #define TIXML_SAFE #ifdef TIXML_SAFE #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) // Microsoft visual studio, version 2005 and higher. #define TIXML_SNPRINTF _snprintf_s #define TIXML_SSCANF sscanf_s #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) // Microsoft visual studio, version 6 and higher. //#pragma message( "Using _sn* functions." ) #define TIXML_SNPRINTF _snprintf #define TIXML_SSCANF sscanf #elif defined(__GNUC__) && (__GNUC__ >= 3 ) // GCC version 3 and higher.s //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf #else #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf #endif #endif class TiXmlDocument; class TiXmlElement; class TiXmlComment; class TiXmlUnknown; class TiXmlAttribute; class TiXmlText; class TiXmlDeclaration; class TiXmlParsingData; const int TIXML_MAJOR_VERSION = 2; const int TIXML_MINOR_VERSION = 6; const int TIXML_PATCH_VERSION = 2; /* Internal structure for tracking location of items in the XML file. */ struct TiXmlCursor { TiXmlCursor() { Clear(); } void Clear() { row = col = -1; } int row; // 0 based. int col; // 0 based. }; /** Implements the interface to the "Visitor pattern" (see the Accept() method.) If you call the Accept() method, it requires being passed a TiXmlVisitor class to handle callbacks. For nodes that contain other nodes (Document, Element) you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves are simply called with Visit(). If you return 'true' from a Visit method, recursive parsing will continue. If you return false, no children of this node or its sibilings will be Visited. All flavors of Visit methods have a default implementation that returns 'true' (continue visiting). You need to only override methods that are interesting to you. Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. You should never change the document from a callback. @sa TiXmlNode::Accept() */ class TiXmlVisitor { public: virtual ~TiXmlVisitor() {} /// Visit a document. virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } /// Visit a document. virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } /// Visit an element. virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } /// Visit an element. virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } /// Visit a declaration virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } /// Visit a text node virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } /// Visit a comment node virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } /// Visit an unknown node virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } }; // Only used by Attribute::Query functions enum { TIXML_SUCCESS, TIXML_NO_ATTRIBUTE, TIXML_WRONG_TYPE }; // Used by the parsing routines. enum TiXmlEncoding { TIXML_ENCODING_UNKNOWN, TIXML_ENCODING_UTF8, TIXML_ENCODING_LEGACY }; const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; /** TiXmlBase is a base class for every class in TinyXml. It does little except to establish that TinyXml classes can be printed and provide some utility functions. In XML, the document and elements can contain other elements and other types of nodes. @verbatim A Document can contain: Element (container or leaf) Comment (leaf) Unknown (leaf) Declaration( leaf ) An Element can contain: Element (container or leaf) Text (leaf) Attributes (not on tree) Comment (leaf) Unknown (leaf) A Decleration contains: Attributes (not on tree) @endverbatim */ class TiXmlBase { friend class TiXmlNode; friend class TiXmlElement; friend class TiXmlDocument; public: TiXmlBase() : userData(0) {} virtual ~TiXmlBase() {} /** All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.) Either or both cfile and str can be null. This is a formatted print, and will insert tabs and newlines. (For an unformatted stream, use the << operator.) */ virtual void Print( FILE* cfile, int depth ) const = 0; /** The world does not agree on whether white space should be kept or not. In order to make everyone happy, these global, static functions are provided to set whether or not TinyXml will condense all white space into a single space or not. The default is to condense. Note changing this value is not thread safe. */ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } /// Return the current white space setting. static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } /** Return the position, in the original source file, of this node or attribute. The row and column are 1-based. (That is the first row and first column is 1,1). If the returns values are 0 or less, then the parser does not have a row and column value. Generally, the row and column value will be set when the TiXmlDocument::Load(), TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set when the DOM was created from operator>>. The values reflect the initial load. Once the DOM is modified programmatically (by adding or changing nodes and attributes) the new values will NOT update to reflect changes in the document. There is a minor performance cost to computing the row and column. Computation can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. @sa TiXmlDocument::SetTabSize() */ int Row() const { return location.row + 1; } int Column() const { return location.col + 1; } ///< See Row() void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. // Table that returs, for a given lead byte, the total number of bytes // in the UTF-8 sequence. static const int utf8ByteTable[256]; virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, or they will be transformed into entities! */ static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); enum { TIXML_NO_ERROR = 0, TIXML_ERROR, TIXML_ERROR_OPENING_FILE, TIXML_ERROR_PARSING_ELEMENT, TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, TIXML_ERROR_READING_ELEMENT_VALUE, TIXML_ERROR_READING_ATTRIBUTES, TIXML_ERROR_PARSING_EMPTY, TIXML_ERROR_READING_END_TAG, TIXML_ERROR_PARSING_UNKNOWN, TIXML_ERROR_PARSING_COMMENT, TIXML_ERROR_PARSING_DECLARATION, TIXML_ERROR_DOCUMENT_EMPTY, TIXML_ERROR_EMBEDDED_NULL, TIXML_ERROR_PARSING_CDATA, TIXML_ERROR_DOCUMENT_TOP_ONLY, TIXML_ERROR_STRING_COUNT }; protected: static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); inline static bool IsWhiteSpace( char c ) { return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); } inline static bool IsWhiteSpace( int c ) { if ( c < 256 ) return IsWhiteSpace( (char) c ); return false; // Again, only truly correct for English/Latin...but usually works. } #ifdef TIXML_USE_STL static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); #endif /* Reads an XML name into the string provided. Returns a pointer just past the last character of the name, or 0 if the function has an error. */ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); /* Reads text. Returns a pointer past the given end tag. Wickedly complex options, but it keeps the (sensitive) code in one place. */ static const char* ReadText( const char* in, // where to start TIXML_STRING* text, // the string read bool ignoreWhiteSpace, // whether to keep the white space const char* endTag, // what ends this text bool ignoreCase, // whether to ignore case in the end tag TiXmlEncoding encoding ); // the current encoding // If an entity has been found, transform it into a character. static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); // Get a character, while interpreting entities. // The length can be from 0 to 4 bytes. inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) { assert( p ); if ( encoding == TIXML_ENCODING_UTF8 ) { *length = utf8ByteTable[ *((const unsigned char*)p) ]; assert( *length >= 0 && *length < 5 ); } else { *length = 1; } if ( *length == 1 ) { if ( *p == '&' ) return GetEntity( p, _value, length, encoding ); *_value = *p; return p+1; } else if ( *length ) { //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), // and the null terminator isn't needed for( int i=0; p[i] && i<*length; ++i ) { _value[i] = p[i]; } return p + (*length); } else { // Not valid text. return 0; } } // Return true if the next characters in the stream are any of the endTag sequences. // Ignore case only works for english, and should only be relied on when comparing // to English words: StringEqual( p, "version", true ) is fine. static bool StringEqual( const char* p, const char* endTag, bool ignoreCase, TiXmlEncoding encoding ); static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; TiXmlCursor location; /// Field containing a generic user pointer void* userData; // None of these methods are reliable for any language except English. // Good for approximation, not great for accuracy. static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); inline static int ToLower( int v, TiXmlEncoding encoding ) { if ( encoding == TIXML_ENCODING_UTF8 ) { if ( v < 128 ) return tolower( v ); return v; } else { return tolower( v ); } } static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); private: TiXmlBase( const TiXmlBase& ); // not implemented. void operator=( const TiXmlBase& base ); // not allowed. struct Entity { const char* str; unsigned int strLength; char chr; }; enum { NUM_ENTITY = 5, MAX_ENTITY_LENGTH = 6 }; static Entity entity[ NUM_ENTITY ]; static bool condenseWhiteSpace; }; /** The parent class for everything in the Document Object Model. (Except for attributes). Nodes have siblings, a parent, and children. A node can be in a document, or stand on its own. The type of a TiXmlNode can be queried, and it can be cast to its more defined type. */ class TiXmlNode : public TiXmlBase { friend class TiXmlDocument; friend class TiXmlElement; public: #ifdef TIXML_USE_STL /** An input stream operator, for every class. Tolerant of newlines and formatting, but doesn't expect them. */ friend std::istream& operator >> (std::istream& in, TiXmlNode& base); /** An output stream operator, for every class. Note that this outputs without any newlines or formatting, as opposed to Print(), which includes tabs and new lines. The operator<< and operator>> are not completely symmetric. Writing a node to a stream is very well defined. You'll get a nice stream of output, without any extra whitespace or newlines. But reading is not as well defined. (As it always is.) If you create a TiXmlElement (for example) and read that from an input stream, the text needs to define an element or junk will result. This is true of all input streams, but it's worth keeping in mind. A TiXmlDocument will read nodes until it reads a root element, and all the children of that root element. */ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); /// Appends the XML node or attribute to a std::string. friend std::string& operator<< (std::string& out, const TiXmlNode& base ); #endif /** The types of XML nodes supported by TinyXml. (All the unsupported types are picked up by UNKNOWN.) */ enum NodeType { TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, TINYXML_UNKNOWN, TINYXML_TEXT, TINYXML_DECLARATION, TINYXML_TYPECOUNT }; virtual ~TiXmlNode(); /** The meaning of 'value' changes for the specific type of TiXmlNode. @verbatim Document: filename of the xml file Element: name of the element Comment: the comment text Unknown: the tag contents Text: the text string @endverbatim The subclasses will wrap this function. */ const char *Value() const { return value.c_str (); } #ifdef TIXML_USE_STL /** Return Value() as a std::string. If you only use STL, this is more efficient than calling Value(). Only available in STL mode. */ const std::string& ValueStr() const { return value; } #endif const TIXML_STRING& ValueTStr() const { return value; } /** Changes the value of the node. Defined as: @verbatim Document: filename of the xml file Element: name of the element Comment: the comment text Unknown: the tag contents Text: the text string @endverbatim */ void SetValue(const char * _value) { value = _value;} #ifdef TIXML_USE_STL /// STL std::string form. void SetValue( const std::string& _value ) { value = _value; } #endif /// Delete all the children of this node. Does not affect 'this'. void Clear(); /// One step up the DOM. TiXmlNode* Parent() { return parent; } const TiXmlNode* Parent() const { return parent; } const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. TiXmlNode* FirstChild() { return firstChild; } const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. /// The first child of this node with the matching 'value'. Will be null if none found. TiXmlNode* FirstChild( const char * _value ) { // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) // call the method, cast the return back to non-const. return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); } const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. TiXmlNode* LastChild() { return lastChild; } const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. TiXmlNode* LastChild( const char * _value ) { return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); } #ifdef TIXML_USE_STL const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. #endif /** An alternate way to walk the children of a node. One way to iterate over nodes is: @verbatim for( child = parent->FirstChild(); child; child = child->NextSibling() ) @endverbatim IterateChildren does the same thing with the syntax: @verbatim child = 0; while( child = parent->IterateChildren( child ) ) @endverbatim IterateChildren takes the previous child as input and finds the next one. If the previous child is null, it returns the first. IterateChildren will return null when done. */ const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; TiXmlNode* IterateChildren( const TiXmlNode* previous ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); } /// This flavor of IterateChildren searches for children with a particular 'value' const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); } #ifdef TIXML_USE_STL const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. #endif /** Add a new node related to this. Adds a child past the LastChild. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); /** Add a new node related to this. Adds a child past the LastChild. NOTE: the node to be added is passed by pointer, and will be henceforth owned (and deleted) by tinyXml. This method is efficient and avoids an extra copy, but should be used with care as it uses a different memory model than the other insert functions. @sa InsertEndChild */ TiXmlNode* LinkEndChild( TiXmlNode* addThis ); /** Add a new node related to this. Adds a child before the specified child. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); /** Add a new node related to this. Adds a child after the specified child. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); /** Replace a child of this node. Returns a pointer to the new object or NULL if an error occured. */ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); /// Delete a child of this node. bool RemoveChild( TiXmlNode* removeThis ); /// Navigate to a sibling node. const TiXmlNode* PreviousSibling() const { return prev; } TiXmlNode* PreviousSibling() { return prev; } /// Navigate to a sibling node. const TiXmlNode* PreviousSibling( const char * ) const; TiXmlNode* PreviousSibling( const char *_prev ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); } #ifdef TIXML_USE_STL const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. #endif /// Navigate to a sibling node. const TiXmlNode* NextSibling() const { return next; } TiXmlNode* NextSibling() { return next; } /// Navigate to a sibling node with the given 'value'. const TiXmlNode* NextSibling( const char * ) const; TiXmlNode* NextSibling( const char* _next ) { return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); } /** Convenience function to get through elements. Calls NextSibling and ToElement. Will skip all non-Element nodes. Returns 0 if there is not another element. */ const TiXmlElement* NextSiblingElement() const; TiXmlElement* NextSiblingElement() { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); } /** Convenience function to get through elements. Calls NextSibling and ToElement. Will skip all non-Element nodes. Returns 0 if there is not another element. */ const TiXmlElement* NextSiblingElement( const char * ) const; TiXmlElement* NextSiblingElement( const char *_next ) { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); } #ifdef TIXML_USE_STL const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. #endif /// Convenience function to get through elements. const TiXmlElement* FirstChildElement() const; TiXmlElement* FirstChildElement() { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); } /// Convenience function to get through elements. const TiXmlElement* FirstChildElement( const char * _value ) const; TiXmlElement* FirstChildElement( const char * _value ) { return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); } #ifdef TIXML_USE_STL const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. #endif /** Query the type (as an enumerated value, above) of this node. The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. */ int Type() const { return type; } /** Return a pointer to the Document this node lives in. Returns null if not in a document. */ const TiXmlDocument* GetDocument() const; TiXmlDocument* GetDocument() { return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); } /// Returns true if this node has no children. bool NoChildren() const { return !firstChild; } virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. /** Create an exact duplicate of this node and return it. The memory must be deleted by the caller. */ virtual TiXmlNode* Clone() const = 0; /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the XML tree will be conditionally visited and the host will be called back via the TiXmlVisitor interface. This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse the XML for the callbacks, so the performance of TinyXML is unchanged by using this interface versus any other.) The interface has been based on ideas from: - http://www.saxproject.org/ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern Which are both good references for "visiting". An example of using Accept(): @verbatim TiXmlPrinter printer; tinyxmlDoc.Accept( &printer ); const char* xmlcstr = printer.CStr(); @endverbatim */ virtual bool Accept( TiXmlVisitor* visitor ) const = 0; protected: TiXmlNode( NodeType _type ); // Copy to the allocated object. Shared functionality between Clone, Copy constructor, // and the assignment operator. void CopyTo( TiXmlNode* target ) const; #ifdef TIXML_USE_STL // The real work of the input operator. virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; #endif // Figure out what is at *p, and parse it. Returns null if it is not an xml node. TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); TiXmlNode* parent; NodeType type; TiXmlNode* firstChild; TiXmlNode* lastChild; TIXML_STRING value; TiXmlNode* prev; TiXmlNode* next; private: TiXmlNode( const TiXmlNode& ); // not implemented. void operator=( const TiXmlNode& base ); // not allowed. }; /** An attribute is a name-value pair. Elements have an arbitrary number of attributes, each with a unique name. @note The attributes are not TiXmlNodes, since they are not part of the tinyXML document object model. There are other suggested ways to look at this problem. */ class TiXmlAttribute : public TiXmlBase { friend class TiXmlAttributeSet; public: /// Construct an empty attribute. TiXmlAttribute() : TiXmlBase() { document = 0; prev = next = 0; } #ifdef TIXML_USE_STL /// std::string constructor. TiXmlAttribute( const std::string& _name, const std::string& _value ) { name = _name; value = _value; document = 0; prev = next = 0; } #endif /// Construct an attribute with a name and value. TiXmlAttribute( const char * _name, const char * _value ) { name = _name; value = _value; document = 0; prev = next = 0; } const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. #ifdef TIXML_USE_STL const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. #endif int IntValue() const; ///< Return the value of this attribute, converted to an integer. double DoubleValue() const; ///< Return the value of this attribute, converted to a double. // Get the tinyxml string representation const TIXML_STRING& NameTStr() const { return name; } /** QueryIntValue examines the value string. It is an alternative to the IntValue() method with richer error checking. If the value is an integer, it is stored in 'value' and the call returns TIXML_SUCCESS. If it is not an integer, it returns TIXML_WRONG_TYPE. A specialized but useful call. Note that for success it returns 0, which is the opposite of almost all other TinyXml calls. */ int QueryIntValue( int* _value ) const; /// QueryDoubleValue examines the value string. See QueryIntValue(). int QueryDoubleValue( double* _value ) const; void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. void SetValue( const char* _value ) { value = _value; } ///< Set the value. void SetIntValue( int _value ); ///< Set the value from an integer. void SetDoubleValue( double _value ); ///< Set the value from a double. #ifdef TIXML_USE_STL /// STL std::string form. void SetName( const std::string& _name ) { name = _name; } /// STL std::string form. void SetValue( const std::string& _value ) { value = _value; } #endif /// Get the next sibling attribute in the DOM. Returns null at end. const TiXmlAttribute* Next() const; TiXmlAttribute* Next() { return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); } /// Get the previous sibling attribute in the DOM. Returns null at beginning. const TiXmlAttribute* Previous() const; TiXmlAttribute* Previous() { return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); } bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } /* Attribute parsing starts: first letter of the name returns: the next char after the value end quote */ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); // Prints this Attribute to a FILE stream. virtual void Print( FILE* cfile, int depth ) const { Print( cfile, depth, 0 ); } void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; // [internal use] // Set the document pointer so the attribute can report errors. void SetDocument( TiXmlDocument* doc ) { document = doc; } private: TiXmlAttribute( const TiXmlAttribute& ); // not implemented. void operator=( const TiXmlAttribute& base ); // not allowed. TiXmlDocument* document; // A pointer back to a document, for error reporting. TIXML_STRING name; TIXML_STRING value; TiXmlAttribute* prev; TiXmlAttribute* next; }; /* A class used to manage a group of attributes. It is only used internally, both by the ELEMENT and the DECLARATION. The set can be changed transparent to the Element and Declaration classes that use it, but NOT transparent to the Attribute which has to implement a next() and previous() method. Which makes it a bit problematic and prevents the use of STL. This version is implemented with circular lists because: - I like circular lists - it demonstrates some independence from the (typical) doubly linked list. */ class TiXmlAttributeSet { public: TiXmlAttributeSet(); ~TiXmlAttributeSet(); void Add( TiXmlAttribute* attribute ); void Remove( TiXmlAttribute* attribute ); const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } TiXmlAttribute* Find( const char* _name ) const; TiXmlAttribute* FindOrCreate( const char* _name ); # ifdef TIXML_USE_STL TiXmlAttribute* Find( const std::string& _name ) const; TiXmlAttribute* FindOrCreate( const std::string& _name ); # endif private: //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), //*ME: this class must be also use a hidden/disabled copy-constructor !!! TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) TiXmlAttribute sentinel; }; /** The element is a container class. It has a value, the element name, and can contain other elements, text, comments, and unknowns. Elements also contain an arbitrary number of attributes. */ class TiXmlElement : public TiXmlNode { public: /// Construct an element. TiXmlElement (const char * in_value); #ifdef TIXML_USE_STL /// std::string constructor. TiXmlElement( const std::string& _value ); #endif TiXmlElement( const TiXmlElement& ); TiXmlElement& operator=( const TiXmlElement& base ); virtual ~TiXmlElement(); /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. */ const char* Attribute( const char* name ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. If the attribute exists and can be converted to an integer, the integer value will be put in the return 'i', if 'i' is non-null. */ const char* Attribute( const char* name, int* i ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. If the attribute exists and can be converted to an double, the double value will be put in the return 'd', if 'd' is non-null. */ const char* Attribute( const char* name, double* d ) const; /** QueryIntAttribute examines the attribute - it is an alternative to the Attribute() method with richer error checking. If the attribute is an integer, it is stored in 'value' and the call returns TIXML_SUCCESS. If it is not an integer, it returns TIXML_WRONG_TYPE. If the attribute does not exist, then TIXML_NO_ATTRIBUTE is returned. */ int QueryIntAttribute( const char* name, int* _value ) const; /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). Note that '1', 'true', or 'yes' are considered true, while '0', 'false' and 'no' are considered false. */ int QueryBoolAttribute( const char* name, bool* _value ) const; /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). int QueryDoubleAttribute( const char* name, double* _value ) const; /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). int QueryFloatAttribute( const char* name, float* _value ) const { double d; int result = QueryDoubleAttribute( name, &d ); if ( result == TIXML_SUCCESS ) { *_value = (float)d; } return result; } #ifdef TIXML_USE_STL /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). int QueryStringAttribute( const char* name, std::string* _value ) const { const char* cstr = Attribute( name ); if ( cstr ) { *_value = std::string( cstr ); return TIXML_SUCCESS; } return TIXML_NO_ATTRIBUTE; } /** Template form of the attribute query which will try to read the attribute into the specified type. Very easy, very powerful, but be careful to make sure to call this with the correct type. NOTE: This method doesn't work correctly for 'string' types that contain spaces. @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE */ template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; std::stringstream sstream( node->ValueStr() ); sstream >> *outValue; if ( !sstream.fail() ) return TIXML_SUCCESS; return TIXML_WRONG_TYPE; } int QueryValueAttribute( const std::string& name, std::string* outValue ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) return TIXML_NO_ATTRIBUTE; *outValue = node->ValueStr(); return TIXML_SUCCESS; } #endif /** Sets an attribute of name to a given value. The attribute will be created if it does not exist, or changed if it does. */ void SetAttribute( const char* name, const char * _value ); #ifdef TIXML_USE_STL const std::string* Attribute( const std::string& name ) const; const std::string* Attribute( const std::string& name, int* i ) const; const std::string* Attribute( const std::string& name, double* d ) const; int QueryIntAttribute( const std::string& name, int* _value ) const; int QueryDoubleAttribute( const std::string& name, double* _value ) const; /// STL std::string form. void SetAttribute( const std::string& name, const std::string& _value ); ///< STL std::string form. void SetAttribute( const std::string& name, int _value ); ///< STL std::string form. void SetDoubleAttribute( const std::string& name, double value ); #endif /** Sets an attribute of name to a given value. The attribute will be created if it does not exist, or changed if it does. */ void SetAttribute( const char * name, int value ); /** Sets an attribute of name to a given value. The attribute will be created if it does not exist, or changed if it does. */ void SetDoubleAttribute( const char * name, double value ); /** Deletes an attribute with the given name. */ void RemoveAttribute( const char * name ); #ifdef TIXML_USE_STL void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. #endif const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } /** Convenience function for easy access to the text inside an element. Although easy and concise, GetText() is limited compared to getting the TiXmlText child and accessing it directly. If the first child of 'this' is a TiXmlText, the GetText() returns the character string of the Text node, else null is returned. This is a convenient method for getting the text of simple contained text: @verbatim This is text const char* str = fooElement->GetText(); @endverbatim 'str' will be a pointer to "This is text". Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then the value of str would be null. The first child node isn't a text node, it is another element. From this XML: @verbatim This is text @endverbatim GetText() will return "This is ". WARNING: GetText() accesses a child node - don't become confused with the similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are safe type casts on the referenced node. */ const char* GetText() const; /// Creates a new Element and returns it - the returned element is a copy. virtual TiXmlNode* Clone() const; // Print the Element to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; /* Attribtue parsing starts: next char past '<' returns: next char past '>' */ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* visitor ) const; protected: void CopyTo( TiXmlElement* target ) const; void ClearThis(); // like clear, but initializes 'this' object as well // Used to be public [internal use] #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif /* [internal use] Reads the "value" of the element -- another element, or text. This should terminate with the current end tag. */ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); private: TiXmlAttributeSet attributeSet; }; /** An XML comment. */ class TiXmlComment : public TiXmlNode { public: /// Constructs an empty comment. TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} /// Construct a comment from text. TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { SetValue( _value ); } TiXmlComment( const TiXmlComment& ); TiXmlComment& operator=( const TiXmlComment& base ); virtual ~TiXmlComment() {} /// Returns a copy of this Comment. virtual TiXmlNode* Clone() const; // Write this Comment to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; /* Attribtue parsing starts: at the ! of the !-- returns: next char past '>' */ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* visitor ) const; protected: void CopyTo( TiXmlComment* target ) const; // used to be public #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif // virtual void StreamOut( TIXML_OSTREAM * out ) const; private: }; /** XML text. A text node can have 2 ways to output the next. "normal" output and CDATA. It will default to the mode it was parsed from the XML file and you generally want to leave it alone, but you can change the output mode with SetCDATA() and query it with CDATA(). */ class TiXmlText : public TiXmlNode { friend class TiXmlElement; public: /** Constructor for text element. By default, it is treated as normal, encoded text. If you want it be output as a CDATA text element, set the parameter _cdata to 'true' */ TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) { SetValue( initValue ); cdata = false; } virtual ~TiXmlText() {} #ifdef TIXML_USE_STL /// Constructor. TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) { SetValue( initValue ); cdata = false; } #endif TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } // Write this text object to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; /// Queries whether this represents text using a CDATA section. bool CDATA() const { return cdata; } /// Turns on or off a CDATA representation of text. void SetCDATA( bool _cdata ) { cdata = _cdata; } virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* content ) const; protected : /// [internal use] Creates a new Element and returns it. virtual TiXmlNode* Clone() const; void CopyTo( TiXmlText* target ) const; bool Blank() const; // returns true if all white space and new lines // [internal use] #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: bool cdata; // true if this should be input and output as a CDATA style text element }; /** In correct XML the declaration is the first entry in the file. @verbatim @endverbatim TinyXml will happily read or write files without a declaration, however. There are 3 possible attributes to the declaration: version, encoding, and standalone. Note: In this version of the code, the attributes are handled as special cases, not generic attributes, simply because there can only be at most 3 and they are always the same. */ class TiXmlDeclaration : public TiXmlNode { public: /// Construct an empty declaration. TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} #ifdef TIXML_USE_STL /// Constructor. TiXmlDeclaration( const std::string& _version, const std::string& _encoding, const std::string& _standalone ); #endif /// Construct. TiXmlDeclaration( const char* _version, const char* _encoding, const char* _standalone ); TiXmlDeclaration( const TiXmlDeclaration& copy ); TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); virtual ~TiXmlDeclaration() {} /// Version. Will return an empty string if none was found. const char *Version() const { return version.c_str (); } /// Encoding. Will return an empty string if none was found. const char *Encoding() const { return encoding.c_str (); } /// Is this a standalone document? const char *Standalone() const { return standalone.c_str (); } /// Creates a copy of this Declaration and returns it. virtual TiXmlNode* Clone() const; // Print this declaration to a FILE stream. virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; virtual void Print( FILE* cfile, int depth ) const { Print( cfile, depth, 0 ); } virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* visitor ) const; protected: void CopyTo( TiXmlDeclaration* target ) const; // used to be public #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: TIXML_STRING version; TIXML_STRING encoding; TIXML_STRING standalone; }; /** Any tag that tinyXml doesn't recognize is saved as an unknown. It is a tag of text, but should not be modified. It will be written back to the XML, unchanged, when the file is saved. DTD tags get thrown into TiXmlUnknowns. */ class TiXmlUnknown : public TiXmlNode { public: TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} virtual ~TiXmlUnknown() {} TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } /// Creates a copy of this Unknown and returns it. virtual TiXmlNode* Clone() const; // Print this Unknown to a FILE stream. virtual void Print( FILE* cfile, int depth ) const; virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* content ) const; protected: void CopyTo( TiXmlUnknown* target ) const; #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: }; /** Always the top level node. A document binds together all the XML pieces. It can be saved, loaded, and printed to the screen. The 'value' of a document node is the xml file name. */ class TiXmlDocument : public TiXmlNode { public: /// Create an empty document, that has no name. TiXmlDocument(); /// Create a document with a name. The name of the document is also the filename of the xml. TiXmlDocument( const char * documentName ); #ifdef TIXML_USE_STL /// Constructor. TiXmlDocument( const std::string& documentName ); #endif TiXmlDocument( const TiXmlDocument& copy ); TiXmlDocument& operator=( const TiXmlDocument& copy ); virtual ~TiXmlDocument() {} /** Load a file using the current document value. Returns true if successful. Will delete any existing document data before loading. */ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the current document value. Returns true if successful. bool SaveFile() const; /// Load a file using the given filename. Returns true if successful. bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the given filename. Returns true if successful. bool SaveFile( const char * filename ) const; /** Load a file using the given FILE*. Returns true if successful. Note that this method doesn't stream - the entire object pointed at by the FILE* will be interpreted as an XML file. TinyXML doesn't stream in XML from the current file location. Streaming may be added in the future. */ bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /// Save a file using the given FILE*. Returns true if successful. bool SaveFile( FILE* ) const; #ifdef TIXML_USE_STL bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. { return LoadFile( filename.c_str(), encoding ); } bool SaveFile( const std::string& filename ) const ///< STL std::string version. { return SaveFile( filename.c_str() ); } #endif /** Parse the given null terminated block of xml data. Passing in an encoding to this method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml to use that encoding, regardless of what TinyXml might otherwise try to detect. */ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); /** Get the root element -- the only top level element -- of the document. In well formed XML, there should only be one. TinyXml is tolerant of multiple elements at the document level. */ const TiXmlElement* RootElement() const { return FirstChildElement(); } TiXmlElement* RootElement() { return FirstChildElement(); } /** If an error occurs, Error will be set to true. Also, - The ErrorId() will contain the integer identifier of the error (not generally useful) - The ErrorDesc() method will return the name of the error. (very useful) - The ErrorRow() and ErrorCol() will return the location of the error (if known) */ bool Error() const { return error; } /// Contains a textual (english) description of the error if one occurs. const char * ErrorDesc() const { return errorDesc.c_str (); } /** Generally, you probably want the error string ( ErrorDesc() ). But if you prefer the ErrorId, this function will fetch it. */ int ErrorId() const { return errorId; } /** Returns the location (if known) of the error. The first column is column 1, and the first row is row 1. A value of 0 means the row and column wasn't applicable (memory errors, for example, have no row/column) or the parser lost the error. (An error in the error reporting, in that case.) @sa SetTabSize, Row, Column */ int ErrorRow() const { return errorLocation.row+1; } int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) to report the correct values for row and column. It does not change the output or input in any way. By calling this method, with a tab size greater than 0, the row and column of each node and attribute is stored when the file is loaded. Very useful for tracking the DOM back in to the source file. The tab size is required for calculating the location of nodes. If not set, the default of 4 is used. The tabsize is set per document. Setting the tabsize to 0 disables row/column tracking. Note that row and column tracking is not supported when using operator>>. The tab size needs to be enabled before the parse or load. Correct usage: @verbatim TiXmlDocument doc; doc.SetTabSize( 8 ); doc.Load( "myfile.xml" ); @endverbatim @sa Row, Column */ void SetTabSize( int _tabsize ) { tabsize = _tabsize; } int TabSize() const { return tabsize; } /** If you have handled the error, it can be reset with this call. The error state is automatically cleared if you Parse a new XML block. */ void ClearError() { error = false; errorId = 0; errorDesc = ""; errorLocation.row = errorLocation.col = 0; //errorLocation.last = 0; } /** Write the document to standard out using formatted printing ("pretty print"). */ void Print() const { Print( stdout, 0 ); } /* Write the document to a string using formatted printing ("pretty print"). This will allocate a character array (new char[]) and return it as a pointer. The calling code pust call delete[] on the return char* to avoid a memory leak. */ //char* PrintToMemory() const; /// Print this Document to a FILE stream. virtual void Print( FILE* cfile, int depth = 0 ) const; // [internal use] void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. /** Walk the XML tree visiting this node and all of its children. */ virtual bool Accept( TiXmlVisitor* content ) const; protected : // [internal use] virtual TiXmlNode* Clone() const; #ifdef TIXML_USE_STL virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); #endif private: void CopyTo( TiXmlDocument* target ) const; bool error; int errorId; TIXML_STRING errorDesc; int tabsize; TiXmlCursor errorLocation; bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. }; /** A TiXmlHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml DOM structure. It is a separate utility class. Take an example: @verbatim @endverbatim Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very easy to write a *lot* of code that looks like: @verbatim TiXmlElement* root = document.FirstChildElement( "Document" ); if ( root ) { TiXmlElement* element = root->FirstChildElement( "Element" ); if ( element ) { TiXmlElement* child = element->FirstChildElement( "Child" ); if ( child ) { TiXmlElement* child2 = child->NextSiblingElement( "Child" ); if ( child2 ) { // Finally do something useful. @endverbatim And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity of such code. A TiXmlHandle checks for null pointers so it is perfectly safe and correct to use: @verbatim TiXmlHandle docHandle( &document ); TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); if ( child2 ) { // do something useful @endverbatim Which is MUCH more concise and useful. It is also safe to copy handles - internally they are nothing more than node pointers. @verbatim TiXmlHandle handleCopy = handle; @endverbatim What they should not be used for is iteration: @verbatim int i=0; while ( true ) { TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); if ( !child ) break; // do something ++i; } @endverbatim It seems reasonable, but it is in fact two embedded while loops. The Child method is a linear walk to find the element, so this code would iterate much more than it needs to. Instead, prefer: @verbatim TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); for( child; child; child=child->NextSiblingElement() ) { // do something } @endverbatim */ class TiXmlHandle { public: /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } /// Copy constructor TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } /// Return a handle to the first child node. TiXmlHandle FirstChild() const; /// Return a handle to the first child node with the given name. TiXmlHandle FirstChild( const char * value ) const; /// Return a handle to the first child element. TiXmlHandle FirstChildElement() const; /// Return a handle to the first child element with the given name. TiXmlHandle FirstChildElement( const char * value ) const; /** Return a handle to the "index" child with the given name. The first child is 0, the second 1, etc. */ TiXmlHandle Child( const char* value, int index ) const; /** Return a handle to the "index" child. The first child is 0, the second 1, etc. */ TiXmlHandle Child( int index ) const; /** Return a handle to the "index" child element with the given name. The first child element is 0, the second 1, etc. Note that only TiXmlElements are indexed: other types are not counted. */ TiXmlHandle ChildElement( const char* value, int index ) const; /** Return a handle to the "index" child element. The first child element is 0, the second 1, etc. Note that only TiXmlElements are indexed: other types are not counted. */ TiXmlHandle ChildElement( int index ) const; #ifdef TIXML_USE_STL TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } #endif /** Return the handle as a TiXmlNode. This may return null. */ TiXmlNode* ToNode() const { return node; } /** Return the handle as a TiXmlElement. This may return null. */ TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } /** Return the handle as a TiXmlText. This may return null. */ TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } /** Return the handle as a TiXmlUnknown. This may return null. */ TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } /** @deprecated use ToNode. Return the handle as a TiXmlNode. This may return null. */ TiXmlNode* Node() const { return ToNode(); } /** @deprecated use ToElement. Return the handle as a TiXmlElement. This may return null. */ TiXmlElement* Element() const { return ToElement(); } /** @deprecated use ToText() Return the handle as a TiXmlText. This may return null. */ TiXmlText* Text() const { return ToText(); } /** @deprecated use ToUnknown() Return the handle as a TiXmlUnknown. This may return null. */ TiXmlUnknown* Unknown() const { return ToUnknown(); } private: TiXmlNode* node; }; /** Print to memory functionality. The TiXmlPrinter is useful when you need to: -# Print to memory (especially in non-STL mode) -# Control formatting (line endings, etc.) When constructed, the TiXmlPrinter is in its default "pretty printing" mode. Before calling Accept() you can call methods to control the printing of the XML document. After TiXmlNode::Accept() is called, the printed document can be accessed via the CStr(), Str(), and Size() methods. TiXmlPrinter uses the Visitor API. @verbatim TiXmlPrinter printer; printer.SetIndent( "\t" ); doc.Accept( &printer ); fprintf( stdout, "%s", printer.CStr() ); @endverbatim */ class TiXmlPrinter : public TiXmlVisitor { public: TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), buffer(), indent( " " ), lineBreak( "\n" ) {} virtual bool VisitEnter( const TiXmlDocument& doc ); virtual bool VisitExit( const TiXmlDocument& doc ); virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); virtual bool VisitExit( const TiXmlElement& element ); virtual bool Visit( const TiXmlDeclaration& declaration ); virtual bool Visit( const TiXmlText& text ); virtual bool Visit( const TiXmlComment& comment ); virtual bool Visit( const TiXmlUnknown& unknown ); /** Set the indent characters for printing. By default 4 spaces but tab (\t) is also useful, or null/empty string for no indentation. */ void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } /// Query the indention string. const char* Indent() { return indent.c_str(); } /** Set the line breaking string. By default set to newline (\n). Some operating systems prefer other characters, or can be set to the null/empty string for no indenation. */ void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } /// Query the current line breaking string. const char* LineBreak() { return lineBreak.c_str(); } /** Switch over to "stream printing" which is the most dense formatting without linebreaks. Common when the XML is needed for network transmission. */ void SetStreamPrinting() { indent = ""; lineBreak = ""; } /// Return the result. const char* CStr() { return buffer.c_str(); } /// Return the length of the result string. size_t Size() { return buffer.size(); } #ifdef TIXML_USE_STL /// Return the result. const std::string& Str() { return buffer; } #endif private: void DoIndent() { for( int i=0; i(-1); // Null rep. TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; void TiXmlString::reserve (size_type cap) { if (cap > capacity()) { TiXmlString tmp; tmp.init(length(), cap); memcpy(tmp.start(), data(), length()); swap(tmp); } } TiXmlString& TiXmlString::assign(const char* str, size_type len) { size_type cap = capacity(); if (len > cap || cap > 3*(len + 8)) { TiXmlString tmp; tmp.init(len); memcpy(tmp.start(), str, len); swap(tmp); } else { memmove(start(), str, len); set_size(len); } return *this; } TiXmlString& TiXmlString::append(const char* str, size_type len) { size_type newsize = length() + len; if (newsize > capacity()) { reserve (newsize + capacity()); } memmove(finish(), str, len); set_size(newsize); return *this; } TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) { TiXmlString tmp; tmp.reserve(a.length() + b.length()); tmp += a; tmp += b; return tmp; } TiXmlString operator + (const TiXmlString & a, const char* b) { TiXmlString tmp; TiXmlString::size_type b_len = static_cast( strlen(b) ); tmp.reserve(a.length() + b_len); tmp += a; tmp.append(b, b_len); return tmp; } TiXmlString operator + (const char* a, const TiXmlString & b) { TiXmlString tmp; TiXmlString::size_type a_len = static_cast( strlen(a) ); tmp.reserve(a_len + b.length()); tmp.append(a, a_len); tmp += b; return tmp; } #endif // TIXML_USE_STL blobby-1.0rc3/src/blobnet/Logger.hpp0000644000175000017500000000362512042452372020716 0ustar danielknobedanielknobe/*============================================================================= blobNet Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* Includes */ /** * Macros for primitive plattform independend logging and debugging * * This header include the the logging macros for debugging. * You can log information by calling the LOG(class, message) macro. * There are the following log-modes available which you can set by changing the * definition of the LOGGER_MODE macro: */ #define LOGGER_OFF 0 // Debugging is off and all overhead is removed #define LOGGER_CONSOLE 1 // The debugging information is printed to the std::out /** * Set the mode here: */ #define LOGGER_MODE LOGGER_CONSOLE /* Implemenation */ #if LOGGER_MODE == LOGGER_OFF #define LOG(class, message) #endif #if LOGGER_MODE == LOGGER_CONSOLE #include #include #define LOG(class, message) \ { \ time_t timeRaw; \ struct tm * timeInfo; \ char* timeAsc; \ time(&timeRaw); \ timeInfo = localtime(&timeRaw); \ timeAsc = asctime(timeInfo); \ timeAsc[strlen(timeAsc)-1] = '\0'; \ std::cout << timeAsc << " " << class << ": " << message << std::endl; \ } #endif blobby-1.0rc3/src/blobnet/CMakeLists.txt0000644000175000017500000000014012042452372021513 0ustar danielknobedanielknobeset (blobnet_SRC layer/Http.cpp layer/Http.hpp ) add_library(blobnet STATIC ${blobnet_SRC}) blobby-1.0rc3/src/server/DedicatedServer.h0000644000175000017500000000223512042452372022051 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include #include "raknet/NetworkTypes.h" class NetworkGame; typedef std::map > PlayerMap; typedef std::list< boost::shared_ptr > GameList; blobby-1.0rc3/src/server/NetworkPlayer.cpp0000644000175000017500000000455012042452372022157 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "NetworkPlayer.h" /* includes */ /* implementation */ // initialise NetworkPlayer. Set NetworkID to 0.0.0.0:0, so we are sure no player // will ever have this. NetworkPlayer::NetworkPlayer() : mID(), mName(), mColor(), mDesiredSide(NO_PLAYER) { mID.binaryAddress = 0; mID.port = 0; } NetworkPlayer::NetworkPlayer(PlayerID id, const std::string& name, Color color, PlayerSide side) :mID(id), mName(name), mColor(color), mDesiredSide(side) { } NetworkPlayer::NetworkPlayer(PlayerID id, RakNet::BitStream stream) : mID(id) { int playerSide; stream.Read(playerSide); mDesiredSide = (PlayerSide)playerSide; // Read the Playername char charName[16]; stream.Read(charName, sizeof(charName)); // ensures that charName is null terminated charName[sizeof(charName)-1] = '\0'; mName = charName; // read colour data int color; stream.Read(color); mColor = color; } bool NetworkPlayer::valid() const { return mDesiredSide != NO_PLAYER; } const PlayerID& NetworkPlayer::getID() const { return mID; } const std::string& NetworkPlayer::getName() const { return mName; } const Color& NetworkPlayer::getColor() const { return mColor; } PlayerSide NetworkPlayer::getDesiredSide() const { return mDesiredSide; } const boost::shared_ptr& NetworkPlayer::getGame() const { return mGame; } blobby-1.0rc3/src/server/NetworkPlayer.h0000644000175000017500000000414512042452372021624 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "raknet/NetworkTypes.h" #include "Global.h" class NetworkGame; /*! \brief class for managing an online player \details This class manages an online player, that is, his identity (name, color, ...), his network address and associated game */ /// \todo add data to log when last packet arrived class NetworkPlayer { public: NetworkPlayer(); NetworkPlayer(PlayerID id, const std::string& name, Color color, PlayerSide side); // i guess we should! not need to make a copy here // but this is saver as this constructor can't mess up other code. NetworkPlayer(PlayerID id, RakNet::BitStream stream); bool valid() const; const PlayerID& getID() const; const std::string& getName() const; const Color& getColor() const; PlayerSide getDesiredSide() const; const boost::shared_ptr& getGame() const; private: PlayerID mID; /* Identity */ std::string mName; Color mColor; PlayerSide mDesiredSide; /* Game Data */ boost::shared_ptr mGame; /* we could add more data such as stats, accoutn info, etc later. */ }; blobby-1.0rc3/src/server/DedicatedServer.cpp0000644000175000017500000003515512042452372022413 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "DedicatedServer.h" /* includes */ #include #include #include #include #include #include #include "raknet/RakServer.h" #include "raknet/PacketEnumerations.h" #include "raknet/GetTime.h" #include #include "InputSource.h" #include "PhysicWorld.h" #include "NetworkGame.h" #include "UserConfig.h" #include "NetworkMessage.h" #include "SpeedController.h" #include "RakNetPacket.h" #include "NetworkPlayer.h" #include "FileSystem.h" // platform specific #ifndef WIN32 #include #include #else #include #endif /* implementation */ #ifdef WIN32 #undef main // function for logging to replacing syslog enum { LOG_ERR, LOG_NOTICE, LOG_DEBUG }; void syslog(int pri, const char* format, ...); #endif static bool g_run_in_foreground = false; static bool g_print_syslog_to_stderr = false; static bool g_workaround_memleaks = false; // ... void printHelp(); void process_arguments(int argc, char** argv); void fork_to_background(); void wait_and_restart_child(); void setup_physfs(char* argv0); // server workload statistics int SWLS_PacketCount = 0; int SWLS_Connections = 0; int SWLS_Games = 0; int SWLS_GameSteps = 0; int SWLS_RunningTime = 0; // functions for processing certain network packets void createNewGame(); int main(int argc, char** argv) { process_arguments(argc, argv); FileSystem fileSys(argv[0]); if (!g_run_in_foreground) { fork_to_background(); } if (g_workaround_memleaks) { wait_and_restart_child(); } int startTime = SDL_GetTicks(); #ifndef WIN32 int syslog_options = LOG_CONS | LOG_PID | (g_print_syslog_to_stderr ? LOG_PERROR : 0); openlog("blobby-server", syslog_options, LOG_DAEMON); #endif setup_physfs(argv[0]); GameList gamelist; PlayerMap playermap; RakServer server; UserConfig config; NetworkPlayer firstPlayer; int port = BLOBBY_PORT; int maxClients = 100; try { config.loadFile("server.xml"); port = config.getInteger("port"); maxClients = config.getInteger("maximum_clients"); // bring that value into a sane range if(maxClients <= 0 || maxClients > 1000) maxClients = 150; } catch (std::exception& e) { syslog(LOG_ERR, "server.xml not found. Falling back to default values."); } int clients = 0; ServerInfo myinfo(config); float speed = myinfo.gamespeed; if (!server.Start(maxClients, 1, port)) { syslog(LOG_ERR, "Couldn´t bind to port %i, exiting", port); return 2; } SpeedController scontroller(speed); SpeedController::setMainInstance(&scontroller); syslog(LOG_NOTICE, "Blobby Volley 2 dedicated server version %i.%i started", BLOBBY_VERSION_MAJOR, BLOBBY_VERSION_MINOR); packet_ptr packet; while (1) { // ------------------------------------------------------------------------------- // process all incoming packets , probably relay them to responsible network games // ------------------------------------------------------------------------------- while ((packet = receivePacket(&server))) { SWLS_PacketCount++; switch(packet->data[0]) { case ID_NEW_INCOMING_CONNECTION: clients++; SWLS_Connections++; syslog(LOG_DEBUG, "New incoming connection, %d clients connected now", clients); break; case ID_CONNECTION_LOST: case ID_DISCONNECTION_NOTIFICATION: { bool cond1 = firstPlayer.valid(); bool cond2 = firstPlayer.getID() == packet->playerId; // if first player disconncted, reset if (cond1 && cond2) firstPlayer = NetworkPlayer(); // delete the disconnectiong player if ( playermap.find(packet->playerId) != playermap.end() ) { /// \todo what are we doing here??? /// seems not a good idea to let injectPacket remove the game from the game list... /// maybe we should add a centralized way to delete unused games and players! // inject the packet into the game /// strange, injectPacket just pushes the packet into a queue. That cannot delete /// the game??? playermap[packet->playerId]->injectPacket(packet); // if it was the last player, the game is removed from the game list. // thus, give the game a last chance to process the last // input // check, wether game was removed from game list (not a good idea!), in that case, process manually if( std::find(gamelist.begin(), gamelist.end(), playermap[packet->playerId]) == gamelist.end()) { playermap[packet->playerId]->step(); } // then delete the player playermap.erase(packet->playerId); } clients--; syslog(LOG_DEBUG, "Connection closed, %d clients connected now", clients); break; } case ID_INPUT_UPDATE: case ID_PAUSE: case ID_UNPAUSE: case ID_CHAT_MESSAGE: case ID_REPLAY: if (playermap.find(packet->playerId) != playermap.end()){ playermap[packet->playerId]->injectPacket(packet); // check, wether game was delete from this, in this case, process manually /// \todo here again, injectPacket is not able to delete the game. So, what are we doing here? if( std::find(gamelist.begin(), gamelist.end(), playermap[packet->playerId]) == gamelist.end()) { playermap[packet->playerId]->step(); } } else { syslog(LOG_ERR, "player not found!"); #ifdef DEBUG std::cout << " received game packet for no longer existing game! " << (int)packet->data[0] << " - " << packet->playerId.binaryAddress << " : " << packet->playerId.port << "\n"; // only quit in debug mode as this is not a problem endangering the stability // of the running server, but a situation that should never occur. return 3; #endif } break; case ID_ENTER_GAME: { RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); //ID_ENTER_GAME if (!firstPlayer.valid()) { /// \todo does the copy-ctor what i assume it does? deep copy? firstPlayer = NetworkPlayer(packet->playerId, stream); } else // We have two players now { NetworkPlayer secondPlayer = NetworkPlayer(packet->playerId, stream); /// \todo refactor this, this code is awful! /// one swap should be enough NetworkPlayer leftPlayer = firstPlayer; NetworkPlayer rightPlayer = secondPlayer; PlayerSide switchSide = NO_PLAYER; if(RIGHT_PLAYER == firstPlayer.getDesiredSide()) { std::swap(leftPlayer, rightPlayer); } if (secondPlayer.getDesiredSide() == firstPlayer.getDesiredSide()) { if (secondPlayer.getDesiredSide() == LEFT_PLAYER) switchSide = RIGHT_PLAYER; if (secondPlayer.getDesiredSide() == RIGHT_PLAYER) switchSide = LEFT_PLAYER; } boost::shared_ptr newgame (new NetworkGame( server, leftPlayer.getID(), rightPlayer.getID(), leftPlayer.getName(), rightPlayer.getName(), leftPlayer.getColor(), rightPlayer.getColor(), switchSide) ); playermap[leftPlayer.getID()] = newgame; playermap[rightPlayer.getID()] = newgame; gamelist.push_back(newgame); SWLS_Games++; #ifdef DEBUG std::cout << "NEW GAME CREATED:\t"<data, packet->length, false); // If the client knows nothing about versioning, the version is 0.0 int major = 0; int minor = 0; bool wrongPackageSize = true; // actuel client has bytesize 72 if(packet->bitSize == 72) { stream.IgnoreBytes(1); //ID_BLOBBY_SERVER_PRESENT stream.Read(major); stream.Read(minor); wrongPackageSize = false; } RakNet::BitStream stream2; if (wrongPackageSize) { printf("major: %d minor: %d\n", major, minor); stream2.Write((unsigned char)ID_VERSION_MISMATCH); stream2.Write((int)BLOBBY_VERSION_MAJOR); stream2.Write((int)BLOBBY_VERSION_MINOR); server.Send(&stream2, LOW_PRIORITY, RELIABLE_ORDERED, 0, packet->playerId, false); } else if (major < BLOBBY_VERSION_MAJOR || (major == BLOBBY_VERSION_MAJOR && minor < BLOBBY_VERSION_MINOR)) // Check if the packet contains matching version numbers { stream2.Write((unsigned char)ID_VERSION_MISMATCH); stream2.Write((int)BLOBBY_VERSION_MAJOR); stream2.Write((int)BLOBBY_VERSION_MINOR); server.Send(&stream2, LOW_PRIORITY, RELIABLE_ORDERED, 0, packet->playerId, false); } else { myinfo.activegames = gamelist.size(); if (!firstPlayer.valid()) { myinfo.setWaitingPlayer("none"); } else { myinfo.setWaitingPlayer(firstPlayer.getName()); } stream2.Write((unsigned char)ID_BLOBBY_SERVER_PRESENT); myinfo.writeToBitstream(stream2); server.Send(&stream2, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->playerId, false); } break; } case ID_RECEIVED_STATIC_DATA: break; default: syslog(LOG_DEBUG, "Unknown packet %d received\n", int(packet->data[0])); } } // ------------------------------------------------------------------------------- // now, step through all network games and process input - if a game ended, delete it // ------------------------------------------------------------------------------- SWLS_RunningTime++; if(SWLS_RunningTime % (75 * 60 * 60 /*1h*/) == 0 ) { std::cout << "Blobby Server Status Report " << (SWLS_RunningTime / 75 / 60 / 60) << "h running \n"; std::cout << " packet count: " << SWLS_PacketCount << "\n"; std::cout << " accepted connections: " << SWLS_Connections << "\n"; std::cout << " started games: " << SWLS_Games << "\n"; std::cout << " game steps: " << SWLS_GameSteps << "\n"; } for (GameList::iterator iter = gamelist.begin(); gamelist.end() != iter; ++iter) { SWLS_GameSteps++; if (!(*iter)->step()) { iter = gamelist.erase(iter); // workarround to prevent increment of // past-end-iterator if(iter == gamelist.end()) break; } } scontroller.update(); if (g_workaround_memleaks) { // Workaround for memory leak // Restart the server after 1 hour if no player is // connected if ((SDL_GetTicks() - startTime) > 60 * 60 * 1000) { if (gamelist.empty() && !firstPlayer.valid()) { exit(0); } } } } syslog(LOG_NOTICE, "Blobby Volley 2 dedicated server shutting down"); #ifndef WIN32 closelog(); #endif } // ----------------------------------------------------------------------------------------- void createNewGame() { } // ----------------------------------------------------------------------------------------- void printHelp() { std::cout << "Usage: blobby-server [OPTION...]" << std::endl; std::cout << " -m, --memleak-hack Workaround memory leaks by restarting regularly" << std::endl; std::cout << " -n, --no-daemon Don´t run as background process" << std::endl; std::cout << " -p, --print-msgs Print messages to stderr" << std::endl; std::cout << " -h, --help This message" << std::endl; } void process_arguments(int argc, char** argv) { if (argc > 1) { for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "--memleak-hack") == 0 || strcmp(argv[i], "-m") == 0) { g_workaround_memleaks = true; continue; } if (strcmp(argv[i], "--no-daemon") == 0 || strcmp(argv[i], "-n") == 0) { g_run_in_foreground = true; continue; } if (strcmp(argv[i], "--print-msgs") == 0 || strcmp(argv[i], "-p") == 0) { g_print_syslog_to_stderr = true; continue; } if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { printHelp(); exit(3); } std::cout << "Unknown option \"" << argv[i] << "\"" << std::endl; printHelp(); exit(1); } } } void fork_to_background() { #ifndef WIN32 pid_t f_return = fork(); if (f_return == -1) { perror("fork"); exit(1); } if (f_return != 0) { std::cout << "Running in background as PID " << f_return << std::endl; exit(0); } #else std::cerr<<"fork is not available under windows\n"; #endif } void wait_and_restart_child() { #ifndef WIN32 pid_t leaking_server; while ((leaking_server = fork()) > 0) { int status; // Wait for server to quit and refork waitpid(leaking_server, &status, 0); // Error will propably occur again if (WEXITSTATUS(status) != 0) { exit(WEXITSTATUS(status)); } } if (leaking_server == -1) { perror("fork"); exit(1); } #else std::cerr<<"fork is not available under windows\n"; #endif } void setup_physfs(char* argv0) { FileSystem& fs = FileSystem::getSingleton(); fs.addToSearchPath("data"); } #ifdef WIN32 #undef main void syslog(int pri, const char* format, ...) { // first, look where we want to send our message to FILE* target = stdout; switch(pri) { case LOG_ERR: target = stderr; break; case LOG_NOTICE: case LOG_DEBUG: target = stdout; break; } // create a string containing date and time std::time_t time_v = std::time(0); std::tm* time = localtime(&time_v); char buffer[128]; std::strftime(buffer, sizeof(buffer), "%x - %X", time); // print it fprintf(target, "%s: ", buffer); // now relay the passed arguments and format string to vfprintf for output va_list args; va_start (args, format); vfprintf(target, format, args); va_end (args); // end finish with a newline fprintf(target, "\n"); } #endif blobby-1.0rc3/src/state/NetworkState.h0000644000175000017500000000631212042452373021261 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "State.h" #include "NetworkMessage.h" #include #include class RakClient; class RakServer; class DuelMatch; class NetworkGame; /*! \class NetworkGameState \brief State for Network Game \details state which is responsible for presenting a network game, sending player input to the server, managing chat etc. */ class NetworkGameState : public State { public: /// create a NetworkGameState with connection to a certain server /// \param servername Name of server /// \param port Target port NetworkGameState(const std::string& servername, Uint16 port); virtual ~NetworkGameState(); virtual void step(); virtual const char* getStateName() const; private: enum { CONNECTING, WAITING_FOR_OPPONENT, OPPONENT_DISCONNECTED, DISCONNECTED, CONNECTION_FAILED, SERVER_FULL, PLAYING, PLAYER_WON, PAUSING } mNetworkState; Player mLeftPlayer; Player mRightPlayer; // these are pointers to mLeftPlayer or mRightPlayer respectively, so we don't need a smart pointer here Player* mLocalPlayer; Player* mRemotePlayer; bool mUseRemoteColor; boost::scoped_ptr mLocalInput; PlayerSide mServingPlayer; bool mSaveReplay; bool mWaitingForReplay; std::string mFilename; std::string mErrorMessage; boost::scoped_ptr mClient; PlayerSide mOwnSide; PlayerSide mWinningPlayer; boost::scoped_ptr mFakeMatch; // This hack is necessary to let MouseInputDevice // access the necessary game variables // Chat Vars std::vector mChatlog; std::vector mChatOrigin; int mSelectedChatmessage; unsigned mChatCursorPosition; std::string mChattext; }; /*! \class NetworkHostState \brief state for hosting a game locally \details This class is a wrapper for NetworkGameState to run an instance of NetworkGame \todo this construction seems like a big hack ;) */ class NetworkHostState : public State { public: NetworkHostState(); virtual ~NetworkHostState(); virtual void step(); virtual const char* getStateName() const; private: NetworkGameState* mGameState; NetworkGame* mNetworkGame; RakServer* mServer; PlayerSide mLocalPlayerSide; PlayerID mLocalPlayer; PlayerID mRemotePlayer; Color mLeftColor; Color mRightColor; std::string mLocalPlayerName; std::string mRemotePlayerName; }; blobby-1.0rc3/src/state/NetworkSearchState.cpp0000644000175000017500000003304412042452373022744 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "NetworkSearchState.h" /* includes */ #include #include #include #include // debugging #include #include "raknet/RakClient.h" #include "raknet/PacketEnumerations.h" #include "raknet/RakServer.h" #include "blobnet/layer/Http.hpp" #include "blobnet/exception/HttpException.hpp" #include "tinyxml/tinyxml.h" #include "NetworkState.h" #include "TextManager.h" #include "IMGUI.h" #include "RakNetPacket.h" #include "IUserConfigReader.h" #include "FileWrite.h" #include "FileRead.h" /* implementation */ NetworkSearchState::NetworkSearchState() : mPingClient(new RakClient) { IMGUI::getSingleton().resetSelection(); mSelectedServer = 0; mServerBoxPosition = 0; mDisplayInfo = false; mEnteringServer = false; mDisplayUpdateNotification = false; } NetworkSearchState::~NetworkSearchState() { // Disconnect from servers for (ClientList::iterator iter = mQueryClients.begin(); iter != mQueryClients.end(); ++iter) { if (*iter) { (*iter)->Disconnect(50); delete *iter; } } } void NetworkSearchState::step() { packet_ptr packet; for (ClientList::iterator iter = mQueryClients.begin(); iter != mQueryClients.end(); ++iter) { bool skip = false; bool skip_iter = false; while ((packet = receivePacket(*iter)) && !skip) { switch(packet->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: { printf("connection accepted from %s:%d\n", mPingClient->PlayerIDToDottedIP( packet->playerId), packet->playerId.port); RakNet::BitStream stream; stream.Write((unsigned char)ID_BLOBBY_SERVER_PRESENT); stream.Write(BLOBBY_VERSION_MAJOR); stream.Write(BLOBBY_VERSION_MINOR); (*iter)->Send(&stream, LOW_PRIORITY, RELIABLE_ORDERED, 0); break; } case ID_BLOBBY_SERVER_PRESENT: { //FIXME: We must copy the needed informations, so that we can call DeallocatePacket(packet) //FIXME: The client finds a server at this point, which is not valid RakNet::BitStream stream((char*)packet->data, packet->length, false); printf("server is a blobby server\n"); stream.IgnoreBytes(1); //ID_BLOBBY_SERVER_PRESENT ServerInfo info(stream, (*iter)->PlayerIDToDottedIP( packet->playerId), packet->playerId.port); if (std::find( mScannedServers.begin(), mScannedServers.end(), info) == mScannedServers.end() // check whether the packet sizes match && packet->length == ServerInfo::BLOBBY_SERVER_PRESENT_PACKET_SIZE ){ mScannedServers.push_back(info); } // the RakClient will be deleted, so // we must free the packet here packet.reset(); (*iter)->Disconnect(50); delete *iter; iter = mQueryClients.erase(iter); if (iter == mQueryClients.end()) skip_iter = true; skip = true; break; } case ID_VERSION_MISMATCH: { // this packet is send when the client is older than the server! // so RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); // ID_VERSION_MISMATCH // default values if server does not send versions. // thats the 0.9 behaviour int smajor = 0, sminor = 9; stream.Read(smajor); // load server version information stream.Read(sminor); printf("found blobby server with version %d.%d\n", smajor, sminor); mDisplayUpdateNotification = true; // the RakClient will be deleted, so // we must free the packet here packet.reset(); (*iter)->Disconnect(50); delete *iter; iter = mQueryClients.erase(iter); if (iter == mQueryClients.end()) skip_iter = true; skip = true; break; } default: break; } if (skip) break; } if (skip_iter) break; } while (packet = receivePacket(mPingClient.get())) { switch (packet->data[0]) { case ID_PONG: { std::string hostname = mPingClient->PlayerIDToDottedIP(packet->playerId); printf("got ping response by \"%s:%d\", trying to connect\n", hostname.c_str(), packet->playerId.port); RakClient* newClient = new RakClient; newClient->Connect( hostname.c_str(), packet->playerId.port, 0, 0, RAKNET_THREAD_SLEEP_TIME); mQueryClients.push_back(newClient); } default: break; } } IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); imgui.doInactiveMode(false); if (mDisplayInfo || mEnteringServer) { imgui.doInactiveMode(true); } if (imgui.doButton(GEN_ID, Vector2(10, 20), TextManager::NET_SERVER_SCAN)) searchServers(); if (imgui.doButton(GEN_ID, Vector2(420, 20), TextManager::NET_DIRECT_CONNECT) && !mEnteringServer) { mEnteringServer = true; imgui.resetSelection(); mEnteredServer = ""; mServerBoxPosition = 0; } std::vector servernames; for (int i = 0; i < mScannedServers.size(); i++) { servernames.push_back(std::string(mScannedServers[i].name) + " (" + mScannedServers[i].waitingplayer + ")" ); } bool doEnterServer = false; if( imgui.doSelectbox(GEN_ID, Vector2(25.0, 60.0), Vector2(775.0, 470.0), servernames, mSelectedServer) == SBA_DBL_CLICK ) { doEnterServer = true; } if (imgui.doButton(GEN_ID, Vector2(50, 480), TextManager::NET_SERVER_INFO) && !mDisplayInfo && !mScannedServers.empty()) { mDisplayInfo = true; imgui.resetSelection(); } if (mEnteringServer) { imgui.doInactiveMode(false); imgui.doOverlay(GEN_ID, Vector2(100.0, 200.0), Vector2(650.0, 400.0)); // Game crashes if the mEnteredServer is not a possible input imgui.doEditbox(GEN_ID, Vector2(130.0, 210.0), 20, mEnteredServer, mServerBoxPosition); if (imgui.doButton(GEN_ID, Vector2(270.0, 300.0), TextManager::LBL_OK)) { //std::string server = mScannedServers[mSelectedServer].hostname; std::string server = mEnteredServer; int port = BLOBBY_PORT; std::size_t found = mEnteredServer.find(':'); if (found != std::string::npos) { server = mEnteredServer.substr(0, found); try { port = boost::lexical_cast(mEnteredServer.substr(found+1)); } catch (boost::bad_lexical_cast) { /// \todo inform the user that default port was selected } if ((port <= 0) || (port > 65535)) port = BLOBBY_PORT; } deleteCurrentState(); setCurrentState(new NetworkGameState(server, port)); return; } if (imgui.doButton(GEN_ID, Vector2(370.0, 300.0), TextManager::LBL_CANCEL)) { mEnteringServer = false; imgui.resetSelection(); } imgui.doInactiveMode(true); } if (mDisplayInfo) { imgui.doInactiveMode(false); imgui.doOverlay(GEN_ID, Vector2(40.0, 80.0), Vector2(760.0, 440.0)); imgui.doText(GEN_ID, Vector2(50, 100), mScannedServers[mSelectedServer].name); imgui.doText(GEN_ID, Vector2(50, 130), mScannedServers[mSelectedServer].hostname); std::stringstream activegames; activegames << TextManager::getSingleton()->getString(TextManager::NET_ACTIVE_GAMES) << ": " << mScannedServers[mSelectedServer].activegames; imgui.doText(GEN_ID, Vector2(50, 160), activegames.str()); std::stringstream waitingplayer; waitingplayer << TextManager::getSingleton()->getString(TextManager::NET_WAITING_PLAYER) << mScannedServers[mSelectedServer].waitingplayer; imgui.doText(GEN_ID, Vector2(50, 190), waitingplayer.str()); std::stringstream gamespeed; gamespeed << TextManager::getSingleton()->getString(TextManager::OP_SPEED)<<" " << int(100.0 / 75.0 * mScannedServers[mSelectedServer].gamespeed)<<"%"; imgui.doText(GEN_ID, Vector2(50, 220), gamespeed.str()); std::string description = mScannedServers[mSelectedServer].description; for (int i = 0; i < description.length(); i += 29) { imgui.doText(GEN_ID, Vector2(50, 250 + i / 29 * 30), description.substr(i, 29)); } if (imgui.doButton(GEN_ID, Vector2(410, 405), TextManager::LBL_OK)) { mDisplayInfo = false; imgui.resetSelection(); } imgui.doInactiveMode(true); } if (imgui.doButton(GEN_ID, Vector2(450, 480), TextManager::NET_HOST_GAME) && !mDisplayInfo) { deleteCurrentState(); setCurrentState(new NetworkHostState()); return; } if (imgui.doButton(GEN_ID, Vector2(230, 530), TextManager::LBL_OK) && !mScannedServers.empty() || doEnterServer) { ServerInfo server = mScannedServers[mSelectedServer]; deleteCurrentState(); setCurrentState(new NetworkGameState(server.hostname, server.port)); } if (imgui.doButton(GEN_ID, Vector2(480, 530), TextManager::LBL_CANCEL)) { deleteCurrentState(); setCurrentState(new MainMenuState); } if(mDisplayUpdateNotification) { imgui.doOverlay(GEN_ID, Vector2(71, 572), Vector2(729, 590), Color(128, 0, 0)); imgui.doText(GEN_ID, Vector2(85, 577), TextManager::UPDATE_NOTIFICATION, TF_SMALL_FONT); } } const char* NetworkSearchState::getStateName() const { return "NetworkSearchState"; } // the different networkmodi classes (online/LAN) OnlineSearchState::OnlineSearchState() { searchServers(); } void OnlineSearchState::searchServers() { // Get the serverlist try { BlobNet::Layer::Http http("blobby.sourceforge.net", 80); std::stringstream serverListXml; http.request("server.php", serverListXml); // this trows an exception if the file could not be opened for writing FileWrite file("onlineserver.xml"); file.write(serverListXml.str()); file.close(); } catch (...) { std::cout << "Can't get onlineserver.xml" << std::endl; } std::vector< std::pair > serverList; // Get the serverlist try { boost::shared_ptr serverListXml = FileRead::readXMLDocument("onlineserver.xml"); if (serverListXml->Error()) { std::cerr << "Warning: Parse error in " << "onlineserver.xml"; std::cerr << "!" << std::endl; } TiXmlElement* onlineserverElem = serverListXml->FirstChildElement("onlineserver"); if (onlineserverElem == NULL) { std::cout << "Can't read onlineserver.xml" << std::endl; return; } for (TiXmlElement* serverElem = onlineserverElem->FirstChildElement("server"); serverElem != NULL; serverElem = serverElem->NextSiblingElement("server")) { std::string host; int port; for (TiXmlElement* varElem = serverElem->FirstChildElement("var"); varElem != NULL; varElem = varElem->NextSiblingElement("var")) { const char* tmp; tmp = varElem->Attribute("host"); if(tmp) { host = tmp; continue; } tmp = varElem->Attribute("port"); if(tmp) { try { port = boost::lexical_cast(tmp); } catch (boost::bad_lexical_cast) { port = BLOBBY_PORT; } if ((port <= 0) || (port > 65535)) { port = BLOBBY_PORT; } continue; } } std::pair pairs(host, port); serverList.push_back(pairs); } } catch (...) { std::cout << "Can't read onlineserver.xml" << std::endl; } /// \todo check if we already try to connect to this one! std::string address = IUserConfigReader::createUserConfigReader("config.xml")->getString("additional_network_server"); std::string server = address; int port = BLOBBY_PORT; std::size_t found = address.find(':'); if (found != std::string::npos) { server = address.substr(0, found); try { port = boost::lexical_cast(address.substr(found+1)); } catch (boost::bad_lexical_cast) { /// \todo inform the user that default port was selected } if ((port <= 0) || (port > 65535)) port = BLOBBY_PORT; } std::pair pairs(server.c_str(), port); serverList.push_back(pairs); /// \todo does anyone know how exaclty mPingClient works? mScannedServers.clear(); for(int i = 0; i < serverList.size(); i++) { mPingClient->PingServer(serverList[i].first.c_str(), serverList[i].second, 0, true); } } const char* OnlineSearchState::getStateName() const { return "OnlineSearchState"; } LANSearchState::LANSearchState() { searchServers(); } void LANSearchState::searchServers() { mScannedServers.clear(); mPingClient->PingServer("255.255.255.255", BLOBBY_PORT, 0, true); } const char* LANSearchState::getStateName() const { return "LANSearchState"; } blobby-1.0rc3/src/state/State.cpp0000644000175000017500000002034112042452373020240 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "State.h" /* includes */ #include #include "LocalGameState.h" #include "ReplaySelectionState.h" #include "NetworkState.h" #include "NetworkSearchState.h" #include "OptionsState.h" #include "DuelMatch.h" #include "SoundManager.h" #include "IMGUI.h" #include "TextManager.h" #include "SpeedController.h" #include "Blood.h" /* implementation */ State* State::mCurrentState = 0; State::State() { } State* State::getCurrentState() { if (mCurrentState == 0) { mCurrentState = new MainMenuState; } return mCurrentState; } void State::deleteCurrentState() { /// \todo well, the deleteCurrentState/setCurrentState as we have it now /// seems to have several flaws. First, we have to delete our /// current state BEFORE we create the next one. If I recall right /// this was because some destructors wrote things too disk which /// other constructors had to load (?), so they had to be called /// first. /// So, if the construction of the new state fails, the old is /// already deleted and we have now way to roll back. /// Second, we need two methods were one should be sufficient. delete mCurrentState; mCurrentState = 0; } void State::setCurrentState(State* newState) { assert(!mCurrentState); mCurrentState = newState; } /* void switchState(State* newState) { delete mCurrentState; mCurrentState = newState; } */ void State::presentGame(const DuelMatch& match) { RenderManager& rmanager = RenderManager::getSingleton(); SoundManager& smanager = SoundManager::getSingleton(); // enable game drawing rmanager.drawGame(true); rmanager.setScore(match.getScore(LEFT_PLAYER), match.getScore(RIGHT_PLAYER), match.getServingPlayer() == LEFT_PLAYER, match.getServingPlayer() == RIGHT_PLAYER); rmanager.setBlob(LEFT_PLAYER, match.getBlobPosition(LEFT_PLAYER), match.getWorld().getBlobState(LEFT_PLAYER)); rmanager.setBlob(RIGHT_PLAYER, match.getBlobPosition(RIGHT_PLAYER), match.getWorld().getBlobState(RIGHT_PLAYER)); rmanager.setBall(match.getBallPosition(), match.getWorld().getBallRotation()); rmanager.setTime(match.getClock().getTimeString()); int events = match.getEvents(); if(events & DuelMatch::EVENT_LEFT_BLOBBY_HIT) { smanager.playSound("sounds/bums.wav", match.getWorld().lastHitIntensity() + BALL_HIT_PLAYER_SOUND_VOLUME); Vector2 hitPos = match.getBallPosition() + (match.getBlobPosition(LEFT_PLAYER) - match.getBallPosition()).normalise().scale(31.5); BloodManager::getSingleton().spillBlood(hitPos, match.getWorld().lastHitIntensity(), 0); } if (events & DuelMatch::EVENT_RIGHT_BLOBBY_HIT) { smanager.playSound("sounds/bums.wav", match.getWorld().lastHitIntensity() + BALL_HIT_PLAYER_SOUND_VOLUME); Vector2 hitPos = match.getBallPosition() + (match.getBlobPosition(RIGHT_PLAYER) - match.getBallPosition()).normalise().scale(31.5); BloodManager::getSingleton().spillBlood(hitPos, match.getWorld().lastHitIntensity(), 1); } if (events & DuelMatch::EVENT_ERROR) smanager.playSound("sounds/pfiff.wav", ROUND_START_SOUND_VOLUME); } const char* State::getCurrenStateName() { return getCurrentState()->getStateName(); } MainMenuState::MainMenuState() { IMGUI::getSingleton().resetSelection(); // set main menu fps SpeedController::getMainInstance()->setGameSpeed(75); } MainMenuState::~MainMenuState() { } void MainMenuState::step() { IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); imgui.doImage(GEN_ID, Vector2(250.0, 210.0), "gfx/titel.bmp"); if (imgui.doButton(GEN_ID, Vector2(434, 350.0), TextManager::MNU_LABEL_ONLINE)) { deleteCurrentState(); setCurrentState(new OnlineSearchState()); } if (imgui.doButton(GEN_ID, Vector2(434, 380.0), TextManager::MNU_LABEL_LAN)) { deleteCurrentState(); setCurrentState(new LANSearchState()); } if (imgui.doButton(GEN_ID, Vector2(434.0, 410.0), TextManager::MNU_LABEL_START)) { try { deleteCurrentState(); setCurrentState(new LocalGameState()); } catch (const ScriptException& except) { FILE* file = fopen("lualog.txt", "wb"); fprintf(file, "Lua Error: %s\n", except.luaerror.c_str()); fclose(file); } } if (imgui.doButton(GEN_ID, Vector2(434.0, 440.0), TextManager::MNU_LABEL_OPTIONS)) { deleteCurrentState(); setCurrentState(new OptionState()); } if (imgui.doButton(GEN_ID, Vector2(434.0, 470.0), TextManager::MNU_LABEL_REPLAY)) { deleteCurrentState(); setCurrentState(new ReplaySelectionState()); } if (imgui.doButton(GEN_ID, Vector2(434.0, 500.0), TextManager::MNU_LABEL_CREDITS)) { deleteCurrentState(); setCurrentState(new CreditsState()); } if (imgui.doButton(GEN_ID, Vector2(434.0, 530.0), TextManager::MNU_LABEL_EXIT)) { /// \todo This is not the right way to end Blobby! /// We have shutdown actions in main.cpp, if we change /// those, we'd have to update these here too. /// we should have this at just one place. RenderManager::getSingleton().deinit(); SoundManager::getSingleton().deinit(); deleteCurrentState(); SDL_Quit(); exit(0); } } const char* MainMenuState::getStateName() const { return "MainMenuState"; } CreditsState::CreditsState() { IMGUI::getSingleton().resetSelection(); mYPosition = 600; } void CreditsState::step() { IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); const float xPosition = 50; imgui.doText(GEN_ID, Vector2(xPosition, mYPosition), TextManager::CRD_PROGRAMMERS); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+30), "Daniel Knobe"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+60), " (daniel-knobe(at)web.de)", TF_SMALL_FONT); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+85), "Jonathan Sieber"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+115), " (jonathan_sieber(at)yahoo.de)", TF_SMALL_FONT); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+140), "Sven Rech"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+170), " (svenrech(at)gmx.de)", TF_SMALL_FONT); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+195), "Erik Schultheis"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+225), " (erik-schultheis(at)freenet.de)", TF_SMALL_FONT); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+255), TextManager::CRD_GRAPHICS); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+285), "Silvio Mummert"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+315), " (mummertathome(at)t-online.de)", TF_SMALL_FONT); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+340), "Richard Bertrand"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+370), " (ricbertrand(at)hotmail.com)", TF_SMALL_FONT); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+415), TextManager::CRD_THX); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+445), "Daniel Skoraszewsky"); imgui.doText(GEN_ID, Vector2(xPosition, mYPosition+475), " (skoraszewsky(at)t-online.de)", TF_SMALL_FONT); if (mYPosition > 20) mYPosition -= 2.5; if (imgui.doButton(GEN_ID, Vector2(400.0, 560.0), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState()); return; } } const char* CreditsState::getStateName() const { return "CreditsState"; } blobby-1.0rc3/src/state/OptionsState.h0000644000175000017500000000702512042452373021265 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Global.h" #include "UserConfig.h" #include "State.h" #include /*! \class OptionState \brief State for managing the main options menu */ class OptionState : public State { public: OptionState(); virtual ~OptionState(); virtual void step(); virtual const char* getStateName() const; private: /// writes current settings to disk void save(); UserConfig mOptionConfig; std::vector mScriptNames; int mPlayerOptions[MAX_PLAYERS]; std::string mPlayerName[MAX_PLAYERS]; unsigned mPlayerNamePosition[MAX_PLAYERS]; unsigned mBotStrength[MAX_PLAYERS]; }; /*! \class GraphicOptionsState \brief State for managing the graphics options menu */ class GraphicOptionsState : public State { public: GraphicOptionsState(); virtual ~GraphicOptionsState(); virtual void step(); virtual const char* getStateName() const; private: /// writes current settings to disk void save(); UserConfig mOptionConfig; bool mFullscreen; std::string mRenderer; int mR1, mG1, mB1, mR2, mG2, mB2; bool mLeftMorphing, mRightMorphing; bool mShowShadow; }; /*! \class InputOptionsState \brief State for managing the input options menu */ class InputOptionsState : public State { public: InputOptionsState(); virtual ~InputOptionsState(); virtual void step(); virtual const char* getStateName() const; private: /// writes current settings to disk void save(); UserConfig mOptionConfig; std::string oldString; int mOldInteger; int mSetKeyboard; // 1-10 for LeftKeyboard | 11-20 for RightKeyboard //left data: std::string mLeftBlobbyDevice; int mLeftBlobbyMouseJumpbutton; std::string mLeftBlobbyKeyboardLeft; std::string mLeftBlobbyKeyboardRight; std::string mLeftBlobbyKeyboardJump; std::string mLeftBlobbyJoystickLeft; std::string mLeftBlobbyJoystickRight; std::string mLeftBlobbyJoystickJump; //right data: std::string mRightBlobbyDevice; int mRightBlobbyMouseJumpbutton; std::string mRightBlobbyKeyboardLeft; std::string mRightBlobbyKeyboardRight; std::string mRightBlobbyKeyboardJump; std::string mRightBlobbyJoystickLeft; std::string mRightBlobbyJoystickRight; std::string mRightBlobbyJoystickJump; }; /*! \class MiscOptionsState \brief State for managing the misc options menu */ class MiscOptionsState : public State { public: MiscOptionsState(); virtual ~MiscOptionsState(); virtual void step(); virtual const char* getStateName() const; private: /// writes current settings to disk void save(); UserConfig mOptionConfig; std::vector mBackgrounds; int mBackground; float mVolume; bool mMute; int mGameFPS; bool mShowFPS; bool mShowBlood; int mNetworkSide; std::string mLanguage; }; blobby-1.0rc3/src/state/ReplayState.h0000644000175000017500000000325512042452373021067 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include "State.h" #include "Vector.h" class DuelMatch; class ReplayPlayer; /*! \class ReplayState \brief State playing a replay */ class ReplayState : public State { public: ReplayState(); ~ReplayState(); virtual void step(); virtual const char* getStateName() const; void loadReplay(const std::string& replay); private: boost::scoped_ptr mReplayMatch; boost::scoped_ptr mReplayPlayer; bool mChecksumError; bool mVersionError; Vector2 mLastMousePosition; int mMouseShowTimer; Player mLeftPlayer; Player mRightPlayer; // controls int mPositionJump; bool mPaused; // replay speed control int mSpeedValue; int mSpeedTimer; }; blobby-1.0rc3/src/state/ReplaySelectionState.cpp0000644000175000017500000001552712042452373023275 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "ReplaySelectionState.h" /* includes */ #include #include #include // for cerr #include #include "ReplayState.h" #include "IMGUI.h" #include "TextManager.h" #include "SpeedController.h" #include "FileSystem.h" #include "IReplayLoader.h" /* implementation */ ReplaySelectionState::ReplaySelectionState() { IMGUI::getSingleton().resetSelection(); mChecksumError = false; mVersionError = false; mShowReplayInfo = false; mSelectedReplay = 0; mReplayFiles = FileSystem::getSingleton().enumerateFiles("replays", ".bvr"); if (mReplayFiles.size() == 0) mSelectedReplay = -1; std::sort(mReplayFiles.rbegin(), mReplayFiles.rend()); SpeedController::getMainInstance()->setGameSpeed(75); } void ReplaySelectionState::step() { IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); if (imgui.doButton(GEN_ID, Vector2(224.0, 10.0), TextManager::RP_PLAY) && mSelectedReplay != -1) { std::string loadrep = mReplayFiles[mSelectedReplay]; /// \todo we have to do something against this construction! /// this is dangerous. we delete this state before it has done /// all of its work. ReplayState* rs = 0; try { rs = new ReplayState(); rs->loadReplay(loadrep); imgui.resetSelection(); // at least make sure we end here! deleteCurrentState(); setCurrentState(rs); } catch (std::exception& exp) { delete rs; std::cerr << exp.what() << "\n"; } return; } else if (imgui.doButton(GEN_ID, Vector2(424.0, 10.0), TextManager::LBL_CANCEL)) { deleteCurrentState(); setCurrentState(new MainMenuState()); } else imgui.doSelectbox(GEN_ID, Vector2(34.0, 50.0), Vector2(634.0, 550.0), mReplayFiles, mSelectedReplay); if (imgui.doButton(GEN_ID, Vector2(644.0, 60.0), TextManager::RP_INFO)) { if (!mReplayFiles.empty()) { try { mReplayLoader.reset(IReplayLoader::createReplayLoader(std::string("replays/" + mReplayFiles[mSelectedReplay] + ".bvr"))); mShowReplayInfo = true; } catch (std::exception& e) { std::cerr << e.what() << std::endl; } } } if (imgui.doButton(GEN_ID, Vector2(644.0, 95.0), TextManager::RP_DELETE)) { if (!mReplayFiles.empty()) if (FileSystem::getSingleton().deleteFile("replays/" + mReplayFiles[mSelectedReplay] + ".bvr")) { mReplayFiles.erase(mReplayFiles.begin()+mSelectedReplay); if (mSelectedReplay >= mReplayFiles.size()) mSelectedReplay = mReplayFiles.size()-1; } } if(mShowReplayInfo) { // setup std::string left = mReplayLoader->getPlayerName(LEFT_PLAYER); std::string right = mReplayLoader->getPlayerName(RIGHT_PLAYER); const int MARGIN = std::min(std::max(int(300 - 24*(std::max(left.size(),right.size()))), 50), 150); const int RIGHT = 800 - MARGIN; imgui.doInactiveMode(false); imgui.doOverlay(GEN_ID, Vector2(MARGIN, 180), Vector2(800-MARGIN, 445)); std::string repname = mReplayFiles[mSelectedReplay]; imgui.doText(GEN_ID, Vector2(400-repname.size()*12, 190), repname); // calculate text positions imgui.doText(GEN_ID, Vector2(MARGIN + 20, 225), left); imgui.doText(GEN_ID, Vector2(400-24, 225), "vs"); imgui.doText(GEN_ID, Vector2(RIGHT - 20 - 24*right.size(), 225), right); time_t rd = mReplayLoader->getDate(); struct tm* ptm; ptm = gmtime ( &rd ); //std:: char buffer[255]; std::strftime(buffer, sizeof(buffer), "%d.%m.%Y - %H:%M", ptm); std::string date = buffer; imgui.doText(GEN_ID, Vector2(400 - 12*date.size(), 255), date); imgui.doText(GEN_ID, Vector2(MARGIN+20, 300), TextManager::OP_SPEED); std::string speed = boost::lexical_cast(mReplayLoader->getSpeed() *100 / 75) + "%" ; imgui.doText(GEN_ID, Vector2(RIGHT - 20 - 24*speed.size(), 300), speed); imgui.doText(GEN_ID, Vector2(MARGIN+20, 335), TextManager::RP_DURATION); std::string dur; if(mReplayLoader->getDuration() > 99) { // +30 because of rounding dur = boost::lexical_cast((mReplayLoader->getDuration() + 30) / 60) + "min"; } else { dur = boost::lexical_cast(mReplayLoader->getDuration()) + "s"; } imgui.doText(GEN_ID, Vector2(RIGHT - 20 - 24*dur.size(), 335), dur); std::string res; res = boost::lexical_cast(mReplayLoader->getFinalScore(LEFT_PLAYER)) + " : " + boost::lexical_cast(mReplayLoader->getFinalScore(RIGHT_PLAYER)); imgui.doText(GEN_ID, Vector2(MARGIN+20, 370), TextManager::RP_RESULT); imgui.doText(GEN_ID, Vector2(RIGHT - 20 - 24*res.size(), 370), res); if (imgui.doButton(GEN_ID, Vector2(400-24, 410), TextManager::LBL_OK)) { mShowReplayInfo = false; } else { imgui.doInactiveMode(true); } } if (mChecksumError) { imgui.doInactiveMode(false); imgui.doOverlay(GEN_ID, Vector2(210, 180), Vector2(650, 370)); imgui.doText(GEN_ID, Vector2(250, 200), TextManager::RP_CHECKSUM); imgui.doText(GEN_ID, Vector2(250, 250), TextManager::RP_FILE_CORRUPT); if (imgui.doButton(GEN_ID, Vector2(400, 330), TextManager::LBL_OK)) { mChecksumError = false; } else { imgui.doInactiveMode(true); } } if (mVersionError) { imgui.doInactiveMode(false); imgui.doOverlay(GEN_ID, Vector2(210, 180), Vector2(650, 370)); imgui.doText(GEN_ID, Vector2(250, 200), TextManager::RP_VERSION); imgui.doText(GEN_ID, Vector2(250, 250), TextManager::RP_FILE_OUTDATED); if (imgui.doButton(GEN_ID, Vector2(400, 330), TextManager::LBL_OK)) { mVersionError = false; } else { imgui.doInactiveMode(true); } } } const char* ReplaySelectionState::getStateName() const { return "ReplaySelectionState"; } blobby-1.0rc3/src/state/LocalGameState.cpp0000644000175000017500000001544512042452373022016 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "LocalGameState.h" /* includes */ #include #include #include "DuelMatch.h" #include "InputManager.h" #include "IMGUI.h" #include "ReplayRecorder.h" #include "SoundManager.h" #include "TextManager.h" #include "SpeedController.h" #include "Blood.h" #include "IUserConfigReader.h" #include "FileExceptions.h" #include "FileSystem.h" #include "FileWrite.h" #include "GenericIO.h" /* implementation */ LocalGameState::~LocalGameState() { InputManager::getSingleton()->endGame(); } LocalGameState::LocalGameState() : State(), mLeftPlayer(LEFT_PLAYER), mRightPlayer(RIGHT_PLAYER), mRecorder(new ReplayRecorder()) { mSaveReplay = false; mWinner = false; mErrorMessage = ""; mLeftPlayer.loadFromConfig("left"); mRightPlayer.loadFromConfig("right"); // create default replay name mFilename = mLeftPlayer.getName(); if(mFilename.size() > 7) mFilename.resize(7); mFilename += " vs "; std::string oppname = mRightPlayer.getName(); if(oppname.size() > 7) oppname.resize(7); mFilename += oppname; // set speed SpeedController::getMainInstance()->setGameSpeed( (float)IUserConfigReader::createUserConfigReader("config.xml")->getInteger("gamefps") ); SoundManager::getSingleton().playSound("sounds/pfiff.wav", ROUND_START_SOUND_VOLUME); mRecorder->setPlayerNames(mLeftPlayer.getName(), mRightPlayer.getName()); mRecorder->setPlayerColors( mLeftPlayer.getColor(), mRightPlayer.getColor() ); mRecorder->setGameSpeed((float)IUserConfigReader::createUserConfigReader("config.xml")->getInteger("gamefps")); mMatch.reset(new DuelMatch(mLeftPlayer.getInputSource(), mRightPlayer.getInputSource(), true, false)); RenderManager::getSingleton().setPlayernames(mLeftPlayer.getName(), mRightPlayer.getName()); IMGUI::getSingleton().resetSelection(); } void LocalGameState::step() { RenderManager* rmanager = &RenderManager::getSingleton(); IMGUI& imgui = IMGUI::getSingleton(); if(mErrorMessage != "") { imgui.doOverlay(GEN_ID, Vector2(100, 200), Vector2(700, 360)); size_t split = mErrorMessage.find(':'); std::string mProblem = mErrorMessage.substr(0, split); std::string mInfo = mErrorMessage.substr(split+1); imgui.doText(GEN_ID, Vector2(120, 220), mProblem); imgui.doText(GEN_ID, Vector2(120, 260), mInfo); if(imgui.doButton(GEN_ID, Vector2(330, 320), TextManager::LBL_OK)) { mErrorMessage = ""; } imgui.doCursor(); } else if (mSaveReplay) { imgui.doOverlay(GEN_ID, Vector2(150, 200), Vector2(650, 400)); imgui.doText(GEN_ID, Vector2(190, 220), TextManager::RP_SAVE_NAME); static unsigned cpos; imgui.doEditbox(GEN_ID, Vector2(180, 270), 18, mFilename, cpos); if (imgui.doButton(GEN_ID, Vector2(220, 330), TextManager::LBL_OK)) { try { std::string repFileName = std::string("replays/") + mFilename + std::string(".bvr"); if (mFilename != "") { boost::shared_ptr savetarget = boost::make_shared(repFileName); /// \todo add a check whether we overwrite a file mRecorder->save(savetarget); savetarget->close(); mSaveReplay = false; } imgui.resetSelection(); } catch( FileLoadException& ex) { mErrorMessage = std::string("Unable to create file:" + ex.getFileName()); imgui.resetSelection(); } catch( FileAlreadyExistsException& ex) { mErrorMessage = std::string("File already exists!:"+ ex.getFileName()); imgui.resetSelection(); } catch( std::exception& ex) { mErrorMessage = std::string("Could not save replay: "); imgui.resetSelection(); } } if (imgui.doButton(GEN_ID, Vector2(440, 330), TextManager::LBL_CANCEL)) { mSaveReplay = false; imgui.resetSelection(); } imgui.doCursor(); } else if (mMatch->isPaused()) { imgui.doOverlay(GEN_ID, Vector2(180, 200), Vector2(670, 400)); imgui.doText(GEN_ID, Vector2(281, 260), TextManager::LBL_CONF_QUIT); if (imgui.doButton(GEN_ID, Vector2(530, 300), TextManager::LBL_NO)){ mMatch->unpause(); } if (imgui.doButton(GEN_ID, Vector2(260, 300), TextManager::LBL_YES)) { deleteCurrentState(); setCurrentState(new MainMenuState); } if (imgui.doButton(GEN_ID, Vector2(293, 340), TextManager::RP_SAVE)) { mSaveReplay = true; imgui.resetSelection(); } imgui.doCursor(); } else if (mWinner) { std::string tmp; if(mMatch->winningPlayer() == LEFT_PLAYER) tmp = mLeftPlayer.getName(); else tmp = mRightPlayer.getName(); imgui.doOverlay(GEN_ID, Vector2(200, 150), Vector2(700, 450)); imgui.doImage(GEN_ID, Vector2(200, 250), "gfx/pokal.bmp"); imgui.doText(GEN_ID, Vector2(274, 250), tmp); imgui.doText(GEN_ID, Vector2(274, 300), TextManager::GAME_WIN); if (imgui.doButton(GEN_ID, Vector2(290, 350), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState()); } if (imgui.doButton(GEN_ID, Vector2(400, 350), TextManager::GAME_TRY_AGAIN)) { deleteCurrentState(); setCurrentState(new LocalGameState()); } if (imgui.doButton(GEN_ID, Vector2(320, 390), TextManager::RP_SAVE)) { mSaveReplay = true; imgui.resetSelection(); } imgui.doCursor(); } else if (InputManager::getSingleton()->exit()) { if (mSaveReplay) { mSaveReplay = false; IMGUI::getSingleton().resetSelection(); } else if (mMatch->isPaused()) { deleteCurrentState(); setCurrentState(new MainMenuState); } else { RenderManager::getSingleton().redraw(); mMatch->pause(); } } else { mRecorder->record(mMatch->getState()); mMatch->step(); if (mMatch->winningPlayer() != NO_PLAYER) { mWinner = true; mRecorder->finalize( mMatch->getScore(LEFT_PLAYER), mMatch->getScore(RIGHT_PLAYER) ); } presentGame(*mMatch); rmanager->setBlobColor(LEFT_PLAYER, mLeftPlayer.getColor()); rmanager->setBlobColor(RIGHT_PLAYER, mRightPlayer.getColor()); } } const char* LocalGameState::getStateName() const { return "LocalGameState"; } blobby-1.0rc3/src/state/NetworkSearchState.h0000644000175000017500000000513312042452373022407 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "State.h" #include "PhysicWorld.h" #include "NetworkMessage.h" #include #include #include class RakClient; class RakServer; class DuelMatch; class NetworkGame; /*! \class NetworkSearchState \brief Base class for search states \details This class provides a search interface. The actual search process has to be implemented by derived class by overriding the searchServers() method. */ class NetworkSearchState : public State { public: NetworkSearchState(); virtual ~NetworkSearchState(); virtual void step(); // onlinegames connect to the masterserver // LAN games send a broadcast to local network virtual void searchServers() = 0; virtual const char* getStateName() const; protected: std::vector mScannedServers; boost::scoped_ptr mPingClient; private: typedef std::list ClientList; ClientList mQueryClients; int mSelectedServer; bool mDisplayInfo; bool mEnteringServer; bool mDisplayUpdateNotification; std::string mEnteredServer; unsigned mServerBoxPosition; }; /*! \class OnlineSearchState \brief State for online server search screen. */ class OnlineSearchState : public NetworkSearchState { public: OnlineSearchState(); virtual ~OnlineSearchState() {}; virtual void searchServers(); virtual const char* getStateName() const; }; /*! \class LANSearchState \brief State for LAN game search screen. */ class LANSearchState : public NetworkSearchState { public: LANSearchState(); virtual ~LANSearchState() {}; virtual void searchServers(); virtual const char* getStateName() const; }; blobby-1.0rc3/src/state/State.h0000644000175000017500000000514212042452373017707 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Global.h" #include "Player.h" class DuelMatch; class InputSource; class ReplayRecorder; /*! \class State \brief Base class for all programme states. \details A programm state describes which state a programme is in ( e.g. MainMenu, OptionsMenu, SingleplayerGame etc. ). It defines an abstract step function which is called each frame for the currently active state. Switching to a new state is a little cumbersome right now, as it requires deleting the current State (deleteCurrentState()) and setting the new state afterwards. This approach is very error prone and generally not nice, so I hope we can replace it someday with something better ;) */ class State { private: static State* mCurrentState; protected: State(); void deleteCurrentState(); void setCurrentState(State* newState); void switchState(State* newState); /// static protected helper function that /// draws the game. It is in State because /// this functionality is shared by /// LocalGameState, NetworkGameState and ReplayState static void presentGame(const DuelMatch& match); public: virtual ~State() {} virtual void step() = 0; static State* getCurrentState(); virtual const char* getStateName() const = 0; static const char* getCurrenStateName(); }; /*! \class MainMenuState \brief state for main menu */ class MainMenuState : public State { private: public: MainMenuState(); virtual ~MainMenuState(); virtual void step(); virtual const char* getStateName() const; }; /*! \class CreditsState \brief State for credits screen */ class CreditsState : public State { public: CreditsState(); virtual void step(); virtual const char* getStateName() const; private: float mYPosition; }; blobby-1.0rc3/src/state/ReplaySelectionState.h0000644000175000017500000000306512042452373022734 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "State.h" #include #include class DuelMatch; class ReplayPlayer; class IReplayLoader; /*! \class ReplaySelectionState \brief State for replay selection screen */ class ReplaySelectionState : public State { public: ReplaySelectionState(); virtual void step(); virtual const char* getStateName() const; private: std::vector mReplayFiles; int mSelectedReplay; bool mShowReplayInfo; boost::scoped_ptr mReplayLoader; bool mChecksumError; bool mVersionError; int mPlayButton; int mCancelButton; int mDeleteButton; }; blobby-1.0rc3/src/state/LocalGameState.h0000644000175000017500000000266412042452373021462 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2008 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Player.h" #include "State.h" #include /*! \class LocalGameState \brief state for singleplayer game */ class LocalGameState : public State { private: Player mLeftPlayer; Player mRightPlayer; std::string mErrorMessage; bool mSaveReplay; bool mWinner; std::string mFilename; boost::scoped_ptr mMatch; boost::scoped_ptr mRecorder; public: LocalGameState(); virtual ~LocalGameState(); virtual void step(); virtual const char* getStateName() const; }; blobby-1.0rc3/src/state/NetworkState.cpp0000644000175000017500000005556712042452373021634 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ /* includes */ #include #include #include #include #include #include #include "raknet/RakClient.h" #include "raknet/RakServer.h" #include "raknet/PacketEnumerations.h" #include "raknet/GetTime.h" #include "NetworkState.h" #include "NetworkMessage.h" #include "NetworkGame.h" #include "RakNetPacket.h" #include "TextManager.h" #include "ReplayRecorder.h" #include "IMGUI.h" #include "SoundManager.h" #include "LocalInputSource.h" #include "UserConfig.h" #include "FileExceptions.h" #include "GenericIO.h" #include "FileWrite.h" /* implementation */ NetworkGameState::NetworkGameState(const std::string& servername, Uint16 port): mLeftPlayer(LEFT_PLAYER), mRightPlayer(RIGHT_PLAYER), mClient(new RakClient()), mFakeMatch(new DuelMatch(0, 0, true, true)) { IMGUI::getSingleton().resetSelection(); mWinningPlayer = NO_PLAYER; /// \todo we need read-only access here! UserConfig config; config.loadFile("config.xml"); mOwnSide = (PlayerSide)config.getInteger("network_side"); mUseRemoteColor = config.getBool("use_remote_color"); mLocalInput.reset(new LocalInputSource(mOwnSide)); mSaveReplay = false; mWaitingForReplay = false; mErrorMessage = ""; RenderManager::getSingleton().redraw(); if (mClient->Connect(servername.c_str(), port, 0, 0, RAKNET_THREAD_SLEEP_TIME)) mNetworkState = CONNECTING; else mNetworkState = CONNECTION_FAILED; // game is not started until two players are connected mFakeMatch->pause(); // load/init players mLeftPlayer.loadFromConfig("left", false); mRightPlayer.loadFromConfig("right", false); if(mOwnSide == LEFT_PLAYER){ mRightPlayer.setName(""); mLocalPlayer = &mLeftPlayer; mRemotePlayer = &mRightPlayer; }else{ mLeftPlayer.setName(""); mLocalPlayer = &mRightPlayer; mRemotePlayer = &mLeftPlayer; } RenderManager::getSingleton().setScore(0, 0, false, false); RenderManager::getSingleton().setPlayernames(mLeftPlayer.getName(), mRightPlayer.getName()); mSelectedChatmessage = 0; mChatCursorPosition = 0; mChattext = ""; } NetworkGameState::~NetworkGameState() { mClient->Disconnect(50); } void NetworkGameState::step() { IMGUI& imgui = IMGUI::getSingleton(); RenderManager* rmanager = &RenderManager::getSingleton(); packet_ptr packet; while (packet = receivePacket(mClient.get())) { switch(packet->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: { RakNet::BitStream stream; stream.Write((unsigned char)ID_ENTER_GAME); stream.Write(mOwnSide); // Send playername char myname[16]; strncpy(myname, mLocalPlayer->getName().c_str(), sizeof(myname)); stream.Write(myname, sizeof(myname)); // send color settings stream.Write(mLocalPlayer->getColor().toInt()); mClient->Send(&stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0); mNetworkState = WAITING_FOR_OPPONENT; break; } case ID_PHYSIC_UPDATE: { RakNet::BitStream stream((char*)packet->data, packet->length, false); int ival; stream.IgnoreBytes(1); //ID_PHYSIC_UPDATE stream.IgnoreBytes(1); //ID_TIMESTAMP stream.Read(ival); //TODO: un-lag based on timestamp delta //printf("Physic packet received. Time: %d\n", ival); mFakeMatch->setState(&stream); break; } case ID_WIN_NOTIFICATION: { RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); //ID_WIN_NOTIFICATION stream.Read((int&)mWinningPlayer); // last point must not be added anymore, because // the score is also simulated local so it is already // right. under strange circumstances this need not // be true, but then the score is set to the correy value // by ID_BALL_RESET mNetworkState = PLAYER_WON; break; } case ID_OPPONENT_DISCONNECTED: { // In this state, a leaving opponent would not be very surprising if (mNetworkState != PLAYER_WON) mNetworkState = OPPONENT_DISCONNECTED; break; } case ID_BALL_RESET: { RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); //ID_BALL_RESET stream.Read((int&)mServingPlayer); // read and set new score int nLeftScore; int nRightScore; int time; stream.Read(nLeftScore); stream.Read(nRightScore); stream.Read(time); mFakeMatch->setScore(nLeftScore, nRightScore); mFakeMatch->setServingPlayer(mServingPlayer); // sync the clocks... normally, they should not differ mFakeMatch->getClock().setTime(time); /// \attention /// we can get a problem here: /// assume the packet informing about the game event which lead to this /// either BALL_GROUND_COLLISION or BALL_PLAYER_COLLISION got stalled /// and arrives at the same time time as this packet. Then we get the following behaviour: /// we set the score to the right value... the event causing the score to happen gets processed /// -> that player scores -> score is off! /// /// i don't have a clean fix for this right now, so we'll have to live with a workaround for now /// we just order the game to reset all triggered events. mFakeMatch->resetTriggeredEvents(); /// \todo a good fix would involve ensuring we process all events in the right order break; } case ID_BALL_GROUND_COLLISION: { int side; RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); //ID_BALL_GROUND_COLLISION stream.Read(side); switch((PlayerSide)side){ case LEFT_PLAYER: mFakeMatch->trigger(DuelMatch::EVENT_BALL_HIT_LEFT_GROUND); break; case RIGHT_PLAYER: mFakeMatch->trigger(DuelMatch::EVENT_BALL_HIT_RIGHT_GROUND); break; default: assert(0); } break; } case ID_BALL_PLAYER_COLLISION: { float intensity; int side; RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); //ID_PLAYER_BALL_COLLISION // FIXME ensure that this intensity is used stream.Read(intensity); stream.Read(side); switch((PlayerSide)side){ case LEFT_PLAYER: mFakeMatch->trigger(DuelMatch::EVENT_LEFT_BLOBBY_HIT); break; case RIGHT_PLAYER: mFakeMatch->trigger(DuelMatch::EVENT_RIGHT_BLOBBY_HIT); break; default: assert(0); } break; } case ID_PAUSE: if (mNetworkState == PLAYING) { mNetworkState = PAUSING; mFakeMatch->pause(); } break; case ID_UNPAUSE: if (mNetworkState == PAUSING) { mNetworkState = PLAYING; mFakeMatch->unpause(); } break; case ID_GAME_READY: { char charName[16]; RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); // ignore ID_GAME_READY // read gamespeed int speed; stream.Read(speed); SpeedController::getMainInstance()->setGameSpeed(speed); // read playername stream.Read(charName, sizeof(charName)); // ensures that charName is null terminated charName[sizeof(charName)-1] = '\0'; // read colors int temp; stream.Read(temp); Color ncolor = temp; mRemotePlayer->setName(std::string(charName)); mFilename = mLocalPlayer->getName(); if(mFilename.size() > 7) mFilename.resize(7); mFilename += " vs "; std::string oppname = mRemotePlayer->getName(); if(oppname.size() > 7) oppname.resize(7); mFilename += oppname; // set names in render manager RenderManager::getSingleton().setPlayernames(mLeftPlayer.getName(), mRightPlayer.getName()); // check whether to use remote player color if(mUseRemoteColor){ mRemotePlayer->setColor(ncolor); RenderManager::getSingleton().setBlobColor(LEFT_PLAYER, mLeftPlayer.getColor()); RenderManager::getSingleton().setBlobColor(RIGHT_PLAYER, mRightPlayer.getColor()); RenderManager::getSingleton().redraw(); } // Workarround for SDL-Renderer // Hides the GUI when networkgame starts rmanager->redraw(); mNetworkState = PLAYING; // start game mFakeMatch->unpause(); // game ready whistle SoundManager::getSingleton().playSound("sounds/pfiff.wav", ROUND_START_SOUND_VOLUME); break; } case ID_CONNECTION_ATTEMPT_FAILED: mNetworkState = CONNECTION_FAILED; break; case ID_REMOTE_DISCONNECTION_NOTIFICATION: case ID_REMOTE_CONNECTION_LOST: break; case ID_DISCONNECTION_NOTIFICATION: case ID_CONNECTION_LOST: if (mNetworkState != PLAYER_WON) mNetworkState = DISCONNECTED; break; case ID_NO_FREE_INCOMING_CONNECTIONS: mNetworkState = SERVER_FULL; break; case ID_RECEIVED_STATIC_DATA: break; case ID_REMOTE_NEW_INCOMING_CONNECTION: break; case ID_REMOTE_EXISTING_CONNECTION: break; case ID_CHAT_MESSAGE: { RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); // ID_CHAT_MESSAGE // Insert Message in the log and focus the last element char message[31]; stream.Read(message, sizeof(message)); message[30] = '\0'; // Insert Message in the log and focus the last element mChatlog.push_back((std::string) message); mChatOrigin.push_back(false); mSelectedChatmessage = mChatlog.size() - 1; SoundManager::getSingleton().playSound("sounds/chat.wav", ROUND_START_SOUND_VOLUME); break; } case ID_REPLAY: { /// \todo we should take more action if server sends replay /// even if not requested! if(!mWaitingForReplay) break; boost::shared_ptr stream = boost::make_shared((char*)packet->data, packet->length, false); stream->IgnoreBytes(1); // ID_REPLAY try { boost::shared_ptr reader = createGenericReader(stream); ReplayRecorder dummyRec; dummyRec.receive( reader ); boost::shared_ptr fw = boost::make_shared((std::string("replays/") + mFilename + std::string(".bvr"))); dummyRec.save( fw ); } catch( FileLoadException& ex) { mErrorMessage = std::string("Unable to create file:" + ex.getFileName()); mSaveReplay = true; // try again } catch( FileAlreadyExistsException& ex) { mErrorMessage = std::string("File already exists!:"+ ex.getFileName()); mSaveReplay = true; } catch( std::exception& ex) { mErrorMessage = std::string("Could not save replay: "); // it is not expected to catch any exception here! save should only // create FileLoad and FileAlreadyExists exceptions mSaveReplay = true; } // mWaitingForReplay will be set to false even if replay could not be saved because // the server won't send it again. mWaitingForReplay = false; break; } default: printf("Received unknown Packet %d\n", packet->data[0]); std::cout<data<<"\n"; break; } } PlayerInput input = mNetworkState == PLAYING ? mLocalInput->getInput() : PlayerInput(); presentGame(*mFakeMatch); rmanager->setBlobColor(LEFT_PLAYER, mLeftPlayer.getColor()); rmanager->setBlobColor(RIGHT_PLAYER, mRightPlayer.getColor()); if (InputManager::getSingleton()->exit() && mNetworkState != PLAYING) { if(mNetworkState == PAUSING) { // end pause RakNet::BitStream stream; stream.Write((unsigned char)ID_UNPAUSE); mClient->Send(&stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0); } else { deleteCurrentState(); setCurrentState(new MainMenuState); } } else if (InputManager::getSingleton()->exit() && mSaveReplay) { mSaveReplay = false; IMGUI::getSingleton().resetSelection(); } else if (mErrorMessage != "") { imgui.doOverlay(GEN_ID, Vector2(100, 200), Vector2(700, 360)); size_t split = mErrorMessage.find(':'); std::string mProblem = mErrorMessage.substr(0, split); std::string mInfo = mErrorMessage.substr(split+1); imgui.doText(GEN_ID, Vector2(120, 220), mProblem); imgui.doText(GEN_ID, Vector2(120, 260), mInfo); if(imgui.doButton(GEN_ID, Vector2(330, 320), TextManager::LBL_OK)) { mErrorMessage = ""; } imgui.doCursor(); } else if (mSaveReplay) { imgui.doOverlay(GEN_ID, Vector2(150, 200), Vector2(650, 400)); imgui.doText(GEN_ID, Vector2(190, 220), TextManager::RP_SAVE_NAME); static unsigned cpos; imgui.doEditbox(GEN_ID, Vector2(180, 270), 18, mFilename, cpos); if (imgui.doButton(GEN_ID, Vector2(220, 330), TextManager::LBL_OK)) { if (mFilename != "") { // request replay from server RakNet::BitStream stream; stream.Write((unsigned char)ID_REPLAY); mClient->Send(&stream, LOW_PRIORITY, RELIABLE_ORDERED, 0); } mSaveReplay = false; mWaitingForReplay = true; imgui.resetSelection(); } if (imgui.doButton(GEN_ID, Vector2(440, 330), TextManager::LBL_CANCEL)) { mSaveReplay = false; imgui.resetSelection(); } imgui.doCursor(); } else if (mWaitingForReplay) { imgui.doOverlay(GEN_ID, Vector2(150, 200), Vector2(650, 400)); imgui.doText(GEN_ID, Vector2(190, 220), TextManager::RP_WAIT_REPLAY); if (imgui.doButton(GEN_ID, Vector2(440, 330), TextManager::LBL_CANCEL)) { mSaveReplay = false; mWaitingForReplay = false; imgui.resetSelection(); } imgui.doCursor(); } else switch (mNetworkState) { case CONNECTING: { imgui.doOverlay(GEN_ID, Vector2(100.0, 210.0), Vector2(700.0, 310.0)); imgui.doText(GEN_ID, Vector2(150.0, 250.0), TextManager::NET_CONNECTING); break; } case WAITING_FOR_OPPONENT: { imgui.doOverlay(GEN_ID, Vector2(100.0, 210.0), Vector2(700.0, 310.0)); imgui.doText(GEN_ID, Vector2(150.0, 250.0), TextManager::GAME_WAITING); break; } case OPPONENT_DISCONNECTED: { imgui.doCursor(); imgui.doOverlay(GEN_ID, Vector2(100.0, 210.0), Vector2(700.0, 370.0)); imgui.doText(GEN_ID, Vector2(140.0, 250.0), TextManager::GAME_OPP_LEFT); if (imgui.doButton(GEN_ID, Vector2(230.0, 300.0), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState); } if (imgui.doButton(GEN_ID, Vector2(350.0, 300.0), TextManager::RP_SAVE)) { mSaveReplay = true; imgui.resetSelection(); } break; } case DISCONNECTED: { imgui.doCursor(); imgui.doOverlay(GEN_ID, Vector2(100.0, 210.0), Vector2(700.0, 370.0)); imgui.doText(GEN_ID, Vector2(120.0, 250.0), TextManager::NET_DISCONNECT); if (imgui.doButton(GEN_ID, Vector2(230.0, 320.0), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState); } if (imgui.doButton(GEN_ID, Vector2(350.0, 320.0), TextManager::RP_SAVE)) { mSaveReplay = true; imgui.resetSelection(); } break; } case CONNECTION_FAILED: { imgui.doCursor(); imgui.doOverlay(GEN_ID, Vector2(100.0, 210.0), Vector2(700.0, 370.0)); imgui.doText(GEN_ID, Vector2(200.0, 250.0), TextManager::NET_CON_FAILED); if (imgui.doButton(GEN_ID, Vector2(350.0, 300.0), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState); } break; } case SERVER_FULL: { imgui.doCursor(); imgui.doOverlay(GEN_ID, Vector2(100.0, 210.0), Vector2(700.0, 370.0)); imgui.doText(GEN_ID, Vector2(200.0, 250.0), TextManager::NET_SERVER_FULL); if (imgui.doButton(GEN_ID, Vector2(350.0, 300.0), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState); } break; } case PLAYING: { mFakeMatch->step(); if (InputManager::getSingleton()->exit()) { RakNet::BitStream stream; stream.Write((unsigned char)ID_PAUSE); mClient->Send(&stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0); } RakNet::BitStream stream; stream.Write((unsigned char)ID_INPUT_UPDATE); stream.Write((unsigned char)ID_TIMESTAMP); ///! \todo do we really need this time stamps? stream.Write(RakNet::GetTime()); stream.Write(input.left); stream.Write(input.right); stream.Write(input.up); mClient->Send(&stream, HIGH_PRIORITY, UNRELIABLE_SEQUENCED, 0); break; } case PLAYER_WON: { std::string tmp; if(mWinningPlayer==LEFT_PLAYER) tmp = mLeftPlayer.getName(); else tmp = mRightPlayer.getName(); imgui.doOverlay(GEN_ID, Vector2(200, 150), Vector2(700, 450)); imgui.doImage(GEN_ID, Vector2(200, 250), "gfx/pokal.bmp"); imgui.doText(GEN_ID, Vector2(274, 240), tmp); imgui.doText(GEN_ID, Vector2(274, 300), TextManager::GAME_WIN); if (imgui.doButton(GEN_ID, Vector2(290, 360), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new MainMenuState()); } if (imgui.doButton(GEN_ID, Vector2(380, 360), TextManager::RP_SAVE)) { mSaveReplay = true; imgui.resetSelection(); } imgui.doCursor(); break; } case PAUSING: { imgui.doOverlay(GEN_ID, Vector2(175, 20), Vector2(625, 175)); imgui.doText(GEN_ID, Vector2(275, 35), TextManager::GAME_PAUSED); if (imgui.doButton(GEN_ID, Vector2(205, 95), TextManager::LBL_CONTINUE)) { RakNet::BitStream stream; stream.Write((unsigned char)ID_UNPAUSE); mClient->Send(&stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0); } // Chat imgui.doChatbox(GEN_ID, Vector2(10, 190), Vector2(790, 450), mChatlog, mSelectedChatmessage, mChatOrigin); if (imgui.doEditbox(GEN_ID, Vector2(30, 460), 30, mChattext, mChatCursorPosition, 0, true)) { // GUI-Hack, so that we can send messages if ((InputManager::getSingleton()->getLastActionKey() == "return") && (mChattext != "")) { RakNet::BitStream stream; char message[31]; strncpy(message, mChattext.c_str(), sizeof(message)); stream.Write((unsigned char)ID_CHAT_MESSAGE); stream.Write(message, sizeof(message)); mClient->Send(&stream, LOW_PRIORITY, RELIABLE_ORDERED, 0); mChatlog.push_back(mChattext); mChatOrigin.push_back(true); mSelectedChatmessage = mChatlog.size() - 1; mChattext = ""; mChatCursorPosition = 0; SoundManager::getSingleton().playSound("sounds/chat.wav", ROUND_START_SOUND_VOLUME); } } if (imgui.doButton(GEN_ID, Vector2(500, 95), TextManager::GAME_QUIT)) { deleteCurrentState(); setCurrentState(new MainMenuState); } if (imgui.doButton(GEN_ID, Vector2(285, 125), TextManager::RP_SAVE)) { mSaveReplay = true; imgui.resetSelection(); } imgui.doCursor(); } } } const char* NetworkGameState::getStateName() const { return "NetworkGameState"; } NetworkHostState::NetworkHostState() { mServer = new RakServer; mServer->Start(2, 0, BLOBBY_PORT); mNetworkGame = 0; mGameState = new NetworkGameState("localhost", BLOBBY_PORT); mLocalPlayerSide = NO_PLAYER; } NetworkHostState::~NetworkHostState() { delete mGameState; delete mNetworkGame; mServer->Disconnect(1); delete mServer; } void NetworkHostState::step() { packet_ptr packet; while (packet = receivePacket(mServer)) { switch (packet->data[0]) { case ID_DISCONNECTION_NOTIFICATION: case ID_CONNECTION_LOST: case ID_INPUT_UPDATE: case ID_CHAT_MESSAGE: case ID_PAUSE: case ID_UNPAUSE: case ID_REPLAY: { if (packet->playerId == mLocalPlayer || packet->playerId == mRemotePlayer) { if (mNetworkGame) mNetworkGame->injectPacket(packet); } break; } case ID_BLOBBY_SERVER_PRESENT: { ServerInfo myinfo(mLocalPlayerSide == NO_PLAYER ? "somebody" : mLocalPlayerName.c_str()); myinfo.activegames = mNetworkGame ? 1 : 0; if (mLocalPlayerSide == NO_PLAYER || mNetworkGame) { strncpy(myinfo.waitingplayer, "none", sizeof(myinfo.waitingplayer) - 1); } else { strncpy(myinfo.waitingplayer, mLocalPlayerName.c_str(), sizeof(myinfo.waitingplayer) - 1); } RakNet::BitStream stream; stream.Write((unsigned char)ID_BLOBBY_SERVER_PRESENT); myinfo.writeToBitstream(stream); mServer->Send(&stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->playerId, false); break; } case ID_ENTER_GAME: { int ival; RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); //ID_ENTER_GAME // read playername and side stream.Read(ival); char charName[16]; stream.Read(charName, sizeof(charName)); // read colour data int color; stream.Read(color); // ensures that charName is null terminated charName[sizeof(charName)-1] = '\0'; std::string playerName(charName); PlayerSide newSide = (PlayerSide)ival; if (mLocalPlayerSide == NO_PLAYER) { // First player is probably the local one mLocalPlayerSide = newSide; mLocalPlayer = packet->playerId; mLocalPlayerName = playerName; // set the color switch(newSide){ case LEFT_PLAYER: mLeftColor = color; break; case RIGHT_PLAYER: mRightColor = color; break; } } else { mRemotePlayer = packet->playerId; mRemotePlayerName = playerName; PlayerID leftPlayer; PlayerID rightPlayer; std::string leftPlayerName; std::string rightPlayerName; if (LEFT_PLAYER == mLocalPlayerSide) { leftPlayer = mLocalPlayer; rightPlayer = packet->playerId; leftPlayerName = mLocalPlayerName; rightPlayerName = playerName; // set other color mRightColor = color; } else { leftPlayer = packet->playerId; rightPlayer = mLocalPlayer; leftPlayerName = playerName; rightPlayerName = mLocalPlayerName; // set other color mLeftColor = color; } PlayerSide switchSide = NO_PLAYER; if (newSide == mLocalPlayerSide) { if (newSide == LEFT_PLAYER) switchSide = RIGHT_PLAYER; if (newSide == RIGHT_PLAYER) switchSide = LEFT_PLAYER; } mNetworkGame = new NetworkGame( *mServer, leftPlayer, rightPlayer, leftPlayerName, rightPlayerName, mLeftColor, mRightColor, switchSide); } } } } mGameState->step(); if (dynamic_cast(getCurrentState()) != 0) { if (mNetworkGame) { mNetworkGame->step(); } } } const char* NetworkHostState::getStateName() const { return "NetworkHostState"; } blobby-1.0rc3/src/state/OptionsState.cpp0000644000175000017500000010605412042452373021622 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "OptionsState.h" /* includes */ #include #include #include "State.h" #include "RenderManager.h" #include "LocalInputSource.h" #include "SpeedController.h" #include "SoundManager.h" #include "Blood.h" #include "IMGUI.h" #include "TextManager.h" #include "FileSystem.h" /* implementation */ OptionState::OptionState() { IMGUI::getSingleton().resetSelection(); mOptionConfig.loadFile("config.xml"); mPlayerOptions[LEFT_PLAYER] = 0; mPlayerOptions[RIGHT_PLAYER] = 0; std::string leftScript = mOptionConfig.getString("left_script_name"); std::string rightScript = mOptionConfig.getString("right_script_name"); mScriptNames = FileSystem::getSingleton().enumerateFiles("scripts", ".lua"); // hack. we cant use something like push_front, though mScriptNames.push_back("Human"); std::swap(mScriptNames[0], mScriptNames[mScriptNames.size() - 1]); for(int i = 0; i < mScriptNames.size(); ++i) { if (mScriptNames[i] == leftScript) mPlayerOptions[LEFT_PLAYER] = i; if (mScriptNames[i] == rightScript) mPlayerOptions[RIGHT_PLAYER] = i; } if (mOptionConfig.getBool("left_player_human")) mPlayerOptions[LEFT_PLAYER] = 0; if (mOptionConfig.getBool("right_player_human")) mPlayerOptions[RIGHT_PLAYER] = 0; mPlayerName[LEFT_PLAYER] = mOptionConfig.getString("left_player_name"); mPlayerName[RIGHT_PLAYER] = mOptionConfig.getString("right_player_name"); mPlayerNamePosition[RIGHT_PLAYER] = 0; mPlayerNamePosition[LEFT_PLAYER] = 0; mBotStrength[LEFT_PLAYER] = mOptionConfig.getInteger("left_script_strength"); mBotStrength[RIGHT_PLAYER] = mOptionConfig.getInteger("right_script_strength"); } OptionState::~OptionState() { } void OptionState::save() { if (mPlayerOptions[LEFT_PLAYER] == 0) { mOptionConfig.setBool("left_player_human", true); } else { mOptionConfig.setBool("left_player_human", false); mOptionConfig.setString("left_script_name", mScriptNames[mPlayerOptions[LEFT_PLAYER]]); } if (mPlayerOptions[RIGHT_PLAYER] == 0) { mOptionConfig.setBool("right_player_human", true); } else { mOptionConfig.setBool("right_player_human", false); mOptionConfig.setString("right_script_name", mScriptNames[mPlayerOptions[RIGHT_PLAYER]]); } mOptionConfig.setString("left_player_name", mPlayerName[LEFT_PLAYER]); mOptionConfig.setString("right_player_name", mPlayerName[RIGHT_PLAYER]); mOptionConfig.setInteger("left_script_strength", mBotStrength[LEFT_PLAYER]); mOptionConfig.setInteger("right_script_strength", mBotStrength[RIGHT_PLAYER]); mOptionConfig.saveFile("config.xml"); } void OptionState::step() { const int MAX_BOT_DELAY = 25; // 25 frames = 0.33s (gamespeed: normal) IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); imgui.doEditbox(GEN_ID, Vector2(5.0, 10.0), 15, mPlayerName[LEFT_PLAYER], mPlayerNamePosition[LEFT_PLAYER]); imgui.doEditbox(GEN_ID, Vector2(425.0, 10.0), 15, mPlayerName[RIGHT_PLAYER], mPlayerNamePosition[RIGHT_PLAYER]); imgui.doSelectbox(GEN_ID, Vector2(5.0, 50.0), Vector2(375.0, 300.0), mScriptNames, mPlayerOptions[LEFT_PLAYER]); imgui.doSelectbox(GEN_ID, Vector2(425.0, 50.0), Vector2(795.0, 300.0), mScriptNames, mPlayerOptions[RIGHT_PLAYER]); imgui.doText(GEN_ID, Vector2(270.0, 310.0), TextManager::OP_DIFFICULTY ); float f = 1.f - (float)mBotStrength[0] / MAX_BOT_DELAY; imgui.doScrollbar(GEN_ID, Vector2(15.0, 350.0), f); mBotStrength[0] = static_cast ((1.f-f) * MAX_BOT_DELAY + 0.5f); imgui.doText(GEN_ID, Vector2(235.0, 350.0), f > 0.66 ? TextManager::OP_STRONG : (f > 0.33 ? TextManager::OP_MEDIUM: TextManager::OP_WEAK)); f = 1.f - (float)mBotStrength[1] / MAX_BOT_DELAY; imgui.doScrollbar(GEN_ID, Vector2(440.0, 350.0), f); mBotStrength[1] = static_cast ((1.f - f) * MAX_BOT_DELAY + 0.5f); imgui.doText(GEN_ID, Vector2(660.0, 350.0), f > 0.66 ? TextManager::OP_STRONG : (f > 0.33 ? TextManager::OP_MEDIUM: TextManager::OP_WEAK)); if (imgui.doButton(GEN_ID, Vector2(40.0, 390.0), TextManager::OP_INPUT_OP)) { save(); deleteCurrentState(); setCurrentState(new InputOptionsState()); } if (imgui.doButton(GEN_ID, Vector2(40.0, 430.0), TextManager::OP_GFX_OP)) { save(); deleteCurrentState(); setCurrentState(new GraphicOptionsState()); } if (imgui.doButton(GEN_ID, Vector2(40.0, 470.0), TextManager::OP_MISC)) { save(); deleteCurrentState(); setCurrentState(new MiscOptionsState()); } if (imgui.doButton(GEN_ID, Vector2(224.0, 530.0), TextManager::LBL_OK)) { save(); deleteCurrentState(); setCurrentState(new MainMenuState()); } if (imgui.doButton(GEN_ID, Vector2(424.0, 530.0), TextManager::LBL_CANCEL)) { deleteCurrentState(); setCurrentState(new MainMenuState()); } } const char* OptionState::getStateName() const { return "OptionState"; } GraphicOptionsState::GraphicOptionsState() { IMGUI::getSingleton().resetSelection(); mOptionConfig.loadFile("config.xml"); mFullscreen = mOptionConfig.getBool("fullscreen"); mRenderer = mOptionConfig.getString("device"); mR1 = mOptionConfig.getInteger("left_blobby_color_r"); mG1 = mOptionConfig.getInteger("left_blobby_color_g"); mB1 = mOptionConfig.getInteger("left_blobby_color_b"); mR2 = mOptionConfig.getInteger("right_blobby_color_r"); mG2 = mOptionConfig.getInteger("right_blobby_color_g"); mB2 = mOptionConfig.getInteger("right_blobby_color_b"); mLeftMorphing = mOptionConfig.getBool("left_blobby_oscillate"); mRightMorphing = mOptionConfig.getBool("right_blobby_oscillate"); mShowShadow = mOptionConfig.getBool("show_shadow"); } GraphicOptionsState::~GraphicOptionsState() { } void GraphicOptionsState::save() { if ((mOptionConfig.getBool("fullscreen") != mFullscreen) || (mOptionConfig.getString("device") != mRenderer)) { mOptionConfig.setBool("fullscreen", mFullscreen); mOptionConfig.setString("device", mRenderer); if (mRenderer == "OpenGL") RenderManager::createRenderManagerGL2D()->init(800, 600, mFullscreen); else RenderManager::createRenderManagerSDL()->init(800, 600, mFullscreen); RenderManager::getSingleton().setBackground( std::string("backgrounds/") + mOptionConfig.getString("background")); } if(mOptionConfig.getBool("show_shadow") != mShowShadow) { RenderManager::getSingleton().showShadow(mShowShadow); mOptionConfig.setBool("show_shadow", mShowShadow); } mOptionConfig.setInteger("left_blobby_color_r", mR1); mOptionConfig.setInteger("left_blobby_color_g", mG1); mOptionConfig.setInteger("left_blobby_color_b", mB1); mOptionConfig.setInteger("right_blobby_color_r", mR2); mOptionConfig.setInteger("right_blobby_color_g", mG2); mOptionConfig.setInteger("right_blobby_color_b", mB2); mOptionConfig.setBool("left_blobby_oscillate", mLeftMorphing); mOptionConfig.setBool("right_blobby_oscillate", mRightMorphing); mOptionConfig.saveFile("config.xml"); } void GraphicOptionsState::step() { IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); imgui.doText(GEN_ID, Vector2(34.0, 10.0), TextManager::OP_VIDEO); if (imgui.doButton(GEN_ID, Vector2(34.0, 40.0), TextManager::OP_FULLSCREEN)) mFullscreen = true; if (imgui.doButton(GEN_ID, Vector2(34.0, 70.0), TextManager::OP_WINDOW)) mFullscreen = false; if (mFullscreen) imgui.doImage(GEN_ID, Vector2(18.0, 52.0), "gfx/pfeil_rechts.bmp"); else imgui.doImage(GEN_ID, Vector2(18.0, 82.0), "gfx/pfeil_rechts.bmp"); imgui.doText(GEN_ID, Vector2(444.0, 10.0), TextManager::OP_RENDER_DEVICE); if (imgui.doButton(GEN_ID, Vector2(444.0, 40.0), "OpenGL")) mRenderer = "OpenGL"; if (imgui.doButton(GEN_ID, Vector2(444.0, 70.0), "SDL")) mRenderer = "SDL"; if (mRenderer == "OpenGL") imgui.doImage(GEN_ID, Vector2(428.0, 52.0), "gfx/pfeil_rechts.bmp"); else imgui.doImage(GEN_ID, Vector2(428.0, 82.0), "gfx/pfeil_rechts.bmp"); imgui.doText(GEN_ID, Vector2(34.0, 110.0), TextManager::OP_SHOW_SHADOW); if (imgui.doButton(GEN_ID, Vector2(72.0, 140), TextManager::LBL_YES)) mShowShadow = true; if (imgui.doButton(GEN_ID, Vector2(220.0, 140), TextManager::LBL_NO)) mShowShadow = false; if (mShowShadow) imgui.doImage(GEN_ID, Vector2(54.0, 152.0), "gfx/pfeil_rechts.bmp"); else imgui.doImage(GEN_ID, Vector2(204.0, 152.0), "gfx/pfeil_rechts.bmp"); //Blob colors: imgui.doText(GEN_ID, Vector2(280.0, 190.0), TextManager::OP_BLOB_COLORS); //left blob: imgui.doText(GEN_ID, Vector2(34.0, 230.0), TextManager::OP_LEFT_PLAYER); { imgui.doText(GEN_ID, Vector2(34.0, 260), TextManager::OP_RED); float r1 = (float)mR1/255; imgui.doScrollbar(GEN_ID, Vector2(160.0, 260.0), r1); mR1 = (int)(r1*255); } { imgui.doText(GEN_ID, Vector2(34.0, 290), TextManager::OP_GREEN); float g1 = (float)mG1/255; imgui.doScrollbar(GEN_ID, Vector2(160.0, 290.0), g1); mG1 = (int)(g1*255); } { imgui.doText(GEN_ID, Vector2(34.0, 320), TextManager::OP_BLUE); float b1 = (float)mB1/255; imgui.doScrollbar(GEN_ID, Vector2(160.0, 320), b1); mB1 = (int)(b1*255); } imgui.doText(GEN_ID, Vector2(34.0, 360), TextManager::OP_MORPHING); if (imgui.doButton(GEN_ID, Vector2(72.0, 390), TextManager::LBL_YES)) mLeftMorphing = true; if (imgui.doButton(GEN_ID, Vector2(220.0, 390), TextManager::LBL_NO)) mLeftMorphing = false; if (mLeftMorphing) imgui.doImage(GEN_ID, Vector2(54.0, 402.0), "gfx/pfeil_rechts.bmp"); else imgui.doImage(GEN_ID, Vector2(204.0, 402.0), "gfx/pfeil_rechts.bmp"); //draw left blob: { float time = float(SDL_GetTicks()) / 1000.0; Color ourCol = Color(mR1, mG1, mB1); if (mLeftMorphing) ourCol = Color(int((sin(time*2) + 1.0) * 128), int((sin(time*4) + 1.0) * 128), int((sin(time*3) + 1.0) * 128)); imgui.doBlob(GEN_ID, Vector2(110, 500), ourCol); } //right blob: imgui.doText(GEN_ID, Vector2(434.0, 230.0), TextManager::OP_RIGHT_PLAYER); { imgui.doText(GEN_ID, Vector2(434.0, 260), TextManager::OP_RED); float r2 = (float)mR2/255; imgui.doScrollbar(GEN_ID, Vector2(560.0, 260.0), r2); mR2 = (int)(r2*255); } { imgui.doText(GEN_ID, Vector2(434.0, 290), TextManager::OP_GREEN); float g2 = (float)mG2/255; imgui.doScrollbar(GEN_ID, Vector2(560.0, 290.0), g2); mG2 = (int)(g2*255); } { imgui.doText(GEN_ID, Vector2(434.0, 320), TextManager::OP_BLUE); float b2 = (float)mB2/255; imgui.doScrollbar(GEN_ID, Vector2(560.0, 320), b2); mB2 = (int)(b2*255); } imgui.doText(GEN_ID, Vector2(434.0, 360), TextManager::OP_MORPHING); if (imgui.doButton(GEN_ID, Vector2(472.0, 390), TextManager::LBL_YES)) mRightMorphing = true; if (imgui.doButton(GEN_ID, Vector2(620.0, 390), TextManager::LBL_NO)) mRightMorphing = false; if (mRightMorphing) imgui.doImage(GEN_ID, Vector2(454.0, 402.0), "gfx/pfeil_rechts.bmp"); else imgui.doImage(GEN_ID, Vector2(604.0, 402.0), "gfx/pfeil_rechts.bmp"); //draw right blob: { float time = float(SDL_GetTicks()) / 1000.0; Color ourCol = Color(mR2, mG2, mB2); if (mRightMorphing) ourCol = Color(int((cos(time*2) + 1.0) * 128), int((cos(time*4) + 1.0) * 128), int((cos(time*3) + 1.0) * 128)); imgui.doBlob(GEN_ID, Vector2(670, 500), ourCol); } if (imgui.doButton(GEN_ID, Vector2(224.0, 530.0), TextManager::LBL_OK)) { save(); deleteCurrentState(); setCurrentState(new OptionState()); } if (imgui.doButton(GEN_ID, Vector2(424.0, 530.0), TextManager::LBL_CANCEL)) { deleteCurrentState(); setCurrentState(new OptionState()); } } const char* GraphicOptionsState::getStateName() const { return "GraphicOptionsState"; } InputOptionsState::InputOptionsState() { IMGUI::getSingleton().resetSelection(); mSetKeyboard = 0; mOptionConfig.loadFile("inputconfig.xml"); //left data: mLeftBlobbyDevice = mOptionConfig.getString("left_blobby_device"); mLeftBlobbyMouseJumpbutton = mOptionConfig.getInteger("left_blobby_mouse_jumpbutton"); mLeftBlobbyKeyboardLeft = mOptionConfig.getString("left_blobby_keyboard_left"); mLeftBlobbyKeyboardRight = mOptionConfig.getString("left_blobby_keyboard_right"); mLeftBlobbyKeyboardJump = mOptionConfig.getString("left_blobby_keyboard_jump"); mLeftBlobbyJoystickLeft = mOptionConfig.getString("left_blobby_joystick_left"); mLeftBlobbyJoystickRight = mOptionConfig.getString("left_blobby_joystick_right"); mLeftBlobbyJoystickJump = mOptionConfig.getString("left_blobby_joystick_jump"); //right data: mRightBlobbyDevice = mOptionConfig.getString("right_blobby_device"); mRightBlobbyMouseJumpbutton = mOptionConfig.getInteger("right_blobby_mouse_jumpbutton"); mRightBlobbyKeyboardLeft = mOptionConfig.getString("right_blobby_keyboard_left"); mRightBlobbyKeyboardRight = mOptionConfig.getString("right_blobby_keyboard_right"); mRightBlobbyKeyboardJump = mOptionConfig.getString("right_blobby_keyboard_jump"); mRightBlobbyJoystickLeft = mOptionConfig.getString("right_blobby_joystick_left"); mRightBlobbyJoystickRight = mOptionConfig.getString("right_blobby_joystick_right"); mRightBlobbyJoystickJump = mOptionConfig.getString("right_blobby_joystick_jump"); } InputOptionsState::~InputOptionsState() { } void InputOptionsState::save() { //left data: mOptionConfig.setString("left_blobby_device", mLeftBlobbyDevice); mOptionConfig.setInteger("left_blobby_mouse_jumpbutton", mLeftBlobbyMouseJumpbutton); mOptionConfig.setString("left_blobby_keyboard_left", mLeftBlobbyKeyboardLeft); mOptionConfig.setString("left_blobby_keyboard_right", mLeftBlobbyKeyboardRight); mOptionConfig.setString("left_blobby_keyboard_jump", mLeftBlobbyKeyboardJump); mOptionConfig.setString("left_blobby_joystick_left", mLeftBlobbyJoystickLeft); mOptionConfig.setString("left_blobby_joystick_right", mLeftBlobbyJoystickRight); mOptionConfig.setString("left_blobby_joystick_jump", mLeftBlobbyJoystickJump); //right data: mOptionConfig.setString("right_blobby_device", mRightBlobbyDevice); mOptionConfig.setInteger("right_blobby_mouse_jumpbutton", mRightBlobbyMouseJumpbutton); mOptionConfig.setString("right_blobby_keyboard_left", mRightBlobbyKeyboardLeft); mOptionConfig.setString("right_blobby_keyboard_right", mRightBlobbyKeyboardRight); mOptionConfig.setString("right_blobby_keyboard_jump", mRightBlobbyKeyboardJump); mOptionConfig.setString("right_blobby_joystick_left", mRightBlobbyJoystickLeft); mOptionConfig.setString("right_blobby_joystick_right", mRightBlobbyJoystickRight); mOptionConfig.setString("right_blobby_joystick_jump", mRightBlobbyJoystickJump); mOptionConfig.saveFile("inputconfig.xml"); } void InputOptionsState::step() { IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); std::string lastActionKey = InputManager::getSingleton()->getLastActionKey(); // left player side: imgui.doText(GEN_ID, Vector2(34.0, 10.0), TextManager::OP_LEFT_PLAYER); // dirty hack for languagesupport TextManager::STRING deviceLanguage = TextManager::OP_JOYSTICK; if (mLeftBlobbyDevice[0] == 'k') deviceLanguage = TextManager::OP_KEYBOARD; else if (mLeftBlobbyDevice[0] == 'm') deviceLanguage = TextManager::OP_MOUSE; if (imgui.doButton(GEN_ID, Vector2(80.0, 60.0), deviceLanguage)) { if (mLeftBlobbyDevice == "mouse") { mLeftBlobbyDevice = "keyboard"; } else if (mLeftBlobbyDevice == "keyboard") { mLeftBlobbyDevice = "joystick"; } else if (mLeftBlobbyDevice == "joystick") { if (mRightBlobbyDevice != "mouse") { mLeftBlobbyDevice = "mouse"; } else { mLeftBlobbyDevice = "keyboard"; } } } //if mouse device is selected: if (mLeftBlobbyDevice == "mouse") { imgui.doText(GEN_ID, Vector2(34.0, 120.0), TextManager::OP_JUMP_BUTTON); std::ostringstream text; if (mLeftBlobbyMouseJumpbutton >= 0) text << "Button " << mLeftBlobbyMouseJumpbutton; else text << "Button "; if (imgui.doButton(GEN_ID, Vector2(50, 150.0), text.str())) { mOldInteger = mLeftBlobbyMouseJumpbutton; mLeftBlobbyMouseJumpbutton = -2; } } if ((mLeftBlobbyMouseJumpbutton == -2) && (InputManager::getSingleton()->getLastMouseButton() == -1)) mLeftBlobbyMouseJumpbutton = -1; //if keyboard device is selected: if (mLeftBlobbyDevice == "keyboard") { if (imgui.doButton(GEN_ID, Vector2(34, 350.0), TextManager::OP_SET_ALL)) mSetKeyboard = 1; imgui.doText(GEN_ID, Vector2(34.0, 120.0), TextManager::OP_LEFT_KEY); if (imgui.doButton(GEN_ID, Vector2(50, 150.0), std::string("Key ")+mLeftBlobbyKeyboardLeft) || mSetKeyboard == 1) { lastActionKey = ""; oldString = mLeftBlobbyKeyboardLeft; mLeftBlobbyKeyboardLeft = ""; } if (mSetKeyboard == 1) mSetKeyboard = 2; if (mSetKeyboard == 2 && mLeftBlobbyKeyboardLeft != "") mSetKeyboard = 3; imgui.doText(GEN_ID, Vector2(34.0, 190.0), TextManager::OP_RIGHT_KEY); if (imgui.doButton(GEN_ID, Vector2(50, 220.0), std::string("Key ")+mLeftBlobbyKeyboardRight) || mSetKeyboard == 3) { lastActionKey = ""; oldString = mLeftBlobbyKeyboardRight; mLeftBlobbyKeyboardRight = ""; } if (mSetKeyboard == 3) mSetKeyboard = 4; if (mSetKeyboard == 4 && mLeftBlobbyKeyboardRight != "") mSetKeyboard = 5; imgui.doText(GEN_ID, Vector2(34.0, 260.0), TextManager::OP_JUMP_KEY ); if (imgui.doButton(GEN_ID, Vector2(50, 290.0), std::string("Key ")+mLeftBlobbyKeyboardJump) || mSetKeyboard == 5) { lastActionKey = ""; oldString = mLeftBlobbyKeyboardJump; mLeftBlobbyKeyboardJump = ""; } if (mSetKeyboard == 5) mSetKeyboard = 6; if (mSetKeyboard == 6 && mLeftBlobbyKeyboardJump != "") mSetKeyboard = 0; } //if joystick device is selected: if (mLeftBlobbyDevice == "joystick") { imgui.doText(GEN_ID, Vector2(34.0, 120.0), TextManager::OP_LEFT_BUTTON); if (imgui.doButton(GEN_ID, Vector2(50, 150.0), mLeftBlobbyJoystickLeft)) { oldString = mLeftBlobbyJoystickLeft; mLeftBlobbyJoystickLeft = ""; } imgui.doText(GEN_ID, Vector2(34.0, 190.0), TextManager::OP_RIGHT_BUTTON); if (imgui.doButton(GEN_ID, Vector2(50, 220.0), mLeftBlobbyJoystickRight)) { oldString = mLeftBlobbyJoystickRight; mLeftBlobbyJoystickRight = ""; } imgui.doText(GEN_ID, Vector2(34.0, 260.0), TextManager::OP_JUMP_BUTTON); if (imgui.doButton(GEN_ID, Vector2(50, 290.0), mLeftBlobbyJoystickJump)) { oldString = mLeftBlobbyJoystickJump; mLeftBlobbyJoystickJump = ""; } } //right player side: imgui.doText(GEN_ID, Vector2(434.0, 10.0), TextManager::OP_RIGHT_PLAYER); // dirty hack for languagesupport deviceLanguage = TextManager::OP_JOYSTICK; if (mRightBlobbyDevice[0] == 'k') deviceLanguage = TextManager::OP_KEYBOARD; else if (mRightBlobbyDevice[0] == 'm') deviceLanguage = TextManager::OP_MOUSE; if (imgui.doButton(GEN_ID, Vector2(480.0, 60.0), deviceLanguage)) { if (mRightBlobbyDevice == "mouse") { mRightBlobbyDevice = "keyboard"; } else if (mRightBlobbyDevice == "keyboard") { mRightBlobbyDevice = "joystick"; } else if (mRightBlobbyDevice == "joystick") { if (mLeftBlobbyDevice != "mouse") { mRightBlobbyDevice = "mouse"; } else { mRightBlobbyDevice = "keyboard"; } } } //if mouse device is selected: if (mRightBlobbyDevice == "mouse") { imgui.doText(GEN_ID, Vector2(434.0, 120.0), TextManager::OP_JUMP_BUTTON); std::ostringstream text; if (mRightBlobbyMouseJumpbutton >= 0) text << "Button " << mRightBlobbyMouseJumpbutton; else text << "Button "; if (imgui.doButton(GEN_ID, Vector2(450, 150.0), text.str())) { mOldInteger = mRightBlobbyMouseJumpbutton; mRightBlobbyMouseJumpbutton = -2; } } if ((mRightBlobbyMouseJumpbutton == -2) && (InputManager::getSingleton()->getLastMouseButton() == -1)) mRightBlobbyMouseJumpbutton = -1; //if keyboard device is selected: if (mRightBlobbyDevice == "keyboard") { if (imgui.doButton(GEN_ID, Vector2(434.0, 350.0), TextManager::OP_SET_ALL)) mSetKeyboard = 11; imgui.doText(GEN_ID, Vector2(434.0, 120.0), TextManager::OP_LEFT_KEY); if (imgui.doButton(GEN_ID, Vector2(450, 150.0), std::string("Key ")+mRightBlobbyKeyboardLeft) || mSetKeyboard == 11) { lastActionKey = ""; oldString = mRightBlobbyKeyboardLeft; mRightBlobbyKeyboardLeft = ""; } if (mSetKeyboard == 11) mSetKeyboard = 12; if (mSetKeyboard == 12 && mRightBlobbyKeyboardLeft != "") mSetKeyboard = 13; imgui.doText(GEN_ID, Vector2(434.0, 190.0), TextManager::OP_RIGHT_KEY); if (imgui.doButton(GEN_ID, Vector2(450, 220.0), std::string("Key ")+mRightBlobbyKeyboardRight) || mSetKeyboard == 13) { lastActionKey = ""; oldString = mRightBlobbyKeyboardRight; mRightBlobbyKeyboardRight = ""; } if (mSetKeyboard == 13) mSetKeyboard = 14; if (mSetKeyboard == 14 && mRightBlobbyKeyboardRight != "") mSetKeyboard = 15; imgui.doText(GEN_ID, Vector2(434.0, 260.0), TextManager::OP_JUMP_KEY); if (imgui.doButton(GEN_ID, Vector2(450, 290.0), std::string("Key ")+mRightBlobbyKeyboardJump) || mSetKeyboard == 15) { lastActionKey = ""; oldString = mRightBlobbyKeyboardJump; mRightBlobbyKeyboardJump = ""; } if (mSetKeyboard == 15) mSetKeyboard = 16; if (mSetKeyboard == 16 && mRightBlobbyKeyboardJump != "") mSetKeyboard = 17; } //if joystick device is selected: if (mRightBlobbyDevice == "joystick") { imgui.doText(GEN_ID, Vector2(434.0, 120.0), TextManager::OP_LEFT_BUTTON); if (imgui.doButton(GEN_ID, Vector2(450, 150.0), mRightBlobbyJoystickLeft)) { oldString = mRightBlobbyJoystickLeft; mRightBlobbyJoystickLeft = ""; } imgui.doText(GEN_ID, Vector2(434.0, 190.0), TextManager::OP_RIGHT_BUTTON); if (imgui.doButton(GEN_ID, Vector2(450, 220.0), mRightBlobbyJoystickRight)) { oldString = mRightBlobbyJoystickRight; mRightBlobbyJoystickRight = ""; } imgui.doText(GEN_ID, Vector2(434.0, 260.0), TextManager::OP_JUMP_BUTTON); if (imgui.doButton(GEN_ID, Vector2(450, 290.0), mRightBlobbyJoystickJump)) { oldString = mRightBlobbyJoystickJump; mRightBlobbyJoystickJump = ""; } } //check if a capture window is open, to set all widgets inactive: if (mLeftBlobbyKeyboardLeft != "" && mLeftBlobbyKeyboardRight != "" && mLeftBlobbyKeyboardJump != "" && mLeftBlobbyJoystickLeft != "" && mLeftBlobbyJoystickRight != "" && mLeftBlobbyJoystickJump != "" && mLeftBlobbyMouseJumpbutton != -1 && mRightBlobbyKeyboardLeft != "" && mRightBlobbyKeyboardRight != "" && mRightBlobbyKeyboardJump != "" && mRightBlobbyJoystickLeft != "" && mRightBlobbyJoystickRight != "" && mRightBlobbyJoystickJump != "" && mRightBlobbyMouseJumpbutton != -1) { imgui.doCursor(true); imgui.doInactiveMode(false); } else { imgui.doInactiveMode(true); imgui.doCursor(false); } //Capture dialogs: if (mLeftBlobbyMouseJumpbutton == -1) { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(180.0, 250.0), TextManager::OP_PRESS_MOUSE_BUTTON); imgui.doText(GEN_ID, Vector2(290.0, 300.0), TextManager::OP_JUMPING); mLeftBlobbyMouseJumpbutton = InputManager::getSingleton()->getLastMouseButton(); if (InputManager::getSingleton()->exit()) mLeftBlobbyMouseJumpbutton = mOldInteger; } if (mLeftBlobbyKeyboardLeft == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_KEY_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_LEFT); mLeftBlobbyKeyboardLeft = lastActionKey; if (InputManager::getSingleton()->exit()) mLeftBlobbyKeyboardLeft = oldString; } if (mLeftBlobbyKeyboardRight == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_KEY_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_RIGHT); mLeftBlobbyKeyboardRight = lastActionKey; if (InputManager::getSingleton()->exit()) mLeftBlobbyKeyboardRight = oldString; } if (mLeftBlobbyKeyboardJump == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_MOVING_LEFT); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_JUMPING); mLeftBlobbyKeyboardJump = lastActionKey; if (InputManager::getSingleton()->exit()) mLeftBlobbyKeyboardJump = oldString; } if (mLeftBlobbyJoystickLeft == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_BUTTON_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_LEFT); mLeftBlobbyJoystickLeft = InputManager::getSingleton()->getLastJoyAction(); if (InputManager::getSingleton()->exit()) mLeftBlobbyJoystickLeft = oldString; } if (mLeftBlobbyJoystickRight == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_BUTTON_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_RIGHT); mLeftBlobbyJoystickRight = InputManager::getSingleton()->getLastJoyAction(); if (InputManager::getSingleton()->exit()) mLeftBlobbyJoystickRight = oldString; } if (mLeftBlobbyJoystickJump == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_BUTTON_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_JUMPING); mLeftBlobbyJoystickJump = InputManager::getSingleton()->getLastJoyAction(); if (InputManager::getSingleton()->exit()) mLeftBlobbyJoystickJump = oldString; } if (mRightBlobbyMouseJumpbutton == -1) { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(180.0, 250.0), TextManager::OP_PRESS_MOUSE_BUTTON); imgui.doText(GEN_ID, Vector2(290.0, 300.0), TextManager::OP_JUMPING); mRightBlobbyMouseJumpbutton = InputManager::getSingleton()->getLastMouseButton(); if (InputManager::getSingleton()->exit()) mRightBlobbyMouseJumpbutton = mOldInteger; } if (mRightBlobbyKeyboardLeft == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_KEY_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_LEFT); mRightBlobbyKeyboardLeft = lastActionKey; if (InputManager::getSingleton()->exit()) mRightBlobbyKeyboardLeft = oldString; } if (mRightBlobbyKeyboardRight == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_KEY_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_RIGHT); mRightBlobbyKeyboardRight = lastActionKey; if (InputManager::getSingleton()->exit()) mRightBlobbyKeyboardRight = oldString; } if (mRightBlobbyKeyboardJump == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_KEY_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_JUMPING); mRightBlobbyKeyboardJump = lastActionKey; if (InputManager::getSingleton()->exit()) mRightBlobbyKeyboardJump = oldString; } if (mRightBlobbyJoystickLeft == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_BUTTON_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_LEFT); mRightBlobbyJoystickLeft = InputManager::getSingleton()->getLastJoyAction(); if (InputManager::getSingleton()->exit()) mRightBlobbyJoystickLeft = oldString; } if (mRightBlobbyJoystickRight == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_BUTTON_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_MOVING_RIGHT); mRightBlobbyJoystickRight = InputManager::getSingleton()->getLastJoyAction(); if (InputManager::getSingleton()->exit()) mRightBlobbyJoystickRight = oldString; } if (mRightBlobbyJoystickJump == "") { imgui.doOverlay(GEN_ID, Vector2(100.0, 150.0), Vector2(700.0, 450.0)); imgui.doText(GEN_ID, Vector2(250.0, 250.0), TextManager::OP_PRESS_BUTTON_FOR); imgui.doText(GEN_ID, Vector2(270.0, 300.0), TextManager::OP_JUMPING); mRightBlobbyJoystickJump = InputManager::getSingleton()->getLastJoyAction(); if (InputManager::getSingleton()->exit()) mRightBlobbyJoystickJump = oldString; } if (imgui.doButton(GEN_ID, Vector2(224.0, 530.0), TextManager::LBL_OK)) { save(); deleteCurrentState(); setCurrentState(new OptionState()); } if (imgui.doButton(GEN_ID, Vector2(424.0, 530.0), TextManager::LBL_CANCEL)) { deleteCurrentState(); setCurrentState(new OptionState()); } } const char* InputOptionsState::getStateName() const { return "InputOptionsState"; } MiscOptionsState::MiscOptionsState() { IMGUI::getSingleton().resetSelection(); mOptionConfig.loadFile("config.xml"); std::string currentBackground = mOptionConfig.getString("background"); mBackground = -1; mBackgrounds = FileSystem::getSingleton().enumerateFiles("backgrounds", ".bmp", true); for(int i = 0; i < mBackgrounds.size(); ++i) { if (mBackgrounds[i] == currentBackground) { mBackground = i; break; } } mShowFPS = mOptionConfig.getBool("showfps"); mShowBlood = mOptionConfig.getBool("blood"); mVolume = mOptionConfig.getFloat("global_volume"); mMute = mOptionConfig.getBool("mute"); mGameFPS = mOptionConfig.getInteger("gamefps"); mNetworkSide = mOptionConfig.getInteger("network_side"); mLanguage = mOptionConfig.getString("language"); } MiscOptionsState::~MiscOptionsState() { } void MiscOptionsState::save() { mOptionConfig.setBool("showfps", mShowFPS); mOptionConfig.setBool("blood", mShowBlood); mOptionConfig.setFloat("global_volume", mVolume); mOptionConfig.setBool("mute", mMute); mOptionConfig.setInteger("gamefps", mGameFPS); mOptionConfig.setInteger("network_side", mNetworkSide); mOptionConfig.setString("language", mLanguage); if (mBackground > -1) mOptionConfig.setString("background", mBackgrounds[mBackground]); mOptionConfig.saveFile("config.xml"); SpeedController::getMainInstance()->setDrawFPS(mOptionConfig.getBool("showfps")); BloodManager::getSingleton().enable(mOptionConfig.getBool("blood")); SoundManager::getSingleton().setVolume(mOptionConfig.getFloat("global_volume")); SoundManager::getSingleton().setMute(mOptionConfig.getBool("mute")); RenderManager::getSingleton().setBackground(std::string("backgrounds/") + mOptionConfig.getString("background")); TextManager::switchLanguage(mOptionConfig.getString("language")); } void MiscOptionsState::step() { IMGUI& imgui = IMGUI::getSingleton(); imgui.doCursor(); imgui.doImage(GEN_ID, Vector2(400.0, 300.0), "background"); imgui.doOverlay(GEN_ID, Vector2(0.0, 0.0), Vector2(800.0, 600.0)); imgui.doText(GEN_ID, Vector2(34.0, 10.0), TextManager::OP_BACKGROUND); int tmp = mBackground; imgui.doSelectbox(GEN_ID, Vector2(34.0, 40.0), Vector2(400.0, 280.0), mBackgrounds, tmp); if (tmp != mBackground) { mBackground = tmp; if (mBackground > -1) RenderManager::getSingleton().setBackground(std::string("backgrounds/") + mBackgrounds[mBackground]); } imgui.doText(GEN_ID, Vector2(484.0, 10.0), TextManager::OP_VOLUME); if (imgui.doScrollbar(GEN_ID, Vector2(484.0, 50.0), mVolume)) { SoundManager::getSingleton().setVolume(mVolume); SoundManager::getSingleton().playSound("sounds/bums.wav", 1.0); } if (imgui.doButton(GEN_ID, Vector2(531.0, 80.0), TextManager::OP_MUTE)) { mMute = !mMute; SoundManager::getSingleton().setMute(mMute); if (!mMute) SoundManager::getSingleton().playSound("sounds/bums.wav", 1.0); } if (mMute) { imgui.doImage(GEN_ID, Vector2(513.0, 92.0), "gfx/pfeil_rechts.bmp"); } if (imgui.doButton(GEN_ID, Vector2(484.0, 120.0), TextManager::OP_FPS)) { mShowFPS = !mShowFPS; SpeedController::getMainInstance()->setDrawFPS(mShowFPS); } if (mShowFPS) { imgui.doImage(GEN_ID, Vector2(466.0, 132.0), "gfx/pfeil_rechts.bmp"); } if (imgui.doButton(GEN_ID, Vector2(484.0, 160.0), TextManager::OP_BLOOD)) { mShowBlood = !mShowBlood; BloodManager::getSingleton().enable(mShowBlood); BloodManager::getSingleton().spillBlood(Vector2(484.0, 160.0), 1.5, 2); } if (mShowBlood) { imgui.doImage(GEN_ID, Vector2(466.0 ,172.0), "gfx/pfeil_rechts.bmp"); } imgui.doText(GEN_ID, Vector2(434.0, 200.0), TextManager::OP_NETWORK_SIDE); if (imgui.doButton(GEN_ID, Vector2(450.0, 240.0), TextManager::OP_LEFT)) mNetworkSide = 0; if (imgui.doButton(GEN_ID, Vector2(630.0, 240.0), TextManager::OP_RIGHT)) mNetworkSide = 1; if (mNetworkSide == 0) imgui.doImage(GEN_ID, Vector2(432.0, 252.0), "gfx/pfeil_rechts.bmp"); else imgui.doImage(GEN_ID, Vector2(612.0, 252.0), "gfx/pfeil_rechts.bmp"); imgui.doText(GEN_ID, Vector2(292.0, 290.0), TextManager::OP_SPEED); float gamefps = (mGameFPS - 30) / 90.0; if (gamefps < 0.0) gamefps = 0.0; imgui.doScrollbar(GEN_ID, Vector2(295.0, 330.0), gamefps); mGameFPS = (int)(gamefps*90.0+30); if (imgui.doButton(GEN_ID, Vector2(155.0, 380.0), TextManager::OP_VSLOW)) mGameFPS = 30; if (imgui.doButton(GEN_ID, Vector2(450.0, 380.0), TextManager::OP_SLOW)) mGameFPS = 60; if (imgui.doButton(GEN_ID, Vector2(319.0, 415.0), TextManager::OP_DEFAULT)) mGameFPS = 75; if (imgui.doButton(GEN_ID, Vector2(155.0, 450.0), TextManager::OP_FAST)) mGameFPS = 90; if (imgui.doButton(GEN_ID, Vector2(410.0, 450.0), TextManager::OP_VFAST)) mGameFPS = 120; std::stringstream FPSInPercent; FPSInPercent << int((float)mGameFPS/75*100); FPSInPercent << "%"; imgui.doText(GEN_ID, Vector2(515.0, 330.0), FPSInPercent.str()); //! \todo this must be reworket std::map::iterator olang = TextManager::language_names.find(TextManager::getSingleton()->getLang()); if(++olang == TextManager::language_names.end()){ olang = TextManager::language_names.begin(); } if (imgui.doButton(GEN_ID, Vector2(300.0, 490.0), (*olang).second)){ //! \todo autogenerierte liste mit allen lang_ dateien, namen auslesen mLanguage = (*olang).first; TextManager::switchLanguage(mLanguage); } if (imgui.doButton(GEN_ID, Vector2(224.0, 530.0), TextManager::LBL_OK)) { save(); deleteCurrentState(); setCurrentState(new OptionState()); } if (imgui.doButton(GEN_ID, Vector2(424.0, 530.0), TextManager::LBL_CANCEL)) { deleteCurrentState(); setCurrentState(new OptionState()); } } const char* MiscOptionsState::getStateName() const { return "MiscOptionsState"; } blobby-1.0rc3/src/state/ReplayState.cpp0000644000175000017500000001735412042452373021427 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "ReplayState.h" /* includes */ #include #include "IMGUI.h" #include "ReplayPlayer.h" #include "DuelMatch.h" #include "SoundManager.h" #include "TextManager.h" #include "SpeedController.h" #include "IUserConfigReader.h" #include "ReplaySelectionState.h" /* implementation */ ReplayState::ReplayState() : mLeftPlayer(LEFT_PLAYER), mRightPlayer(RIGHT_PLAYER) { IMGUI::getSingleton().resetSelection(); mLeftPlayer.loadFromConfig("left"); mRightPlayer.loadFromConfig("right"); mPositionJump = -1; mPaused = false; mSpeedValue = 8; mSpeedTimer = 0; } ReplayState::~ReplayState() { } void ReplayState::loadReplay(const std::string& file) { mReplayPlayer.reset( new ReplayPlayer() ); //try //{ mReplayPlayer->load(std::string("replays/" + file + ".bvr")); mReplayMatch.reset(new DuelMatch(0, 0, true, false)); RenderManager::getSingleton().setPlayernames( mReplayPlayer->getPlayerName(LEFT_PLAYER), mReplayPlayer->getPlayerName(RIGHT_PLAYER)); SoundManager::getSingleton().playSound( "sounds/pfiff.wav", ROUND_START_SOUND_VOLUME); mLeftPlayer.setColor(mReplayPlayer->getBlobColor(LEFT_PLAYER)); mRightPlayer.setColor(mReplayPlayer->getBlobColor(RIGHT_PLAYER)); SpeedController::getMainInstance()->setGameSpeed( (float)IUserConfigReader::createUserConfigReader("config.xml")->getInteger("gamefps") ); //} /*catch (ChecksumException& e) { delete mReplayRecorder; mReplayRecorder = 0; mChecksumError = true; } catch (VersionMismatchException& e) { delete mReplayRecorder; mReplayRecorder = 0; mVersionError = true; }*/ /// \todo reintroduce error handling } void ReplayState::step() { IMGUI& imgui = IMGUI::getSingleton(); RenderManager* rmanager = &RenderManager::getSingleton(); // only draw cursor when mouse moved in the last second if(mLastMousePosition != InputManager::getSingleton()->position()) { /// \todo we must do this framerate independent mMouseShowTimer = 75; } if(mMouseShowTimer > 0) { imgui.doCursor(); mMouseShowTimer--; } mLastMousePosition = InputManager::getSingleton()->position(); if(mPositionJump != -1) { if(mReplayPlayer->gotoPlayingPosition(mPositionJump, mReplayMatch.get())) mPositionJump = -1; } else if(!mPaused) { while( mSpeedTimer >= 8) { mPaused = !mReplayPlayer->play(mReplayMatch.get()); mSpeedTimer -= 8; presentGame(*mReplayMatch); } mSpeedTimer += mSpeedValue; } mReplayMatch->getClock().setTime( mReplayPlayer->getReplayPosition() / mReplayPlayer->getGameSpeed() ); rmanager->setBlobColor(LEFT_PLAYER, mLeftPlayer.getColor()); rmanager->setBlobColor(RIGHT_PLAYER, mRightPlayer.getColor()); // draw the progress bar Vector2 prog_pos = Vector2(50, 600-22); imgui.doOverlay(GEN_ID, prog_pos, Vector2(750, 600-3), Color(0,0,0)); imgui.doOverlay(GEN_ID, prog_pos, Vector2(700*mReplayPlayer->getPlayProgress()+50, 600-3), Color(0,255,0)); //imgui.doImage(GEN_ID, Vector2(50 + 700*mReplayPlayer->getPlayProgress(), 600-16), "gfx/scrollbar.bmp"); PlayerSide side = mReplayMatch->winningPlayer(); // control replay position Vector2 mousepos = InputManager::getSingleton()->position(); if (side == NO_PLAYER && mousepos.x + 5 > prog_pos.x && mousepos.y > prog_pos.y && mousepos.x < prog_pos.x + 700 && mousepos.y < prog_pos.y + 24.0) { if (InputManager::getSingleton()->click()) { float pos = (mousepos.x - prog_pos.x) / 700.0; mPositionJump = pos * mReplayPlayer->getReplayLength(); } } // play/pause button imgui.doOverlay(GEN_ID, Vector2(350, 535.0), Vector2(450, 575.0)); if(mPaused) { imgui.doImage(GEN_ID, Vector2(400, 555.0), "gfx/btn_play.bmp"); } else { imgui.doImage(GEN_ID, Vector2(400, 555.0), "gfx/btn_pause.bmp"); } imgui.doImage(GEN_ID, Vector2(430, 555.0), "gfx/btn_fast.bmp"); imgui.doImage(GEN_ID, Vector2(370, 555.0), "gfx/btn_slow.bmp"); // handle these image buttons. IMGUI is not capable of doing this. if(side == NO_PLAYER) { if (InputManager::getSingleton()->click()) { Vector2 mousepos = InputManager::getSingleton()->position(); Vector2 btnpos = Vector2(400-12, 550.0-12); if (mousepos.x > btnpos.x && mousepos.y > btnpos.y && mousepos.x < btnpos.x + 24.0 && mousepos.y < btnpos.y + 24.0) { if(mPaused) { mPaused = false; if(mReplayPlayer->endOfFile()) mPositionJump = 0; } else mPaused = true; } Vector2 fastpos = Vector2(430-12, 550.0-12); if (mousepos.x > fastpos.x && mousepos.y > fastpos.y && mousepos.x < fastpos.x + 24.0 && mousepos.y < fastpos.y + 24.0) { mSpeedValue *= 2; if(mSpeedValue > 64) mSpeedValue = 64; } Vector2 slowpos = Vector2(370-12, 550.0-12); if (mousepos.x > slowpos.x && mousepos.y > slowpos.y && mousepos.x < slowpos.x + 24.0 && mousepos.y < slowpos.y + 24.0) { mSpeedValue /= 2; if(mSpeedValue < 1) mSpeedValue = 1; } } } if (side != NO_PLAYER) { std::stringstream tmp; if(side == LEFT_PLAYER) tmp << mReplayPlayer->getPlayerName(LEFT_PLAYER); else tmp << mReplayPlayer->getPlayerName(RIGHT_PLAYER); imgui.doOverlay(GEN_ID, Vector2(200, 150), Vector2(650, 450)); imgui.doImage(GEN_ID, Vector2(200, 250), "gfx/pokal.bmp"); imgui.doText(GEN_ID, Vector2(274, 250), tmp.str()); imgui.doText(GEN_ID, Vector2(274, 300), TextManager::GAME_WIN); if (imgui.doButton(GEN_ID, Vector2(290, 350), TextManager::LBL_OK)) { deleteCurrentState(); setCurrentState(new ReplaySelectionState()); } if (imgui.doButton(GEN_ID, Vector2(400, 350), TextManager::RP_SHOW_AGAIN)) { /// \todo how do we handle reload? // reload... we have to do all the parsing again mReplayMatch.reset( 0 ); mReplayMatch.reset(new DuelMatch(0, 0, true, false)); RenderManager::getSingleton().setPlayernames( mReplayPlayer->getPlayerName(LEFT_PLAYER), mReplayPlayer->getPlayerName(RIGHT_PLAYER)); SoundManager::getSingleton().playSound( "sounds/pfiff.wav", ROUND_START_SOUND_VOLUME); mLeftPlayer.setColor(mReplayPlayer->getBlobColor(LEFT_PLAYER)); mRightPlayer.setColor(mReplayPlayer->getBlobColor(RIGHT_PLAYER)); SpeedController::getMainInstance()->setGameSpeed( (float)IUserConfigReader::createUserConfigReader("config.xml")->getInteger("gamefps") ); mPaused = false; mPositionJump = 0; } imgui.doCursor(); } else if ((InputManager::getSingleton()->exit())) { deleteCurrentState(); setCurrentState(new ReplaySelectionState()); } } const char* ReplayState::getStateName() const { return "ReplayState"; } blobby-1.0rc3/src/lua/lauxlib.c0000644000175000017500000004201112042452374017720 0ustar danielknobedanielknobe/* ** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #include #include #include #include #include #include /* This file uses only the official API of Lua. ** Any function declared here could be written as an application function. */ #define lauxlib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #define FREELIST_REF 0 /* free list of references */ /* convert a stack index to positive */ #define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ lua_gettop(L) + (i) + 1) /* ** {====================================================== ** Error-report functions ** ======================================================= */ LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { lua_Debug ar; if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); lua_getinfo(L, "n", &ar); if (strcmp(ar.namewhat, "method") == 0) { narg--; /* do not count `self' */ if (narg == 0) /* error is in the self argument itself? */ return luaL_error(L, "calling " LUA_QS " on bad self (%s)", ar.name, extramsg); } if (ar.name == NULL) ar.name = "?"; return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", narg, ar.name, extramsg); } LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg)); return luaL_argerror(L, narg, msg); } static void tag_error (lua_State *L, int narg, int tag) { luaL_typerror(L, narg, lua_typename(L, tag)); } LUALIB_API void luaL_where (lua_State *L, int level) { lua_Debug ar; if (lua_getstack(L, level, &ar)) { /* check function at level */ lua_getinfo(L, "Sl", &ar); /* get info about it */ if (ar.currentline > 0) { /* is there info? */ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); return; } } lua_pushliteral(L, ""); /* else, no information available... */ } LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { va_list argp; va_start(argp, fmt); luaL_where(L, 1); lua_pushvfstring(L, fmt, argp); va_end(argp); lua_concat(L, 2); return lua_error(L); } /* }====================================================== */ LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { const char *name = (def) ? luaL_optstring(L, narg, def) : luaL_checkstring(L, narg); int i; for (i=0; lst[i]; i++) if (strcmp(lst[i], name) == 0) return i; return luaL_argerror(L, narg, lua_pushfstring(L, "invalid option " LUA_QS, name)); } LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ if (!lua_isnil(L, -1)) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); lua_newtable(L); /* create metatable */ lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ return 1; } LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { void *p = lua_touserdata(L, ud); if (p != NULL) { /* value is a userdata? */ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ lua_pop(L, 2); /* remove both metatables */ return p; } } } luaL_typerror(L, ud, tname); /* else error */ return NULL; /* to avoid warnings */ } LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { if (!lua_checkstack(L, space)) luaL_error(L, "stack overflow (%s)", mes); } LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { if (lua_type(L, narg) != t) tag_error(L, narg, t); } LUALIB_API void luaL_checkany (lua_State *L, int narg) { if (lua_type(L, narg) == LUA_TNONE) luaL_argerror(L, narg, "value expected"); } LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { const char *s = lua_tolstring(L, narg, len); if (!s) tag_error(L, narg, LUA_TSTRING); return s; } LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, const char *def, size_t *len) { if (lua_isnoneornil(L, narg)) { if (len) *len = (def ? strlen(def) : 0); return def; } else return luaL_checklstring(L, narg, len); } LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { lua_Number d = lua_tonumber(L, narg); if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ tag_error(L, narg, LUA_TNUMBER); return d; } LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { return luaL_opt(L, luaL_checknumber, narg, def); } LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { lua_Integer d = lua_tointeger(L, narg); if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ tag_error(L, narg, LUA_TNUMBER); return d; } LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, lua_Integer def) { return luaL_opt(L, luaL_checkinteger, narg, def); } LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { if (!lua_getmetatable(L, obj)) /* no metatable? */ return 0; lua_pushstring(L, event); lua_rawget(L, -2); if (lua_isnil(L, -1)) { lua_pop(L, 2); /* remove metatable and metafield */ return 0; } else { lua_remove(L, -2); /* remove only metatable */ return 1; } } LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { obj = abs_index(L, obj); if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ return 0; lua_pushvalue(L, obj); lua_call(L, 1, 1); return 1; } LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l) { luaI_openlib(L, libname, l, 0); } static int libsize (const luaL_Reg *l) { int size = 0; for (; l->name; l++) size++; return size; } LUALIB_API void luaI_openlib (lua_State *L, const char *libname, const luaL_Reg *l, int nup) { if (libname) { int size = libsize(l); /* check whether lib already exists */ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); lua_getfield(L, -1, libname); /* get _LOADED[libname] */ if (!lua_istable(L, -1)) { /* not found? */ lua_pop(L, 1); /* remove previous result */ /* try global variable (and create one if it does not exist) */ if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) luaL_error(L, "name conflict for module " LUA_QS, libname); lua_pushvalue(L, -1); lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ } lua_remove(L, -2); /* remove _LOADED table */ lua_insert(L, -(nup+1)); /* move library table to below upvalues */ } for (; l->name; l++) { int i; for (i=0; ifunc, nup); lua_setfield(L, -(nup+2), l->name); } lua_pop(L, nup); /* remove upvalues */ } /* ** {====================================================== ** getn-setn: size for arrays ** ======================================================= */ #if defined(LUA_COMPAT_GETN) static int checkint (lua_State *L, int topop) { int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1; lua_pop(L, topop); return n; } static void getsizes (lua_State *L) { lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); if (lua_isnil(L, -1)) { /* no `size' table? */ lua_pop(L, 1); /* remove nil */ lua_newtable(L); /* create it */ lua_pushvalue(L, -1); /* `size' will be its own metatable */ lua_setmetatable(L, -2); lua_pushliteral(L, "kv"); lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */ lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */ } } LUALIB_API void luaL_setn (lua_State *L, int t, int n) { t = abs_index(L, t); lua_pushliteral(L, "n"); lua_rawget(L, t); if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ lua_pushliteral(L, "n"); /* use it */ lua_pushinteger(L, n); lua_rawset(L, t); } else { /* use `sizes' */ getsizes(L); lua_pushvalue(L, t); lua_pushinteger(L, n); lua_rawset(L, -3); /* sizes[t] = n */ lua_pop(L, 1); /* remove `sizes' */ } } LUALIB_API int luaL_getn (lua_State *L, int t) { int n; t = abs_index(L, t); lua_pushliteral(L, "n"); /* try t.n */ lua_rawget(L, t); if ((n = checkint(L, 1)) >= 0) return n; getsizes(L); /* else try sizes[t] */ lua_pushvalue(L, t); lua_rawget(L, -2); if ((n = checkint(L, 2)) >= 0) return n; return (int)lua_objlen(L, t); } #endif /* }====================================================== */ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, const char *r) { const char *wild; size_t l = strlen(p); luaL_Buffer b; luaL_buffinit(L, &b); while ((wild = strstr(s, p)) != NULL) { luaL_addlstring(&b, s, wild - s); /* push prefix */ luaL_addstring(&b, r); /* push replacement in place of pattern */ s = wild + l; /* continue after `p' */ } luaL_addstring(&b, s); /* push last suffix */ luaL_pushresult(&b); return lua_tostring(L, -1); } LUALIB_API const char *luaL_findtable (lua_State *L, int idx, const char *fname, int szhint) { const char *e; lua_pushvalue(L, idx); do { e = strchr(fname, '.'); if (e == NULL) e = fname + strlen(fname); lua_pushlstring(L, fname, e - fname); lua_rawget(L, -2); if (lua_isnil(L, -1)) { /* no such field? */ lua_pop(L, 1); /* remove this nil */ lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ lua_pushlstring(L, fname, e - fname); lua_pushvalue(L, -2); lua_settable(L, -4); /* set new table into field */ } else if (!lua_istable(L, -1)) { /* field has a non-table value? */ lua_pop(L, 2); /* remove table and value */ return fname; /* return problematic part of the name */ } lua_remove(L, -2); /* remove previous table */ fname = e + 1; } while (*e == '.'); return NULL; } /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ #define bufflen(B) ((B)->p - (B)->buffer) #define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) #define LIMIT (LUA_MINSTACK/2) static int emptybuffer (luaL_Buffer *B) { size_t l = bufflen(B); if (l == 0) return 0; /* put nothing on stack */ else { lua_pushlstring(B->L, B->buffer, l); B->p = B->buffer; B->lvl++; return 1; } } static void adjuststack (luaL_Buffer *B) { if (B->lvl > 1) { lua_State *L = B->L; int toget = 1; /* number of levels to concat */ size_t toplen = lua_strlen(L, -1); do { size_t l = lua_strlen(L, -(toget+1)); if (B->lvl - toget + 1 >= LIMIT || toplen > l) { toplen += l; toget++; } else break; } while (toget < B->lvl); lua_concat(L, toget); B->lvl = B->lvl - toget + 1; } } LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { if (emptybuffer(B)) adjuststack(B); return B->buffer; } LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { while (l--) luaL_addchar(B, *s++); } LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { luaL_addlstring(B, s, strlen(s)); } LUALIB_API void luaL_pushresult (luaL_Buffer *B) { emptybuffer(B); lua_concat(B->L, B->lvl); B->lvl = 1; } LUALIB_API void luaL_addvalue (luaL_Buffer *B) { lua_State *L = B->L; size_t vl; const char *s = lua_tolstring(L, -1, &vl); if (vl <= bufffree(B)) { /* fit into buffer? */ memcpy(B->p, s, vl); /* put it there */ B->p += vl; lua_pop(L, 1); /* remove from stack */ } else { if (emptybuffer(B)) lua_insert(L, -2); /* put buffer before new value */ B->lvl++; /* add new value into B stack */ adjuststack(B); } } LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { B->L = L; B->p = B->buffer; B->lvl = 0; } /* }====================================================== */ LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; t = abs_index(L, t); if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove from stack */ return LUA_REFNIL; /* `nil' has a unique fixed reference */ } lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ lua_pop(L, 1); /* remove it from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ } else { /* no free elements */ ref = (int)lua_objlen(L, t); ref++; /* create new reference */ } lua_rawseti(L, t, ref); return ref; } LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { if (ref >= 0) { t = abs_index(L, t); lua_rawgeti(L, t, FREELIST_REF); lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ lua_pushinteger(L, ref); lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ } } /* ** {====================================================== ** Load functions ** ======================================================= */ typedef struct LoadF { int extraline; FILE *f; char buff[LUAL_BUFFERSIZE]; } LoadF; static const char *getF (lua_State *L, void *ud, size_t *size) { LoadF *lf = (LoadF *)ud; (void)L; if (lf->extraline) { lf->extraline = 0; *size = 1; return "\n"; } if (feof(lf->f)) return NULL; *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); return (*size > 0) ? lf->buff : NULL; } static int errfile (lua_State *L, const char *what, int fnameindex) { const char *serr = strerror(errno); const char *filename = lua_tostring(L, fnameindex) + 1; lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); lua_remove(L, fnameindex); return LUA_ERRFILE; } LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { LoadF lf; int status, readstatus; int c; int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ lf.extraline = 0; if (filename == NULL) { lua_pushliteral(L, "=stdin"); lf.f = stdin; } else { lua_pushfstring(L, "@%s", filename); lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } c = getc(lf.f); if (c == '#') { /* Unix exec. file? */ lf.extraline = 1; while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ if (c == '\n') c = getc(lf.f); } if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); /* skip eventual `#!...' */ while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; lf.extraline = 0; } ungetc(c, lf.f); status = lua_load(L, getF, &lf, lua_tostring(L, -1)); readstatus = ferror(lf.f); if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from `lua_load' */ return errfile(L, "read", fnameindex); } lua_remove(L, fnameindex); return status; } typedef struct LoadS { const char *s; size_t size; } LoadS; static const char *getS (lua_State *L, void *ud, size_t *size) { LoadS *ls = (LoadS *)ud; (void)L; if (ls->size == 0) return NULL; *size = ls->size; ls->size = 0; return ls->s; } LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, const char *name) { LoadS ls; ls.s = buff; ls.size = size; return lua_load(L, getS, &ls, name); } LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) { return luaL_loadbuffer(L, s, strlen(s), s); } /* }====================================================== */ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; if (nsize == 0) { free(ptr); return NULL; } else return realloc(ptr, nsize); } static int panic (lua_State *L) { (void)L; /* to avoid warnings */ fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1)); return 0; } LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); if (L) lua_atpanic(L, &panic); return L; } blobby-1.0rc3/src/lua/ldebug.c0000644000175000017500000004071012042452374017526 0ustar danielknobedanielknobe/* ** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $ ** Debug Interface ** See Copyright Notice in lua.h */ #include #include #include #define ldebug_c #define LUA_CORE #include "lua.h" #include "lapi.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); static int currentpc (lua_State *L, CallInfo *ci) { if (!isLua(ci)) return -1; /* function is not a Lua function? */ if (ci == L->ci) ci->savedpc = L->savedpc; return pcRel(ci->savedpc, ci_func(ci)->l.p); } static int currentline (lua_State *L, CallInfo *ci) { int pc = currentpc(L, ci); if (pc < 0) return -1; /* only active lua functions have current-line information */ else return getline(ci_func(ci)->l.p, pc); } /* ** this function can be called asynchronous (e.g. during a signal) */ LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ mask = 0; func = NULL; } L->hook = func; L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); return 1; } LUA_API lua_Hook lua_gethook (lua_State *L) { return L->hook; } LUA_API int lua_gethookmask (lua_State *L) { return L->hookmask; } LUA_API int lua_gethookcount (lua_State *L) { return L->basehookcount; } LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { int status; CallInfo *ci; lua_lock(L); for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) { level--; if (f_isLua(ci)) /* Lua function? */ level -= ci->tailcalls; /* skip lost tail calls */ } if (level == 0 && ci > L->base_ci) { /* level found? */ status = 1; ar->i_ci = cast_int(ci - L->base_ci); } else if (level < 0) { /* level is of a lost tail call? */ status = 1; ar->i_ci = 0; } else status = 0; /* no such level */ lua_unlock(L); return status; } static Proto *getluaproto (CallInfo *ci) { return (isLua(ci) ? ci_func(ci)->l.p : NULL); } static const char *findlocal (lua_State *L, CallInfo *ci, int n) { const char *name; Proto *fp = getluaproto(ci); if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL) return name; /* is a local variable in a Lua function */ else { StkId limit = (ci == L->ci) ? L->top : (ci+1)->func; if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */ return "(*temporary)"; else return NULL; } } LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { CallInfo *ci = L->base_ci + ar->i_ci; const char *name = findlocal(L, ci, n); lua_lock(L); if (name) luaA_pushobject(L, ci->base + (n - 1)); lua_unlock(L); return name; } LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { CallInfo *ci = L->base_ci + ar->i_ci; const char *name = findlocal(L, ci, n); lua_lock(L); if (name) setobjs2s(L, ci->base + (n - 1), L->top - 1); L->top--; /* pop value */ lua_unlock(L); return name; } static void funcinfo (lua_Debug *ar, Closure *cl) { if (cl->c.isC) { ar->source = "=[C]"; ar->linedefined = -1; ar->lastlinedefined = -1; ar->what = "C"; } else { ar->source = getstr(cl->l.p->source); ar->linedefined = cl->l.p->linedefined; ar->lastlinedefined = cl->l.p->lastlinedefined; ar->what = (ar->linedefined == 0) ? "main" : "Lua"; } luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); } static void info_tailcall (lua_Debug *ar) { ar->name = ar->namewhat = ""; ar->what = "tail"; ar->lastlinedefined = ar->linedefined = ar->currentline = -1; ar->source = "=(tail call)"; luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); ar->nups = 0; } static void collectvalidlines (lua_State *L, Closure *f) { if (f == NULL || f->c.isC) { setnilvalue(L->top); } else { Table *t = luaH_new(L, 0, 0); int *lineinfo = f->l.p->lineinfo; int i; for (i=0; il.p->sizelineinfo; i++) setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); sethvalue(L, L->top, t); } incr_top(L); } static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; if (f == NULL) { info_tailcall(ar); return status; } for (; *what; what++) { switch (*what) { case 'S': { funcinfo(ar, f); break; } case 'l': { ar->currentline = (ci) ? currentline(L, ci) : -1; break; } case 'u': { ar->nups = f->c.nupvalues; break; } case 'n': { ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; if (ar->namewhat == NULL) { ar->namewhat = ""; /* not found */ ar->name = NULL; } break; } case 'L': case 'f': /* handled by lua_getinfo */ break; default: status = 0; /* invalid option */ } } return status; } LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { int status; Closure *f = NULL; CallInfo *ci = NULL; lua_lock(L); if (*what == '>') { StkId func = L->top - 1; luai_apicheck(L, ttisfunction(func)); what++; /* skip the '>' */ f = clvalue(func); L->top--; /* pop function */ } else if (ar->i_ci != 0) { /* no tail call? */ ci = L->base_ci + ar->i_ci; lua_assert(ttisfunction(ci->func)); f = clvalue(ci->func); } status = auxgetinfo(L, what, ar, f, ci); if (strchr(what, 'f')) { if (f == NULL) setnilvalue(L->top); else setclvalue(L, L->top, f); incr_top(L); } if (strchr(what, 'L')) collectvalidlines(L, f); lua_unlock(L); return status; } /* ** {====================================================== ** Symbolic Execution and code checker ** ======================================================= */ #define check(x) if (!(x)) return 0; #define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) #define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) static int precheck (const Proto *pt) { check(pt->maxstacksize <= MAXSTACK); check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); check(!(pt->is_vararg & VARARG_NEEDSARG) || (pt->is_vararg & VARARG_HASARG)); check(pt->sizeupvalues <= pt->nups); check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); return 1; } #define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1]) int luaG_checkopenop (Instruction i) { switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: case OP_RETURN: case OP_SETLIST: { check(GETARG_B(i) == 0); return 1; } default: return 0; /* invalid instruction after an open call */ } } static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) { switch (mode) { case OpArgN: check(r == 0); break; case OpArgU: break; case OpArgR: checkreg(pt, r); break; case OpArgK: check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize); break; } return 1; } static Instruction symbexec (const Proto *pt, int lastpc, int reg) { int pc; int last; /* stores position of last instruction that changed `reg' */ last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ check(precheck(pt)); for (pc = 0; pc < lastpc; pc++) { Instruction i = pt->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); int b = 0; int c = 0; check(op < NUM_OPCODES); checkreg(pt, a); switch (getOpMode(op)) { case iABC: { b = GETARG_B(i); c = GETARG_C(i); check(checkArgMode(pt, b, getBMode(op))); check(checkArgMode(pt, c, getCMode(op))); break; } case iABx: { b = GETARG_Bx(i); if (getBMode(op) == OpArgK) check(b < pt->sizek); break; } case iAsBx: { b = GETARG_sBx(i); if (getBMode(op) == OpArgR) { int dest = pc+1+b; check(0 <= dest && dest < pt->sizecode); if (dest > 0) { int j; /* check that it does not jump to a setlist count; this is tricky, because the count from a previous setlist may have the same value of an invalid setlist; so, we must go all the way back to the first of them (if any) */ for (j = 0; j < dest; j++) { Instruction d = pt->code[dest-1-j]; if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break; } /* if 'j' is even, previous value is not a setlist (even if it looks like one) */ check((j&1) == 0); } } break; } } if (testAMode(op)) { if (a == reg) last = pc; /* change register `a' */ } if (testTMode(op)) { check(pc+2 < pt->sizecode); /* check skip */ check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); } switch (op) { case OP_LOADBOOL: { if (c == 1) { /* does it jump? */ check(pc+2 < pt->sizecode); /* check its jump */ check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST || GETARG_C(pt->code[pc+1]) != 0); } break; } case OP_LOADNIL: { if (a <= reg && reg <= b) last = pc; /* set registers from `a' to `b' */ break; } case OP_GETUPVAL: case OP_SETUPVAL: { check(b < pt->nups); break; } case OP_GETGLOBAL: case OP_SETGLOBAL: { check(ttisstring(&pt->k[b])); break; } case OP_SELF: { checkreg(pt, a+1); if (reg == a+1) last = pc; break; } case OP_CONCAT: { check(b < c); /* at least two operands */ break; } case OP_TFORLOOP: { check(c >= 1); /* at least one result (control variable) */ checkreg(pt, a+2+c); /* space for results */ if (reg >= a+2) last = pc; /* affect all regs above its base */ break; } case OP_FORLOOP: case OP_FORPREP: checkreg(pt, a+3); /* go through */ case OP_JMP: { int dest = pc+1+b; /* not full check and jump is forward and do not skip `lastpc'? */ if (reg != NO_REG && pc < dest && dest <= lastpc) pc += b; /* do the jump */ break; } case OP_CALL: case OP_TAILCALL: { if (b != 0) { checkreg(pt, a+b-1); } c--; /* c = num. returns */ if (c == LUA_MULTRET) { check(checkopenop(pt, pc)); } else if (c != 0) checkreg(pt, a+c-1); if (reg >= a) last = pc; /* affect all registers above base */ break; } case OP_RETURN: { b--; /* b = num. returns */ if (b > 0) checkreg(pt, a+b-1); break; } case OP_SETLIST: { if (b > 0) checkreg(pt, a + b); if (c == 0) { pc++; check(pc < pt->sizecode - 1); } break; } case OP_CLOSURE: { int nup, j; check(b < pt->sizep); nup = pt->p[b]->nups; check(pc + nup < pt->sizecode); for (j = 1; j <= nup; j++) { OpCode op1 = GET_OPCODE(pt->code[pc + j]); check(op1 == OP_GETUPVAL || op1 == OP_MOVE); } if (reg != NO_REG) /* tracing? */ pc += nup; /* do not 'execute' these pseudo-instructions */ break; } case OP_VARARG: { check((pt->is_vararg & VARARG_ISVARARG) && !(pt->is_vararg & VARARG_NEEDSARG)); b--; if (b == LUA_MULTRET) check(checkopenop(pt, pc)); checkreg(pt, a+b-1); break; } default: break; } } return pt->code[last]; } #undef check #undef checkjump #undef checkreg /* }====================================================== */ int luaG_checkcode (const Proto *pt) { return (symbexec(pt, pt->sizecode, NO_REG) != 0); } static const char *kname (Proto *p, int c) { if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) return svalue(&p->k[INDEXK(c)]); else return "?"; } static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos, const char **name) { if (isLua(ci)) { /* a Lua function? */ Proto *p = ci_func(ci)->l.p; int pc = currentpc(L, ci); Instruction i; *name = luaF_getlocalname(p, stackpos+1, pc); if (*name) /* is a local? */ return "local"; i = symbexec(p, pc, stackpos); /* try symbolic execution */ lua_assert(pc != -1); switch (GET_OPCODE(i)) { case OP_GETGLOBAL: { int g = GETARG_Bx(i); /* global index */ lua_assert(ttisstring(&p->k[g])); *name = svalue(&p->k[g]); return "global"; } case OP_MOVE: { int a = GETARG_A(i); int b = GETARG_B(i); /* move from `b' to `a' */ if (b < a) return getobjname(L, ci, b, name); /* get name for `b' */ break; } case OP_GETTABLE: { int k = GETARG_C(i); /* key index */ *name = kname(p, k); return "field"; } case OP_GETUPVAL: { int u = GETARG_B(i); /* upvalue index */ *name = p->upvalues ? getstr(p->upvalues[u]) : "?"; return "upvalue"; } case OP_SELF: { int k = GETARG_C(i); /* key index */ *name = kname(p, k); return "method"; } default: break; } } return NULL; /* no useful name found */ } static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { Instruction i; if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) return NULL; /* calling function is not Lua (or is unknown) */ ci--; /* calling function */ i = ci_func(ci)->l.p->code[currentpc(L, ci)]; if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || GET_OPCODE(i) == OP_TFORLOOP) return getobjname(L, ci, GETARG_A(i), name); else return NULL; /* no useful name can be found */ } /* only ANSI way to check whether a pointer points to an array */ static int isinstack (CallInfo *ci, const TValue *o) { StkId p; for (p = ci->base; p < ci->top; p++) if (o == p) return 1; return 0; } void luaG_typeerror (lua_State *L, const TValue *o, const char *op) { const char *name = NULL; const char *t = luaT_typenames[ttype(o)]; const char *kind = (isinstack(L->ci, o)) ? getobjname(L, L->ci, cast_int(o - L->base), &name) : NULL; if (kind) luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", op, kind, name, t); else luaG_runerror(L, "attempt to %s a %s value", op, t); } void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; lua_assert(!ttisstring(p1) && !ttisnumber(p1)); luaG_typeerror(L, p1, "concatenate"); } void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { TValue temp; if (luaV_tonumber(p1, &temp) == NULL) p2 = p1; /* first operand is wrong */ luaG_typeerror(L, p2, "perform arithmetic on"); } int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { const char *t1 = luaT_typenames[ttype(p1)]; const char *t2 = luaT_typenames[ttype(p2)]; if (t1[2] == t2[2]) luaG_runerror(L, "attempt to compare two %s values", t1); else luaG_runerror(L, "attempt to compare %s with %s", t1, t2); return 0; } static void addinfo (lua_State *L, const char *msg) { CallInfo *ci = L->ci; if (isLua(ci)) { /* is Lua code? */ char buff[LUA_IDSIZE]; /* add file:line information */ int line = currentline(L, ci); luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } } void luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ incr_top(L); luaD_call(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } void luaG_runerror (lua_State *L, const char *fmt, ...) { va_list argp; va_start(argp, fmt); addinfo(L, luaO_pushvfstring(L, fmt, argp)); va_end(argp); luaG_errormsg(L); } blobby-1.0rc3/src/lua/lauxlib.h0000644000175000017500000001322112042452374017726 0ustar danielknobedanielknobe/* ** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #ifndef lauxlib_h #define lauxlib_h #include #include #include "lua.h" #if defined(LUA_COMPAT_GETN) LUALIB_API int (luaL_getn) (lua_State *L, int t); LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); #else #define luaL_getn(L,i) ((int)lua_objlen(L, i)) #define luaL_setn(L,i,j) ((void)0) /* no op! */ #endif #if defined(LUA_COMPAT_OPENLIB) #define luaI_openlib luaL_openlib #endif /* extra error code for `luaL_load' */ #define LUA_ERRFILE (LUA_ERRERR+1) typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg; LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, const luaL_Reg *l, int nup); LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l); LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, size_t *l); LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, const char *def, size_t *l); LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, lua_Integer def); LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); LUALIB_API void (luaL_checkany) (lua_State *L, int narg); LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); LUALIB_API void (luaL_where) (lua_State *L, int lvl); LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, const char *const lst[]); LUALIB_API int (luaL_ref) (lua_State *L, int t); LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, const char *name); LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); LUALIB_API lua_State *(luaL_newstate) (void); LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r); LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, const char *fname, int szhint); /* ** =============================================================== ** some useful macros ** =============================================================== */ #define luaL_argcheck(L, cond,numarg,extramsg) \ ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) #define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) #define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) #define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) #define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) #define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) #define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) #define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) #define luaL_dofile(L, fn) \ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_dostring(L, s) \ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ typedef struct luaL_Buffer { char *p; /* current position in buffer */ int lvl; /* number of strings in the stack (level) */ lua_State *L; char buffer[LUAL_BUFFERSIZE]; } luaL_Buffer; #define luaL_addchar(B,c) \ ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ (*(B)->p++ = (char)(c))) /* compatibility only */ #define luaL_putchar(B,c) luaL_addchar(B,c) #define luaL_addsize(B,n) ((B)->p += (n)) LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); /* }====================================================== */ /* compatibility with ref system */ /* pre-defined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) #define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) #define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) #define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) #define luaL_reg luaL_Reg #endif blobby-1.0rc3/src/lua/ldebug.h0000644000175000017500000000204512042452374017532 0ustar danielknobedanielknobe/* ** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $ ** Auxiliary functions from Debug Interface module ** See Copyright Notice in lua.h */ #ifndef ldebug_h #define ldebug_h #include "lstate.h" #define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) #define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) #define resethookcount(L) (L->hookcount = L->basehookcount) LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o, const char *opname); LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2); LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...); LUAI_FUNC void luaG_errormsg (lua_State *L); LUAI_FUNC int luaG_checkcode (const Proto *pt); LUAI_FUNC int luaG_checkopenop (Instruction i); #endif blobby-1.0rc3/src/lua/ltablib.c0000644000175000017500000001625712042452374017706 0ustar danielknobedanielknobe/* ** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $ ** Library for Table Manipulation ** See Copyright Notice in lua.h */ #include #define ltablib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" #define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) static int foreachi (lua_State *L) { int i; int n = aux_getn(L, 1); luaL_checktype(L, 2, LUA_TFUNCTION); for (i=1; i <= n; i++) { lua_pushvalue(L, 2); /* function */ lua_pushinteger(L, i); /* 1st argument */ lua_rawgeti(L, 1, i); /* 2nd argument */ lua_call(L, 2, 1); if (!lua_isnil(L, -1)) return 1; lua_pop(L, 1); /* remove nil result */ } return 0; } static int foreach (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 2, LUA_TFUNCTION); lua_pushnil(L); /* first key */ while (lua_next(L, 1)) { lua_pushvalue(L, 2); /* function */ lua_pushvalue(L, -3); /* key */ lua_pushvalue(L, -3); /* value */ lua_call(L, 2, 1); if (!lua_isnil(L, -1)) return 1; lua_pop(L, 2); /* remove value and result */ } return 0; } static int maxn (lua_State *L) { lua_Number max = 0; luaL_checktype(L, 1, LUA_TTABLE); lua_pushnil(L); /* first key */ while (lua_next(L, 1)) { lua_pop(L, 1); /* remove value */ if (lua_type(L, -1) == LUA_TNUMBER) { lua_Number v = lua_tonumber(L, -1); if (v > max) max = v; } } lua_pushnumber(L, max); return 1; } static int getn (lua_State *L) { lua_pushinteger(L, aux_getn(L, 1)); return 1; } static int setn (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); #ifndef luaL_setn luaL_setn(L, 1, luaL_checkint(L, 2)); #else luaL_error(L, LUA_QL("setn") " is obsolete"); #endif lua_pushvalue(L, 1); return 1; } static int tinsert (lua_State *L) { int e = aux_getn(L, 1) + 1; /* first empty element */ int pos; /* where to insert new element */ switch (lua_gettop(L)) { case 2: { /* called with only 2 arguments */ pos = e; /* insert new element at the end */ break; } case 3: { int i; pos = luaL_checkint(L, 2); /* 2nd argument is the position */ if (pos > e) e = pos; /* `grow' array if necessary */ for (i = e; i > pos; i--) { /* move up elements */ lua_rawgeti(L, 1, i-1); lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ } break; } default: { return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); } } luaL_setn(L, 1, e); /* new size */ lua_rawseti(L, 1, pos); /* t[pos] = v */ return 0; } static int tremove (lua_State *L) { int e = aux_getn(L, 1); int pos = luaL_optint(L, 2, e); if (!(1 <= pos && pos <= e)) /* position is outside bounds? */ return 0; /* nothing to remove */ luaL_setn(L, 1, e - 1); /* t.n = n-1 */ lua_rawgeti(L, 1, pos); /* result = t[pos] */ for ( ;pos= P */ while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { if (i>u) luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[i] */ } /* repeat --j until a[j] <= P */ while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { if (j #include #include #include #define liolib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" #define IO_INPUT 1 #define IO_OUTPUT 2 static const char *const fnames[] = {"input", "output"}; static int pushresult (lua_State *L, int i, const char *filename) { int en = errno; /* calls to Lua API may change this value */ if (i) { lua_pushboolean(L, 1); return 1; } else { lua_pushnil(L); if (filename) lua_pushfstring(L, "%s: %s", filename, strerror(en)); else lua_pushfstring(L, "%s", strerror(en)); lua_pushinteger(L, en); return 3; } } static void fileerror (lua_State *L, int arg, const char *filename) { lua_pushfstring(L, "%s: %s", filename, strerror(errno)); luaL_argerror(L, arg, lua_tostring(L, -1)); } #define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) static int io_type (lua_State *L) { void *ud; luaL_checkany(L, 1); ud = lua_touserdata(L, 1); lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) lua_pushnil(L); /* not a file */ else if (*((FILE **)ud) == NULL) lua_pushliteral(L, "closed file"); else lua_pushliteral(L, "file"); return 1; } static FILE *tofile (lua_State *L) { FILE **f = tofilep(L); if (*f == NULL) luaL_error(L, "attempt to use a closed file"); return *f; } /* ** When creating file handles, always creates a `closed' file handle ** before opening the actual file; so, if there is a memory error, the ** file is not left opened. */ static FILE **newfile (lua_State *L) { FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); *pf = NULL; /* file handle is currently `closed' */ luaL_getmetatable(L, LUA_FILEHANDLE); lua_setmetatable(L, -2); return pf; } /* ** function to (not) close the standard files stdin, stdout, and stderr */ static int io_noclose (lua_State *L) { lua_pushnil(L); lua_pushliteral(L, "cannot close standard file"); return 2; } /* ** function to close 'popen' files */ static int io_pclose (lua_State *L) { FILE **p = tofilep(L); int ok = lua_pclose(L, *p); *p = NULL; return pushresult(L, ok, NULL); } /* ** function to close regular files */ static int io_fclose (lua_State *L) { FILE **p = tofilep(L); int ok = (fclose(*p) == 0); *p = NULL; return pushresult(L, ok, NULL); } static int aux_close (lua_State *L) { lua_getfenv(L, 1); lua_getfield(L, -1, "__close"); return (lua_tocfunction(L, -1))(L); } static int io_close (lua_State *L) { if (lua_isnone(L, 1)) lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); tofile(L); /* make sure argument is a file */ return aux_close(L); } static int io_gc (lua_State *L) { FILE *f = *tofilep(L); /* ignore closed files */ if (f != NULL) aux_close(L); return 0; } static int io_tostring (lua_State *L) { FILE *f = *tofilep(L); if (f == NULL) lua_pushliteral(L, "file (closed)"); else lua_pushfstring(L, "file (%p)", f); return 1; } static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); FILE **pf = newfile(L); *pf = fopen(filename, mode); return (*pf == NULL) ? pushresult(L, 0, filename) : 1; } /* ** this function has a separated environment, which defines the ** correct __close for 'popen' files */ static int io_popen (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); FILE **pf = newfile(L); *pf = lua_popen(L, filename, mode); return (*pf == NULL) ? pushresult(L, 0, filename) : 1; } static int io_tmpfile (lua_State *L) { FILE **pf = newfile(L); *pf = tmpfile(); return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; } static FILE *getiofile (lua_State *L, int findex) { FILE *f; lua_rawgeti(L, LUA_ENVIRONINDEX, findex); f = *(FILE **)lua_touserdata(L, -1); if (f == NULL) luaL_error(L, "standard %s file is closed", fnames[findex - 1]); return f; } static int g_iofile (lua_State *L, int f, const char *mode) { if (!lua_isnoneornil(L, 1)) { const char *filename = lua_tostring(L, 1); if (filename) { FILE **pf = newfile(L); *pf = fopen(filename, mode); if (*pf == NULL) fileerror(L, 1, filename); } else { tofile(L); /* check that it's a valid file handle */ lua_pushvalue(L, 1); } lua_rawseti(L, LUA_ENVIRONINDEX, f); } /* return current value */ lua_rawgeti(L, LUA_ENVIRONINDEX, f); return 1; } static int io_input (lua_State *L) { return g_iofile(L, IO_INPUT, "r"); } static int io_output (lua_State *L) { return g_iofile(L, IO_OUTPUT, "w"); } static int io_readline (lua_State *L); static void aux_lines (lua_State *L, int idx, int toclose) { lua_pushvalue(L, idx); lua_pushboolean(L, toclose); /* close/not close file when finished */ lua_pushcclosure(L, io_readline, 2); } static int f_lines (lua_State *L) { tofile(L); /* check that it's a valid file handle */ aux_lines(L, 1, 0); return 1; } static int io_lines (lua_State *L) { if (lua_isnoneornil(L, 1)) { /* no arguments? */ /* will iterate over default input */ lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); return f_lines(L); } else { const char *filename = luaL_checkstring(L, 1); FILE **pf = newfile(L); *pf = fopen(filename, "r"); if (*pf == NULL) fileerror(L, 1, filename); aux_lines(L, lua_gettop(L), 1); return 1; } } /* ** {====================================================== ** READ ** ======================================================= */ static int read_number (lua_State *L, FILE *f) { lua_Number d; if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { lua_pushnumber(L, d); return 1; } else return 0; /* read fails */ } static int test_eof (lua_State *L, FILE *f) { int c = getc(f); ungetc(c, f); lua_pushlstring(L, NULL, 0); return (c != EOF); } static int read_line (lua_State *L, FILE *f) { luaL_Buffer b; luaL_buffinit(L, &b); for (;;) { size_t l; char *p = luaL_prepbuffer(&b); if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ luaL_pushresult(&b); /* close buffer */ return (lua_objlen(L, -1) > 0); /* check whether read something */ } l = strlen(p); if (l == 0 || p[l-1] != '\n') luaL_addsize(&b, l); else { luaL_addsize(&b, l - 1); /* do not include `eol' */ luaL_pushresult(&b); /* close buffer */ return 1; /* read at least an `eol' */ } } } static int read_chars (lua_State *L, FILE *f, size_t n) { size_t rlen; /* how much to read */ size_t nr; /* number of chars actually read */ luaL_Buffer b; luaL_buffinit(L, &b); rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ do { char *p = luaL_prepbuffer(&b); if (rlen > n) rlen = n; /* cannot read more than asked */ nr = fread(p, sizeof(char), rlen, f); luaL_addsize(&b, nr); n -= nr; /* still have to read `n' chars */ } while (n > 0 && nr == rlen); /* until end of count or eof */ luaL_pushresult(&b); /* close buffer */ return (n == 0 || lua_objlen(L, -1) > 0); } static int g_read (lua_State *L, FILE *f, int first) { int nargs = lua_gettop(L) - 1; int success; int n; clearerr(f); if (nargs == 0) { /* no arguments? */ success = read_line(L, f); n = first+1; /* to return 1 result */ } else { /* ensure stack space for all results and for auxlib's buffer */ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); success = 1; for (n = first; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { size_t l = (size_t)lua_tointeger(L, n); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); } else { const char *p = lua_tostring(L, n); luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); switch (p[1]) { case 'n': /* number */ success = read_number(L, f); break; case 'l': /* line */ success = read_line(L, f); break; case 'a': /* file */ read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ success = 1; /* always success */ break; default: return luaL_argerror(L, n, "invalid format"); } } } } if (ferror(f)) return pushresult(L, 0, NULL); if (!success) { lua_pop(L, 1); /* remove last result */ lua_pushnil(L); /* push nil instead */ } return n - first; } static int io_read (lua_State *L) { return g_read(L, getiofile(L, IO_INPUT), 1); } static int f_read (lua_State *L) { return g_read(L, tofile(L), 2); } static int io_readline (lua_State *L) { FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); int sucess; if (f == NULL) /* file is already closed? */ luaL_error(L, "file is already closed"); sucess = read_line(L, f); if (ferror(f)) return luaL_error(L, "%s", strerror(errno)); if (sucess) return 1; else { /* EOF */ if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ lua_settop(L, 0); lua_pushvalue(L, lua_upvalueindex(1)); aux_close(L); /* close it */ } return 0; } } /* }====================================================== */ static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - 1; int status = 1; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ status = status && fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; } else { size_t l; const char *s = luaL_checklstring(L, arg, &l); status = status && (fwrite(s, sizeof(char), l, f) == l); } } return pushresult(L, status, NULL); } static int io_write (lua_State *L) { return g_write(L, getiofile(L, IO_OUTPUT), 1); } static int f_write (lua_State *L) { return g_write(L, tofile(L), 2); } static int f_seek (lua_State *L) { static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, "cur", modenames); long offset = luaL_optlong(L, 3, 0); op = fseek(f, offset, mode[op]); if (op) return pushresult(L, 0, NULL); /* error */ else { lua_pushinteger(L, ftell(f)); return 1; } } static int f_setvbuf (lua_State *L) { static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; static const char *const modenames[] = {"no", "full", "line", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); int res = setvbuf(f, NULL, mode[op], sz); return pushresult(L, res == 0, NULL); } static int io_flush (lua_State *L) { return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); } static int f_flush (lua_State *L) { return pushresult(L, fflush(tofile(L)) == 0, NULL); } static const luaL_Reg iolib[] = { {"close", io_close}, {"flush", io_flush}, {"input", io_input}, {"lines", io_lines}, {"open", io_open}, {"output", io_output}, {"popen", io_popen}, {"read", io_read}, {"tmpfile", io_tmpfile}, {"type", io_type}, {"write", io_write}, {NULL, NULL} }; static const luaL_Reg flib[] = { {"close", io_close}, {"flush", f_flush}, {"lines", f_lines}, {"read", f_read}, {"seek", f_seek}, {"setvbuf", f_setvbuf}, {"write", f_write}, {"__gc", io_gc}, {"__tostring", io_tostring}, {NULL, NULL} }; static void createmeta (lua_State *L) { luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ lua_pushvalue(L, -1); /* push metatable */ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ luaL_register(L, NULL, flib); /* file methods */ } static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) { *newfile(L) = f; if (k > 0) { lua_pushvalue(L, -1); lua_rawseti(L, LUA_ENVIRONINDEX, k); } lua_pushvalue(L, -2); /* copy environment */ lua_setfenv(L, -2); /* set it */ lua_setfield(L, -3, fname); } static void newfenv (lua_State *L, lua_CFunction cls) { lua_createtable(L, 0, 1); lua_pushcfunction(L, cls); lua_setfield(L, -2, "__close"); } LUALIB_API int luaopen_io (lua_State *L) { createmeta(L); /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ newfenv(L, io_fclose); lua_replace(L, LUA_ENVIRONINDEX); /* open library */ luaL_register(L, LUA_IOLIBNAME, iolib); /* create (and set) default files */ newfenv(L, io_noclose); /* close function for default files */ createstdfile(L, stdin, IO_INPUT, "stdin"); createstdfile(L, stdout, IO_OUTPUT, "stdout"); createstdfile(L, stderr, 0, "stderr"); lua_pop(L, 1); /* pop environment for default files */ lua_getfield(L, -1, "popen"); newfenv(L, io_pclose); /* create environment for 'popen' */ lua_setfenv(L, -2); /* set fenv for 'popen' */ lua_pop(L, 1); /* pop 'popen' */ return 1; } blobby-1.0rc3/src/lua/lstrlib.c0000644000175000017500000005567112042452374017753 0ustar danielknobedanielknobe/* ** $Id: lstrlib.c,v 1.132.1.4 2008/07/11 17:27:21 roberto Exp $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ #include #include #include #include #include #define lstrlib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* macro to `unsign' a character */ #define uchar(c) ((unsigned char)(c)) static int str_len (lua_State *L) { size_t l; luaL_checklstring(L, 1, &l); lua_pushinteger(L, l); return 1; } static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { /* relative string position: negative means back from end */ if (pos < 0) pos += (ptrdiff_t)len + 1; return (pos >= 0) ? pos : 0; } static int str_sub (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); if (start < 1) start = 1; if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; if (start <= end) lua_pushlstring(L, s+start-1, end-start+1); else lua_pushliteral(L, ""); return 1; } static int str_reverse (lua_State *L) { size_t l; luaL_Buffer b; const char *s = luaL_checklstring(L, 1, &l); luaL_buffinit(L, &b); while (l--) luaL_addchar(&b, s[l]); luaL_pushresult(&b); return 1; } static int str_lower (lua_State *L) { size_t l; size_t i; luaL_Buffer b; const char *s = luaL_checklstring(L, 1, &l); luaL_buffinit(L, &b); for (i=0; i 0) luaL_addlstring(&b, s, l); luaL_pushresult(&b); return 1; } static int str_byte (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); int n, i; if (posi <= 0) posi = 1; if ((size_t)pose > l) pose = l; if (posi > pose) return 0; /* empty interval; return no values */ n = (int)(pose - posi + 1); if (posi + n <= pose) /* overflow? */ luaL_error(L, "string slice too long"); luaL_checkstack(L, n, "string slice too long"); for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) return luaL_error(ms->L, "invalid capture index"); return l; } static int capture_to_close (MatchState *ms) { int level = ms->level; for (level--; level>=0; level--) if (ms->capture[level].len == CAP_UNFINISHED) return level; return luaL_error(ms->L, "invalid pattern capture"); } static const char *classend (MatchState *ms, const char *p) { switch (*p++) { case L_ESC: { if (*p == '\0') luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); return p+1; } case '[': { if (*p == '^') p++; do { /* look for a `]' */ if (*p == '\0') luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); if (*(p++) == L_ESC && *p != '\0') p++; /* skip escapes (e.g. `%]') */ } while (*p != ']'); return p+1; } default: { return p; } } } static int match_class (int c, int cl) { int res; switch (tolower(cl)) { case 'a' : res = isalpha(c); break; case 'c' : res = iscntrl(c); break; case 'd' : res = isdigit(c); break; case 'l' : res = islower(c); break; case 'p' : res = ispunct(c); break; case 's' : res = isspace(c); break; case 'u' : res = isupper(c); break; case 'w' : res = isalnum(c); break; case 'x' : res = isxdigit(c); break; case 'z' : res = (c == 0); break; default: return (cl == c); } return (islower(cl) ? res : !res); } static int matchbracketclass (int c, const char *p, const char *ec) { int sig = 1; if (*(p+1) == '^') { sig = 0; p++; /* skip the `^' */ } while (++p < ec) { if (*p == L_ESC) { p++; if (match_class(c, uchar(*p))) return sig; } else if ((*(p+1) == '-') && (p+2 < ec)) { p+=2; if (uchar(*(p-2)) <= c && c <= uchar(*p)) return sig; } else if (uchar(*p) == c) return sig; } return !sig; } static int singlematch (int c, const char *p, const char *ep) { switch (*p) { case '.': return 1; /* matches any char */ case L_ESC: return match_class(c, uchar(*(p+1))); case '[': return matchbracketclass(c, p, ep-1); default: return (uchar(*p) == c); } } static const char *match (MatchState *ms, const char *s, const char *p); static const char *matchbalance (MatchState *ms, const char *s, const char *p) { if (*p == 0 || *(p+1) == 0) luaL_error(ms->L, "unbalanced pattern"); if (*s != *p) return NULL; else { int b = *p; int e = *(p+1); int cont = 1; while (++s < ms->src_end) { if (*s == e) { if (--cont == 0) return s+1; } else if (*s == b) cont++; } } return NULL; /* string ends out of balance */ } static const char *max_expand (MatchState *ms, const char *s, const char *p, const char *ep) { ptrdiff_t i = 0; /* counts maximum expand for item */ while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) i++; /* keeps trying to match with the maximum repetitions */ while (i>=0) { const char *res = match(ms, (s+i), ep+1); if (res) return res; i--; /* else didn't match; reduce 1 repetition to try again */ } return NULL; } static const char *min_expand (MatchState *ms, const char *s, const char *p, const char *ep) { for (;;) { const char *res = match(ms, s, ep+1); if (res != NULL) return res; else if (ssrc_end && singlematch(uchar(*s), p, ep)) s++; /* try with one more repetition */ else return NULL; } } static const char *start_capture (MatchState *ms, const char *s, const char *p, int what) { const char *res; int level = ms->level; if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); ms->capture[level].init = s; ms->capture[level].len = what; ms->level = level+1; if ((res=match(ms, s, p)) == NULL) /* match failed? */ ms->level--; /* undo capture */ return res; } static const char *end_capture (MatchState *ms, const char *s, const char *p) { int l = capture_to_close(ms); const char *res; ms->capture[l].len = s - ms->capture[l].init; /* close capture */ if ((res = match(ms, s, p)) == NULL) /* match failed? */ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ return res; } static const char *match_capture (MatchState *ms, const char *s, int l) { size_t len; l = check_capture(ms, l); len = ms->capture[l].len; if ((size_t)(ms->src_end-s) >= len && memcmp(ms->capture[l].init, s, len) == 0) return s+len; else return NULL; } static const char *match (MatchState *ms, const char *s, const char *p) { init: /* using goto's to optimize tail recursion */ switch (*p) { case '(': { /* start capture */ if (*(p+1) == ')') /* position capture? */ return start_capture(ms, s, p+2, CAP_POSITION); else return start_capture(ms, s, p+1, CAP_UNFINISHED); } case ')': { /* end capture */ return end_capture(ms, s, p+1); } case L_ESC: { switch (*(p+1)) { case 'b': { /* balanced string? */ s = matchbalance(ms, s, p+2); if (s == NULL) return NULL; p+=4; goto init; /* else return match(ms, s, p+4); */ } case 'f': { /* frontier? */ const char *ep; char previous; p += 2; if (*p != '[') luaL_error(ms->L, "missing " LUA_QL("[") " after " LUA_QL("%%f") " in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s-1); if (matchbracketclass(uchar(previous), p, ep-1) || !matchbracketclass(uchar(*s), p, ep-1)) return NULL; p=ep; goto init; /* else return match(ms, s, ep); */ } default: { if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ s = match_capture(ms, s, uchar(*(p+1))); if (s == NULL) return NULL; p+=2; goto init; /* else return match(ms, s, p+2) */ } goto dflt; /* case default */ } } } case '\0': { /* end of pattern */ return s; /* match succeeded */ } case '$': { if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ return (s == ms->src_end) ? s : NULL; /* check end of string */ else goto dflt; } default: dflt: { /* it is a pattern item */ const char *ep = classend(ms, p); /* points to what is next */ int m = ssrc_end && singlematch(uchar(*s), p, ep); switch (*ep) { case '?': { /* optional */ const char *res; if (m && ((res=match(ms, s+1, ep+1)) != NULL)) return res; p=ep+1; goto init; /* else return match(ms, s, ep+1); */ } case '*': { /* 0 or more repetitions */ return max_expand(ms, s, p, ep); } case '+': { /* 1 or more repetitions */ return (m ? max_expand(ms, s+1, p, ep) : NULL); } case '-': { /* 0 or more repetitions (minimum) */ return min_expand(ms, s, p, ep); } default: { if (!m) return NULL; s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ } } } } } static const char *lmemfind (const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1; /* empty strings are everywhere */ else if (l2 > l1) return NULL; /* avoids a negative `l1' */ else { const char *init; /* to search for a `*s2' inside `s1' */ l2--; /* 1st char will be checked by `memchr' */ l1 = l1-l2; /* `s2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++; /* 1st char is already checked */ if (memcmp(init, s2+1, l2) == 0) return init-1; else { /* correct `l1' and `s1' to try again */ l1 -= init-s1; s1 = init; } } return NULL; /* not found */ } } static void push_onecapture (MatchState *ms, int i, const char *s, const char *e) { if (i >= ms->level) { if (i == 0) /* ms->level == 0, too */ lua_pushlstring(ms->L, s, e - s); /* add whole match */ else luaL_error(ms->L, "invalid capture index"); } else { ptrdiff_t l = ms->capture[i].len; if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); if (l == CAP_POSITION) lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); else lua_pushlstring(ms->L, ms->capture[i].init, l); } } static int push_captures (MatchState *ms, const char *s, const char *e) { int i; int nlevels = (ms->level == 0 && s) ? 1 : ms->level; luaL_checkstack(ms->L, nlevels, "too many captures"); for (i = 0; i < nlevels; i++) push_onecapture(ms, i, s, e); return nlevels; /* number of strings pushed */ } static int str_find_aux (lua_State *L, int find) { size_t l1, l2; const char *s = luaL_checklstring(L, 1, &l1); const char *p = luaL_checklstring(L, 2, &l2); ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; if (init < 0) init = 0; else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; if (find && (lua_toboolean(L, 4) || /* explicit request? */ strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ /* do a plain search */ const char *s2 = lmemfind(s+init, l1-init, p, l2); if (s2) { lua_pushinteger(L, s2-s+1); lua_pushinteger(L, s2-s+l2); return 2; } } else { MatchState ms; int anchor = (*p == '^') ? (p++, 1) : 0; const char *s1=s+init; ms.L = L; ms.src_init = s; ms.src_end = s+l1; do { const char *res; ms.level = 0; if ((res=match(&ms, s1, p)) != NULL) { if (find) { lua_pushinteger(L, s1-s+1); /* start */ lua_pushinteger(L, res-s); /* end */ return push_captures(&ms, NULL, 0) + 2; } else return push_captures(&ms, s1, res); } } while (s1++ < ms.src_end && !anchor); } lua_pushnil(L); /* not found */ return 1; } static int str_find (lua_State *L) { return str_find_aux(L, 1); } static int str_match (lua_State *L) { return str_find_aux(L, 0); } static int gmatch_aux (lua_State *L) { MatchState ms; size_t ls; const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); const char *p = lua_tostring(L, lua_upvalueindex(2)); const char *src; ms.L = L; ms.src_init = s; ms.src_end = s+ls; for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); src <= ms.src_end; src++) { const char *e; ms.level = 0; if ((e = match(&ms, src, p)) != NULL) { lua_Integer newstart = e-s; if (e == src) newstart++; /* empty match? go at least one position */ lua_pushinteger(L, newstart); lua_replace(L, lua_upvalueindex(3)); return push_captures(&ms, src, e); } } return 0; /* not found */ } static int gmatch (lua_State *L) { luaL_checkstring(L, 1); luaL_checkstring(L, 2); lua_settop(L, 2); lua_pushinteger(L, 0); lua_pushcclosure(L, gmatch_aux, 3); return 1; } static int gfind_nodef (lua_State *L) { return luaL_error(L, LUA_QL("string.gfind") " was renamed to " LUA_QL("string.gmatch")); } static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, const char *e) { size_t l, i; const char *news = lua_tolstring(ms->L, 3, &l); for (i = 0; i < l; i++) { if (news[i] != L_ESC) luaL_addchar(b, news[i]); else { i++; /* skip ESC */ if (!isdigit(uchar(news[i]))) luaL_addchar(b, news[i]); else if (news[i] == '0') luaL_addlstring(b, s, e - s); else { push_onecapture(ms, news[i] - '1', s, e); luaL_addvalue(b); /* add capture to accumulated result */ } } } } static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, const char *e) { lua_State *L = ms->L; switch (lua_type(L, 3)) { case LUA_TNUMBER: case LUA_TSTRING: { add_s(ms, b, s, e); return; } case LUA_TFUNCTION: { int n; lua_pushvalue(L, 3); n = push_captures(ms, s, e); lua_call(L, n, 1); break; } case LUA_TTABLE: { push_onecapture(ms, 0, s, e); lua_gettable(L, 3); break; } } if (!lua_toboolean(L, -1)) { /* nil or false? */ lua_pop(L, 1); lua_pushlstring(L, s, e - s); /* keep original text */ } else if (!lua_isstring(L, -1)) luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); luaL_addvalue(b); /* add result to accumulator */ } static int str_gsub (lua_State *L) { size_t srcl; const char *src = luaL_checklstring(L, 1, &srcl); const char *p = luaL_checkstring(L, 2); int tr = lua_type(L, 3); int max_s = luaL_optint(L, 4, srcl+1); int anchor = (*p == '^') ? (p++, 1) : 0; int n = 0; MatchState ms; luaL_Buffer b; luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, "string/function/table expected"); luaL_buffinit(L, &b); ms.L = L; ms.src_init = src; ms.src_end = src+srcl; while (n < max_s) { const char *e; ms.level = 0; e = match(&ms, src, p); if (e) { n++; add_value(&ms, &b, src, e); } if (e && e>src) /* non empty match? */ src = e; /* skip it */ else if (src < ms.src_end) luaL_addchar(&b, *src++); else break; if (anchor) break; } luaL_addlstring(&b, src, ms.src_end-src); luaL_pushresult(&b); lua_pushinteger(L, n); /* number of substitutions */ return 2; } /* }====================================================== */ /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ #define MAX_ITEM 512 /* valid flags in a format specification */ #define FLAGS "-+ #0" /* ** maximum size of each format specification (such as '%-099.99d') ** (+10 accounts for %99.99x plus margin of error) */ #define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { size_t l; const char *s = luaL_checklstring(L, arg, &l); luaL_addchar(b, '"'); while (l--) { switch (*s) { case '"': case '\\': case '\n': { luaL_addchar(b, '\\'); luaL_addchar(b, *s); break; } case '\r': { luaL_addlstring(b, "\\r", 2); break; } case '\0': { luaL_addlstring(b, "\\000", 4); break; } default: { luaL_addchar(b, *s); break; } } s++; } luaL_addchar(b, '"'); } static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { const char *p = strfrmt; while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) luaL_error(L, "invalid format (repeated flags)"); if (isdigit(uchar(*p))) p++; /* skip width */ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ if (*p == '.') { p++; if (isdigit(uchar(*p))) p++; /* skip precision */ if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ } if (isdigit(uchar(*p))) luaL_error(L, "invalid format (width or precision too long)"); *(form++) = '%'; strncpy(form, strfrmt, p - strfrmt + 1); form += p - strfrmt + 1; *form = '\0'; return p; } static void addintlen (char *form) { size_t l = strlen(form); char spec = form[l - 1]; strcpy(form + l - 1, LUA_INTFRMLEN); form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; } static int str_format (lua_State *L) { int arg = 1; size_t sfl; const char *strfrmt = luaL_checklstring(L, arg, &sfl); const char *strfrmt_end = strfrmt+sfl; luaL_Buffer b; luaL_buffinit(L, &b); while (strfrmt < strfrmt_end) { if (*strfrmt != L_ESC) luaL_addchar(&b, *strfrmt++); else if (*++strfrmt == L_ESC) luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ char form[MAX_FORMAT]; /* to store the format (`%...') */ char buff[MAX_ITEM]; /* to store the formatted item */ arg++; strfrmt = scanformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { sprintf(buff, form, (int)luaL_checknumber(L, arg)); break; } case 'd': case 'i': { addintlen(form); sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); break; } case 'o': case 'u': case 'x': case 'X': { addintlen(form); sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); break; } case 'e': case 'E': case 'f': case 'g': case 'G': { sprintf(buff, form, (double)luaL_checknumber(L, arg)); break; } case 'q': { addquoted(L, &b, arg); continue; /* skip the 'addsize' at the end */ } case 's': { size_t l; const char *s = luaL_checklstring(L, arg, &l); if (!strchr(form, '.') && l >= 100) { /* no precision and string is too long to be formatted; keep original string */ lua_pushvalue(L, arg); luaL_addvalue(&b); continue; /* skip the `addsize' at the end */ } else { sprintf(buff, form, s); break; } } default: { /* also treat cases `pnLlh' */ return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " LUA_QL("format"), *(strfrmt - 1)); } } luaL_addlstring(&b, buff, strlen(buff)); } } luaL_pushresult(&b); return 1; } static const luaL_Reg strlib[] = { {"byte", str_byte}, {"char", str_char}, {"dump", str_dump}, {"find", str_find}, {"format", str_format}, {"gfind", gfind_nodef}, {"gmatch", gmatch}, {"gsub", str_gsub}, {"len", str_len}, {"lower", str_lower}, {"match", str_match}, {"rep", str_rep}, {"reverse", str_reverse}, {"sub", str_sub}, {"upper", str_upper}, {NULL, NULL} }; static void createmetatable (lua_State *L) { lua_createtable(L, 0, 1); /* create metatable for strings */ lua_pushliteral(L, ""); /* dummy string */ lua_pushvalue(L, -2); lua_setmetatable(L, -2); /* set string metatable */ lua_pop(L, 1); /* pop dummy string */ lua_pushvalue(L, -2); /* string library... */ lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ lua_pop(L, 1); /* pop metatable */ } /* ** Open string library */ LUALIB_API int luaopen_string (lua_State *L) { luaL_register(L, LUA_STRLIBNAME, strlib); #if defined(LUA_COMPAT_GFIND) lua_getfield(L, -1, "gmatch"); lua_setfield(L, -2, "gfind"); #endif createmetatable(L); return 1; } blobby-1.0rc3/src/lua/lualib.h0000644000175000017500000000200212042452374017531 0ustar danielknobedanielknobe/* ** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lua standard libraries ** See Copyright Notice in lua.h */ #ifndef lualib_h #define lualib_h #include "lua.h" /* Key to file-handle type */ #define LUA_FILEHANDLE "FILE*" #define LUA_COLIBNAME "coroutine" LUALIB_API int (luaopen_base) (lua_State *L); #define LUA_TABLIBNAME "table" LUALIB_API int (luaopen_table) (lua_State *L); #define LUA_IOLIBNAME "io" LUALIB_API int (luaopen_io) (lua_State *L); #define LUA_OSLIBNAME "os" LUALIB_API int (luaopen_os) (lua_State *L); #define LUA_STRLIBNAME "string" LUALIB_API int (luaopen_string) (lua_State *L); #define LUA_MATHLIBNAME "math" LUALIB_API int (luaopen_math) (lua_State *L); #define LUA_DBLIBNAME "debug" LUALIB_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUALIB_API int (luaopen_package) (lua_State *L); /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); #ifndef lua_assert #define lua_assert(x) ((void)0) #endif #endif blobby-1.0rc3/src/lua/ldo.c0000644000175000017500000003500312042452374017041 0ustar danielknobedanielknobe/* ** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #include #include #include #define ldo_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lundump.h" #include "lvm.h" #include "lzio.h" /* ** {====================================================== ** Error-recovery functions ** ======================================================= */ /* chain list of long jump buffers */ struct lua_longjmp { struct lua_longjmp *previous; luai_jmpbuf b; volatile int status; /* error code */ }; void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { switch (errcode) { case LUA_ERRMEM: { setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG)); break; } case LUA_ERRERR: { setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); break; } case LUA_ERRSYNTAX: case LUA_ERRRUN: { setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ break; } } L->top = oldtop + 1; } static void restore_stack_limit (lua_State *L) { lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ int inuse = cast_int(L->ci - L->base_ci); if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ luaD_reallocCI(L, LUAI_MAXCALLS); } } static void resetstack (lua_State *L, int status) { L->ci = L->base_ci; L->base = L->ci->base; luaF_close(L, L->base); /* close eventual pending closures */ luaD_seterrorobj(L, status, L->base); L->nCcalls = L->baseCcalls; L->allowhook = 1; restore_stack_limit(L); L->errfunc = 0; L->errorJmp = NULL; } void luaD_throw (lua_State *L, int errcode) { if (L->errorJmp) { L->errorJmp->status = errcode; LUAI_THROW(L, L->errorJmp); } else { L->status = cast_byte(errcode); if (G(L)->panic) { resetstack(L, errcode); lua_unlock(L); G(L)->panic(L); } exit(EXIT_FAILURE); } } int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { struct lua_longjmp lj; lj.status = 0; lj.previous = L->errorJmp; /* chain new error handler */ L->errorJmp = &lj; LUAI_TRY(L, &lj, (*f)(L, ud); ); L->errorJmp = lj.previous; /* restore old error handler */ return lj.status; } /* }====================================================== */ static void correctstack (lua_State *L, TValue *oldstack) { CallInfo *ci; GCObject *up; L->top = (L->top - oldstack) + L->stack; for (up = L->openupval; up != NULL; up = up->gch.next) gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; for (ci = L->base_ci; ci <= L->ci; ci++) { ci->top = (ci->top - oldstack) + L->stack; ci->base = (ci->base - oldstack) + L->stack; ci->func = (ci->func - oldstack) + L->stack; } L->base = (L->base - oldstack) + L->stack; } void luaD_reallocstack (lua_State *L, int newsize) { TValue *oldstack = L->stack; int realsize = newsize + 1 + EXTRA_STACK; lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue); L->stacksize = realsize; L->stack_last = L->stack+newsize; correctstack(L, oldstack); } void luaD_reallocCI (lua_State *L, int newsize) { CallInfo *oldci = L->base_ci; luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); L->size_ci = newsize; L->ci = (L->ci - oldci) + L->base_ci; L->end_ci = L->base_ci + L->size_ci - 1; } void luaD_growstack (lua_State *L, int n) { if (n <= L->stacksize) /* double size is enough? */ luaD_reallocstack(L, 2*L->stacksize); else luaD_reallocstack(L, L->stacksize + n); } static CallInfo *growCI (lua_State *L) { if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */ luaD_throw(L, LUA_ERRERR); else { luaD_reallocCI(L, 2*L->size_ci); if (L->size_ci > LUAI_MAXCALLS) luaG_runerror(L, "stack overflow"); } return ++L->ci; } void luaD_callhook (lua_State *L, int event, int line) { lua_Hook hook = L->hook; if (hook && L->allowhook) { ptrdiff_t top = savestack(L, L->top); ptrdiff_t ci_top = savestack(L, L->ci->top); lua_Debug ar; ar.event = event; ar.currentline = line; if (event == LUA_HOOKTAILRET) ar.i_ci = 0; /* tail call; no debug information about it */ else ar.i_ci = cast_int(L->ci - L->base_ci); luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ L->ci->top = L->top + LUA_MINSTACK; lua_assert(L->ci->top <= L->stack_last); L->allowhook = 0; /* cannot call hooks inside a hook */ lua_unlock(L); (*hook)(L, &ar); lua_lock(L); lua_assert(!L->allowhook); L->allowhook = 1; L->ci->top = restorestack(L, ci_top); L->top = restorestack(L, top); } } static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { int i; int nfixargs = p->numparams; Table *htab = NULL; StkId base, fixed; for (; actual < nfixargs; ++actual) setnilvalue(L->top++); #if defined(LUA_COMPAT_VARARG) if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */ int nvar = actual - nfixargs; /* number of extra arguments */ lua_assert(p->is_vararg & VARARG_HASARG); luaC_checkGC(L); htab = luaH_new(L, nvar, 1); /* create `arg' table */ for (i=0; itop - nvar + i); /* store counter in field `n' */ setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar)); } #endif /* move fixed parameters to final position */ fixed = L->top - actual; /* first fixed argument */ base = L->top; /* final position of first argument */ for (i=0; itop++, fixed+i); setnilvalue(fixed+i); } /* add `arg' parameter */ if (htab) { sethvalue(L, L->top++, htab); lua_assert(iswhite(obj2gco(htab))); } return base; } static StkId tryfuncTM (lua_State *L, StkId func) { const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); StkId p; ptrdiff_t funcr = savestack(L, func); if (!ttisfunction(tm)) luaG_typeerror(L, func, "call"); /* Open a hole inside the stack at `func' */ for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); incr_top(L); func = restorestack(L, funcr); /* previous call may change stack */ setobj2s(L, func, tm); /* tag method is the new function to be called */ return func; } #define inc_ci(L) \ ((L->ci == L->end_ci) ? growCI(L) : \ (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci)) int luaD_precall (lua_State *L, StkId func, int nresults) { LClosure *cl; ptrdiff_t funcr; if (!ttisfunction(func)) /* `func' is not a function? */ func = tryfuncTM(L, func); /* check the `function' tag method */ funcr = savestack(L, func); cl = &clvalue(func)->l; L->ci->savedpc = L->savedpc; if (!cl->isC) { /* Lua function? prepare its call */ CallInfo *ci; StkId st, base; Proto *p = cl->p; luaD_checkstack(L, p->maxstacksize); func = restorestack(L, funcr); if (!p->is_vararg) { /* no varargs? */ base = func + 1; if (L->top > base + p->numparams) L->top = base + p->numparams; } else { /* vararg function */ int nargs = cast_int(L->top - func) - 1; base = adjust_varargs(L, p, nargs); func = restorestack(L, funcr); /* previous call may change the stack */ } ci = inc_ci(L); /* now `enter' new function */ ci->func = func; L->base = ci->base = base; ci->top = L->base + p->maxstacksize; lua_assert(ci->top <= L->stack_last); L->savedpc = p->code; /* starting point */ ci->tailcalls = 0; ci->nresults = nresults; for (st = L->top; st < ci->top; st++) setnilvalue(st); L->top = ci->top; if (L->hookmask & LUA_MASKCALL) { L->savedpc++; /* hooks assume 'pc' is already incremented */ luaD_callhook(L, LUA_HOOKCALL, -1); L->savedpc--; /* correct 'pc' */ } return PCRLUA; } else { /* if is a C function, call it */ CallInfo *ci; int n; luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ ci = inc_ci(L); /* now `enter' new function */ ci->func = restorestack(L, funcr); L->base = ci->base = ci->func + 1; ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); ci->nresults = nresults; if (L->hookmask & LUA_MASKCALL) luaD_callhook(L, LUA_HOOKCALL, -1); lua_unlock(L); n = (*curr_func(L)->c.f)(L); /* do the actual call */ lua_lock(L); if (n < 0) /* yielding? */ return PCRYIELD; else { luaD_poscall(L, L->top - n); return PCRC; } } } static StkId callrethooks (lua_State *L, StkId firstResult) { ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ luaD_callhook(L, LUA_HOOKRET, -1); if (f_isLua(L->ci)) { /* Lua function? */ while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */ luaD_callhook(L, LUA_HOOKTAILRET, -1); } return restorestack(L, fr); } int luaD_poscall (lua_State *L, StkId firstResult) { StkId res; int wanted, i; CallInfo *ci; if (L->hookmask & LUA_MASKRET) firstResult = callrethooks(L, firstResult); ci = L->ci--; res = ci->func; /* res == final position of 1st result */ wanted = ci->nresults; L->base = (ci - 1)->base; /* restore base */ L->savedpc = (ci - 1)->savedpc; /* restore savedpc */ /* move results to correct place */ for (i = wanted; i != 0 && firstResult < L->top; i--) setobjs2s(L, res++, firstResult++); while (i-- > 0) setnilvalue(res++); L->top = res; return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ } /* ** Call a function (C or Lua). The function to be called is at *func. ** The arguments are on the stack, right after the function. ** When returns, all the results are on the stack, starting at the original ** function position. */ void luaD_call (lua_State *L, StkId func, int nResults) { if (++L->nCcalls >= LUAI_MAXCCALLS) { if (L->nCcalls == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ } if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ luaV_execute(L, 1); /* call it */ L->nCcalls--; luaC_checkGC(L); } static void resume (lua_State *L, void *ud) { StkId firstArg = cast(StkId, ud); CallInfo *ci = L->ci; if (L->status == 0) { /* start coroutine? */ lua_assert(ci == L->base_ci && firstArg > L->base); if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA) return; } else { /* resuming from previous yield */ lua_assert(L->status == LUA_YIELD); L->status = 0; if (!f_isLua(ci)) { /* `common' yield? */ /* finish interrupted execution of `OP_CALL' */ lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); if (luaD_poscall(L, firstArg)) /* complete it... */ L->top = L->ci->top; /* and correct top if not multiple results */ } else /* yielded inside a hook: just continue its execution */ L->base = L->ci->base; } luaV_execute(L, cast_int(L->ci - L->base_ci)); } static int resume_error (lua_State *L, const char *msg) { L->top = L->ci->base; setsvalue2s(L, L->top, luaS_new(L, msg)); incr_top(L); lua_unlock(L); return LUA_ERRRUN; } LUA_API int lua_resume (lua_State *L, int nargs) { int status; lua_lock(L); if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci)) return resume_error(L, "cannot resume non-suspended coroutine"); if (L->nCcalls >= LUAI_MAXCCALLS) return resume_error(L, "C stack overflow"); luai_userstateresume(L, nargs); lua_assert(L->errfunc == 0); L->baseCcalls = ++L->nCcalls; status = luaD_rawrunprotected(L, resume, L->top - nargs); if (status != 0) { /* error? */ L->status = cast_byte(status); /* mark thread as `dead' */ luaD_seterrorobj(L, status, L->top); L->ci->top = L->top; } else { lua_assert(L->nCcalls == L->baseCcalls); status = L->status; } --L->nCcalls; lua_unlock(L); return status; } LUA_API int lua_yield (lua_State *L, int nresults) { luai_userstateyield(L, nresults); lua_lock(L); if (L->nCcalls > L->baseCcalls) luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); L->base = L->top - nresults; /* protect stack slots below */ L->status = LUA_YIELD; lua_unlock(L); return -1; } int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { int status; unsigned short oldnCcalls = L->nCcalls; ptrdiff_t old_ci = saveci(L, L->ci); lu_byte old_allowhooks = L->allowhook; ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef; status = luaD_rawrunprotected(L, func, u); if (status != 0) { /* an error occurred? */ StkId oldtop = restorestack(L, old_top); luaF_close(L, oldtop); /* close eventual pending closures */ luaD_seterrorobj(L, status, oldtop); L->nCcalls = oldnCcalls; L->ci = restoreci(L, old_ci); L->base = L->ci->base; L->savedpc = L->ci->savedpc; L->allowhook = old_allowhooks; restore_stack_limit(L); } L->errfunc = old_errfunc; return status; } /* ** Execute a protected parser. */ struct SParser { /* data to `f_parser' */ ZIO *z; Mbuffer buff; /* buffer to be used by the scanner */ const char *name; }; static void f_parser (lua_State *L, void *ud) { int i; Proto *tf; Closure *cl; struct SParser *p = cast(struct SParser *, ud); int c = luaZ_lookahead(p->z); luaC_checkGC(L); tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, &p->buff, p->name); cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); cl->l.p = tf; for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ cl->l.upvals[i] = luaF_newupval(L); setclvalue(L, L->top, cl); incr_top(L); } int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { struct SParser p; int status; p.z = z; p.name = name; luaZ_initbuffer(L, &p.buff); status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); luaZ_freebuffer(L, &p.buff); return status; } blobby-1.0rc3/src/lua/ldump.c0000644000175000017500000000605212042452374017406 0ustar danielknobedanielknobe/* ** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ ** save precompiled Lua chunks ** See Copyright Notice in lua.h */ #include #define ldump_c #define LUA_CORE #include "lua.h" #include "lobject.h" #include "lstate.h" #include "lundump.h" typedef struct { lua_State* L; lua_Writer writer; void* data; int strip; int status; } DumpState; #define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) #define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) static void DumpBlock(const void* b, size_t size, DumpState* D) { if (D->status==0) { lua_unlock(D->L); D->status=(*D->writer)(D->L,b,size,D->data); lua_lock(D->L); } } static void DumpChar(int y, DumpState* D) { char x=(char)y; DumpVar(x,D); } static void DumpInt(int x, DumpState* D) { DumpVar(x,D); } static void DumpNumber(lua_Number x, DumpState* D) { DumpVar(x,D); } static void DumpVector(const void* b, int n, size_t size, DumpState* D) { DumpInt(n,D); DumpMem(b,n,size,D); } static void DumpString(const TString* s, DumpState* D) { if (s==NULL || getstr(s)==NULL) { size_t size=0; DumpVar(size,D); } else { size_t size=s->tsv.len+1; /* include trailing '\0' */ DumpVar(size,D); DumpBlock(getstr(s),size,D); } } #define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) static void DumpFunction(const Proto* f, const TString* p, DumpState* D); static void DumpConstants(const Proto* f, DumpState* D) { int i,n=f->sizek; DumpInt(n,D); for (i=0; ik[i]; DumpChar(ttype(o),D); switch (ttype(o)) { case LUA_TNIL: break; case LUA_TBOOLEAN: DumpChar(bvalue(o),D); break; case LUA_TNUMBER: DumpNumber(nvalue(o),D); break; case LUA_TSTRING: DumpString(rawtsvalue(o),D); break; default: lua_assert(0); /* cannot happen */ break; } } n=f->sizep; DumpInt(n,D); for (i=0; ip[i],f->source,D); } static void DumpDebug(const Proto* f, DumpState* D) { int i,n; n= (D->strip) ? 0 : f->sizelineinfo; DumpVector(f->lineinfo,n,sizeof(int),D); n= (D->strip) ? 0 : f->sizelocvars; DumpInt(n,D); for (i=0; ilocvars[i].varname,D); DumpInt(f->locvars[i].startpc,D); DumpInt(f->locvars[i].endpc,D); } n= (D->strip) ? 0 : f->sizeupvalues; DumpInt(n,D); for (i=0; iupvalues[i],D); } static void DumpFunction(const Proto* f, const TString* p, DumpState* D) { DumpString((f->source==p || D->strip) ? NULL : f->source,D); DumpInt(f->linedefined,D); DumpInt(f->lastlinedefined,D); DumpChar(f->nups,D); DumpChar(f->numparams,D); DumpChar(f->is_vararg,D); DumpChar(f->maxstacksize,D); DumpCode(f,D); DumpConstants(f,D); DumpDebug(f,D); } static void DumpHeader(DumpState* D) { char h[LUAC_HEADERSIZE]; luaU_header(h); DumpBlock(h,LUAC_HEADERSIZE,D); } /* ** dump Lua function as precompiled chunk */ int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) { DumpState D; D.L=L; D.writer=w; D.data=data; D.strip=strip; D.status=0; DumpHeader(&D); DumpFunction(f,NULL,&D); return D.status; } blobby-1.0rc3/src/lua/ldo.h0000644000175000017500000000355112042452374017051 0ustar danielknobedanielknobe/* ** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #ifndef ldo_h #define ldo_h #include "lobject.h" #include "lstate.h" #include "lzio.h" #define luaD_checkstack(L,n) \ if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ luaD_growstack(L, n); \ else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); #define incr_top(L) {luaD_checkstack(L,1); L->top++;} #define savestack(L,p) ((char *)(p) - (char *)L->stack) #define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) #define saveci(L,p) ((char *)(p) - (char *)L->base_ci) #define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n))) /* results from luaD_precall */ #define PCRLUA 0 /* initiated a call to a Lua function */ #define PCRC 1 /* did a call to a C function */ #define PCRYIELD 2 /* C funtion yielded */ /* type of protected functions, to be ran by `runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize); LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); LUAI_FUNC void luaD_growstack (lua_State *L, int n); LUAI_FUNC void luaD_throw (lua_State *L, int errcode); LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); #endif blobby-1.0rc3/src/lua/loslib.c0000644000175000017500000001355012042452374017552 0ustar danielknobedanielknobe/* ** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $ ** Standard Operating System library ** See Copyright Notice in lua.h */ #include #include #include #include #include #define loslib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" static int os_pushresult (lua_State *L, int i, const char *filename) { int en = errno; /* calls to Lua API may change this value */ if (i) { lua_pushboolean(L, 1); return 1; } else { lua_pushnil(L); lua_pushfstring(L, "%s: %s", filename, strerror(en)); lua_pushinteger(L, en); return 3; } } static int os_execute (lua_State *L) { lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); return 1; } static int os_remove (lua_State *L) { const char *filename = luaL_checkstring(L, 1); return os_pushresult(L, remove(filename) == 0, filename); } static int os_rename (lua_State *L) { const char *fromname = luaL_checkstring(L, 1); const char *toname = luaL_checkstring(L, 2); return os_pushresult(L, rename(fromname, toname) == 0, fromname); } static int os_tmpname (lua_State *L) { char buff[LUA_TMPNAMBUFSIZE]; int err; lua_tmpnam(buff, err); if (err) return luaL_error(L, "unable to generate a unique filename"); lua_pushstring(L, buff); return 1; } static int os_getenv (lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ return 1; } static int os_clock (lua_State *L) { lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); return 1; } /* ** {====================================================== ** Time/Date operations ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, ** wday=%w+1, yday=%j, isdst=? } ** ======================================================= */ static void setfield (lua_State *L, const char *key, int value) { lua_pushinteger(L, value); lua_setfield(L, -2, key); } static void setboolfield (lua_State *L, const char *key, int value) { if (value < 0) /* undefined? */ return; /* does not set field */ lua_pushboolean(L, value); lua_setfield(L, -2, key); } static int getboolfield (lua_State *L, const char *key) { int res; lua_getfield(L, -1, key); res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } static int getfield (lua_State *L, const char *key, int d) { int res; lua_getfield(L, -1, key); if (lua_isnumber(L, -1)) res = (int)lua_tointeger(L, -1); else { if (d < 0) return luaL_error(L, "field " LUA_QS " missing in date table", key); res = d; } lua_pop(L, 1); return res; } static int os_date (lua_State *L) { const char *s = luaL_optstring(L, 1, "%c"); time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); struct tm *stm; if (*s == '!') { /* UTC? */ stm = gmtime(&t); s++; /* skip `!' */ } else stm = localtime(&t); if (stm == NULL) /* invalid date? */ lua_pushnil(L); else if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setfield(L, "sec", stm->tm_sec); setfield(L, "min", stm->tm_min); setfield(L, "hour", stm->tm_hour); setfield(L, "day", stm->tm_mday); setfield(L, "month", stm->tm_mon+1); setfield(L, "year", stm->tm_year+1900); setfield(L, "wday", stm->tm_wday+1); setfield(L, "yday", stm->tm_yday+1); setboolfield(L, "isdst", stm->tm_isdst); } else { char cc[3]; luaL_Buffer b; cc[0] = '%'; cc[2] = '\0'; luaL_buffinit(L, &b); for (; *s; s++) { if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ luaL_addchar(&b, *s); else { size_t reslen; char buff[200]; /* should be big enough for any conversion result */ cc[1] = *(++s); reslen = strftime(buff, sizeof(buff), cc, stm); luaL_addlstring(&b, buff, reslen); } } luaL_pushresult(&b); } return 1; } static int os_time (lua_State *L) { time_t t; if (lua_isnoneornil(L, 1)) /* called without args? */ t = time(NULL); /* get current time */ else { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ ts.tm_sec = getfield(L, "sec", 0); ts.tm_min = getfield(L, "min", 0); ts.tm_hour = getfield(L, "hour", 12); ts.tm_mday = getfield(L, "day", -1); ts.tm_mon = getfield(L, "month", -1) - 1; ts.tm_year = getfield(L, "year", -1) - 1900; ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); } if (t == (time_t)(-1)) lua_pushnil(L); else lua_pushnumber(L, (lua_Number)t); return 1; } static int os_difftime (lua_State *L) { lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), (time_t)(luaL_optnumber(L, 2, 0)))); return 1; } /* }====================================================== */ static int os_setlocale (lua_State *L) { static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}; static const char *const catnames[] = {"all", "collate", "ctype", "monetary", "numeric", "time", NULL}; const char *l = luaL_optstring(L, 1, NULL); int op = luaL_checkoption(L, 2, "all", catnames); lua_pushstring(L, setlocale(cat[op], l)); return 1; } static int os_exit (lua_State *L) { exit(luaL_optint(L, 1, EXIT_SUCCESS)); } static const luaL_Reg syslib[] = { {"clock", os_clock}, {"date", os_date}, {"difftime", os_difftime}, {"execute", os_execute}, {"exit", os_exit}, {"getenv", os_getenv}, {"remove", os_remove}, {"rename", os_rename}, {"setlocale", os_setlocale}, {"time", os_time}, {"tmpname", os_tmpname}, {NULL, NULL} }; /* }====================================================== */ LUALIB_API int luaopen_os (lua_State *L) { luaL_register(L, LUA_OSLIBNAME, syslib); return 1; } blobby-1.0rc3/src/lua/lundump.c0000644000175000017500000001102512042452374017745 0ustar danielknobedanielknobe/* ** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ #include #define lundump_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lmem.h" #include "lobject.h" #include "lstring.h" #include "lundump.h" #include "lzio.h" typedef struct { lua_State* L; ZIO* Z; Mbuffer* b; const char* name; } LoadState; #ifdef LUAC_TRUST_BINARIES #define IF(c,s) #define error(S,s) #else #define IF(c,s) if (c) error(S,s) static void error(LoadState* S, const char* why) { luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); luaD_throw(S->L,LUA_ERRSYNTAX); } #endif #define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) #define LoadByte(S) (lu_byte)LoadChar(S) #define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) #define LoadVector(S,b,n,size) LoadMem(S,b,n,size) static void LoadBlock(LoadState* S, void* b, size_t size) { size_t r=luaZ_read(S->Z,b,size); IF (r!=0, "unexpected end"); } static int LoadChar(LoadState* S) { char x; LoadVar(S,x); return x; } static int LoadInt(LoadState* S) { int x; LoadVar(S,x); IF (x<0, "bad integer"); return x; } static lua_Number LoadNumber(LoadState* S) { lua_Number x; LoadVar(S,x); return x; } static TString* LoadString(LoadState* S) { size_t size; LoadVar(S,size); if (size==0) return NULL; else { char* s=luaZ_openspace(S->L,S->b,size); LoadBlock(S,s,size); return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ } } static void LoadCode(LoadState* S, Proto* f) { int n=LoadInt(S); f->code=luaM_newvector(S->L,n,Instruction); f->sizecode=n; LoadVector(S,f->code,n,sizeof(Instruction)); } static Proto* LoadFunction(LoadState* S, TString* p); static void LoadConstants(LoadState* S, Proto* f) { int i,n; n=LoadInt(S); f->k=luaM_newvector(S->L,n,TValue); f->sizek=n; for (i=0; ik[i]); for (i=0; ik[i]; int t=LoadChar(S); switch (t) { case LUA_TNIL: setnilvalue(o); break; case LUA_TBOOLEAN: setbvalue(o,LoadChar(S)!=0); break; case LUA_TNUMBER: setnvalue(o,LoadNumber(S)); break; case LUA_TSTRING: setsvalue2n(S->L,o,LoadString(S)); break; default: error(S,"bad constant"); break; } } n=LoadInt(S); f->p=luaM_newvector(S->L,n,Proto*); f->sizep=n; for (i=0; ip[i]=NULL; for (i=0; ip[i]=LoadFunction(S,f->source); } static void LoadDebug(LoadState* S, Proto* f) { int i,n; n=LoadInt(S); f->lineinfo=luaM_newvector(S->L,n,int); f->sizelineinfo=n; LoadVector(S,f->lineinfo,n,sizeof(int)); n=LoadInt(S); f->locvars=luaM_newvector(S->L,n,LocVar); f->sizelocvars=n; for (i=0; ilocvars[i].varname=NULL; for (i=0; ilocvars[i].varname=LoadString(S); f->locvars[i].startpc=LoadInt(S); f->locvars[i].endpc=LoadInt(S); } n=LoadInt(S); f->upvalues=luaM_newvector(S->L,n,TString*); f->sizeupvalues=n; for (i=0; iupvalues[i]=NULL; for (i=0; iupvalues[i]=LoadString(S); } static Proto* LoadFunction(LoadState* S, TString* p) { Proto* f; if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep"); f=luaF_newproto(S->L); setptvalue2s(S->L,S->L->top,f); incr_top(S->L); f->source=LoadString(S); if (f->source==NULL) f->source=p; f->linedefined=LoadInt(S); f->lastlinedefined=LoadInt(S); f->nups=LoadByte(S); f->numparams=LoadByte(S); f->is_vararg=LoadByte(S); f->maxstacksize=LoadByte(S); LoadCode(S,f); LoadConstants(S,f); LoadDebug(S,f); IF (!luaG_checkcode(f), "bad code"); S->L->top--; S->L->nCcalls--; return f; } static void LoadHeader(LoadState* S) { char h[LUAC_HEADERSIZE]; char s[LUAC_HEADERSIZE]; luaU_header(h); LoadBlock(S,s,LUAC_HEADERSIZE); IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); } /* ** load precompiled chunk */ Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) { LoadState S; if (*name=='@' || *name=='=') S.name=name+1; else if (*name==LUA_SIGNATURE[0]) S.name="binary string"; else S.name=name; S.L=L; S.Z=Z; S.b=buff; LoadHeader(&S); return LoadFunction(&S,luaS_newliteral(L,"=?")); } /* * make header */ void luaU_header (char* h) { int x=1; memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); h+=sizeof(LUA_SIGNATURE)-1; *h++=(char)LUAC_VERSION; *h++=(char)LUAC_FORMAT; *h++=(char)*(char*)&x; /* endianness */ *h++=(char)sizeof(int); *h++=(char)sizeof(size_t); *h++=(char)sizeof(Instruction); *h++=(char)sizeof(lua_Number); *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */ } blobby-1.0rc3/src/lua/ldblib.c0000644000175000017500000002352512042452374017521 0ustar danielknobedanielknobe/* ** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ #include #include #include #define ldblib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" static int db_getregistry (lua_State *L) { lua_pushvalue(L, LUA_REGISTRYINDEX); return 1; } static int db_getmetatable (lua_State *L) { luaL_checkany(L, 1); if (!lua_getmetatable(L, 1)) { lua_pushnil(L); /* no metatable */ } return 1; } static int db_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table expected"); lua_settop(L, 2); lua_pushboolean(L, lua_setmetatable(L, 1)); return 1; } static int db_getfenv (lua_State *L) { lua_getfenv(L, 1); return 1; } static int db_setfenv (lua_State *L) { luaL_checktype(L, 2, LUA_TTABLE); lua_settop(L, 2); if (lua_setfenv(L, 1) == 0) luaL_error(L, LUA_QL("setfenv") " cannot change environment of given object"); return 1; } static void settabss (lua_State *L, const char *i, const char *v) { lua_pushstring(L, v); lua_setfield(L, -2, i); } static void settabsi (lua_State *L, const char *i, int v) { lua_pushinteger(L, v); lua_setfield(L, -2, i); } static lua_State *getthread (lua_State *L, int *arg) { if (lua_isthread(L, 1)) { *arg = 1; return lua_tothread(L, 1); } else { *arg = 0; return L; } } static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { if (L == L1) { lua_pushvalue(L, -2); lua_remove(L, -3); } else lua_xmove(L1, L, 1); lua_setfield(L, -2, fname); } static int db_getinfo (lua_State *L) { lua_Debug ar; int arg; lua_State *L1 = getthread(L, &arg); const char *options = luaL_optstring(L, arg+2, "flnSu"); if (lua_isnumber(L, arg+1)) { if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { lua_pushnil(L); /* level out of range */ return 1; } } else if (lua_isfunction(L, arg+1)) { lua_pushfstring(L, ">%s", options); options = lua_tostring(L, -1); lua_pushvalue(L, arg+1); lua_xmove(L, L1, 1); } else return luaL_argerror(L, arg+1, "function or level expected"); if (!lua_getinfo(L1, options, &ar)) return luaL_argerror(L, arg+2, "invalid option"); lua_createtable(L, 0, 2); if (strchr(options, 'S')) { settabss(L, "source", ar.source); settabss(L, "short_src", ar.short_src); settabsi(L, "linedefined", ar.linedefined); settabsi(L, "lastlinedefined", ar.lastlinedefined); settabss(L, "what", ar.what); } if (strchr(options, 'l')) settabsi(L, "currentline", ar.currentline); if (strchr(options, 'u')) settabsi(L, "nups", ar.nups); if (strchr(options, 'n')) { settabss(L, "name", ar.name); settabss(L, "namewhat", ar.namewhat); } if (strchr(options, 'L')) treatstackoption(L, L1, "activelines"); if (strchr(options, 'f')) treatstackoption(L, L1, "func"); return 1; /* return table */ } static int db_getlocal (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); lua_Debug ar; const char *name; if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); if (name) { lua_xmove(L1, L, 1); lua_pushstring(L, name); lua_pushvalue(L, -2); return 2; } else { lua_pushnil(L); return 1; } } static int db_setlocal (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); lua_Debug ar; if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); luaL_checkany(L, arg+3); lua_settop(L, arg+3); lua_xmove(L, L1, 1); lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); return 1; } static int auxupvalue (lua_State *L, int get) { const char *name; int n = luaL_checkint(L, 2); luaL_checktype(L, 1, LUA_TFUNCTION); if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); if (name == NULL) return 0; lua_pushstring(L, name); lua_insert(L, -(get+1)); return get + 1; } static int db_getupvalue (lua_State *L) { return auxupvalue(L, 1); } static int db_setupvalue (lua_State *L) { luaL_checkany(L, 3); return auxupvalue(L, 0); } static const char KEY_HOOK = 'h'; static void hookf (lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = {"call", "return", "line", "count", "tail return"}; lua_pushlightuserdata(L, (void *)&KEY_HOOK); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushlightuserdata(L, L); lua_rawget(L, -2); if (lua_isfunction(L, -1)) { lua_pushstring(L, hooknames[(int)ar->event]); if (ar->currentline >= 0) lua_pushinteger(L, ar->currentline); else lua_pushnil(L); lua_assert(lua_getinfo(L, "lS", ar)); lua_call(L, 2, 0); } } static int makemask (const char *smask, int count) { int mask = 0; if (strchr(smask, 'c')) mask |= LUA_MASKCALL; if (strchr(smask, 'r')) mask |= LUA_MASKRET; if (strchr(smask, 'l')) mask |= LUA_MASKLINE; if (count > 0) mask |= LUA_MASKCOUNT; return mask; } static char *unmakemask (int mask, char *smask) { int i = 0; if (mask & LUA_MASKCALL) smask[i++] = 'c'; if (mask & LUA_MASKRET) smask[i++] = 'r'; if (mask & LUA_MASKLINE) smask[i++] = 'l'; smask[i] = '\0'; return smask; } static void gethooktable (lua_State *L) { lua_pushlightuserdata(L, (void *)&KEY_HOOK); lua_rawget(L, LUA_REGISTRYINDEX); if (!lua_istable(L, -1)) { lua_pop(L, 1); lua_createtable(L, 0, 1); lua_pushlightuserdata(L, (void *)&KEY_HOOK); lua_pushvalue(L, -2); lua_rawset(L, LUA_REGISTRYINDEX); } } static int db_sethook (lua_State *L) { int arg, mask, count; lua_Hook func; lua_State *L1 = getthread(L, &arg); if (lua_isnoneornil(L, arg+1)) { lua_settop(L, arg+1); func = NULL; mask = 0; count = 0; /* turn off hooks */ } else { const char *smask = luaL_checkstring(L, arg+2); luaL_checktype(L, arg+1, LUA_TFUNCTION); count = luaL_optint(L, arg+3, 0); func = hookf; mask = makemask(smask, count); } gethooktable(L); lua_pushlightuserdata(L, L1); lua_pushvalue(L, arg+1); lua_rawset(L, -3); /* set new hook */ lua_pop(L, 1); /* remove hook table */ lua_sethook(L1, func, mask, count); /* set hooks */ return 0; } static int db_gethook (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); char buff[5]; int mask = lua_gethookmask(L1); lua_Hook hook = lua_gethook(L1); if (hook != NULL && hook != hookf) /* external hook? */ lua_pushliteral(L, "external hook"); else { gethooktable(L); lua_pushlightuserdata(L, L1); lua_rawget(L, -2); /* get hook */ lua_remove(L, -2); /* remove hook table */ } lua_pushstring(L, unmakemask(mask, buff)); lua_pushinteger(L, lua_gethookcount(L1)); return 3; } static int db_debug (lua_State *L) { for (;;) { char buffer[250]; fputs("lua_debug> ", stderr); if (fgets(buffer, sizeof(buffer), stdin) == 0 || strcmp(buffer, "cont\n") == 0) return 0; if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0)) { fputs(lua_tostring(L, -1), stderr); fputs("\n", stderr); } lua_settop(L, 0); /* remove eventual returns */ } } #define LEVELS1 12 /* size of the first part of the stack */ #define LEVELS2 10 /* size of the second part of the stack */ static int db_errorfb (lua_State *L) { int level; int firstpart = 1; /* still before eventual `...' */ int arg; lua_State *L1 = getthread(L, &arg); lua_Debug ar; if (lua_isnumber(L, arg+2)) { level = (int)lua_tointeger(L, arg+2); lua_pop(L, 1); } else level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ if (lua_gettop(L) == arg) lua_pushliteral(L, ""); else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ else lua_pushliteral(L, "\n"); lua_pushliteral(L, "stack traceback:"); while (lua_getstack(L1, level++, &ar)) { if (level > LEVELS1 && firstpart) { /* no more than `LEVELS2' more levels? */ if (!lua_getstack(L1, level+LEVELS2, &ar)) level--; /* keep going */ else { lua_pushliteral(L, "\n\t..."); /* too many levels */ while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ level++; } firstpart = 0; continue; } lua_pushliteral(L, "\n\t"); lua_getinfo(L1, "Snl", &ar); lua_pushfstring(L, "%s:", ar.short_src); if (ar.currentline > 0) lua_pushfstring(L, "%d:", ar.currentline); if (*ar.namewhat != '\0') /* is there a name? */ lua_pushfstring(L, " in function " LUA_QS, ar.name); else { if (*ar.what == 'm') /* main? */ lua_pushfstring(L, " in main chunk"); else if (*ar.what == 'C' || *ar.what == 't') lua_pushliteral(L, " ?"); /* C function or tail call */ else lua_pushfstring(L, " in function <%s:%d>", ar.short_src, ar.linedefined); } lua_concat(L, lua_gettop(L) - arg); } lua_concat(L, lua_gettop(L) - arg); return 1; } static const luaL_Reg dblib[] = { {"debug", db_debug}, {"getfenv", db_getfenv}, {"gethook", db_gethook}, {"getinfo", db_getinfo}, {"getlocal", db_getlocal}, {"getregistry", db_getregistry}, {"getmetatable", db_getmetatable}, {"getupvalue", db_getupvalue}, {"setfenv", db_setfenv}, {"sethook", db_sethook}, {"setlocal", db_setlocal}, {"setmetatable", db_setmetatable}, {"setupvalue", db_setupvalue}, {"traceback", db_errorfb}, {NULL, NULL} }; LUALIB_API int luaopen_debug (lua_State *L) { luaL_register(L, LUA_DBLIBNAME, dblib); return 1; } blobby-1.0rc3/src/lua/lundump.h0000644000175000017500000000157212042452374017760 0ustar danielknobedanielknobe/* ** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ #ifndef lundump_h #define lundump_h #include "lobject.h" #include "lzio.h" /* load one chunk; from lundump.c */ LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); /* make header; from lundump.c */ LUAI_FUNC void luaU_header (char* h); /* dump one chunk; from ldump.c */ LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); #ifdef luac_c /* print one chunk; from print.c */ LUAI_FUNC void luaU_print (const Proto* f, int full); #endif /* for header of binary files -- this is Lua 5.1 */ #define LUAC_VERSION 0x51 /* for header of binary files -- this is the official format */ #define LUAC_FORMAT 0 /* size of header of binary files */ #define LUAC_HEADERSIZE 12 #endif blobby-1.0rc3/src/lua/lmem.c0000644000175000017500000000417412042452374017222 0ustar danielknobedanielknobe/* ** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #include #define lmem_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" /* ** About the realloc function: ** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); ** (`osize' is the old size, `nsize' is the new size) ** ** Lua ensures that (ptr == NULL) iff (osize == 0). ** ** * frealloc(ud, NULL, 0, x) creates a new block of size `x' ** ** * frealloc(ud, p, x, 0) frees the block `p' ** (in this specific case, frealloc must return NULL). ** particularly, frealloc(ud, NULL, 0, 0) does nothing ** (which is equivalent to free(NULL) in ANSI C) ** ** frealloc returns NULL if it cannot create or reallocate the area ** (any reallocation to an equal or smaller size cannot fail!) */ #define MINSIZEARRAY 4 void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, int limit, const char *errormsg) { void *newblock; int newsize; if (*size >= limit/2) { /* cannot double it? */ if (*size >= limit) /* cannot grow even a little? */ luaG_runerror(L, errormsg); newsize = limit; /* still have at least one free place */ } else { newsize = (*size)*2; if (newsize < MINSIZEARRAY) newsize = MINSIZEARRAY; /* minimum size */ } newblock = luaM_reallocv(L, block, *size, newsize, size_elems); *size = newsize; /* update only when everything else is OK */ return newblock; } void *luaM_toobig (lua_State *L) { luaG_runerror(L, "memory allocation error: block too big"); return NULL; /* to avoid warnings */ } /* ** generic allocation routine. */ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); block = (*g->frealloc)(g->ud, block, osize, nsize); if (block == NULL && nsize > 0) luaD_throw(L, LUA_ERRMEM); lua_assert((nsize == 0) == (block == NULL)); g->totalbytes = (g->totalbytes - osize) + nsize; return block; } blobby-1.0rc3/src/lua/ltm.c0000644000175000017500000000316212042452374017060 0ustar danielknobedanielknobe/* ** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ #include #define ltm_c #define LUA_CORE #include "lua.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" const char *const luaT_typenames[] = { "nil", "boolean", "userdata", "number", "string", "table", "function", "userdata", "thread", "proto", "upval" }; void luaT_init (lua_State *L) { static const char *const luaT_eventname[] = { /* ORDER TM */ "__index", "__newindex", "__gc", "__mode", "__eq", "__add", "__sub", "__mul", "__div", "__mod", "__pow", "__unm", "__len", "__lt", "__le", "__concat", "__call" }; int i; for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); luaS_fix(G(L)->tmname[i]); /* never collect these names */ } } /* ** function to be used with macro "fasttm": optimized for absence of ** tag methods */ const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { const TValue *tm = luaH_getstr(events, ename); lua_assert(event <= TM_EQ); if (ttisnil(tm)) { /* no tag method? */ events->flags |= cast_byte(1u<metatable; break; case LUA_TUSERDATA: mt = uvalue(o)->metatable; break; default: mt = G(L)->mt[ttype(o)]; } return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); } blobby-1.0rc3/src/lua/lstate.c0000644000175000017500000001305212042452374017557 0ustar danielknobedanielknobe/* ** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ #include #define lstate_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "llex.h" #include "lmem.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #define state_size(x) (sizeof(x) + LUAI_EXTRASPACE) #define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE) #define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE)) /* ** Main thread combines a thread state and the global state */ typedef struct LG { lua_State l; global_State g; } LG; static void stack_init (lua_State *L1, lua_State *L) { /* initialize CallInfo array */ L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo); L1->ci = L1->base_ci; L1->size_ci = BASIC_CI_SIZE; L1->end_ci = L1->base_ci + L1->size_ci - 1; /* initialize stack array */ L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue); L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; L1->top = L1->stack; L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1; /* initialize first ci */ L1->ci->func = L1->top; setnilvalue(L1->top++); /* `function' entry for this `ci' */ L1->base = L1->ci->base = L1->top; L1->ci->top = L1->top + LUA_MINSTACK; } static void freestack (lua_State *L, lua_State *L1) { luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo); luaM_freearray(L, L1->stack, L1->stacksize, TValue); } /* ** open parts that may cause memory-allocation errors */ static void f_luaopen (lua_State *L, void *ud) { global_State *g = G(L); UNUSED(ud); stack_init(L, L); /* init stack */ sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ luaT_init(L); luaX_init(L); luaS_fix(luaS_newliteral(L, MEMERRMSG)); g->GCthreshold = 4*g->totalbytes; } static void preinit_state (lua_State *L, global_State *g) { G(L) = g; L->stack = NULL; L->stacksize = 0; L->errorJmp = NULL; L->hook = NULL; L->hookmask = 0; L->basehookcount = 0; L->allowhook = 1; resethookcount(L); L->openupval = NULL; L->size_ci = 0; L->nCcalls = L->baseCcalls = 0; L->status = 0; L->base_ci = L->ci = NULL; L->savedpc = NULL; L->errfunc = 0; setnilvalue(gt(L)); } static void close_state (lua_State *L) { global_State *g = G(L); luaF_close(L, L->stack); /* close all upvalues for this thread */ luaC_freeall(L); /* collect all objects */ lua_assert(g->rootgc == obj2gco(L)); lua_assert(g->strt.nuse == 0); luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); luaZ_freebuffer(L, &g->buff); freestack(L, L); lua_assert(g->totalbytes == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0); } lua_State *luaE_newthread (lua_State *L) { lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); luaC_link(L, obj2gco(L1), LUA_TTHREAD); preinit_state(L1, G(L)); stack_init(L1, L); /* init stack */ setobj2n(L, gt(L1), gt(L)); /* share table of globals */ L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); lua_assert(iswhite(obj2gco(L1))); return L1; } void luaE_freethread (lua_State *L, lua_State *L1) { luaF_close(L1, L1->stack); /* close all upvalues for this thread */ lua_assert(L1->openupval == NULL); luai_userstatefree(L1); freestack(L, L1); luaM_freemem(L, fromstate(L1), state_size(lua_State)); } LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { int i; lua_State *L; global_State *g; void *l = (*f)(ud, NULL, 0, state_size(LG)); if (l == NULL) return NULL; L = tostate(l); g = &((LG *)L)->g; L->next = NULL; L->tt = LUA_TTHREAD; g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); L->marked = luaC_white(g); set2bits(L->marked, FIXEDBIT, SFIXEDBIT); preinit_state(L, g); g->frealloc = f; g->ud = ud; g->mainthread = L; g->uvhead.u.l.prev = &g->uvhead; g->uvhead.u.l.next = &g->uvhead; g->GCthreshold = 0; /* mark it as unfinished state */ g->strt.size = 0; g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(registry(L)); luaZ_initbuffer(L, &g->buff); g->panic = NULL; g->gcstate = GCSpause; g->rootgc = obj2gco(L); g->sweepstrgc = 0; g->sweepgc = &g->rootgc; g->gray = NULL; g->grayagain = NULL; g->weak = NULL; g->tmudata = NULL; g->totalbytes = sizeof(LG); g->gcpause = LUAI_GCPAUSE; g->gcstepmul = LUAI_GCMUL; g->gcdept = 0; for (i=0; imt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { /* memory allocation error: free partial state */ close_state(L); L = NULL; } else luai_userstateopen(L); return L; } static void callallgcTM (lua_State *L, void *ud) { UNUSED(ud); luaC_callGCTM(L); /* call GC metamethods for all udata */ } LUA_API void lua_close (lua_State *L) { L = G(L)->mainthread; /* only the main thread can be closed */ lua_lock(L); luaF_close(L, L->stack); /* close all upvalues for this thread */ luaC_separateudata(L, 1); /* separate udata that have GC metamethods */ L->errfunc = 0; /* no error function during GC metamethods */ do { /* repeat until no more errors */ L->ci = L->base_ci; L->base = L->top = L->ci->base; L->nCcalls = L->baseCcalls = 0; } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); lua_assert(G(L)->tmudata == NULL); luai_userstateclose(L); close_state(L); } blobby-1.0rc3/src/lua/CMakeLists.txt0000644000175000017500000000103012042452374020650 0ustar danielknobedanielknobeset (lua_SRC lapi.c lapi.h lauxlib.c lauxlib.h lbaselib.c lcode.c lcode.h ldblib.c ldebug.c ldebug.h ldo.c ldo.h ldump.c lfunc.c lfunc.h lgc.c lgc.h linit.c liolib.c llex.c llex.h llimits.h lmathlib.c lmem.c lmem.h loadlib.c lobject.c lobject.h lopcodes.c lopcodes.h loslib.c lparser.c lparser.h lstate.c lstate.h lstring.c lstring.h lstrlib.c ltable.c ltable.h ltablib.c ltm.c ltm.h luaconf.h lua.h lualib.h lundump.c lundump.h lvm.c lvm.h lzio.c lzio.h print.c ) add_library(lua STATIC ${lua_SRC}) blobby-1.0rc3/src/lua/lmathlib.c0000644000175000017500000001330712042452374020062 0ustar danielknobedanielknobe/* ** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $ ** Standard mathematical library ** See Copyright Notice in lua.h */ #include #include #define lmathlib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" #undef PI #define PI (3.14159265358979323846) #define RADIANS_PER_DEGREE (PI/180.0) static int math_abs (lua_State *L) { lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); return 1; } static int math_sin (lua_State *L) { lua_pushnumber(L, sin(luaL_checknumber(L, 1))); return 1; } static int math_sinh (lua_State *L) { lua_pushnumber(L, sinh(luaL_checknumber(L, 1))); return 1; } static int math_cos (lua_State *L) { lua_pushnumber(L, cos(luaL_checknumber(L, 1))); return 1; } static int math_cosh (lua_State *L) { lua_pushnumber(L, cosh(luaL_checknumber(L, 1))); return 1; } static int math_tan (lua_State *L) { lua_pushnumber(L, tan(luaL_checknumber(L, 1))); return 1; } static int math_tanh (lua_State *L) { lua_pushnumber(L, tanh(luaL_checknumber(L, 1))); return 1; } static int math_asin (lua_State *L) { lua_pushnumber(L, asin(luaL_checknumber(L, 1))); return 1; } static int math_acos (lua_State *L) { lua_pushnumber(L, acos(luaL_checknumber(L, 1))); return 1; } static int math_atan (lua_State *L) { lua_pushnumber(L, atan(luaL_checknumber(L, 1))); return 1; } static int math_atan2 (lua_State *L) { lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1; } static int math_ceil (lua_State *L) { lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); return 1; } static int math_floor (lua_State *L) { lua_pushnumber(L, floor(luaL_checknumber(L, 1))); return 1; } static int math_fmod (lua_State *L) { lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1; } static int math_modf (lua_State *L) { double ip; double fp = modf(luaL_checknumber(L, 1), &ip); lua_pushnumber(L, ip); lua_pushnumber(L, fp); return 2; } static int math_sqrt (lua_State *L) { lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); return 1; } static int math_pow (lua_State *L) { lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1; } static int math_log (lua_State *L) { lua_pushnumber(L, log(luaL_checknumber(L, 1))); return 1; } static int math_log10 (lua_State *L) { lua_pushnumber(L, log10(luaL_checknumber(L, 1))); return 1; } static int math_exp (lua_State *L) { lua_pushnumber(L, exp(luaL_checknumber(L, 1))); return 1; } static int math_deg (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); return 1; } static int math_rad (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); return 1; } static int math_frexp (lua_State *L) { int e; lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); lua_pushinteger(L, e); return 2; } static int math_ldexp (lua_State *L) { lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); return 1; } static int math_min (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ lua_Number dmin = luaL_checknumber(L, 1); int i; for (i=2; i<=n; i++) { lua_Number d = luaL_checknumber(L, i); if (d < dmin) dmin = d; } lua_pushnumber(L, dmin); return 1; } static int math_max (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ lua_Number dmax = luaL_checknumber(L, 1); int i; for (i=2; i<=n; i++) { lua_Number d = luaL_checknumber(L, i); if (d > dmax) dmax = d; } lua_pushnumber(L, dmax); return 1; } static int math_random (lua_State *L) { /* the `%' avoids the (rare) case of r==1, and is needed also because on some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; switch (lua_gettop(L)) { /* check number of arguments */ case 0: { /* no arguments */ lua_pushnumber(L, r); /* Number between 0 and 1 */ break; } case 1: { /* only upper limit */ int u = luaL_checkint(L, 1); luaL_argcheck(L, 1<=u, 1, "interval is empty"); lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ break; } case 2: { /* lower and upper limits */ int l = luaL_checkint(L, 1); int u = luaL_checkint(L, 2); luaL_argcheck(L, l<=u, 2, "interval is empty"); lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ break; } default: return luaL_error(L, "wrong number of arguments"); } return 1; } static int math_randomseed (lua_State *L) { srand(luaL_checkint(L, 1)); return 0; } static const luaL_Reg mathlib[] = { {"abs", math_abs}, {"acos", math_acos}, {"asin", math_asin}, {"atan2", math_atan2}, {"atan", math_atan}, {"ceil", math_ceil}, {"cosh", math_cosh}, {"cos", math_cos}, {"deg", math_deg}, {"exp", math_exp}, {"floor", math_floor}, {"fmod", math_fmod}, {"frexp", math_frexp}, {"ldexp", math_ldexp}, {"log10", math_log10}, {"log", math_log}, {"max", math_max}, {"min", math_min}, {"modf", math_modf}, {"pow", math_pow}, {"rad", math_rad}, {"random", math_random}, {"randomseed", math_randomseed}, {"sinh", math_sinh}, {"sin", math_sin}, {"sqrt", math_sqrt}, {"tanh", math_tanh}, {"tan", math_tan}, {NULL, NULL} }; /* ** Open math library */ LUALIB_API int luaopen_math (lua_State *L) { luaL_register(L, LUA_MATHLIBNAME, mathlib); lua_pushnumber(L, PI); lua_setfield(L, -2, "pi"); lua_pushnumber(L, HUGE_VAL); lua_setfield(L, -2, "huge"); #if defined(LUA_COMPAT_MOD) lua_getfield(L, -1, "fmod"); lua_setfield(L, -2, "mod"); #endif return 1; } blobby-1.0rc3/src/lua/lvm.c0000644000175000017500000005512612042452374017071 0ustar danielknobedanielknobe/* ** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #include #include #include #define lvm_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" /* limit for table tag-method chains (to avoid loops) */ #define MAXTAGLOOP 100 const TValue *luaV_tonumber (const TValue *obj, TValue *n) { lua_Number num; if (ttisnumber(obj)) return obj; if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { setnvalue(n, num); return n; } else return NULL; } int luaV_tostring (lua_State *L, StkId obj) { if (!ttisnumber(obj)) return 0; else { char s[LUAI_MAXNUMBER2STR]; lua_Number n = nvalue(obj); lua_number2str(s, n); setsvalue2s(L, obj, luaS_new(L, s)); return 1; } } static void traceexec (lua_State *L, const Instruction *pc) { lu_byte mask = L->hookmask; const Instruction *oldpc = L->savedpc; L->savedpc = pc; if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { resethookcount(L); luaD_callhook(L, LUA_HOOKCOUNT, -1); } if (mask & LUA_MASKLINE) { Proto *p = ci_func(L->ci)->l.p; int npc = pcRel(pc, p); int newline = getline(p, npc); /* call linehook when enter a new function, when jump back (loop), or when enter a new line */ if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) luaD_callhook(L, LUA_HOOKLINE, newline); } } static void callTMres (lua_State *L, StkId res, const TValue *f, const TValue *p1, const TValue *p2) { ptrdiff_t result = savestack(L, res); setobj2s(L, L->top, f); /* push function */ setobj2s(L, L->top+1, p1); /* 1st argument */ setobj2s(L, L->top+2, p2); /* 2nd argument */ luaD_checkstack(L, 3); L->top += 3; luaD_call(L, L->top - 3, 1); res = restorestack(L, result); L->top--; setobjs2s(L, res, L->top); } static void callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, const TValue *p3) { setobj2s(L, L->top, f); /* push function */ setobj2s(L, L->top+1, p1); /* 1st argument */ setobj2s(L, L->top+2, p2); /* 2nd argument */ setobj2s(L, L->top+3, p3); /* 3th argument */ luaD_checkstack(L, 4); L->top += 4; luaD_call(L, L->top - 4, 0); } void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; if (ttistable(t)) { /* `t' is a table? */ Table *h = hvalue(t); const TValue *res = luaH_get(h, key); /* do a primitive get */ if (!ttisnil(res) || /* result is no nil? */ (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ setobj2s(L, val, res); return; } /* else will try the tag method */ } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) luaG_typeerror(L, t, "index"); if (ttisfunction(tm)) { callTMres(L, val, tm, t, key); return; } t = tm; /* else repeat with `tm' */ } luaG_runerror(L, "loop in gettable"); } void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; if (ttistable(t)) { /* `t' is a table? */ Table *h = hvalue(t); TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ if (!ttisnil(oldval) || /* result is no nil? */ (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ setobj2t(L, oldval, val); luaC_barriert(L, h, val); return; } /* else will try the tag method */ } else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) luaG_typeerror(L, t, "index"); if (ttisfunction(tm)) { callTM(L, tm, t, key, val); return; } t = tm; /* else repeat with `tm' */ } luaG_runerror(L, "loop in settable"); } static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (ttisnil(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ if (ttisnil(tm)) return 0; callTMres(L, res, tm, p1, p2); return 1; } static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, TMS event) { const TValue *tm1 = fasttm(L, mt1, event); const TValue *tm2; if (tm1 == NULL) return NULL; /* no metamethod */ if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ tm2 = fasttm(L, mt2, event); if (tm2 == NULL) return NULL; /* no metamethod */ if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ return tm1; return NULL; } static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event) { const TValue *tm1 = luaT_gettmbyobj(L, p1, event); const TValue *tm2; if (ttisnil(tm1)) return -1; /* no metamethod? */ tm2 = luaT_gettmbyobj(L, p2, event); if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ return -1; callTMres(L, L->top, tm1, p1, p2); return !l_isfalse(L->top); } static int l_strcmp (const TString *ls, const TString *rs) { const char *l = getstr(ls); size_t ll = ls->tsv.len; const char *r = getstr(rs); size_t lr = rs->tsv.len; for (;;) { int temp = strcoll(l, r); if (temp != 0) return temp; else { /* strings are equal up to a `\0' */ size_t len = strlen(l); /* index of first `\0' in both strings */ if (len == lr) /* r is finished? */ return (len == ll) ? 0 : 1; else if (len == ll) /* l is finished? */ return -1; /* l is smaller than r (because r is not finished) */ /* both strings longer than `len'; go on comparing (after the `\0') */ len++; l += len; ll -= len; r += len; lr -= len; } } } int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { int res; if (ttype(l) != ttype(r)) return luaG_ordererror(L, l, r); else if (ttisnumber(l)) return luai_numlt(nvalue(l), nvalue(r)); else if (ttisstring(l)) return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) return res; return luaG_ordererror(L, l, r); } static int lessequal (lua_State *L, const TValue *l, const TValue *r) { int res; if (ttype(l) != ttype(r)) return luaG_ordererror(L, l, r); else if (ttisnumber(l)) return luai_numle(nvalue(l), nvalue(r)); else if (ttisstring(l)) return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ return res; else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ return !res; return luaG_ordererror(L, l, r); } int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; lua_assert(ttype(t1) == ttype(t2)); switch (ttype(t1)) { case LUA_TNIL: return 1; case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_TUSERDATA: { if (uvalue(t1) == uvalue(t2)) return 1; tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } case LUA_TTABLE: { if (hvalue(t1) == hvalue(t2)) return 1; tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } default: return gcvalue(t1) == gcvalue(t2); } if (tm == NULL) return 0; /* no TM? */ callTMres(L, L->top, tm, t1, t2); /* call TM */ return !l_isfalse(L->top); } void luaV_concat (lua_State *L, int total, int last) { do { StkId top = L->base + last + 1; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) luaG_concaterror(L, top-2, top-1); } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ (void)tostring(L, top - 2); /* result is first op (as string) */ else { /* at least two string values; get as many as possible */ size_t tl = tsvalue(top-1)->len; char *buffer; int i; /* collect total length */ for (n = 1; n < total && tostring(L, top-n-1); n++) { size_t l = tsvalue(top-n-1)->len; if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); tl += l; } buffer = luaZ_openspace(L, &G(L)->buff, tl); tl = 0; for (i=n; i>0; i--) { /* concat all strings */ size_t l = tsvalue(top-i)->len; memcpy(buffer+tl, svalue(top-i), l); tl += l; } setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); } total -= n-1; /* got `n' strings to create 1 new */ last -= n-1; } while (total > 1); /* repeat until only 1 result left */ } static void Arith (lua_State *L, StkId ra, const TValue *rb, const TValue *rc, TMS op) { TValue tempb, tempc; const TValue *b, *c; if ((b = luaV_tonumber(rb, &tempb)) != NULL && (c = luaV_tonumber(rc, &tempc)) != NULL) { lua_Number nb = nvalue(b), nc = nvalue(c); switch (op) { case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; default: lua_assert(0); break; } } else if (!call_binTM(L, rb, rc, ra, op)) luaG_aritherror(L, rb, rc); } /* ** some macros for common tasks in `luaV_execute' */ #define runtime_check(L, c) { if (!(c)) break; } #define RA(i) (base+GETARG_A(i)) /* to be used after possible stack reallocation */ #define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) #define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) #define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) #define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) #define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) #define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} #define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } #define arith_op(op,tm) { \ TValue *rb = RKB(i); \ TValue *rc = RKC(i); \ if (ttisnumber(rb) && ttisnumber(rc)) { \ lua_Number nb = nvalue(rb), nc = nvalue(rc); \ setnvalue(ra, op(nb, nc)); \ } \ else \ Protect(Arith(L, ra, rb, rc, tm)); \ } void luaV_execute (lua_State *L, int nexeccalls) { LClosure *cl; StkId base; TValue *k; const Instruction *pc; reentry: /* entry point */ lua_assert(isLua(L->ci)); pc = L->savedpc; cl = &clvalue(L->ci->func)->l; base = L->base; k = cl->p->k; /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; StkId ra; if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { traceexec(L, pc); if (L->status == LUA_YIELD) { /* did hook yield? */ L->savedpc = pc - 1; return; } base = L->base; } /* warning!! several calls may realloc the stack and invalidate `ra' */ ra = RA(i); lua_assert(base == L->base && L->base == L->ci->base); lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); switch (GET_OPCODE(i)) { case OP_MOVE: { setobjs2s(L, ra, RB(i)); continue; } case OP_LOADK: { setobj2s(L, ra, KBx(i)); continue; } case OP_LOADBOOL: { setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ continue; } case OP_LOADNIL: { TValue *rb = RB(i); do { setnilvalue(rb--); } while (rb >= ra); continue; } case OP_GETUPVAL: { int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); continue; } case OP_GETGLOBAL: { TValue g; TValue *rb = KBx(i); sethvalue(L, &g, cl->env); lua_assert(ttisstring(rb)); Protect(luaV_gettable(L, &g, rb, ra)); continue; } case OP_GETTABLE: { Protect(luaV_gettable(L, RB(i), RKC(i), ra)); continue; } case OP_SETGLOBAL: { TValue g; sethvalue(L, &g, cl->env); lua_assert(ttisstring(KBx(i))); Protect(luaV_settable(L, &g, KBx(i), ra)); continue; } case OP_SETUPVAL: { UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra); continue; } case OP_SETTABLE: { Protect(luaV_settable(L, ra, RKB(i), RKC(i))); continue; } case OP_NEWTABLE: { int b = GETARG_B(i); int c = GETARG_C(i); sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); Protect(luaC_checkGC(L)); continue; } case OP_SELF: { StkId rb = RB(i); setobjs2s(L, ra+1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra)); continue; } case OP_ADD: { arith_op(luai_numadd, TM_ADD); continue; } case OP_SUB: { arith_op(luai_numsub, TM_SUB); continue; } case OP_MUL: { arith_op(luai_nummul, TM_MUL); continue; } case OP_DIV: { arith_op(luai_numdiv, TM_DIV); continue; } case OP_MOD: { arith_op(luai_nummod, TM_MOD); continue; } case OP_POW: { arith_op(luai_numpow, TM_POW); continue; } case OP_UNM: { TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); setnvalue(ra, luai_numunm(nb)); } else { Protect(Arith(L, ra, rb, rb, TM_UNM)); } continue; } case OP_NOT: { int res = l_isfalse(RB(i)); /* next assignment may change this value */ setbvalue(ra, res); continue; } case OP_LEN: { const TValue *rb = RB(i); switch (ttype(rb)) { case LUA_TTABLE: { setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); break; } case LUA_TSTRING: { setnvalue(ra, cast_num(tsvalue(rb)->len)); break; } default: { /* try metamethod */ Protect( if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) luaG_typeerror(L, rb, "get length of"); ) } } continue; } case OP_CONCAT: { int b = GETARG_B(i); int c = GETARG_C(i); Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); setobjs2s(L, RA(i), base+b); continue; } case OP_JMP: { dojump(L, pc, GETARG_sBx(i)); continue; } case OP_EQ: { TValue *rb = RKB(i); TValue *rc = RKC(i); Protect( if (equalobj(L, rb, rc) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_LT: { Protect( if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_LE: { Protect( if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) dojump(L, pc, GETARG_sBx(*pc)); ) pc++; continue; } case OP_TEST: { if (l_isfalse(ra) != GETARG_C(i)) dojump(L, pc, GETARG_sBx(*pc)); pc++; continue; } case OP_TESTSET: { TValue *rb = RB(i); if (l_isfalse(rb) != GETARG_C(i)) { setobjs2s(L, ra, rb); dojump(L, pc, GETARG_sBx(*pc)); } pc++; continue; } case OP_CALL: { int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ L->savedpc = pc; switch (luaD_precall(L, ra, nresults)) { case PCRLUA: { nexeccalls++; goto reentry; /* restart luaV_execute over new Lua function */ } case PCRC: { /* it was a C function (`precall' called it); adjust results */ if (nresults >= 0) L->top = L->ci->top; base = L->base; continue; } default: { return; /* yield */ } } } case OP_TAILCALL: { int b = GETARG_B(i); if (b != 0) L->top = ra+b; /* else previous instruction set top */ L->savedpc = pc; lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); switch (luaD_precall(L, ra, LUA_MULTRET)) { case PCRLUA: { /* tail call: put new frame in place of previous one */ CallInfo *ci = L->ci - 1; /* previous frame */ int aux; StkId func = ci->func; StkId pfunc = (ci+1)->func; /* previous function index */ if (L->openupval) luaF_close(L, ci->base); L->base = ci->base = ci->func + ((ci+1)->base - pfunc); for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ setobjs2s(L, func+aux, pfunc+aux); ci->top = L->top = func+aux; /* correct top */ lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); ci->savedpc = L->savedpc; ci->tailcalls++; /* one more call lost */ L->ci--; /* remove new frame */ goto reentry; } case PCRC: { /* it was a C function (`precall' called it) */ base = L->base; continue; } default: { return; /* yield */ } } } case OP_RETURN: { int b = GETARG_B(i); if (b != 0) L->top = ra+b-1; if (L->openupval) luaF_close(L, base); L->savedpc = pc; b = luaD_poscall(L, ra); if (--nexeccalls == 0) /* was previous function running `here'? */ return; /* no: return */ else { /* yes: continue its execution */ if (b) L->top = L->ci->top; lua_assert(isLua(L->ci)); lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); goto reentry; } } case OP_FORLOOP: { lua_Number step = nvalue(ra+2); lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ lua_Number limit = nvalue(ra+1); if (luai_numlt(0, step) ? luai_numle(idx, limit) : luai_numle(limit, idx)) { dojump(L, pc, GETARG_sBx(i)); /* jump back */ setnvalue(ra, idx); /* update internal index... */ setnvalue(ra+3, idx); /* ...and external index */ } continue; } case OP_FORPREP: { const TValue *init = ra; const TValue *plimit = ra+1; const TValue *pstep = ra+2; L->savedpc = pc; /* next steps may throw errors */ if (!tonumber(init, ra)) luaG_runerror(L, LUA_QL("for") " initial value must be a number"); else if (!tonumber(plimit, ra+1)) luaG_runerror(L, LUA_QL("for") " limit must be a number"); else if (!tonumber(pstep, ra+2)) luaG_runerror(L, LUA_QL("for") " step must be a number"); setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); dojump(L, pc, GETARG_sBx(i)); continue; } case OP_TFORLOOP: { StkId cb = ra + 3; /* call base */ setobjs2s(L, cb+2, ra+2); setobjs2s(L, cb+1, ra+1); setobjs2s(L, cb, ra); L->top = cb+3; /* func. + 2 args (state and index) */ Protect(luaD_call(L, cb, GETARG_C(i))); L->top = L->ci->top; cb = RA(i) + 3; /* previous call may change the stack */ if (!ttisnil(cb)) { /* continue loop? */ setobjs2s(L, cb-1, cb); /* save control variable */ dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ } pc++; continue; } case OP_SETLIST: { int n = GETARG_B(i); int c = GETARG_C(i); int last; Table *h; if (n == 0) { n = cast_int(L->top - ra) - 1; L->top = L->ci->top; } if (c == 0) c = cast_int(*pc++); runtime_check(L, ttistable(ra)); h = hvalue(ra); last = ((c-1)*LFIELDS_PER_FLUSH) + n; if (last > h->sizearray) /* needs more space? */ luaH_resizearray(L, h, last); /* pre-alloc it at once */ for (; n > 0; n--) { TValue *val = ra+n; setobj2t(L, luaH_setnum(L, h, last--), val); luaC_barriert(L, h, val); } continue; } case OP_CLOSE: { luaF_close(L, ra); continue; } case OP_CLOSURE: { Proto *p; Closure *ncl; int nup, j; p = cl->p->p[GETARG_Bx(i)]; nup = p->nups; ncl = luaF_newLclosure(L, nup, cl->env); ncl->l.p = p; for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)]; else { lua_assert(GET_OPCODE(*pc) == OP_MOVE); ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); } } setclvalue(L, ra, ncl); Protect(luaC_checkGC(L)); continue; } case OP_VARARG: { int b = GETARG_B(i) - 1; int j; CallInfo *ci = L->ci; int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; if (b == LUA_MULTRET) { Protect(luaD_checkstack(L, n)); ra = RA(i); /* previous call may change the stack */ b = n; L->top = ra + n; } for (j = 0; j < b; j++) { if (j < n) { setobjs2s(L, ra + j, ci->base - n + j); } else { setnilvalue(ra + j); } } continue; } } } } blobby-1.0rc3/src/lua/lmem.h0000644000175000017500000000272612042452374017230 0ustar danielknobedanielknobe/* ** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #ifndef lmem_h #define lmem_h #include #include "llimits.h" #include "lua.h" #define MEMERRMSG "not enough memory" #define luaM_reallocv(L,b,on,n,e) \ ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \ luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ luaM_toobig(L)) #define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) #define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) #define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t)) #define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t)) #define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) #define luaM_newvector(L,n,t) \ cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) #define luaM_growvector(L,v,nelems,size,t,limit,e) \ if ((nelems)+1 > (size)) \ ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) #define luaM_reallocvector(L, v,oldn,n,t) \ ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, size_t size); LUAI_FUNC void *luaM_toobig (lua_State *L); LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elem, int limit, const char *errormsg); #endif blobby-1.0rc3/src/lua/ltm.h0000644000175000017500000000177212042452374017072 0ustar danielknobedanielknobe/* ** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $ ** Tag methods ** See Copyright Notice in lua.h */ #ifndef ltm_h #define ltm_h #include "lobject.h" /* * WARNING: if you change the order of this enumeration, * grep "ORDER TM" */ typedef enum { TM_INDEX, TM_NEWINDEX, TM_GC, TM_MODE, TM_EQ, /* last tag method with `fast' access */ TM_ADD, TM_SUB, TM_MUL, TM_DIV, TM_MOD, TM_POW, TM_UNM, TM_LEN, TM_LT, TM_LE, TM_CONCAT, TM_CALL, TM_N /* number of elements in the enum */ } TMS; #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) #define fasttm(l,et,e) gfasttm(G(l), et, e) LUAI_DATA const char *const luaT_typenames[]; LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event); LUAI_FUNC void luaT_init (lua_State *L); #endif blobby-1.0rc3/src/lua/lstate.h0000644000175000017500000001162312042452374017566 0ustar danielknobedanielknobe/* ** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $ ** Global State ** See Copyright Notice in lua.h */ #ifndef lstate_h #define lstate_h #include "lua.h" #include "lobject.h" #include "ltm.h" #include "lzio.h" struct lua_longjmp; /* defined in ldo.c */ /* table of globals */ #define gt(L) (&L->l_gt) /* registry */ #define registry(L) (&G(L)->l_registry) /* extra stack space to handle TM calls and some other extras */ #define EXTRA_STACK 5 #define BASIC_CI_SIZE 8 #define BASIC_STACK_SIZE (2*LUA_MINSTACK) typedef struct stringtable { GCObject **hash; lu_int32 nuse; /* number of elements */ int size; } stringtable; /* ** informations about a call */ typedef struct CallInfo { StkId base; /* base for this function */ StkId func; /* function index in the stack */ StkId top; /* top for this function */ const Instruction *savedpc; int nresults; /* expected number of results from this function */ int tailcalls; /* number of tail calls lost under this entry */ } CallInfo; #define curr_func(L) (clvalue(L->ci->func)) #define ci_func(ci) (clvalue((ci)->func)) #define f_isLua(ci) (!ci_func(ci)->c.isC) #define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) /* ** `global state', shared by all threads of this state */ typedef struct global_State { stringtable strt; /* hash table for strings */ lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to `frealloc' */ lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ int sweepstrgc; /* position of sweep in `strt' */ GCObject *rootgc; /* list of all collectable objects */ GCObject **sweepgc; /* position of sweep in `rootgc' */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of weak tables (to be cleared) */ GCObject *tmudata; /* last element of list of userdata to be GC */ Mbuffer buff; /* temporary buffer for string concatentation */ lu_mem GCthreshold; lu_mem totalbytes; /* number of bytes currently allocated */ lu_mem estimate; /* an estimate of number of bytes actually in use */ lu_mem gcdept; /* how much GC is `behind schedule' */ int gcpause; /* size of pause between successive GCs */ int gcstepmul; /* GC `granularity' */ lua_CFunction panic; /* to be called in unprotected errors */ TValue l_registry; struct lua_State *mainthread; UpVal uvhead; /* head of double-linked list of all open upvalues */ struct Table *mt[NUM_TAGS]; /* metatables for basic types */ TString *tmname[TM_N]; /* array with tag-method names */ } global_State; /* ** `per thread' state */ struct lua_State { CommonHeader; lu_byte status; StkId top; /* first free slot in the stack */ StkId base; /* base of current function */ global_State *l_G; CallInfo *ci; /* call info for current function */ const Instruction *savedpc; /* `savedpc' of current function */ StkId stack_last; /* last free slot in the stack */ StkId stack; /* stack base */ CallInfo *end_ci; /* points after end of ci array*/ CallInfo *base_ci; /* array of CallInfo's */ int stacksize; int size_ci; /* size of array `base_ci' */ unsigned short nCcalls; /* number of nested C calls */ unsigned short baseCcalls; /* nested C calls when resuming coroutine */ lu_byte hookmask; lu_byte allowhook; int basehookcount; int hookcount; lua_Hook hook; TValue l_gt; /* table of globals */ TValue env; /* temporary place for environments */ GCObject *openupval; /* list of open upvalues in this stack */ GCObject *gclist; struct lua_longjmp *errorJmp; /* current error recover point */ ptrdiff_t errfunc; /* current error handling function (stack index) */ }; #define G(L) (L->l_G) /* ** Union of all collectable objects */ union GCObject { GCheader gch; union TString ts; union Udata u; union Closure cl; struct Table h; struct Proto p; struct UpVal uv; struct lua_State th; /* thread */ }; /* macros to convert a GCObject into a specific value */ #define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) #define gco2ts(o) (&rawgco2ts(o)->tsv) #define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) #define gco2u(o) (&rawgco2u(o)->uv) #define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) #define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) #define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) #define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) #define ngcotouv(o) \ check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv)) #define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) /* macro to convert any Lua object into a GCObject */ #define obj2gco(v) (cast(GCObject *, (v))) LUAI_FUNC lua_State *luaE_newthread (lua_State *L); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); #endif blobby-1.0rc3/src/lua/lvm.h0000644000175000017500000000220712042452374017066 0ustar danielknobedanielknobe/* ** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lvm_h #define lvm_h #include "ldo.h" #include "lobject.h" #include "ltm.h" #define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) #define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \ (((o) = luaV_tonumber(o,n)) != NULL)) #define equalobj(L,o1,o2) \ (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val); LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val); LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); #endif blobby-1.0rc3/src/lua/ltable.c0000644000175000017500000003760712042452374017542 0ustar danielknobedanielknobe/* ** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ /* ** Implementation of tables (aka arrays, objects, or hash tables). ** Tables keep its elements in two parts: an array part and a hash part. ** Non-negative integer keys are all candidates to be kept in the array ** part. The actual size of the array is the largest `n' such that at ** least half the slots between 0 and n are in use. ** Hash uses a mix of chained scatter table with Brent's variation. ** A main invariant of these tables is that, if an element is not ** in its main position (i.e. the `original' position that its hash gives ** to it), then the colliding element is in its own main position. ** Hence even when the load factor reaches 100%, performance remains good. */ #include #include #define ltable_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "ltable.h" /* ** max size of array part is 2^MAXBITS */ #if LUAI_BITSINT > 26 #define MAXBITS 26 #else #define MAXBITS (LUAI_BITSINT-2) #endif #define MAXASIZE (1 << MAXBITS) #define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) #define hashstr(t,str) hashpow2(t, (str)->tsv.hash) #define hashboolean(t,p) hashpow2(t, p) /* ** for some types, it is better to avoid modulus by power of 2, as ** they tend to have many 2 factors. */ #define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) #define hashpointer(t,p) hashmod(t, IntPoint(p)) /* ** number of ints inside a lua_Number */ #define numints cast_int(sizeof(lua_Number)/sizeof(int)) #define dummynode (&dummynode_) static const Node dummynode_ = { {{NULL}, LUA_TNIL}, /* value */ {{{NULL}, LUA_TNIL, NULL}} /* key */ }; /* ** hash for lua_Numbers */ static Node *hashnum (const Table *t, lua_Number n) { unsigned int a[numints]; int i; if (luai_numeq(n, 0)) /* avoid problems with -0 */ return gnode(t, 0); memcpy(a, &n, sizeof(a)); for (i = 1; i < numints; i++) a[0] += a[i]; return hashmod(t, a[0]); } /* ** returns the `main' position of an element in a table (that is, the index ** of its hash value) */ static Node *mainposition (const Table *t, const TValue *key) { switch (ttype(key)) { case LUA_TNUMBER: return hashnum(t, nvalue(key)); case LUA_TSTRING: return hashstr(t, rawtsvalue(key)); case LUA_TBOOLEAN: return hashboolean(t, bvalue(key)); case LUA_TLIGHTUSERDATA: return hashpointer(t, pvalue(key)); default: return hashpointer(t, gcvalue(key)); } } /* ** returns the index for `key' if `key' is an appropriate key to live in ** the array part of the table, -1 otherwise. */ static int arrayindex (const TValue *key) { if (ttisnumber(key)) { lua_Number n = nvalue(key); int k; lua_number2int(k, n); if (luai_numeq(cast_num(k), n)) return k; } return -1; /* `key' did not match some condition */ } /* ** returns the index of a `key' for table traversals. First goes all ** elements in the array part, then elements in the hash part. The ** beginning of a traversal is signalled by -1. */ static int findindex (lua_State *L, Table *t, StkId key) { int i; if (ttisnil(key)) return -1; /* first iteration */ i = arrayindex(key); if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ return i-1; /* yes; that's the index (corrected to C) */ else { Node *n = mainposition(t, key); do { /* check whether `key' is somewhere in the chain */ /* key may be dead already, but it is ok to use it in `next' */ if (luaO_rawequalObj(key2tval(n), key) || (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && gcvalue(gkey(n)) == gcvalue(key))) { i = cast_int(n - gnode(t, 0)); /* key index in hash table */ /* hash elements are numbered after array ones */ return i + t->sizearray; } else n = gnext(n); } while (n); luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ return 0; /* to avoid warnings */ } } int luaH_next (lua_State *L, Table *t, StkId key) { int i = findindex(L, t, key); /* find original element */ for (i++; i < t->sizearray; i++) { /* try first array part */ if (!ttisnil(&t->array[i])) { /* a non-nil value? */ setnvalue(key, cast_num(i+1)); setobj2s(L, key+1, &t->array[i]); return 1; } } for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ setobj2s(L, key, key2tval(gnode(t, i))); setobj2s(L, key+1, gval(gnode(t, i))); return 1; } } return 0; /* no more elements */ } /* ** {============================================================= ** Rehash ** ============================================================== */ static int computesizes (int nums[], int *narray) { int i; int twotoi; /* 2^i */ int a = 0; /* number of elements smaller than 2^i */ int na = 0; /* number of elements to go to array part */ int n = 0; /* optimal size for array part */ for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { if (nums[i] > 0) { a += nums[i]; if (a > twotoi/2) { /* more than half elements present? */ n = twotoi; /* optimal size (till now) */ na = a; /* all elements smaller than n will go to array part */ } } if (a == *narray) break; /* all elements already counted */ } *narray = n; lua_assert(*narray/2 <= na && na <= *narray); return na; } static int countint (const TValue *key, int *nums) { int k = arrayindex(key); if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ nums[ceillog2(k)]++; /* count as such */ return 1; } else return 0; } static int numusearray (const Table *t, int *nums) { int lg; int ttlg; /* 2^lg */ int ause = 0; /* summation of `nums' */ int i = 1; /* count to traverse all array keys */ for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ int lc = 0; /* counter */ int lim = ttlg; if (lim > t->sizearray) { lim = t->sizearray; /* adjust upper limit */ if (i > lim) break; /* no more elements to count */ } /* count elements in range (2^(lg-1), 2^lg] */ for (; i <= lim; i++) { if (!ttisnil(&t->array[i-1])) lc++; } nums[lg] += lc; ause += lc; } return ause; } static int numusehash (const Table *t, int *nums, int *pnasize) { int totaluse = 0; /* total number of elements */ int ause = 0; /* summation of `nums' */ int i = sizenode(t); while (i--) { Node *n = &t->node[i]; if (!ttisnil(gval(n))) { ause += countint(key2tval(n), nums); totaluse++; } } *pnasize += ause; return totaluse; } static void setarrayvector (lua_State *L, Table *t, int size) { int i; luaM_reallocvector(L, t->array, t->sizearray, size, TValue); for (i=t->sizearray; iarray[i]); t->sizearray = size; } static void setnodevector (lua_State *L, Table *t, int size) { int lsize; if (size == 0) { /* no elements to hash part? */ t->node = cast(Node *, dummynode); /* use common `dummynode' */ lsize = 0; } else { int i; lsize = ceillog2(size); if (lsize > MAXBITS) luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); for (i=0; ilsizenode = cast_byte(lsize); t->lastfree = gnode(t, size); /* all positions are free */ } static void resize (lua_State *L, Table *t, int nasize, int nhsize) { int i; int oldasize = t->sizearray; int oldhsize = t->lsizenode; Node *nold = t->node; /* save old hash ... */ if (nasize > oldasize) /* array part must grow? */ setarrayvector(L, t, nasize); /* create new hash part with appropriate size */ setnodevector(L, t, nhsize); if (nasize < oldasize) { /* array part must shrink? */ t->sizearray = nasize; /* re-insert elements from vanishing slice */ for (i=nasize; iarray[i])) setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); } /* shrink array */ luaM_reallocvector(L, t->array, oldasize, nasize, TValue); } /* re-insert elements from hash part */ for (i = twoto(oldhsize) - 1; i >= 0; i--) { Node *old = nold+i; if (!ttisnil(gval(old))) setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old)); } if (nold != dummynode) luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */ } void luaH_resizearray (lua_State *L, Table *t, int nasize) { int nsize = (t->node == dummynode) ? 0 : sizenode(t); resize(L, t, nasize, nsize); } static void rehash (lua_State *L, Table *t, const TValue *ek) { int nasize, na; int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ int i; int totaluse; for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ nasize = numusearray(t, nums); /* count keys in array part */ totaluse = nasize; /* all those keys are integer keys */ totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ /* count extra key */ nasize += countint(ek, nums); totaluse++; /* compute new size for array part */ na = computesizes(nums, &nasize); /* resize the table to new computed sizes */ resize(L, t, nasize, totaluse - na); } /* ** }============================================================= */ Table *luaH_new (lua_State *L, int narray, int nhash) { Table *t = luaM_new(L, Table); luaC_link(L, obj2gco(t), LUA_TTABLE); t->metatable = NULL; t->flags = cast_byte(~0); /* temporary values (kept only if some malloc fails) */ t->array = NULL; t->sizearray = 0; t->lsizenode = 0; t->node = cast(Node *, dummynode); setarrayvector(L, t, narray); setnodevector(L, t, nhash); return t; } void luaH_free (lua_State *L, Table *t) { if (t->node != dummynode) luaM_freearray(L, t->node, sizenode(t), Node); luaM_freearray(L, t->array, t->sizearray, TValue); luaM_free(L, t); } static Node *getfreepos (Table *t) { while (t->lastfree-- > t->node) { if (ttisnil(gkey(t->lastfree))) return t->lastfree; } return NULL; /* could not find a free place */ } /* ** inserts a new key into a hash table; first, check whether key's main ** position is free. If not, check whether colliding node is in its main ** position or not: if it is not, move colliding node to an empty place and ** put new key in its main position; otherwise (colliding node is in its main ** position), new key goes to an empty position. */ static TValue *newkey (lua_State *L, Table *t, const TValue *key) { Node *mp = mainposition(t, key); if (!ttisnil(gval(mp)) || mp == dummynode) { Node *othern; Node *n = getfreepos(t); /* get a free place */ if (n == NULL) { /* cannot find a free place? */ rehash(L, t, key); /* grow table */ return luaH_set(L, t, key); /* re-insert key into grown table */ } lua_assert(n != dummynode); othern = mainposition(t, key2tval(mp)); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ gnext(mp) = NULL; /* now `mp' is free */ setnilvalue(gval(mp)); } else { /* colliding node is in its own main position */ /* new node will go into free position */ gnext(n) = gnext(mp); /* chain new position */ gnext(mp) = n; mp = n; } } gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; luaC_barriert(L, t, key); lua_assert(ttisnil(gval(mp))); return gval(mp); } /* ** search function for integers */ const TValue *luaH_getnum (Table *t, int key) { /* (1 <= key && key <= t->sizearray) */ if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) return &t->array[key-1]; else { lua_Number nk = cast_num(key); Node *n = hashnum(t, nk); do { /* check whether `key' is somewhere in the chain */ if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) return gval(n); /* that's it */ else n = gnext(n); } while (n); return luaO_nilobject; } } /* ** search function for strings */ const TValue *luaH_getstr (Table *t, TString *key) { Node *n = hashstr(t, key); do { /* check whether `key' is somewhere in the chain */ if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) return gval(n); /* that's it */ else n = gnext(n); } while (n); return luaO_nilobject; } /* ** main search function */ const TValue *luaH_get (Table *t, const TValue *key) { switch (ttype(key)) { case LUA_TNIL: return luaO_nilobject; case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); case LUA_TNUMBER: { int k; lua_Number n = nvalue(key); lua_number2int(k, n); if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ return luaH_getnum(t, k); /* use specialized version */ /* else go through */ } default: { Node *n = mainposition(t, key); do { /* check whether `key' is somewhere in the chain */ if (luaO_rawequalObj(key2tval(n), key)) return gval(n); /* that's it */ else n = gnext(n); } while (n); return luaO_nilobject; } } } TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { const TValue *p = luaH_get(t, key); t->flags = 0; if (p != luaO_nilobject) return cast(TValue *, p); else { if (ttisnil(key)) luaG_runerror(L, "table index is nil"); else if (ttisnumber(key) && luai_numisnan(nvalue(key))) luaG_runerror(L, "table index is NaN"); return newkey(L, t, key); } } TValue *luaH_setnum (lua_State *L, Table *t, int key) { const TValue *p = luaH_getnum(t, key); if (p != luaO_nilobject) return cast(TValue *, p); else { TValue k; setnvalue(&k, cast_num(key)); return newkey(L, t, &k); } } TValue *luaH_setstr (lua_State *L, Table *t, TString *key) { const TValue *p = luaH_getstr(t, key); if (p != luaO_nilobject) return cast(TValue *, p); else { TValue k; setsvalue(L, &k, key); return newkey(L, t, &k); } } static int unbound_search (Table *t, unsigned int j) { unsigned int i = j; /* i is zero or a present index */ j++; /* find `i' and `j' such that i is present and j is not */ while (!ttisnil(luaH_getnum(t, j))) { i = j; j *= 2; if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ /* table was built with bad purposes: resort to linear search */ i = 1; while (!ttisnil(luaH_getnum(t, i))) i++; return i - 1; } } /* now do a binary search between them */ while (j - i > 1) { unsigned int m = (i+j)/2; if (ttisnil(luaH_getnum(t, m))) j = m; else i = m; } return i; } /* ** Try to find a boundary in table `t'. A `boundary' is an integer index ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). */ int luaH_getn (Table *t) { unsigned int j = t->sizearray; if (j > 0 && ttisnil(&t->array[j - 1])) { /* there is a boundary in the array part: (binary) search for it */ unsigned int i = 0; while (j - i > 1) { unsigned int m = (i+j)/2; if (ttisnil(&t->array[m - 1])) j = m; else i = m; } return i; } /* else must find a boundary in hash part */ else if (t->node == dummynode) /* hash part is empty? */ return j; /* that is easy... */ else return unbound_search(t, j); } #if defined(LUA_DEBUG) Node *luaH_mainposition (const Table *t, const TValue *key) { return mainposition(t, key); } int luaH_isdummy (Node *n) { return n == dummynode; } #endif blobby-1.0rc3/src/lua/llex.c0000644000175000017500000003027212042452374017232 0ustar danielknobedanielknobe/* ** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #include #include #include #define llex_c #define LUA_CORE #include "lua.h" #include "ldo.h" #include "llex.h" #include "lobject.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "lzio.h" #define next(ls) (ls->current = zgetc(ls->z)) #define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') /* ORDER RESERVED */ const char *const luaX_tokens [] = { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "..", "...", "==", ">=", "<=", "~=", "", "", "", "", NULL }; #define save_and_next(ls) (save(ls, ls->current), next(ls)) static void save (LexState *ls, int c) { Mbuffer *b = ls->buff; if (b->n + 1 > b->buffsize) { size_t newsize; if (b->buffsize >= MAX_SIZET/2) luaX_lexerror(ls, "lexical element too long", 0); newsize = b->buffsize * 2; luaZ_resizebuffer(ls->L, b, newsize); } b->buffer[b->n++] = cast(char, c); } void luaX_init (lua_State *L) { int i; for (i=0; itsv.reserved = cast_byte(i+1); /* reserved word */ } } #define MAXSRC 80 const char *luaX_token2str (LexState *ls, int token) { if (token < FIRST_RESERVED) { lua_assert(token == cast(unsigned char, token)); return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) : luaO_pushfstring(ls->L, "%c", token); } else return luaX_tokens[token-FIRST_RESERVED]; } static const char *txtToken (LexState *ls, int token) { switch (token) { case TK_NAME: case TK_STRING: case TK_NUMBER: save(ls, '\0'); return luaZ_buffer(ls->buff); default: return luaX_token2str(ls, token); } } void luaX_lexerror (LexState *ls, const char *msg, int token) { char buff[MAXSRC]; luaO_chunkid(buff, getstr(ls->source), MAXSRC); msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); if (token) luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token)); luaD_throw(ls->L, LUA_ERRSYNTAX); } void luaX_syntaxerror (LexState *ls, const char *msg) { luaX_lexerror(ls, msg, ls->t.token); } TString *luaX_newstring (LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; TString *ts = luaS_newlstr(L, str, l); TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ if (ttisnil(o)) setbvalue(o, 1); /* make sure `str' will not be collected */ return ts; } static void inclinenumber (LexState *ls) { int old = ls->current; lua_assert(currIsNewline(ls)); next(ls); /* skip `\n' or `\r' */ if (currIsNewline(ls) && ls->current != old) next(ls); /* skip `\n\r' or `\r\n' */ if (++ls->linenumber >= MAX_INT) luaX_syntaxerror(ls, "chunk has too many lines"); } void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) { ls->decpoint = '.'; ls->L = L; ls->lookahead.token = TK_EOS; /* no look-ahead token */ ls->z = z; ls->fs = NULL; ls->linenumber = 1; ls->lastline = 1; ls->source = source; luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ next(ls); /* read first char */ } /* ** ======================================================= ** LEXICAL ANALYZER ** ======================================================= */ static int check_next (LexState *ls, const char *set) { if (!strchr(set, ls->current)) return 0; save_and_next(ls); return 1; } static void buffreplace (LexState *ls, char from, char to) { size_t n = luaZ_bufflen(ls->buff); char *p = luaZ_buffer(ls->buff); while (n--) if (p[n] == from) p[n] = to; } static void trydecpoint (LexState *ls, SemInfo *seminfo) { /* format error: try to update decimal point separator */ struct lconv *cv = localeconv(); char old = ls->decpoint; ls->decpoint = (cv ? cv->decimal_point[0] : '.'); buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { /* format error with correct decimal point: no more options */ buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ luaX_lexerror(ls, "malformed number", TK_NUMBER); } } /* LUA_NUMBER */ static void read_numeral (LexState *ls, SemInfo *seminfo) { lua_assert(isdigit(ls->current)); do { save_and_next(ls); } while (isdigit(ls->current) || ls->current == '.'); if (check_next(ls, "Ee")) /* `E'? */ check_next(ls, "+-"); /* optional exponent sign */ while (isalnum(ls->current) || ls->current == '_') save_and_next(ls); save(ls, '\0'); buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ trydecpoint(ls, seminfo); /* try to update decimal point separator */ } static int skip_sep (LexState *ls) { int count = 0; int s = ls->current; lua_assert(s == '[' || s == ']'); save_and_next(ls); while (ls->current == '=') { save_and_next(ls); count++; } return (ls->current == s) ? count : (-count) - 1; } static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { int cont = 0; (void)(cont); /* avoid warnings when `cont' is not used */ save_and_next(ls); /* skip 2nd `[' */ if (currIsNewline(ls)) /* string starts with a newline? */ inclinenumber(ls); /* skip it */ for (;;) { switch (ls->current) { case EOZ: luaX_lexerror(ls, (seminfo) ? "unfinished long string" : "unfinished long comment", TK_EOS); break; /* to avoid warnings */ #if defined(LUA_COMPAT_LSTR) case '[': { if (skip_sep(ls) == sep) { save_and_next(ls); /* skip 2nd `[' */ cont++; #if LUA_COMPAT_LSTR == 1 if (sep == 0) luaX_lexerror(ls, "nesting of [[...]] is deprecated", '['); #endif } break; } #endif case ']': { if (skip_sep(ls) == sep) { save_and_next(ls); /* skip 2nd `]' */ #if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2 cont--; if (sep == 0 && cont >= 0) break; #endif goto endloop; } break; } case '\n': case '\r': { save(ls, '\n'); inclinenumber(ls); if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ break; } default: { if (seminfo) save_and_next(ls); else next(ls); } } } endloop: if (seminfo) seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), luaZ_bufflen(ls->buff) - 2*(2 + sep)); } static void read_string (LexState *ls, int del, SemInfo *seminfo) { save_and_next(ls); while (ls->current != del) { switch (ls->current) { case EOZ: luaX_lexerror(ls, "unfinished string", TK_EOS); continue; /* to avoid warnings */ case '\n': case '\r': luaX_lexerror(ls, "unfinished string", TK_STRING); continue; /* to avoid warnings */ case '\\': { int c; next(ls); /* do not save the `\' */ switch (ls->current) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\n': /* go through */ case '\r': save(ls, '\n'); inclinenumber(ls); continue; case EOZ: continue; /* will raise an error next loop */ default: { if (!isdigit(ls->current)) save_and_next(ls); /* handles \\, \", \', and \? */ else { /* \xxx */ int i = 0; c = 0; do { c = 10*c + (ls->current-'0'); next(ls); } while (++i<3 && isdigit(ls->current)); if (c > UCHAR_MAX) luaX_lexerror(ls, "escape sequence too large", TK_STRING); save(ls, c); } continue; } } save(ls, c); next(ls); continue; } default: save_and_next(ls); } } save_and_next(ls); /* skip delimiter */ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, luaZ_bufflen(ls->buff) - 2); } static int llex (LexState *ls, SemInfo *seminfo) { luaZ_resetbuffer(ls->buff); for (;;) { switch (ls->current) { case '\n': case '\r': { inclinenumber(ls); continue; } case '-': { next(ls); if (ls->current != '-') return '-'; /* else is a comment */ next(ls); if (ls->current == '[') { int sep = skip_sep(ls); luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ if (sep >= 0) { read_long_string(ls, NULL, sep); /* long comment */ luaZ_resetbuffer(ls->buff); continue; } } /* else short comment */ while (!currIsNewline(ls) && ls->current != EOZ) next(ls); continue; } case '[': { int sep = skip_sep(ls); if (sep >= 0) { read_long_string(ls, seminfo, sep); return TK_STRING; } else if (sep == -1) return '['; else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); } case '=': { next(ls); if (ls->current != '=') return '='; else { next(ls); return TK_EQ; } } case '<': { next(ls); if (ls->current != '=') return '<'; else { next(ls); return TK_LE; } } case '>': { next(ls); if (ls->current != '=') return '>'; else { next(ls); return TK_GE; } } case '~': { next(ls); if (ls->current != '=') return '~'; else { next(ls); return TK_NE; } } case '"': case '\'': { read_string(ls, ls->current, seminfo); return TK_STRING; } case '.': { save_and_next(ls); if (check_next(ls, ".")) { if (check_next(ls, ".")) return TK_DOTS; /* ... */ else return TK_CONCAT; /* .. */ } else if (!isdigit(ls->current)) return '.'; else { read_numeral(ls, seminfo); return TK_NUMBER; } } case EOZ: { return TK_EOS; } default: { if (isspace(ls->current)) { lua_assert(!currIsNewline(ls)); next(ls); continue; } else if (isdigit(ls->current)) { read_numeral(ls, seminfo); return TK_NUMBER; } else if (isalpha(ls->current) || ls->current == '_') { /* identifier or reserved word */ TString *ts; do { save_and_next(ls); } while (isalnum(ls->current) || ls->current == '_'); ts = luaX_newstring(ls, luaZ_buffer(ls->buff), luaZ_bufflen(ls->buff)); if (ts->tsv.reserved > 0) /* reserved word? */ return ts->tsv.reserved - 1 + FIRST_RESERVED; else { seminfo->ts = ts; return TK_NAME; } } else { int c = ls->current; next(ls); return c; /* single-char tokens (+ - / ...) */ } } } } } void luaX_next (LexState *ls) { ls->lastline = ls->linenumber; if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ ls->t = ls->lookahead; /* use this one */ ls->lookahead.token = TK_EOS; /* and discharge it */ } else ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ } void luaX_lookahead (LexState *ls) { lua_assert(ls->lookahead.token == TK_EOS); ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); } blobby-1.0rc3/src/lua/lgc.c0000644000175000017500000004720312042452374017035 0ustar danielknobedanielknobe/* ** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ #include #define lgc_c #define LUA_CORE #include "lua.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #define GCSTEPSIZE 1024u #define GCSWEEPMAX 40 #define GCSWEEPCOST 10 #define GCFINALIZECOST 100 #define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) #define makewhite(g,x) \ ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) #define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) #define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) #define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) #define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) #define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) #define KEYWEAK bitmask(KEYWEAKBIT) #define VALUEWEAK bitmask(VALUEWEAKBIT) #define markvalue(g,o) { checkconsistency(o); \ if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } #define markobject(g,t) { if (iswhite(obj2gco(t))) \ reallymarkobject(g, obj2gco(t)); } #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) static void removeentry (Node *n) { lua_assert(ttisnil(gval(n))); if (iscollectable(gkey(n))) setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ } static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); white2gray(o); switch (o->gch.tt) { case LUA_TSTRING: { return; } case LUA_TUSERDATA: { Table *mt = gco2u(o)->metatable; gray2black(o); /* udata are never gray */ if (mt) markobject(g, mt); markobject(g, gco2u(o)->env); return; } case LUA_TUPVAL: { UpVal *uv = gco2uv(o); markvalue(g, uv->v); if (uv->v == &uv->u.value) /* closed? */ gray2black(o); /* open upvalues are never black */ return; } case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; g->gray = o; break; } case LUA_TTABLE: { gco2h(o)->gclist = g->gray; g->gray = o; break; } case LUA_TTHREAD: { gco2th(o)->gclist = g->gray; g->gray = o; break; } case LUA_TPROTO: { gco2p(o)->gclist = g->gray; g->gray = o; break; } default: lua_assert(0); } } static void marktmu (global_State *g) { GCObject *u = g->tmudata; if (u) { do { u = u->gch.next; makewhite(g, u); /* may be marked, if left from previous GC */ reallymarkobject(g, u); } while (u != g->tmudata); } } /* move `dead' udata that need finalization to list `tmudata' */ size_t luaC_separateudata (lua_State *L, int all) { global_State *g = G(L); size_t deadmem = 0; GCObject **p = &g->mainthread->next; GCObject *curr; while ((curr = *p) != NULL) { if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) p = &curr->gch.next; /* don't bother with them */ else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { markfinalized(gco2u(curr)); /* don't need finalization */ p = &curr->gch.next; } else { /* must call its gc method */ deadmem += sizeudata(gco2u(curr)); markfinalized(gco2u(curr)); *p = curr->gch.next; /* link `curr' at the end of `tmudata' list */ if (g->tmudata == NULL) /* list is empty? */ g->tmudata = curr->gch.next = curr; /* creates a circular list */ else { curr->gch.next = g->tmudata->gch.next; g->tmudata->gch.next = curr; g->tmudata = curr; } } } return deadmem; } static int traversetable (global_State *g, Table *h) { int i; int weakkey = 0; int weakvalue = 0; const TValue *mode; if (h->metatable) markobject(g, h->metatable); mode = gfasttm(g, h->metatable, TM_MODE); if (mode && ttisstring(mode)) { /* is there a weak mode? */ weakkey = (strchr(svalue(mode), 'k') != NULL); weakvalue = (strchr(svalue(mode), 'v') != NULL); if (weakkey || weakvalue) { /* is really weak? */ h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ h->marked |= cast_byte((weakkey << KEYWEAKBIT) | (weakvalue << VALUEWEAKBIT)); h->gclist = g->weak; /* must be cleared after GC, ... */ g->weak = obj2gco(h); /* ... so put in the appropriate list */ } } if (weakkey && weakvalue) return 1; if (!weakvalue) { i = h->sizearray; while (i--) markvalue(g, &h->array[i]); } i = sizenode(h); while (i--) { Node *n = gnode(h, i); lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); if (ttisnil(gval(n))) removeentry(n); /* remove empty entries */ else { lua_assert(!ttisnil(gkey(n))); if (!weakkey) markvalue(g, gkey(n)); if (!weakvalue) markvalue(g, gval(n)); } } return weakkey || weakvalue; } /* ** All marks are conditional because a GC may happen while the ** prototype is still being created */ static void traverseproto (global_State *g, Proto *f) { int i; if (f->source) stringmark(f->source); for (i=0; isizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i=0; isizeupvalues; i++) { /* mark upvalue names */ if (f->upvalues[i]) stringmark(f->upvalues[i]); } for (i=0; isizep; i++) { /* mark nested protos */ if (f->p[i]) markobject(g, f->p[i]); } for (i=0; isizelocvars; i++) { /* mark local-variable names */ if (f->locvars[i].varname) stringmark(f->locvars[i].varname); } } static void traverseclosure (global_State *g, Closure *cl) { markobject(g, cl->c.env); if (cl->c.isC) { int i; for (i=0; ic.nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->c.upvalue[i]); } else { int i; lua_assert(cl->l.nupvalues == cl->l.p->nups); markobject(g, cl->l.p); for (i=0; il.nupvalues; i++) /* mark its upvalues */ markobject(g, cl->l.upvals[i]); } } static void checkstacksizes (lua_State *L, StkId max) { int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ int s_used = cast_int(max - L->stack); /* part of stack in use */ if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ return; /* do not touch the stacks */ if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ condhardstacktests(luaD_reallocCI(L, ci_used + 1)); if (4*s_used < L->stacksize && 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ condhardstacktests(luaD_reallocstack(L, s_used)); } static void traversestack (global_State *g, lua_State *l) { StkId o, lim; CallInfo *ci; markvalue(g, gt(l)); lim = l->top; for (ci = l->base_ci; ci <= l->ci; ci++) { lua_assert(ci->top <= l->stack_last); if (lim < ci->top) lim = ci->top; } for (o = l->stack; o < l->top; o++) markvalue(g, o); for (; o <= lim; o++) setnilvalue(o); checkstacksizes(l, lim); } /* ** traverse one gray object, turning it to black. ** Returns `quantity' traversed. */ static l_mem propagatemark (global_State *g) { GCObject *o = g->gray; lua_assert(isgray(o)); gray2black(o); switch (o->gch.tt) { case LUA_TTABLE: { Table *h = gco2h(o); g->gray = h->gclist; if (traversetable(g, h)) /* table is weak? */ black2gray(o); /* keep it gray */ return sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(Node) * sizenode(h); } case LUA_TFUNCTION: { Closure *cl = gco2cl(o); g->gray = cl->c.gclist; traverseclosure(g, cl); return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : sizeLclosure(cl->l.nupvalues); } case LUA_TTHREAD: { lua_State *th = gco2th(o); g->gray = th->gclist; th->gclist = g->grayagain; g->grayagain = o; black2gray(o); traversestack(g, th); return sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * th->size_ci; } case LUA_TPROTO: { Proto *p = gco2p(o); g->gray = p->gclist; traverseproto(g, p); return sizeof(Proto) + sizeof(Instruction) * p->sizecode + sizeof(Proto *) * p->sizep + sizeof(TValue) * p->sizek + sizeof(int) * p->sizelineinfo + sizeof(LocVar) * p->sizelocvars + sizeof(TString *) * p->sizeupvalues; } default: lua_assert(0); return 0; } } static size_t propagateall (global_State *g) { size_t m = 0; while (g->gray) m += propagatemark(g); return m; } /* ** The next function tells whether a key or value can be cleared from ** a weak table. Non-collectable objects are never removed from weak ** tables. Strings behave as `values', so are never removed too. for ** other objects: if really collected, cannot keep them; for userdata ** being finalized, keep them in keys, but not in values */ static int iscleared (const TValue *o, int iskey) { if (!iscollectable(o)) return 0; if (ttisstring(o)) { stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ return 0; } return iswhite(gcvalue(o)) || (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); } /* ** clear collected entries from weaktables */ static void cleartable (GCObject *l) { while (l) { Table *h = gco2h(l); int i = h->sizearray; lua_assert(testbit(h->marked, VALUEWEAKBIT) || testbit(h->marked, KEYWEAKBIT)); if (testbit(h->marked, VALUEWEAKBIT)) { while (i--) { TValue *o = &h->array[i]; if (iscleared(o, 0)) /* value was collected? */ setnilvalue(o); /* remove value */ } } i = sizenode(h); while (i--) { Node *n = gnode(h, i); if (!ttisnil(gval(n)) && /* non-empty entry? */ (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { setnilvalue(gval(n)); /* remove value ... */ removeentry(n); /* remove entry from table */ } } l = h->gclist; } } static void freeobj (lua_State *L, GCObject *o) { switch (o->gch.tt) { case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; case LUA_TTABLE: luaH_free(L, gco2h(o)); break; case LUA_TTHREAD: { lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); luaE_freethread(L, gco2th(o)); break; } case LUA_TSTRING: { G(L)->strt.nuse--; luaM_freemem(L, o, sizestring(gco2ts(o))); break; } case LUA_TUSERDATA: { luaM_freemem(L, o, sizeudata(gco2u(o))); break; } default: lua_assert(0); } } #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { GCObject *curr; global_State *g = G(L); int deadmask = otherwhite(g); while ((curr = *p) != NULL && count-- > 0) { if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ sweepwholelist(L, &gco2th(curr)->openupval); if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); makewhite(g, curr); /* make it white (for next cycle) */ p = &curr->gch.next; } else { /* must erase `curr' */ lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); *p = curr->gch.next; if (curr == g->rootgc) /* is the first element of the list? */ g->rootgc = curr->gch.next; /* adjust first */ freeobj(L, curr); } } return p; } static void checkSizes (lua_State *L) { global_State *g = G(L); /* check size of string hash */ if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && g->strt.size > MINSTRTABSIZE*2) luaS_resize(L, g->strt.size/2); /* table is too big */ /* check size of buffer */ if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ size_t newsize = luaZ_sizebuffer(&g->buff) / 2; luaZ_resizebuffer(L, &g->buff, newsize); } } static void GCTM (lua_State *L) { global_State *g = G(L); GCObject *o = g->tmudata->gch.next; /* get first element */ Udata *udata = rawgco2u(o); const TValue *tm; /* remove udata from `tmudata' */ if (o == g->tmudata) /* last element? */ g->tmudata = NULL; else g->tmudata->gch.next = udata->uv.next; udata->uv.next = g->mainthread->next; /* return it to `root' list */ g->mainthread->next = o; makewhite(g, o); tm = fasttm(L, udata->uv.metatable, TM_GC); if (tm != NULL) { lu_byte oldah = L->allowhook; lu_mem oldt = g->GCthreshold; L->allowhook = 0; /* stop debug hooks during GC tag method */ g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ setobj2s(L, L->top, tm); setuvalue(L, L->top+1, udata); L->top += 2; luaD_call(L, L->top - 2, 0); L->allowhook = oldah; /* restore hooks */ g->GCthreshold = oldt; /* restore threshold */ } } /* ** Call all GC tag methods */ void luaC_callGCTM (lua_State *L) { while (G(L)->tmudata) GCTM(L); } void luaC_freeall (lua_State *L) { global_State *g = G(L); int i; g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ sweepwholelist(L, &g->rootgc); for (i = 0; i < g->strt.size; i++) /* free all string lists */ sweepwholelist(L, &g->strt.hash[i]); } static void markmt (global_State *g) { int i; for (i=0; imt[i]) markobject(g, g->mt[i]); } /* mark root set */ static void markroot (lua_State *L) { global_State *g = G(L); g->gray = NULL; g->grayagain = NULL; g->weak = NULL; markobject(g, g->mainthread); /* make global table be traversed before main stack */ markvalue(g, gt(g->mainthread)); markvalue(g, registry(L)); markmt(g); g->gcstate = GCSpropagate; } static void remarkupvals (global_State *g) { UpVal *uv; for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); if (isgray(obj2gco(uv))) markvalue(g, uv->v); } } static void atomic (lua_State *L) { global_State *g = G(L); size_t udsize; /* total size of userdata to be finalized */ /* remark occasional upvalues of (maybe) dead threads */ remarkupvals(g); /* traverse objects cautch by write barrier and by 'remarkupvals' */ propagateall(g); /* remark weak tables */ g->gray = g->weak; g->weak = NULL; lua_assert(!iswhite(obj2gco(g->mainthread))); markobject(g, L); /* mark running thread */ markmt(g); /* mark basic metatables (again) */ propagateall(g); /* remark gray again */ g->gray = g->grayagain; g->grayagain = NULL; propagateall(g); udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ marktmu(g); /* mark `preserved' userdata */ udsize += propagateall(g); /* remark, to propagate `preserveness' */ cleartable(g->weak); /* remove collected objects from weak tables */ /* flip current white */ g->currentwhite = cast_byte(otherwhite(g)); g->sweepstrgc = 0; g->sweepgc = &g->rootgc; g->gcstate = GCSsweepstring; g->estimate = g->totalbytes - udsize; /* first estimate */ } static l_mem singlestep (lua_State *L) { global_State *g = G(L); /*lua_checkmemory(L);*/ switch (g->gcstate) { case GCSpause: { markroot(L); /* start a new collection */ return 0; } case GCSpropagate: { if (g->gray) return propagatemark(g); else { /* no more `gray' objects */ atomic(L); /* finish mark phase */ return 0; } } case GCSsweepstring: { lu_mem old = g->totalbytes; sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ g->gcstate = GCSsweep; /* end sweep-string phase */ lua_assert(old >= g->totalbytes); g->estimate -= old - g->totalbytes; return GCSWEEPCOST; } case GCSsweep: { lu_mem old = g->totalbytes; g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) { /* nothing more to sweep? */ checkSizes(L); g->gcstate = GCSfinalize; /* end sweep phase */ } lua_assert(old >= g->totalbytes); g->estimate -= old - g->totalbytes; return GCSWEEPMAX*GCSWEEPCOST; } case GCSfinalize: { if (g->tmudata) { GCTM(L); if (g->estimate > GCFINALIZECOST) g->estimate -= GCFINALIZECOST; return GCFINALIZECOST; } else { g->gcstate = GCSpause; /* end collection */ g->gcdept = 0; return 0; } } default: lua_assert(0); return 0; } } void luaC_step (lua_State *L) { global_State *g = G(L); l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; if (lim == 0) lim = (MAX_LUMEM-1)/2; /* no limit */ g->gcdept += g->totalbytes - g->GCthreshold; do { lim -= singlestep(L); if (g->gcstate == GCSpause) break; } while (lim > 0); if (g->gcstate != GCSpause) { if (g->gcdept < GCSTEPSIZE) g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ else { g->gcdept -= GCSTEPSIZE; g->GCthreshold = g->totalbytes; } } else { lua_assert(g->totalbytes >= g->estimate); setthreshold(g); } } void luaC_fullgc (lua_State *L) { global_State *g = G(L); if (g->gcstate <= GCSpropagate) { /* reset sweep marks to sweep all elements (returning them to white) */ g->sweepstrgc = 0; g->sweepgc = &g->rootgc; /* reset other collector lists */ g->gray = NULL; g->grayagain = NULL; g->weak = NULL; g->gcstate = GCSsweepstring; } lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); /* finish any pending sweep phase */ while (g->gcstate != GCSfinalize) { lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); singlestep(L); } markroot(L); while (g->gcstate != GCSpause) { singlestep(L); } setthreshold(g); } void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); lua_assert(ttype(&o->gch) != LUA_TTABLE); /* must keep invariant? */ if (g->gcstate == GCSpropagate) reallymarkobject(g, v); /* restore invariant */ else /* don't mind */ makewhite(g, o); /* mark as white just to avoid other barriers */ } void luaC_barrierback (lua_State *L, Table *t) { global_State *g = G(L); GCObject *o = obj2gco(t); lua_assert(isblack(o) && !isdead(g, o)); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); black2gray(o); /* make table gray (again) */ t->gclist = g->grayagain; g->grayagain = o; } void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { global_State *g = G(L); o->gch.next = g->rootgc; g->rootgc = o; o->gch.marked = luaC_white(g); o->gch.tt = tt; } void luaC_linkupval (lua_State *L, UpVal *uv) { global_State *g = G(L); GCObject *o = obj2gco(uv); o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ g->rootgc = o; if (isgray(o)) { if (g->gcstate == GCSpropagate) { gray2black(o); /* closed upvalues need barrier */ luaC_barrier(L, uv, uv->v); } else { /* sweep phase: sweep it (turning it into white) */ makewhite(g, o); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); } } } blobby-1.0rc3/src/lua/loadlib.c0000644000175000017500000004542012042452374017675 0ustar danielknobedanielknobe/* ** $Id: loadlib.c,v 1.52.1.3 2008/08/06 13:29:28 roberto Exp $ ** Dynamic library loader for Lua ** See Copyright Notice in lua.h ** ** This module contains an implementation of loadlib for Unix systems ** that have dlfcn, an implementation for Darwin (Mac OS X), an ** implementation for Windows, and a stub for other systems. */ #include #include #define loadlib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* prefix for open functions in C libraries */ #define LUA_POF "luaopen_" /* separator for open functions in C libraries */ #define LUA_OFSEP "_" #define LIBPREFIX "LOADLIB: " #define POF LUA_POF #define LIB_FAIL "open" /* error codes for ll_loadfunc */ #define ERRLIB 1 #define ERRFUNC 2 #define setprogdir(L) ((void)0) static void ll_unloadlib (void *lib); static void *ll_load (lua_State *L, const char *path); static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); #if defined(LUA_DL_DLOPEN) /* ** {======================================================================== ** This is an implementation of loadlib based on the dlfcn interface. ** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, ** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least ** as an emulation layer on top of native functions. ** ========================================================================= */ #include static void ll_unloadlib (void *lib) { dlclose(lib); } static void *ll_load (lua_State *L, const char *path) { void *lib = dlopen(path, RTLD_NOW); if (lib == NULL) lua_pushstring(L, dlerror()); return lib; } static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = (lua_CFunction)dlsym(lib, sym); if (f == NULL) lua_pushstring(L, dlerror()); return f; } /* }====================================================== */ #elif defined(LUA_DL_DLL) /* ** {====================================================================== ** This is an implementation of loadlib for Windows using native functions. ** ======================================================================= */ #include #undef setprogdir static void setprogdir (lua_State *L) { char buff[MAX_PATH + 1]; char *lb; DWORD nsize = sizeof(buff)/sizeof(char); DWORD n = GetModuleFileNameA(NULL, buff, nsize); if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) luaL_error(L, "unable to get ModuleFileName"); else { *lb = '\0'; luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); lua_remove(L, -2); /* remove original string */ } } static void pusherror (lua_State *L) { int error = GetLastError(); char buffer[128]; if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buffer, sizeof(buffer), NULL)) lua_pushstring(L, buffer); else lua_pushfstring(L, "system error %d\n", error); } static void ll_unloadlib (void *lib) { FreeLibrary((HINSTANCE)lib); } static void *ll_load (lua_State *L, const char *path) { HINSTANCE lib = LoadLibraryA(path); if (lib == NULL) pusherror(L); return lib; } static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); if (f == NULL) pusherror(L); return f; } /* }====================================================== */ #elif defined(LUA_DL_DYLD) /* ** {====================================================================== ** Native Mac OS X / Darwin Implementation ** ======================================================================= */ #include /* Mac appends a `_' before C function names */ #undef POF #define POF "_" LUA_POF static void pusherror (lua_State *L) { const char *err_str; const char *err_file; NSLinkEditErrors err; int err_num; NSLinkEditError(&err, &err_num, &err_file, &err_str); lua_pushstring(L, err_str); } static const char *errorfromcode (NSObjectFileImageReturnCode ret) { switch (ret) { case NSObjectFileImageInappropriateFile: return "file is not a bundle"; case NSObjectFileImageArch: return "library is for wrong CPU type"; case NSObjectFileImageFormat: return "bad format"; case NSObjectFileImageAccess: return "cannot access file"; case NSObjectFileImageFailure: default: return "unable to load library"; } } static void ll_unloadlib (void *lib) { NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); } static void *ll_load (lua_State *L, const char *path) { NSObjectFileImage img; NSObjectFileImageReturnCode ret; /* this would be a rare case, but prevents crashing if it happens */ if(!_dyld_present()) { lua_pushliteral(L, "dyld not present"); return NULL; } ret = NSCreateObjectFileImageFromFile(path, &img); if (ret == NSObjectFileImageSuccess) { NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR); NSDestroyObjectFileImage(img); if (mod == NULL) pusherror(L); return mod; } lua_pushstring(L, errorfromcode(ret)); return NULL; } static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); if (nss == NULL) { lua_pushfstring(L, "symbol " LUA_QS " not found", sym); return NULL; } return (lua_CFunction)NSAddressOfSymbol(nss); } /* }====================================================== */ #else /* ** {====================================================== ** Fallback for other systems ** ======================================================= */ #undef LIB_FAIL #define LIB_FAIL "absent" #define DLMSG "dynamic libraries not enabled; check your Lua installation" static void ll_unloadlib (void *lib) { (void)lib; /* to avoid warnings */ } static void *ll_load (lua_State *L, const char *path) { (void)path; /* to avoid warnings */ lua_pushliteral(L, DLMSG); return NULL; } static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { (void)lib; (void)sym; /* to avoid warnings */ lua_pushliteral(L, DLMSG); return NULL; } /* }====================================================== */ #endif static void **ll_register (lua_State *L, const char *path) { void **plib; lua_pushfstring(L, "%s%s", LIBPREFIX, path); lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ if (!lua_isnil(L, -1)) /* is there an entry? */ plib = (void **)lua_touserdata(L, -1); else { /* no entry yet; create one */ lua_pop(L, 1); plib = (void **)lua_newuserdata(L, sizeof(const void *)); *plib = NULL; luaL_getmetatable(L, "_LOADLIB"); lua_setmetatable(L, -2); lua_pushfstring(L, "%s%s", LIBPREFIX, path); lua_pushvalue(L, -2); lua_settable(L, LUA_REGISTRYINDEX); } return plib; } /* ** __gc tag method: calls library's `ll_unloadlib' function with the lib ** handle */ static int gctm (lua_State *L) { void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); if (*lib) ll_unloadlib(*lib); *lib = NULL; /* mark library as closed */ return 0; } static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { void **reg = ll_register(L, path); if (*reg == NULL) *reg = ll_load(L, path); if (*reg == NULL) return ERRLIB; /* unable to load library */ else { lua_CFunction f = ll_sym(L, *reg, sym); if (f == NULL) return ERRFUNC; /* unable to find function */ lua_pushcfunction(L, f); return 0; /* return function */ } } static int ll_loadlib (lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *init = luaL_checkstring(L, 2); int stat = ll_loadfunc(L, path, init); if (stat == 0) /* no errors? */ return 1; /* return the loaded function */ else { /* error; error message is on stack top */ lua_pushnil(L); lua_insert(L, -2); lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); return 3; /* return nil, error message, and where */ } } /* ** {====================================================== ** 'require' function ** ======================================================= */ static int readable (const char *filename) { FILE *f = fopen(filename, "r"); /* try to open file */ if (f == NULL) return 0; /* open failed */ fclose(f); return 1; } static const char *pushnexttemplate (lua_State *L, const char *path) { const char *l; while (*path == *LUA_PATHSEP) path++; /* skip separators */ if (*path == '\0') return NULL; /* no more templates */ l = strchr(path, *LUA_PATHSEP); /* find next separator */ if (l == NULL) l = path + strlen(path); lua_pushlstring(L, path, l - path); /* template */ return l; } static const char *findfile (lua_State *L, const char *name, const char *pname) { const char *path; name = luaL_gsub(L, name, ".", LUA_DIRSEP); lua_getfield(L, LUA_ENVIRONINDEX, pname); path = lua_tostring(L, -1); if (path == NULL) luaL_error(L, LUA_QL("package.%s") " must be a string", pname); lua_pushliteral(L, ""); /* error accumulator */ while ((path = pushnexttemplate(L, path)) != NULL) { const char *filename; filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); lua_remove(L, -2); /* remove path template */ if (readable(filename)) /* does file exist and is readable? */ return filename; /* return that file name */ lua_pushfstring(L, "\n\tno file " LUA_QS, filename); lua_remove(L, -2); /* remove file name */ lua_concat(L, 2); /* add entry to possible error message */ } return NULL; /* not found */ } static void loaderror (lua_State *L, const char *filename) { luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", lua_tostring(L, 1), filename, lua_tostring(L, -1)); } static int loader_Lua (lua_State *L) { const char *filename; const char *name = luaL_checkstring(L, 1); filename = findfile(L, name, "path"); if (filename == NULL) return 1; /* library not found in this path */ if (luaL_loadfile(L, filename) != 0) loaderror(L, filename); return 1; /* library loaded successfully */ } static const char *mkfuncname (lua_State *L, const char *modname) { const char *funcname; const char *mark = strchr(modname, *LUA_IGMARK); if (mark) modname = mark + 1; funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); funcname = lua_pushfstring(L, POF"%s", funcname); lua_remove(L, -2); /* remove 'gsub' result */ return funcname; } static int loader_C (lua_State *L) { const char *funcname; const char *name = luaL_checkstring(L, 1); const char *filename = findfile(L, name, "cpath"); if (filename == NULL) return 1; /* library not found in this path */ funcname = mkfuncname(L, name); if (ll_loadfunc(L, filename, funcname) != 0) loaderror(L, filename); return 1; /* library loaded successfully */ } static int loader_Croot (lua_State *L) { const char *funcname; const char *filename; const char *name = luaL_checkstring(L, 1); const char *p = strchr(name, '.'); int stat; if (p == NULL) return 0; /* is root */ lua_pushlstring(L, name, p - name); filename = findfile(L, lua_tostring(L, -1), "cpath"); if (filename == NULL) return 1; /* root not found */ funcname = mkfuncname(L, name); if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { if (stat != ERRFUNC) loaderror(L, filename); /* real error */ lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, name, filename); return 1; /* function not found */ } return 1; } static int loader_preload (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_getfield(L, LUA_ENVIRONINDEX, "preload"); if (!lua_istable(L, -1)) luaL_error(L, LUA_QL("package.preload") " must be a table"); lua_getfield(L, -1, name); if (lua_isnil(L, -1)) /* not found? */ lua_pushfstring(L, "\n\tno field package.preload['%s']", name); return 1; } static const int sentinel_ = 0; #define sentinel ((void *)&sentinel_) static int ll_require (lua_State *L) { const char *name = luaL_checkstring(L, 1); int i; lua_settop(L, 1); /* _LOADED table will be at index 2 */ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); lua_getfield(L, 2, name); if (lua_toboolean(L, -1)) { /* is it there? */ if (lua_touserdata(L, -1) == sentinel) /* check loops */ luaL_error(L, "loop or previous error loading module " LUA_QS, name); return 1; /* package is already loaded */ } /* else must load it; iterate over available loaders */ lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); if (!lua_istable(L, -1)) luaL_error(L, LUA_QL("package.loaders") " must be a table"); lua_pushliteral(L, ""); /* error message accumulator */ for (i=1; ; i++) { lua_rawgeti(L, -2, i); /* get a loader */ if (lua_isnil(L, -1)) luaL_error(L, "module " LUA_QS " not found:%s", name, lua_tostring(L, -2)); lua_pushstring(L, name); lua_call(L, 1, 1); /* call it */ if (lua_isfunction(L, -1)) /* did it find module? */ break; /* module loaded successfully */ else if (lua_isstring(L, -1)) /* loader returned error message? */ lua_concat(L, 2); /* accumulate it */ else lua_pop(L, 1); } lua_pushlightuserdata(L, sentinel); lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ lua_pushstring(L, name); /* pass name as argument to module */ lua_call(L, 1, 1); /* run loaded module */ if (!lua_isnil(L, -1)) /* non-nil return? */ lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ lua_getfield(L, 2, name); if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ lua_pushboolean(L, 1); /* use true as result */ lua_pushvalue(L, -1); /* extra copy to be returned */ lua_setfield(L, 2, name); /* _LOADED[name] = true */ } return 1; } /* }====================================================== */ /* ** {====================================================== ** 'module' function ** ======================================================= */ static void setfenv (lua_State *L) { lua_Debug ar; if (lua_getstack(L, 1, &ar) == 0 || lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ lua_iscfunction(L, -1)) luaL_error(L, LUA_QL("module") " not called from a Lua function"); lua_pushvalue(L, -2); lua_setfenv(L, -2); lua_pop(L, 1); } static void dooptions (lua_State *L, int n) { int i; for (i = 2; i <= n; i++) { lua_pushvalue(L, i); /* get option (a function) */ lua_pushvalue(L, -2); /* module */ lua_call(L, 1, 0); } } static void modinit (lua_State *L, const char *modname) { const char *dot; lua_pushvalue(L, -1); lua_setfield(L, -2, "_M"); /* module._M = module */ lua_pushstring(L, modname); lua_setfield(L, -2, "_NAME"); dot = strrchr(modname, '.'); /* look for last dot in module name */ if (dot == NULL) dot = modname; else dot++; /* set _PACKAGE as package name (full module name minus last part) */ lua_pushlstring(L, modname, dot - modname); lua_setfield(L, -2, "_PACKAGE"); } static int ll_module (lua_State *L) { const char *modname = luaL_checkstring(L, 1); int loaded = lua_gettop(L) + 1; /* index of _LOADED table */ lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); lua_getfield(L, loaded, modname); /* get _LOADED[modname] */ if (!lua_istable(L, -1)) { /* not found? */ lua_pop(L, 1); /* remove previous result */ /* try global variable (and create one if it does not exist) */ if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL) return luaL_error(L, "name conflict for module " LUA_QS, modname); lua_pushvalue(L, -1); lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */ } /* check whether table already has a _NAME field */ lua_getfield(L, -1, "_NAME"); if (!lua_isnil(L, -1)) /* is table an initialized module? */ lua_pop(L, 1); else { /* no; initialize it */ lua_pop(L, 1); modinit(L, modname); } lua_pushvalue(L, -1); setfenv(L); dooptions(L, loaded - 1); return 0; } static int ll_seeall (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); if (!lua_getmetatable(L, 1)) { lua_createtable(L, 0, 1); /* create new metatable */ lua_pushvalue(L, -1); lua_setmetatable(L, 1); } lua_pushvalue(L, LUA_GLOBALSINDEX); lua_setfield(L, -2, "__index"); /* mt.__index = _G */ return 0; } /* }====================================================== */ /* auxiliary mark (for internal use) */ #define AUXMARK "\1" static void setpath (lua_State *L, const char *fieldname, const char *envname, const char *def) { const char *path = getenv(envname); if (path == NULL) /* no environment variable? */ lua_pushstring(L, def); /* use default */ else { /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, LUA_PATHSEP AUXMARK LUA_PATHSEP); luaL_gsub(L, path, AUXMARK, def); lua_remove(L, -2); } setprogdir(L); lua_setfield(L, -2, fieldname); } static const luaL_Reg pk_funcs[] = { {"loadlib", ll_loadlib}, {"seeall", ll_seeall}, {NULL, NULL} }; static const luaL_Reg ll_funcs[] = { {"module", ll_module}, {"require", ll_require}, {NULL, NULL} }; static const lua_CFunction loaders[] = {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; LUALIB_API int luaopen_package (lua_State *L) { int i; /* create new type _LOADLIB */ luaL_newmetatable(L, "_LOADLIB"); lua_pushcfunction(L, gctm); lua_setfield(L, -2, "__gc"); /* create `package' table */ luaL_register(L, LUA_LOADLIBNAME, pk_funcs); #if defined(LUA_COMPAT_LOADLIB) lua_getfield(L, -1, "loadlib"); lua_setfield(L, LUA_GLOBALSINDEX, "loadlib"); #endif lua_pushvalue(L, -1); lua_replace(L, LUA_ENVIRONINDEX); /* create `loaders' table */ lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1); /* fill it with pre-defined loaders */ for (i=0; loaders[i] != NULL; i++) { lua_pushcfunction(L, loaders[i]); lua_rawseti(L, -2, i+1); } lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ /* store config information */ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" LUA_EXECDIR "\n" LUA_IGMARK); lua_setfield(L, -2, "config"); /* set field `loaded' */ luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2); lua_setfield(L, -2, "loaded"); /* set field `preload' */ lua_newtable(L); lua_setfield(L, -2, "preload"); lua_pushvalue(L, LUA_GLOBALSINDEX); luaL_register(L, NULL, ll_funcs); /* open lib into global table */ lua_pop(L, 1); return 1; /* return 'package' table */ } blobby-1.0rc3/src/lua/lfunc.c0000644000175000017500000001101212042452374017364 0ustar danielknobedanielknobe/* ** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #include #define lfunc_c #define LUA_CORE #include "lua.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) { Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); luaC_link(L, obj2gco(c), LUA_TFUNCTION); c->c.isC = 1; c->c.env = e; c->c.nupvalues = cast_byte(nelems); return c; } Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); luaC_link(L, obj2gco(c), LUA_TFUNCTION); c->l.isC = 0; c->l.env = e; c->l.nupvalues = cast_byte(nelems); while (nelems--) c->l.upvals[nelems] = NULL; return c; } UpVal *luaF_newupval (lua_State *L) { UpVal *uv = luaM_new(L, UpVal); luaC_link(L, obj2gco(uv), LUA_TUPVAL); uv->v = &uv->u.value; setnilvalue(uv->v); return uv; } UpVal *luaF_findupval (lua_State *L, StkId level) { global_State *g = G(L); GCObject **pp = &L->openupval; UpVal *p; UpVal *uv; while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) { lua_assert(p->v != &p->u.value); if (p->v == level) { /* found a corresponding upvalue? */ if (isdead(g, obj2gco(p))) /* is it dead? */ changewhite(obj2gco(p)); /* ressurect it */ return p; } pp = &p->next; } uv = luaM_new(L, UpVal); /* not found: create a new one */ uv->tt = LUA_TUPVAL; uv->marked = luaC_white(g); uv->v = level; /* current value lives in the stack */ uv->next = *pp; /* chain it in the proper position */ *pp = obj2gco(uv); uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ uv->u.l.next = g->uvhead.u.l.next; uv->u.l.next->u.l.prev = uv; g->uvhead.u.l.next = uv; lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); return uv; } static void unlinkupval (UpVal *uv) { lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ uv->u.l.prev->u.l.next = uv->u.l.next; } void luaF_freeupval (lua_State *L, UpVal *uv) { if (uv->v != &uv->u.value) /* is it open? */ unlinkupval(uv); /* remove from open list */ luaM_free(L, uv); /* free upvalue */ } void luaF_close (lua_State *L, StkId level) { UpVal *uv; global_State *g = G(L); while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { GCObject *o = obj2gco(uv); lua_assert(!isblack(o) && uv->v != &uv->u.value); L->openupval = uv->next; /* remove from `open' list */ if (isdead(g, o)) luaF_freeupval(L, uv); /* free upvalue */ else { unlinkupval(uv); setobj(L, &uv->u.value, uv->v); uv->v = &uv->u.value; /* now current value lives here */ luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ } } } Proto *luaF_newproto (lua_State *L) { Proto *f = luaM_new(L, Proto); luaC_link(L, obj2gco(f), LUA_TPROTO); f->k = NULL; f->sizek = 0; f->p = NULL; f->sizep = 0; f->code = NULL; f->sizecode = 0; f->sizelineinfo = 0; f->sizeupvalues = 0; f->nups = 0; f->upvalues = NULL; f->numparams = 0; f->is_vararg = 0; f->maxstacksize = 0; f->lineinfo = NULL; f->sizelocvars = 0; f->locvars = NULL; f->linedefined = 0; f->lastlinedefined = 0; f->source = NULL; return f; } void luaF_freeproto (lua_State *L, Proto *f) { luaM_freearray(L, f->code, f->sizecode, Instruction); luaM_freearray(L, f->p, f->sizep, Proto *); luaM_freearray(L, f->k, f->sizek, TValue); luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); luaM_free(L, f); } void luaF_freeclosure (lua_State *L, Closure *c) { int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) : sizeLclosure(c->l.nupvalues); luaM_freemem(L, c, size); } /* ** Look for n-th local variable at line `line' in function `func'. ** Returns NULL if not found. */ const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { int i; for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { if (pc < f->locvars[i].endpc) { /* is variable active? */ local_number--; if (local_number == 0) return getstr(f->locvars[i].varname); } } return NULL; /* not found */ } blobby-1.0rc3/src/lua/lparser.c0000644000175000017500000010753012042452374017740 0ustar danielknobedanielknobe/* ** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ #include #define lparser_c #define LUA_CORE #include "lua.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #define hasmultret(k) ((k) == VCALL || (k) == VVARARG) #define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]]) #define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m) /* ** nodes for block list (list of active blocks) */ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ int breaklist; /* list of jumps out of this loop */ lu_byte nactvar; /* # active locals outside the breakable structure */ lu_byte upval; /* true if some variable in the block is an upvalue */ lu_byte isbreakable; /* true if `block' is a loop */ } BlockCnt; /* ** prototypes for recursive non-terminal functions */ static void chunk (LexState *ls); static void expr (LexState *ls, expdesc *v); static void anchor_token (LexState *ls) { if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { TString *ts = ls->t.seminfo.ts; luaX_newstring(ls, getstr(ts), ts->tsv.len); } } static void error_expected (LexState *ls, int token) { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token))); } static void errorlimit (FuncState *fs, int limit, const char *what) { const char *msg = (fs->f->linedefined == 0) ? luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) : luaO_pushfstring(fs->L, "function at line %d has more than %d %s", fs->f->linedefined, limit, what); luaX_lexerror(fs->ls, msg, 0); } static int testnext (LexState *ls, int c) { if (ls->t.token == c) { luaX_next(ls); return 1; } else return 0; } static void check (LexState *ls, int c) { if (ls->t.token != c) error_expected(ls, c); } static void checknext (LexState *ls, int c) { check(ls, c); luaX_next(ls); } #define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } static void check_match (LexState *ls, int what, int who, int where) { if (!testnext(ls, what)) { if (where == ls->linenumber) error_expected(ls, what); else { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, LUA_QS " expected (to close " LUA_QS " at line %d)", luaX_token2str(ls, what), luaX_token2str(ls, who), where)); } } } static TString *str_checkname (LexState *ls) { TString *ts; check(ls, TK_NAME); ts = ls->t.seminfo.ts; luaX_next(ls); return ts; } static void init_exp (expdesc *e, expkind k, int i) { e->f = e->t = NO_JUMP; e->k = k; e->u.s.info = i; } static void codestring (LexState *ls, expdesc *e, TString *s) { init_exp(e, VK, luaK_stringK(ls->fs, s)); } static void checkname(LexState *ls, expdesc *e) { codestring(ls, e, str_checkname(ls)); } static int registerlocalvar (LexState *ls, TString *varname) { FuncState *fs = ls->fs; Proto *f = fs->f; int oldsize = f->sizelocvars; luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, LocVar, SHRT_MAX, "too many local variables"); while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; f->locvars[fs->nlocvars].varname = varname; luaC_objbarrier(ls->L, f, varname); return fs->nlocvars++; } #define new_localvarliteral(ls,v,n) \ new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) static void new_localvar (LexState *ls, TString *name, int n) { FuncState *fs = ls->fs; luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); } static void adjustlocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; fs->nactvar = cast_byte(fs->nactvar + nvars); for (; nvars; nvars--) { getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; } } static void removevars (LexState *ls, int tolevel) { FuncState *fs = ls->fs; while (fs->nactvar > tolevel) getlocvar(fs, --fs->nactvar).endpc = fs->pc; } static int indexupvalue (FuncState *fs, TString *name, expdesc *v) { int i; Proto *f = fs->f; int oldsize = f->sizeupvalues; for (i=0; inups; i++) { if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { lua_assert(f->upvalues[i] == name); return i; } } /* new one */ luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues"); luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues, TString *, MAX_INT, ""); while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL; f->upvalues[f->nups] = name; luaC_objbarrier(fs->L, f, name); lua_assert(v->k == VLOCAL || v->k == VUPVAL); fs->upvalues[f->nups].k = cast_byte(v->k); fs->upvalues[f->nups].info = cast_byte(v->u.s.info); return f->nups++; } static int searchvar (FuncState *fs, TString *n) { int i; for (i=fs->nactvar-1; i >= 0; i--) { if (n == getlocvar(fs, i).varname) return i; } return -1; /* not found */ } static void markupval (FuncState *fs, int level) { BlockCnt *bl = fs->bl; while (bl && bl->nactvar > level) bl = bl->previous; if (bl) bl->upval = 1; } static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { if (fs == NULL) { /* no more levels? */ init_exp(var, VGLOBAL, NO_REG); /* default is global variable */ return VGLOBAL; } else { int v = searchvar(fs, n); /* look up at current level */ if (v >= 0) { init_exp(var, VLOCAL, v); if (!base) markupval(fs, v); /* local will be used as an upval */ return VLOCAL; } else { /* not found at current level; try upper one */ if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL) return VGLOBAL; var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */ var->k = VUPVAL; /* upvalue in this level */ return VUPVAL; } } } static void singlevar (LexState *ls, expdesc *var) { TString *varname = str_checkname(ls); FuncState *fs = ls->fs; if (singlevaraux(fs, varname, var, 1) == VGLOBAL) var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */ } static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { FuncState *fs = ls->fs; int extra = nvars - nexps; if (hasmultret(e->k)) { extra++; /* includes call itself */ if (extra < 0) extra = 0; luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ if (extra > 1) luaK_reserveregs(fs, extra-1); } else { if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ if (extra > 0) { int reg = fs->freereg; luaK_reserveregs(fs, extra); luaK_nil(fs, reg, extra); } } } static void enterlevel (LexState *ls) { if (++ls->L->nCcalls > LUAI_MAXCCALLS) luaX_lexerror(ls, "chunk has too many syntax levels", 0); } #define leavelevel(ls) ((ls)->L->nCcalls--) static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { bl->breaklist = NO_JUMP; bl->isbreakable = isbreakable; bl->nactvar = fs->nactvar; bl->upval = 0; bl->previous = fs->bl; fs->bl = bl; lua_assert(fs->freereg == fs->nactvar); } static void leaveblock (FuncState *fs) { BlockCnt *bl = fs->bl; fs->bl = bl->previous; removevars(fs->ls, bl->nactvar); if (bl->upval) luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); /* a block either controls scope or breaks (never both) */ lua_assert(!bl->isbreakable || !bl->upval); lua_assert(bl->nactvar == fs->nactvar); fs->freereg = fs->nactvar; /* free registers */ luaK_patchtohere(fs, bl->breaklist); } static void pushclosure (LexState *ls, FuncState *func, expdesc *v) { FuncState *fs = ls->fs; Proto *f = fs->f; int oldsize = f->sizep; int i; luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "constant table overflow"); while (oldsize < f->sizep) f->p[oldsize++] = NULL; f->p[fs->np++] = func->f; luaC_objbarrier(ls->L, f, func->f); init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); for (i=0; if->nups; i++) { OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); } } static void open_func (LexState *ls, FuncState *fs) { lua_State *L = ls->L; Proto *f = luaF_newproto(L); fs->f = f; fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; fs->L = L; ls->fs = fs; fs->pc = 0; fs->lasttarget = -1; fs->jpc = NO_JUMP; fs->freereg = 0; fs->nk = 0; fs->np = 0; fs->nlocvars = 0; fs->nactvar = 0; fs->bl = NULL; f->source = ls->source; f->maxstacksize = 2; /* registers 0/1 are always valid */ fs->h = luaH_new(L, 0, 0); /* anchor table of constants and prototype (to avoid being collected) */ sethvalue2s(L, L->top, fs->h); incr_top(L); setptvalue2s(L, L->top, f); incr_top(L); } static void close_func (LexState *ls) { lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; removevars(ls, 0); luaK_ret(fs, 0, 0); /* final return */ luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); f->sizecode = fs->pc; luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); f->sizelineinfo = fs->pc; luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); f->sizek = fs->nk; luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); f->sizep = fs->np; luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); f->sizelocvars = fs->nlocvars; luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *); f->sizeupvalues = f->nups; lua_assert(luaG_checkcode(f)); lua_assert(fs->bl == NULL); ls->fs = fs->prev; L->top -= 2; /* remove table and prototype from the stack */ /* last token read was anchored in defunct function; must reanchor it */ if (fs) anchor_token(ls); } Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { struct LexState lexstate; struct FuncState funcstate; lexstate.buff = buff; luaX_setinput(L, &lexstate, z, luaS_new(L, name)); open_func(&lexstate, &funcstate); funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ luaX_next(&lexstate); /* read first token */ chunk(&lexstate); check(&lexstate, TK_EOS); close_func(&lexstate); lua_assert(funcstate.prev == NULL); lua_assert(funcstate.f->nups == 0); lua_assert(lexstate.fs == NULL); return funcstate.f; } /*============================================================*/ /* GRAMMAR RULES */ /*============================================================*/ static void field (LexState *ls, expdesc *v) { /* field -> ['.' | ':'] NAME */ FuncState *fs = ls->fs; expdesc key; luaK_exp2anyreg(fs, v); luaX_next(ls); /* skip the dot or colon */ checkname(ls, &key); luaK_indexed(fs, v, &key); } static void yindex (LexState *ls, expdesc *v) { /* index -> '[' expr ']' */ luaX_next(ls); /* skip the '[' */ expr(ls, v); luaK_exp2val(ls->fs, v); checknext(ls, ']'); } /* ** {====================================================================== ** Rules for Constructors ** ======================================================================= */ struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ int nh; /* total number of `record' elements */ int na; /* total number of array elements */ int tostore; /* number of array elements pending to be stored */ }; static void recfield (LexState *ls, struct ConsControl *cc) { /* recfield -> (NAME | `['exp1`]') = exp1 */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; expdesc key, val; int rkkey; if (ls->t.token == TK_NAME) { luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); checkname(ls, &key); } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; checknext(ls, '='); rkkey = luaK_exp2RK(fs, &key); expr(ls, &val); luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val)); fs->freereg = reg; /* free registers */ } static void closelistfield (FuncState *fs, struct ConsControl *cc) { if (cc->v.k == VVOID) return; /* there is no list item */ luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; if (cc->tostore == LFIELDS_PER_FLUSH) { luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */ cc->tostore = 0; /* no more items pending */ } } static void lastlistfield (FuncState *fs, struct ConsControl *cc) { if (cc->tostore == 0) return; if (hasmultret(cc->v.k)) { luaK_setmultret(fs, &cc->v); luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET); cc->na--; /* do not count last expression (unknown number of elements) */ } else { if (cc->v.k != VVOID) luaK_exp2nextreg(fs, &cc->v); luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); } } static void listfield (LexState *ls, struct ConsControl *cc) { expr(ls, &cc->v); luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); cc->na++; cc->tostore++; } static void constructor (LexState *ls, expdesc *t) { /* constructor -> ?? */ FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); struct ConsControl cc; cc.na = cc.nh = cc.tostore = 0; cc.t = t; init_exp(t, VRELOCABLE, pc); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ checknext(ls, '{'); do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == '}') break; closelistfield(fs, &cc); switch(ls->t.token) { case TK_NAME: { /* may be listfields or recfields */ luaX_lookahead(ls); if (ls->lookahead.token != '=') /* expression? */ listfield(ls, &cc); else recfield(ls, &cc); break; } case '[': { /* constructor_item -> recfield */ recfield(ls, &cc); break; } default: { /* constructor_part -> listfield */ listfield(ls, &cc); break; } } } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc); SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ } /* }====================================================================== */ static void parlist (LexState *ls) { /* parlist -> [ param { `,' param } ] */ FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; f->is_vararg = 0; if (ls->t.token != ')') { /* is `parlist' not empty? */ do { switch (ls->t.token) { case TK_NAME: { /* param -> NAME */ new_localvar(ls, str_checkname(ls), nparams++); break; } case TK_DOTS: { /* param -> `...' */ luaX_next(ls); #if defined(LUA_COMPAT_VARARG) /* use `arg' as default name */ new_localvarliteral(ls, "arg", nparams++); f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG; #endif f->is_vararg |= VARARG_ISVARARG; break; } default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); } } while (!f->is_vararg && testnext(ls, ',')); } adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG)); luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ } static void body (LexState *ls, expdesc *e, int needself, int line) { /* body -> `(' parlist `)' chunk END */ FuncState new_fs; open_func(ls, &new_fs); new_fs.f->linedefined = line; checknext(ls, '('); if (needself) { new_localvarliteral(ls, "self", 0); adjustlocalvars(ls, 1); } parlist(ls); checknext(ls, ')'); chunk(ls); new_fs.f->lastlinedefined = ls->linenumber; check_match(ls, TK_END, TK_FUNCTION, line); close_func(ls); pushclosure(ls, &new_fs, e); } static int explist1 (LexState *ls, expdesc *v) { /* explist1 -> expr { `,' expr } */ int n = 1; /* at least one expression */ expr(ls, v); while (testnext(ls, ',')) { luaK_exp2nextreg(ls->fs, v); expr(ls, v); n++; } return n; } static void funcargs (LexState *ls, expdesc *f) { FuncState *fs = ls->fs; expdesc args; int base, nparams; int line = ls->linenumber; switch (ls->t.token) { case '(': { /* funcargs -> `(' [ explist1 ] `)' */ if (line != ls->lastline) luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; else { explist1(ls, &args); luaK_setmultret(fs, &args); } check_match(ls, ')', '(', line); break; } case '{': { /* funcargs -> constructor */ constructor(ls, &args); break; } case TK_STRING: { /* funcargs -> STRING */ codestring(ls, &args, ls->t.seminfo.ts); luaX_next(ls); /* must use `seminfo' before `next' */ break; } default: { luaX_syntaxerror(ls, "function arguments expected"); return; } } lua_assert(f->k == VNONRELOC); base = f->u.s.info; /* base register for call */ if (hasmultret(args.k)) nparams = LUA_MULTRET; /* open call */ else { if (args.k != VVOID) luaK_exp2nextreg(fs, &args); /* close last argument */ nparams = fs->freereg - (base+1); } init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); luaK_fixline(fs, line); fs->freereg = base+1; /* call remove function and arguments and leaves (unless changed) one result */ } /* ** {====================================================================== ** Expression parsing ** ======================================================================= */ static void prefixexp (LexState *ls, expdesc *v) { /* prefixexp -> NAME | '(' expr ')' */ switch (ls->t.token) { case '(': { int line = ls->linenumber; luaX_next(ls); expr(ls, v); check_match(ls, ')', '(', line); luaK_dischargevars(ls->fs, v); return; } case TK_NAME: { singlevar(ls, v); return; } default: { luaX_syntaxerror(ls, "unexpected symbol"); return; } } } static void primaryexp (LexState *ls, expdesc *v) { /* primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ FuncState *fs = ls->fs; prefixexp(ls, v); for (;;) { switch (ls->t.token) { case '.': { /* field */ field(ls, v); break; } case '[': { /* `[' exp1 `]' */ expdesc key; luaK_exp2anyreg(fs, v); yindex(ls, &key); luaK_indexed(fs, v, &key); break; } case ':': { /* `:' NAME funcargs */ expdesc key; luaX_next(ls); checkname(ls, &key); luaK_self(fs, v, &key); funcargs(ls, v); break; } case '(': case TK_STRING: case '{': { /* funcargs */ luaK_exp2nextreg(fs, v); funcargs(ls, v); break; } default: return; } } } static void simpleexp (LexState *ls, expdesc *v) { /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | FUNCTION body | primaryexp */ switch (ls->t.token) { case TK_NUMBER: { init_exp(v, VKNUM, 0); v->u.nval = ls->t.seminfo.r; break; } case TK_STRING: { codestring(ls, v, ls->t.seminfo.ts); break; } case TK_NIL: { init_exp(v, VNIL, 0); break; } case TK_TRUE: { init_exp(v, VTRUE, 0); break; } case TK_FALSE: { init_exp(v, VFALSE, 0); break; } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; check_condition(ls, fs->f->is_vararg, "cannot use " LUA_QL("...") " outside a vararg function"); fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); break; } case '{': { /* constructor */ constructor(ls, v); return; } case TK_FUNCTION: { luaX_next(ls); body(ls, v, 0, ls->linenumber); return; } default: { primaryexp(ls, v); return; } } luaX_next(ls); } static UnOpr getunopr (int op) { switch (op) { case TK_NOT: return OPR_NOT; case '-': return OPR_MINUS; case '#': return OPR_LEN; default: return OPR_NOUNOPR; } } static BinOpr getbinopr (int op) { switch (op) { case '+': return OPR_ADD; case '-': return OPR_SUB; case '*': return OPR_MUL; case '/': return OPR_DIV; case '%': return OPR_MOD; case '^': return OPR_POW; case TK_CONCAT: return OPR_CONCAT; case TK_NE: return OPR_NE; case TK_EQ: return OPR_EQ; case '<': return OPR_LT; case TK_LE: return OPR_LE; case '>': return OPR_GT; case TK_GE: return OPR_GE; case TK_AND: return OPR_AND; case TK_OR: return OPR_OR; default: return OPR_NOBINOPR; } } static const struct { lu_byte left; /* left priority for each binary operator */ lu_byte right; /* right priority */ } priority[] = { /* ORDER OPR */ {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ {10, 9}, {5, 4}, /* power and concat (right associative) */ {3, 3}, {3, 3}, /* equality and inequality */ {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ {2, 2}, {1, 1} /* logical (and/or) */ }; #define UNARY_PRIORITY 8 /* priority for unary operators */ /* ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } ** where `binop' is any binary operator with a priority higher than `limit' */ static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) { BinOpr op; UnOpr uop; enterlevel(ls); uop = getunopr(ls->t.token); if (uop != OPR_NOUNOPR) { luaX_next(ls); subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls->fs, uop, v); } else simpleexp(ls, v); /* expand while operators have priorities higher than `limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; BinOpr nextop; luaX_next(ls); luaK_infix(ls->fs, op, v); /* read sub-expression with higher priority */ nextop = subexpr(ls, &v2, priority[op].right); luaK_posfix(ls->fs, op, v, &v2); op = nextop; } leavelevel(ls); return op; /* return first untreated operator */ } static void expr (LexState *ls, expdesc *v) { subexpr(ls, v, 0); } /* }==================================================================== */ /* ** {====================================================================== ** Rules for Statements ** ======================================================================= */ static int block_follow (int token) { switch (token) { case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_UNTIL: case TK_EOS: return 1; default: return 0; } } static void block (LexState *ls) { /* block -> chunk */ FuncState *fs = ls->fs; BlockCnt bl; enterblock(fs, &bl, 0); chunk(ls); lua_assert(bl.breaklist == NO_JUMP); leaveblock(fs); } /* ** structure to chain all variables in the left-hand side of an ** assignment */ struct LHS_assign { struct LHS_assign *prev; expdesc v; /* variable (global, local, upvalue, or indexed) */ }; /* ** check whether, in an assignment to a local variable, the local variable ** is needed in a previous assignment (to a table). If so, save original ** local value in a safe place and use this safe copy in the previous ** assignment. */ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { FuncState *fs = ls->fs; int extra = fs->freereg; /* eventual position to save local variable */ int conflict = 0; for (; lh; lh = lh->prev) { if (lh->v.k == VINDEXED) { if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ conflict = 1; lh->v.u.s.info = extra; /* previous assignment will use safe copy */ } if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ conflict = 1; lh->v.u.s.aux = extra; /* previous assignment will use safe copy */ } } } if (conflict) { luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */ luaK_reserveregs(fs, 1); } } static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { expdesc e; check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, "syntax error"); if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ struct LHS_assign nv; nv.prev = lh; primaryexp(ls, &nv.v); if (nv.v.k == VLOCAL) check_conflict(ls, lh, &nv.v); luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, "variables in assignment"); assignment(ls, &nv, nvars+1); } else { /* assignment -> `=' explist1 */ int nexps; checknext(ls, '='); nexps = explist1(ls, &e); if (nexps != nvars) { adjust_assign(ls, nvars, nexps, &e); if (nexps > nvars) ls->fs->freereg -= nexps - nvars; /* remove extra values */ } else { luaK_setoneret(ls->fs, &e); /* close last expression */ luaK_storevar(ls->fs, &lh->v, &e); return; /* avoid default */ } } init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ luaK_storevar(ls->fs, &lh->v, &e); } static int cond (LexState *ls) { /* cond -> exp */ expdesc v; expr(ls, &v); /* read condition */ if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ luaK_goiftrue(ls->fs, &v); return v.f; } static void breakstat (LexState *ls) { FuncState *fs = ls->fs; BlockCnt *bl = fs->bl; int upval = 0; while (bl && !bl->isbreakable) { upval |= bl->upval; bl = bl->previous; } if (!bl) luaX_syntaxerror(ls, "no loop to break"); if (upval) luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); } static void whilestat (LexState *ls, int line) { /* whilestat -> WHILE cond DO block END */ FuncState *fs = ls->fs; int whileinit; int condexit; BlockCnt bl; luaX_next(ls); /* skip WHILE */ whileinit = luaK_getlabel(fs); condexit = cond(ls); enterblock(fs, &bl, 1); checknext(ls, TK_DO); block(ls); luaK_patchlist(fs, luaK_jump(fs), whileinit); check_match(ls, TK_END, TK_WHILE, line); leaveblock(fs); luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ } static void repeatstat (LexState *ls, int line) { /* repeatstat -> REPEAT block UNTIL cond */ int condexit; FuncState *fs = ls->fs; int repeat_init = luaK_getlabel(fs); BlockCnt bl1, bl2; enterblock(fs, &bl1, 1); /* loop block */ enterblock(fs, &bl2, 0); /* scope block */ luaX_next(ls); /* skip REPEAT */ chunk(ls); check_match(ls, TK_UNTIL, TK_REPEAT, line); condexit = cond(ls); /* read condition (inside scope block) */ if (!bl2.upval) { /* no upvalues? */ leaveblock(fs); /* finish scope */ luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */ } else { /* complete semantics when there are upvalues */ breakstat(ls); /* if condition then break */ luaK_patchtohere(ls->fs, condexit); /* else... */ leaveblock(fs); /* finish scope... */ luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */ } leaveblock(fs); /* finish loop */ } static int exp1 (LexState *ls) { expdesc e; int k; expr(ls, &e); k = e.k; luaK_exp2nextreg(ls->fs, &e); return k; } static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { /* forbody -> DO block */ BlockCnt bl; FuncState *fs = ls->fs; int prep, endfor; adjustlocalvars(ls, 3); /* control variables */ checknext(ls, TK_DO); prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); enterblock(fs, &bl, 0); /* scope for declared variables */ adjustlocalvars(ls, nvars); luaK_reserveregs(fs, nvars); block(ls); leaveblock(fs); /* end of scope for declared variables */ luaK_patchtohere(fs, prep); endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1); } static void fornum (LexState *ls, TString *varname, int line) { /* fornum -> NAME = exp1,exp1[,exp1] forbody */ FuncState *fs = ls->fs; int base = fs->freereg; new_localvarliteral(ls, "(for index)", 0); new_localvarliteral(ls, "(for limit)", 1); new_localvarliteral(ls, "(for step)", 2); new_localvar(ls, varname, 3); checknext(ls, '='); exp1(ls); /* initial value */ checknext(ls, ','); exp1(ls); /* limit */ if (testnext(ls, ',')) exp1(ls); /* optional step */ else { /* default step = 1 */ luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1)); luaK_reserveregs(fs, 1); } forbody(ls, base, line, 1, 1); } static void forlist (LexState *ls, TString *indexname) { /* forlist -> NAME {,NAME} IN explist1 forbody */ FuncState *fs = ls->fs; expdesc e; int nvars = 0; int line; int base = fs->freereg; /* create control variables */ new_localvarliteral(ls, "(for generator)", nvars++); new_localvarliteral(ls, "(for state)", nvars++); new_localvarliteral(ls, "(for control)", nvars++); /* create declared variables */ new_localvar(ls, indexname, nvars++); while (testnext(ls, ',')) new_localvar(ls, str_checkname(ls), nvars++); checknext(ls, TK_IN); line = ls->linenumber; adjust_assign(ls, 3, explist1(ls, &e), &e); luaK_checkstack(fs, 3); /* extra space to call generator */ forbody(ls, base, line, nvars - 3, 0); } static void forstat (LexState *ls, int line) { /* forstat -> FOR (fornum | forlist) END */ FuncState *fs = ls->fs; TString *varname; BlockCnt bl; enterblock(fs, &bl, 1); /* scope for loop and control variables */ luaX_next(ls); /* skip `for' */ varname = str_checkname(ls); /* first variable name */ switch (ls->t.token) { case '=': fornum(ls, varname, line); break; case ',': case TK_IN: forlist(ls, varname); break; default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); } check_match(ls, TK_END, TK_FOR, line); leaveblock(fs); /* loop scope (`break' jumps to this point) */ } static int test_then_block (LexState *ls) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ int condexit; luaX_next(ls); /* skip IF or ELSEIF */ condexit = cond(ls); checknext(ls, TK_THEN); block(ls); /* `then' part */ return condexit; } static void ifstat (LexState *ls, int line) { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ FuncState *fs = ls->fs; int flist; int escapelist = NO_JUMP; flist = test_then_block(ls); /* IF cond THEN block */ while (ls->t.token == TK_ELSEIF) { luaK_concat(fs, &escapelist, luaK_jump(fs)); luaK_patchtohere(fs, flist); flist = test_then_block(ls); /* ELSEIF cond THEN block */ } if (ls->t.token == TK_ELSE) { luaK_concat(fs, &escapelist, luaK_jump(fs)); luaK_patchtohere(fs, flist); luaX_next(ls); /* skip ELSE (after patch, for correct line info) */ block(ls); /* `else' part */ } else luaK_concat(fs, &escapelist, flist); luaK_patchtohere(fs, escapelist); check_match(ls, TK_END, TK_IF, line); } static void localfunc (LexState *ls) { expdesc v, b; FuncState *fs = ls->fs; new_localvar(ls, str_checkname(ls), 0); init_exp(&v, VLOCAL, fs->freereg); luaK_reserveregs(fs, 1); adjustlocalvars(ls, 1); body(ls, &b, 0, ls->linenumber); luaK_storevar(fs, &v, &b); /* debug information will only see the variable after this point! */ getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; } static void localstat (LexState *ls) { /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ int nvars = 0; int nexps; expdesc e; do { new_localvar(ls, str_checkname(ls), nvars++); } while (testnext(ls, ',')); if (testnext(ls, '=')) nexps = explist1(ls, &e); else { e.k = VVOID; nexps = 0; } adjust_assign(ls, nvars, nexps, &e); adjustlocalvars(ls, nvars); } static int funcname (LexState *ls, expdesc *v) { /* funcname -> NAME {field} [`:' NAME] */ int needself = 0; singlevar(ls, v); while (ls->t.token == '.') field(ls, v); if (ls->t.token == ':') { needself = 1; field(ls, v); } return needself; } static void funcstat (LexState *ls, int line) { /* funcstat -> FUNCTION funcname body */ int needself; expdesc v, b; luaX_next(ls); /* skip FUNCTION */ needself = funcname(ls, &v); body(ls, &b, needself, line); luaK_storevar(ls->fs, &v, &b); luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ } static void exprstat (LexState *ls) { /* stat -> func | assignment */ FuncState *fs = ls->fs; struct LHS_assign v; primaryexp(ls, &v.v); if (v.v.k == VCALL) /* stat -> func */ SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ else { /* stat -> assignment */ v.prev = NULL; assignment(ls, &v, 1); } } static void retstat (LexState *ls) { /* stat -> RETURN explist */ FuncState *fs = ls->fs; expdesc e; int first, nret; /* registers with returned values */ luaX_next(ls); /* skip RETURN */ if (block_follow(ls->t.token) || ls->t.token == ';') first = nret = 0; /* return no values */ else { nret = explist1(ls, &e); /* optional return values */ if (hasmultret(e.k)) { luaK_setmultret(fs, &e); if (e.k == VCALL && nret == 1) { /* tail call? */ SET_OPCODE(getcode(fs,&e), OP_TAILCALL); lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); } first = fs->nactvar; nret = LUA_MULTRET; /* return all values */ } else { if (nret == 1) /* only one single value? */ first = luaK_exp2anyreg(fs, &e); else { luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ first = fs->nactvar; /* return all `active' values */ lua_assert(nret == fs->freereg - first); } } } luaK_ret(fs, first, nret); } static int statement (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ switch (ls->t.token) { case TK_IF: { /* stat -> ifstat */ ifstat(ls, line); return 0; } case TK_WHILE: { /* stat -> whilestat */ whilestat(ls, line); return 0; } case TK_DO: { /* stat -> DO block END */ luaX_next(ls); /* skip DO */ block(ls); check_match(ls, TK_END, TK_DO, line); return 0; } case TK_FOR: { /* stat -> forstat */ forstat(ls, line); return 0; } case TK_REPEAT: { /* stat -> repeatstat */ repeatstat(ls, line); return 0; } case TK_FUNCTION: { funcstat(ls, line); /* stat -> funcstat */ return 0; } case TK_LOCAL: { /* stat -> localstat */ luaX_next(ls); /* skip LOCAL */ if (testnext(ls, TK_FUNCTION)) /* local function? */ localfunc(ls); else localstat(ls); return 0; } case TK_RETURN: { /* stat -> retstat */ retstat(ls); return 1; /* must be last statement */ } case TK_BREAK: { /* stat -> breakstat */ luaX_next(ls); /* skip BREAK */ breakstat(ls); return 1; /* must be last statement */ } default: { exprstat(ls); return 0; /* to avoid warnings */ } } } static void chunk (LexState *ls) { /* chunk -> { stat [`;'] } */ int islast = 0; enterlevel(ls); while (!islast && !block_follow(ls->t.token)) { islast = statement(ls); testnext(ls, ';'); lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && ls->fs->freereg >= ls->fs->nactvar); ls->fs->freereg = ls->fs->nactvar; /* free registers */ } leavelevel(ls); } /* }====================================================================== */ blobby-1.0rc3/src/lua/lopcodes.c0000644000175000017500000000550412042452374020076 0ustar danielknobedanielknobe/* ** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ ** See Copyright Notice in lua.h */ #define lopcodes_c #define LUA_CORE #include "lopcodes.h" /* ORDER OP */ const char *const luaP_opnames[NUM_OPCODES+1] = { "MOVE", "LOADK", "LOADBOOL", "LOADNIL", "GETUPVAL", "GETGLOBAL", "GETTABLE", "SETGLOBAL", "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL", "DIV", "MOD", "POW", "UNM", "NOT", "LEN", "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL", "RETURN", "FORLOOP", "FORPREP", "TFORLOOP", "SETLIST", "CLOSE", "CLOSURE", "VARARG", NULL }; #define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) const lu_byte luaP_opmodes[NUM_OPCODES] = { /* T A B C mode opcode */ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */ ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */ ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ }; blobby-1.0rc3/src/lua/ltable.h0000644000175000017500000000224012042452374017530 0ustar danielknobedanielknobe/* ** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ #ifndef ltable_h #define ltable_h #include "lobject.h" #define gnode(t,i) (&(t)->node[i]) #define gkey(n) (&(n)->i_key.nk) #define gval(n) (&(n)->i_val) #define gnext(n) ((n)->i_key.nk.next) #define key2tval(n) (&(n)->i_key.tvk) LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); LUAI_FUNC void luaH_free (lua_State *L, Table *t); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); LUAI_FUNC int luaH_getn (Table *t); #if defined(LUA_DEBUG) LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); LUAI_FUNC int luaH_isdummy (Node *n); #endif #endif blobby-1.0rc3/src/lua/llex.h0000644000175000017500000000420112042452374017230 0ustar danielknobedanielknobe/* ** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #ifndef llex_h #define llex_h #include "lobject.h" #include "lzio.h" #define FIRST_RESERVED 257 /* maximum length of a reserved word */ #define TOKEN_LEN (sizeof("function")/sizeof(char)) /* * WARNING: if you change the order of this enumeration, * grep "ORDER RESERVED" */ enum RESERVED { /* terminal symbols denoted by reserved words */ TK_AND = FIRST_RESERVED, TK_BREAK, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, TK_NAME, TK_STRING, TK_EOS }; /* number of reserved words */ #define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) /* array with token `names' */ LUAI_DATA const char *const luaX_tokens []; typedef union { lua_Number r; TString *ts; } SemInfo; /* semantics information */ typedef struct Token { int token; SemInfo seminfo; } Token; typedef struct LexState { int current; /* current character (charint) */ int linenumber; /* input line counter */ int lastline; /* line of last token `consumed' */ Token t; /* current token */ Token lookahead; /* look ahead token */ struct FuncState *fs; /* `FuncState' is private to the parser */ struct lua_State *L; ZIO *z; /* input stream */ Mbuffer *buff; /* buffer for tokens */ TString *source; /* current source name */ char decpoint; /* locale decimal point */ } LexState; LUAI_FUNC void luaX_init (lua_State *L); LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source); LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); LUAI_FUNC void luaX_next (LexState *ls); LUAI_FUNC void luaX_lookahead (LexState *ls); LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token); LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s); LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); #endif blobby-1.0rc3/src/lua/lgc.h0000644000175000017500000000612712042452374017042 0ustar danielknobedanielknobe/* ** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $ ** Garbage Collector ** See Copyright Notice in lua.h */ #ifndef lgc_h #define lgc_h #include "lobject.h" /* ** Possible states of the Garbage Collector */ #define GCSpause 0 #define GCSpropagate 1 #define GCSsweepstring 2 #define GCSsweep 3 #define GCSfinalize 4 /* ** some userful bit tricks */ #define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) #define setbits(x,m) ((x) |= (m)) #define testbits(x,m) ((x) & (m)) #define bitmask(b) (1<<(b)) #define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) #define l_setbit(x,b) setbits(x, bitmask(b)) #define resetbit(x,b) resetbits(x, bitmask(b)) #define testbit(x,b) testbits(x, bitmask(b)) #define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) #define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) #define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) /* ** Layout for bit use in `marked' field: ** bit 0 - object is white (type 0) ** bit 1 - object is white (type 1) ** bit 2 - object is black ** bit 3 - for userdata: has been finalized ** bit 3 - for tables: has weak keys ** bit 4 - for tables: has weak values ** bit 5 - object is fixed (should not be collected) ** bit 6 - object is "super" fixed (only the main thread) */ #define WHITE0BIT 0 #define WHITE1BIT 1 #define BLACKBIT 2 #define FINALIZEDBIT 3 #define KEYWEAKBIT 3 #define VALUEWEAKBIT 4 #define FIXEDBIT 5 #define SFIXEDBIT 6 #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) #define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) #define isblack(x) testbit((x)->gch.marked, BLACKBIT) #define isgray(x) (!isblack(x) && !iswhite(x)) #define otherwhite(g) (g->currentwhite ^ WHITEBITS) #define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) #define changewhite(x) ((x)->gch.marked ^= WHITEBITS) #define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) #define luaC_checkGC(L) { \ condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ if (G(L)->totalbytes >= G(L)->GCthreshold) \ luaC_step(L); } #define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ luaC_barrierf(L,obj2gco(p),gcvalue(v)); } #define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \ luaC_barrierback(L,t); } #define luaC_objbarrier(L,p,o) \ { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ luaC_barrierf(L,obj2gco(p),obj2gco(o)); } #define luaC_objbarriert(L,t,o) \ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); LUAI_FUNC void luaC_callGCTM (lua_State *L); LUAI_FUNC void luaC_freeall (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_fullgc (lua_State *L); LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv); LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t); #endif blobby-1.0rc3/src/lua/lfunc.h0000644000175000017500000000214512042452374017400 0ustar danielknobedanielknobe/* ** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #ifndef lfunc_h #define lfunc_h #include "lobject.h" #define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ cast(int, sizeof(TValue)*((n)-1))) #define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ cast(int, sizeof(TValue *)*((n)-1))) LUAI_FUNC Proto *luaF_newproto (lua_State *L); LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e); LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e); LUAI_FUNC UpVal *luaF_newupval (lua_State *L); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_close (lua_State *L, StkId level); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c); LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, int pc); #endif blobby-1.0rc3/src/lua/lopcodes.h0000644000175000017500000001762612042452374020113 0ustar danielknobedanielknobe/* ** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lopcodes_h #define lopcodes_h #include "llimits.h" /*=========================================================================== We assume that instructions are unsigned numbers. All instructions have an opcode in the first 6 bits. Instructions can have the following fields: `A' : 8 bits `B' : 9 bits `C' : 9 bits `Bx' : 18 bits (`B' and `C' together) `sBx' : signed Bx A signed argument is represented in excess K; that is, the number value is the unsigned value minus K. K is exactly the maximum value for that argument (so that -max is represented by 0, and +max is represented by 2*max), which is half the maximum for the corresponding unsigned argument. ===========================================================================*/ enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ /* ** size and position of opcode arguments. */ #define SIZE_C 9 #define SIZE_B 9 #define SIZE_Bx (SIZE_C + SIZE_B) #define SIZE_A 8 #define SIZE_OP 6 #define POS_OP 0 #define POS_A (POS_OP + SIZE_OP) #define POS_C (POS_A + SIZE_A) #define POS_B (POS_C + SIZE_C) #define POS_Bx POS_C /* ** limits for opcode arguments. ** we use (signed) int to manipulate most arguments, ** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) */ #if SIZE_Bx < LUAI_BITSINT-1 #define MAXARG_Bx ((1<>1) /* `sBx' is signed */ #else #define MAXARG_Bx MAX_INT #define MAXARG_sBx MAX_INT #endif #define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0))) #define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0))) #define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0))) #define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \ ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0))) #define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \ ((cast(Instruction, b)< C) then pc++ */ OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ OP_FORLOOP,/* A sBx R(A)+=R(A+2); if R(A) =) R(A)*/ OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ } OpCode; #define NUM_OPCODES (cast(int, OP_VARARG) + 1) /*=========================================================================== Notes: (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, and can be 0: OP_CALL then sets `top' to last_result+1, so next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. (*) In OP_VARARG, if (B == 0) then use actual number of varargs and set top (like in OP_CALL with C == 0). (*) In OP_RETURN, if (B == 0) then return up to `top' (*) In OP_SETLIST, if (B == 0) then B = `top'; if (C == 0) then next `instruction' is real C (*) For comparisons, A specifies what condition the test should accept (true or false). (*) All `skips' (pc++) assume that next instruction is a jump ===========================================================================*/ /* ** masks for instruction properties. The format is: ** bits 0-1: op mode ** bits 2-3: C arg mode ** bits 4-5: B arg mode ** bit 6: instruction set register A ** bit 7: operator is a test */ enum OpArgMask { OpArgN, /* argument is not used */ OpArgU, /* argument is used */ OpArgR, /* argument is a register or a jump offset */ OpArgK /* argument is a constant or register/constant */ }; LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; #define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) #define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) #define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) #define testAMode(m) (luaP_opmodes[m] & (1 << 6)) #define testTMode(m) (luaP_opmodes[m] & (1 << 7)) LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ /* number of list items to accumulate before a SETLIST instruction */ #define LFIELDS_PER_FLUSH 50 #endif blobby-1.0rc3/src/lua/lbaselib.c0000644000175000017500000004122612042452374020044 0ustar danielknobedanielknobe/* ** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $ ** Basic library ** See Copyright Notice in lua.h */ #include #include #include #include #define lbaselib_c #define LUA_LIB #include "lua.h" #include "lauxlib.h" #include "lualib.h" /* ** If your system does not support `stdout', you can just remove this function. ** If you need, you can define your own `print' function, following this ** model but changing `fputs' to put the strings at a proper place ** (a console window or a log file, for instance). */ static int luaB_print (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int i; lua_getglobal(L, "tostring"); for (i=1; i<=n; i++) { const char *s; lua_pushvalue(L, -1); /* function to be called */ lua_pushvalue(L, i); /* value to print */ lua_call(L, 1, 1); s = lua_tostring(L, -1); /* get result */ if (s == NULL) return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); if (i>1) fputs("\t", stdout); fputs(s, stdout); lua_pop(L, 1); /* pop result */ } fputs("\n", stdout); return 0; } static int luaB_tonumber (lua_State *L) { int base = luaL_optint(L, 2, 10); if (base == 10) { /* standard conversion */ luaL_checkany(L, 1); if (lua_isnumber(L, 1)) { lua_pushnumber(L, lua_tonumber(L, 1)); return 1; } } else { const char *s1 = luaL_checkstring(L, 1); char *s2; unsigned long n; luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); n = strtoul(s1, &s2, base); if (s1 != s2) { /* at least one valid digit? */ while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ if (*s2 == '\0') { /* no invalid trailing characters? */ lua_pushnumber(L, (lua_Number)n); return 1; } } } lua_pushnil(L); /* else not a number */ return 1; } static int luaB_error (lua_State *L) { int level = luaL_optint(L, 2, 1); lua_settop(L, 1); if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ luaL_where(L, level); lua_pushvalue(L, 1); lua_concat(L, 2); } return lua_error(L); } static int luaB_getmetatable (lua_State *L) { luaL_checkany(L, 1); if (!lua_getmetatable(L, 1)) { lua_pushnil(L); return 1; /* no metatable */ } luaL_getmetafield(L, 1, "__metatable"); return 1; /* returns either __metatable field (if present) or metatable */ } static int luaB_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_checktype(L, 1, LUA_TTABLE); luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table expected"); if (luaL_getmetafield(L, 1, "__metatable")) luaL_error(L, "cannot change a protected metatable"); lua_settop(L, 2); lua_setmetatable(L, 1); return 1; } static void getfunc (lua_State *L, int opt) { if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); else { lua_Debug ar; int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1); luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); if (lua_getstack(L, level, &ar) == 0) luaL_argerror(L, 1, "invalid level"); lua_getinfo(L, "f", &ar); if (lua_isnil(L, -1)) luaL_error(L, "no function environment for tail call at level %d", level); } } static int luaB_getfenv (lua_State *L) { getfunc(L, 1); if (lua_iscfunction(L, -1)) /* is a C function? */ lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ else lua_getfenv(L, -1); return 1; } static int luaB_setfenv (lua_State *L) { luaL_checktype(L, 2, LUA_TTABLE); getfunc(L, 0); lua_pushvalue(L, 2); if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) { /* change environment of current thread */ lua_pushthread(L); lua_insert(L, -2); lua_setfenv(L, -2); return 0; } else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0) luaL_error(L, LUA_QL("setfenv") " cannot change environment of given object"); return 1; } static int luaB_rawequal (lua_State *L) { luaL_checkany(L, 1); luaL_checkany(L, 2); lua_pushboolean(L, lua_rawequal(L, 1, 2)); return 1; } static int luaB_rawget (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); lua_settop(L, 2); lua_rawget(L, 1); return 1; } static int luaB_rawset (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); luaL_checkany(L, 3); lua_settop(L, 3); lua_rawset(L, 1); return 1; } static int luaB_gcinfo (lua_State *L) { lua_pushinteger(L, lua_getgccount(L)); return 1; } static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", "count", "step", "setpause", "setstepmul", NULL}; static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL}; int o = luaL_checkoption(L, 1, "collect", opts); int ex = luaL_optint(L, 2, 0); int res = lua_gc(L, optsnum[o], ex); switch (optsnum[o]) { case LUA_GCCOUNT: { int b = lua_gc(L, LUA_GCCOUNTB, 0); lua_pushnumber(L, res + ((lua_Number)b/1024)); return 1; } case LUA_GCSTEP: { lua_pushboolean(L, res); return 1; } default: { lua_pushnumber(L, res); return 1; } } } static int luaB_type (lua_State *L) { luaL_checkany(L, 1); lua_pushstring(L, luaL_typename(L, 1)); return 1; } static int luaB_next (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); /* create a 2nd argument if there isn't one */ if (lua_next(L, 1)) return 2; else { lua_pushnil(L); return 1; } } static int luaB_pairs (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ lua_pushvalue(L, 1); /* state, */ lua_pushnil(L); /* and initial value */ return 3; } static int ipairsaux (lua_State *L) { int i = luaL_checkint(L, 2); luaL_checktype(L, 1, LUA_TTABLE); i++; /* next value */ lua_pushinteger(L, i); lua_rawgeti(L, 1, i); return (lua_isnil(L, -1)) ? 0 : 2; } static int luaB_ipairs (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ lua_pushvalue(L, 1); /* state, */ lua_pushinteger(L, 0); /* and initial value */ return 3; } static int load_aux (lua_State *L, int status) { if (status == 0) /* OK? */ return 1; else { lua_pushnil(L); lua_insert(L, -2); /* put before error message */ return 2; /* return nil plus error message */ } } static int luaB_loadstring (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); const char *chunkname = luaL_optstring(L, 2, s); return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); } static int luaB_loadfile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); return load_aux(L, luaL_loadfile(L, fname)); } /* ** Reader for generic `load' function: `lua_load' uses the ** stack for internal stuff, so the reader cannot change the ** stack top. Instead, it keeps its resulting string in a ** reserved slot inside the stack. */ static const char *generic_reader (lua_State *L, void *ud, size_t *size) { (void)ud; /* to avoid warnings */ luaL_checkstack(L, 2, "too many nested functions"); lua_pushvalue(L, 1); /* get function */ lua_call(L, 0, 1); /* call it */ if (lua_isnil(L, -1)) { *size = 0; return NULL; } else if (lua_isstring(L, -1)) { lua_replace(L, 3); /* save string in a reserved stack slot */ return lua_tolstring(L, 3, size); } else luaL_error(L, "reader function must return a string"); return NULL; /* to avoid warnings */ } static int luaB_load (lua_State *L) { int status; const char *cname = luaL_optstring(L, 2, "=(load)"); luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L, 3); /* function, eventual name, plus one reserved slot */ status = lua_load(L, generic_reader, NULL, cname); return load_aux(L, status); } static int luaB_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); int n = lua_gettop(L); if (luaL_loadfile(L, fname) != 0) lua_error(L); lua_call(L, 0, LUA_MULTRET); return lua_gettop(L) - n; } static int luaB_assert (lua_State *L) { luaL_checkany(L, 1); if (!lua_toboolean(L, 1)) return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); return lua_gettop(L); } static int luaB_unpack (lua_State *L) { int i, e, n; luaL_checktype(L, 1, LUA_TTABLE); i = luaL_optint(L, 2, 1); e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); if (i > e) return 0; /* empty range */ n = e - i + 1; /* number of elements */ if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */ return luaL_error(L, "too many results to unpack"); lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */ while (i++ < e) /* push arg[i + 1...e] */ lua_rawgeti(L, 1, i); return n; } static int luaB_select (lua_State *L) { int n = lua_gettop(L); if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { lua_pushinteger(L, n-1); return 1; } else { int i = luaL_checkint(L, 1); if (i < 0) i = n + i; else if (i > n) i = n; luaL_argcheck(L, 1 <= i, 1, "index out of range"); return n - i; } } static int luaB_pcall (lua_State *L) { int status; luaL_checkany(L, 1); status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); lua_pushboolean(L, (status == 0)); lua_insert(L, 1); return lua_gettop(L); /* return status + all results */ } static int luaB_xpcall (lua_State *L) { int status; luaL_checkany(L, 2); lua_settop(L, 2); lua_insert(L, 1); /* put error function under function to be called */ status = lua_pcall(L, 0, LUA_MULTRET, 1); lua_pushboolean(L, (status == 0)); lua_replace(L, 1); return lua_gettop(L); /* return status + all results */ } static int luaB_tostring (lua_State *L) { luaL_checkany(L, 1); if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ return 1; /* use its value */ switch (lua_type(L, 1)) { case LUA_TNUMBER: lua_pushstring(L, lua_tostring(L, 1)); break; case LUA_TSTRING: lua_pushvalue(L, 1); break; case LUA_TBOOLEAN: lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); break; case LUA_TNIL: lua_pushliteral(L, "nil"); break; default: lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); break; } return 1; } static int luaB_newproxy (lua_State *L) { lua_settop(L, 1); lua_newuserdata(L, 0); /* create proxy */ if (lua_toboolean(L, 1) == 0) return 1; /* no metatable */ else if (lua_isboolean(L, 1)) { lua_newtable(L); /* create a new metatable `m' ... */ lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ lua_pushboolean(L, 1); lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ } else { int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ if (lua_getmetatable(L, 1)) { lua_rawget(L, lua_upvalueindex(1)); validproxy = lua_toboolean(L, -1); lua_pop(L, 1); /* remove value */ } luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); lua_getmetatable(L, 1); /* metatable is valid; get it */ } lua_setmetatable(L, 2); return 1; } static const luaL_Reg base_funcs[] = { {"assert", luaB_assert}, {"collectgarbage", luaB_collectgarbage}, {"dofile", luaB_dofile}, {"error", luaB_error}, {"gcinfo", luaB_gcinfo}, {"getfenv", luaB_getfenv}, {"getmetatable", luaB_getmetatable}, {"loadfile", luaB_loadfile}, {"load", luaB_load}, {"loadstring", luaB_loadstring}, {"next", luaB_next}, {"pcall", luaB_pcall}, {"print", luaB_print}, {"rawequal", luaB_rawequal}, {"rawget", luaB_rawget}, {"rawset", luaB_rawset}, {"select", luaB_select}, {"setfenv", luaB_setfenv}, {"setmetatable", luaB_setmetatable}, {"tonumber", luaB_tonumber}, {"tostring", luaB_tostring}, {"type", luaB_type}, {"unpack", luaB_unpack}, {"xpcall", luaB_xpcall}, {NULL, NULL} }; /* ** {====================================================== ** Coroutine library ** ======================================================= */ #define CO_RUN 0 /* running */ #define CO_SUS 1 /* suspended */ #define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ #define CO_DEAD 3 static const char *const statnames[] = {"running", "suspended", "normal", "dead"}; static int costatus (lua_State *L, lua_State *co) { if (L == co) return CO_RUN; switch (lua_status(co)) { case LUA_YIELD: return CO_SUS; case 0: { lua_Debug ar; if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ return CO_NOR; /* it is running */ else if (lua_gettop(co) == 0) return CO_DEAD; else return CO_SUS; /* initial state */ } default: /* some error occured */ return CO_DEAD; } } static int luaB_costatus (lua_State *L) { lua_State *co = lua_tothread(L, 1); luaL_argcheck(L, co, 1, "coroutine expected"); lua_pushstring(L, statnames[costatus(L, co)]); return 1; } static int auxresume (lua_State *L, lua_State *co, int narg) { int status = costatus(L, co); if (!lua_checkstack(co, narg)) luaL_error(L, "too many arguments to resume"); if (status != CO_SUS) { lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); return -1; /* error flag */ } lua_xmove(L, co, narg); lua_setlevel(L, co); status = lua_resume(co, narg); if (status == 0 || status == LUA_YIELD) { int nres = lua_gettop(co); if (!lua_checkstack(L, nres + 1)) luaL_error(L, "too many results to resume"); lua_xmove(co, L, nres); /* move yielded values */ return nres; } else { lua_xmove(co, L, 1); /* move error message */ return -1; /* error flag */ } } static int luaB_coresume (lua_State *L) { lua_State *co = lua_tothread(L, 1); int r; luaL_argcheck(L, co, 1, "coroutine expected"); r = auxresume(L, co, lua_gettop(L) - 1); if (r < 0) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; /* return false + error message */ } else { lua_pushboolean(L, 1); lua_insert(L, -(r + 1)); return r + 1; /* return true + `resume' returns */ } } static int luaB_auxwrap (lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); int r = auxresume(L, co, lua_gettop(L)); if (r < 0) { if (lua_isstring(L, -1)) { /* error object is a string? */ luaL_where(L, 1); /* add extra info */ lua_insert(L, -2); lua_concat(L, 2); } lua_error(L); /* propagate error */ } return r; } static int luaB_cocreate (lua_State *L) { lua_State *NL = lua_newthread(L); luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); lua_pushvalue(L, 1); /* move function to top */ lua_xmove(L, NL, 1); /* move function from L to NL */ return 1; } static int luaB_cowrap (lua_State *L) { luaB_cocreate(L); lua_pushcclosure(L, luaB_auxwrap, 1); return 1; } static int luaB_yield (lua_State *L) { return lua_yield(L, lua_gettop(L)); } static int luaB_corunning (lua_State *L) { if (lua_pushthread(L)) lua_pushnil(L); /* main thread is not a coroutine */ return 1; } static const luaL_Reg co_funcs[] = { {"create", luaB_cocreate}, {"resume", luaB_coresume}, {"running", luaB_corunning}, {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, {NULL, NULL} }; /* }====================================================== */ static void auxopen (lua_State *L, const char *name, lua_CFunction f, lua_CFunction u) { lua_pushcfunction(L, u); lua_pushcclosure(L, f, 1); lua_setfield(L, -2, name); } static void base_open (lua_State *L) { /* set global _G */ lua_pushvalue(L, LUA_GLOBALSINDEX); lua_setglobal(L, "_G"); /* open lib into global table */ luaL_register(L, "_G", base_funcs); lua_pushliteral(L, LUA_VERSION); lua_setglobal(L, "_VERSION"); /* set global _VERSION */ /* `ipairs' and `pairs' need auxliliary functions as upvalues */ auxopen(L, "ipairs", luaB_ipairs, ipairsaux); auxopen(L, "pairs", luaB_pairs, luaB_next); /* `newproxy' needs a weaktable as upvalue */ lua_createtable(L, 0, 1); /* new table `w' */ lua_pushvalue(L, -1); /* `w' will be its own metatable */ lua_setmetatable(L, -2); lua_pushliteral(L, "kv"); lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */ lua_pushcclosure(L, luaB_newproxy, 1); lua_setglobal(L, "newproxy"); /* set global `newproxy' */ } LUALIB_API int luaopen_base (lua_State *L) { base_open(L); luaL_register(L, LUA_COLIBNAME, co_funcs); return 2; } blobby-1.0rc3/src/lua/lparser.h0000644000175000017500000000432512042452374017743 0ustar danielknobedanielknobe/* ** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $ ** Lua Parser ** See Copyright Notice in lua.h */ #ifndef lparser_h #define lparser_h #include "llimits.h" #include "lobject.h" #include "lzio.h" /* ** Expression descriptor */ typedef enum { VVOID, /* no value */ VNIL, VTRUE, VFALSE, VK, /* info = index of constant in `k' */ VKNUM, /* nval = numerical value */ VLOCAL, /* info = local register */ VUPVAL, /* info = index of upvalue in `upvalues' */ VGLOBAL, /* info = index of table; aux = index of global name in `k' */ VINDEXED, /* info = table register; aux = index register (or `k') */ VJMP, /* info = instruction pc */ VRELOCABLE, /* info = instruction pc */ VNONRELOC, /* info = result register */ VCALL, /* info = instruction pc */ VVARARG /* info = instruction pc */ } expkind; typedef struct expdesc { expkind k; union { struct { int info, aux; } s; lua_Number nval; } u; int t; /* patch list of `exit when true' */ int f; /* patch list of `exit when false' */ } expdesc; typedef struct upvaldesc { lu_byte k; lu_byte info; } upvaldesc; struct BlockCnt; /* defined in lparser.c */ /* state needed to generate code for a given function */ typedef struct FuncState { Proto *f; /* current function header */ Table *h; /* table to find (and reuse) elements in `k' */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct lua_State *L; /* copy of the Lua state */ struct BlockCnt *bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to `ncode') */ int lasttarget; /* `pc' of last `jump target' */ int jpc; /* list of pending jumps to `pc' */ int freereg; /* first free register */ int nk; /* number of elements in `k' */ int np; /* number of elements in `p' */ short nlocvars; /* number of elements in `locvars' */ lu_byte nactvar; /* number of active local variables */ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ } FuncState; LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name); #endif blobby-1.0rc3/src/lua/lzio.c0000644000175000017500000000313412042452374017240 0ustar danielknobedanielknobe/* ** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ ** a generic input stream interface ** See Copyright Notice in lua.h */ #include #define lzio_c #define LUA_CORE #include "lua.h" #include "llimits.h" #include "lmem.h" #include "lstate.h" #include "lzio.h" int luaZ_fill (ZIO *z) { size_t size; lua_State *L = z->L; const char *buff; lua_unlock(L); buff = z->reader(L, z->data, &size); lua_lock(L); if (buff == NULL || size == 0) return EOZ; z->n = size - 1; z->p = buff; return char2int(*(z->p++)); } int luaZ_lookahead (ZIO *z) { if (z->n == 0) { if (luaZ_fill(z) == EOZ) return EOZ; else { z->n++; /* luaZ_fill removed first byte; put back it */ z->p--; } } return char2int(*z->p); } void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { z->L = L; z->reader = reader; z->data = data; z->n = 0; z->p = NULL; } /* --------------------------------------------------------------- read --- */ size_t luaZ_read (ZIO *z, void *b, size_t n) { while (n) { size_t m; if (luaZ_lookahead(z) == EOZ) return n; /* return number of missing bytes */ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ memcpy(b, z->p, m); z->n -= m; z->p += m; b = (char *)b + m; n -= m; } return 0; } /* ------------------------------------------------------------------------ */ char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { if (n > buff->buffsize) { if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; luaZ_resizebuffer(L, buff, n); } return buff->buffer; } blobby-1.0rc3/src/lua/linit.c0000644000175000017500000000137512042452374017407 0ustar danielknobedanielknobe/* ** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ ** Initialization of libraries for lua.c ** See Copyright Notice in lua.h */ #define linit_c #define LUA_LIB #include "lua.h" #include "lualib.h" #include "lauxlib.h" static const luaL_Reg lualibs[] = { {"", luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_DBLIBNAME, luaopen_debug}, {NULL, NULL} }; LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib = lualibs; for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); lua_call(L, 1, 0); } } blobby-1.0rc3/src/lua/lobject.c0000644000175000017500000001257212042452374017713 0ustar danielknobedanielknobe/* ** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ #include #include #include #include #include #define lobject_c #define LUA_CORE #include "lua.h" #include "ldo.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "lvm.h" const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL}; /* ** converts an integer to a "floating point byte", represented as ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if ** eeeee != 0 and (xxx) otherwise. */ int luaO_int2fb (unsigned int x) { int e = 0; /* expoent */ while (x >= 16) { x = (x+1) >> 1; e++; } if (x < 8) return x; else return ((e+1) << 3) | (cast_int(x) - 8); } /* converts back */ int luaO_fb2int (int x) { int e = (x >> 3) & 31; if (e == 0) return x; else return ((x & 7)+8) << (e - 1); } int luaO_log2 (unsigned int x) { static const lu_byte log_2[256] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }; int l = -1; while (x >= 256) { l += 8; x >>= 8; } return l + log_2[x]; } int luaO_rawequalObj (const TValue *t1, const TValue *t2) { if (ttype(t1) != ttype(t2)) return 0; else switch (ttype(t1)) { case LUA_TNIL: return 1; case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); default: lua_assert(iscollectable(t1)); return gcvalue(t1) == gcvalue(t2); } } int luaO_str2d (const char *s, lua_Number *result) { char *endptr; *result = lua_str2number(s, &endptr); if (endptr == s) return 0; /* conversion failed */ if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ *result = cast_num(strtoul(s, &endptr, 16)); if (*endptr == '\0') return 1; /* most common case */ while (isspace(cast(unsigned char, *endptr))) endptr++; if (*endptr != '\0') return 0; /* invalid trailing characters? */ return 1; } static void pushstr (lua_State *L, const char *str) { setsvalue2s(L, L->top, luaS_new(L, str)); incr_top(L); } /* this function handles only `%d', `%c', %f, %p, and `%s' formats */ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { int n = 1; pushstr(L, ""); for (;;) { const char *e = strchr(fmt, '%'); if (e == NULL) break; setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt)); incr_top(L); switch (*(e+1)) { case 's': { const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; pushstr(L, s); break; } case 'c': { char buff[2]; buff[0] = cast(char, va_arg(argp, int)); buff[1] = '\0'; pushstr(L, buff); break; } case 'd': { setnvalue(L->top, cast_num(va_arg(argp, int))); incr_top(L); break; } case 'f': { setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); incr_top(L); break; } case 'p': { char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ sprintf(buff, "%p", va_arg(argp, void *)); pushstr(L, buff); break; } case '%': { pushstr(L, "%"); break; } default: { char buff[3]; buff[0] = '%'; buff[1] = *(e+1); buff[2] = '\0'; pushstr(L, buff); break; } } n += 2; fmt = e+2; } pushstr(L, fmt); luaV_concat(L, n+1, cast_int(L->top - L->base) - 1); L->top -= n; return svalue(L->top - 1); } const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { const char *msg; va_list argp; va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); va_end(argp); return msg; } void luaO_chunkid (char *out, const char *source, size_t bufflen) { if (*source == '=') { strncpy(out, source+1, bufflen); /* remove first char */ out[bufflen-1] = '\0'; /* ensures null termination */ } else { /* out = "source", or "...source" */ if (*source == '@') { size_t l; source++; /* skip the `@' */ bufflen -= sizeof(" '...' "); l = strlen(source); strcpy(out, ""); if (l > bufflen) { source += (l-bufflen); /* get last part of file name */ strcat(out, "..."); } strcat(out, source); } else { /* out = [string "string"] */ size_t len = strcspn(source, "\n\r"); /* stop at first newline */ bufflen -= sizeof(" [string \"...\"] "); if (len > bufflen) len = bufflen; strcpy(out, "[string \""); if (source[len] != '\0') { /* must truncate? */ strncat(out, source, len); strcat(out, "..."); } else strcat(out, source); strcat(out, "\"]"); } } } blobby-1.0rc3/src/lua/llimits.h0000644000175000017500000000445512042452374017754 0ustar danielknobedanielknobe/* ** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $ ** Limits, basic types, and some other `installation-dependent' definitions ** See Copyright Notice in lua.h */ #ifndef llimits_h #define llimits_h #include #include #include "lua.h" typedef LUAI_UINT32 lu_int32; typedef LUAI_UMEM lu_mem; typedef LUAI_MEM l_mem; /* chars used as small naturals (so that `char' is reserved for characters) */ typedef unsigned char lu_byte; #define MAX_SIZET ((size_t)(~(size_t)0)-2) #define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) #define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ /* ** conversion of pointer to integer ** this is for hashing only; there is no problem if the integer ** cannot hold the whole pointer value */ #define IntPoint(p) ((unsigned int)(lu_mem)(p)) /* type to ensure maximum alignment */ typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; /* result of a `usual argument conversion' over lua_Number */ typedef LUAI_UACNUMBER l_uacNumber; /* internal assertions for in-house debugging */ #ifdef lua_assert #define check_exp(c,e) (lua_assert(c), (e)) #define api_check(l,e) lua_assert(e) #else #define lua_assert(c) ((void)0) #define check_exp(c,e) (e) #define api_check luai_apicheck #endif #ifndef UNUSED #define UNUSED(x) ((void)(x)) /* to avoid warnings */ #endif #ifndef cast #define cast(t, exp) ((t)(exp)) #endif #define cast_byte(i) cast(lu_byte, (i)) #define cast_num(i) cast(lua_Number, (i)) #define cast_int(i) cast(int, (i)) /* ** type for virtual-machine instructions ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) */ typedef lu_int32 Instruction; /* maximum stack for a Lua function */ #define MAXSTACK 250 /* minimum size for the string table (must be power of 2) */ #ifndef MINSTRTABSIZE #define MINSTRTABSIZE 32 #endif /* minimum size for string buffer */ #ifndef LUA_MINBUFFER #define LUA_MINBUFFER 32 #endif #ifndef lua_lock #define lua_lock(L) ((void) 0) #define lua_unlock(L) ((void) 0) #endif #ifndef luai_threadyield #define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} #endif /* ** macro to control inclusion of some hard tests on stack reallocation */ #ifndef HARDSTACKTESTS #define condhardstacktests(x) ((void)0) #else #define condhardstacktests(x) x #endif #endif blobby-1.0rc3/src/lua/lstring.c0000644000175000017500000000604612042452374017752 0ustar danielknobedanielknobe/* ** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ #include #define lstring_c #define LUA_CORE #include "lua.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" void luaS_resize (lua_State *L, int newsize) { GCObject **newhash; stringtable *tb; int i; if (G(L)->gcstate == GCSsweepstring) return; /* cannot resize during GC traverse */ newhash = luaM_newvector(L, newsize, GCObject *); tb = &G(L)->strt; for (i=0; isize; i++) { GCObject *p = tb->hash[i]; while (p) { /* for each node in the list */ GCObject *next = p->gch.next; /* save next */ unsigned int h = gco2ts(p)->hash; int h1 = lmod(h, newsize); /* new position */ lua_assert(cast_int(h%newsize) == lmod(h, newsize)); p->gch.next = newhash[h1]; /* chain it */ newhash[h1] = p; p = next; } } luaM_freearray(L, tb->hash, tb->size, TString *); tb->size = newsize; tb->hash = newhash; } static TString *newlstr (lua_State *L, const char *str, size_t l, unsigned int h) { TString *ts; stringtable *tb; if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) luaM_toobig(L); ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); ts->tsv.len = l; ts->tsv.hash = h; ts->tsv.marked = luaC_white(G(L)); ts->tsv.tt = LUA_TSTRING; ts->tsv.reserved = 0; memcpy(ts+1, str, l*sizeof(char)); ((char *)(ts+1))[l] = '\0'; /* ending 0 */ tb = &G(L)->strt; h = lmod(h, tb->size); ts->tsv.next = tb->hash[h]; /* chain new entry */ tb->hash[h] = obj2gco(ts); tb->nuse++; if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) luaS_resize(L, tb->size*2); /* too crowded */ return ts; } TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { GCObject *o; unsigned int h = cast(unsigned int, l); /* seed */ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ size_t l1; for (l1=l; l1>=step; l1-=step) /* compute hash */ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; o != NULL; o = o->gch.next) { TString *ts = rawgco2ts(o); if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { /* string may be dead */ if (isdead(G(L), o)) changewhite(o); return ts; } } return newlstr(L, str, l, h); /* not found */ } Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { Udata *u; if (s > MAX_SIZET - sizeof(Udata)) luaM_toobig(L); u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); u->uv.marked = luaC_white(G(L)); /* is not finalized */ u->uv.tt = LUA_TUSERDATA; u->uv.len = s; u->uv.metatable = NULL; u->uv.env = e; /* chain it on udata list (after main thread) */ u->uv.next = G(L)->mainthread->next; G(L)->mainthread->next = obj2gco(u); return u; } blobby-1.0rc3/src/lua/lzio.h0000644000175000017500000000302412042452374017243 0ustar danielknobedanielknobe/* ** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $ ** Buffered streams ** See Copyright Notice in lua.h */ #ifndef lzio_h #define lzio_h #include "lua.h" #include "lmem.h" #define EOZ (-1) /* end of stream */ typedef struct Zio ZIO; #define char2int(c) cast(int, cast(unsigned char, (c))) #define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z)) typedef struct Mbuffer { char *buffer; size_t n; size_t buffsize; } Mbuffer; #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) #define luaZ_buffer(buff) ((buff)->buffer) #define luaZ_sizebuffer(buff) ((buff)->buffsize) #define luaZ_bufflen(buff) ((buff)->n) #define luaZ_resetbuffer(buff) ((buff)->n = 0) #define luaZ_resizebuffer(L, buff, size) \ (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ (buff)->buffsize = size) #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data); LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ LUAI_FUNC int luaZ_lookahead (ZIO *z); /* --------- Private Part ------------------ */ struct Zio { size_t n; /* bytes still unread */ const char *p; /* current position in buffer */ lua_Reader reader; void* data; /* additional data */ lua_State *L; /* Lua state (for reader) */ }; LUAI_FUNC int luaZ_fill (ZIO *z); #endif blobby-1.0rc3/src/lua/lapi.c0000644000175000017500000005426412042452374017222 0ustar danielknobedanielknobe/* ** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $ ** Lua API ** See Copyright Notice in lua.h */ #include #include #include #include #define lapi_c #define LUA_CORE #include "lua.h" #include "lapi.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lgc.h" #include "lmem.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lundump.h" #include "lvm.h" const char lua_ident[] = "$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n" "$Authors: " LUA_AUTHORS " $\n" "$URL: www.lua.org $\n"; #define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) #define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject) #define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} static TValue *index2adr (lua_State *L, int idx) { if (idx > 0) { TValue *o = L->base + (idx - 1); api_check(L, idx <= L->ci->top - L->base); if (o >= L->top) return cast(TValue *, luaO_nilobject); else return o; } else if (idx > LUA_REGISTRYINDEX) { api_check(L, idx != 0 && -idx <= L->top - L->base); return L->top + idx; } else switch (idx) { /* pseudo-indices */ case LUA_REGISTRYINDEX: return registry(L); case LUA_ENVIRONINDEX: { Closure *func = curr_func(L); sethvalue(L, &L->env, func->c.env); return &L->env; } case LUA_GLOBALSINDEX: return gt(L); default: { Closure *func = curr_func(L); idx = LUA_GLOBALSINDEX - idx; return (idx <= func->c.nupvalues) ? &func->c.upvalue[idx-1] : cast(TValue *, luaO_nilobject); } } } static Table *getcurrenv (lua_State *L) { if (L->ci == L->base_ci) /* no enclosing function? */ return hvalue(gt(L)); /* use global table as environment */ else { Closure *func = curr_func(L); return func->c.env; } } void luaA_pushobject (lua_State *L, const TValue *o) { setobj2s(L, L->top, o); api_incr_top(L); } LUA_API int lua_checkstack (lua_State *L, int size) { int res = 1; lua_lock(L); if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) res = 0; /* stack overflow */ else if (size > 0) { luaD_checkstack(L, size); if (L->ci->top < L->top + size) L->ci->top = L->top + size; } lua_unlock(L); return res; } LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { int i; if (from == to) return; lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to)); api_check(from, to->ci->top - to->top >= n); from->top -= n; for (i = 0; i < n; i++) { setobj2s(to, to->top++, from->top + i); } lua_unlock(to); } LUA_API void lua_setlevel (lua_State *from, lua_State *to) { to->nCcalls = from->nCcalls; } LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { lua_CFunction old; lua_lock(L); old = G(L)->panic; G(L)->panic = panicf; lua_unlock(L); return old; } LUA_API lua_State *lua_newthread (lua_State *L) { lua_State *L1; lua_lock(L); luaC_checkGC(L); L1 = luaE_newthread(L); setthvalue(L, L->top, L1); api_incr_top(L); lua_unlock(L); luai_userstatethread(L, L1); return L1; } /* ** basic stack manipulation */ LUA_API int lua_gettop (lua_State *L) { return cast_int(L->top - L->base); } LUA_API void lua_settop (lua_State *L, int idx) { lua_lock(L); if (idx >= 0) { api_check(L, idx <= L->stack_last - L->base); while (L->top < L->base + idx) setnilvalue(L->top++); L->top = L->base + idx; } else { api_check(L, -(idx+1) <= (L->top - L->base)); L->top += idx+1; /* `subtract' index (index is negative) */ } lua_unlock(L); } LUA_API void lua_remove (lua_State *L, int idx) { StkId p; lua_lock(L); p = index2adr(L, idx); api_checkvalidindex(L, p); while (++p < L->top) setobjs2s(L, p-1, p); L->top--; lua_unlock(L); } LUA_API void lua_insert (lua_State *L, int idx) { StkId p; StkId q; lua_lock(L); p = index2adr(L, idx); api_checkvalidindex(L, p); for (q = L->top; q>p; q--) setobjs2s(L, q, q-1); setobjs2s(L, p, L->top); lua_unlock(L); } LUA_API void lua_replace (lua_State *L, int idx) { StkId o; lua_lock(L); /* explicit test for incompatible code */ if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci) luaG_runerror(L, "no calling environment"); api_checknelems(L, 1); o = index2adr(L, idx); api_checkvalidindex(L, o); if (idx == LUA_ENVIRONINDEX) { Closure *func = curr_func(L); api_check(L, ttistable(L->top - 1)); func->c.env = hvalue(L->top - 1); luaC_barrier(L, func, L->top - 1); } else { setobj(L, o, L->top - 1); if (idx < LUA_GLOBALSINDEX) /* function upvalue? */ luaC_barrier(L, curr_func(L), L->top - 1); } L->top--; lua_unlock(L); } LUA_API void lua_pushvalue (lua_State *L, int idx) { lua_lock(L); setobj2s(L, L->top, index2adr(L, idx)); api_incr_top(L); lua_unlock(L); } /* ** access functions (stack -> C) */ LUA_API int lua_type (lua_State *L, int idx) { StkId o = index2adr(L, idx); return (o == luaO_nilobject) ? LUA_TNONE : ttype(o); } LUA_API const char *lua_typename (lua_State *L, int t) { UNUSED(L); return (t == LUA_TNONE) ? "no value" : luaT_typenames[t]; } LUA_API int lua_iscfunction (lua_State *L, int idx) { StkId o = index2adr(L, idx); return iscfunction(o); } LUA_API int lua_isnumber (lua_State *L, int idx) { TValue n; const TValue *o = index2adr(L, idx); return tonumber(o, &n); } LUA_API int lua_isstring (lua_State *L, int idx) { int t = lua_type(L, idx); return (t == LUA_TSTRING || t == LUA_TNUMBER); } LUA_API int lua_isuserdata (lua_State *L, int idx) { const TValue *o = index2adr(L, idx); return (ttisuserdata(o) || ttislightuserdata(o)); } LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { StkId o1 = index2adr(L, index1); StkId o2 = index2adr(L, index2); return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : luaO_rawequalObj(o1, o2); } LUA_API int lua_equal (lua_State *L, int index1, int index2) { StkId o1, o2; int i; lua_lock(L); /* may call tag method */ o1 = index2adr(L, index1); o2 = index2adr(L, index2); i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2); lua_unlock(L); return i; } LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { StkId o1, o2; int i; lua_lock(L); /* may call tag method */ o1 = index2adr(L, index1); o2 = index2adr(L, index2); i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : luaV_lessthan(L, o1, o2); lua_unlock(L); return i; } LUA_API lua_Number lua_tonumber (lua_State *L, int idx) { TValue n; const TValue *o = index2adr(L, idx); if (tonumber(o, &n)) return nvalue(o); else return 0; } LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) { TValue n; const TValue *o = index2adr(L, idx); if (tonumber(o, &n)) { lua_Integer res; lua_Number num = nvalue(o); lua_number2integer(res, num); return res; } else return 0; } LUA_API int lua_toboolean (lua_State *L, int idx) { const TValue *o = index2adr(L, idx); return !l_isfalse(o); } LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { StkId o = index2adr(L, idx); if (!ttisstring(o)) { lua_lock(L); /* `luaV_tostring' may create a new string */ if (!luaV_tostring(L, o)) { /* conversion failed? */ if (len != NULL) *len = 0; lua_unlock(L); return NULL; } luaC_checkGC(L); o = index2adr(L, idx); /* previous call may reallocate the stack */ lua_unlock(L); } if (len != NULL) *len = tsvalue(o)->len; return svalue(o); } LUA_API size_t lua_objlen (lua_State *L, int idx) { StkId o = index2adr(L, idx); switch (ttype(o)) { case LUA_TSTRING: return tsvalue(o)->len; case LUA_TUSERDATA: return uvalue(o)->len; case LUA_TTABLE: return luaH_getn(hvalue(o)); case LUA_TNUMBER: { size_t l; lua_lock(L); /* `luaV_tostring' may create a new string */ l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0); lua_unlock(L); return l; } default: return 0; } } LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { StkId o = index2adr(L, idx); return (!iscfunction(o)) ? NULL : clvalue(o)->c.f; } LUA_API void *lua_touserdata (lua_State *L, int idx) { StkId o = index2adr(L, idx); switch (ttype(o)) { case LUA_TUSERDATA: return (rawuvalue(o) + 1); case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } } LUA_API lua_State *lua_tothread (lua_State *L, int idx) { StkId o = index2adr(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } LUA_API const void *lua_topointer (lua_State *L, int idx) { StkId o = index2adr(L, idx); switch (ttype(o)) { case LUA_TTABLE: return hvalue(o); case LUA_TFUNCTION: return clvalue(o); case LUA_TTHREAD: return thvalue(o); case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: return lua_touserdata(L, idx); default: return NULL; } } /* ** push functions (C -> stack) */ LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); setnilvalue(L->top); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); setnvalue(L->top, n); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); setnvalue(L->top, cast_num(n)); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { lua_lock(L); luaC_checkGC(L); setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushstring (lua_State *L, const char *s) { if (s == NULL) lua_pushnil(L); else lua_pushlstring(L, s, strlen(s)); } LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); luaC_checkGC(L); ret = luaO_pushvfstring(L, fmt, argp); lua_unlock(L); return ret; } LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); luaC_checkGC(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); lua_unlock(L); return ret; } LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { Closure *cl; lua_lock(L); luaC_checkGC(L); api_checknelems(L, n); cl = luaF_newCclosure(L, n, getcurrenv(L)); cl->c.f = fn; L->top -= n; while (n--) setobj2n(L, &cl->c.upvalue[n], L->top+n); setclvalue(L, L->top, cl); lua_assert(iswhite(obj2gco(cl))); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushboolean (lua_State *L, int b) { lua_lock(L); setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { lua_lock(L); setpvalue(L->top, p); api_incr_top(L); lua_unlock(L); } LUA_API int lua_pushthread (lua_State *L) { lua_lock(L); setthvalue(L, L->top, L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); } /* ** get functions (Lua -> stack) */ LUA_API void lua_gettable (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2adr(L, idx); api_checkvalidindex(L, t); luaV_gettable(L, t, L->top - 1, L->top - 1); lua_unlock(L); } LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { StkId t; TValue key; lua_lock(L); t = index2adr(L, idx); api_checkvalidindex(L, t); setsvalue(L, &key, luaS_new(L, k)); luaV_gettable(L, t, &key, L->top); api_incr_top(L); lua_unlock(L); } LUA_API void lua_rawget (lua_State *L, int idx) { StkId t; lua_lock(L); t = index2adr(L, idx); api_check(L, ttistable(t)); setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); lua_unlock(L); } LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { StkId o; lua_lock(L); o = index2adr(L, idx); api_check(L, ttistable(o)); setobj2s(L, L->top, luaH_getnum(hvalue(o), n)); api_incr_top(L); lua_unlock(L); } LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { lua_lock(L); luaC_checkGC(L); sethvalue(L, L->top, luaH_new(L, narray, nrec)); api_incr_top(L); lua_unlock(L); } LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt = NULL; int res; lua_lock(L); obj = index2adr(L, objindex); switch (ttype(obj)) { case LUA_TTABLE: mt = hvalue(obj)->metatable; break; case LUA_TUSERDATA: mt = uvalue(obj)->metatable; break; default: mt = G(L)->mt[ttype(obj)]; break; } if (mt == NULL) res = 0; else { sethvalue(L, L->top, mt); api_incr_top(L); res = 1; } lua_unlock(L); return res; } LUA_API void lua_getfenv (lua_State *L, int idx) { StkId o; lua_lock(L); o = index2adr(L, idx); api_checkvalidindex(L, o); switch (ttype(o)) { case LUA_TFUNCTION: sethvalue(L, L->top, clvalue(o)->c.env); break; case LUA_TUSERDATA: sethvalue(L, L->top, uvalue(o)->env); break; case LUA_TTHREAD: setobj2s(L, L->top, gt(thvalue(o))); break; default: setnilvalue(L->top); break; } api_incr_top(L); lua_unlock(L); } /* ** set functions (stack -> Lua) */ LUA_API void lua_settable (lua_State *L, int idx) { StkId t; lua_lock(L); api_checknelems(L, 2); t = index2adr(L, idx); api_checkvalidindex(L, t); luaV_settable(L, t, L->top - 2, L->top - 1); L->top -= 2; /* pop index and value */ lua_unlock(L); } LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { StkId t; TValue key; lua_lock(L); api_checknelems(L, 1); t = index2adr(L, idx); api_checkvalidindex(L, t); setsvalue(L, &key, luaS_new(L, k)); luaV_settable(L, t, &key, L->top - 1); L->top--; /* pop value */ lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { StkId t; lua_lock(L); api_checknelems(L, 2); t = index2adr(L, idx); api_check(L, ttistable(t)); setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); luaC_barriert(L, hvalue(t), L->top-1); L->top -= 2; lua_unlock(L); } LUA_API void lua_rawseti (lua_State *L, int idx, int n) { StkId o; lua_lock(L); api_checknelems(L, 1); o = index2adr(L, idx); api_check(L, ttistable(o)); setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1); luaC_barriert(L, hvalue(o), L->top-1); L->top--; lua_unlock(L); } LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; lua_lock(L); api_checknelems(L, 1); obj = index2adr(L, objindex); api_checkvalidindex(L, obj); if (ttisnil(L->top - 1)) mt = NULL; else { api_check(L, ttistable(L->top - 1)); mt = hvalue(L->top - 1); } switch (ttype(obj)) { case LUA_TTABLE: { hvalue(obj)->metatable = mt; if (mt) luaC_objbarriert(L, hvalue(obj), mt); break; } case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) luaC_objbarrier(L, rawuvalue(obj), mt); break; } default: { G(L)->mt[ttype(obj)] = mt; break; } } L->top--; lua_unlock(L); return 1; } LUA_API int lua_setfenv (lua_State *L, int idx) { StkId o; int res = 1; lua_lock(L); api_checknelems(L, 1); o = index2adr(L, idx); api_checkvalidindex(L, o); api_check(L, ttistable(L->top - 1)); switch (ttype(o)) { case LUA_TFUNCTION: clvalue(o)->c.env = hvalue(L->top - 1); break; case LUA_TUSERDATA: uvalue(o)->env = hvalue(L->top - 1); break; case LUA_TTHREAD: sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1)); break; default: res = 0; break; } if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); L->top--; lua_unlock(L); return res; } /* ** `load' and `call' functions (run Lua code) */ #define adjustresults(L,nres) \ { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; } #define checkresults(L,na,nr) \ api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na))) LUA_API void lua_call (lua_State *L, int nargs, int nresults) { StkId func; lua_lock(L); api_checknelems(L, nargs+1); checkresults(L, nargs, nresults); func = L->top - (nargs+1); luaD_call(L, func, nresults); adjustresults(L, nresults); lua_unlock(L); } /* ** Execute a protected call. */ struct CallS { /* data to `f_call' */ StkId func; int nresults; }; static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); luaD_call(L, c->func, c->nresults); } LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { struct CallS c; int status; ptrdiff_t func; lua_lock(L); api_checknelems(L, nargs+1); checkresults(L, nargs, nresults); if (errfunc == 0) func = 0; else { StkId o = index2adr(L, errfunc); api_checkvalidindex(L, o); func = savestack(L, o); } c.func = L->top - (nargs+1); /* function to be called */ c.nresults = nresults; status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); adjustresults(L, nresults); lua_unlock(L); return status; } /* ** Execute a protected C call. */ struct CCallS { /* data to `f_Ccall' */ lua_CFunction func; void *ud; }; static void f_Ccall (lua_State *L, void *ud) { struct CCallS *c = cast(struct CCallS *, ud); Closure *cl; cl = luaF_newCclosure(L, 0, getcurrenv(L)); cl->c.f = c->func; setclvalue(L, L->top, cl); /* push function */ api_incr_top(L); setpvalue(L->top, c->ud); /* push only argument */ api_incr_top(L); luaD_call(L, L->top - 2, 0); } LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) { struct CCallS c; int status; lua_lock(L); c.func = func; c.ud = ud; status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0); lua_unlock(L); return status; } LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname) { ZIO z; int status; lua_lock(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname); lua_unlock(L); return status; } LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { int status; TValue *o; lua_lock(L); api_checknelems(L, 1); o = L->top - 1; if (isLfunction(o)) status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0); else status = 1; lua_unlock(L); return status; } LUA_API int lua_status (lua_State *L) { return L->status; } /* ** Garbage-collection function */ LUA_API int lua_gc (lua_State *L, int what, int data) { int res = 0; global_State *g; lua_lock(L); g = G(L); switch (what) { case LUA_GCSTOP: { g->GCthreshold = MAX_LUMEM; break; } case LUA_GCRESTART: { g->GCthreshold = g->totalbytes; break; } case LUA_GCCOLLECT: { luaC_fullgc(L); break; } case LUA_GCCOUNT: { /* GC values are expressed in Kbytes: #bytes/2^10 */ res = cast_int(g->totalbytes >> 10); break; } case LUA_GCCOUNTB: { res = cast_int(g->totalbytes & 0x3ff); break; } case LUA_GCSTEP: { lu_mem a = (cast(lu_mem, data) << 10); if (a <= g->totalbytes) g->GCthreshold = g->totalbytes - a; else g->GCthreshold = 0; while (g->GCthreshold <= g->totalbytes) { luaC_step(L); if (g->gcstate == GCSpause) { /* end of cycle? */ res = 1; /* signal it */ break; } } break; } case LUA_GCSETPAUSE: { res = g->gcpause; g->gcpause = data; break; } case LUA_GCSETSTEPMUL: { res = g->gcstepmul; g->gcstepmul = data; break; } default: res = -1; /* invalid option */ } lua_unlock(L); return res; } /* ** miscellaneous functions */ LUA_API int lua_error (lua_State *L) { lua_lock(L); api_checknelems(L, 1); luaG_errormsg(L); lua_unlock(L); return 0; /* to avoid warnings */ } LUA_API int lua_next (lua_State *L, int idx) { StkId t; int more; lua_lock(L); t = index2adr(L, idx); api_check(L, ttistable(t)); more = luaH_next(L, hvalue(t), L->top - 1); if (more) { api_incr_top(L); } else /* no more elements */ L->top -= 1; /* remove key */ lua_unlock(L); return more; } LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); if (n >= 2) { luaC_checkGC(L); luaV_concat(L, n, cast_int(L->top - L->base) - 1); L->top -= (n-1); } else if (n == 0) { /* push empty string */ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); api_incr_top(L); } /* else n == 1; nothing to do */ lua_unlock(L); } LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { lua_Alloc f; lua_lock(L); if (ud) *ud = G(L)->ud; f = G(L)->frealloc; lua_unlock(L); return f; } LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { lua_lock(L); G(L)->ud = ud; G(L)->frealloc = f; lua_unlock(L); } LUA_API void *lua_newuserdata (lua_State *L, size_t size) { Udata *u; lua_lock(L); luaC_checkGC(L); u = luaS_newudata(L, size, getcurrenv(L)); setuvalue(L, L->top, u); api_incr_top(L); lua_unlock(L); return u + 1; } static const char *aux_upvalue (StkId fi, int n, TValue **val) { Closure *f; if (!ttisfunction(fi)) return NULL; f = clvalue(fi); if (f->c.isC) { if (!(1 <= n && n <= f->c.nupvalues)) return NULL; *val = &f->c.upvalue[n-1]; return ""; } else { Proto *p = f->l.p; if (!(1 <= n && n <= p->sizeupvalues)) return NULL; *val = f->l.upvals[n-1]->v; return getstr(p->upvalues[n-1]); } } LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val; lua_lock(L); name = aux_upvalue(index2adr(L, funcindex), n, &val); if (name) { setobj2s(L, L->top, val); api_incr_top(L); } lua_unlock(L); return name; } LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val; StkId fi; lua_lock(L); fi = index2adr(L, funcindex); api_checknelems(L, 1); name = aux_upvalue(fi, n, &val); if (name) { L->top--; setobj(L, val, L->top); luaC_barrier(L, clvalue(fi), L->top); } lua_unlock(L); return name; } blobby-1.0rc3/src/lua/lcode.c0000644000175000017500000005152712042452374017362 0ustar danielknobedanielknobe/* ** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #include #define lcode_c #define LUA_CORE #include "lua.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lgc.h" #include "llex.h" #include "lmem.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" #include "ltable.h" #define hasjumps(e) ((e)->t != (e)->f) static int isnumeral(expdesc *e) { return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); } void luaK_nil (FuncState *fs, int from, int n) { Instruction *previous; if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ if (fs->pc == 0) { /* function start? */ if (from >= fs->nactvar) return; /* positions are already clean */ } else { previous = &fs->f->code[fs->pc-1]; if (GET_OPCODE(*previous) == OP_LOADNIL) { int pfrom = GETARG_A(*previous); int pto = GETARG_B(*previous); if (pfrom <= from && from <= pto+1) { /* can connect both? */ if (from+n-1 > pto) SETARG_B(*previous, from+n-1); return; } } } } luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */ } int luaK_jump (FuncState *fs) { int jpc = fs->jpc; /* save list of jumps to here */ int j; fs->jpc = NO_JUMP; j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); luaK_concat(fs, &j, jpc); /* keep them on hold */ return j; } void luaK_ret (FuncState *fs, int first, int nret) { luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); } static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { luaK_codeABC(fs, op, A, B, C); return luaK_jump(fs); } static void fixjump (FuncState *fs, int pc, int dest) { Instruction *jmp = &fs->f->code[pc]; int offset = dest-(pc+1); lua_assert(dest != NO_JUMP); if (abs(offset) > MAXARG_sBx) luaX_syntaxerror(fs->ls, "control structure too long"); SETARG_sBx(*jmp, offset); } /* ** returns current `pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). */ int luaK_getlabel (FuncState *fs) { fs->lasttarget = fs->pc; return fs->pc; } static int getjump (FuncState *fs, int pc) { int offset = GETARG_sBx(fs->f->code[pc]); if (offset == NO_JUMP) /* point to itself represents end of list */ return NO_JUMP; /* end of list */ else return (pc+1)+offset; /* turn offset into absolute position */ } static Instruction *getjumpcontrol (FuncState *fs, int pc) { Instruction *pi = &fs->f->code[pc]; if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) return pi-1; else return pi; } /* ** check whether list has any jump that do not produce a value ** (or produce an inverted value) */ static int need_value (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) { Instruction i = *getjumpcontrol(fs, list); if (GET_OPCODE(i) != OP_TESTSET) return 1; } return 0; /* not found */ } static int patchtestreg (FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) SETARG_A(*i, reg); else /* no register to put value or register already has the value */ *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); return 1; } static void removevalues (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) patchtestreg(fs, list, NO_REG); } static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, int dtarget) { while (list != NO_JUMP) { int next = getjump(fs, list); if (patchtestreg(fs, list, reg)) fixjump(fs, list, vtarget); else fixjump(fs, list, dtarget); /* jump to default target */ list = next; } } static void dischargejpc (FuncState *fs) { patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); fs->jpc = NO_JUMP; } void luaK_patchlist (FuncState *fs, int list, int target) { if (target == fs->pc) luaK_patchtohere(fs, list); else { lua_assert(target < fs->pc); patchlistaux(fs, list, target, NO_REG, target); } } void luaK_patchtohere (FuncState *fs, int list) { luaK_getlabel(fs); luaK_concat(fs, &fs->jpc, list); } void luaK_concat (FuncState *fs, int *l1, int l2) { if (l2 == NO_JUMP) return; else if (*l1 == NO_JUMP) *l1 = l2; else { int list = *l1; int next; while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ list = next; fixjump(fs, list, l2); } } void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { if (newstack >= MAXSTACK) luaX_syntaxerror(fs->ls, "function or expression too complex"); fs->f->maxstacksize = cast_byte(newstack); } } void luaK_reserveregs (FuncState *fs, int n) { luaK_checkstack(fs, n); fs->freereg += n; } static void freereg (FuncState *fs, int reg) { if (!ISK(reg) && reg >= fs->nactvar) { fs->freereg--; lua_assert(reg == fs->freereg); } } static void freeexp (FuncState *fs, expdesc *e) { if (e->k == VNONRELOC) freereg(fs, e->u.s.info); } static int addk (FuncState *fs, TValue *k, TValue *v) { lua_State *L = fs->L; TValue *idx = luaH_set(L, fs->h, k); Proto *f = fs->f; int oldsize = f->sizek; if (ttisnumber(idx)) { lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); return cast_int(nvalue(idx)); } else { /* constant not found; create a new entry */ setnvalue(idx, cast_num(fs->nk)); luaM_growvector(L, f->k, fs->nk, f->sizek, TValue, MAXARG_Bx, "constant table overflow"); while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); setobj(L, &f->k[fs->nk], v); luaC_barrier(L, f, v); return fs->nk++; } } int luaK_stringK (FuncState *fs, TString *s) { TValue o; setsvalue(fs->L, &o, s); return addk(fs, &o, &o); } int luaK_numberK (FuncState *fs, lua_Number r) { TValue o; setnvalue(&o, r); return addk(fs, &o, &o); } static int boolK (FuncState *fs, int b) { TValue o; setbvalue(&o, b); return addk(fs, &o, &o); } static int nilK (FuncState *fs) { TValue k, v; setnilvalue(&v); /* cannot use nil as key; instead use table itself to represent nil */ sethvalue(fs->L, &k, fs->h); return addk(fs, &k, &v); } void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { if (e->k == VCALL) { /* expression is an open function call? */ SETARG_C(getcode(fs, e), nresults+1); } else if (e->k == VVARARG) { SETARG_B(getcode(fs, e), nresults+1); SETARG_A(getcode(fs, e), fs->freereg); luaK_reserveregs(fs, 1); } } void luaK_setoneret (FuncState *fs, expdesc *e) { if (e->k == VCALL) { /* expression is an open function call? */ e->k = VNONRELOC; e->u.s.info = GETARG_A(getcode(fs, e)); } else if (e->k == VVARARG) { SETARG_B(getcode(fs, e), 2); e->k = VRELOCABLE; /* can relocate its simple result */ } } void luaK_dischargevars (FuncState *fs, expdesc *e) { switch (e->k) { case VLOCAL: { e->k = VNONRELOC; break; } case VUPVAL: { e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0); e->k = VRELOCABLE; break; } case VGLOBAL: { e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info); e->k = VRELOCABLE; break; } case VINDEXED: { freereg(fs, e->u.s.aux); freereg(fs, e->u.s.info); e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux); e->k = VRELOCABLE; break; } case VVARARG: case VCALL: { luaK_setoneret(fs, e); break; } default: break; /* there is one value available (somewhere) */ } } static int code_label (FuncState *fs, int A, int b, int jump) { luaK_getlabel(fs); /* those instructions may be jump targets */ return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); } static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: { luaK_nil(fs, reg, 1); break; } case VFALSE: case VTRUE: { luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); break; } case VK: { luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); break; } case VKNUM: { luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); break; } case VRELOCABLE: { Instruction *pc = &getcode(fs, e); SETARG_A(*pc, reg); break; } case VNONRELOC: { if (reg != e->u.s.info) luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0); break; } default: { lua_assert(e->k == VVOID || e->k == VJMP); return; /* nothing to do... */ } } e->u.s.info = reg; e->k = VNONRELOC; } static void discharge2anyreg (FuncState *fs, expdesc *e) { if (e->k != VNONRELOC) { luaK_reserveregs(fs, 1); discharge2reg(fs, e, fs->freereg-1); } } static void exp2reg (FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); if (e->k == VJMP) luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ if (hasjumps(e)) { int final; /* position after whole expression */ int p_f = NO_JUMP; /* position of an eventual LOAD false */ int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (need_value(fs, e->t) || need_value(fs, e->f)) { int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); p_f = code_label(fs, reg, 0, 1); p_t = code_label(fs, reg, 1, 0); luaK_patchtohere(fs, fj); } final = luaK_getlabel(fs); patchlistaux(fs, e->f, final, reg, p_f); patchlistaux(fs, e->t, final, reg, p_t); } e->f = e->t = NO_JUMP; e->u.s.info = reg; e->k = VNONRELOC; } void luaK_exp2nextreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); luaK_reserveregs(fs, 1); exp2reg(fs, e, fs->freereg - 1); } int luaK_exp2anyreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); if (e->k == VNONRELOC) { if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ exp2reg(fs, e, e->u.s.info); /* put value on it */ return e->u.s.info; } } luaK_exp2nextreg(fs, e); /* default */ return e->u.s.info; } void luaK_exp2val (FuncState *fs, expdesc *e) { if (hasjumps(e)) luaK_exp2anyreg(fs, e); else luaK_dischargevars(fs, e); } int luaK_exp2RK (FuncState *fs, expdesc *e) { luaK_exp2val(fs, e); switch (e->k) { case VKNUM: case VTRUE: case VFALSE: case VNIL: { if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */ e->u.s.info = (e->k == VNIL) ? nilK(fs) : (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) : boolK(fs, (e->k == VTRUE)); e->k = VK; return RKASK(e->u.s.info); } else break; } case VK: { if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */ return RKASK(e->u.s.info); else break; } default: break; } /* not a constant in the right range: put it in a register */ return luaK_exp2anyreg(fs, e); } void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); exp2reg(fs, ex, var->u.s.info); return; } case VUPVAL: { int e = luaK_exp2anyreg(fs, ex); luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); break; } case VGLOBAL: { int e = luaK_exp2anyreg(fs, ex); luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); break; } case VINDEXED: { int e = luaK_exp2RK(fs, ex); luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); break; } default: { lua_assert(0); /* invalid var kind to store */ break; } } freeexp(fs, ex); } void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { int func; luaK_exp2anyreg(fs, e); freeexp(fs, e); func = fs->freereg; luaK_reserveregs(fs, 2); luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key)); freeexp(fs, key); e->u.s.info = func; e->k = VNONRELOC; } static void invertjump (FuncState *fs, expdesc *e) { Instruction *pc = getjumpcontrol(fs, e->u.s.info); lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && GET_OPCODE(*pc) != OP_TEST); SETARG_A(*pc, !(GETARG_A(*pc))); } static int jumponcond (FuncState *fs, expdesc *e, int cond) { if (e->k == VRELOCABLE) { Instruction ie = getcode(fs, e); if (GET_OPCODE(ie) == OP_NOT) { fs->pc--; /* remove previous OP_NOT */ return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); } /* else go through */ } discharge2anyreg(fs, e); freeexp(fs, e); return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond); } void luaK_goiftrue (FuncState *fs, expdesc *e) { int pc; /* pc of last jump */ luaK_dischargevars(fs, e); switch (e->k) { case VK: case VKNUM: case VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } case VFALSE: { pc = luaK_jump(fs); /* always jump */ break; } case VJMP: { invertjump(fs, e); pc = e->u.s.info; break; } default: { pc = jumponcond(fs, e, 0); break; } } luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ luaK_patchtohere(fs, e->t); e->t = NO_JUMP; } static void luaK_goiffalse (FuncState *fs, expdesc *e) { int pc; /* pc of last jump */ luaK_dischargevars(fs, e); switch (e->k) { case VNIL: case VFALSE: { pc = NO_JUMP; /* always false; do nothing */ break; } case VTRUE: { pc = luaK_jump(fs); /* always jump */ break; } case VJMP: { pc = e->u.s.info; break; } default: { pc = jumponcond(fs, e, 1); break; } } luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ luaK_patchtohere(fs, e->f); e->f = NO_JUMP; } static void codenot (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: case VFALSE: { e->k = VTRUE; break; } case VK: case VKNUM: case VTRUE: { e->k = VFALSE; break; } case VJMP: { invertjump(fs, e); break; } case VRELOCABLE: case VNONRELOC: { discharge2anyreg(fs, e); freeexp(fs, e); e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0); e->k = VRELOCABLE; break; } default: { lua_assert(0); /* cannot happen */ break; } } /* interchange true and false lists */ { int temp = e->f; e->f = e->t; e->t = temp; } removevalues(fs, e->f); removevalues(fs, e->t); } void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { t->u.s.aux = luaK_exp2RK(fs, k); t->k = VINDEXED; } static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { lua_Number v1, v2, r; if (!isnumeral(e1) || !isnumeral(e2)) return 0; v1 = e1->u.nval; v2 = e2->u.nval; switch (op) { case OP_ADD: r = luai_numadd(v1, v2); break; case OP_SUB: r = luai_numsub(v1, v2); break; case OP_MUL: r = luai_nummul(v1, v2); break; case OP_DIV: if (v2 == 0) return 0; /* do not attempt to divide by 0 */ r = luai_numdiv(v1, v2); break; case OP_MOD: if (v2 == 0) return 0; /* do not attempt to divide by 0 */ r = luai_nummod(v1, v2); break; case OP_POW: r = luai_numpow(v1, v2); break; case OP_UNM: r = luai_numunm(v1); break; case OP_LEN: return 0; /* no constant folding for 'len' */ default: lua_assert(0); r = 0; break; } if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */ e1->u.nval = r; return 1; } static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { if (constfolding(op, e1, e2)) return; else { int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; int o1 = luaK_exp2RK(fs, e1); if (o1 > o2) { freeexp(fs, e1); freeexp(fs, e2); } else { freeexp(fs, e2); freeexp(fs, e1); } e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2); e1->k = VRELOCABLE; } } static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, expdesc *e2) { int o1 = luaK_exp2RK(fs, e1); int o2 = luaK_exp2RK(fs, e2); freeexp(fs, e2); freeexp(fs, e1); if (cond == 0 && op != OP_EQ) { int temp; /* exchange args to replace by `<' or `<=' */ temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ cond = 1; } e1->u.s.info = condjump(fs, op, cond, o1, o2); e1->k = VJMP; } void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { expdesc e2; e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; switch (op) { case OPR_MINUS: { if (!isnumeral(e)) luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ codearith(fs, OP_UNM, e, &e2); break; } case OPR_NOT: codenot(fs, e); break; case OPR_LEN: { luaK_exp2anyreg(fs, e); /* cannot operate on constants */ codearith(fs, OP_LEN, e, &e2); break; } default: lua_assert(0); } } void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { switch (op) { case OPR_AND: { luaK_goiftrue(fs, v); break; } case OPR_OR: { luaK_goiffalse(fs, v); break; } case OPR_CONCAT: { luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ break; } case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: case OPR_MOD: case OPR_POW: { if (!isnumeral(v)) luaK_exp2RK(fs, v); break; } default: { luaK_exp2RK(fs, v); break; } } } void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { switch (op) { case OPR_AND: { lua_assert(e1->t == NO_JUMP); /* list must be closed */ luaK_dischargevars(fs, e2); luaK_concat(fs, &e2->f, e1->f); *e1 = *e2; break; } case OPR_OR: { lua_assert(e1->f == NO_JUMP); /* list must be closed */ luaK_dischargevars(fs, e2); luaK_concat(fs, &e2->t, e1->t); *e1 = *e2; break; } case OPR_CONCAT: { luaK_exp2val(fs, e2); if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1); freeexp(fs, e1); SETARG_B(getcode(fs, e2), e1->u.s.info); e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info; } else { luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ codearith(fs, OP_CONCAT, e1, e2); } break; } case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; case OPR_POW: codearith(fs, OP_POW, e1, e2); break; case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; default: lua_assert(0); } } void luaK_fixline (FuncState *fs, int line) { fs->f->lineinfo[fs->pc - 1] = line; } static int luaK_code (FuncState *fs, Instruction i, int line) { Proto *f = fs->f; dischargejpc(fs); /* `pc' will change */ /* put new instruction in code array */ luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction, MAX_INT, "code size overflow"); f->code[fs->pc] = i; /* save corresponding line information */ luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, MAX_INT, "code size overflow"); f->lineinfo[fs->pc] = line; return fs->pc++; } int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { lua_assert(getOpMode(o) == iABC); lua_assert(getBMode(o) != OpArgN || b == 0); lua_assert(getCMode(o) != OpArgN || c == 0); return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline); } int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); lua_assert(getCMode(o) == OpArgN); return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline); } void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; int b = (tostore == LUA_MULTRET) ? 0 : tostore; lua_assert(tostore != 0); if (c <= MAXARG_C) luaK_codeABC(fs, OP_SETLIST, base, b, c); else { luaK_codeABC(fs, OP_SETLIST, base, b, 0); luaK_code(fs, cast(Instruction, c), fs->ls->lastline); } fs->freereg = base + 1; /* free registers with list values */ } blobby-1.0rc3/src/lua/lua.h0000644000175000017500000002665012042452374017061 0ustar danielknobedanielknobe/* ** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $ ** Lua - An Extensible Extension Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file */ #ifndef lua_h #define lua_h #include #include #include "luaconf.h" #define LUA_VERSION "Lua 5.1" #define LUA_RELEASE "Lua 5.1.4" #define LUA_VERSION_NUM 501 #define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" /* mark for precompiled code (`Lua') */ #define LUA_SIGNATURE "\033Lua" /* option for multiple returns in `lua_pcall' and `lua_call' */ #define LUA_MULTRET (-1) /* ** pseudo-indices */ #define LUA_REGISTRYINDEX (-10000) #define LUA_ENVIRONINDEX (-10001) #define LUA_GLOBALSINDEX (-10002) #define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) /* thread status; 0 is OK */ #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRERR 5 typedef struct lua_State lua_State; typedef int (*lua_CFunction) (lua_State *L); /* ** functions that read/write blocks when loading/dumping Lua chunks */ typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); /* ** prototype for memory-allocation functions */ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); /* ** basic types */ #define LUA_TNONE (-1) #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8 /* minimum Lua stack available to a C function */ #define LUA_MINSTACK 20 /* ** generic extra include file */ #if defined(LUA_USER_H) #include LUA_USER_H #endif /* type of numbers in Lua */ typedef LUA_NUMBER lua_Number; /* type for integer functions */ typedef LUA_INTEGER lua_Integer; /* ** state manipulation */ LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); /* ** basic stack manipulation */ LUA_API int (lua_gettop) (lua_State *L); LUA_API void (lua_settop) (lua_State *L, int idx); LUA_API void (lua_pushvalue) (lua_State *L, int idx); LUA_API void (lua_remove) (lua_State *L, int idx); LUA_API void (lua_insert) (lua_State *L, int idx); LUA_API void (lua_replace) (lua_State *L, int idx); LUA_API int (lua_checkstack) (lua_State *L, int sz); LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); /* ** access functions (stack -> C) */ LUA_API int (lua_isnumber) (lua_State *L, int idx); LUA_API int (lua_isstring) (lua_State *L, int idx); LUA_API int (lua_iscfunction) (lua_State *L, int idx); LUA_API int (lua_isuserdata) (lua_State *L, int idx); LUA_API int (lua_type) (lua_State *L, int idx); LUA_API const char *(lua_typename) (lua_State *L, int tp); LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); LUA_API int (lua_toboolean) (lua_State *L, int idx); LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); LUA_API size_t (lua_objlen) (lua_State *L, int idx); LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); LUA_API void *(lua_touserdata) (lua_State *L, int idx); LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); LUA_API const void *(lua_topointer) (lua_State *L, int idx); /* ** push functions (C -> stack) */ LUA_API void (lua_pushnil) (lua_State *L); LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); LUA_API void (lua_pushstring) (lua_State *L, const char *s); LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); LUA_API void (lua_pushboolean) (lua_State *L, int b); LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); LUA_API int (lua_pushthread) (lua_State *L); /* ** get functions (Lua -> stack) */ LUA_API void (lua_gettable) (lua_State *L, int idx); LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); LUA_API void (lua_rawget) (lua_State *L, int idx); LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); LUA_API int (lua_getmetatable) (lua_State *L, int objindex); LUA_API void (lua_getfenv) (lua_State *L, int idx); /* ** set functions (stack -> Lua) */ LUA_API void (lua_settable) (lua_State *L, int idx); LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); LUA_API void (lua_rawset) (lua_State *L, int idx); LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); LUA_API int (lua_setmetatable) (lua_State *L, int objindex); LUA_API int (lua_setfenv) (lua_State *L, int idx); /* ** `load' and `call' functions (load and run Lua code) */ LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname); LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); /* ** coroutine functions */ LUA_API int (lua_yield) (lua_State *L, int nresults); LUA_API int (lua_resume) (lua_State *L, int narg); LUA_API int (lua_status) (lua_State *L); /* ** garbage-collection function and options */ #define LUA_GCSTOP 0 #define LUA_GCRESTART 1 #define LUA_GCCOLLECT 2 #define LUA_GCCOUNT 3 #define LUA_GCCOUNTB 4 #define LUA_GCSTEP 5 #define LUA_GCSETPAUSE 6 #define LUA_GCSETSTEPMUL 7 LUA_API int (lua_gc) (lua_State *L, int what, int data); /* ** miscellaneous functions */ LUA_API int (lua_error) (lua_State *L); LUA_API int (lua_next) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); /* ** =============================================================== ** some useful macros ** =============================================================== */ #define lua_pop(L,n) lua_settop(L, -(n)-1) #define lua_newtable(L) lua_createtable(L, 0, 0) #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) #define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) #define lua_strlen(L,i) lua_objlen(L, (i)) #define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) #define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) #define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) #define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) #define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) #define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) #define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) #define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) #define lua_pushliteral(L, s) \ lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) #define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) #define lua_tostring(L,i) lua_tolstring(L, (i), NULL) /* ** compatibility macros and functions */ #define lua_open() luaL_newstate() #define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) #define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) #define lua_Chunkreader lua_Reader #define lua_Chunkwriter lua_Writer /* hack */ LUA_API void lua_setlevel (lua_State *from, lua_State *to); /* ** {====================================================================== ** Debug API ** ======================================================================= */ /* ** Event codes */ #define LUA_HOOKCALL 0 #define LUA_HOOKRET 1 #define LUA_HOOKLINE 2 #define LUA_HOOKCOUNT 3 #define LUA_HOOKTAILRET 4 /* ** Event masks */ #define LUA_MASKCALL (1 << LUA_HOOKCALL) #define LUA_MASKRET (1 << LUA_HOOKRET) #define LUA_MASKLINE (1 << LUA_HOOKLINE) #define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) typedef struct lua_Debug lua_Debug; /* activation record */ /* Functions to be called by the debuger in specific events */ typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); LUA_API lua_Hook lua_gethook (lua_State *L); LUA_API int lua_gethookmask (lua_State *L); LUA_API int lua_gethookcount (lua_State *L); struct lua_Debug { int event; const char *name; /* (n) */ const char *namewhat; /* (n) `global', `local', `field', `method' */ const char *what; /* (S) `Lua', `C', `main', `tail' */ const char *source; /* (S) */ int currentline; /* (l) */ int nups; /* (u) number of upvalues */ int linedefined; /* (S) */ int lastlinedefined; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ int i_ci; /* active function */ }; /* }====================================================================== */ /****************************************************************************** * Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ #endif blobby-1.0rc3/src/lua/lobject.h0000644000175000017500000002046612042452374017721 0ustar danielknobedanielknobe/* ** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ #ifndef lobject_h #define lobject_h #include #include "llimits.h" #include "lua.h" /* tags for values visible from Lua */ #define LAST_TAG LUA_TTHREAD #define NUM_TAGS (LAST_TAG+1) /* ** Extra tags for non-values */ #define LUA_TPROTO (LAST_TAG+1) #define LUA_TUPVAL (LAST_TAG+2) #define LUA_TDEADKEY (LAST_TAG+3) /* ** Union of all collectable objects */ typedef union GCObject GCObject; /* ** Common Header for all collectable objects (in macro form, to be ** included in other objects) */ #define CommonHeader GCObject *next; lu_byte tt; lu_byte marked /* ** Common header in struct form */ typedef struct GCheader { CommonHeader; } GCheader; /* ** Union of all Lua values */ typedef union { GCObject *gc; void *p; lua_Number n; int b; } Value; /* ** Tagged Values */ #define TValuefields Value value; int tt typedef struct lua_TValue { TValuefields; } TValue; /* Macros to test type */ #define ttisnil(o) (ttype(o) == LUA_TNIL) #define ttisnumber(o) (ttype(o) == LUA_TNUMBER) #define ttisstring(o) (ttype(o) == LUA_TSTRING) #define ttistable(o) (ttype(o) == LUA_TTABLE) #define ttisfunction(o) (ttype(o) == LUA_TFUNCTION) #define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN) #define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) #define ttisthread(o) (ttype(o) == LUA_TTHREAD) #define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) /* Macros to access values */ #define ttype(o) ((o)->tt) #define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) #define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) #define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) #define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) #define tsvalue(o) (&rawtsvalue(o)->tsv) #define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u) #define uvalue(o) (&rawuvalue(o)->uv) #define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) #define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h) #define bvalue(o) check_exp(ttisboolean(o), (o)->value.b) #define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th) #define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) /* ** for internal debug only */ #define checkconsistency(obj) \ lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt)) #define checkliveness(g,obj) \ lua_assert(!iscollectable(obj) || \ ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) /* Macros to set values */ #define setnilvalue(obj) ((obj)->tt=LUA_TNIL) #define setnvalue(obj,x) \ { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; } #define setpvalue(obj,x) \ { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; } #define setbvalue(obj,x) \ { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; } #define setsvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \ checkliveness(G(L),i_o); } #define setuvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \ checkliveness(G(L),i_o); } #define setthvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \ checkliveness(G(L),i_o); } #define setclvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \ checkliveness(G(L),i_o); } #define sethvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \ checkliveness(G(L),i_o); } #define setptvalue(L,obj,x) \ { TValue *i_o=(obj); \ i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \ checkliveness(G(L),i_o); } #define setobj(L,obj1,obj2) \ { const TValue *o2=(obj2); TValue *o1=(obj1); \ o1->value = o2->value; o1->tt=o2->tt; \ checkliveness(G(L),o1); } /* ** different types of sets, according to destination */ /* from stack to (same) stack */ #define setobjs2s setobj /* to stack (not from same stack) */ #define setobj2s setobj #define setsvalue2s setsvalue #define sethvalue2s sethvalue #define setptvalue2s setptvalue /* from table to same table */ #define setobjt2t setobj /* to table */ #define setobj2t setobj /* to new object */ #define setobj2n setobj #define setsvalue2n setsvalue #define setttype(obj, tt) (ttype(obj) = (tt)) #define iscollectable(o) (ttype(o) >= LUA_TSTRING) typedef TValue *StkId; /* index to stack elements */ /* ** String headers for string table */ typedef union TString { L_Umaxalign dummy; /* ensures maximum alignment for strings */ struct { CommonHeader; lu_byte reserved; unsigned int hash; size_t len; } tsv; } TString; #define getstr(ts) cast(const char *, (ts) + 1) #define svalue(o) getstr(rawtsvalue(o)) typedef union Udata { L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ struct { CommonHeader; struct Table *metatable; struct Table *env; size_t len; } uv; } Udata; /* ** Function Prototypes */ typedef struct Proto { CommonHeader; TValue *k; /* constants used by the function */ Instruction *code; struct Proto **p; /* functions defined inside the function */ int *lineinfo; /* map from opcodes to source lines */ struct LocVar *locvars; /* information about local variables */ TString **upvalues; /* upvalue names */ TString *source; int sizeupvalues; int sizek; /* size of `k' */ int sizecode; int sizelineinfo; int sizep; /* size of `p' */ int sizelocvars; int linedefined; int lastlinedefined; GCObject *gclist; lu_byte nups; /* number of upvalues */ lu_byte numparams; lu_byte is_vararg; lu_byte maxstacksize; } Proto; /* masks for new-style vararg */ #define VARARG_HASARG 1 #define VARARG_ISVARARG 2 #define VARARG_NEEDSARG 4 typedef struct LocVar { TString *varname; int startpc; /* first point where variable is active */ int endpc; /* first point where variable is dead */ } LocVar; /* ** Upvalues */ typedef struct UpVal { CommonHeader; TValue *v; /* points to stack or to its own value */ union { TValue value; /* the value (when closed) */ struct { /* double linked list (when open) */ struct UpVal *prev; struct UpVal *next; } l; } u; } UpVal; /* ** Closures */ #define ClosureHeader \ CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \ struct Table *env typedef struct CClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; } CClosure; typedef struct LClosure { ClosureHeader; struct Proto *p; UpVal *upvals[1]; } LClosure; typedef union Closure { CClosure c; LClosure l; } Closure; #define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC) #define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC) /* ** Tables */ typedef union TKey { struct { TValuefields; struct Node *next; /* for chaining */ } nk; TValue tvk; } TKey; typedef struct Node { TValue i_val; TKey i_key; } Node; typedef struct Table { CommonHeader; lu_byte flags; /* 1<

Replay File V 1.x:
OffsetSizeValuesDescription
File Header
04 bytesbv2rReplay file header
44 bytes01xpFile version information. First byte ist always 0, second byte contains major version (1), x is minor version and p contains additional versioning information. \sa rep_versioning
84 byteschecksumcontains a checksum of the whole file
Replay Header
124 bytesintlength of this replay header in bytes
164 bytespointerPoints to the starting position of the attributes section
204 bytesintLength of attributes section
244 bytespointerPoints to the starting position of the jump table
284 bytesintLength of jump table section
324 bytespointerPoints to the starting position of the data section
364 bytesintLength of data section (bytes)
Attributes Section (AS)
AS+04 bytesatr\nAttribute beginning indicator
AS+44 bytesintGamespeed
AS+84 bytesintGame duration [sec]
AS+124 bytesintGame duration [steps]
AS+164 bytesintDate of match
AS+204 bytesintLeft player color
AS+244 bytesintRight player color
AS+284 bytesintLeft player score
AS+324 bytesintRight player score
AS+36stringstringLeft player name
...stringstringRight player name
...
Jump Table (JT)
JT+04 bytesjpt\nJump table beginning indicator
[currently undefined]
Data Section (DS)
DS+04 bytesjpt\nData beginning indicator
DS+44 bytesintLength of data section [steps]
DS+8...*Packetgame data
**/ blobby-1.0rc3/src/Blood.h0000644000175000017500000000636112042452374016553 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Vector.h" #include #include //Bleeding blobs can be a lot of fun :) /*! \class Blood \brief Container to hold the data of a single drop of blood */ class Blood { public: /// \brief constructor, takes position, direction and player /// \param position Position this drop starts at /// \param direction initial velocity of the drop /// \param player Player this blood drop started from. Blood(const Vector2& position, const Vector2& direction, int player); /// this function has to be called each step /// it updates position and velocity. void step(); /// gets the current position of this drop const Vector2& getPosition() const { return mPos; } private: Vector2 mPos; ///< the drops position Vector2 mDir; ///< the drops current velocity int mPlayer; ///< player who spilled this blood drop int mLastFrame; ///< time this drop was updated for the last time }; /*! \class BloodManager \brief Manages blood effects \details this class is responsible for managing blood effects, creating and deleting the particles, updating their positions etc. It is designed as a singleton, so it is noncopyable. */ class BloodManager : private boost::noncopyable { public: /// update function, to be called each step. void step(); /// \brief creates a blood effect /// \param pos Position the effect occurs /// \param intensity intensity of the hit. determines the number of particles /// \param player player which was hit, determines the colour of the particles void spillBlood(Vector2 pos, float intensity, int player); /// enables or disables blood effects void enable(bool enable) { mEnabled = enable; } /// gets the instance of BloodManager, creating one if it does not exists static BloodManager& getSingleton() { if (!mSingleton) mSingleton = new BloodManager; return *mSingleton; } private: /// default constructor, sets mEnabled to the value /// set in config.xml BloodManager(); /// helper function which returns an integer between /// min and max, boundaries included static int random(int min, int max); /// list which contains all currently existing blood particles std::list mParticles; /// true, if blood should be handled/drawn bool mEnabled; /// singleton static BloodManager* mSingleton; }; blobby-1.0rc3/src/RenderManagerNull.cpp0000644000175000017500000000205412042452374021407 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #include "RenderManagerNull.h" RenderManager* RenderManager::createRenderManagerNull() { return new RenderManagerNull(); } blobby-1.0rc3/src/IUserConfigReader.h0000644000175000017500000000277412042452374021020 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include class IUserConfigReader { public: IUserConfigReader() {}; static boost::shared_ptr createUserConfigReader(const std::string& file); virtual ~IUserConfigReader() {}; virtual std::string getValue(const std::string& name) const = 0; virtual float getFloat(const std::string& name) const = 0; virtual std::string getString(const std::string& name) const = 0; virtual bool getBool(const std::string& name) const = 0; virtual int getInteger(const std::string& name) const = 0; }; blobby-1.0rc3/src/DuelMatchState.cpp0000644000175000017500000000306712042452374020716 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "DuelMatchState.h" /* includes */ #include "raknet/BitStream.h" #include "GameConstants.h" #include "GenericIO.h" /* implementation */ void DuelMatchState::swapSides() { worldState.swapSides(); logicState.swapSides(); } USER_SERIALIZER_IMPLEMENTATION_HELPER(DuelMatchState) { io.template generic (value.worldState); io.template generic (value.logicState); } bool DuelMatchState::operator==(const DuelMatchState& other) const { return worldState == other.worldState && logicState == other.logicState; } blobby-1.0rc3/src/DuelMatch.h0000644000175000017500000001124312042452374017355 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "PhysicWorld.h" #include "GameLogic.h" #include "Vector.h" class InputSource; class DuelMatchState; /*! \class DuelMatch \brief class representing a blobby game. \details This class represents a single game between two players It applys the rules itself and provides an interface for querying different parameters. For this purpose it is designed as something similar to a singleton, but it can be instantiated multiple times on a server or be completely unavailable */ class DuelMatch { public: // This constructor takes the input sources used to get player input // The parameter output tells DuelMatch if it should report its // results to the user through RenderManager and SoundManager. // A deacivation of the output is useful on dedicated servers // If global is true, the instance registered as the main // game and can be accessed from everywhere. There can only // be one global game at a time, otherwise an assertion fails. // If remote is true, only physical responses will be calculated // but hit events and score events are received from network DuelMatch(InputSource* linput, InputSource* rinput, bool global, bool remote); ~DuelMatch(); // Allthough DuelMatch can be instantiated multiple times, a // singleton may be registered for the purpose of scripted or // interactive input. Note this can return 0. static DuelMatch* getMainGame(); void reset(); // This steps through one frame void step(); // this methods allow external input // events triggered by the network void setScore(int left, int right); void trigger(int event); void resetTriggeredEvents(); // This reports the index of the winning player and -1 if the // game is still running PlayerSide winningPlayer(); // This methods report the current game state and a useful for // the input manager, which needs information about the blob // positions and for lua export, which makes them accessable // for scripted input sources int getScore(PlayerSide player) const; int getScoreToWin() const; PlayerSide getServingPlayer() const; int getHitcount(PlayerSide player) const; Vector2 getBallPosition() const; Vector2 getBallVelocity() const; Vector2 getBlobPosition(PlayerSide player) const; const PhysicWorld& getWorld() const{ return mPhysicWorld; }; const Clock& getClock() const; Clock& getClock(); bool getBallDown() const; bool getBallActive() const; void pause(); void unpause(); bool isPaused() const{ return mPaused; } // This functions returns true if the player launched // and is jumping at the moment bool getBlobJump(PlayerSide player) const; // Set a new state received from server over a RakNet BitStream void setState(RakNet::BitStream* stream); /// Set a new state using a saved DuelMatchState void setState(const DuelMatchState& state); /// gets the current state DuelMatchState getState() const; //Input stuff for recording and playing replays const PlayerInput* getPlayersInput() const; void setPlayersInput(const PlayerInput& left, const PlayerInput& right); void setServingPlayer(PlayerSide side); enum { EVENT_LEFT_BLOBBY_HIT = 1, EVENT_RIGHT_BLOBBY_HIT = 2, EVENT_BALL_HIT_LEFT_GROUND = 4, EVENT_BALL_HIT_RIGHT_GROUND = 8, EVENT_BALL_HIT_GROUND = EVENT_BALL_HIT_LEFT_GROUND | EVENT_BALL_HIT_RIGHT_GROUND, EVENT_ERROR_LEFT = 16, EVENT_ERROR_RIGHT = 32, EVENT_ERROR = EVENT_ERROR_LEFT | EVENT_ERROR_RIGHT, EVENT_RESET = 64 }; int getEvents() const { return events; } private: static DuelMatch* mMainGame; bool mGlobal; PhysicWorld mPhysicWorld; InputSource* mLeftInput; InputSource* mRightInput; GameLogic mLogic; bool mBallDown; bool mPaused; int events; int external_events; bool mRemote; }; blobby-1.0rc3/src/GameConstants.h0000644000175000017500000000515312042452374020260 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ // Difficulty Settings const float BALL_SPEED_FACTOR = 1.00; // Border Settings const float LEFT_PLANE = 0; const float RIGHT_PLANE = 800.0; // These numbers should include the blobbys width, but in the original game // the blobbys can go a bit into the walls too. // Blobby Settings const float BLOBBY_HEIGHT = 89; //const float BLOBBY_WIDTH = 75; // what is the meaning of this value ??????? const float BLOBBY_UPPER_SPHERE = 19; const float BLOBBY_UPPER_RADIUS = 25; const float BLOBBY_LOWER_SPHERE = 13; const float BLOBBY_LOWER_RADIUS = 33; // Ground Settings const float GROUND_PLANE_HEIGHT_MAX = 500; const float GROUND_PLANE_HEIGHT = GROUND_PLANE_HEIGHT_MAX - BLOBBY_HEIGHT / 2.0; // This is exactly the half of the gravitation, i checked it in // the original code const float BLOBBY_MAX_JUMP_HEIGHT = GROUND_PLANE_HEIGHT - 206.375; // GROUND_Y - MAX_Y const float BLOBBY_JUMP_ACCELERATION = 15.1; // these values are calculated from the other two const float GRAVITATION = BLOBBY_JUMP_ACCELERATION * BLOBBY_JUMP_ACCELERATION / BLOBBY_MAX_JUMP_HEIGHT; const float BLOBBY_JUMP_BUFFER = GRAVITATION / 2; // Ball Settings const float BALL_RADIUS = 31.5; const float BALL_GRAVITATION = 0.287 * BALL_SPEED_FACTOR * BALL_SPEED_FACTOR; const float BALL_COLLISION_VELOCITY = std::sqrt(0.75 * RIGHT_PLANE * BALL_GRAVITATION); /// \todo work on a full-fledged physics spec // Volley Ball Net const float NET_POSITION_X = RIGHT_PLANE / 2; const float NET_POSITION_Y = 438; const float NET_RADIUS = 7; //const float NET_SPHERE = 154; // what is the meaning of this value ??????? const float NET_SPHERE_POSITION = 284; const float STANDARD_BALL_HEIGHT = 269 + BALL_RADIUS; blobby-1.0rc3/src/RenderManager.h0000644000175000017500000001703012042452374020221 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include #include "Vector.h" #include "Global.h" // Text definitions #define FONT_WIDTH_NORMAL 24 // Height and width of the normal font. #define LINE_SPACER_NORMAL 6 // Extra space between 2 lines in a normal SelectBox. #define FONT_WIDTH_SMALL 8 // Height and width of the small font. #define LINE_SPACER_SMALL 2 // Extra space between 2 lines in a small SelectBox. #define FONT_INDEX_ASTERISK 36 // M.W. : Currently a dot because there is no asterisk yet. // Text flags (usable for the RenderManager::drawText() flag parameter) // Just using one byte for now - up to 8 flags. #define TF_NORMAL 0x00 // 0 == false (backward compatibility for state modules) #define TF_HIGHLIGHT 0x01 // 1 == true (backward compatibility for state modules) #define TF_SMALL_FONT 0x02 // Draw a smaller font. (8px instead of 24px) #define TF_OBFUSCATE 0x04 // Obfuscate the text with asterisks. (for password Editboxes) // Text Alignment Flags #define TF_ALIGN_LEFT 0x00 // Text aligned to the left (default) #define TF_ALIGN_CENTER 0x08 // Text centered #define TF_ALIGN_RIGHT 0x10 // Text aligned right struct SDL_Surface; /*! \struct BufferedImage \brief image data \details couples the raw image data with its size in a way that is independend of the used renderer. */ struct BufferedImage { int w; int h; union { SDL_Surface* sdlImage; unsigned glHandle; }; }; /*! \class RenderManager \brief class for managing rendering \details This rendering class reduces all drawing stuff to a few calls to refresh the objects states. It also abstracts from specific graphics APIs. The following implementations are planned (ordered by importance) RenderManagerSDL: Uses standard SDL blits for drawing. It depends on precomputed rotated sprites and colors the blobs manually. Its fixed to the traditional resolution 800x600. RenderManagerGL2D: This manager relies on OpenGL to accelerate 2D drawing on systems like Linux/X11 where SDL acceleration is difficult. It rotates and colors its sprites in realtime, but still uses 2D graphics. RenderManagerGL3D: The GL3D is the top-end RenderManager. It uses newly created meshes and therefore supports vertex morphing for the blobs. It makes use of OpenGL to present special effects like per-pixel-lighting, stencil shadows, motion blur, and much more. It will requiere OpenGL 2.0 compliant graphics hardware. RenderManagerGP2X: This manager is used to port Blobby Volley to the GP2X handheld. It makes use of a fixed resolution at 320x240 and others for TV-Out. It also uses highly optimised loading routines with raw image data. In all other terms its similar to the RenderManagerSDL \todo This classes need a complete rework! They include far too much information about the actual game. */ class RenderManager { public: virtual ~RenderManager(){}; static RenderManager* createRenderManagerSDL(); static RenderManager* createRenderManagerGP2X(); static RenderManager* createRenderManagerGL2D(); static RenderManager* createRenderManagerNull(); static RenderManager& getSingleton() { return *mSingleton; } // Draws the stuff virtual void draw() = 0; // This swaps the screen buffers and should be called // after all draw calls virtual void refresh() {}; // Init with the desired Resolution. // Note: It is not guaranteed that this resolution will be selected virtual void init(int xResolution, int yResolution, bool fullscreen) {}; // Frees all internal data virtual void deinit() {}; // Set a background image by filename // Note: There is a default, you dont need to do this // Returns true on success virtual bool setBackground(const std::string& filename) { return true; }; // Colors the standard blob image, which are red and green by default virtual void setBlobColor(int player, Color color) {}; virtual void showShadow(bool shadow) {}; // Takes the new balls position and its rotation in radians virtual void setBall(const Vector2& position, float rotation) {}; // Takes the new position and the animation state as a float, // because some renderers may interpolate the animation virtual void setBlob(int player, const Vector2& position, float animationState) {}; virtual void setMouseMarker(float position); // Set the displayed score values and the serve notifications virtual void setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning) {}; // Set the names virtual void setPlayernames(std::string leftName, std::string rightName) {}; // Set the time virtual void setTime(const std::string& time) {}; // This simply draws the given text with its top left corner at the // given position and doesn't care about line feeds. virtual void drawText(const std::string& text, Vector2 position, unsigned int flags = TF_NORMAL) {}; // This loads and draws an image by name // The according Surface is automatically colorkeyed // The image is centered around position virtual void drawImage(const std::string& filename, Vector2 position) {}; // This draws a greyed-out area virtual void drawOverlay(float opacity, Vector2 pos1, Vector2 pos2, Color col = Color(0,0,0)) {} //Draws a blob virtual void drawBlob(const Vector2& pos, const Color& col){}; // Enables particle drawing virtual void startDrawParticles() {}; //Draw blood particle virtual void drawParticle(const Vector2& pos, int player){}; // Finishes drawing particles virtual void endDrawParticles() {}; // This forces a redraw of the background, for example // when the windows was minimized void redraw(); // This can disable the rendering of ingame graphics, for example for // the main menu void drawGame(bool draw); // This function may be useful for displaying framerates void setTitle(const std::string& title); protected: RenderManager(); // Returns -1 on EOF // Returns index for ? on unknown char int getNextFontIndex(std::string& string); SDL_Surface* highlightSurface(SDL_Surface* surface, int luminance); SDL_Surface* loadSurface(std::string filename); SDL_Surface* createEmptySurface(unsigned int width, unsigned int height); Vector2 blobShadowPosition(const Vector2& position); Vector2 ballShadowPosition(const Vector2& position); SDL_Rect blobRect(const Vector2& position); SDL_Rect blobShadowRect(const Vector2& position); SDL_Rect ballRect(const Vector2& position); SDL_Rect ballShadowRect(const Vector2& position); bool mDrawGame; std::map mImageMap; float mMouseMarkerPosition; bool mNeedRedraw; private: static RenderManager *mSingleton; }; blobby-1.0rc3/src/File.cpp0000644000175000017500000000706212042452374016725 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "File.h" /* includes */ #include #include #include "Global.h" #include "FileSystem.h" /* implementation */ File::File() : mHandle(0) { } File::File(const std::string& filename, OpenMode mode, bool no_override) : mHandle(0), mFileName("") { open(filename, mode, no_override); } File::~File() { // make sure we close this! close(); } void File::open(const std::string& filename, OpenMode mode, bool no_override) { // check that we don't have anything opened! /// \todo maybe we could just close the old file here... but /// then, this could also lead to errors... assert(mHandle == 0); // open depending on mode if( mode == OPEN_WRITE ) { if(no_override && FileSystem::getSingleton().exists(filename)) { throw FileAlreadyExistsException(filename); } mHandle = PHYSFS_openWrite(filename.c_str()); } else { mHandle = PHYSFS_openRead(filename.c_str()); } if (!mHandle) { throw FileLoadException(filename); } mFileName = filename; } void File::close() { // if handle is 0, no file is currently opened, so close does not do anything // maybe we could assert this, but i'm not sure that that is necessary. // we cannot assert this, because this function is run in the destrucor! if(mHandle) { if (PHYSFS_close( reinterpret_cast (mHandle) ) ) { /// we can't throw an error here, as this function gets called /// in the destructor and therefore might be called while another /// excpetion is active so we cant throw. }; mHandle = 0; mFileName = ""; } } void* File::getPHYSFS_file() { return mHandle; } bool File::is_open() const { return mHandle; } uint32_t File::length() const { check_file_open(); PHYSFS_sint64 len = PHYSFS_fileLength( reinterpret_cast (mHandle) ); if( len == -1 ) { throw( PhysfsFileException(mFileName) ); } return len; } uint32_t File::tell() const { check_file_open(); PHYSFS_sint64 tp = PHYSFS_tell( reinterpret_cast (mHandle) ); if(tp == -1) throw( PhysfsFileException(mFileName) ); return tp; } std::string File::getFileName() const { return mFileName; } void File::seek(uint32_t target) { check_file_open(); if(!PHYSFS_seek( reinterpret_cast(mHandle), target)) { throw( PhysfsFileException(mFileName) ); } } void File::check_file_open() const { // check that we have a handle if( !mHandle ) { throw( NoFileOpenedException() ); } } blobby-1.0rc3/src/InputSource.cpp0000644000175000017500000000241312042452374020321 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "InputSource.h" /* includes */ #include /* implementation */ /// \todo do we still need this file? std::ostream& operator<< (std::ostream& out, const PlayerInput& input) { out << (input.left ? 't' : 'f') << (input.right ? 't' : 'f') << (input.up ? 't' : 'f'); return out; } blobby-1.0rc3/src/FileRead.h0000644000175000017500000001117412042452374017165 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "File.h" #include // forward declarations for convenience functions struct lua_State; class TiXmlDocument; /** \class FileRead \brief Extension of file interface for reading file access. \details Provides various methods for reading numbers, strings and raw bytes from a file. \todo add more convenience methods for easier integration with lua script loading and tinyXML. \sa FileWrite */ class FileRead : public File { public: /// \brief default ctor /// \details File has to be opended with open() /// \throw nothing explicit FileRead(); /// \brief constructor which opens a file. /// \param filename File to be opened for reading /// \throw FileLoadException, if the file could not be loaded FileRead(const std::string& filename); /// \brief opens a file. /// \param filename File to be opened for reading /// \throw FileLoadException, if the file could not be loaded /// \pre No file is currently opened. void open(const std::string& filename); /// destructor, closes the file (if any open) /// \sa close() /// \throw nothing ~FileRead(); // ------------------------------------ // reading interface // ------------------------------------ /// reads bytes into a buffer /// \param target buffer to read into /// \param num_of_bytes number of bytes to read /// \throw PhysfsFileException when nothing could be read /// \throw NoFileOpenedException when called while no file is opened. /// \throw EOFException when cless than \p num_of_bytes bytes are available. uint32_t readRawBytes( char* target, std::size_t num_of_bytes ); /// reads bytes and returns a safe-pointed buffer /// the buffer is allocated by this function and has a size of \p num_of_bytes /// \param num_of_bytes Number of bytes to read; size of buffer /// \throw PhysfsFileException when nothing could be read /// \throw NoFileOpenedException when called while no file is opened. /// \throw EOFException when cless than \p num_of_bytes bytes are available. boost::shared_array readRawBytes( std::size_t num_of_bytes ); /// reads exactly one byte /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. char readByte(); /// reads an unsinged 32 bit integer from the next four bytes in the file /// the integer is expected to be in little-endian-order and is converted /// to the native format. /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. uint32_t readUInt32(); /// reads a 32 bit float from the next four bytes in the file /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. float readFloat(); /// reads a null-terminated string from the file /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. std::string readString(); // helper function for checksum /// calculates a crc checksum of the file contents beginning at posInFile till the end of the file. uint32_t calcChecksum(uint32_t start); // ----------------------------------------------------------------------------------------- // LUA/XML reading helper function // ----------------------------------------------------------------------------------------- static int readLuaScript(std::string filename, lua_State* mState); static boost::shared_ptr readXMLDocument(const std::string& filename); }; blobby-1.0rc3/src/RakNetPacket.h0000644000175000017500000000257612042452374020034 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include "raknet/RakPeer.h" class Packet; typedef boost::shared_ptr packet_ptr; struct deleter { RakPeer* peer; void operator()(Packet* p) { peer->DeallocatePacket(p); } }; inline packet_ptr receivePacket(RakPeer* peer) { deleter del; del.peer = peer; Packet* pptr = peer->Receive(); if(pptr) { return packet_ptr(pptr, del); } else { return packet_ptr(); } } blobby-1.0rc3/src/RenderManagerSDL.h0000644000175000017500000001052512042452374020566 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "RenderManager.h" /*! \class RenderManagerSDL \brief Render Manager on top of SDL \details This render manager uses SDL for all drawing operations. This means it is highly portable, but somewhat slow (e.g. when doing morphing blobs). */ class RenderManagerSDL : public RenderManager { public: RenderManagerSDL(); virtual void init(int xResolution, int yResolution, bool fullscreen); virtual void deinit(); virtual void draw(); virtual void refresh(); virtual bool setBackground(const std::string& filename); virtual void setBlobColor(int player, Color color); virtual void showShadow(bool shadow); virtual void setBall(const Vector2& position, float rotation); virtual void setBlob(int player, const Vector2& position, float animationState); virtual void setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning); virtual void setPlayernames(std::string leftName, std::string rightName); virtual void setTime(const std::string& t); virtual void setMouseMarker(float position); virtual void drawText(const std::string& text, Vector2 position, unsigned int flags = TF_NORMAL); virtual void drawImage(const std::string& filename, Vector2 position); virtual void drawOverlay(float opacity, Vector2 pos1, Vector2 pos2, Color col); virtual void drawBlob(const Vector2& pos, const Color& col); virtual void drawParticle(const Vector2& pos, int player); private: struct DynamicColoredSurface { // constructors // start surface is expected to have color 0xffffff DynamicColoredSurface() : mSDLsf(0), mColor(255, 255, 255) {}; explicit DynamicColoredSurface(SDL_Surface* sf) : mSDLsf(sf), mColor(255, 255, 255) {}; DynamicColoredSurface(SDL_Surface* sf, Color c) : mSDLsf(sf), mColor(c) {}; SDL_Surface* mSDLsf; Color mColor; }; SDL_Surface* mBackground; SDL_Surface* mBallShadow; SDL_Surface* mScroll; std::vector mBall; std::vector mStandardBlob; std::vector mStandardBlobShadow; SDL_Surface* mStandardBlobBlood; std::vector mLeftBlob; std::vector mLeftBlobShadow; SDL_Surface* mLeftBlobBlood; std::vector mRightBlob; std::vector mRightBlobShadow; SDL_Surface* mRightBlobBlood; std::vector mFont; std::vector mHighlightFont; std::vector mSmallFont; std::vector mHighlightSmallFont; SDL_Surface *mOverlaySurface; SDL_Surface *mScreen; Vector2 mBallPosition; float mBallRotation; Vector2 mLeftBlobPosition; float mLeftBlobAnimationState; Vector2 mRightBlobPosition; float mRightBlobAnimationState; bool mShowShadow; int mLeftPlayerScore; int mRightPlayerScore; bool mLeftPlayerWarning; bool mRightPlayerWarning; std::string mLeftPlayerName; std::string mRightPlayerName; SDL_Surface* mLeftPlayerNameTexture; SDL_Surface* mRightPlayerNameTexture; std::string mTime; // Store color for caching Color mBlobColor[MAX_PLAYERS]; // colors a surface // the returned SDL_Surface* is already converted into DisplayFormat DynamicColoredSurface colorSurface(SDL_Surface *surface, Color color); void drawTextImpl(const std::string& text, Vector2 position, unsigned int flags, SDL_Surface* screen); void colorizeBlobs(int player); }; blobby-1.0rc3/src/InputDevice.h0000644000175000017500000000646112042452374017734 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "Global.h" #include "InputSource.h" #include #include "LagDetectionSystem.h" class JoystickPool { public: static JoystickPool& getSingleton(); SDL_Joystick* getJoystick(int id); int probeJoysticks(); void closeJoysticks(); private: typedef std::map JoyMap; JoyMap mJoyMap; static JoystickPool* mSingleton; }; struct JoystickAction { enum Type { AXIS, BUTTON, // We don't implement these exotic input methods here // HAT, // TRACKBALL }; JoystickAction(std::string string); JoystickAction(int _joyid, Type _type, int _number) : type(_type), joy(0), joyid(_joyid), number(_number) {} ~JoystickAction(); JoystickAction(const JoystickAction& action); std::string toString(); Type type; SDL_Joystick* joy; int joyid; // Note: Axis are stored as the SDL axis +1, so we can used // the signedness as direction indication int number; }; /*! \class InputDevice \brief Abstract base class for game input methods */ class InputDevice { public: InputDevice() {} virtual ~InputDevice() {} virtual void transferInput(PlayerInput& mInput) = 0; }; /*! \class MouseInputDevice \brief Ingame mouse control */ class MouseInputDevice : public InputDevice { private: PlayerSide mPlayer; int mJumpButton; int mMarkerX; bool mDelay; // The pressed button of the mainmenu must be ignored boost::circular_buffer mInputs; LagDetector mLag; public: virtual ~MouseInputDevice(){}; MouseInputDevice(PlayerSide player, int jumpbutton); void transferInput(PlayerInput& input); }; /*! \class KeyboardInputDevice \brief Ingame keyboard input */ class KeyboardInputDevice : public InputDevice { private: SDLKey mLeftKey; SDLKey mRightKey; SDLKey mJumpKey; public: virtual ~KeyboardInputDevice(){}; KeyboardInputDevice(SDLKey leftKey, SDLKey rightKey, SDLKey jumpKey); void transferInput(PlayerInput& input); }; /*! \class JoystickInputDevice \brief Ingame Joystick input */ class JoystickInputDevice : public InputDevice { private: bool getAction(const JoystickAction& action); JoystickAction mLeftAction; JoystickAction mRightAction; JoystickAction mJumpAction; public: ~JoystickInputDevice() {}; JoystickInputDevice(JoystickAction laction, JoystickAction raction, JoystickAction jaction); void transferInput(PlayerInput& input); }; blobby-1.0rc3/src/TextManager.h0000644000175000017500000001010112042452374017716 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include /// \brief class for managing the text /// \details multilanguage support /// the string can be loaded from a xml file /// PacketQueue; class NetworkGame { public: // The given server is used to send messages to the client, received // messages have to bo injected manually in this class. // The PlayerID parameters are the IDs of the participating players. // The IDs are assumed to be on the same side as they are named. // If both players want to be on the same side, switchedSide // decides which player is switched. NetworkGame(RakServer& server, PlayerID leftPlayer, PlayerID rightPlayer, std::string leftPlayerName, std::string rightPlayerName, Color leftColor, Color rightColor, PlayerSide switchedSide = NO_PLAYER); ~NetworkGame(); void injectPacket(const packet_ptr& packet); // This function processes the queued network packets, // makes a physic step, checks the rules and broadcasts // the current state and outstanding messages to the clients. // It returns whether there are still clients connected. bool step(); private: void broadcastBitstream(RakNet::BitStream* stream, RakNet::BitStream* switchedstream); void broadcastBitstream(RakNet::BitStream* stream); void broadcastPhysicState(); RakServer& mServer; PlayerID mLeftPlayer; PlayerID mRightPlayer; PlayerSide mSwitchedSide; std::string mLeftPlayerName; std::string mRightPlayerName; PacketQueue mPacketQueue; DuelMatch* mMatch; boost::scoped_ptr mLeftInput; boost::scoped_ptr mRightInput; PlayerSide mWinningPlayer; boost::scoped_ptr mRecorder; bool mPausing; float mGameSpeed; SpeedController* mGameFPSController; }; blobby-1.0rc3/src/FileWrite.cpp0000644000175000017500000000441412042452374017736 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "FileWrite.h" /* includes */ #include #include #include "Global.h" /* implementation */ FileWrite::FileWrite() { } FileWrite::FileWrite(const std::string& filename, bool no_override) : File(filename, File::OPEN_WRITE, no_override) { } FileWrite::~FileWrite() { // no more actions than what ~File already does } void FileWrite::open(const std::string& filename, bool no_override) { File::open(filename, File::OPEN_WRITE, no_override); } void FileWrite::writeByte(char c) { write(&c, sizeof(c)); } void FileWrite::writeUInt32(uint32_t v) { check_file_open(); if( !PHYSFS_writeULE32( reinterpret_cast(mHandle), v) ) { throw( PhysfsFileException(mFileName) ); } } void FileWrite::writeFloat(float fl) { write(reinterpret_cast(&fl), sizeof(fl)); } void FileWrite::write(const std::string& data) { write(data.data(), data.size()); } void FileWrite::writeNullTerminated(const std::string& data) { write(data.c_str(), data.size() + 1); } void FileWrite::write(const char* data, std::size_t length) { check_file_open(); if( PHYSFS_write(reinterpret_cast(mHandle), data, 1, length) != length ) { throw( PhysfsFileException(mFileName) ); } } blobby-1.0rc3/src/Clock.cpp0000644000175000017500000000537212042452374017103 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "Clock.h" /* includes */ #include #include "SDL/SDL.h" /* implementation */ Clock::Clock() : mRunning(false), mGameTime(0), mLastTime(0) { } void Clock::reset() { // set all variables to their default values mRunning = false; mGameTime = 0; mLastTime = SDL_GetTicks(); } void Clock::start() { mLastTime = SDL_GetTicks(); mRunning = true; } void Clock::stop() { mRunning = false; } bool Clock::isRunning() const { return mRunning; } int Clock::getTime() const { return mGameTime / 1000; } void Clock::setTime(int newTime) { mGameTime = newTime * 1000; } std::string Clock::getTimeString() const { /// \todo maybe it makes sense to cache this value. we call this function ~75times a seconds /// when the string changes only once. guest it does not make that much of a difference, but still... // calculate seconds, minutes and hours as integers int time_sec = mGameTime / 1000; int seconds = time_sec % 60; int minutes = ((time_sec - seconds) / 60) % 60; int hours = ((time_sec - 60 * minutes - seconds) / 3600) % 60; // now convert to string via stringstream std::stringstream stream; // only write hours if already player more than 1h if(hours > 0) stream << hours << ":"; // write minutes // leading 0 if minutes < 10 if(minutes < 10) stream << "0"; stream << minutes << ":"; // write seconds // leading 0 if seconds < 10 if(seconds < 10) stream << "0"; stream << seconds; // convert stringstream to string and return return stream.str(); } void Clock::step() { if(mRunning) { int newTime = SDL_GetTicks(); if(newTime > mLastTime) { mGameTime += newTime - mLastTime; } mLastTime = newTime; } } blobby-1.0rc3/src/GameLogic.h0000644000175000017500000001400512042452374017335 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "Global.h" #include "Clock.h" class GameLogicState; /// \class IGameLogic /// \brief Interface for managing game rules, score counting etc. /// \details this class is told what happens in the game and it applies the rules to count /// the points. it is designed as a abstract base class to provide different /// implementations (ie old/new volleyball rules) class IGameLogic { public: // constuctor and destructor IGameLogic(); virtual ~IGameLogic(); // ----------------------------------------------------------------------------------------- // Read/Write Basic Data // ----------------------------------------------------------------------------------------- // methods for querying the score/touches of a patricular team /// returns current points of one player int getScore(PlayerSide side) const; /// sets the score of the specified player void setScore(PlayerSide side, int score); // when might need such a method if we add saved games /// returns the number of times a player has hit the ball. int getHits(PlayerSide side) const; // method for querying and setting the serving player /// returns which player is the serving player PlayerSide getServingPlayer() const; /// sets which player is currently the serving player void setServingPlayer(PlayerSide side); /// returns the winning player or NO_PLAYER if the /// game still runs PlayerSide getWinningPlayer() const; /// \brief returns which player made the last mistake. /// After this request, that value is reset. PlayerSide getLastErrorSide(); // methods for setting/getting the target score /// sets the score required for winning. void setScoreToWin(int stw); /// returns the score required for winning. int getScoreToWin() const; /// gets the associated clock Clock& getClock(); // ----------------------------------------------------------------------------------------- // Read / Write - State // ----------------------------------------------------------------------------------------- GameLogicState getState() const; void setState(GameLogicState gls); // ----------------------------------------------------------------------------------------- // Event - Handlers // ----------------------------------------------------------------------------------------- // methods to inform the game logic what is happening in the game /// called when ball hits ground void onBallHitsGround(PlayerSide side); /// called when ball hits player void onBallHitsPlayer(PlayerSide side); /// returns whether the collision was valid (max. 3 hits) bool isCollisionValid(PlayerSide side) const; // set/unset pause mode /// pauses the game logic. void onPause(); /// disables pause mode void onUnPause(); /// must be called every step void step(); protected: /// this method must be called if a team scores /// is increments the points of that team void score(PlayerSide side); // helper functions /// convert player side into array index static inline int side2index(PlayerSide side) { assert(side == LEFT_PLAYER || side == RIGHT_PLAYER); return side - LEFT_PLAYER; } /// determine the opposite player side static inline PlayerSide other_side(PlayerSide side) { switch(side) { case LEFT_PLAYER: return RIGHT_PLAYER; case RIGHT_PLAYER: return LEFT_PLAYER; default: assert(0); } } private: /// resets score and touches void reset(); /// this is called when a player makes a mistake void onError(PlayerSide side); /// this function is called by onError, it contains the customizable part of the /// error handling virtual void OnMistake(PlayerSide side) = 0; /// this function handles ball/player hits virtual bool OnBallHitsPlayerHandler(PlayerSide ply, int numOfHits) = 0; /// this function checks whether a player has won the game virtual PlayerSide checkWin() const = 0; // data memberss /// this array contains the scores int mScores[2]; /// in this array the number of touches are counted int mTouches[2]; /// this is an helper array to prevent counting hits that happen too fast twice int mSquish[2]; /// last side that made an error PlayerSide mLastError; /// player that is currently serving PlayerSide mServingPlayer; /// player that has won the game /// \todo do we really need to cache this information here?? PlayerSide mWinningPlayer; /// config parameter: score to win /// \todo how do we use config parameters with lua rules? int mScoreToWin; /// clock for determining game tome Clock clock; }; /// typedef to make GameLogic an auto_ptr /// \todo is auto_ptr the best choice here? typedef std::auto_ptr GameLogic; // function for creating a game logic object GameLogic createGameLogic(const std::string& rulefile); blobby-1.0rc3/src/GameLogicState.h0000644000175000017500000000264712042452374020347 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include "Global.h" #include "GenericIOFwd.h" struct GameLogicState { unsigned int leftScore; unsigned int rightScore; PlayerSide servingPlayer; unsigned int leftSquish; unsigned int rightSquish; void swapSides(); void serialize(GenericOut* io) const; void deserialize(GenericIn* io); // equality comparision bool operator==(const GameLogicState& other) const; }; std::ostream& operator<<(std::ostream&, const GameLogicState&); blobby-1.0rc3/test/FileTest.cpp0000644000175000017500000003257112042452364017757 0ustar danielknobedanielknobe#define BOOST_TEST_MODULE FileAbstraction #include #include "FileRead.h" #include "FileWrite.h" #include "FileSystem.h" #include #include #include #define TEST_EXECUTION_PATH "C:\\Dokumente und Einstellungen\\Erik\\Eigene Dateien\\Blobby Volley 2\\test" // helper void init_Physfs() { static bool initialised = false; if(!initialised) { std::cout << "initialising physfs to " << TEST_EXECUTION_PATH << "\n"; static FileSystem fs( TEST_EXECUTION_PATH ); fs.setWriteDir("."); PHYSFS_addToSearchPath(".", 1); initialised = true; } } #define CHECK_EXCEPTION_SAFETY(expr, excp) try { \ BOOST_TEST_CHECKPOINT("trying " #expr); \ expr; \ BOOST_ERROR(#expr " does not cause " #excp " to be thrown");\ } \ catch(excp& e) {\ \ } catch (std::exception& exp) \ { \ BOOST_ERROR(std::string("unexpected exception ") + exp.what() + "instead of " #excp " caught from "#expr); \ }; // Tests of common FileSystem functions // test init BOOST_AUTO_TEST_SUITE( FileSystemTest ) BOOST_AUTO_TEST_CASE( default_constructor ) { /// \todo spec what happens here; currently asserts // FileSystem::getSingleton(); /// \todo how to make this a sensible path on all platforms? { FileSystem fs( TEST_EXECUTION_PATH ); BOOST_CHECK_EQUAL( &fs, &FileSystem::getSingleton()); /// \todo currently, an assertion fails here! // try to init again //FileSystem fs2("C:\\Dokumente und Einstellungen\\Erik\\Eigene Dateien\\Blobby Volley 2\\test\\bin\\debug\\"); } // here, fs is deleted so we can create a new file system // try to create it with spam path /// \todo spec, what error happens here FileSystem fs3("__SPAM__"); } // the functions deleteFile, exists, isDirectory, addToSearchPath, removeFromSearchPath, setWriteDir and getUserDir // currently just wrap PHYSFS functions, so they actually don't do anything. Thus, these functions are not // tested here. Once we have a defined error reporting policy etc, tests will be added. /// \todo test EnumerateFiles BOOST_AUTO_TEST_CASE( enumerate_files ) { /// \todo spec what happens here; currently asserts // FileSystem::getSingleton(); /// \todo how to make this a sensible path on all platforms? { FileSystem fs( TEST_EXECUTION_PATH ); BOOST_CHECK_EQUAL( &fs, &FileSystem::getSingleton()); /// \todo currently, an assertion fails here! // try to init again //FileSystem fs2("C:\\Dokumente und Einstellungen\\Erik\\Eigene Dateien\\Blobby Volley 2\\test\\bin\\debug\\"); } // here, fs is deleted so we can create a new file system // try to create it with spam path /// \todo spec, what error happens here FileSystem fs3("__SPAM__"); } /// \todo test probeDir BOOST_AUTO_TEST_SUITE_END() // most of the following functions just wrap to PHYSFS calls. /// \todo how to test these? // test enumerate files // test deleteFile // test exists // test isDirectory // test mkdir // ... /// \todo check all FileSystem methods are covered in tests /// \todo check all FileRead/FileWrite methods are covered in tests BOOST_AUTO_TEST_SUITE( ReadFileTest ) BOOST_AUTO_TEST_CASE( default_constructor ) { FileRead default_constructed; // no file is opened after default construction BOOST_CHECK( default_constructed.is_open() == false ); BOOST_CHECK( default_constructed.getPHYSFS_file() == 0 ); BOOST_CHECK( default_constructed.getFileName() == "" ); // all other operations should raise an assertion! CHECK_EXCEPTION_SAFETY (default_constructed.length(), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.tell(), NoFileOpenedException); char target; CHECK_EXCEPTION_SAFETY (default_constructed.readRawBytes(&target, 1), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.readRawBytes(1), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.readUInt32(), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.readString(), NoFileOpenedException); } BOOST_AUTO_TEST_CASE( open_read_constructor ) { init_Physfs(); // create a temp file for the next check try { FileWrite write_file("test_open_read_constructor.tmp"); write_file.write("test"); write_file.close(); } catch (std::exception& s) { std::cout << "Error: " << s.what() << "\n"; BOOST_ERROR("this should never happen!"); } // now this file exists try{ FileRead read_file("test_open_read_constructor.tmp"); // now, a file is opened! BOOST_REQUIRE( read_file.is_open() == true ); BOOST_CHECK( read_file.getPHYSFS_file() != 0 ); BOOST_CHECK( read_file.getFileName() == "test_open_read_constructor.tmp" ); BOOST_CHECK( read_file.length() == 4); read_file.close(); BOOST_CHECK( read_file.is_open() == false ); BOOST_CHECK( read_file.getPHYSFS_file() == 0 ); BOOST_CHECK( read_file.getFileName() == "" ); } catch (std::exception& e) { BOOST_ERROR(e.what()); } CHECK_EXCEPTION_SAFETY(FileRead read_file("this_file_surely_does_not_exists?!@<|.tmp"), FileLoadException); PHYSFS_delete("test_open_read_constructor.tmp"); } // !!!!!!!!!!! // we don't have any tests for the functions close, getPHYSFS_file and is_open. // These are already tested together with the constructors (and other functions) // // wrongly closed file BOOST_AUTO_TEST_CASE( wrongly_closed_file_test ) { init_Physfs() ; FileWrite create_test_file("test_open_close.tmp"); create_test_file.writeByte(1); // close the file, loughing wickedly ;) // don't ever do that in non-test code! create_test_file.close(); FileRead test_file ("test_open_close.tmp"); test_file.close(); /// \todo this test can't work as we do it now because physfs just crashes when we /// do this. //PHYSFS_close( (PHYSFS_file*)test_file.getPHYSFS_file() ); // now, every action we try to perform on that file should yield an excpetion /// For now, these are all functions we have to test /// make sure to add new ones! CHECK_EXCEPTION_SAFETY(test_file.tell(), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(test_file.seek(2), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(test_file.length(), NoFileOpenedException); char buffer[3]; CHECK_EXCEPTION_SAFETY(test_file.readRawBytes(buffer, 3), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(test_file.readRawBytes(1), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(test_file.readUInt32(), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(test_file.readString(), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(create_test_file.writeByte(5), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(create_test_file.writeUInt32(5), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(create_test_file.write( std::string("bye bye world;)") ), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(create_test_file.writeNullTerminated( std::string("bye bye world;)") ), NoFileOpenedException); CHECK_EXCEPTION_SAFETY(create_test_file.write( "bye bye world;)", 8 ), NoFileOpenedException); } BOOST_AUTO_TEST_CASE( exception_test ) { init_Physfs() ; // create a temp helper file { FileWrite helper("read.tmp"); helper.writeByte(5); } FileRead test_file("read.tmp"); BOOST_REQUIRE( test_file.is_open() == true ); // move reader in front of file beginning CHECK_EXCEPTION_SAFETY(test_file.seek(-1), PhysfsException); // move reader after file ending CHECK_EXCEPTION_SAFETY(test_file.seek(100), PhysfsException); char buffer[3]; // read negative amounts of bytes CHECK_EXCEPTION_SAFETY(test_file.readRawBytes(buffer, -5), PhysfsException); CHECK_EXCEPTION_SAFETY(test_file.readRawBytes(-5), std::bad_alloc); test_file.seek(0); // read more than there is CHECK_EXCEPTION_SAFETY(test_file.readRawBytes(buffer, 3), EOFException); CHECK_EXCEPTION_SAFETY(test_file.readUInt32(), EOFException); CHECK_EXCEPTION_SAFETY(test_file.readRawBytes(5), EOFException); CHECK_EXCEPTION_SAFETY(test_file.readString(), EOFException); } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE( WriteFileTest ) BOOST_AUTO_TEST_CASE( default_constructor ) { FileWrite default_constructed; // no file is opened after default construction BOOST_CHECK( default_constructed.is_open() == false ); BOOST_CHECK( default_constructed.getPHYSFS_file() == 0 ); BOOST_CHECK( default_constructed.getFileName() == "" ); // all other operations should raise an assertion! CHECK_EXCEPTION_SAFETY (default_constructed.length(), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.tell(), NoFileOpenedException); char target; CHECK_EXCEPTION_SAFETY (default_constructed.writeByte('c'), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.write(std::string("c")), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.writeUInt32(5), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.writeNullTerminated(std::string("c")), NoFileOpenedException); CHECK_EXCEPTION_SAFETY (default_constructed.write(&target, 1), NoFileOpenedException); } BOOST_AUTO_TEST_CASE( open_write_constructor ) { init_Physfs() ; FileWrite write_file("test_open_write_constructor.tmp"); // now, a file is opened! BOOST_REQUIRE( write_file.is_open() == true ); BOOST_CHECK( write_file.getPHYSFS_file() != 0 ); BOOST_CHECK( write_file.getFileName() == "test_open_write_constructor.tmp" ); // this file is new, so length should be 0 BOOST_CHECK( write_file.length() == 0 ); write_file.close(); BOOST_CHECK( write_file.is_open() == false ); // make sure we delete this file after the test, so we can run the test a second time // under same circumstances PHYSFS_delete("test_open_write_constructor.tmp"); try { FileWrite write_file2("this_file_surely_cannot_exists?!@<|.tmp"); BOOST_ERROR("opening fiels with invalid names should lead to an exception"); } catch (std::exception& s) { // fine } } BOOST_AUTO_TEST_CASE( open_close_test ) { init_Physfs(); FileWrite test_file("test_open_close.tmp"); BOOST_REQUIRE( test_file.is_open() == true ); test_file.close(); BOOST_REQUIRE( test_file.is_open() == false ); // now open another file test_file.open("test_open_close2.tmp"); BOOST_REQUIRE( test_file.is_open() == true ); test_file.close(); BOOST_REQUIRE( test_file.is_open() == false ); // and again the first file test_file.open("test_open_close.tmp"); BOOST_REQUIRE( test_file.is_open() == true ); test_file.close(); BOOST_REQUIRE( test_file.is_open() == false ); // cleanup PHYSFS_delete("test_open_close.tmp"); PHYSFS_delete("test_open_close2.tmp"); } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE( FileWriteReadCycle ) BOOST_AUTO_TEST_CASE( raw_data_test ) { init_Physfs(); char data[] = { 's', 'p', 'a', 'm', ' ', 't', 'e', 's', 't' }; FileWrite writer("cycle.tmp"); BOOST_REQUIRE( writer.is_open() == true ); //FileRead reader_e("cycle.tmp"); /// \todo we need to define what happens when we open a file for reading and writing simultaniuosly writer.write( data, sizeof(data) ); writer.close(); FileRead reader("cycle.tmp"); char data2[sizeof(data)]; reader.readRawBytes(data2, sizeof(data)); BOOST_CHECK( std::memcmp(data, data2, sizeof(data)) == 0 ); reader.seek(0); boost::shared_array data3 = reader.readRawBytes(sizeof(data)); BOOST_CHECK( std::memcmp(data, data3.get(), sizeof(data)) == 0 ); PHYSFS_delete("cycle.tmp"); } BOOST_AUTO_TEST_CASE( string_test ) { init_Physfs(); std::string teststr = "hello world!"; BOOST_CHECKPOINT( "string_test: writing test file" ); FileWrite writer("cycle.tmp"); BOOST_REQUIRE( writer.is_open() == true ); writer.write( teststr ); writer.writeNullTerminated( teststr ); writer.write( teststr ); writer.close(); BOOST_CHECKPOINT( "string_test: reading test file" ); FileRead reader("cycle.tmp"); boost::shared_array data = reader.readRawBytes(teststr.size()); BOOST_CHECK_EQUAL (reader.tell(), teststr.size() ); std::string str2 = reader.readString(); BOOST_CHECK_EQUAL (reader.tell(), 2 * teststr.size() + 1 ); BOOST_CHECK( std::memcmp(data.get(), teststr.data(), teststr.length()) == 0 ); BOOST_CHECK_EQUAL( teststr, str2 ); // now, try to read as null terminated when it isn't CHECK_EXCEPTION_SAFETY( reader.readString(), EOFException); PHYSFS_delete("cycle.tmp"); } BOOST_AUTO_TEST_CASE( int_test ) { init_Physfs(); BOOST_CHECKPOINT( "int_test: writing test file" ); FileWrite writer("cycle.tmp"); BOOST_REQUIRE( writer.is_open() == true ); const int TEST_INT_1 = 12; const int TEST_INT_2 = -8; const int TEST_INT_3 = 1275343; writer.writeUInt32( TEST_INT_1 ); writer.writeUInt32( TEST_INT_2 ); writer.writeUInt32( TEST_INT_3 ); writer.writeByte( 5 ); writer.close(); BOOST_CHECKPOINT( "int_test: reading test file" ); FileRead reader("cycle.tmp"); int res = reader.readUInt32( ); BOOST_CHECK_EQUAL (res, TEST_INT_1 ); res = reader.readUInt32( ); BOOST_CHECK_EQUAL (res, TEST_INT_2 ); res = reader.readUInt32( ); BOOST_CHECK_EQUAL (res, TEST_INT_3 ); // try to read more CHECK_EXCEPTION_SAFETY( reader.readUInt32(), EOFException); PHYSFS_delete("cycle.tmp"); } BOOST_AUTO_TEST_SUITE_END() blobby-1.0rc3/test/LagDetectionTest.cpp0000644000175000017500000002606712042452364021445 0ustar danielknobedanielknobe//#define BOOST_TEST_ALTERNATIVE_INIT_API #define BOOST_TEST_MODULE LagDetector #include #include "InputSource.h" #include "LagDetectionSystem.h" #include #include "CrossCorrelation.h" #include #include #include #include #include #include #include #include #include using namespace boost::accumulators; PlayerInput randomInputGen() { return PlayerInput( rand() % 2, rand() % 2, rand() % 2); } PlayerInput randomInput(int p = 1) { static PlayerInput last = randomInputGen(); if(rand() % p == 0) { last = randomInputGen(); } return last; } /// \todo add parameters: lag, packet loss, input randomness struct LaggingNetworkInputSimulator { LaggingNetworkInputSimulator(int p) { lag_simulator.resize(p + 1); srand(5011); } void sendInput( PlayerInput ip) { lag_simulator.push_back(ip); } void changePing(int p) { lag_simulator.set_capacity(p + 1); // we must ensure that this is full, otherwise // back and front do not work as expected? while(!lag_simulator.full()) { lag_simulator.push_front(getRemote()); } } PlayerInput getLocal() const { return lag_simulator.back(); } PlayerInput getRemote() const { return lag_simulator.front(); } private: boost::circular_buffer lag_simulator; }; /// Fixture for creating a LagDetector struct LagDetectionSetup { LagDetectionSetup() : D (LagDetector()), NetworkSimulator(LaggingNetworkInputSimulator( 0 )) { } LagDetectionSetup(int l) : D (LagDetector()), NetworkSimulator(LaggingNetworkInputSimulator( l )) { } ~LagDetectionSetup() { }; void reset_simulator(int l) { *this = LagDetectionSetup(l); } void simulate( int steps = 1, int p = 1) { for(int i = 0; i < steps; ++i) { PlayerInput ip = randomInput(p); NetworkSimulator.sendInput(ip); D.insertData(NetworkSimulator.getLocal(), NetworkSimulator.getRemote()); } } LagDetector D; LaggingNetworkInputSimulator NetworkSimulator; }; // ------------------------------------------------------------------------------------------------- // // T E S T S // // ------------------------------------------------------------------------------------------------- /// file which logs additional debug data std::fstream file ("debug.txt", std::fstream::out); /// file which logs quality data std::fstream result ("results.txt", std::fstream::out); /// how many steps do we simulate each test const int SIMULATION_DURATION = 500; /// \todo add a test which looks how good the algorithm performs on periodic data BOOST_AUTO_TEST_SUITE( lag_detector ) // correlation BOOST_AUTO_TEST_CASE( self_correlation ) { boost::circular_buffer buf; buf.resize(100); // insert zero lag data for(int i=0; i < 100; ++i) { PlayerInput ip = randomInput(); buf.push_back(ip); } int t = crossCorrelation(buf, buf).offset; BOOST_REQUIRE(t == 0); } // test that checks wether LagDetector finds zero lag when no lag is present BOOST_FIXTURE_TEST_CASE( zero_lag, LagDetectionSetup ) { // insert zero lag data simulate(10); // now we do 50 steps and check for lag for(int i = 0; i < SIMULATION_DURATION; ++i) { simulate(); int lag = D.getLag(); if( lag != 0 ) { char errormsg[1024]; sprintf(errormsg, "lag of %d detected when simulating zero lag", lag); BOOST_FAIL(""); }; } } // test that checks wether LagDetector finds constant lags /// \todo use http://www.boost.org/doc/libs/1_48_0/libs/test/doc/html/utf/user-guide/test-organization/unary-test-case.html BOOST_FIXTURE_TEST_CASE( constant_lag, LagDetectionSetup ) { file << "\nconstant lag" << "\n"; // test for all small constant lags for(int clag = 1; clag < 10; clag++) { reset_simulator(clag); simulate(25); // now we do 50 steps and check for lag for(int i = 0; i < SIMULATION_DURATION; ++i) { simulate(); int lag = D.getLag(); if( lag != clag ) { file << D.getDebugString() << "\n"; char errormsg[1024]; sprintf(errormsg, "detected lag of %d when constant lag of %d was simulated", lag, clag); BOOST_FAIL(errormsg); }; } } } // CONFIGURE: const int REAL_INPUT_PATTERN_LENGTH = 40; const int INPUT_CHANGE_STEP_WIDTH = 5; // check constant lag with bad input data BOOST_FIXTURE_TEST_CASE( constant_lag_real_input, LagDetectionSetup ) { file << "\nconstant lag - real input" << "\n"; result << "constant lag - real input\n"; result << "In\tF%\tsig\n"; for(int input_quality = 10; input_quality <= REAL_INPUT_PATTERN_LENGTH; input_quality += INPUT_CHANGE_STEP_WIDTH ) { // determine a random lag and set up the simulation int clag = rand() % 4 + 4; reset_simulator(clag); // start simulate(25, input_quality); // now we do 500 steps and check for lag int errors = 0; int cum_err = 0; for(int i = 0; i < SIMULATION_DURATION; ++i) { simulate(1, input_quality); int lag = D.getLag(); // if( lag != clag ) { file << D.getDebugString() << "\n"; errors++; cum_err += (lag - clag) * (lag - clag); }; } result << input_quality << ",\t" << (int)((100.f * errors / SIMULATION_DURATION)) << ",\t" << std::sqrt(cum_err / (float)SIMULATION_DURATION) << "\n"; // we consider this test failed if more than 20% of our results are incorrect if(errors / (float)SIMULATION_DURATION > 0.2) { char errormsg[1024]; sprintf(errormsg, "realisitc input and constant lag: %d %% not correct", (int)(100.f * errors / SIMULATION_DURATION) ); BOOST_ERROR(errormsg); } } } const int LAG_CHANGE_RATE = 10; // test with high quality data but changing lags // in this test, we can see how fast die algorithm can detect changing lag // in this test, the lag changes are only slight, so more than 1frame per change // does not happen BOOST_FIXTURE_TEST_CASE( changing_lag, LagDetectionSetup ) { file << "\nchanging lag - real input" << "\n"; result << "changing lag - real input - small change\n"; // test for all small constant lags int clag = rand() % 8 + 4; NetworkSimulator.changePing(clag); // start a game simulate(100); // accumulator for collecting statistical data of our run accumulator_set< int, features< tag::count, tag::max, tag::mean, tag::mean_of_variates > > acc; int lag = 0; int errors = 0; int timer = 0; int cum_timer = 0; // now we do 500 steps and check for lag for(int i = 0; i < 500; ++i) { // randomly change lags. we are friendly for now. // only change lag after system has adapted to new lag if(rand() % LAG_CHANGE_RATE == 0 && clag == lag) { int nclag = (rand() % 2) * 2 - 1 + clag; nclag = std::max(5, std::min(15, nclag)); clag = nclag; NetworkSimulator.changePing(clag); timer = 0; } simulate(); lag = D.getLag(); if( lag != clag ) { errors++; timer++; } else if(timer != 0) { result << "took " << timer << " steps to recognice lag of "<< lag << std::endl; file << lag << " " << timer << "\n" << D.getDebugString() << "\n"; // calculate cum timer cum_timer += timer; acc(std::max(0, timer - lag), covariate1 = timer); timer = 0; } } // when we take longer than 10ms to detect the lag change after the first packet with new lag arrived // we are too slow. // when we take longer than 15ms once, it is bad, too if( mean(acc) > 5 || max(acc) > 15) { char errormsg[1024]; sprintf(errormsg, "LagDetector takes too long to detect lag change of 1ms. Add: %d, Avg: %d, Max: %d", (int)mean(acc), (int)boost::accumulators::mean_of_variates(acc), max(acc)); BOOST_ERROR(errormsg); } result << "maximum reaction time: " << max(acc) << std::endl; result << "average reaction time: " << boost::accumulators::mean_of_variates(acc) << std::endl; result << "average additional reaction time: " << mean(acc) << std::endl; } // test with high quality data but changing lags // in this test, we can see how fast die algorithm can detect changing lag // in this test, the lag changes are only slight, so more than 1frame per change // does not happen BOOST_FIXTURE_TEST_CASE( changing_lag_real, LagDetectionSetup ) { file << "\nchanging lag - good input" << "\n"; result << "changing lag - good input - small change\n"; for(int input_quality = 10; input_quality <= REAL_INPUT_PATTERN_LENGTH; input_quality += INPUT_CHANGE_STEP_WIDTH ) { result << "data quality: " << input_quality << std::endl; // test for all small constant lags int clag = rand() % 8 + 4; NetworkSimulator.changePing(clag); // start a game simulate(100, input_quality); // accumulator for collecting statistical data of our run accumulator_set< int, features< tag::count, tag::max, tag::mean, tag::mean_of_variates > > acc; int lag = 0; int errors = 0; int timer = 0; int cum_timer = 0; // now we do 500 steps and check for lag for(int i = 0; i < 500; ++i) { // randomly change lags. we are friendly for now. // only change lag after system has adapted to new lag if(rand() % LAG_CHANGE_RATE == 0 && clag == lag) { int nclag = (rand() % 2) * 2 - 1 + clag; nclag = std::max(5, std::min(15, nclag)); clag = nclag; NetworkSimulator.changePing(clag); timer = 0; } simulate(1, input_quality); lag = D.getLag(); if( lag != clag ) { errors++; timer++; } else if(timer != 0) { // don't do any reporting here, as we would get too much messags // result << "took " << timer << " steps to recognice lag of "<< lag << std::endl; // file << lag << " " << timer << "\n" << D.getDebugString() << "\n"; // calculate cum timer cum_timer += timer; acc(std::max(0, timer - lag), covariate1 = timer); timer = 0; } } // when we take longer than 10ms to detect the lag change after the first packet with new lag arrived // we are too slow. // when we take longer than 15ms once, it is bad, too if( mean(acc) > 10 || max(acc) > 25) { char errormsg[1024]; sprintf(errormsg, "LagDetector takes too long to detect lag change of 1ms. Add: %d, Avg: %d, Max: %d", (int)mean(acc), (int)boost::accumulators::mean_of_variates(acc), max(acc)); BOOST_ERROR(errormsg); } result << "maximum reaction time: " << max(acc) << std::endl; result << "average reaction time: " << boost::accumulators::mean_of_variates(acc) << std::endl; result << "average additional reaction time: " << mean(acc) << std::endl; } } BOOST_AUTO_TEST_SUITE_END() blobby-1.0rc3/test/GenericIOTest.cpp0000644000175000017500000003142212042452364020676 0ustar danielknobedanielknobe#define BOOST_TEST_MODULE FileAbstraction #include #include "FileRead.h" #include "FileWrite.h" #include "FileSystem.h" #include #include #include "GenericIO.h" #include "InputSource.h" #include "DuelMatchState.h" #include "raknet/BitStream.h" #include #include #include #include #include #include #define TEST_EXECUTION_PATH "C:\\Dokumente und Einstellungen\\Erik\\Eigene Dateien\\Blobby Volley 2\\test" //#define DISABLE_COMPILATION_TEST // helper void init_Physfs() { static bool initialised = false; if(!initialised) { std::cout << "initialising physfs to " << TEST_EXECUTION_PATH << "\n"; static FileSystem fs( TEST_EXECUTION_PATH ); fs.setWriteDir("."); initialised = true; } } void generic_io_types_test_f(boost::shared_ptr in, boost::shared_ptr out); void generic_io_types_test_generics_f(boost::shared_ptr in, boost::shared_ptr out); template void generic_io_types_test_vector_f(boost::shared_ptr in, boost::shared_ptr out); void generic_io_seek_tell_f(boost::shared_ptr in, boost::shared_ptr out); void generic_io_types_test_special_f(boost::shared_ptr in, boost::shared_ptr out); #define CHECK_EXCEPTION_SAFETY(expr, excp) try { \ BOOST_TEST_CHECKPOINT("trying " #expr); \ expr; \ BOOST_ERROR(#expr " does not cause " #excp " to be thrown");\ } \ catch(excp& e) {\ \ } catch (std::exception& exp) \ { \ BOOST_ERROR(std::string("unexpected exception ") + exp.what() + "instead of " #excp " caught from "#expr); \ }; // Tests of common FileSystem functions // test init BOOST_AUTO_TEST_SUITE( GenericIOTest ) BOOST_AUTO_TEST_CASE( generic_io_create ) { init_Physfs(); boost::shared_ptr write = boost::make_shared("test.tmp"); boost::shared_ptr read = boost::make_shared("test.tmp"); boost::shared_ptr stream = boost::make_shared(); // no checks, just let this pass without exceptions createGenericReader( read ); createGenericWriter( write ); createGenericReader( stream ); createGenericWriter( stream ); } BOOST_AUTO_TEST_CASE( generic_io_types_test_file ) { boost::shared_ptr write = boost::make_shared("test.tmp"); boost::shared_ptr read = boost::make_shared("test.tmp"); boost::shared_ptr inf = createGenericReader( read ); boost::shared_ptr outf = createGenericWriter( write ); generic_io_types_test_f( inf, outf); }; BOOST_AUTO_TEST_CASE( generic_io_types_test_stream ) { boost::shared_ptr stream = boost::make_shared(); boost::shared_ptr ins = createGenericReader( stream ); boost::shared_ptr outs = createGenericWriter( stream ); generic_io_types_test_f( ins, outs); }; BOOST_AUTO_TEST_CASE( generic_io_generic_types_file ) { boost::shared_ptr write = boost::make_shared("test.tmp"); boost::shared_ptr read = boost::make_shared("test.tmp"); boost::shared_ptr inf = createGenericReader( read ); boost::shared_ptr outf = createGenericWriter( write ); generic_io_types_test_generics_f( inf, outf); }; BOOST_AUTO_TEST_CASE( generic_io_generic_types_stream ) { boost::shared_ptr stream = boost::make_shared(); boost::shared_ptr ins = createGenericReader( stream ); boost::shared_ptr outs = createGenericWriter( stream ); generic_io_types_test_generics_f( ins, outs); }; BOOST_AUTO_TEST_CASE( generic_io_seek_tell ) { boost::shared_ptr write = boost::make_shared("test.tmp"); boost::shared_ptr read = boost::make_shared("test.tmp"); boost::shared_ptr inf = createGenericReader( read ); boost::shared_ptr outf = createGenericWriter( write ); generic_io_seek_tell_f( inf, outf); boost::shared_ptr stream = boost::make_shared(); boost::shared_ptr ins = createGenericReader( stream ); boost::shared_ptr outs = createGenericWriter( stream ); generic_io_seek_tell_f( ins, outs); }; BOOST_AUTO_TEST_CASE( generic_io_generic_types_vector ) { boost::shared_ptr write = boost::make_shared("test.tmp"); boost::shared_ptr read = boost::make_shared("test.tmp"); boost::shared_ptr inf = createGenericReader( read ); boost::shared_ptr outf = createGenericWriter( write ); generic_io_types_test_vector_f >( inf, outf ); generic_io_types_test_vector_f >( inf, outf ); generic_io_types_test_vector_f >( inf, outf ); boost::shared_ptr stream = boost::make_shared(); boost::shared_ptr ins = createGenericReader( stream ); boost::shared_ptr outs = createGenericWriter( stream ); generic_io_types_test_vector_f >( ins, outs ); generic_io_types_test_vector_f >( ins, outs ); generic_io_types_test_vector_f >( ins, outs ); }; BOOST_AUTO_TEST_CASE( generic_io_special_types ) { boost::shared_ptr write = boost::make_shared("test.tmp"); boost::shared_ptr read = boost::make_shared("test.tmp"); boost::shared_ptr inf = createGenericReader( read ); boost::shared_ptr outf = createGenericWriter( write ); generic_io_types_test_special_f( inf, outf); boost::shared_ptr stream = boost::make_shared(); boost::shared_ptr ins = createGenericReader( stream ); boost::shared_ptr outs = createGenericWriter( stream ); generic_io_types_test_special_f( ins, outs); }; BOOST_AUTO_TEST_SUITE_END() void generic_io_types_test_f(boost::shared_ptr in, boost::shared_ptr out) { // writing const unsigned char byte1 = 6; const unsigned char byte2 = 222; const unsigned char byte3 = rand(); out->byte( byte1 ); out->byte( byte2 ); out->byte( byte3 ); out->boolean(true); out->boolean(false); const unsigned int int1 = 8; const unsigned int int2 = 123456; const unsigned int int3 = rand(); out->uint32( int1 ); out->uint32( int2 ); out->uint32( int3 ); const float f1 = 1.54f; const float f2 = -0.785f; const float f3 = (float)rand() / RAND_MAX * 1000; const float f4 = 1.0e10f; out->number( f1 ); out->number( f2 ); out->number( f3 ); out->number( f4 ); std::string str1 = "hello world"; std::string str2 = std::string("s w \0 in it", 11); std::string str3 = std::string(1000, 'l'); out->string( str1 ); out->string( str2 ); out->string( str3 ); out->array( str1.c_str(), str1.size() ); out->array( str2.c_str(), str2.size() ); out->array( str3.c_str(), str3.size() ); // reading unsigned char bytec; in->byte(bytec); BOOST_CHECK_EQUAL( bytec, byte1 ); in->byte(bytec); BOOST_CHECK_EQUAL( bytec, byte2 ); in->byte(bytec); BOOST_CHECK_EQUAL( bytec, byte3 ); bool boolc; in->boolean(boolc); BOOST_CHECK_EQUAL(boolc, true); in->boolean(boolc); BOOST_CHECK_EQUAL(boolc, false); unsigned int intc; in->uint32( intc ); BOOST_CHECK_EQUAL(intc, int1); in->uint32( intc ); BOOST_CHECK_EQUAL(intc, int2); in->uint32( intc ); BOOST_CHECK_EQUAL(intc, int3); float fc; in->number(fc); BOOST_CHECK_EQUAL(fc, f1); in->number(fc); BOOST_CHECK_EQUAL(fc, f2); in->number(fc); BOOST_CHECK_EQUAL(fc, f3); in->number(fc); BOOST_CHECK_EQUAL(fc, f4); std::string stringc; in->string(stringc); BOOST_CHECK_EQUAL(stringc, str1); in->string(stringc); BOOST_CHECK_EQUAL(stringc, str2); in->string(stringc); BOOST_CHECK_EQUAL(stringc, str3); boost::scoped_array ar1(new char[str1.size()]); in->array(ar1.get(), str1.size()); BOOST_CHECK( memcmp(ar1.get(), str1.data(), str1.size()) == 0); boost::scoped_array ar2(new char[str2.size()]); in->array(ar2.get(), str2.size()); BOOST_CHECK( memcmp(ar2.get(), str2.data(), str2.size()) == 0); boost::scoped_array ar3(new char[str3.size()]); in->array(ar3.get(), str3.size()); BOOST_CHECK( memcmp(ar3.get(), str3.data(), str3.size()) == 0); } void generic_io_types_test_generics_f(boost::shared_ptr in, boost::shared_ptr out) { #ifndef DISABLE_COMPILATION_TEST // writing // these are not really run time tests... // it is tested here wether this code compiles. const unsigned char byte = 6; out->generic( byte ); out->generic(true); const unsigned int intv = 8; out->generic( intv ); const float fv = 1.54f; out->generic( fv ); std::string str1 = "hello world"; out->generic( str1 ); // reading unsigned char bytec; in->generic(bytec); BOOST_CHECK_EQUAL( bytec, byte ); bool boolc; in->generic(boolc); BOOST_CHECK_EQUAL(boolc, true); unsigned int intc; in->generic( intc ); BOOST_CHECK_EQUAL(intc, intv); float fc; in->generic(fc); BOOST_CHECK_EQUAL(fc, fv); std::string stringc; in->generic(stringc); #endif } template void generic_io_types_test_vector_f(boost::shared_ptr in, boost::shared_ptr out) { // writing T basevec; for(int i=0; i < 100; ++i) { basevec.push_back(rand()); } const T vector_of_bytes(basevec); out->generic( vector_of_bytes ); // reading // make sure at least one entry of basevec is changed. *basevec.begin() = ~*basevec.begin(); in->generic< T >(basevec); typename T::const_iterator j = vector_of_bytes.begin(); for(typename T::iterator i = basevec.begin(); i != basevec.end(); ++i, ++j) { BOOST_CHECK_EQUAL( *i, *j ); } } void generic_io_seek_tell_f(boost::shared_ptr in, boost::shared_ptr out) { out->uint32(5); int pos = out->tell(); out->uint32(76); out->uint32(13); out->seek(pos); out->uint32(42); unsigned int val; in->uint32(val); pos = in->tell(); in->uint32(val); BOOST_CHECK_EQUAL(val, 42); in->uint32(val); in->seek(pos); in->uint32(val); BOOST_CHECK_EQUAL(val, 42); } void generic_io_types_test_special_f(boost::shared_ptr in, boost::shared_ptr out) { const PlayerInput pi(true, false, false); out->generic(pi); const PlayerSide ps1 = LEFT_PLAYER; const PlayerSide ps2 = RIGHT_PLAYER; const PlayerSide ps3 = NO_PLAYER; out->generic(ps1); out->generic(ps2); out->generic(ps3); const Color col(255, 127, 127); out->generic(col); DuelMatchState dlms; dlms.logicState.leftScore = 12; dlms.logicState.rightScore = 6; dlms.logicState.leftSquish = 8; dlms.logicState.rightSquish = 3; dlms.logicState.servingPlayer = RIGHT_PLAYER; dlms.worldState.blobPosition[LEFT_PLAYER] = Vector2(65, 12); dlms.worldState.blobPosition[RIGHT_PLAYER] = Vector2(465, 120); dlms.worldState.blobVelocity[LEFT_PLAYER] = Vector2(5.2f, 1.f); dlms.worldState.blobVelocity[RIGHT_PLAYER] = Vector2(-5.2f, 1.f); dlms.worldState.ballVelocity = Vector2(8.2f, 12.f); dlms.worldState.ballPosition = Vector2(122.7f, 765.f); dlms.worldState.isBallValid = true; dlms.worldState.isGameRunning = false; dlms.worldState.ballAngularVelocity = 7.43f; dlms.worldState.playerInput[LEFT_PLAYER] = pi; dlms.worldState.playerInput[RIGHT_PLAYER] = PlayerInput(false, false, true); out->generic(dlms); PlayerInput piv; in->generic(piv); /// \todo we can not use CHECK_EQUAL here because we don't have a pretty printer for PlayerInput BOOST_CHECK( pi == piv ); PlayerSide psv; in->generic(psv); BOOST_CHECK_EQUAL(ps1, psv); in->generic(psv); BOOST_CHECK_EQUAL(ps2, psv); in->generic(psv); BOOST_CHECK_EQUAL(ps3, psv); Color colv; in->generic(colv); BOOST_CHECK(col == colv); DuelMatchState dlmsv; in->generic (dlmsv); BOOST_CHECK( dlmsv == dlms ); // sub-object test for better error localisation BOOST_CHECK( dlmsv.logicState == dlms.logicState ); BOOST_CHECK( dlmsv.worldState == dlms.worldState ); } blobby-1.0rc3/test/RulesTest.cpp0000644000175000017500000000352712042452364020171 0ustar danielknobedanielknobe#define BOOST_TEST_MODULE GameLogic #include #include #include "physfs.h" #include "GameLogic.h" #include "DuelMatch.h" #include "DuelMatchState.h" #include "IGenericIO.h" #include "FileRead.h" #include BOOST_AUTO_TEST_SUITE( rules_test ) #define PASSING_TIME() for(int i=0; i < 50; ++i, GL->step()); BOOST_AUTO_TEST_CASE( katja_replay ) { BOOST_REQUIRE( PHYSFS_init("C:\\Dokumente und Einstellungen\\Erik\\Eigene Dateien\\Blobby Volley 2\\test\\bin\\debug\\") ); PHYSFS_setWriteDir("."); PHYSFS_addToSearchPath(".", 1); GameLogic GL = createGameLogic("rules.lua"); // thats the scenario from katja's replay PASSING_TIME(); GL->onBallHitsPlayer(LEFT_PLAYER); GL->onBallHitsPlayer(RIGHT_PLAYER); PASSING_TIME(); GL->onBallHitsPlayer(LEFT_PLAYER); PASSING_TIME(); GL->onBallHitsPlayer(LEFT_PLAYER); PASSING_TIME(); BOOST_CHECK_EQUAL(GL->getScore(LEFT_PLAYER), 0); BOOST_CHECK_EQUAL(GL->getScore(RIGHT_PLAYER), 0); } BOOST_AUTO_TEST_CASE( net_squish ) { //BOOST_REQUIRE( PHYSFS_init("C:\\Dokumente und Einstellungen\\Erik\\Eigene Dateien\\Blobby Volley 2\\test\\bin\\debug\\") ); //PHYSFS_setWriteDir("."); //PHYSFS_addToSearchPath(".", 1); std::fstream deb_out ("debug.txt", std::fstream::out); DuelMatch match(0, 0, false, false); DuelMatchState mstate; FileRead reader ("katjareplay_crash.state"); boost::shared_ptr gin = GenericIO::createReader(reader); mstate.deserialize(gin.get()); match.setState(mstate); for(int i=0; i < 75; ++i) { match.setPlayersInput( PlayerInput(false, true, true), PlayerInput(true, false, true) ); match.step(); deb_out << match.getState().worldState.ballPosition.x << "\t" << 600 - match.getState().worldState.ballPosition.y << "\n";; } } BOOST_AUTO_TEST_SUITE_END() blobby-1.0rc3/data/inputconfig.xml0000644000175000017500000000172712042452401020524 0ustar danielknobedanielknobe blobby-1.0rc3/data/lang_de.xml0000644000175000017500000001443312042452401017566 0ustar danielknobedanielknobe blobby-1.0rc3/data/lang_en.xml0000644000175000017500000001431412042452401017576 0ustar danielknobedanielknobe blobby-1.0rc3/data/Icon.bmp0000644000175000017500000003006612042452401017043 0ustar danielknobedanielknobeBM606(@@0 rrr{{{mmpmipjjmgmtilinkkwtwttt___YYYWWW^^Zdd`eeeeeeeeeeeeeeeeeeeeeddd___YYY]YY```___jjmfffc_j___YYY]YY\c\c_jeeeeeeeeeeeeeeeeeeeeedd`]YYWWW^^Zdddeeeddddd`^^Zmipmipohhjjmkhddd`]]]dd`ebliiiiiiblikhdjjcdhdeeeeee___WWWWWW]]]dddeeeeeeeeedddccc___urrrrrurrrrrrornqqjjmhhhhhhmipsllnnnnnnnkkjjmiiiiiikhdccc^^ZWWWYYY```eeeeeeeeeeeeeeeeeeeeeddd___sllpvswtwvyy{xuzwtvvsrrrmmpnkknnnurrrrrpvsrrrrorsllmmpnnnohheeeYYYTTT]YYccceeeeeeeeeeeeeeeeeeeeeeeeccc]]]WWWc_jnkkrrrurru{r{xuzwtvvsrornnnsllqqwxxxxxxvyyu{rurrurrurrmtmnkkjcc[__YYY___ddheeeeeeeeeeeeeeeeeeeeeeee```YYYYYY^^Zjjmrortttvvspvywtwwtwwtwttnnkkohhrorxxx}}}}}}|y{xxvyywtwwtwrornkkeee```dd`dddhhhcjjeeeeeeeeeeeeeeeeeeccc[__TTTTTTYYYdd`pvy{{{~~}}}{{{{xxvswtwrornkkror{xx|||{{{{xxzwtvvsqqwmipeeedhdhhhnkkohhiiijccjccddheeeeeeeeedd`]YYTTTPPPTTT]YY```x~~{xxvvspvyvs}}}x~~|||x~x{xxzwtwtwrorilihhhiiisllrorsllnnniliiiicjjeeeeeeddd___YYYTTTTTTTTTWWW]YY]]]~~|||||||y{{{{{{{{{{{{{xuwtwmmpjjmjjmnnnurrpvsrrrmtmnnnnnniiieeeddd```]YYYYYYYYYYY]YY]YY]YY]YY]YY~~}}}}}}{{{wtwrrrnkkmmprrrtttvvsrrrrrrrornnnnnniiifbb^^ZYYY]YY]]]```dd`dd````___]]]]YY~|ywtwurrurrvvszwtvvsqqwurrrorrornnniliccc^^ZYYY^^Zdd`dddeeeeeeeeeddddd````]]]~{{{{xx{xxvyywtwqqwrrrrrrttnslliii```]YY^^Z\c\cccQV2I0N2REYeeeddddd````}}}{{{xxxwtwpvsurrrornkkfff]]]]YY___ccchbb(!(!>Q^eeeeeeeeefbb}}}{xxu{rwtwrorilidd`]YY]YYdd`eeenad$I,+ /Leeeeeeeeeeeeiyeyl|}}}{xxurriii```]YY___ccckhdrc_)CnE 6O^eeeeeeeeeeee?Q"4$50Lav}}}xxxnkkdd`___```fffmgc1<|@F/-.Lgddeeeeeeeee=B(\L+;QgwRmPiOeOgVkcubr^qUgJ^AYF.OWb"+(91=9?MRgnptbgJP6?ZMQK"-)UB!!VU;6E4&DkD"#)?P80J]eeeeeeeeeeee=B,HST\+5&08BU\WZihO]*V/I(.q@GKJ'MT@:AA=6f5A0+"*Jdddeeeeeeeee+6'BT%QlZ+8DJr1_=943&P]IeE&N;@Y8KjEC@ 28S5 /HTdd`dddddd*4 (3Q)SVa@GQU]a;``(LUpW34'P(NVcJ#*.I`76: ?@1*#Ha[^```dd`ddd"+(3.Uj4_sTXNUse@V&.`-Q5>VRG3H2OLBQ"B))7< ?G6-0>4?MS1:''F\sfaoVlW:J7OYY`YYYhhaǃW`JQ@JLPel1;&{dwi%.1I~QZ>D9D;D7@6971 " bA`B^z`*(  $[__[__iliAH&ĉg1YeY /RiVT.1PZRe;Z^\66  %jcccc\dd`jjmľêç^j)%+4Wdmi)64Odi=<"67kkDbU]9>'$~VYdddhhhqqw_oVrYtd~mnu֢݇зugg~e~[nSkNePiWqQjObQkeʊySjLeSoiv+>*(4(~f1]sd$/Vnvv.- %'xkD`Lb:=*3xa^nqq{xu&/!/(58@3@4>6B0<3CDZCX)6*:2?$/ /4=HT4<#1-6>2>&9?TF_.;#/7?,8$0&3->;}x_^19iptt/99N', $sFaPa68/:|ps )5֌ކݕጠܫ唅;C!0",ϖݢ086:|{_~ݑihULhx؉ي6> +)jRSjЀsk(7%$,y:aŮҍt=d~k$0Ysmr$26$!"~nCbQZ+1FF +5ѝ=fƙѧ}\~sr*1ZzY[筁9b䭎jmysXu(W5`ڑik(2TTc1^ׇu%1bw3:.+{yQmʬz$!3BLbD^Mfai1<{AI"-+$.dfcʞ0<闗xuRx秔)2楒Hq矌FlVv劂ݩAlܩyBhbwtȏCiȻԧBhڏ~`x+Zy4=DZȦ]_**7MgڐVxBhΏedSvᴐ%6ݬCkwg:IΠeg25"#AF[pH[4?ALkl蚋BK"/#-`a]~1Bȣ07(1wqX|vv{3@$0yGoݳjgs/;.9⮒^U$229zm)609̤vuPQ:=7533/9ZZѪKtz(3\\[#3)30:rRU&24\k䭰WaJR@H3>n|ǿ*p Ǔ맫읠~䪯}ɿȿǿǿblobby-1.0rc3/data/server.xml0000644000175000017500000000052612042452401017501 0ustar danielknobedanielknobe blobby-1.0rc3/data/lang_fr.xml0000644000175000017500000001453312042452401017606 0ustar danielknobedanielknobe blobby-1.0rc3/data/rules.lua0000644000175000017500000000403212042452401017302 0ustar danielknobedanielknobe-- most simple ruleset: for each mistake, the opponent gets a point -- includes comments for documentation purposes -- rules.lua doc -- function OnMistake -- IMPLEMENTED BY RULES.lua -- called when a player makes a mistake -- when this function is called, servinglayer() returns which player has -- served (so it is not neccesarily the enemy of the player who made the mistake) -- param: player - player who made the mistake -- return: none -- when a player makes a mistake, the other one gets a point if he was the serving player function OnMistake(player) -- function opponent -- PREDEFINED -- param: player - player of whom you want to get the opponent -- return: opponent of the player, so, for LEFT_PLAYER, RIGHT_PLAYER is returned and vice-versa -- function servingplayer -- PREDEFINED -- param: none -- return: which player has served if( opponent(player) == servingplayer() ) then -- function score -- PREDEFINED -- param: player - player who gets a point -- return: none score(opponent(player)) end end -- function IsWinning -- IMPLEMENTED BY rules.lua -- called when it is determined whether a player has won -- params: lscore: score of left player -- rscore: score of right player -- return: whether a player has won function IsWinning(lscore, rscore) -- constant SCORE_TO_WIN: number of points for a player to win if lscore >= SCORE_TO_WIN and lscore >= rscore + 2 then return true end if rscore >= SCORE_TO_WIN and rscore >= lscore + 2 then return true end return false end -- function OnBallHitsPlayer -- IMPLEMENTEDBY rules.lua -- called when a valid collision between a player and the ball happens. -- params: player: The player that hit the ball -- touches: Number of touches the player has made -- return: true, if this collision was allowed, false if it was a mistake. If false is returned, -- the ball is resetted and OnMistake is called. function OnBallHitsPlayer(player, touches) return touches <= 3 end blobby-1.0rc3/data/lang_it.xml0000644000175000017500000001463312042452401017614 0ustar danielknobedanielknobe blobby-1.0rc3/data/CMakeLists.txt0000644000175000017500000000210712042452401020206 0ustar danielknobedanielknobemacro(add_zip_archive _dir _filetype) file(GLOB_RECURSE archive_src RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${_dir}/*.${_filetype}) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_dir}.zip COMMAND zip ${CMAKE_CURRENT_BINARY_DIR}/${_dir}.zip ${archive_src} DEPENDS ${archive_src} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} VERBATIM ) add_custom_target(${_dir}_zip ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_dir}.zip) # install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_dir].zip DESTINATION /usr) endmacro(add_zip_archive _dir _filetype) add_zip_archive(gfx bmp) add_zip_archive(sounds wav) add_zip_archive(scripts lua) add_zip_archive(backgrounds bmp) set(install_files ${CMAKE_CURRENT_BINARY_DIR}/gfx.zip ${CMAKE_CURRENT_BINARY_DIR}/sounds.zip ${CMAKE_CURRENT_BINARY_DIR}/scripts.zip ${CMAKE_CURRENT_BINARY_DIR}/backgrounds.zip config.xml inputconfig.xml server.xml lang_de.xml lang_en.xml lang_fr.xml rules.lua) if (WIN32) install(FILES ${install_files} DESTINATION data) elseif (UNIX) install(FILES ${install_files} DESTINATION share/blobby) endif (WIN32) blobby-1.0rc3/data/config.xml0000644000175000017500000000256412042457302017451 0ustar danielknobedanielknobe blobby-1.0rc3/AUTHORS0000644000175000017500000000072112042452402015606 0ustar danielknobedanielknobe------------ Programming: ------------ Jonathan Sieber (Mail: jonathan_sieber@yahoo.de) Daniel Knobe (Mail: daniel-knobe@web.de) Sven Rech (Mail: svenrech@gmx.de) Erik Schultheis (Mail: erik-schultheis@freenet.de) --------- Graphics: --------- Silvio Mummert (Mail: mummertathome@t-online.de) New beach image: Richard Bertrand (Mail: ricbertrand@hotmail.com) --------------- Special Thanks: --------------- Daniel Skoraszewsky (Mail: skoraszewsky@t-online.de) blobby-1.0rc3/ChangeLog0000644000175000017500000000701412042452402016312 0ustar danielknobedanielknobeNew in 1.0 RC 3 (rev. 1123) since RC 2: - Fixed ball rotation in onlinegame - Fixed switched player bug - Fixed missing colon in server info - Fixed broken play again button in replay player New in 1.0 RC 2 (rev. 1111) since RC 1: - Show waiting player in network game menu (currently not updated) - Load server list from blobby.sourceforge.net - Option to specify maximum clients in server.xml - Added replay info screen - Added italian translation - Optimized sending replays over network - New replay default names - Possibility to select server port to connect to (Thanks to Stefano Ansaloni) - Fix: CPU usage in network game menu - Fix: trying to save replays with invalid filename now issues an error message - Fix: writing over existing replays no longer possible - Fix: Ball speed - Fix: typos in translation - Fix: "two hit bug" (again ;) - Fix: server crash when server could not find server.xml - Fix: server did not delete temp replay files - Fix: file list properly freed in replay menu - some code refractoring - REPLAY PLAYER: - more information saved in replay files: final score - better jumping to replay positions - show replay info in replay menu New in 1.0 RC 1 (rev. 732) since Beta 9: * Features: - REPLAY PLAYER: still in development, features for now: - pausing - fast forwarding - jumping to certain positions in replay (may take some time new replay save file format, not fixed yet, so replays may become incompatible in the future - enabled writing in chat without clicking into editbox - Blobby speed more stable on slow systems (framedrop) - auto disconnect when game is closed - use native keyboard layout when possible - small physics improvements - mouse input for online games - join server with double click - improved chat UI - small gfx enhancements * Bugfixes: - Fix: severe network problem - Fix: "two hit bug" - Fix: wrong winning condition in default rules - Fix: online replays sometimes not saved - squished ball generates only one sound now - corrected version in server startup message - [Windows] fixed server output messages - [Windows] pointer no longer freezes when alt+tab * Performance: - fixed a major memory leak in dedicated server - removed possible lag due to rounding issues in speed controller - removed needless wait in network server (only 1 ms) - improved OpenGL performance significantly (10-30%) - fixed some small and unlikely memleaks - fixed performance killer in "... has won" screen New in Beta 9 (rev. 622) since Alpha 8: - network game: reduced data transfer - fixed memleaks - OGL: improved text rendering speed - SDL: improved render speed for morphing blobs - generally improved performance - limited FPS in menu - chat - clock - blood in SDL mode - bots sorted - online replays - improved bots: difficulty adjustable, better api, better documentation - customizable rules file New in Alpha 8 (rev. 469) since Alpha 6: - some graphics improvements - performance improvements - names - credits added New in Alpha 6 (rev. 335) since Alpha 5: - More options - Network support - Fixed Speedcontroller - Fixed ball reset Since Alpha 4: - Many options configurable through new GUI - Configurable game speed, with frame dropping for slow PCs - Sound volume option - Keyboard, mouse, Joystick and Gamepad can be used for input now - Further optimized physics by Fliegus123 (old replays won't work) - Many bugfixes for the scripting API, bots which rely on estimate/estimx/estimy now work much better - Special cooperation with the OpenAnno team: Sven Rech will work for Blobby until final release blobby-1.0rc3/CMakeLists.txt0000644000175000017500000000042412042452402017276 0ustar danielknobedanielknobecmake_minimum_required(VERSION 2.6) include(CPack) project(Blobby) # process the config.h configure_file(${Blobby_SOURCE_DIR}/config.h.in ${Blobby_BINARY_DIR}/config.h) include_directories(${Blobby_BINARY_DIR}) add_subdirectory(data) add_subdirectory(src) blobby-1.0rc3/config.h.in0000644000175000017500000000173212042452402016564 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #define BLOBBY_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" blobby-1.0rc3/COPYING0000644000175000017500000004312212042452402015573 0ustar danielknobedanielknobe GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. blobby-1.0rc3/doxyfile0000644000175000017500000021067312042452402016315 0ustar danielknobedanielknobe# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Blobby Volley 2" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = "1.0 RC2" # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ./src/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.f90 \ *.f \ *.vhd \ *.vhdl # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = src/lua \ src/raknet \ src/tinyxml # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = YES # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES blobby-1.0rc3/INSTALL0000644000175000017500000000035412042452402015571 0ustar danielknobedanielknobeYou must have CMake installed. Then execute: cmake . make Then you can just execute blobby. Just type this in the package directory: src/blobby If you want install blobby, type this as root (NOTE: This is experimental): make install blobby-1.0rc3/NEWS0000644000175000017500000000000012042452402015223 0ustar danielknobedanielknobeblobby-1.0rc3/README0000644000175000017500000000173012042452402015417 0ustar danielknobedanielknobe============================================================== ====================Blobby Volley 2 README==================== =============The head-to-head multiplayer ball game=========== ============================================================== =================http://blobby.sourceforge.net================ ============================================================== System requirements: Either Windows 95 or later, or Linux Graphics card with DirectDraw-Acceleration (on Windows) OpenGL-compliant graphics card recommended (both systems) You can hire/buy banner ads of the new background (strand1.bmp). Contact us per EMail: Daniel-Knobe@web.de The "Dedicaded Server" runs with a Gamespeed of 100%, which means 75 FPS The Port for the Server is 1234. Source code: On Linux, with Subversion installed just type: svn co https://svn.sourceforge.net/svnroot/blobby blobby For further instructions see http://sourceforge.net/svn/?group_id=169341 Credits: See AUTHORS blobby-1.0rc3/TODO0000644000175000017500000000160212042452402015225 0ustar danielknobedanielknobeTODO: coderework: - new clean rendererarchitecture - multithreading fps independent physics - new gui system - more lua scripting features: - option for 3 types of shadow (off, pixelshadow, alphablendingshadow) - lobbysystem - full unicode support (e.g. for Russian characters) - improve replays (fast forward, rewind, etc) - improve bot system (more commands etc) - more dedicated server options (black/whitelist, statistics, etc) bugs: - mouse-input does not work very good in online-game - net bugs: - squeezing ball through blocking blobby ? Please try to verify this - flat ball (replay Mr. Enderson) - don't close chat if one player leaves the game data: - new smaller font (current one too small?) - monochrome font - more translation REQUIREMENTS FOR 1.0 RELEASE: fully functional online replays with versioning support and possibility to extend capabilities in the future blobby-1.0rc3/src/blobnet/adt/0000755000175000017500000000000012042455103017523 5ustar danielknobedanielknobeblobby-1.0rc3/src/blobnet/layer/0000755000175000017500000000000012042455107020073 5ustar danielknobedanielknobeblobby-1.0rc3/src/blobnet/exception/0000755000175000017500000000000012042455105020753 5ustar danielknobedanielknobeblobby-1.0rc3/data/gfx/font_small/0000755000175000017500000000000012042455051020374 5ustar danielknobedanielknobeblobby-1.0rc3/data/scripts/old/0000755000175000017500000000000012042455062017721 5ustar danielknobedanielknobeblobby-1.0rc3/data/gf2x/font_small/0000755000175000017500000000000012042455040020454 5ustar danielknobedanielknobeblobby-1.0rc3/doc/codeconvention/0000755000175000017500000000000012042455074020330 5ustar danielknobedanielknobeblobby-1.0rc3/src/raknet/0000755000175000017500000000000012042455114016614 5ustar danielknobedanielknobeblobby-1.0rc3/src/tinyxml/0000755000175000017500000000000012042455121017032 5ustar danielknobedanielknobeblobby-1.0rc3/src/blobnet/0000755000175000017500000000000012042455102016752 5ustar danielknobedanielknobeblobby-1.0rc3/src/server/0000755000175000017500000000000012042455115016637 5ustar danielknobedanielknobeblobby-1.0rc3/src/state/0000755000175000017500000000000012042455117016453 5ustar danielknobedanielknobeblobby-1.0rc3/src/lua/0000755000175000017500000000000012042455111016106 5ustar danielknobedanielknobeblobby-1.0rc3/data/replays/0000755000175000017500000000000012042455054017134 5ustar danielknobedanielknobeblobby-1.0rc3/data/gfx/0000755000175000017500000000000012042455043016237 5ustar danielknobedanielknobeblobby-1.0rc3/data/scripts/0000755000175000017500000000000012042455060017141 5ustar danielknobedanielknobeblobby-1.0rc3/data/backgrounds/0000755000175000017500000000000012042455033017754 5ustar danielknobedanielknobeblobby-1.0rc3/data/gf2x/0000755000175000017500000000000012042455037016324 5ustar danielknobedanielknobeblobby-1.0rc3/data/sounds/0000755000175000017500000000000012042455065016772 5ustar danielknobedanielknobeblobby-1.0rc3/doc/0000755000175000017500000000000012042455073015312 5ustar danielknobedanielknobeblobby-1.0rc3/src/0000755000175000017500000000000012042455100015323 5ustar danielknobedanielknobeblobby-1.0rc3/test/0000755000175000017500000000000012042455126015523 5ustar danielknobedanielknobeblobby-1.0rc3/data/0000755000175000017500000000000012042457302015453 5ustar danielknobedanielknobeblobby-1.0rc3/0000755000175000017500000000000012042455227014546 5ustar danielknobedanielknobe

lsizenode)) #define luaO_nilobject (&luaO_nilobject_) LUAI_DATA const TValue luaO_nilobject_; #define ceillog2(x) (luaO_log2((x)-1) + 1) LUAI_FUNC int luaO_log2 (unsigned int x); LUAI_FUNC int luaO_int2fb (unsigned int x); LUAI_FUNC int luaO_fb2int (int x); LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp); LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); #endif blobby-1.0rc3/src/lua/lstring.h0000644000175000017500000000145612042452374017757 0ustar danielknobedanielknobe/* ** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ #ifndef lstring_h #define lstring_h #include "lgc.h" #include "lobject.h" #include "lstate.h" #define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) #define sizeudata(u) (sizeof(union Udata)+(u)->len) #define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s))) #define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ (sizeof(s)/sizeof(char))-1)) #define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); #endif blobby-1.0rc3/src/lua/lapi.h0000644000175000017500000000040612042452374017214 0ustar danielknobedanielknobe/* ** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ #ifndef lapi_h #define lapi_h #include "lobject.h" LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o); #endif blobby-1.0rc3/src/lua/lcode.h0000644000175000017500000000527612042452374017367 0ustar danielknobedanielknobe/* ** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #ifndef lcode_h #define lcode_h #include "llex.h" #include "lobject.h" #include "lopcodes.h" #include "lparser.h" /* ** Marks the end of a patch list. It is an invalid value both as an absolute ** address, and as a list link (would link an element to itself). */ #define NO_JUMP (-1) /* ** grep "ORDER OPR" if you change these enums */ typedef enum BinOpr { OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, OPR_CONCAT, OPR_NE, OPR_EQ, OPR_LT, OPR_LE, OPR_GT, OPR_GE, OPR_AND, OPR_OR, OPR_NOBINOPR } BinOpr; typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; #define getcode(fs,e) ((fs)->f->code[(e)->u.s.info]) #define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) #define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_jump (FuncState *fs); LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); LUAI_FUNC int luaK_getlabel (FuncState *fs); LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v); LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2); LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); #endif blobby-1.0rc3/src/lua/luaconf.h0000644000175000017500000005343312042452374017726 0ustar danielknobedanielknobe/* ** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ #ifndef lconfig_h #define lconfig_h #include #include /* ** ================================================================== ** Search for "@@" to find all configurable definitions. ** =================================================================== */ /* @@ LUA_ANSI controls the use of non-ansi features. ** CHANGE it (define it) if you want Lua to avoid the use of any ** non-ansi feature or library. */ #if defined(__STRICT_ANSI__) #define LUA_ANSI #endif #if !defined(LUA_ANSI) && defined(_WIN32) #define LUA_WIN #endif #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #define LUA_USE_READLINE /* needs some extra libraries */ #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX #define LUA_DL_DYLD /* does not need extra library */ #endif /* @@ LUA_USE_POSIX includes all functionallity listed as X/Open System @* Interfaces Extension (XSI). ** CHANGE it (define it) if your system is XSI compatible. */ #if defined(LUA_USE_POSIX) #define LUA_USE_MKSTEMP #define LUA_USE_ISATTY #define LUA_USE_POPEN #define LUA_USE_ULONGJMP #endif /* @@ LUA_PATH and LUA_CPATH are the names of the environment variables that @* Lua check to set its paths. @@ LUA_INIT is the name of the environment variable that Lua @* checks for initialization code. ** CHANGE them if you want different names. */ #define LUA_PATH "LUA_PATH" #define LUA_CPATH "LUA_CPATH" #define LUA_INIT "LUA_INIT" /* @@ LUA_PATH_DEFAULT is the default path that Lua uses to look for @* Lua libraries. @@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for @* C libraries. ** CHANGE them if your machine has a non-conventional directory ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ #if defined(_WIN32) /* ** In Windows, any exclamation mark ('!') in the path is replaced by the ** path of the directory of the executable file of the current process. */ #define LUA_LDIR "!\\lua\\" #define LUA_CDIR "!\\" #define LUA_PATH_DEFAULT \ ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" #define LUA_CPATH_DEFAULT \ ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" #else #define LUA_ROOT "/usr/local/" #define LUA_LDIR LUA_ROOT "share/lua/5.1/" #define LUA_CDIR LUA_ROOT "lib/lua/5.1/" #define LUA_PATH_DEFAULT \ "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" #define LUA_CPATH_DEFAULT \ "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" #endif /* @@ LUA_DIRSEP is the directory separator (for submodules). ** CHANGE it if your machine does not use "/" as the directory separator ** and is not Windows. (On Windows Lua automatically uses "\".) */ #if defined(_WIN32) #define LUA_DIRSEP "\\" #else #define LUA_DIRSEP "/" #endif /* @@ LUA_PATHSEP is the character that separates templates in a path. @@ LUA_PATH_MARK is the string that marks the substitution points in a @* template. @@ LUA_EXECDIR in a Windows path is replaced by the executable's @* directory. @@ LUA_IGMARK is a mark to ignore all before it when bulding the @* luaopen_ function name. ** CHANGE them if for some reason your system cannot use those ** characters. (E.g., if one of those characters is a common character ** in file/directory names.) Probably you do not need to change them. */ #define LUA_PATHSEP ";" #define LUA_PATH_MARK "?" #define LUA_EXECDIR "!" #define LUA_IGMARK "-" /* @@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. ** CHANGE that if ptrdiff_t is not adequate on your machine. (On most ** machines, ptrdiff_t gives a good choice between int or long.) */ #define LUA_INTEGER ptrdiff_t /* @@ LUA_API is a mark for all core API functions. @@ LUALIB_API is a mark for all standard library functions. ** CHANGE them if you need to define those functions in some special way. ** For instance, if you want to create one Windows DLL with the core and ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ #if defined(LUA_BUILD_AS_DLL) #if defined(LUA_CORE) || defined(LUA_LIB) #define LUA_API __declspec(dllexport) #else #define LUA_API __declspec(dllimport) #endif #else #define LUA_API extern #endif /* more often than not the libs go together with the core */ #define LUALIB_API LUA_API /* @@ LUAI_FUNC is a mark for all extern functions that are not to be @* exported to outside modules. @@ LUAI_DATA is a mark for all extern (const) variables that are not to @* be exported to outside modules. ** CHANGE them if you need to mark them in some special way. Elf/gcc ** (versions 3.2 and later) mark them as "hidden" to optimize access ** when Lua is compiled as a shared library. */ #if defined(luaall_c) #define LUAI_FUNC static #define LUAI_DATA /* empty */ #elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ defined(__ELF__) #define LUAI_FUNC __attribute__((visibility("hidden"))) extern #define LUAI_DATA LUAI_FUNC #else #define LUAI_FUNC extern #define LUAI_DATA extern #endif /* @@ LUA_QL describes how error messages quote program elements. ** CHANGE it if you want a different appearance. */ #define LUA_QL(x) "'" x "'" #define LUA_QS LUA_QL("%s") /* @@ LUA_IDSIZE gives the maximum size for the description of the source @* of a function in debug information. ** CHANGE it if you want a different size. */ #define LUA_IDSIZE 60 /* ** {================================================================== ** Stand-alone configuration ** =================================================================== */ #if defined(lua_c) || defined(luaall_c) /* @@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that @* is, whether we're running lua interactively). ** CHANGE it if you have a better definition for non-POSIX/non-Windows ** systems. */ #if defined(LUA_USE_ISATTY) #include #define lua_stdin_is_tty() isatty(0) #elif defined(LUA_WIN) #include #include #define lua_stdin_is_tty() _isatty(_fileno(stdin)) #else #define lua_stdin_is_tty() 1 /* assume stdin is a tty */ #endif /* @@ LUA_PROMPT is the default prompt used by stand-alone Lua. @@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. ** CHANGE them if you want different prompts. (You can also change the ** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) */ #define LUA_PROMPT "> " #define LUA_PROMPT2 ">> " /* @@ LUA_PROGNAME is the default name for the stand-alone Lua program. ** CHANGE it if your stand-alone interpreter has a different name and ** your system is not able to detect that name automatically. */ #define LUA_PROGNAME "lua" /* @@ LUA_MAXINPUT is the maximum length for an input line in the @* stand-alone interpreter. ** CHANGE it if you need longer lines. */ #define LUA_MAXINPUT 512 /* @@ lua_readline defines how to show a prompt and then read a line from @* the standard input. @@ lua_saveline defines how to "save" a read line in a "history". @@ lua_freeline defines how to free a line read by lua_readline. ** CHANGE them if you want to improve this functionality (e.g., by using ** GNU readline and history facilities). */ #if defined(LUA_USE_READLINE) #include #include #include #define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) #define lua_saveline(L,idx) \ if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ add_history(lua_tostring(L, idx)); /* add it to history */ #define lua_freeline(L,b) ((void)L, free(b)) #else #define lua_readline(L,b,p) \ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ #define lua_saveline(L,idx) { (void)L; (void)idx; } #define lua_freeline(L,b) { (void)L; (void)b; } #endif #endif /* }================================================================== */ /* @@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles @* as a percentage. ** CHANGE it if you want the GC to run faster or slower (higher values ** mean larger pauses which mean slower collection.) You can also change ** this value dynamically. */ #define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ /* @@ LUAI_GCMUL defines the default speed of garbage collection relative to @* memory allocation as a percentage. ** CHANGE it if you want to change the granularity of the garbage ** collection. (Higher values mean coarser collections. 0 represents ** infinity, where each step performs a full collection.) You can also ** change this value dynamically. */ #define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ /* @@ LUA_COMPAT_GETN controls compatibility with old getn behavior. ** CHANGE it (define it) if you want exact compatibility with the ** behavior of setn/getn in Lua 5.0. */ #undef LUA_COMPAT_GETN /* @@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. ** CHANGE it to undefined as soon as you do not need a global 'loadlib' ** function (the function is still available as 'package.loadlib'). */ #undef LUA_COMPAT_LOADLIB /* @@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. ** CHANGE it to undefined as soon as your programs use only '...' to ** access vararg parameters (instead of the old 'arg' table). */ #define LUA_COMPAT_VARARG /* @@ LUA_COMPAT_MOD controls compatibility with old math.mod function. ** CHANGE it to undefined as soon as your programs use 'math.fmod' or ** the new '%' operator instead of 'math.mod'. */ #define LUA_COMPAT_MOD /* @@ LUA_COMPAT_LSTR controls compatibility with old long string nesting @* facility. ** CHANGE it to 2 if you want the old behaviour, or undefine it to turn ** off the advisory error when nesting [[...]]. */ #define LUA_COMPAT_LSTR 1 /* @@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. ** CHANGE it to undefined as soon as you rename 'string.gfind' to ** 'string.gmatch'. */ #define LUA_COMPAT_GFIND /* @@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' @* behavior. ** CHANGE it to undefined as soon as you replace to 'luaL_register' ** your uses of 'luaL_openlib' */ #define LUA_COMPAT_OPENLIB /* @@ luai_apicheck is the assert macro used by the Lua-C API. ** CHANGE luai_apicheck if you want Lua to perform some checks in the ** parameters it gets from API calls. This may slow down the interpreter ** a bit, but may be quite useful when debugging C code that interfaces ** with Lua. A useful redefinition is to use assert.h. */ #if defined(LUA_USE_APICHECK) #include #define luai_apicheck(L,o) { (void)L; assert(o); } #else #define luai_apicheck(L,o) { (void)L; } #endif /* @@ LUAI_BITSINT defines the number of bits in an int. ** CHANGE here if Lua cannot automatically detect the number of bits of ** your machine. Probably you do not need to change this. */ /* avoid overflows in comparison */ #if INT_MAX-20 < 32760 #define LUAI_BITSINT 16 #elif INT_MAX > 2147483640L /* int has at least 32 bits */ #define LUAI_BITSINT 32 #else #error "you must define LUA_BITSINT with number of bits in an integer" #endif /* @@ LUAI_UINT32 is an unsigned integer with at least 32 bits. @@ LUAI_INT32 is an signed integer with at least 32 bits. @@ LUAI_UMEM is an unsigned integer big enough to count the total @* memory used by Lua. @@ LUAI_MEM is a signed integer big enough to count the total memory @* used by Lua. ** CHANGE here if for some weird reason the default definitions are not ** good enough for your machine. (The definitions in the 'else' ** part always works, but may waste space on machines with 64-bit ** longs.) Probably you do not need to change this. */ #if LUAI_BITSINT >= 32 #define LUAI_UINT32 unsigned int #define LUAI_INT32 int #define LUAI_MAXINT32 INT_MAX #define LUAI_UMEM size_t #define LUAI_MEM ptrdiff_t #else /* 16-bit ints */ #define LUAI_UINT32 unsigned long #define LUAI_INT32 long #define LUAI_MAXINT32 LONG_MAX #define LUAI_UMEM unsigned long #define LUAI_MEM long #endif /* @@ LUAI_MAXCALLS limits the number of nested calls. ** CHANGE it if you need really deep recursive calls. This limit is ** arbitrary; its only purpose is to stop infinite recursion before ** exhausting memory. */ #define LUAI_MAXCALLS 20000 /* @@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function @* can use. ** CHANGE it if you need lots of (Lua) stack space for your C ** functions. This limit is arbitrary; its only purpose is to stop C ** functions to consume unlimited stack space. (must be smaller than ** -LUA_REGISTRYINDEX) */ #define LUAI_MAXCSTACK 8000 /* ** {================================================================== ** CHANGE (to smaller values) the following definitions if your system ** has a small C stack. (Or you may want to change them to larger ** values if your system has a large C stack and these limits are ** too rigid for you.) Some of these constants control the size of ** stack-allocated arrays used by the compiler or the interpreter, while ** others limit the maximum number of recursive calls that the compiler ** or the interpreter can perform. Values too large may cause a C stack ** overflow for some forms of deep constructs. ** =================================================================== */ /* @@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and @* syntactical nested non-terminals in a program. */ #define LUAI_MAXCCALLS 200 /* @@ LUAI_MAXVARS is the maximum number of local variables per function @* (must be smaller than 250). */ #define LUAI_MAXVARS 200 /* @@ LUAI_MAXUPVALUES is the maximum number of upvalues per function @* (must be smaller than 250). */ #define LUAI_MAXUPVALUES 60 /* @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. */ #define LUAL_BUFFERSIZE BUFSIZ /* }================================================================== */ /* ** {================================================================== @@ LUA_NUMBER is the type of numbers in Lua. ** CHANGE the following definitions only if you want to build Lua ** with a number type different from double. You may also need to ** change lua_number2int & lua_number2integer. ** =================================================================== */ #define LUA_NUMBER_DOUBLE #define LUA_NUMBER double /* @@ LUAI_UACNUMBER is the result of an 'usual argument conversion' @* over a number. */ #define LUAI_UACNUMBER double /* @@ LUA_NUMBER_SCAN is the format for reading numbers. @@ LUA_NUMBER_FMT is the format for writing numbers. @@ lua_number2str converts a number to a string. @@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. @@ lua_str2number converts a string to a number. */ #define LUA_NUMBER_SCAN "%lf" #define LUA_NUMBER_FMT "%.14g" #define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) #define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ #define lua_str2number(s,p) strtod((s), (p)) /* @@ The luai_num* macros define the primitive operations over numbers. */ #if defined(LUA_CORE) #include #define luai_numadd(a,b) ((a)+(b)) #define luai_numsub(a,b) ((a)-(b)) #define luai_nummul(a,b) ((a)*(b)) #define luai_numdiv(a,b) ((a)/(b)) #define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) #define luai_numpow(a,b) (pow(a,b)) #define luai_numunm(a) (-(a)) #define luai_numeq(a,b) ((a)==(b)) #define luai_numlt(a,b) ((a)<(b)) #define luai_numle(a,b) ((a)<=(b)) #define luai_numisnan(a) (!luai_numeq((a), (a))) #endif /* @@ lua_number2int is a macro to convert lua_Number to int. @@ lua_number2integer is a macro to convert lua_Number to lua_Integer. ** CHANGE them if you know a faster way to convert a lua_Number to ** int (with any rounding method and without throwing errors) in your ** system. In Pentium machines, a naive typecast from double to int ** in C is extremely slow, so any alternative is worth trying. */ /* On a Pentium, resort to a trick */ #if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ (defined(__i386) || defined (_M_IX86) || defined(__i386__)) /* On a Microsoft compiler, use assembler */ #if defined(_MSC_VER) #define lua_number2int(i,d) __asm fld d __asm fistp i #define lua_number2integer(i,n) lua_number2int(i, n) /* the next trick should work on any Pentium, but sometimes clashes with a DirectX idiosyncrasy */ #else union luai_Cast { double l_d; long l_l; }; #define lua_number2int(i,d) \ { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } #define lua_number2integer(i,n) lua_number2int(i, n) #endif /* this option always works, but may be slow */ #else #define lua_number2int(i,d) ((i)=(int)(d)) #define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) #endif /* }================================================================== */ /* @@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. ** CHANGE it if your system requires alignments larger than double. (For ** instance, if your system supports long doubles and they must be ** aligned in 16-byte boundaries, then you should add long double in the ** union.) Probably you do not need to change this. */ #define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } /* @@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. ** CHANGE them if you prefer to use longjmp/setjmp even with C++ ** or if want/don't to use _longjmp/_setjmp instead of regular ** longjmp/setjmp. By default, Lua handles errors with exceptions when ** compiling as C++ code, with _longjmp/_setjmp when asked to use them, ** and with longjmp/setjmp otherwise. */ #if defined(__cplusplus) /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) #define LUAI_TRY(L,c,a) try { a } catch(...) \ { if ((c)->status == 0) (c)->status = -1; } #define luai_jmpbuf int /* dummy variable */ #elif defined(LUA_USE_ULONGJMP) /* in Unix, try _longjmp/_setjmp (more efficient) */ #define LUAI_THROW(L,c) _longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #else /* default handling with long jumps */ #define LUAI_THROW(L,c) longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #endif /* @@ LUA_MAXCAPTURES is the maximum number of captures that a pattern @* can do during pattern-matching. ** CHANGE it if you need more captures. This limit is arbitrary. */ #define LUA_MAXCAPTURES 32 /* @@ lua_tmpnam is the function that the OS library uses to create a @* temporary name. @@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. ** CHANGE them if you have an alternative to tmpnam (which is considered ** insecure) or if you want the original tmpnam anyway. By default, Lua ** uses tmpnam except when POSIX is available, where it uses mkstemp. */ #if defined(loslib_c) || defined(luaall_c) #if defined(LUA_USE_MKSTEMP) #include #define LUA_TMPNAMBUFSIZE 32 #define lua_tmpnam(b,e) { \ strcpy(b, "/tmp/lua_XXXXXX"); \ e = mkstemp(b); \ if (e != -1) close(e); \ e = (e == -1); } #else #define LUA_TMPNAMBUFSIZE L_tmpnam #define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } #endif #endif /* @@ lua_popen spawns a new process connected to the current one through @* the file streams. ** CHANGE it if you have a way to implement it in your system. */ #if defined(LUA_USE_POPEN) #define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) #define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) #elif defined(LUA_WIN) #define lua_popen(L,c,m) ((void)L, _popen(c,m)) #define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) #else #define lua_popen(L,c,m) ((void)((void)c, m), \ luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) #define lua_pclose(L,file) ((void)((void)L, file), 0) #endif /* @@ LUA_DL_* define which dynamic-library system Lua should use. ** CHANGE here if Lua has problems choosing the appropriate ** dynamic-library system for your platform (either Windows' DLL, Mac's ** dyld, or Unix's dlopen). If your system is some kind of Unix, there ** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for ** it. To use dlopen you also need to adapt the src/Makefile (probably ** adding -ldl to the linker options), so Lua does not select it ** automatically. (When you change the makefile to add -ldl, you must ** also add -DLUA_USE_DLOPEN.) ** If you do not want any kind of dynamic library, undefine all these ** options. ** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. */ #if defined(LUA_USE_DLOPEN) #define LUA_DL_DLOPEN #endif #if defined(LUA_WIN) #define LUA_DL_DLL #endif /* @@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State @* (the data goes just *before* the lua_State pointer). ** CHANGE (define) this if you really need that. This value must be ** a multiple of the maximum alignment required for your machine. */ #define LUAI_EXTRASPACE 0 /* @@ luai_userstate* allow user-specific actions on threads. ** CHANGE them if you defined LUAI_EXTRASPACE and need to do something ** extra when a thread is created/deleted/resumed/yielded. */ #define luai_userstateopen(L) ((void)L) #define luai_userstateclose(L) ((void)L) #define luai_userstatethread(L,L1) ((void)L) #define luai_userstatefree(L) ((void)L) #define luai_userstateresume(L,n) ((void)L) #define luai_userstateyield(L,n) ((void)L) /* @@ LUA_INTFRMLEN is the length modifier for integer conversions @* in 'string.format'. @@ LUA_INTFRM_T is the integer type correspoding to the previous length @* modifier. ** CHANGE them if your system supports long long or does not support long. */ #if defined(LUA_USELONGLONG) #define LUA_INTFRMLEN "ll" #define LUA_INTFRM_T long long #else #define LUA_INTFRMLEN "l" #define LUA_INTFRM_T long #endif /* =================================================================== */ /* ** Local configuration. You can use this space to add your redefinitions ** without modifying the main part of the file. */ #endif blobby-1.0rc3/src/lua/print.c0000644000175000017500000001152012042452374017415 0ustar danielknobedanielknobe/* ** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $ ** print bytecodes ** See Copyright Notice in lua.h */ #include #include #define luac_c #define LUA_CORE #include "ldebug.h" #include "lobject.h" #include "lopcodes.h" #include "lundump.h" #define PrintFunction luaU_print #define Sizeof(x) ((int)sizeof(x)) #define VOID(p) ((const void*)(p)) static void PrintString(const TString* ts) { const char* s=getstr(ts); size_t i,n=ts->tsv.len; putchar('"'); for (i=0; ik[i]; switch (ttype(o)) { case LUA_TNIL: printf("nil"); break; case LUA_TBOOLEAN: printf(bvalue(o) ? "true" : "false"); break; case LUA_TNUMBER: printf(LUA_NUMBER_FMT,nvalue(o)); break; case LUA_TSTRING: PrintString(rawtsvalue(o)); break; default: /* cannot happen */ printf("? type=%d",ttype(o)); break; } } static void PrintCode(const Proto* f) { const Instruction* code=f->code; int pc,n=f->sizecode; for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); printf("%-9s\t",luaP_opnames[o]); switch (getOpMode(o)) { case iABC: printf("%d",a); if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); break; case iABx: if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); break; case iAsBx: if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); break; } switch (o) { case OP_LOADK: printf("\t; "); PrintConstant(f,bx); break; case OP_GETUPVAL: case OP_SETUPVAL: printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); break; case OP_GETGLOBAL: case OP_SETGLOBAL: printf("\t; %s",svalue(&f->k[bx])); break; case OP_GETTABLE: case OP_SELF: if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } break; case OP_SETTABLE: case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_POW: case OP_EQ: case OP_LT: case OP_LE: if (ISK(b) || ISK(c)) { printf("\t; "); if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); printf(" "); if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); } break; case OP_JMP: case OP_FORLOOP: case OP_FORPREP: printf("\t; to %d",sbx+pc+2); break; case OP_CLOSURE: printf("\t; %p",VOID(f->p[bx])); break; case OP_SETLIST: if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c); break; default: break; } printf("\n"); } } #define SS(x) (x==1)?"":"s" #define S(x) x,SS(x) static void PrintHeader(const Proto* f) { const char* s=getstr(f->source); if (*s=='@' || *s=='=') s++; else if (*s==LUA_SIGNATURE[0]) s="(bstring)"; else s="(string)"; printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", (f->linedefined==0)?"main":"function",s, f->linedefined,f->lastlinedefined, S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); printf("%d%s param%s, %d slot%s, %d upvalue%s, ", f->numparams,f->is_vararg?"+":"",SS(f->numparams), S(f->maxstacksize),S(f->nups)); printf("%d local%s, %d constant%s, %d function%s\n", S(f->sizelocvars),S(f->sizek),S(f->sizep)); } static void PrintConstants(const Proto* f) { int i,n=f->sizek; printf("constants (%d) for %p:\n",n,VOID(f)); for (i=0; isizelocvars; printf("locals (%d) for %p:\n",n,VOID(f)); for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); } } static void PrintUpvalues(const Proto* f) { int i,n=f->sizeupvalues; printf("upvalues (%d) for %p:\n",n,VOID(f)); if (f->upvalues==NULL) return; for (i=0; iupvalues[i])); } } void PrintFunction(const Proto* f, int full) { int i,n=f->sizep; PrintHeader(f); PrintCode(f); if (full) { PrintConstants(f); PrintLocals(f); PrintUpvalues(f); } for (i=0; ip[i],full); } blobby-1.0rc3/data/gfx/pfeil_rechts.bmp0000644000175000017500000000322612042452377021420 0ustar danielknobedanielknobeBM6(`  u00ZZd,,ZuH5LLLtyd5* 0,ttdH  00td5**0ZdV5 $&GE\Z\ZZuyH*  GEvuuyH5*  GEvuyH5 GEvu)V5* GEvu..).*d5  GEvu.).*d*  GEvu$&vu\Zvu$&GEblobby-1.0rc3/data/gfx/font10.bmp0000644000175000017500000000337012042452377020060 0ustar danielknobedanielknobeBM6( YXYSSS===@Aux\_89LKLi+l&~DF```ϯQOP55Ţ)BC/./റ`^`'&'JN?=@000ڰebf"!#w`K/0KHK%"%ׂfK$%zvzGDG)&)ycɆ6444<<<555...~ROR""hbD  ɼ״d`e)%)($(&"&1(2L?MmWn{p}K%&ёmem`Y_nbo}̜ͿfNa5dOOOzzzɡۺƛȯ~o|M}   ~z'#'ջέJ6KA-AHIHί'('˞+/+8<8ǵӴjjwvhyh_h^ɵٵΞgfSmRjzj߹୚ݙihOxM xvPN)(3'઱}HF5VnVHF%N$rqOJ3}1Ղeb31EhD}zDA [ZYV blobby-1.0rc3/data/gfx/font11.bmp0000644000175000017500000000336612042452377020066 0ustar danielknobedanielknobeBM6( %bf@ B3 3798:68<=9934.0$%%Ӹvztx%7EIMMIc:eI-J// I1Ƹ&>`zۓԌ̀͵rOO0P/0pIrjS8A~DpDr|\}{ղޣuk@mD,E&'#qqK9'()$)TKTѸײػdN3O.-sGt{P0i%l" "ǥɐhV=W,,_/y,|   Բ|e~H6G9|Q~zDׁ023mkm¼ãnco)$)҄nCP,Q uxv212dې޼ehAj !xv||͝п^=4>$1$;G;zRRR&7'N}Nlkvvyv(2(<7<Ɵgh:J9"/"' "%D#7j6QOa^rpBsB̦XW>q>8k6;w9;:@>CBFDJHMKOME{C282ةێmkSPFDNLfdnlsoni_ZQOA=}}vs_\XVQPsq֎䘛WT:7.h->[=~ifKH@=#/#bab_0-+)(WULH-)2/*.,%"!%!W  \$\-m,KE<8D?>:)$  [ > ; ^   d:r9yuvsxusoeaJE -(+%&" &DxDqqх쓘xu`\MIHCQM[WXUNJR(5N4{+\ ]PDnpTr% ]ZX^+܅)(m)O5Q}I 56kIia+ ׈h0cdbB_A'=sSsG߉(//Д|>`"^  sYroܗ8=:by3o)&2+1ڸXa+T ZJUסĨGT$B q\hߦ|FsQ$9 {ᬽGcU'4 }gj֥։[^e531 cRSٯԟvXxQ.L81'  E<9Ͳm:ky'Pk=\1W;Ti_SďsOc2JO A>6imWzxSb5blobby-1.0rc3/data/gfx/font13.bmp0000644000175000017500000000337012042452377020063 0ustar danielknobedanielknobeBM6(  l%kNM2/%#  3ӵ#]WKED=@7?44*   ^K,ҭ !y).~1ql+SH3-    r?pjlG3Ŀ79[g\Lwt;MC%)* 2"/nQЦ7/&S1G]x֚_[W>10$ wyT̲5(jX`׷bZ]/24 D+>^Ǣ8Q!BfR{)+B  qt–GV*J pmcϠ_?#Q iJ`b[SOJE)$    )!A9O;Jg,;9.|s||ql[UGB>9F?WPocjX=_,8Z5}uΜ➟喛㐘㋜Ҋsd?W5 blobby-1.0rc3/data/gfx/font50.bmp0000644000175000017500000000336612042452377020071 0ustar danielknobedanielknobeBM6( "lqlqQSFIFIFIFIFIJLZ/[Z/[P(QR3TA5224>444AźHQQTZ   }{|~~~~~""[F[aammmmmdbdNddNd?0?blobby-1.0rc3/data/gfx/ball01.bmp0000644000175000017500000001206612042452377020026 0ustar danielknobedanielknobeBM66(@@ xw{fehcbe |{}lkmdbeb_b\Z\vuvqpqifhgefpnnljjgeezyytssmlldcc[ZZyxwxwvhgfeec``^rrpggeuutbba\\[ vxu ^`^z|zoqojkjefertsehg z||eggGHHwxxopp\]]suvkmoz{}[[]ssunnokkleef}}}|||{{{xxxtttrrrnnniiihhhfffeeedddccc```___]]]YYYWWWTTTPPP222$$$ `+FE--[E-FEF%8EEr8@a**z$+8F$oy67D+*o$%-+*V67D$yV***]+&{FD?+%)y+Ew{Ep)+))7Da+%&&E--v S)Dv\)7`$$`w-ppRuݛ6*o]------Slޛ+D,F-EE-!!݂A:WS**D7D*`FFEE  444__ Rlݛ))y?$-F[Euف3333944ݛo*+-,ݛ9933_:ڂݛ)V`E--EuSl:393'!)*-88_APјӁ4ڂ+E8EE43433 4јڂlޛEEE3443ҙԚڂޛ+zEE,_4_43ԙ3ә4ڂܛ*D7+,EF-FE^^^3443ԙ3_ۛ)p`E EPP344՚4444zaFEE͗qU3444Aᛛ)+-E2h34ppSS)y]a---Eh2ʗP4Aقp܂ޛ\*aE--Η3ق!Sv%`F^ƖRڂޛ)*zzExmP4'SڂS*z?x2ihiq݂ڂ**DD?+&(2'oDV77+>mʎfffÖ3'SyDV)6*$` =OeeeeeĖɗPәӁ_\vD]{{`¾OfPP94ASy?&>EXO1OfffP^4lS)]ꍎOOfOffǗkׂ'p*%z6ᾔ0N00Ogfffh3قuo+//////0efTÀf–jPњ4 ؂py)0dd/ƖOfP3؂p)00dd/Ofeefȗ94BCdd/0gff˗P3SMdMcc~~ccddN1Xi: ~~M~~~~c0001ehUUjW:bb~/ccc}bbbbb~~c00gTh˘4LL~cb~~bLLbbbbb~~0d/0ϙ_bMLbb}bLLbbb}~// ~LJK}LLbddd01TjkbbJ}bKbccc0bLLLL~Mbbbb~/00UhbIIII|bbLLbKLLbb~d/d0TIIJcLLbbCd/0f.MbLLLL/JIcbb~~~cdIGb~ccJG.LKJM~cMNIbMHI.LbbMCNGIbcBIJKIILGLbIIIMJ.blobby-1.0rc3/data/gfx/font14.bmp0000644000175000017500000000337012042452377020064 0ustar danielknobedanielknobeBM6( )8 8v Y:2+ &*+12\S1 wc^dt  2 ?o.arQN/!.6(5(6+8,>2@6;..Ai;7.ajKlcR0 $=S1pPjsustmql\74 +HOnBnOD2#;    &)|hVK!,rY`Q 6='p[% =x^?4 e AS/_I  & X? A9~"'] &@$ v'-@G,;$ f B?<>A?:3#2'' hS CDBA'bJL5ќ)|]Q0001 n''LќҞ|}gQQQRS_C4Cљ''ƛWLKМܠݡiZC_uUVAJaIUUUUǛɛL+feܡT_3ppUUUUU'd}S3!HaUU''Ǜ)|=f, Lҝ|=.~ '™šB)<S U7šqW5]>Rhg pqqϝQ GGǂqd &&&& p]&F&& 7''ǛgU&E&&&7bƛ|FFE&&{'B+M+z&Iq΃E&&&&{H7'FF&{'@% & pa&E6% E { E&&%%&  EE& E%%%E&{ %&%%&&&%E%&E 6&&E&%%%EEEblobby-1.0rc3/data/gfx/font15.bmp0000644000175000017500000000337012042452377020065 0ustar danielknobedanielknobeBM6( 9]lF)D%yCGd$J&f+ MV!3#G guFOQ8b"az&Ot05~OJpHG GB-^%dQ5&iI^Q3  wdKʜcj)m>.őgq:CE&nL8uGK)X/ 5$nOU9a4$)4&"l̈qyaP?R0'/3$ 2$#4'%." 2'%+ $( 2)(N:6ڞۦ՚paUuMDtTMgaxs}}sqgehf2)(aOI轳າΠ~spgyϢ溵㿼ԱϭNA@*&$sc`i_\Էėnkpnxvts:22Һܾծ~{-#"-''ϳẸ|{D87ԫ_KK     H??ڶԥ||oo]]kEEtJJlAAnEEh==d<<<22ⳳ譭ԖɆԊуuullRRD;;ܾبڧਨߡퟟ [[blobby-1.0rc3/data/gfx/font52.bmp0000644000175000017500000000336612042452377020073 0ustar danielknobedanielknobeBM6( 0 1sx^bOQHKABHJR&TX.YR)SQ.RF+G?@8;4236*?>.>aJbz\|gmmdbrXs^I^E5E.".blobby-1.0rc3/data/gfx/ball03.bmp0000644000175000017500000001206612042452377020030 0ustar danielknobedanielknobeBM66(@@  gfhfdgusv}-,-qoqRQRyxyifhfdetrsb^^$##gee~~hggywv eebvvtqqpmmlbbagheoqn cecZ\ZVXVegexyx_`_ eggNOOJKKfggcdd()*`acrsueeg::;wwxuuvppqccd__`WWX}}}zzzwwwtttsssrrrooonnnjjjhhheeeccc^^^\\\[[[YYYXXXVVVTTT ?!?U\$T \@??8V@T? @((Tq[[[[?pp???NXqq@@\'''??>>?@@@@\n'Z' 틊??pq n11[1119Q@\@\||1x[>?@V \qk|M>?p\@q@YڔMMd1'?닋?@@ggg1? [2?(T((@1;0w//Lޔ''[Qp((q\k/.K.L;M111'?(p(@\@.K.ڔm|1Z[>(0mm%;01|?q\@\0gϓLL1????q͇./%..ӓL1d1'?????q@ч.ΓΓL??@\@bΓԇΓK싋??lqq̓wғaa˒ϓ01 >q@5wԓaaaϓ10>$@qq,b1101'>>>>??@{{aaaa.10L0'?(X@\zϒ{a1L0 $Ka..0LL0'?p\@cÈГwL|[99@\gJJaΒLL0ݔ999%I_``JvaaaawL1[2[9>?(!@a`aa,a0L19[?eþvaaL0LLNÑu+vacwL9Qq(^vJaK.0''''[@\q+PKLg111 \+^H5.ϓ:M|1 ~Gϓ-ߔdk1Zp\n=Gva,aZ[((tOG`Ja{/Ymd[[++vÑ-a:Mx'OO=ttJÑ,bwMߔ|h=Ja::L0ݔOG=`u`b%ccccmE&sOOOOGPHPIeb%LhHHvÁE**E}HPPEsEEE H_WasE**E=t**DEO=P_GBBBBCEO^=H{EBrrrBrBBBB* GvbrA rrrrrBBOG HB E8Fe$0    L!%KVo.<1!&\''g''q*-<>NTtCHU^GRO,7 5EBKJ[\]`哙Ӊjwb?L# MLfdwwpq~XYghҫ踿ҟ]gH6= ׀~3,.ȰiUZ痢E00ƜA88᪩ݥ&c\S֭||@33ljU--#¥bPP,)%cy{YAH/ ޺& J<9D86@71?4-2+"oeIVTf6&.aWWУWEDR?<}vxkzǟvxat=Lp&& Ͱ񿾹W?<ÈȍСڵ׾Td'"F YNN堞gf#2&#G95ZLCqdX~lj^yE-Mpp]X    sjD=3((ΎzK?0#+{w{qE32X$  a]wcJC<n; E132&) E21i`~h~Uq1nvpL\T?9nQ?qL@cW%blobby-1.0rc3/data/gfx/ball04.bmp0000644000175000017500000001206612042452377020031 0ustar danielknobedanielknobeBM66(@@ dcfcbe [Z\tqt~a_agegpnomkl~zwx|xx\ZZljjgeeecbihghgeqpn ``]$$#uusggewwvlmj susoqojljegeghg eggsttoppcddqrsjklmnp^^avvxeeg~~hhiggh]]^}}}zzzxxxvvvsssqqqpppnnnmmmllliiihhhgggeeecccbbb```___\\\[[[YYYSSSCCC000  sꂄtt tS5RKn8@t9"W恁Ktt"DD"tDPntPd?n0pD707~d"##t~0cdDRncmicpc0rrQT&חd7ccd7#6hhoM0d 77pcD"0L//7dpciM7いQdmTӃ3a33c7DQ66/aaϖ0c&60?5t̃hh3_֗7Ԗ&&60taaM&ؗdD|b%ӗٗ&idDt|`/^`ؗ&dr5{`ؗ&&drnt]{{{ӗb&D?t%$JJ}|{{{bחD#t $$\({Ɋ^{{ϗc/ח0r{({{{{Ηcݗח0dQ5S|ÿ{{{{&ۗ՗0?Q#þ{\Ӗ&7rW^%\yyzy\{|&7脂f$]¾y\yyyy\|&&c7D1R(J¿\yy\{{{T07R@=!"$$\yyy|{{&&֗0P@"Vy\]¾y{{{\|ٗ&&~d=Ry\\yx\y{їo&6c7⁎1WQ.xxxxxyyy|ח&Ds9RRxwIwyyy\aaod DDPQrwxxIxxxxxa&0ddd?.x{`՗6iim0dpIIv-------vxxyy`hחcjw--wwvvv\{`//-,HH--vx.x%3}i CH--xxwwxyb^3&D++Uvv-vwwVbg_u+-+++v|_]{]b~,+[u+++[vv--vy{||]JJ++G+[-[[--vy{{\{o+[[[-wxxxV{{+*++++-vIxV\]g^))*+F++vIyg++XX)++u+[vvvvw_$X-vv---w'EE*+-v---vC|XEXEv-vqAXE+-Z-v'+[XXE'+[[-EEEE-,AXEB-EX)'Z[-+XX+XXXEEX)XZ-Yblobby-1.0rc3/data/gfx/font17.bmp0000644000175000017500000000337012042452377020067 0ustar danielknobedanielknobeBM6( x1v^^BB & !}vYV<9V8\$\43ND W RnIkf}C{K&J  >y1+" vfGa*'LFӴdT {ztskF8> Y2Lm3o"EB?|nO\B$sV ~U;qQ>4=%_uu7sK\cOxXA8e&. "ֈU}'N=!  yX@3$>pshq7HW, Oa=[@-#V<($pYG;_''" U6't ) Y5T+`5 5$="! G     dN"vc]EC( iDQ;4"VEE#N -W 6qvfD5 /Lr>awڇڑٖыXbBP   j`>-/"    zs.Ew/8 ^wN{nC8 N jyQOH 2YoOE u %`L1~-PAbT   qR`9+(V ,:"wfE;$b Ge6z_A"(#lwm3(-rlY398:-q`=ygdMGNgBHp9#1'1"O|G?w6/H!blobby-1.0rc3/data/gfx/font53.bmp0000644000175000017500000000336612042452377020074 0ustar danielknobedanielknobeBM6( sssWWW333}}}HHHppp===rrq000 z}lo<>(((尰WWWOR" ΋  DDDޚLMMK#++qqr|322Ufߧ2$ $۪\^]    EFFԌ7:8ltq-0/͝V^ZGKKƵɿ/50 ǼEUI8=;do2D7 |ʊMnV" z}lo<>"&%θڮbo1N7OR" ΋  foۯt·B|K)K#++#*'ͤ{ٍM[-_4Ufߧ2$ $`kXi070    Օcq.8^  Qv[|:D% 4 ~ԇip'-" ^eW[2;1B6ޑrz7w<blobby-1.0rc3/data/gfx/font18.bmp0000644000175000017500000000337012042452377020070 0ustar danielknobedanielknobeBM6( {bneIB(ѲtnC9* f[N"?Bݜ@2T1lV;nO Ɣt/ !H'7\)ĸW <WE Wa4H`bM - g; ml = x?T\rF+$k6xMN %YkYQx7e?FsAb%?5׃lw;Ka%.geމ][v36%/%%՟jI($ yyMDK0`nRҍibG$/7*Ֆߊ~eOoA젘ZK?N;zq!=blobby-1.0rc3/data/gfx/ball05.bmp0000644000175000017500000001206612042452377020032 0ustar danielknobedanielknobeBM66(@@ usv}z}hfhgegMLMsrsWVWdbca_`mkl|yzgeeRQQ}||wvvpoolkkife~utszyw PPNmmkgge~~}rrqddc__^sur "$"orouvututpqpjkjVWVeigwyx bddeggyzzkllhii_``XYZz{|nop ffiddfccevvxjjliijffg|||{{{xxxwwwssspppnnnhhheeedddcccbbb^^^[[[YYYXXXTTTOOOHHH???000|&L "f fOFtOt NOOOMM (F uuAceM'(GNLLLLL%b=M'끜 OUUU%M]MLu&cM뜂|Lrrr:c%:L%ucY'( `r}UuMLL%%%uMe|O~`r8666oo}U=Mb%%cd'f oLLLUL%倀&cM onorrU`%瀀u&bbM'eO r??vvn766q%&&cM'e U}U!577 rU%bbMMd''e86oT5vnnoo~%&cM'eIٟn^!nnn5nn67U&(nnܟTnn6oU'(eے>>>ъ!6onnnn6opULc(O҉ωTnߋnn6orrUU(NnЛ44mno6Tnnn6oULbꁝNOm4m֟nn5nnnoU%(N>43ˈ2щ۟Tnn6oUL%b";2x24nnnnnn6oUU%cMOOȇx2444nn67rULcN҉ʇ@͈4oonvٟnnoo7rUUL'N6nnnnnnnooULL'4ΈƇ556oqU%f43wԟn6777o6qr`uM''뜖f@@ww͉ԉ4ωSorrUMYMF9Oađ1@ 4Њ6rUUrU%cY=&MFOw12kkww4ЊrrrrU%%=Mcb'eN00w00ΊoUA=dcu=]{e0kkٟ̉֟I6%bM'M&M /0/jjj/k0Ԋ֟6rMbuM e/j///0k00w44Њ5r:uuu'9'ɿ$jj/0003?7bd}Xj//0k@w2>nvv LY('$ii./jwRk͉;n_`'iij000kTv! rH0ćmϊT}Qii-ii./j// 0Rl3Њ?o}hQ.i.-/j//0kax3mS?ތ#,ii-ijRwaLjlЉ;nq+W,iKƑwЊvW,-@3Њn**-iQ0wwa4PP*iii-h-/†)))))PQ-K03mS°PP../w͒j#)))PX/g)*J))))QiijK))))*Q-j))Q-))))+))ii-i)))),ii,,+))h-ii*))))i))))))))P+))))))))))iblobby-1.0rc3/data/gfx/font19.bmp0000644000175000017500000000337012042452377020071 0ustar danielknobedanielknobeBM6(  |abIG?80' ȦxxMI-&   ekgU:a_72 sAgWsOeRvun*jg/* jA`k'`\ I{a2-#j<c`PY ! g.Q MXOt rI)Ej-41 oR*WUPv?x(wF0DcߡB55 ʌci$((}u|'3'>(8))p~~~i3'>(788}umhoI~I3W'((Z}m%ShԐIIohWX>((7)%goi&IIhi|'>Y((ihHhh|>(Z{22{IhIIhi'(ZZ2deiאIIh~Y(ZHdRʏfӑ{{g%&'(ZɏdeԐIi'(KɏɏԐIoi'$7{nɏɏ{ԐI'K2ȍc1ʏɏԐI~'YZ)uKedK1cɏI'dKɏe{I7}"m12cU1VĎɏe{{Ip21ɏ11ʏ{Ґh'[[)Q͏2ʏe{Hh38[lmK nÍ1ȏ{{i>(8[lVq1{i33>$Z)unGGUʏHii3'(P77mj1GbbbbT11ȏϐhhH~3'$7"#1T0zzҐ{{Ӑh'YYX>$(Y>>(8<````zG2f3|(Z}[`r//``zˏɏe{ӑp3 (0F``///`00Rd2ӑi&|>( P㶰__///`zGn1ɏIhi((8ǯ/F//`zG1hho~WY((FFF//F///////`V>8r//EE//Ə{%h~iWY(Z(E/G0gHI...E`zbȏde{~.LLL..-.E/`zbG1Io,,,,,,,.EabVVcRd ,,D,.,,,E/UUg+D-D,,,,.ȏ^Dy+E..*****,,x+D,=,Eza0U1w****w=***C.D,,,EzC,,**.,,EbGVƏD*AB*DD-.GqÂ.,,\AA,*].q*\*@@@AA,,***.,../_*A@@@*D***,D,,.?A?AADD,,*AA]@A,-E*AA@@@DDyLE?*?Aw*@@Ax***,EA@],?vA???AD*AAD*A@A@,,D,LD*,,,AACAAAAAA***AA@??AB******blobby-1.0rc3/data/gfx/ball07.bmp0000644000175000017500000001206612042452377020034 0ustar danielknobedanielknobeBM66(@@  dbfwvxgfhfeg||z|~}~popzwy|z{ebbhffgeepoogff`__ywv ddbaa_hhfgge bdbz|zegewxwopoghg}~  z||eggIJJhiiWYZbde^_`~~--.zz|eegxxyppqnno|||yyyxxxuuurrrpppmmmjjjeeecccbbb```___]]]ZZZXXXWWWUUUQQQOOOAAA;;;### #r$OtrOLtk7tq"k9#k U "k:O TTTT"kt TT00_ 憆kq__ hN {//[ p_ x[ T[_p/Hّ.3/ kkL J.ZH[ N$ GG`3HJ TT"V$ԐQQYYYG`HJ[ ~T8g֏2o22YՀHH[ NT#O##q3QoѐؑHH3[_ "VVVV7jjQ`Y P2Ґ.ր`.3[ ##-oQӐҐY[[ k,-2YQ22ҐYY#o222ҐY T"#$LPя-,-ҐYYpk7ViMM-2Y--oѐY.H"k,FɍY,--oo`H N ""kM-[[["Uw,-EE[HHH TOvmmEEY.H[.ؑ[NU FmmmmmmmmEEoYG`JZ"SmREmmmmmE2.H3/_Uk^?EmmmE`..ב[",ŌcXREmmFӐ-`Y``Z/0VO78+XmmYYYYHTkVSEF2-E`YN k}mEmmE,QY3[UkkVUWWEmmmmmEFώ2Y~跺XEmmmEύ PϏYHp~ U價WWbbm,--ΎQJ_挷WD,΍x/[ND*W+Ώ HJb))*F, aHDWn3/[h>CEnmmmmnQJgJ=B!BDmmmn+3..!Xc}m`Y2`a!AAWWXRw\ю QA(uBBsnAAWWˍ(CuA'AWXn,\BAAA'AB)bsA<''BDDD,m%'AACDW,<%BAA)Dm<&B)DEl;@AAW;<'A*'Au;'AuA'C&'AB'B@l%<BūB'blobby-1.0rc3/data/gfx/ball08.bmp0000644000175000017500000001206612042452377020035 0ustar danielknobedanielknobeBM66(@@  YWZfcfigigeg767QPQNMN{z{rqrjija_`zxxnllgeekjjhggbaaqnldcb~fecggeaa`xywklj  ^b^egeABA~~vwvpqphihegf bee;<<`aajklvvxnnpggieegxxyhhi[[\}}}tttmmmlllkkkgggeeeccc```^^^YYYWWWVVVTTTIII...%%% 酈&d-l- :lGz>A^--l,-+Uf -, '^-GdeeUde -'  dd<<<$qNʗWOOjdff qPOOOQ3deVf,^PpʗΗOde f >POOOO*c& -NmBmOO;R4i*% -fp2OOOPijd&--V7~OOq~N}QR;i<dV:-+bN2(͗}}}pPOTdz+h}MNOP}}}OOOΙ5cUV}OƖPPOT6*8:]lffOPO44Cc%> fV==Y–¿}p}}}O̘nXXXi*elf罾|1KLMhh}}(nn3CiU&>%JLYĖ}OPՁ֋C݃cJ{II{¾LƏP;R߂&o0{{{{–}PRS$ՋX T+{½I{}MQRSwF¾00{}ƐQSjc000}ǗPQ! jco0{L}2W_;4x00WИmΙTg{{JY–mΘgygL{=}QH{{{KL–}2;RvH/{{YҀPEgI{|}2W0|bȗE{F{1–/I{D/F{1L.{.1{/JK{I0Y/blobby-1.0rc3/data/gfx/ball09.bmp0000644000175000017500000001206612042452377020036 0ustar danielknobedanielknobeBM66(@@ vux`_a|{}{z|$#$fdfecezyztstrqrqpqifhheftrrgeewvvpooa^]~}yxwvut{{z qro "{|zwxv WYWegeKLK|}|xyxuvuopoghgdedcdc {~~eggVXYstughi eehcce^^`ooqeegNNOrrs}}}xxxsssmmmjjjhhhgggeeedddcccbbb```___]]]ZZZXXXWWWUUUQQQ>>>000)'j醌#``{'c&&痗#f5e5U**_i**f5BB)f&*ii 66fe5)&`{)d6UBBBTej''zT NBB*'{z1ۓUdBgPb?1NN1di' 1❂s//?*h&*?uׂڂ?Y16ffgsXނM:rr:X0ڂ055Bdfga?!MXXdBNNBe ru!q/:ق311Nd)irr!ҁ:rӁM1d' ֜r777:ҁ!ru1Yf*j|r·r9Mٝ1)'jsќ̇pӁ:݂)iqWp9pՂۂ 8ẄpWqre*j98K71//؂fh8pK..Kˈڂ?ݝfg#p}.V۝dT'..NNdd6h.˚w..Ɉq::qޝ11NNbdBU c}..pqrׂ1NNde6;(o.}Lqp9rقN33fP #nnq!:؂ڝBBTTdT55 2\ʈnnn}!)))U5*錍JnɛM|ud5f)f&P{hJn¾.:XfP;-.̇8qXd6*i-pn.ҁ|/Yefg#2))ž"".nnǛ!r?ThQRHI"nnnnĖ.嗆v-HH"nnnn.L9!rׂ1"""H[nnnnnnǛ8Mu/>["mmI-H"nnÖ}͇WM["-Aƚo9HmH"-ƚ\VWrXaFFHH"-śoȚ.ww\ɈpsFGH}}ρr:u,~~~,,FFF[Hʈˇq~DDD,,FFH"nop!8qDkDk,HEFFFHq9++++++++CFFHp+l,~~EFFm\.C,,D,,FFHI+D~@kkFFFFHnQDk+DkC,FFFDk+Z~F,,Flk,,k~~kDD~mH+,,D++mmm[~D~DF[H",+~DF+,k+,~D,D~+,,D,D,~D"DC~+blobby-1.0rc3/data/gfx/abbruchbild.bmp0000644000175000017500000040373012042452377021216 0ustar danielknobedanielknobeBM6(,! JHJ[KTf>JI9=T@d46i9;f&(u23V""JY++B&&T22nBB =**D66 )4#!:&# [>:fFBeJFnRN~^ZvZVfb~b^njjfvrzv~zL2.E.*V:5pZVzb^t^ZrnzvgVS~z~nkl_]?*&R72lNHvVOZC>S>:M:6YFByurnɻN60^B;bE>hJC.!~^V^F@z\UE40f^iPJe^mfunhSNTC?~f`_NJ~3+)vqtc_|jf<42KCAóZ>6rTL9*&nRJJ72vZRbZ_JDjbnWQrjngzaSOugczr~vL?;zgaJ2*dNFv^V<0,ybZjbvnrjzVJFg[Wnf~vo^VWPMWC9D<8B@?<97jF&^I71/-+*([ZXTE$wb.SM<;5#~rN bT540^X;sj;_\KFA!FC.ifEOM0YX+vv$$#JN fjR[e3:>*TXCYi5>o;]x.=I(-3"CI8E^ET06I L\:WiEV!HPACu7bB`1*HQ98D3/=*X<(>"#. 4"?14Y--z )g!(Y!5m./L+8,9R6%M"9@`> "EAVIzgFbZ+1'06,4;19C6>G333,33,,,+3,03,33-03-330-03,3,3-0-3+0,-,033300-303330-,00-3+0,3030-`3300,-,33-303-33+03-003,-,3333303`300,303+0,33300333330+3-3+3333,-00333,3--0333330-0-,,3+0,`,30-,333,3--3+0,30030-300---333,3-30,-3,33-3033330303`,0033,+3333,30,`3,33-`333`,30-+3330-0--,-33-30|@-33.B/|k||VkXm|./mkzQ./B.W).//kUdzWkWX|3/WWXW`QW`zIIV/.dBzk|-0WjW||`-VVzBXW3XX.|B-/|.-.dVVB.kIW.-/mkVmOXBzzQ|k|d/-.zdW|BQVd/VVWkXkVmmB`d/dmddV|`Bmm/`zQ-.Q.BVdBWVkmd`.k|`.--z-B/`./XmWBVXmW3.ddkd.B3.mmX|..|dQXV`0VVVk/zm||V-3mX.|B-/|.-.dVVB/k|.-/IXImmdB)/dVddd.BdVkkWdWBkWdWkWWXjm/-B.QXdBV|`Q).BB`/d/|VWXmIVQBX)Bz/)`WOdQddmVdXk|X|3.|dkd-.B3.mIXBBkOqVWmX`(,`,WXkB1SSceS\\xS:yQBxu<{y_b_y}}}LS._g1\inT^ni6__~_xARC2g6~8cTBhT6~inhTT_x}he6yx]a{}h_T}2S^^c{^{c~:yQ1]Sa_S6h}2:rn62S{{c8h}1x2}~R1{nnnS~^]=yceh:xC6g1R_Th>a^1A]:n~x^y~r_6h{yyS^22~{x~~6rnS2eechceeR^6ch1RcT.B_:aCC1A}8}8TSiC-_SAQgLh2A=SSScny6<}R{_B/.RSg{xh]{T]A\\B]_AQB2\^6S]CR~u2C_}Ti8~=S]{giS{g_~<_R~Liccc[6]ccTha_<~MnS2eech}g8C^TT1\6~C\1}81^xBS{c=}RS_AB/.x}h}^i_c~_^12^Ryc^Cxc_}iSnh1n{S=M_\]{{~:gg~{A^ygr~8~6_i}TW-2/aST62c~__^2egehyR],.g]1aSA8r:}yC^i8~yQ`C]Si{_uST_.S}xy2}RBwy~T^g~2CQ2_Sy\^nh]}cTun]h{,-:L}`ChSC]_in}_{/1c2RR`\c]BS}1y}^]x1c~gh{R^,`{\Ay]Qc~{S]S>L:}/1]Sh21=1_x`{6]yT2`/1yQ/6uy]_Lg1~ac>}C}_]}CQhS~T^g>h}_S{{Sx81yg81yc-.<>e_T`B]AS1QTe8STr:gyQ`Rx22]L]_1.c}8hcunR_yQ./2i^2C^Ji_^<]x_\QxCB]:=r<1,,B_xT:B,xcCCc1h2B/2SAQ,,^rA,C9A^cy{ngx26c^AQ2=<}{cT~i}\Q<~c2^Lh8{x<{rS,,hn\,28S}TSgcRRT}^1ynLi2xih1xR1ihhSQ]yhe2^8AA]Q`\69xC_nnu2-,B2r\_gc::B,16R{x:rS\BSTCA-,y_/{i^}u>cyScR1TSCQ`Rhhy{cTa:eCA8661x~SR2eghMiR1}1\]1_8a}~R\c6^CS^y>]crnh{S6L8T\TxQ]gc^huac6hg]i2]]_SChcaRC8:}^gC/\x^8chiaS]rRc]{]B^6hR{>8{u~B2:-1>6:yy=hCC{T]Agy}B}n8y1{:hiy}:y2ccgx.88{y_S2]hre~n1ST^yTA/B6BBcu2_~_\6R.xSTBT_ARCB6g2c-Ch_\]hg^6wwLeR__2_T5cTA^8S_6gS1SSR_c:nc 2Sxci2Ty{M^T{iMSA8yy_S2CW-giC_>_,Qn:.,6ngihR}hae9Lcxc2B]T21S6]A.B:{8ySS\]S]c6{hh_T1Rgx,/~:{{^\^}Syg1_^QC,.i2R_^S/AiSraRyygxR{^}g1]a9ayARSn:^}T}}C{g1Rc62}i}g//1~Lnx-x^ynaxgn,^:hcR-]~<:B^R_T\Ry8gcR`\Cy_c}c~^}2Rc62}n_:6..Cc]`^c:yrn]Qc8yAB~i].T6./_\-QQB{T]2i}R{TRRxSucu~2y~<:1cnhhgAcc]2AB2CST\Ry~6T1 ky7xyg^RcgS~Lh{ic/Q]6MM].]SVF]2:RQR,{hT~_B`A:CQ,,C~y~ngx}]2~Th_SCQA8i}S2ah}L]x}rc,`~hx{8Cchy1A.RcyS]]~ya1-_nn2cy5ghM]i]y__2BA`_8:{c_1u_/S6hy\/Ci^RQ,,C6{yyCT<:RxyT{y1R:6T_~>6S~R-ygB.a8n~g2\}h:8]y{{r>:uw_]c=nS,,26yBC{B_T1\B`y_^i=1R_x^B,4c]iun~chLiuL\^n_6cCC`^}y_R~CAyhy8<^BQB^~iL}Q]:yC2i6neCC=c^_e~A^1BT~^C{y}hhL:1\BQ_~8n_Th:x.S=iy2~r]-}y]{xQCS8Lu^S_h.c/Ah1Ryg{i]B:_;E?vvvvs5{kt^egR_^}82R^2_eg8neRRABSghuSzwL_{S2C\_i>icR{_R{6}~cc{..{}>^gT^M}h>hhinT>TSn~6xRg~MhneSS2`{hC_6A.2~yxCgwi\6r_aihL~~}<{Cg~ir}~ha:h{{2`{wCS~\/_gT1C~gB_ggB`]}]C2{\R_y6]yc:]/_BBaSx^~1`C^8~Sc2}ghrgTygu::rn8y=.,8n^_TRQB2]B]hx_eTc:}/1gc}h}1^AS=T6iS`2M8Q8x/{{a{2}TiTAccScQ`.Rniy]S{c~2c6_6gS2~{xy}>Sx_8_Q^Sec_LyC}^QC2^c~gSM8y=c_TMe6gnMng{.,h:2{cCBA_]B]hC_e}a:g_.R6Tyg{CCQ^:~STegcx.Su:BB6nx/{{~y^}y:yIv̧v}:S{hc^Tc>y^S8^.1_~T]hSR~_B1y .t]Tuc^yngc66a/r-Y~:h:_x]2:uc~^C_Bch^_^B_^xi:2_~].AQQgh}A/\2h^\_A/yT4cxxTycyB/Qx6TnSwi/xxc~2y}xB{cC}L=_y=g{~}nhg8T1T~Sc~e~}SgT/]u{12_RhgM>C]yi6Cy:a1^}2..}:i>C/x}gh_CCC8hS]{Aah^_LSCy:1R:h2xyx.AQQgCBCSi{1c_Rhg{~BB2^S^Q//]8{_6~{x{iT]{c~c_x{_/2SACh_RhgrR]{cWvvDMihiy]y\gi_{_R{xg]^c1.\B _RQ}c{6BB]12x`-BTS>Sw/Q^hg]BB_^^R-\}SC,/y/Rh{26gT~yR^c_^h7aT{ih^Tnh\R_:SC:S,B8>x-]<]1{B/cie~{_Byy//]8vv~]^aA.g=8Luy}yQTS^hT_\`cn61__._y6Q5mvLM1SSQA{c8ihS~cRRACahC1\F:urcx_eT]^=r_R6C,/RAQ.SsxcR~=}eh2T~n8{~2Cg}TeS{T211]RcaA/Q\TMnTT2`^]/cnhx/T=MwM^{_:>T\R2{BR}SaMn6~6=eih=6Rgu=SA^eice6]_RQ6Q\>uueyccTQTh8~caT..yh:{R\R\~yMyBR}1n}~TR]{iCA{~_Bxgng~ySy]\AAcaA/xaMnTy^/{S\ri^QyL[u_}M{n{Ax^/ByyerhhhL:=T\~u>1QQx~:y6c]_RQ[c/=hM:c}Sh_/Tih~cTmiCC}SR^h~6y_y]RA\RgM8RQ1x/6]Q{n>C]{9y-Bh~2~7shTgnhB.}gA^>86cg{_i{RI_._ygne/`\g~Q]}]]<{^S_^:]/3wB:}2]ih1:n2ygTin8yB,{}-\ML:hhSAa6a:hc_2hMrL2A-x6hT2RQcng2~_._rhgB/RAQ^TcSy~icSg<=g}]B_e~e]CM2i:y}hR_>SQ:6S_eAchR]ThM=yB,{g/]:~~}SAa688T_:Mu{C-`^8i}_\QaMiTTQyL~cec~AB^xA{gc~nuT8{B_9iBQBR1Sh~RR6hha}~8yR`R<{Sx:}:~Sg\/}_1ya}{cxB]]cg2_h2={Tx{gSS_=KR.xcc6~^,S_wQQS_c=hhi{~M>Lecvvyy^Mx,}{2ShLc{i6CRRtT{cgS\-AhSS]\i}:~x}\/}21T~TB3uLRg_{a_rL_]A`R~2\]c]cg86R\T8A_RM2\QQC]S]%`R\QC~nn^xT]Rx_1_BB>nh~]/QBS_S::n~SS/ATQ,SixRgMS\c_.c8]^cSB/}~C9eBAR^S2~n]A1}x_}x~S{cC1c}xRRA2}QSg]hy}MgSL]xB`_~S}^w~_gh:h]]8]}^T]BRC]S2B`/11B]hL2xT2Rx_1_8B\rn2`./]1^=rh}y/R6A`{R\~gL{C}xQ`Tr~Cxgh~T_B`_yR~hC]^Tc{hMxBxTC2V5x[g_g::11g<]}^=}2R|.tCQ1n]Cy]\]S^{hRC6x`./]1^hX,?Z\:Tx:hS]AThSB2_BBycyS181acRB6ga2Tin>_C~R`hSS^i2RA{1,B{^~<^~rTRgTiigTxRTBA21g_CAT-\{^S8RRg{C:TS2Ci~1y_BgaT1Shi1BT,6<_yyLy1_R,/S^m=_hiSS:cxQc2.~c~6)S{ASh_a>_{S2T}>],2:T\AT-AQ3H/STR]1B]Ty6~{26RSCThBRa_B]6hBBi8A}u8SB^{\_hn:]^T2xSaRyg:^>{,/1_T8g]\^BAC.RT]Sy}\2yB_iMwMB,y:]A.xciuwi16}/TiCSc>ng}C~gxy2}]1i8gS8TS:h8STgcS}yBT~1S_2}T~~6cn{8{c\xgyR_h=xCL=]h}A^:SA__SecSygxc6gxB8},\{ci{Ci>A`.B`Rhy]=_y6\]SQ2}-AygiTgn{^au:x]1xxc<}~y_h~iSI k{hi{xh^`gihx,^MX3^xSSxT8RRRBS\_T^hn>e}{2{.22Q/~>}~{Sg2RA/^nT]e^R^__{gnQQc~g^,,B1\R~TQ}iA/`2nM]B1RBCce\8n2B]hMgxx2~2{x:SR:Lca}iSBch_S_R}6_hiT}>RQ}~_Ac:Byc2]6;S^TT1{~ARRSR{gcr:aTS}RBy{RBhL8ig~i{xBB`x{1h_1{T2yhBA{T}x,,Q_^_ihR8\/`x:`B.-.1i6\:}xy^xghi:A{Tc2niS{gS]x}yB{n{Lr:_TcxQyh~i2Ay\Bh}-R~{{T6S{a2i]/.R2R^i>y_y]CQ,xi:cy{_\`-SayMxSi=:2/_^\^xccA1:g~chcRR_^2}:gahvTiy{yR/S8cg:rS]mUSy^2iS/_=_B1_C_hh^]SxCQ,xm,x{B_u}__QxTA8~A/ChLiyy{\,A6~M<~B~gyc2Ch}T~e8h}_=hLSA]ST6{C_81_=gnM:8~gTy~n~.Ah1hRTyA1]TSx}cruh{yyRcaRScinTC{r8xgcScyCc{^}RB8~^}\^rn> *E__y}c~8{^<>hrc^2_ST_\^6\xX3?H_TRxwrn_^cy8B~h:cA_8MA`R1yu^g=2r_By8iy2e~6nMr8CAxy~~S]S\:gS>hinrh]]S,`:BCT~CC]}S_S16c{uihSh>CQQxig`y]Bi{Txgg/Ag6cS]{ySS2ScCCM>^xgTSg6Q.T~g}RyMr:B,RSLigr2=e>hSr2B{6hySg6yir~6\AC_}c2x:]Ah~2c8y}{\1y/QiQ\yQciCBRCS^S2A_2x6yT1<2RR_i},h2gu>n/,CSMi$F1/2cg2CTTTu:^\CS}c2x<{C8|3JQ.{yyih2}8_BA8{Cgy{neTi<}AQA1Th~6{Q5BQTTgi}BQ2y`BCBQ2~c8n:~~cg_{CAcg_=w>1Q-1`}L6]1QyhyRCyR18~}]S=hc}_2TT}_1My/_SReg1~Laxch/QcS_8_/1e1he/`Syyh]y~^i/Qc^R~TRTL:Th:SQ.B]cTS6S62QyBQ}T>}B`C],QRQ/1a6TSThi8T~}B{g2{RyyA6eQ/`S/Tnc1]Q_<~_A2_h~_BCe}2^Rxyge}SLSQ]_<^8={e_Q.BR{hg6h}!`ES.`Ch],/\Q/1c8a}g8T681{_-N]2.-xhiS\88y]y}`^:22C2M:Thn:]Q]8h^B]AQTa:r6/B}6S_^S]yiy~L:Axi_\xSyx~2SMc~e~Q/~ha}2AQ-`h=RCy^h^2e~RR22{nn~ig}L~1a~A]7xynaB~gg}y_^yh1_{C2STS}riR]:~h~C/Bx1BQ~a>_`1nS]]6^i~{{}R,_R__SMB^:ny8gCS:cc^Sg8hnB.y:a{{^AB./n1]iTR}cnRC}cRR1S}ygrn]^:i}8h^R_SB.5)RnT]]a^hh6y_}g]-C}]}8xyhXL6Sex1hgg2}2A]]auy/chTi_{}Sa{2ci{SeMC,}ugT6]C1T~_{:T_{cTC8:]x8cg>T~]y.}_h6yu^Rherh8S.72{2/^^n:BBSaRBB}8S:hT_6TSyS=~SaT>hS{2~hrRRgc6}xSe1116cBchnhy}6Rx]C{{_ay{rC,ThT^ThT]__g:yS8yxhn1SSR~CR~}~<:{612AgrM=g\S_/\^2r2S_n}TiM{~]Rcgy]_u[nxQ^11TT1]Tn8i_AMT}hch}R1{C~M=SgMShCS2Q}c]2hSR8uu2S_u2B`8c^>]\RxAx~r:A]hx}=~^2gSC1B{h}=1Bcn}B]^/\^2___:nT2g6=h2~i^CaeT^S[CQ]1xc:_S~g^Bu_T8Ti{RSRcni2}_:C__B6~M2_h_Agu^y={=C/Tn{]L_xC]4BCScrT}SyTiLx_}%tIbS2Rxc<{x]y:{RSyeni6x^^-0?K2S1ShSxhiSy:6M[A]CxyC_8c{_]Th82Tr]A_,B6>ghru].^AQC]B.x}y]x]R2h_Cg^_8:yn6Q1CCyR_e}{2]yTC8hiMhiu>~R.C8nh\.-,Ax6Tyc>6SQ,_n~]yLe]^B^2RCx==LL2//C8{xSg~B^Q,R8{^]R~L~:%cch<=1~1-Q2CRg<1R2xQ1a]/{h=c6hTng2\\/^r6y11c^C~a2TgT88xyh6Mhh>hS2Q`2g~~x`]R]c{2AS}C-`R6n^`{>rTR2ux,ARxrrS%B]:>T]_6aA_,Re{]]Cgnng{~.,TTin:]~c22~T{iiTghTi^ST_gi:h2/^C2c\Q_iCC>={S~y{a}{TT{g~}{Sc:h{S{c:x^SSSSheT~hxBBC=8/BS8in8R]:i8cA2nMi}C.]h2^c~Ti{-A1cTR1aMc,\y_xTi~cv]]M8acy8~S21xSy*c822y{]1aT_ci2RC]~.B_gic-3wu{Tc]_:2{w~6gSS_^\>h}h>e^Ay6xRB1hhy:iy_h6g{_x28S__2S^_igS]Q.AB.^y\C6S1}B{gge^_{R]gx_Ty}_y{_Cnc<:cx}8]R2hTLi_xcc~nS]~=g1S~}>:e{Bgcx_ghhLLn{CT6\B.\}y1p}_:]S8ySh~c>{_\Q}T6gSxc_\n8^4hcTr}C~TncSTL1SxCa}~yTS1gxghig1{~TTecyBT>i].c6,.CRTa^aB1{CxABQRgTy_yS}~Sx]S=xQ2x\c2BQRhSQhuhh~1Tc>c_8R/_ghn:}yxBL~g~_\.S^AgaCxcT2i<{2i6{>nT_8xSa_]n}yg}cy]]:6:Q/]Tn{SA,/8/Q>`\QSgCR]TSc>h^Rx_6icu6iC,{Mg~{A2cxB_ciiMw8gigSRn_/_6{S^Rx~gxSn}_y1. S2}nS]2yihMrc^R-/SUo'oV_xAR_c1y{y:]~TB^Mn~6Mn{ceB,^V,x,S<}2M}.xC}c]^R1gihA``C]yi=}xQB]^Lg:8ge}y1CRCSxci22heC]TC-2~]ThcTi}/^RT~Sai]A2R1C1y6i1Q.`y=n=A,]cS/Cn~.R2_hSSx^h<:R-``^_cneSC]T}w=ccc{_RB/RQ^62_gC]TC-]}C6>gA1_xcygn2RSx2]_aahn:y2xQciT}B}r6Sx2hy>eQB//6~^LuTWkR}=].Q/1]SegSR/B1. tb2R\RBRQ2~1x8^{8_.{2ac}=c_aehxg<^S_y]Q^aB\TRcg~ech~e6]xS=h:>ec}R_ACa__~6^Q,{yhB~iy_}Su6RRy^_{h6~hCB]6c_12CSe{cC,/yhnrgx{:{/~:8cn]1e_CcQQQC}_]rcS~^hue:>S1yTTQS1S2y^Q^aB\yA{h2^y=iMSRyc_2c=CR~TT\RS_/.BQxSB]:]C/SRhTiih:h_x~c^,`{}8CRLhA_L2{R8hS__T2A]c~B,1~~^x]RRShg2RAS~1/R]82Q/B]1QBCRS6R{nSx`^h8}:y_n=}.Q8:^^{i8/ByCBx]~a~L{{Cxg~}e}~_ATig}gTy2]A^cyT]/]c_in}2S{hBA2nx_ngBcM8S_chc2}iR, ]-]CiLg==i81R~c~}V${<}/ARBShhnM\RR~^\^g=hS_a_x~w8encgycBu2xggg>h}6g>}Qc_{c]_\S}C\Bx{yc}:c^Anue=TQ\yhi^^:Mh1]cS.-yS.QBA_<}B}8Q]R2:{^{CThrc}T~i6S>8~=Sg}`Cm-HA\hu}8rSRa8_,2>c1Sy]QTgCc>yAST]Q_S\AyyA]L_Ry~icy2QA~a^1x=S~{S8M8>C/aST_Qhuhg~g8Cx1C~R^:c\C>i=RA{RAeMhyhS\cg2,2~^{}2B~hx6T\y6_A7a]Ryy\1in}A.C_c}TTR1:rhy_2n^}__gM2Bg:a}ih__:82y>}xc_6MT_nr~~y/gT1S8].guhni8~~g^{v\~c^1C8C{]]cnuS\Xu/Q6r{h_x_1r>_S^ST]8]i>BCrrinhxA/A6SAS_B^S6CS_1ih26y-,-Sgcc/c1T6cc_T6yiyxc_B.\8{A8MgyS/QxSg8a1u={{cgSSg6a{SMa._2\1y{2_chSB/._g1{u8SBBT>h~y2cB%yLhTi_C2xM_S^yiT]_A^<2xBxhcC__B1__Tg_.\RCyhe/.c8B6BCMrL:]RQC* {.RR\:]cT-,-S~}T/Th_SST{6h~c`zA`AThia]^SBMh~xh>]}~nw^-}M_}C`_6cneB1]R]h~6_}6y_`/y_]yh8yx]y^x{ghe8~,.~gaT8}y:<}C-B^}6_na}x,,2^RTTAQ^6~Ti^^_Qy/A_chCThMLmnc^h~1T{g-Q_n_ST4 TgnhR_]2c{ga\Bgc^2TQ]SRCA{cQ3wi:]_TL{{c]S~CBSABA_i6B/,_>^y8_R-/R]gi6~:yyyny=nhR,B1^SCQycSTTAe>_yx/CSAB2\A^^/B6B.T]y>rygT]Q]a_h^S6^{8{hQA{R`/xy=ng^aR`T}ryciShhgh;eRCh2Q8{ySR^68r]\SAB_QSABS8A/,_n2yhC,B]]_R/{:cS}}Chx]A.1.3wuh<1.8QTSx6}CC1y>2.Q.Q{cT.BiTy:~SSS{ycgyA^8SB\gQyiSQAS\TAC}2A1~h11~TgcC2xT]R]B1_1R16]^ia{:S8:T\C]B2aQ-g:\Q2}^e8c8_nT_2}hh~h__axCn}2hhT::cahR-Tc.2_xc{A\]}n{.Q.%Sg/A}T>c~6~8h<^}i_.Bi:\~nc\]c1c=RCy]Ax6xxc2y_1S]a_1y]}gy__~1xgS]62:h{A\1RSc.,a~BQSn6R_hh:]=rS1_~f }A-`heBT=i_QA_\ShCT2\]8={2c2y_R_Q3Mai~\~iBR~6yyTcyxC6TS_BCggMy]=y{6SSe:aT<\_hR.=-{6hhC^cA_6_R\aa\_inc\\]TCR_^Mg~1y2^_g~CC_^:<1]BCyxBT1x_T{S~]R:>T~h__hc1RT>c1Cxxyc{cSS_28}B{~/Bc6y{{{SRCghyS2Bxg8uc^>Ty~T~nn:8_aiAiQCc=]Sa2c2R\cc\_:cRC2c^^6ih_}gxRxc}AC_2:CCB`1TRQ:ny2]SchTc>{1L=cei{yTC\}6y_B]h8M{]>yy~y5T.,g.\y68h8\16\{gS1]2SiyA\Q3wM~h>{A2:e=T_2~xSx,,CC_~xAycT{cSS:6}_S~/-6/w6SC]6iu6_Sc{^]_SB^{Bg8SS~h_B.1y^]i^{C,,Cx_~xATa}y~yf-}:_,{gy1xci}^_g}_2TcxTrg\cia2Sk3[:L8n2xT8_/]~y_^R{2A_~}1e6/A}1B>nB2TRA_^gB,-2cRCggx{]hn8e:~{iag<{.QThy6c6_^~c{c1`]hy{i~>1g~=6T{xAy>}TT~<6g<2xcT::__2A_g^`RSThC{6Mnhy\yecc_a^6Q/>L1-.RiccyC~c/c}2u_a:{x{T1/B62^:2c_8a6hgSMhhThn:_.\:unih=aTi:S.]c~:in6Sh^2~={_y}g2yni^S62 :9^S\-xhx//`^Q.2TraLCcuy}_BQ.L^6~CSTTB,.TMn6e~_cy_1{cSMgB-2ii_BR2hS_{6cS{{8S}yQ}}]8hcRa~~8~\6r}1{B]}T2CgRCA_g2Rc}-R~]\cg~SAR2TnSRBRSS2RciThy}{=iABgh^_SB/2Ty,/6hTachT1S_^C2y2uh\`_=Sx_cMne~ei>hT_S:ry6ng\cyxg8cLy_L_=iT~xSMggS]Cx\_8_RT:c]8n6Tyn/_T2hi~22S6u~^C^cc{Lg~gia2}T_1{cSM\.S=So}~T211}=^y~hS/}M~2i8n_1nhcggV,L^8{cg~:{A2ch{C~M=_T~k-w^T2]:nr8R`,B^B,Aiur~x}6}c{R-By2~iSa>iQAuwn_R{h{S~T}cT~6\Q\]^C\y:c{6h<>>gx_6{R_C_cL]-2h_{1AhLcS8Q/]{C{>1^{Qygxc>}7~Tg~]{i}==]/1g]T^^:r1.\2Q,{B_S^^xQ,By_yg]/C}{c<]y~CSh.RwSC}8ea~6}gc]]{6~{^~c<6ii62}haR1{1SgM^-Sc8i2Ag~y2c~B{c^}LyaM1>TihM<*x/,B:uMyAyac6Tx/xgST8]vt}R^~`/M=]B_:cT:gh~h~R^T}_1cgTUc/,C_y{]{{ACg}\/Q]h:Syc=TgCQB^}aTncTR{}122-Shi]A1QRSx1y~nBQ^2xAc_]RS:h8iMch}BR~{\Q~wSyr;S>c1y~~c8nyBxcycM/,_82-Si]1yC{y_{aS\]{R-.ce6hc^_g:cQx_y}ScaR]8}RQ/18~x2Sh}]S/Q]hT}neyi}S2/1g^BC]{i_CyC]T]^c8wLx\yy_C~{2]}h~cg8>8A1hTC[uTc>gM2yc}{aiSA]gceL\.~}QgMa}hy2ge_vRa{B./x8~]Sc>}]RC_:Tcnoc1B\gR,behgxB2BCc^^}8BBycSxgy2z3{R]RRR`1cMg_/B{h}{RCS^\crLS8nnS6>>S{~^AA2iic}B\TCCC^2xhh]\giyA,/^}28^68RTT2R_8cxx8aCSy~h~g:ig^2ygyB^8~A/RRx\..QB^_{T]xAQ,,`BCSTc6yR]RCR._MeuS.Q{~~e^iS\R_]ATn6]}=}__Sg]/^hM>y6\..xyy{^\TcRBR]C2TxCRx1R]{h6}>:h]/2S^hh~_{ST8}cMh1iL__8aQ`^~~T}ru]]}aM^6n8Sge8__c1RS]S>eS{^_ghTCS}~:^/SyR]_/BT}T{6}B,\c{CQ`TMihSxx]g2BSMr:h~_cCQThTCSy2x~>~{]S_{8}gScc6B`^Wy}M1T~2ThT}i]2^\_]2ic2nS^SghTB3\Tc}=8_^B`R~i~_hS\:6_RScr~~\CTggn_S8_28{}}Ax^\y22ag1{aQ^i~Sah{T6T_6i_y{x26~T}M\R:{1`.{i:6S^86]Scgc~Q_Tyc_h66_2CvS\/]ia]2B~S1nix{~uhTBA`RSMxChc2{{/C^\aahgR_n6BS>ih}>xT:8in}S1]\-x6iRc8\y2ySx-7rL^,/TcC.2a_x^LT^c2a6{LcSxSS,,-xnigCC1x\_n1^T8{`Q{\CnnyB^6TTT~gTi~aR{cA2h8i}T__]._hnx}eAy8C^c1}MLn^,.{ax`1hSRBAu{1hT]cn6{LgT^ySS1chy~{_S8{h_6cTRQ^y_~1ynna-,/xng:^1]1Rx:Cx{:~/\8__SBvv4_1`2=\T~S~RS:{].gw],AQAi^\~SxSiS]~eygr_B8Sx}y6ua-_}g}S}h:}T9R}y{ryxh2QniB^=cLx-{g}SS~2{2B~:h_]S8~Shy./]rL_xA]S}~xn}Q~{1_^g:u^C{h},`e~ByL6y:h>1-.Q]g{<=66{R\,,yi{S=~C.TyB{6Axc~:8{>^]cc~8x_cTSc:gg]8~cu}xh^/~iihB]cLC-{hgTchSS].T^C2~T^~nc/Q2]R2}8hC2M~B~y1_]}anLS]y:c,`~{`_nT_SQBB^yigyCR,,^g6_BTn2Be8A2}Axg~:]e6}ryR8]/aehA_nnc<|o2C.{M66xCy_CT:hARTu1RA_}ehR^M~Vu{S1RCxTgh{cCQnR_cR2TLun_}~_c_6w^~=<]1SR1ce=]2~2BR1Srr_B.\n{xR^}cA^iT/BS_CQxrc.xue1.\_y{`1h8LTRQ_iT_hjvmhJLc8^A^y]_yg{}2cwN]_RCS}cACS\`QBCcxx>SC1S~8x{>cB`3K_^{S]CBRRQ^:_Q{i~2~:Tcr:TC_8eh6yn_a}\Ragyyy{1B~6ci{gcSi~_`SLTy}]CB-]nix-1Sy~x\c}/`1y_T~e<_Rg=Me}My8~_6BB22TSSag]TLhghnh{2S_Sh]T1QS6{R-Be_}{ynh2_cc_`,\y1S2ahyS=ec}12}ehh^B}:}SS=c]]T{/BgneQTa{i~S_:SCA]8<~SgSScc6R_gag{yyx]c~gn^_A.Sgc]`B6iB3cgLh6,xg}]\B]ihTg6CB^_{S1CScT_gy.\BQ.QC]cB]Sei{_8h12axT12uixc~cnigT=~}unSecT./B2y6gh8}yxRTiiy_:y6_gy~^x6ny]^cS16}2_cnSC^~:gcy16guh6\`28S]CS>ia6RQ]C2SCR_cgyS8Ma\SxB.B\C:yQ1Sg21c6e2yh^6]1Mh\y~6n:6{iy_i]gyhS./_cgh_h{]Q:LuLuw=n>hTg:2BAg}^y{gTAgyiQ1}}\{]8RhL_C}88y.~:~cce=acgg>:]>}8iLw>]{c6n=y_T]26g,,}g2g}\,]hx-x:x,A{_RC2yCgh}u_]~eh6y{hMnyC:~C6~y^B\iMLunM8z}T_.,Acc]yh~a:~2/]86c_x}^__28cS8Mm3cRQ1Ax:6_/Bn_cxCS^_Cc:T22}hn=]\g:^]1.A8>uc-`..`.__R>}R{_C~TTcgLiS_grnSR6xCaaT./yigy{11{yyC.CxS~Sc~6]Q~n^}gxQ%{22chxRSC]i]/]gQQ}].yh{g]}LyA2h>h1A_x^=a]/BnMy6Lx1T{y^ay]]T>:\/g:^]x.Aeg}``..`.S_xhnTR__2g8~6^x}n^AT\BSS~2-.{hc_]A]TyyC/2_~8hh2/yA2T,CcR1yCCSACC.Rc.`4aR-_g_a1TnSB]g6C_x^62.Bn_{S}_yriR1R.^1]CRnT_2A}SCc8g}\gM~rnc2xTT11ABchc1y_`^icM2xSA-/CyT]rgCA{],Ani_ci>c\,,B6gSAQSheTBS~66RRnLS{6A,iurir>_Q/1ga1]8gC,-_{/_2BQ=h^S1_nRx\.2]^RR:{1hR/Sc]Rey_I`N\{gn62^{_c:Tc~hugx2TyT{]RA{yx_]`ChgTgy8>y_xBQ_hcR.,1icLS{>gS8/{>u=hugx6iBQSy8c=ML{ghSg2}{`Rc2RcT1c<8hu}_`/BBC2_cr8xBS{_Lg~6ngh1~Mih_2~rh{_{_c:T}ch6R]yT}{1ABSyx_].2gST]T=c{^B{gx.,]iT82_:}8/_:c=Mc\T`-L_{~yhLL}hS={~RSy^8g_e~:]CTh}S2QR^]T_R\x11x8{Q^^]SS]]{6{Qa~/\2,6r\TCQyTe=ihSCA2c~yb8hyygn}_BB^^SaRQ1Sn_]8c]>~S\Shcc=iQ`_cSC^a}6rL8SR_M]Sc~6{`CT_nTx1}__~R] W~6TS1_]{2ARC]1gRS21Sy2A1_TnM{Qag-0O\RxgrnxA~g{_y6c]9c1:a2xgT1A2QTnc__\^6n>a]Ay^~~1^6{^iu;C-_CxyT/.Sc6xB^2SRA6gS^Scr{1~c1:c^\gT_hC\h8}^inS^yh8Snc/BCx:__TC]xcn]CTAAT{BShcrTccAQ.STSh_-\8~_R:<]Thi~A/SnM__]\L:~y}^^iMiB/^A.}in,eT^~y2}].ARQA]y~r\1~yT1]:h^1cxBh{iS^i_ThaS2_}n2\81]2_Q`]5S{y:hn1A{BQQS_/_r:~TccQ-12x6^-CgSR2}:nnRQyn__]\ha{T]^iB.^B-{A,igT^gT^yx`\RQACS8~rnC_h6g__=S_eRBTRTuS]2}~{2x^]Ae1S}cR.Rgh1x1_he1RTAATyQSr:gu}66\B.]2x6^`Lr>_A{^^x~}S{xxhQ`]B-yMr.0w{_2hy_S{2Q_h-1RxLrySTgTgMi^_~{]gaBC}6_y2,,R_h6~A^Lrn^B2:nx}i_y8_aA,B=<}yy^T~ah{_1/x{2^_]_=_.1cM}\T~{]ng]xT}xC_6cAccRyT{^BATLcS_:y^2~_x/^e-^C1M:MuTSST2Ti2_hgT^86BxcgTS,,R_h6gA2r>:]Q^:nx}]_6}h\,Bi}yy2T~~1ST6{c{Ch>aS__A^}{Sy_yn^`C2x`1{S1>~x1ca]RxSTBTc^gg~yxS:_]2{:i2S{2Q_h\`]C1i}{{T2TiU _:~nQ,A^8cgaB1c8C.18gRTh]_6c/0B\R~hMg}hM{hc1TB,]TC=Mr~cicSirg1]_CQCc~]^hSyanS_g}cihQyxAcL\A_6TxxMLS{2hS_~^B8{R]_11_gwh1RR^i~_CQ}a\CCg:Ma{eh~2g}]cA,^TCnn{Se6{]^SxBC6hSyr}{{CSyS~2`QSi6_cw<2]_A^~BQ2gc^C^n2_]ahSCCc]QgSA]S]Rx{rC\1yc2B/S{Q\R6h=~}hMSc^},_cxh=ny_8a4 1}2ThS{cx]{1__^~:niS]S:hghc{J\]^/2_B{c22]A/]^yTR{i=~TerhM~~C]~S_xA^cih8h:nc}hCBc8~8BRT^Cx8yxCh_C\AS<~cinM6~6\R2122]c=T2ya:uiLeac\xc2]\Bxyhg6ehnc}ixa8~nXS1x{~~h~BRcaL:_iS6:ycrB_~~n].2=_CC^1CQ.8i}c_2eSR~i:cS]g>y1}e1^}~6^_1`R>CT}STL{C_AS^C\[:}SS8Sa~x_TT^_ac6ichaci}a>{x^y]]}8y}{2c6^_8L8cy^1{gghc/ByT~{T8ycB]{{}hca3s:~]1cy]TMrT]_]cgC66]cgchwLu=u^-^~:T^1_SB,/~a{cynMyx]guhC_n}AThhyyiiT1CR,,\{mJx_1BcRSeTg1S^By_\^\1R-yyA.-BcTQQ,-Tg21:2^TBR^`/2_~T}iR]8].Q}n:6cSc:M6\^^xTySx2c_}2B_:hS]T~86hxyLw>ny]}1BRhc.\a=LniS6g:RheS`6Sc}cSC_1,/~}RS}8n]yL{Ra_A]\2^/\yyCQ.\~cQQ,-ThTSyygRC^`/^]c{{>hA18ex`.{=8Ty^ch:ryA11RT}{:^y8}g{R^gi~x\2{cTgy/x~8^26^Ax6/xM:Mg^Tc8QRg/BwTcTncT^{2`BcRSTgxS_A}21x\,BS^B.-. oc]Ch>}TcQAx`.xxy__:\18g]./T>h6ec:nnq`?pB\]2]S~_y6y=gnTCChiT~{~i__Ma_^8r~c{RcLS\Sh2C8i^]~aS}xBx82cM1..2ge6T}{~QC_T6}yg~\R6g{CB.C:=T2_QBx:rc\82/aiB,Bx}{haRx2SBg}TaC,ALi^_22R^B\]2]_a_TgT:hMa]^iiT~T}S:}{hgc{R}M6]c=}2g/\]y~a}hx]ha]Q,Ba8ghyC1`/Ag<{A8_QeR/xS8{h}AxS{ghe{_ahh],/g<8yS~=cc~ARR2{yiSSTSS}SAygi}2BA1R_{{y6>QA6}1:8x_}]TiyySxxQ~=]Tin2x:__Ty}aTgc1/R}agcgyT{{ng~g_]~;y{k*!inc28hg~nh9A`BScT8M>:i],/8=ihT{VwninaBTMn_R]S]~}xTS^:{C2~_Sn_TTRyTC8gCyy1y2./RSReA.`QABMgS{2c~}Cx8:TS_2{SSh~R{}QC{{h_Cgy]hn~S^.~x6}\xhCSn=in6B6r_R^{Se8i~\BT<2/S8STTR\B{ax{__MgS6M}ShxSSQSSR88x\{{x{2.Q/+[:eTg}C}6xz12CT\`^g2B^Ly{c_h{S_y_1_y:S8ngyTS^{uyy:e/^T]iA]8{Sg{:aTRTg~_T^\]c{Qxe:e~ehu~]-Shy{ccy{~hg{xryx^e~c}C/x]\T>B-}TByy]yy]x6i2Rc8rnCx6eCQ]h_x}R.^SCyr~~gTca}gy2Ta=iS~>~S{S]2r]CTnSCSChiAA]S_M6_8~B-8R~gy8h_1~2C66{~7x~xQ2S]61/S^Q]nSS}{iS_kgn8yTS_TTyi.C^A~hBQC}]]M~_ich}1cU,M}RB\C]_{}2Bxh{/x^S8cTTn~yT_::TTcy~iiMT}][Scuec}R^6ihTS8:}_n=M}=5S{__^_c8c_CS8y]Chgh6BAh8]g~hi5hBMTR}c{_2/`c{cSxggScT6hT]RQ{gRRC1:;2]_S2{}~SAxy/12{6gM:~eTgghM~nhy}RiwaR^=S{y6]ai:T_~y^9~6RMTc1n[i{aue}SQTCT~21T~y2g>ihcX?p~}SxB\2~gT<2Sh6~Sa_{{QA}ycg_{SB\2xCB\22]Sn{^xgC^T>ww>i2M^C^{SCR2}ie1\T8cx2^]8h_\S],,\~_]B8y/_ggLeT~/_66i_6g69}iTBQ~a}8}_]}yS6S}{]C/xTRBQSih8y\CS=hch_{hchT_h}~~R^h6h}a}R]T_2CC2^1_=S]Tu6QA]8<1iM]R]_2\]}8h]R}he}x2^]:Tx}_,,A~=_2BR:aQS8M8y6.1TT1{hhingxBB}y{6{^2g~}:a;c2\/C}RB/C}ySxB\Sh>hgLSTi8~Sc2SSQ6cg_SW t2^Sn{^Aah\1ywghgRhaRC]1\\^T=8hQ3PTny]]AR]x]2~{_^1_x]iTB_2ScRc6CB\xB,Q^\/^_/^rScSShnS~^T=aRQ]~:c_:Mc~_S:6{gg]-`2_S2A{x,/_xxnTcach~TTcyyS^ShT2RCR\_^_S_c}{{_2S^Cug22RC_]2h]}_^C\T^STBS{}]82R]_\-B^\/^n_-RnR]RC{c2~i~2{yB.xc6_h>LT~_Shc:8^`]:>_{S\}],Q{]^LyyyS6yeghug}2]_8S1R]1CyST}T8cS{_2y4QSny]]C1S2SwiSe}{^x_x^iTBS{}6\k.o2RQ2_Ci2y22~gi8ni1}hTC288_B/xc|Z1cg^{1BBBA2Tr:]i6BRc{xy2y}iy,Qh{Cy6]RggB/h=2_n\x:ScM~/BC8Q2e{2TcRTi^,^y}Sr}}~e6yAg=}a}Rx6}TSTeSxS=e-^y~^/^6hTg.TTx1cM_8\Ax]1yehieCxT22cSBQ8h]/Tc]cn_{Snh}B^icR^gi6AQcLg~{xRCQ7T.1A *AT=_,_hyS1gy{{{_1/Tu_S_QB_]]]Sc^A2hgW3w}xBSa}hg_\SScgQa=_6~BRR8A]_C_}>cQR_1,.{\{{_hc~:MT^c:/Riih{A2^ycB/h~yi]QQ}cBxxhix_Tx_c>aBC{1,.{\__]6T^_}h^A<2cy`A88hg:}MgCT_hAAT_a<}.B:h{B2:\2h_xCS{{^A6AQ86x_:c,/SgC}2tgg]Tnx~M8a_yRB_6gnT]}cRwh~=SA\~8R^2]A=n_~}`8cchn:gad[L_{Byc>h/B66h_^c{~T2^]S~}^8}BaTS\,Cen=]]c2\`^8B`]cgC6S~6-]yB\1xAxT]^~{~g]~Sa1-/Sh}cTr82Sec\_gx]a^1^CC_{66cTCTCcn2]=}ciT1_c~y1{c_wh2{QShc/Qy{~xRgS_6}S__y~28}Ba>{S,\a=2_gStq\/2e:x6S6a-]h2Q]1A1Ty}g{Tg_.Bcge~i\_gx]6_^_xR1_y6ayTTxcx6L_2<{yi}x]{S]`0ach_]2x\_{^cR^h_S].^8_2}c_xSTy81^hgx_LThATn{C2Lc:hx{22gh}/_c~ncTncnRcyx^hygA2]]cgS/Sacha~g~^y=c-Q{]{AB5gmmg{8~:2gg\-R^2}e}hy{_Q\2ARSe2_>}Sh6cTX.1/y;i>LiecB2^1x:<}Tn~LhBS<^B\g:im]AS>rn<6}AC/CxS^QCS^iMgyCcT}T:hS_T2S6ig~h}ggC2:{x}Tx,QihC]TS^yc}]=uLg/Qx1R^_/2>g_hMr2T8h~RT:agg/x}C_8in{e886S1C1Q.ync{8A1u:~c5xS=i8S:6xL^B1x{^BCS2r8c]86c}:__:a^]y8TThy6~R^hT]~9x`Rn_{g}Sa6nwa1ia`/RCAx]BS>g_h]Sc6}xcMgR{h^_ghcn>SgggcSx1V h}Sh:1Sh~c^B1h:g{i{B6iR/\R2xRygTrc]86XtFTxxS9SS8T~g2yy\{SB,Qih]26TS6hy:S8:-uSARRc]cy{n]2RCA.\^/\TTy,,xR_gcchcTaccg2]:}BRx{<2~{n>],1{.^]^A..~u2:cn_BT{1~}}:::TA2>MRC~S6-/S6~ixeT}~6a8hSR8rnh]]hg\MT/a,^i=hg]\\Ti}\S>6cM_{xCA.R2/RT}y,,R1}{T}yccacx\~_.xxS2Be\y=2/S6BxTS_C//enxn8yh>:_BQ_^C6g~h_]Mx]\RB\_aC]}}T,-R*5TyRc^-ACSTxhA2g\,CS.\S_Sx.-T:/R~B3HcT1^6]a=ig1x6y\{h_icC2{g~Sg_]ci}R,Qca2,Sicc=c]:L}~e6SS~igSSg]-QR4.RTShugnic1S}Q,AT2]:g­qy,Cn]]gT~}yhe5B\}yRThaAcMgS^22a<S{T<=ieSx1S//1Ryy~igc//_{~nxByLT,\ihh}QB/^^B^CAATM:^B,Q}rSS:~S`R~{A^~Mh}R1.2{{2-RniTLc]\a_By8hg{1AC2SShLhyhhhaA^Tc{T{RB.1iyx-T~r:a:S,\=}xQAT1y .]]TcgMi}}LiQ/ygxBC}MR,|1Q\^]{T]a}_Mu{_`x{hSShw\-/A_],/{QRnLn:Th:rLi__~Mgxhi21QCSM[{c~ieBx~S^_R1SccSaRAT}nRBxgTy21:QQ=BS}Ry}2]AQyRghC8],28rcSxCCQ]h8~^\S{_gxAy2A~8A`}ic_h2-{hnLux./^x,/^`Q=n=e_~8gne]/1cThM]hi1~iA-/\i_y}hc/C~{Sy^_~ii8_CySLR1;}T21=..:u`x_Q^:_C]CAaw^2<{-_y^gR\C/t褬vk^CcT]<]-_g~_{i_`1Sh{{hwAQx_~T/B{.B<phC-Cg~=uS2hC/BAi_yy~5/R}21^Rx_~V3uOQ}r:i/giQ]_.QxTAC~c/BT1.gn}~STTSy>car2:r}A1{RSnhyQRhgh/6:xcMeRCSyB\}y/BSR.hc~SSS^62iyy8_:}>82/dﲬOjC_c]cr6/Q6Tg/ehS._T2ch_{6M{B//_cR16Q2>n^/x8g^\xTga68Trh}ƽWvi{y_2g6_h{-/1_gaQ^2Qxn1QB]aMhBy}R~t1TM21uu8S]:8xRyhML=yAx1xSg6^ynyRim3_x\x8hT}S``_:}ccxyi6gnie_~c\R6ec6nTy:QQc82\]S~{\S^\2a=i]BRBTn8g{}h1nRTn{h:Q_exAS6Q6n6g~^Q_61x{6>yB/6g_c{xB\c7xRThg]1ha2CBAy1Qa=S1^c~_]RBQ_~{]}S``_6g1y:Syihhc^}TA1=:T}~=__8/`}:{x2yc^Q]R.26~]BBR/^hy{a1y}ruyg8B_8xASAA}ccecc6}\-xS]TncR`icc]B1~~1R}h8]CвIT^C1cc^Ryj,w[uLyyn:xBSx{=^gh_c~nc1ciaxx}yA]MyT<_2Trry12{hgR12\.C=:^x2C:iLhc<_~gA18h_gy}8SxRC:iS{T8S~Sci8]RyT_{CTCQS__n81`,xyBQ}ig/,\8_RR\S1Qx:yxx{_Bgun_Rgy:xB{1{iC6]T{}yCye^^~_BC~S{h]1Tuc^ST8TQAR/,Q8hSA~6cMh1``_aR8nB,OĬ^B^cc1QS2Q~r]]ig2R6S6]~g^gic6^68 x]6R\TycSyxChiy]S~}^1xT{8cTS~2\^yA,C6R`BxRyR\g]1C2LihABxSnwhC2STSTy]ciCB{STBT_1SM=\Rh8CQx}/yyu~2RhLga8{2_gyxccnSxgicce6~urS^xQQx{cS_T}]CS]^r]2\Ag:_x{c_^1cy:wMc~2xS6C,^h].ѯʴb{=~{~8g/C1>eCy7ecggSh^\cTTBTy_cx1:g k2yRBhM6}6ieShLni_A}:Ty=6SSRB`++?O,CihS__icRxCy=}\Cxgx/:C1r\/_S{g~<{=aLaA,Bcc6{}:nM86hx/B2ya<2B2}^RATcB`Tn~S^.B\Bch^STC2{-,^[}^Mc=r<_{S2xQR:{x}=arrc{8acxC_R]CA{,,nM{yLw>~ihc2.,Ah^\ATSRxR_8_QAR~x/hhARnBBT}}g:_i:yhTB,\~g8}ch=cei]Q{a8nyASy1A{TA\iT^.BRA_{~2T~.,^_R>6{h_y{_]QChS]Mhrrrr<}T:e_2T]V{fr_h:T]`,\h6xCCgc]_]}TRhh~S<8STS_`{>_\cMC]}y8Li2^S~cCRS~SR\2S1BTcxex\ncS:{}c22gh6]_gcc8_]hR\gSQR1BR_xSTST1-AyCA18i:h^~h_8c8TgLeB./iSAiih~y2}haRTTCc}ch>SR>iicnw:cc^{\\^g6x^_Q/xSBxRT=y.`]^QS_2h]`2]\hya8hixScc}]B2hTyT2y_C_}AS\A6A{y1CQa}]haBC/Q^^Sxyc.-B6SRc}CB._{2~ic2h:~:>MS/8}66rn~}SciaAB8g:=T2gT}>ggScx2c:^yTQ2cA^xThi~/.__B}{{_/21A8{7~g:]ycc}]B2ncccS}}2CS}A2QQ{Q__R~1B6{AcS.1BB]12\SyQ`Agy18^xAz⺭ܭty>{Q68e=n~}ThnC\:hi{^ga6~na)12_Q/x91S_8=uh\Bcc|ç.]RB~_yc<_ahh~Ly:r~~SS\y_AT~^]n:eLrS^icTBC_c_Ba>T1S=}R6e^~xQ_ghayC/`1AB~hiy`,ղoݬ|1T=x8u}{Ty_T\Rg.^{chT/,/C\_6ScS.C~x/Ry2/R]]:gIýѬgg8g66{Tx~}xgk-wya~:SS`,A8h6}.\iyQ,Q]C]y_yBh_ia-x<2x]8nec\`]SA]ic1_cggh8hgin8}c_]xc^`RTLe]_1~ihcr2yT}x^iuR]~x^g}]_n_28h~]ScgR`]w[hS12/R}/QS__~T5xRhRCua8g:SS-,Ci~Qx~A,A{^y6ycAx}`\2nLT^SiLrgT-2}CSg^yg:hh8hg>~T}_]xc^`Cy8S}_igui_SSyACenS1~L<~c2y~x._Tʭײy^^]]riQA~:rc]h8S~S~6S8_1xcuT1xTi>=y2}S.Q^B/ACST\AA-T_{T{{_]A]gi8SA{_\12T]{hy2AB]2^_`.gh_xggguLy.`yc}igyrhA/2Lݶj{x=}.cLg6rn8R,,.=.B}huc]hy8ykMSC_8nMMh}cS`o|oѭoBQ-Q{2SySy{LSRB3+upy2BS~yya}hc2S{S1T:_A^>{x^_RB^S2~62c~_ch8^e<,B`-Syg~STy8={QQ]h>gx]xABRhAQLuS22..S^C:Q8nniC^Sxee{xBTinMyce~x,Q]eƦWb}xQhL{2c_BS~<T{~hTvtu<_^yc2xT~7i62Ħv֭B,Q--2S~6_T}:}B-0Mi~AR~h_y]cMhhT_h}^}SQ\2S:g2^1hn~}~h~xCc2}g_rTyT8_8g^:TQ]ST}ai8a_.-B2^]_a{R^hA,2ur~c~1/2xALxC6c^ch2a~Scy_~{>~cc~^Q1BAx/\ra]S_h=\]h6xB]{a}BA~hcy:i::}_M_aS/A]Shnc1xxhn~Tca11c_}Le_~c6^6}xgS`RSccch:cS]B`.C{{_S6_1R,{~}C.]RQhnxRh8~_ch26~{6}yMhTh:{A_\C2B|W86xB^}h~AR~hi{c_8uh88T_hLnThMo~}Tg={SchLe_vѬϬ18S`A2{y}h6y^3(+ohri8^8hhgAh:c]62{ynM2Q}n{_8T}=e{^ecT:uhCcT^c].cnS\S{Tu:nh6}}]x6rhah2Q\Sc^{_1h=g.`~=~_}aQTg~gyh:c~}Su8{1Qc:cTTixB6]S~`,BC2RSM{AS82/\TT6c~]h<==]1>ng^~_TeSQan_]gyTcS2cTh>x6T2c]/6n^B22SM6gicTT~2xchnaT:yAxc8]^1R~hQ/nc^{}Q}uecgg}_hT2AgnA/1^C_:c]{hS^]^hS~wnB`h_8~Rcc8Rxg_TS~>ccTc8]Qy}\QAy_1i~ciy,QxT>cicg8~{8=6]}cchy\2c~AQg8]R>uc1Mh2AQ_eyT^>~h/`/1hnyCT6_cgrC/1]\1~{C_~S2^2_~\/oĬBBTxS26nhh:kttս*:g6i~2,Qxcnc->~.:]QC_SgyS]]n2cyS~~SBA~SC{Rx~yMS`68^Ci8cTc:]Rcag~S]}hc=SiQQB/in2MhS^a:i__c]eMM1R1ge1^ytpe^x2SSuS1:.x2Tc]QQ:2A/c2xh2AShc_yT/C8SAS:n:{CARx]RQg9/^n}MTRxScha6xBch_S>y./xexSSxx^C.R{2C1xR//^c~_^TyC/CS1y2AcT{^x]^8c_R_:~Cx_\S>_xxRccx\]^2=r_xuBQ^S6g_B:],AS~>y~n^TR-]ggei{8S82^Jrc6h>ihie6{_]._~/S~2hx`A2{}aRScxQyMgA.^82Shc}:yBC]2:6^xST_^~g]/\Q`y=CRTcx2c^\S{Riu1\c>2{8cg6_QQAh:r=x.1}8T}nu^ݭ~uTQenA,Svݲ/,{8g5yc~1{c^}C\h>3++PNBT^Q{:c]_C:w}-{]TgB-x{8egxBCR6iB]y\_8hi6]g[@wTgaT{6LcTcS_26ch2^1R}CS~h>8T6eS]}e1{<~S,,x]cgTS_^R,,Rin_AA/C6:h}Ay=8}{:12^Qce{A,Ax-BT^Q{hS}yR:~QT^,_LS~:-R^TT}xQ\{hQxguc^TincRTwL6x_21C26}~S]B]6}h^]xRTR2cenhc~{ygScnT`/5Tih}^x,,hu]BB.\}g~SB_:68gDvOto-\gS-B}iT~rnc\^8B~S,yh_cd驓{Q1g_\2̰ձ~]{_^x_hS_y]]Q]cTk3(,,y}ge]C}gc6=~BShSL_\~T^6TA-`^hiT6gTB]axQ1CA6]\x2626y_~M2T6hx]ccB,-Qy6]{cT~CR2{R.QR^8:_\ST2h]gy~g]]}Th}ch]CSTSSgn_.RSR{icRA~y]c{B-`^hiie_CRia^cw~1ARyhy/BgMwu}CR6cghc:h/1cBCQ22x:Sx]_g_gTSgS_y81-Bxc6gT8}AB1~ni_A.QA1~]26cکݺo~{1TcTyhMT.1TCTaR\g>}_gXǬd]=~2}wfooݛ9{:_RSg_y}_66Qx}B.,,uewyT_2{{M~T}grMrua8nhhrgB.^}:h]C\2irnR/{B\8:Q/^x~S.gu>8~=Sy~a~yxScyhe_AA^]gh,BrQBSxQeg~A,`1:ec8SC68g{]R]irnyC^_..:cy~c6gec1Srw:aui]21x]x{<_C1_=nT6geL~B.^}:h]C\_unR/:a\`C:~/`CAcry.~uTTy}>hLgc6{]}h~>i{xC2^e\,C6LSR=:i]`BTLnga2\T6~{]AxhLS\x^/BέݬǭD>S{^]2]}y]2{>LcMMi)*xcLun\.hrF)sx,_g6__8^_hc~=TT<~d3ݱﴴNB{L~]2_yacTgh6^2ehTS{T.,]}c6~S_ScTRTC/1}=SAxx]_2T6hg]^gycxyh}RQ}>_QB/x]/AAB6y_=hncR]SB.`^=B_={]1{>{ehy~<>5/Q_~xC=S./u{RyrhBTgh\CS>n_{h{^A~S~h~^]~ig{__{.,]-޾詤Q^6>n}]_Sy}Tag:8__h~xxy.B_yA-_y]]\]R\xAxR-AaM8y_2cy_S1S~uu<~n{_y21]ASMyx/B8gQ21]c6ycx,`]_6M1/w_xM@rSx8i{6iyiCAQ]SS^CCS:^g^6:^xTxg<_``ݭ.QS~:a2T]2c2_~~S2/Q].A}g]QvodoVx.\c8uF˵ݓg1yTx/B6y`CcB^_1_w81cycS_hSB_S^RSgn}AaT__gg^c{]Axh:a~c}QRin2Ta^2c6R]n1S6>~y>`Qi{]{hc{Mg^1B/^c_CT~_QTwTT:gxCR_\ASBQ^6hyx}]_2`\h{16c_^CB``]Tc6_Cg:S~R_]_8CRcx`RC\/RS{h]`]S]]ccxTg_xAxh:c~c6CxKi:urygLg21SgSB_L^{yhh^x\]B2Q/Ry-/h2C2g}{t+[w}Rc^Qng\19{ur~c6u]{u}cTcacy6>~^1CceRAyccguR-_8x.Se_xx7i^1hhy^_^_~_2i6xy}1C\T8C~nڻo:C^gyT1]}]1cCyM:yS1Wʬx{`fୌv^cgxRAx\`/aTA]cUSc}2c]`CaTT6crLycx\yRBA}y1xC\{^RT~}]^}T{{-]x8hh>~S1Th~^/CryB<{6Ti}SCQBS{RQg_B/__Q]c/B2B^cMcyCSM~66]A.1g2Qyy]^Sy{i2^=c\`.S<:QR:yQxi8]SS2~{/^eac}{1^:n~Q}^\R~~S__^6y2~~}]^y{kϬǴoM_`ahyg6S2R/Q2SxBh*`ʲӬtdo/acc2_T7}]cLd8S^y_1iySx^{S<_A_x^2}:Tx8_1_]T~]ARh=]QhuwM{C^}{.,gg21\}rR,/SScTR~ngRBgT`]g_hhRS^/_~xgM}c6Rc>g}c8T{y}hT{{R.Q6a`x=hgR1gSSi_\S]_Sgg_u6yyxSh\2n=]SDvSTS^TS:M2A`/8/^k˫ׯ.ܬv*|v*/6nnnW|_`,yoٱ+?HcAxcL2ixChc^-_n^C~a8{y]2Syi1\Q.2TB^:uMuy21n~_-_>1\a}~SS:h~h8y6hh{~:}]6n^^_~<_]T~gcS]Bc8c_QB}6y2S~ga=A/A^R16Se>SiB1hSi]hxa2^LcQRy_26fS/zDzݴ+PB6g\`QCy:hx1}.Ch~a.BT~SS_caS_]S1BR{_6nhTCA_nLcT262]RCh}C:n8Tcgehi:gThT{_{Q1{S2{gTuTC]C2g}8_C2nu:~SB\2}rhQx}gS`^2ii8/Bg{c2^^yT_}ycSxS8Teic1R_>gTgyT>aT1C~y\h=~a{{c~ghec{~y2^8yQ1{S]_6y>cx^x<^8Sc:]BxLg{]R_c:BxcgL~B}MhS~\1S]˭dSg=1R8y.R6hQ\n~ĸtv䵫8S]A}if *Y/5aac]BAS]Q^c\BSThBQ8T_gT]C^]]__TR,.B1]~c\x_\{=2T~_^R^2x}>y.B{yS]>=eyS<:QQc]CrS_iS{c]/1~2x}nn].QSai:yx2gSAy:^x}6ARhT]cSCRxxCcy6]/AR^28gC]SC{nii=i]yc}}_}cS~>=T.Bi\`1}}<~}S\Syi}`Q2A/R\xyh{Q-`cy~<]\yxBQyySg2cyhr_iS}S/QxS{Sx=]CBCQS_iB-__C^h>_A]_2Tncy8__C-R8}Q.xT1]icMh]C\`{^2=_2{cSBS6Q]n6>i^C^^_u{CR.,Q]SThc]Bgu}CyA,c^]SghC`^=].}66cy8S~g_ccSx{^-Rn],^gynL{:uhSy1^2in_.S}S2_Qx~ASS,xx\]h{~h^SCC^xc=_]nTA]]/,QC^Tehc1B:cCTA,}^]{:x`_]QyTT~i>h{T}h:Tegy^~}BSuT,^hM1/^ٲ*yygceyM]*tΧʵϷĺ*tWie\\xotĭ)TTTwMhTxx{LSS}x_]x]xT8]}6/`cc{hc_Bi[R18~}=ehABC2T].\{]iCAxThg1yM8\Ty^^_1A\TchM_Tc8a.`^CC\T].An2^2TSBT8hQ\_]c}]grn~y=A6R,`{~i~112BSLg1T}~_A]8Mge8]{^CxC{e]T>C\8n~]BhwARTg}TghS21S^/Sce~B^c{hynnn:]__2hi^^~}_<}TccRh}A/areA{7QS_1AR}g>TQ^r6Mr8~i^R:8C_nnytܬݴby1QAB.1BCFtħׯ̶ᵰ˴F|kvFQ[>cA.Q]6{R/^>TT{R\Qax^{Sh}CcaCSc_RAxS}yB^S/Sun1\{T]2T}TS^~c{8g^/BS6c{cS6T^x/B^RC^ycR8nh}2}T_\]:]6h]~:8M{\ARy`,]ccTihhyih/Qyhhn61/C_^y{=u{`B28c]/]=<{S_A\Q:uuyce88xc}B1SxRAx_TyB^S.2^RT}_{:{]6}Sg^/BShh~h}g}2C/B2C1{~h\C8nhc_c}2A1hM]T~R}gac2]2gB.yn:~:~~>8x:g`._a~Zپ头W={{_A\Qhβt*tdt˭h|3|@iiaS;icR{gyCx{y]x]^a8CBx_g8cQ]~]]\/`RT1CCQh~\C2AQx_1}hh~ga~B\^A.{S^868yC1SS1Q]:^BB}Tah::_S_\Ccih=iSSc:2QBxe~Rxc~2S:{Qx_}^A>hA\_ggSgy5hh}ihy^8cRyeTCxy}_]^_giS2y^~h:9]TAC\BQ`Ry]1C-/r8C]{CB_}2ch~6c6B\2xB}T_h~gyC1{{]AST\R6Tahh__2AR6=hT=ThC_Si7}yRenicgTCc~yh86c~c}i}2g_/Qc^TTB.Ax^<~\ShAQR\_]MT8_a}]hhSC^_}T//c~SRTgT_SC]2y~6={C1ScayT]^h/_rMh]QC~^-]i_]ghh~xB1cCx~^}e}x,`]gc~SRy:82\S8hThL~}hi`ByCyhSaMϾݭjn{:SCyiitĭú˺ŬjLB]I,p\h==C,.cTx>CB]~ghinc_c\}u8hM<~cg^\ACShyy}S\R_c=h~8^B]cB,\S_21\_[SQ}MTiLuy12y]R_ghi~2T>S,S_x{i<,^i_,.R]xC1_{h>~2{]/Q}r6T]~c}nc~hC,`{i]Agh:A,.gMg_1.R{Tae:2x{Byn::}^{SRAR1Se=yy}yx]TL=hS\SeQ,]xC\B1[SQygM2ernSC2c2\_~hic]{S,_:_CShhQ,1LM}`Q^S_]2ya=McecCxh}{B1a}T=IݭgM9_M2Q1cꬭdo[S.|3wuA2T8ec_C2}:cR_gTc6_1aT{hxcinrMegic_^xC^=n~g:aB.R_2c~~S_^Ax1}8]^8yBg>S8rMh_CxS2RThc~RRLg^~MB_iLuBBc~ST8g_2i8]c].\acT:\^S}c{6^x_~]TLT2{{1\_a1]6Q>~i>MLa{c6c2]]A.ng8i8\Q]}y:hcT^BRR_h}RC:c]/T]}n61ARS^Ry::~8nRRMcx7n~Q2:iM>\R:c~hiccyin}]=>e^S_R1ܬId}M6S}սحtIڧ鴦^_=n6}_C^i~R26{{_1a~AR]TT{TS2~72ng{gygi1\CRTLuTTy--A~_{:_ax^T6~68g^hMhhARc6^A6R]hg{Q\~8un}\./Ta]]]-,y~2c~T<>}_CxghTR2hiSC_Sci:hA2yShy`2_xgrn_x82c~/SnhC]_~g6~aT:TL:ye_}8\B\\_<1]],,QTg]^g^yh\x{6gg8>h^8ug~~BATg=Lyxh^_igx_216e]xcy^6g~nLgB,,S88_A{T{^6yg~hca]-R~cTy,/~/˹XhSh~voϴg_\C}2QT-uKri{~nRQ~>~SBaR_{M6B6iTa~c{Q1]_TTg^A2^cc\SL:QBcT^{g\}2{yAB_:urc_h\Re{a1Ry6cR{gT>hA1~M:_i^~<_].,^>}_ghcQ\~cc~S}h6,\ahgS]S8he6~}\Bhh_ySB]}e=i{~*ݴvn^_S٦ϭvtou:}_iBRW3uN6iS]6xC=x~R\/ghgS_r}cgcaQ2_^ARSBQcThLhSgw9,{h/QcB.ii^]Tx.C[hCcS2y2rS:<~_/]~Mna8u:AC_c8h:R1SiiTQC]{Mc]RB/}=gCx1^h^26<1]hiTRC6}Shn=xC=:\]gxQ_iTc_RA6__ySS`xxR:}BR_B/TSS>a]Tuc-~BARQnny_}1B_ui]~:]1_1>RaMcT]B/x}M~hQ\]y6g6~~nx2T>7Qx2TrM~_xB/}==gC1]_i_SgThL鞭obرxĭVr[R}hxRQ0GT^`C8QBTchgC^h~}2~~nRR_^SS8_~8{=hx]}R_i>h]_n]=]^\Q:8AS~x,QLcCS~8gyu{`\n8_=}]y_QS::~{:r~B.S=hcAC}{]~SQ2wx1A.cih_RCxy}i{c<~}^CTnˬܲt`߰ܭV`AcR_cST|3N{Ln_C_SAx_xTT{g8hSy:]^RC_R}M^C^2hy]c}iTA_CSgR2SA_=nS^cB,ca6h2^T}6`,cTMT6{QTgx_S1g-`A}^.-/T^\xTh:nS_:\A]B`\8RQxh=A_:gn=h~2^gMhc1guuyB8gx~T]gMcTccQxu6BQQAA/}:~^^=g{:wMC/Ax_eSagSxA9hT]}_A_iyQx:nc2}S:-x666nh2]{Tt鲲vU6_eM8y}8<1|uS}-C:{Rxg6xyrTB]xCx_Tg~]y~yhh}_y:]Bx{hS~]Q_Q2hy]T8hSQiyL6a>iT=xy~chh^A\{y`-2_{SBS8QB.C^.`_hy1__xCTe{,A82BCgcx_u8iMS.\AR]_TS]8TSg8y]S:]Bxy^6yBS{6hcce>h_8}hTAT],-_acTThTAShx..,RyB-RhcncAa^6\2_,/e`,TK8uhCci6g~8_Ay1/xS]\]yxB.QhSg{ByTR2{{:<8>65}B/y,-}>g_y2R_6yTex^x.,/=Sx{__h-Qh2~MTA{TT8yT6h_c}_~n8TA}1,-}h6S_c]a>SBQ,y2Sh_hh_t~RBa\,-ynR-x2}Mg1g:B-y>>h2Qx<{Bge/QSR`Q/Q2SRxh5ByS_ST~_nn_R^{h>iMeA_:T}S/^2CigTi:i{6x]8R_hhA,`SC__S>~:2x{cRR{TySQ,B21c6~8_~}Sacg^T--Rcr1-]_ynaRc=B,_ig1`Ahn_BQB{x.BQB2_\Ce{BSTxC^TM6_{]{6r]6h~\gne_{ne^6c~C魌2}nh{ic--A{u_Q2STI3Jx2h\QA,2~T~S.\]C\-,^h~eLR1T<12}_~]^\/cS]c=}2cR.ahRQ=M~yCB_c~cx{21T^2gg,.a<\2cA{igS1R.{_]RR6yTB,yLMSAcS_cnMBRgCeT_{ncx]Q{gc1^h\Q\,26ya2Q`A:CQ,,C6{yiyQR{ihRx{_g^2A/Ba:{{S8ngc^B=x.c:g}a2A}:L^T2C=g{n1xcc,{1S~AT8{]C/Rc_]:RR{^SB,{L_gr=cchLSnMi8M~BA`1ykˬgB.6i:Su::cy\Cc~*oT~^\]2Sg~=~R1x\cu_d3wL_{S2CA2:9SABy:{Cyh~Tcccy/QTc]gLT]LLThhnc>T_hg^1hh_~=gh{<6y>}{=i}~hc8>8:hTT_/cw~A1yB-1}SR\ceB_u2\c{2T6]]yTc^c<=2B2\A~=xB{S^cgRc:nyg_grc{_TMe6~68>T>nB.h=6x]_/QxR/R6BCa6>Rynh8~i6]1Q^:ay}hg^/SMeQ\8M^BT}h6{e~cR~~}hCAC{iySFN\i~nScu:i:ic_i.,8Yg::2\Rx8hS1SBT8x^xB]6AB6c]2a^/CB~}.\2h2\_\Q}c{6BB2^S_\B.Ac{_w/CCca]{yCQ{6]~nSSighh~2}T]{}~cS^}{Q^2A]i{xh~M^Sa=g1ci8_T8}BQ}:i>^|tvtB~9TAxyTcyB//A{2i]:/BC.0uO\}hT}6S^B.RT8ni]^}Q-Th}cg^_i^/S]2>c\SR/:Q`//chaS{c2a,B8M>R^iLyA\/R:urcx_g:T]2={181,/xRB.2i:~2_y:_S2S8]{h]B]6n1i.`/}<.B:[M2^x1]xSnS/_{i8BRcg^]g=e}cMS^=i\-Q]CATTgS./CSCB__/BSTgig2TyRABRT<~\C._rcCCQ1cx_86^Snu{x^.\_^CBT8c1]Sx1C/1TCcSxS~h:=TBcrn{\x~:}h9^SxBMcACL8c}c2{{S/Thhg6~T.-_8c2]C\=u{2c]6C~{T=cC2y:RAyg_A1=}T8_]y2\Aa6A/QRccT_/S_8ML]QSinx_>~]86x]{cR]T2}Mcac=icghah2/bgnc]]c=>g:g]_RQ[y`M9yi{12^2.y:8acc~T/.yh}1C\Aayj˴do.2^B8r_Bcnu[uS}r{h2BBx^/BySW-ZB:6Axc8yhrTA_gLhT~a/-STQR:~gT{TgySMTC_]^gh~:g18{._ygneQ`Bac/x}1_>}STySe]1i>g86~c7h~}.CLi2--B2~8~~~x]CRy:6hM6x~6hTQgcSa>{^SS2~1Q3wLAa2^eC=]{~~nric:hQ,_y`A>ic6hgi}Chh6gr8^}A~__<~A~iC_TT:}iiB,Tc.RM:}SAa6c:cS4=urT]-]g:T^\Q}n~2~2`]:iMB/RAQ^yc_y>r=ccyc>^/_A_nic~ix{VR״~riaSBB{i>gT^CuiT}Q{na}e6\-1ihi<]TCBc__hTyS1in,_h]C]}i__hT>LRBARxSh~\Rahhcy~8S\-Ah22RB8_~Ly1gR/~S^cg}_T\B2^6~]]hAS>yyC_h6T_=R`Ry6e2,S_h,,R\^~}yc1}n1i<^T\/T]^eSS_C:R,y_]Sr86>SiBQB\]~Tx9iga8_A-\hiSSxBhnSa{xg]TS~y^S/RRSy1_hASmccLr^/xcc6~1,^]6i,A1cSST]6~TT1T:__h_QA{g8_{:h~6Z鴭ݴy5~i:e=e_A_n6c_1>uagS8\/c_1y6d3]:yc~SM_^\.1hT1SRTxT~6}\AyC{C{xCx^S_-/1xB]hn]CyxAR]C^ABLi8]./Q_2_iL86c]gASixRgr{R6_Q`yL}\Cc:c{B`_TR~u6/QBR1R{igCQRSA2}Rc]2g_C_eg^]]B]T._g]hycMeSrn^1-R~SC_\wc2~g_SnSe^8M2\QQAR]xQ.Q2_BR~x\S1Rx_1_eBBn:~^/QBSS{6SS/AyA`SixRm/g11~ha52.,^_B}u~/QBR1R_gxA]6_c2hSScx]~~^1]C{~/WϬܴR]xQ`S{1Tc{hy^_}ScxCix`./]]{m-H1=g^i:5ic^.{hg<^c~h]]eyR.cMT]-}yc7>^A6TR\5TRC6yg_{22~6^,1}:6Cxng`RSg_~=2^c1ncyS11}yB6TSC2h=>1TA`811RghRQB2R,/2xci1}n_B{gecin~xA{/B]1~_TM:yx.,2ac:]~h_Si:yCihyBc2S_}n:CQTiyCR__B2g]T^{2]{SiA,Ryh8]Ce.C2a1SRR;c1incyS*|hna1SiiR/_Q,8n22xiCQBS_/x8}i>{hu~x6^A6cR^88_^8~iTcySc6n^,28n:}\Aic.xA3JBy}C^^Q]Ty6aS_:^c^6iBA}2QxcA:C~<}2i{R{=r=S{6{T]6r~2\2A`\yMr~Q__`S:CSc>~{CiS8}S_>nh~2gTSi:T6e{6tݭ豫*2i{R{>}ci~ych^g:SRiwgRy6:~1Ai1B`0rQA}2S6AQ}i8g:SACBR~S1ec2_i~Sa2TurB]McBA]8iaT]1g.]@chah{^enS{S2ShLh<~T8A/_]xC2r_,Q{2B.`]{ShgSRBa}yS]{\xi6B\~S16BBMg-\i^{a]{i21aSyy{yM:aT8\Q{S21дd/.1__8Lc^^ChM=68}hSyLi^Se^T_A8urc]2y~tñv魭YB^McBA]icMugy11:/1@ygi}gM1\}u=_W3LAQy]R~_`^c~=1_~}8h}1]2RSSS{A\]c.TS]yg\.~1A\_t׺|ܴoyT_]:ni1{g{h~g_{Q:>=],2LS^B1LX,2]y{C{cRCB_\S}_LnT{^SB.^^QQ:Mea~1hn1B_x`.-/^=h]:>]B1~>c\\^{cx9^R{yS_/_cRCCQ^T^e:hSSB`2yxQS6/Si~y]CTLT{6c]Te\RCASC{aSiM:6T{}RQ{SARuh~chSCA/^ny]:_x_S1^TT`/y6~],,Qx\Rg6B}i/BSnM]BxA/Sn]M{\S:na\AɭvnhSS^61/T**WAyia_in=ggx`Sc]BTB-}>gT]CyvtjSS6_xR\Q2n}^<{_}cST8nQghhS,_1^iA~>CQ.xiky7^8:{aT]CT{.xh6_gn~C_{\/T:~hrS]g]Ai~.1}TcccnML{/2:^B]_1Scyg{SR._8TySR`-Th]TyBC~gxQy_C_]~gAR88hgyABC]\]h=ySc_^B,CiX3uL]T]MLi}{TA]}R6/Ch{{_z,Q}cn>hRhag21iSSagh:S]i~M~_c~~}^Ti^2>_\2{T6{RS2_=gnnayyyTc6hMBxi~AT6\aoWa2ǬcitvC6VIoSRg>6S]ydzt`Ƿݧ8i>\hhaeTSacg8:ic{nMhLT1{}a}1{Cxk3?YS}RAnL{_:g}8ABc~g}A_nng:e/,ACShnu2]^}e8c{ny\hg]TTchc2y~>,-ci/{Bg=1AC]y^]cCB_^C8r:gg2RQ/Rhc,{]B~cCeLSA}T`Qc}y^C_yScTa^CMr__:~ch\Ac~g}AS<:Q,B\2<8Snhnwh_L]/_6hS16g~>uncR\1y~~S2yAhc1yg{T:c1STn`.8h`B_Bg=VAo`tLXz*魤tt}j.BWWVko*ox8nnn{h[}nST\1iyC6yS>gSg8_/.BR{:h~:cT\cAB}Tgii{/`1:_,/\Q/]ag86=n>ighhxe:2SR/_hS\~iC\Q=x`2>}1xQ{h{\AS\1=g7e2BCgTy^]{TT^RS/STC{_Q2SAS6`.y2]~2/^MSJiB.{yy:_6h{A\6^R6yB{n8{ehSQ`/2gc{aS6_BcA6c8<{/`1_,BxQ_~~T{ci:68~A6~CxQ.]g^AgiAB/_/T{CCQ{h{AĬdDY~SXŦv4o/˲t秶oV6ahThyA6RC8C{_Sh8\Tc\2}_^1_T1x]y2Q]ncQxB/]ii}x-n\BS2BB]9~RBgn86]h8SA^hS,1ncc]_~_nnRB^hSSA`SygnT.Qy~ccyc{C~1S6~}}}C]T1^rx8S/TgQRr:R^^.`1i8Q18R\T{`2na\_cS2^y}1BAC2R`A}yB^RA\Q^cx`nLCAySRA_~}B/6>i~hSAc8{11e2,C=yyC^M<~inRB2h<{AkŻtbڲzS`˭o˭ˬĦddoCg6\21x1A{_QCr]R{2R\S86\B8=iMy\~:cd3s8u~cTcySgn=^ST26g::M2R=T22~^h:aS]}g]-RT1T}c\2ic:c^}TB]:STr}~hu~Q/cT__n{<=T_y6x,\{Cy{{L/Rc:c]{SBx:6~n>^{hi~Q/c=cTUoշӭtvٶǭ̓ׯ̭ײݩڧBcgr}/2MLeT5Mwg^hhgcS}e^`R{cTL>Qxi8c:_ch^^i~g6^eT^}Tia.{6g_g]_S]S]RS8]1~rC,T:a_acC\RST]2c^M_{SC1Ry^_~}~~^cr8_}`{g2h9T^\hnieS.}^{2.]2:BB_nexA~n:TTcyxag^_{T{:ch~eM^Rhi~Mui˺嶭贫˴ٲڭ.ǭ٬]C{iiSS:2`TiyCARy8{T6_WSBc~BT{W-w12A{ha>S\~M<6]}cC_}6r_^^hna~nr1{~\QSy^A2uM]Q]xA]8niSRC_ia:_Anu]2achTABxQ2hiaRSun_8^]/T8cn]]hT1ww^S:2i]Q-gyxn_1]2x_nSyn=c2_Q\^_M>]2^h>yyh]{~A/{cSCS1B_^C_8n}]^Tnh<_n_S6{h6^/BxQ2h6xTMS]T{C=L2^h_\grM*.կܶv˴ײ˴Ĭ7zVmX1yr2\UWUD:SRCSnLDqX**a{h~_QA_`3?Y^_x_S]:_SicwixTS{hS~6{SSg=niTnc~c~nh{Q.]iM<]AQ`1_wn2]x^8cSQ,_T\2>Mrh\\.\c6],,ycA_8}S61,`^TJ]\S1\h{L{xy]nia1Q^SiS>{1cx-C8iyyMx.2AQ1_B.xcT2^_xS{]=2^~}w8B2]]cxS}{21{i~^M6~}gTAQ]hLCQ.,xSiwLy_2{ieTB,{cC_iu>yR\.\c8{`,~xch~iS.{gL^^\/Qg~^T1*ڴz̲鬭XskRB_ir]o*l2]xTtt_iu>cC]-3uungghTg2CRQ2ihTT}__cg^x\.CyC-`Rcnnh}8/,yy8SnS/Rc2C}caL>]R_]B^g2Q{=geia2RxB_uc22~S]h:{c8{gL]y~Th6a8gTSAQShe]/{yT=h{2xQ]~1-`C~x,_cyn},A{2]6iLuh}icA1xBR^}y}i1huMn6:=en~CR{>]]nigch}yg6yy_2cc}{22~ni}yTgn<2}8y^6}_y::1\xic-/^~:ncx:Sy:2Q2~<~Q\^yhcgA/~TR/yy_x`/exB1RTuTy\c-\_2}SA{iA16_S}yaT}nu=~TRBxB.2}\18T^~Ay8h:{ya2Si_Swac~ycT{]=niy1~h]RB1~S8>_]ggLT28nTgyhL6}:nyAAcSA1}~gA]{cy2{nB^c_Cn_S6{TSxh6Ra8hcxShe_1]6hg~]]uLyQhe,-BQ_SxLy/x{1_]CA.^=c8rycey^\_Tn]B21xgiy1C2}Bii<}S{~eac\Ac_Ai:.xRy}^Ntoʱ϶ݬ筭`籩ĭvcS}LhBR2RBhnQ3Ps,/h6z-/hB`A/]yBQCTS~S]]_a=~y./S2{ha]CS1.Cyc_C]Te2cTc\TSQ]niy{2aC,1=ac_B]TR/x{8g8>rui~g=c_]r_/S~yS^RAx~]_Sx_1B,,BT:yrg>B/2~Liy{R,Q>\/RnLR/xB{~RA1y_6y2Sci>=gyBR6T6{^}]/]T}^R^}:Tc{C}{/]r6cMyaA,2n8g{\_6CQ^Th:6hyxRhn^QyLc}S^C2::V*cT^{cQ,x<|3,Cg~cQCc.CCcc]21^8i1``-R]gnn^QB^2M8h~6_^1A]AS_2he^{8_.ScC{8y}c.B1RT~Sa11AC\RSScha^xAB~ix:u~{x2:TneQB/Q8]c>Mn=ic_1AB2i~RRy8{_2Q..y=nMR,_:~RSQx2^8g_Sx]gn2//Q_SgnnnhyRR{ywnh:~c2^^xS1a<_xg~x2c].yh_chy2CQgc:aB~M}S^S:cLARB]cvtܴoݴv|_~1}cy:{.Bx`0cCcn~Q6ehayg~1xgS]gBQ`Q_^2c_CS\ThngLnM6_6~6\}i^S_TSAyhR]aAScc~Tg7a}CCSihh>gTyBA_\\_RR__-,2:>gc}RSCxT^]}}x.,]:yhhQ}8h]x2^MaRR}S{TuPZDͥݬʩݴ鴭o{8CaCce~8chg8e2Qu6y}^28:S]hhyyR~6^\/ScA>r}}S168R{McT22/R2Ry6rT{8^bh6Sgn]AT^{=y{c6i_TiTcc{y_S_Rcgc~yeLgRyn2QR}yg\_a2i~r}~\_ehcRSiCa>:>8ccT_yT^e8/-xT::~T12iTyn=Ti}Cgg2RQ}h^rTc{1hi_g~}y5B]MrT_8Ma6ir{{=~h2}RQ{_y6c~e>TCg~gcTy_S_Cc~c8}h^~yQAS{T8.RrT1}guR2gh}\Sc/2vtttttttod#_R6_{c}gg=yRgh-=cCAxy{Q.B]T22A-^h8T8~~2xhigh~S,`ceL2_}>BRa2R22~a6Ln}~_2:~ScSTRQ]~y_6}2xR_gcaS/^M2\ah:Tyg:21A^nh]_MhQ}rMg{yyc_R2>^/cy_{^15h~2\\S~1QC18yA\2c}C\]2cA_2\`SRh}:h{1:<8hT`/~hn}ahrw\]hy^y_Ty{=_}__hT~Tc]BSy_cc}S]xR_gc6yB_uT]hiSSy}iA~Qx=gR]n88uhT{yc_A2gh,^hS{c_1_8~2\\{g*`7gr~S_Tgych1Q{gV-T>8TSc_B]R1e{/SM}RA_g68e2S\SMhgcnL2\S{{~cA`T:^^~Q2Lg~>:Sc}_6~c`,}]R11\QyL{xc:Xڭ宭ݴk}~2~c6SxS\Bh~x2edu6_M>i_86yT::12yL/R:ae^QB/^8gMR11>h^-\hha~}8hnh>].cc]Rx]C.R_]2_hC`2^Tg{gni^AA1c^`C{^2TRB}~hg8>~gcCCei<8\.A8y/2TxcnMr\Ch6}ccS~hScc^~MThc^guy[w~T7ih:1SiauQx:gcS]hBxxAC]Sh~:hx,]\,^c{yew=aB]g}gxB6e8~}^B_x1]^R}ghTQ.S]QSBSh8cyAc>iny116o㭭ݴ,ܴ-.t.TM[x{{]]2{hgC,_1,x|M25=1A}iB-_6A`h=2_eT/-}S.QB^cBchQA~cQR_C_hncy62C~=w:hiL:}inc_c2,B={RChhh=6_yTMu^`^RxS2{1a:^]CR_6ggc=c]>~SQ\}8yc~>}Qyx^_\/1}CRB]Tc6T:c^Anug=TQA{]]gy~Lc}86_1ckoﴧ˯xgw::M6g=i2cyAmKRxichSBSTnx,R>~^{72~~\{2B_c2TTRRccRA2~\/1{8{SgSR]iT{_2cSShn]BhL8Tii{{~}~x^c2Q2RS=~TMhgCi6]{ih].~eh:hgx1]CghRCe>yBR><\{Rxi8u}1eh],Ca]{6S\i]~cA{c2TTx1ga\A^LaAQ2}:S]c]QAg~2^]:]y2^6iC/Ti::hLhhgh_yh}x{C{>~]Cia8cQA:}1S8].gu8hi8aci6C]Sy>yyM{Rvt˼̺}R^iuicyS_}_2~u_AkLBB~riy^BxA>:x]R2hT^_nBC8]RQRey\{SB2Sy6hy/RCC:^6}--/6hA~=]}6a~:nT`2}\/\eLSAe6{S./A]cgcCQ6unMccc^_aTT_2:}-]h]Ax}}SS~:]Q/.S>6\^Mr6^/Byc{]TB~u~T]5_1]R^hy]SMC{h]R/\e{CT_B]SSc{B1]1h]}y-,`yh~cQa\Sy{cTc~yS,Ry]]R_e2h6cA]Ti~1Ber_iSyyC]y{{2^hT-]:^\1y_1]{g2B\C;S~~^/ٺݴvto`oI;:\81yTycT~~n~.)3u\`ATh~h\AC`8>c8{g>_~rhr[2-T:_6x-S6cn8B1^Ayc6~^/CeS{TaTu6T~CS~_88CT_6<_^hhSA.C\~:{}~y1Sc{_./y{}hiyRx_x\S~6g},`Tccchac8_Sc_7^^Ryc]cehCB8hB]62=6yxCSg^B,1h]{}Tc~TTQBSihr=_8iMMiS.R/2T>hia..6S\g_S]Ti1}MirrS1y8^Q,\T~M>R_>hy{T6yy/Q_cnCTenCSxQ{T.ӶݴO*彴h:~SSa_Mc]_1T~d3:h]_yQ8^^2A1{}in\TRA\yx,Sn_Th=c2BR_T.Q]c>Tx7h1QCggMyc2{]B_/3}hC.86.^=2Ry_AR_^.\B/B}n8Q\c}ii~g~g8ic\26R-.g~QyiSQAS\}x^g_Ax~hxx6_T{_T2c]R]Bx2C\Ccxxhy2e<_hi:}RC]ASc.,a8B/2=}]~i~g:ScSSaihg}yh^Cinc2hT:ic~hR-agBa6Sh~1\R_i^.\Q/BT8hA1n66SSSSTa8T\_~R-`g~Qyi{BRyRyiACT2x_:=__hTgcC_172x_A^S1C182SaS8i_hhiTA\CB_}-,}~Q.^yB1ccy61=rS1xS~~~hcg^]ftܶ7c紴̶d16SC2h_2y~g{aA,p]~}cg/Bcc__2_2RA}a}yAb:Lc^n>TTgcg=nR_n6.,8i-{6hh\1exT~2R\aaR_c]^{g]\_^hhaga1{g1C]e~C]S_]]`]TR/nS1x_T8<__8_xic8__hhS_8r:y\RxxTc{cSS_2}}A]~}SB6<~~TB.1n{}T}n^=~}c_`]}R\c~6y2=~h1SuMr6ABR^]CSC2gcnTgiT^6M:S]]i8]y],,]]{8_]~~}S}_{n5ygQ`~=T,}~SC1~La_{c{^2TcRSMcRh:y{gyA.2}n6{gxy^A]{1^Tey}n8B1n]g82]}}n,-^TAR~6RSg]8h:___ni\{h_/]ca;C/]S>i~_CT<~a^y_\Sgh--inA,B8{y>T1e8BC8_CMMCS~xR__:B,`_~x]hh2a<_e6~hg{ui~hn:_`Ag6h~~216cSTCCe_2giy=ott孭ܶfur_{6{BxiMC_}S~B2>cS6gxR2Aar6xx\T8BSx2=}8hhhrg]yTCy8R8ax8Mi=_Sghy]gcCxc~>y16y^{c8}BTuT{A\SR1~xA{~n_^{nTg:iS}nwhx]Sr8^]iMccihSyC-1i>C//`_Q.nCSraL\Trhc8c\QC/SyC]1gxRc_Ty8a~g~Lc1yTRye\Bg}RgMi8^_}chh2Cc{AS_8h_/vtXy6yR/?pBSyAQS~6B,.}g]8n6}yn.]Sxg8~S\AxyhrrTCCS{_]ThcTcy=iB~h^22B/^}~]A>}a~ic^TyS^T~yi6B-]y2{6Mn8~:ey^]a>]Sch}BRcTR~}]Sy6h]^6=SAaLi]{6Sc2-Bhy/Q_~SC1\Sr~:hhM^`^R/~r8]SLR`,Bx/,uc\y}yS1Q,Bc=ych_R26yTi~\{~CS-QM]B_:}}6gcT~TAQ\]^RAyi~T8i~h1_n>}6i_hM8gna::SRan:c<]/1cR2RR6>Mi_/]{B,B:uc\{T{y^Q,Q_^Sa{x_gTg_c]T=`/LixB2hTyec6~e8xA]{y^C}i6y~h}~hc^y6C^}2}hr2-_iSy~\Bg~y2~CB2{CS>1_n}Ba:^~ncy:}hcBxhc1A:[}6}ncx_}}y6i{Axc{cM.,ShS/}n{S~2C}c{y~yxScx`/686:c2_cgS`,x^_^gh^Sh7.Q^82STig_SB//x8Sy=e=nh~}\2hS\]i],^hi_A]QR{1]c:r[RAS{2Rch{^C{g}yc688R^~^Ri[6g}>}C_Tc}8ncx]}S}/,S:{/cySyC__^_aS˶٬Ƕp8gnn8{eTSڬ˴ttf*.r_\xAAR`]ni~uTB}h^_A\^]\crrh_g>nixSh1^yCQQ1hT~x6^11__81Ai_]h}\,Q28h^\ceSh_]hhy]yie2_i^y}hTg8Tc8e^2{~{B_h8RB^^_]BQ\_Sy6_]CB,,-QRS}ehhS2A\^x{uy]yii2Sc]ARSe8BRc1C^{S:n]\ii^Ce6R,Q2e^Ac{hi1\c6{]c=hSS=:_T5~h}h~8i_^S6{B_hhC^2_1../B1^S}|d٭oV]ATLncCyiWt魭Ħtݭ068=:n2.^SS<8T6T6~grgRh^2ga/S:h~~{2ccr]T62TT}2_c]C{2Sg_n{2Sge{xSTc8x`2{R2{BR~gcygcB,RayR`,_{]12g]/2hL=:c8CQB_g~~_x}aC\xS]{}ABA]]Sc=c~~gi1/2S^h~ScTc:~guxhn]2aTQ`2ih=2_~gu_68yg=e__}]xy_{62>_1SiTCSTc8x`2SA1^.Q{yyygA-1e}C.,xiic__{h_.^u==hTxBBSecS1Sy\BݱڌݶXS6Q-^6cyy8ru^bݭoJBSccry_xBShTyC:}^hcQC2nyn6AR}h=:~^/CyrM1xc2S{Q1_Cc{yh1SeB{n}8hT}~T2Qy8_}T_{h66>;RCi=yR,,2>ri~{]gTC^{c:g>}gyc_ghT68Ty]CST^_T]S_yy1^1ecB_{ShhcS:cR]S}:8hc8Thc_gh{{mUkWADZn}}BSc6{^_6h~QA~]]nncASh6y_y=}_i:]~R_ec8ay}__1`S=M]~hC}:]^yCQ,_u=h{RgSxce}rgT]yny\h82Cyh6a>_{iy]TTcR-B_c_~unx{><^,,\<8R\R\^^_~gQRVoǻ˶oninC,`S}R`]hyx\h̽٭+1Sgc5c:}T~1gTx:_Bi:6.Rc^`QB^i~_h~cS,,{h}RTn^BegBS6nC^8~gc2:C]gMi2S}S^S~hg~h]h:h]iSB~hha.\88Sh>A,_6{TeSy2Qcu~g1\]g~S:=nc/Q_M2C2y8]SuRy1S^caM_1Tc,/T`]S]~gi:R`/RhTng}_BB,,{h}C}nIVmttĭݬvy:nA,_h~{}hSy^.{Ko,,M^^xCx]}ag>{8S\nR_~x{TnMh]}a2c2cwCgLr}TTCx{cTARyC`BB]{A/i>SC1{8h1y{/Q2]RB.B}L}Q{r[RQ}i_]~T}i~6rhgy8cx{c{Mh6R`1Ta6/]h=8n{CBy6Tic2c^1~S/\<<{_CARR{=L}T]^{6x^2hx2{xS1ynx~n22T]^Tchc\xyC/CC]MSB.AinSRx{:^cLg\A{2C/A6r~.]>B/}i]Cy__~yScTT^cg2ceyiL8}\-1Tc6Q]=ڴڭපtWy]ynxg=S_yRR_T:ڭ˩,K_]yT_1R]/1:_Qy<~_8icAcL~SR2gg8ay:CycR^>=h~}_A.{_2g]yS^LS`{LT{}S2R.{CC__cR/ByTQByuy6}aih]ALrha_y__icx2ca_`,xc2cneCST].xc{SRCTSxy6_\]A_MMS;ݭݭש<6_ayx}cS{cTycc8^6Lhghƿ(~h=TB,Ra{x\^ia:]2]__CR2Tg{R_L].\BB/B\R8S/\]Tcxxcc2y2~2S]~:i~yi=i_^hxcy:cA\]x{ca~gT{xA{^R~:]{TCy2}9RB{=^C_gy]cgy261{g_A_gx`BT2cT6c2]]cyA/\y2A1T<^xTy\/~]hnSA~Th^AnRa]6c81B{STy^S~T6}^]1ccRQRcSCyi^x}T`uT<]iyCc2aCAnR~2yyAQ}S1T~6xyy/QS811i]B]2hT_,,1hnn8i{xg~ѹ׬oI^B]cC-BT>_c{8ySSh2\]6yAxyݴ˴説Ǿ,+PN,Q__112_\cTbnnxC}gh6y{:uyC:~Cyyx\/BhnMi<6_}6]BC:g{cTcxr}=iBxTLTA{1h6QTR9xc:r~Q6e6i~ha{__c\8_TwR.1_Sehn{^{x]}}g,,_yRy_A`_i1`11,Ay_x1Sc]c_^66{TBA>{cM^-/iM>_B/1g~x2],-^_.B}6]CST]SC]R/_^_CBc6_^2\68SC~i1B6Lcy~xQ^]QBBAgLia1y_`2ic^S~\,`rhna_Qo,-^_.B^]ȧ`ǭŶDni^{RQ]_R\AReLigS~y`^ic>nhx_ey_S]^:n8T1{1c~x]y2c6BTMng]A~SSm-ZR}ny_{2Th{TTgu8]_~6TS1AAyTxSx`\a:uy2{]yc{2AAT=h]Q-xThSS~{h\/SrhcM6R}h``r}~g>=]yc2ch]ycQxa2Cc}2ghruy_A./BC__cM~CyySMgyT:=]aL:2]~iT{yx_~2__T}R]}ca}_xR}yR^1`Rhgy6S~6_BB{81/,]icMyT8SigB`^Lhch^B.i~^y8}iMn_c6]yx_{.Rc^R}Tx}8uTSR..BQ\1]T=qܴ-tŬ 1MyS}2cn~}_R\}gx.,1i}:r}cLyh\/_:MT~.uHgcchrLe_RS>_y~ehg]B^c^6{\C{^_6AC_Rych<]RT8}{^QC2_c{xRCx]]hTQ]11_y^\^T9uyQ}6/\2,~n1]82C8Lh2}8a]BB^cgc_h__Tuye_A___c.x2gS2h~2e_AyniL=BQ}hc^{h8giurh{C2h>}R1SyT{R/xT_Tx1T1]}Ax{]68S^~iic/BRR_2yRxxc~1^^BSLB_ix^:/yuw~\iC]~^1x2nnn{C8u1}_Axiny2r}^:hSRQ_]jjWU麵ɿokˬdCLiey={_}R/{^^xAngy_SRxhM~c6{ghnh]]^{^_~2A6}ySRS6yy}2^i^`CSx`Cb1\:{AATc1\1yc\8h2ccT_Tn6{Shy__8S]QS<1Q21^icyTaSTS}=6ShR28h{T:S,,\_hg:R_rh]Q18Ry8x_a62,1rec}_aeg1Sya_{_eTh}QAycSaT-`x{gh8R^enMh]Q^:nx}h]_cT.+LRR~hc_6g>c]~y1cA,_yA6hec}icnh]]SCBCc~^y6_S\_S_6_./_::y1Sg:ahxC1^M}y8cyiSS}2R^h1^L}S{CSy{~_/QSi6_Thne^12R_QQ22:MiT}gy{:cTh8.S\Qy9Q/26^R2r2_]ahS\4\.yh1Qx_1\x}utt=ymvRjyTL=T}y\/\y}xS>~T}x{Ty~_./_::c2}wi2]^B]k^_S2~in_x28a8ySL\]^.x]/S~{{Sx_>]{{\{:hTy8ecc\^e{_CB]y8~}~8y{g/y9~>C]gy22iy]]R.ARc8ye1~u~xSB_=S^ncTc~>Myc2QC6}~_RRRT=>g{>SSTC2c2S{26hny2yii>aR]_Q_n{A}~S]x.]<{66x1a<{^T8M~6C1~yS1A^cihg8n}}RQc~:R]e{]x8>c]1\.\CcT~Ccr}B]h.-Cgi2xhh^12_hMyc2Qx6T~ig2RCCTg{jt:h*|ooB.SnTecQB28gS2~:8gx^e{_C]}g86c:CBa8g-wr>c2]ye8T/BS{cg{>yei2{aQ2TcnryBynh_Cx_]xRn8g2]~_CghT_Ry2ASc\Rhg2ceT}S/]i^gyScrT]yx}i]R\[u~i:^_~2T~^}eey}:hha]ciSS~_S2C^\R}{6c{gg^2gh62]{gg:aQA}8cT8T~uh2T_=e^.2iL}22y]R/.gTc_2eSR6cS]~_CygxxiSe:yTS.1>8\{cycMT1{Rc^xC>u{~a\C{1{cxy~S1]}Ty:6]cS_62_i:]Ax2Rxc=Tݭ4c鴶x.crMc]x^C\//h=c~S_{Cgi>hT_16SR{~1^yhTcd3p:_]6}^SL{R1RS=}1e8_g{6uLMiiryQ_~L:T21_{CA:ha~yM}C16rg1i_/Sag2^~gec42^-`]ahiTS_gige^ThB,_ry_}Mg_2SS{cTgy{ecyhySB_hc2T~T^RS_2g{2SSQ,.6cS}ynMyRxgLhR^_/Sag__8:g_2]--x{:y6Ty_9i~g^c,_rh8}^C_}^]B221^]ST_yhTSg_2~B]gyS6h~S^}c7ݬd\2G˭׬ZC`cnhc{T}A,Qg~yaTTx]~rR2{/_ch}}heS2Q0ZgiS.{_R~=yRC-R]\gQC_xC2c8gTxQx8-BS~cgS8:1A}~}:c_h^_SihBB}2/\_>y}Tcghiiyx12}{8}SaT_6g{S1^Lw.R^{c{rwh{T_Syx]~:S{inSgyx]6:}7^6Mg{CQg[y}aTg}^{/{S]8yxSQS{1gT.A1C]y6T_B`B{>h/,BAy~hTh~._{_8T_g]^ShBBT^/\2STag:ihS\R\]CSTScy^T6S_]_MwA.C^_~8~S]n8_{^]]Bx}8STiie_6{x1cg8{::gSCtשּׂth1A5ܲ謬o-]2g=gxQSTScSh2_{<\Rg_/R_888iK1S^`8T_~y~\^_\c_A]BAAyyCBQx:hBQ,-Tg2]>yy=gC]2`Q226yygBR}}\`.Sw~hu>ha_}S/AaRSeTe_}}RShxQRQ\B,/^^B.-B}ncQQ,.6:h{2:]]8yBR^`/^^cS_gBR}T\`.26SyS~i<~R1xCSS_e8x^}S6}x_hh21{aaTeTQCMn:L6]yeC_Jg/o/cSBVoڶotBx.Bh>=aync2h}C2.Q2_~}Tn:\1g}R-.TLgg{~g2A`\h=g]_SB1nugxy\h>B-B1cLS8TBx_TAg}T6C,\Mc66a_yC^{T__c^{cT>iu8CRgg2y]{g1x~R--Sh8cac-unT}{g~6:}x.\6ii6gyT{y~ggS2hg~cyi:i^\M~_gi~ySceh>hhx`B^{_T>~R,.aaSSggh1^]]2Sih^Tcyyg7xCcgcS1]S2}a6~u\]he2u}Ax_\_yT{^^BgCS{a\Bic]aMege6h6T8{R`R~8:}T_2]^:i}yT1Ccy{_^gr:=_xh{hic{S~hnnihA`B^S_T6gR,.}hhiTS~TSyBAA12_h]]_^Tgcxxc=n8S\1R_S{chMiBR61u6R]SRSgy{~hin=1/R{cT9~gR,:h}yU0[>ng\~n2CS~}ig\BT<^Q{yc61CR.2g_}1.T}R__C8iSR_eS_i1_2/B^h2\~6AB_2\_1-/]T]hCB/QQ6n~yTSghgxx:TSyyccc:]1~rhC_a}hun1BT2RrMc{BRchy_g^/^MSguuM>cB}]BCySh8geyB/_x.^}^{TRAA`x6Sa^Qc~1y_^SxShy{nix22/B^h^\ccAB2^Ay2./CSC6Q`-QQ6y]]x8hg^2ay_2_22ecAAc~A^}Tn]}2CMM}SQRr^nDt˭IacM2xݭݭ˴/`_g{yRhr:^}S^={xShTTL_TTB\_h_A~cCxcc^c2.Q/+[i}hg\1~h^Ry>a{hSB_e]Q]yS}{TT5a{2yc:_~>~S{2x]xxc{-A2\~8QQRT]]}^~hT8yR}:hTg:{gcC}6\.C{]~]/S6C/R<^]_^8SS_y21{}hSeney}y^_r^]~MT-R2AcgQQRc^]ny]ce}h}xc8SchxBR{SQxg:g69wh{/6r=6c}{_2}gc^RrTC28ga^Q]2CcnLB-g81hvvgy86Cg:^)WtUm~>6MhTcy2_n]]6y`xSx;iQC}]]>n~Tie:}xckMcRAC^_Tg<~]{nR{}xS6{{{>MgyySiia:agc>LnySSBchxTg~2^cg_]~=}=6ca}}Sy~8~}_ae_^ih6\iM:_~i~h{e/cngTC}cySRQ/\~T~Ly_iic8~8i~_^Rg_^2{:cAB\x]_yc2BRcB^SC}y22={^_^hh}}~c8=ir>}cRi~1SnTS_Qy~RTeS2c~2x6TyT{{1x_a}2C_gyCAgnhiCCrS~h}g_~8/eMhTRy8c{SRQ.\~Tc_]hhy;k˴kRAx^_cghToooF}ĭsnMnc6C:~1_nTy5a:C{g~2]}~{{:LM:aXY~T]BR{rM6gngh}]T^{TBRaTg{TyAC{^]\RSS2ynSxQSr6Q2u88C:Le2]2S2A]{{}<=gSTx]^.QyS}gSy{A]}2xAA2^]Sny2A}ugB\_ggRh~x\x22RR^Ti:^Rcha11R\ch2\S1,,B}i{_ARi~Q2i}caS}`]cai_ceh~cAR8~cT_2c}{e˺t8gc_R]Ti<ړ.BV2CCSS_ynLT2RT~Q\_:Mugig\cRAR]1RR]{:Q?scM}^^\]6T}wncS2xA^R^}ASSy>gx~g]AC^,SxB_L^,Ah1{__eg]c~S7}B.CTghuTMug8:C]i:~ih_./Si>_SS\T],/2Cx8n_{{S66n~~gMcc{_}6Sx]1xS2S5S8c6T5}SABTL}^]A\^x1grw{}y2x_\Rg^/]]_aRa8_R]2B,Q^RB_LS`Ch1_1]T6eni1}h}1S::TA/1c~Sirrc6x1ecSgcC`x8<_{yRc^,/]RRgS}TTcn66gMcc{_T:T2RCR\bot\crrgS{]^y_SQbt_\-ASxBSL{`Rhx_xxST:a1T8yC2SQ`A{6hW3p2gM2T]]]_ch_c/BSSxuT{}~c,inT1T~]]\Bi11iSg~c_/QBghx2TT86]RRA`R2^cnc__2T}iin^A}~6inh6_C./C6Lr8c_x_heS2SiugMhRAyTyhpS212gi{~{CCC]U鴭_2Xܓ72]hR<>]1g1yS^R`/Q6hC^{T^Ty_CA\B`AiW3wKB.Tn~68}{SS\g/_BAhh{6rgBBR8/_hy^y6]6S-S:cT_iM}}~e}SQ}hS{TCx6TT__T]]h:R/STg1-Re~caR/6a^1yu]gC1x{6cgyBB2^]}TA:=inSAeSgLyy{nigC_}\]~_/`{ng~SR\CQac.]=Bx:_S{.BR8QSc_c=cRc_-_:}{^}__{TS^/yu2S_BS_{2Sc^A^:8h\.2S6^.]:gSe8S}Q`S_RCyi288BBC1x{~g}A{]]}yAAh=i2Q}c]cݬoUiyB/cMhLic2^boo}/AtɴϻXg]~>S-Sc{^e>}2]_S21/Th_{_QB_^]x^{CBCghV,wwa^{g6cx{{~8AB:gTh2A\g8\RRge1SC^Tc1y2,Q}1c}y:hc~:MTCSg8Q]ih~g~yL~RS]hiQ/2xc6/RncRSn:\22\A2_^x/_8/.cax2{,Qc<=]6=S1Cr6CSLC~Leec2iTCBye~>h{\2_}~B/:h~=_BQSyBRCeh\2T1SccBxy^,Q}Cy{2~c_y~:n_\n2cy-hgh:h:ghch]a{ABS]}hhhS`BihyR{L1{]\A2_2xQy:Q/ggC2y,Bcr:RTh1\9-٭g{6^\chLa]}tܭ`cB]^k~1y_,Q}CyS26T^_}8i]RnS~c`a6~~=gc6ghWrSQAxCBC}_Shch_hB.]~8\c_cc-xriSBA1xAxT2_gyg8^g2.QSg{ySgM~n>_{e}A_h]2eS2SxAxS}ggcc~^STR}n2]T~c1]yTSRg8{gc=__/_}>h.Qyy6^]iTT}^]]_~}xcS.{iS_\,Cn^_~^\t˛eg`2ry\C2^x2~yT_;Tgchϴ~CSh81^gS^_1C_{acyyT\1a1~LS^i{yhS\C2yy/3Lgg:cRB\B/]S^6C2hSS1.1hyyeSxS}y8Lx]~1SMr:}ag}}}2Sg}}8TcTny\hB,Q2y2R\2RQR^n~gA^8_T]/CAR_Bxihg{T}___Cx{{Ta^:^BggB,B_Ty]1y]A]{rhhR{h^T^QCBx.x>8h8}S2T~y:}]]{Q,1Tx,662^hfVQ2cQ`cegig]C1R_y_X譻t2a~}_cxSLrh~STc___RC_^]c=hcSye_i]/}T.,/]}}Q3?Z_2cg}y{{B\:^-C1ThS]MM6y8}{__yur]Cc]Q}:<=>gig]]ccTy66Mnr=C~M}\C~8gh1{_B21]cgS.]}cc}8iig`QhchcT:{{cgc\gSTT~_Ay>g-B}S61A}̶~]g8R`1SS8:~6c}\]IӶ{2h:g6}TʹbQTi=r=}~TAA__]]uySxQxxS]B]}S6^8J6}cihS_h=}]]y8TTg{ca\]{_g1`C_yeaT88wraRg~i6c_-/RRAx^.2hT.B^^C^_/2r=T]e\]gcCci:~8Q^aCx}8}}nnTi}2une}aS\^ihTcCǽ׌V]r:~2h>g~}Wx{gyyhc8gR^hSCT{B,QhgRxyS2Th~>wLaxhMhm-uLS//2y\{nSS2_RCA.xyB]6~c,C]cychcTccccxRgS`A\_:^Qc\cn=_/S~1y{y^BQhACi}^c:~^By_]g6aie8{B_nnx18y=yB{hg8=A]hc6~{{STS]MMSS=]xucB~B,xh:~g~cSAQQ{axTn{SL]]\\B.xSBx6aa`/^x_ecg8TS}y{SAB}]-CxSi^BhaB_>SBye\1x]\..gR]ng_ggcaT^^TgT]]ynnR,/An_B`Ry_M}{<6_A]TQ,ReTX˶^.S~]S_Sh}\RcTxT鴶yT_ShR.B\_~h}Xy6BBASn6Ch8]_c=_CSR\S{R,Qc6n_,^8d>n_RQr~]B6T}_\Sc{nuMMheT~2hhygSyaS}_-`_xB:hn}T{eh6~n_xT_:i.Sc_{a{^]gy_S{nh2x^{yScMM}_SxT2_2RT>y_a2A}]:iQAc2/2h{^Sa{SRx6^{TTn6]A,B6~Gvķ鬌R_8=g12BS}ctt tc2/S6gc_1AC_S{hMiyhhcACB3uZh6Thu62/Ry~r:ciirT,.}_A-6]hT5/A{T6]^6~uy{8-,]yRB1h_..x^BRiyyhrrg,.M{B_6Ty_S:hhiuSA6_AexC>h:CR_A6ihaSi8ARS2yaCRCCx_y]A-Ac{26T1`\ch8iL{,.g:_-B}xhncTQR}aLSBccSnnM}\1{RS:y/QT{c9/>CA{nTQ2}R]hgQBT1.hc~S{{C{1gn2SL{nh}62-]e_C{i_S~Mu>T/B{g1^nR}u_.RacCBAScTTa:hygLiggT}}S~n8^2]16}^~_-/C^cTQS{]rSAA_g=LrhBQ{yAgM6^^{_}]eayu6_Ra^6uSVŻmESR^2^\Tng6^yrcC=m3CB/B}gyh6/`_:}cTA^gSyihhc^}TA6ha2_{hr]xgQ/6Myx2T6_B_1BCy=h_A\1Q^hyya1^g^=CcrLc/]cQ]iAx8n:~g86x.^TRR_T>TB-Qhhy6SC/A}TCAy~^]:nhT^Rx~n2Q6SC\CyT1:i/`/_~c{c//}>8gR_hTcinCBQ18}y./6:tv=hch]2^]nSC}1y:RycxT{cyCyhTRR{1.y1]e^2~g_{c~R1^-:S2y^~L6i}_6^6gA_T:n{Sc^RRxic~~8S~iS}ig]_:}^{CTn1B}y{ng\,,]}Q~8/,\h:c^^]c1Qx{SR/B^]/}hRC8g_R6Sa=1ag]6:ya}1ciaxxT^/AcS}<_26g_{a:aAR.,/gi21_xchga{hT1Sn~rc~T]1^}66g_~>Tc8T\/x~_]SCyiJvnB,2nn~RR\S1Qx~6]BRySuLyynnLS1y禊~8~Sh_S8yA]8n__1xytXǴ^{]c~T:~}B.^_S~cn^h{iL6\-xa~inr8n1QB_ya2B^S2C\caB/~g71_1~d0[x\1y_AC~<62]TT2B_SAT_Bx_6.Q_2}>eRACS^\cic]]T=}^ByyRa=ABn^C~h68e_yT^Shi8_Ry~6{_i:2SS/ACQAx\^__c2.Cc]BBcnncnT=a{a~BQna]r>7hnWkl6lqVA9cwTTS~=_`S]B1c:BA}y~h]1_6~_ShhS2aMc*tݤC\MS^hc_xy82]_g~{ynghSRa]_r<~~cR\1SSCxST:^R~giu6CcS/A=hCS_R^B,]2RhgT}8g^]Thy:ic^S8h}yh<8_Qhc1}c^:^aMڬtt4{~}T.{6yCR6h2ghg4yunh2]Scc2xS6g=CB}ch~1a_`QhvWBcy2_a~_2chgk,y{Sg}Tc:nx\:nh2xy2_h6_:}^{R]Ti{a~x\yh]yShL>nuiC\~ya}T{Q_2x6ih81}hh=8{\{=S__x{T2xcxyAcS{Rg2\TTSQ2BQ1x^A_{/-Bc2\}cCAB.S}{6uhAQymdB72{c~xSaahW3wSx]1y{xS\Cn8.^yc:y.,`B/C_xT2`Sh1y_xc{Ly\1>T_/x^_nnihnr}hiia}x~TxyS6hc~rS{neA^n{6SQ}c2T=n6Rc8_~xQS8hc{R.-RB/T62-,.,Ch}iT}{^A\_gA}^Rx]cc^T\CMhQSce=gQ,-B/CSxS1]gR_]A_x~:x/Aha]1`AC1>na~}n~_6~iuhTyRc{Rghn{_c6n_Sn8\{rchcxirhygt~8^cR/]6eTSA.-RBQc~8^-,-Q,A8Tiyccyx]}h\6MS2S_66{e^_uh/_iT6B1T^TS/yi2cy^g{yB\:cC,/BR>:hh~Y6cc22Ay_ATV[L}cc622-,R:e~Q1cR-,.n:/6L_RaT2~{g6{S_^SM_\R{=rMhchg\C}]A12c=~xCx/Cg}~86~{L{x{iSAS]Bx]T]{hyS1CRAS_2S`.~]A}ccMnS.`{}y}_hn6B`x[wyTMBCTR6c]cSg~{e^hxRSM_\\_8iMTSh\C}]\BR^g_BBQ`Ac{}6TyS=n_C_:ig_5SRRxyxSh{Sxx]1}g~e\igxTT}_-_{ShcSL8B`x[wur_Si\.S8c~8gS\{-y>~cL}A,,-g.A~i~_cS8~{hTM{_ea]]}:i>nS]{yB_\BC1Shy\B-Qb]_{__^=]A/Yc_BB2ch~A`S{ir{1^]{2]Tg=yTa}2~{Cxiu^Ax_\Qx_]68yThgx]CBQRAQLuS{yQ.S2CiBQghAx^}T^\/Sn:2ScyA,.\}i=Le^~c2Tnx^{a1/8n2xT_BS~in_y~cR,.\Th6x~c2}^_c~]/8n2x7SA_}hgyQ1]g]Rx]˽Sc6y}6}_e}]yMny12{\Qx21}i6_g{gh^8B,B-2_a}2ySg=yQ-0w=cQ}g=}g{>hy2>gy}BR^Si8^R\aiy_{6TCRT^Tn~2M:hhTi{=aB]}gg8c_RQ.C{{__cSR^\,Su~e1/_xBinR\ac]{cA_S]yS2ncS6~8_B^A\1Q]>8_}y\h=R]8]2}e:~AR~h<{T]cMhge8Sx6ic_cBx_Sh>cxCR6g{6gBT~6gy:cc2C6_CQThcTTi=]Be^{`,Brn]y<1y{A{:{QCc6g8^hAhhy\{cC^TMSr}S:}{=hch2CTS2~L:C6T^a]/c_RSSyu8incT621a:ngccx_hQ/nT8hR~heS8TT_]MT^Bch6}}]Ah_S~`,BL2}=]}u}Cy:{QCa~gnc6C~gegAh}R}j|Vm{Ar}S:c6>hnc2aTnuy:Rc=}ehB{x2^}}}Tcgx/ScCBCcy_:Mhy,B1cnaww:nh2_i6]T}ch~2}\B8h2RuT1ih^A/^6S}S6g}`,hS6T]gB,/^iLc]}T]Scn\/x1AC}_A]c^xC|jXXuRQy]88>RC~_Tyn6ca~<]Q{~1\xcy_ihgc<^,QCy=TO-Mg.:]B]{The6y{nMi^B}a}::yBAc2]BRhy2^STT\Be~c{}x]g6g6S^6~_}xhBBQ.^u:T1ca\BTiT{S{g}{T1^x,/_y]6_g<^_y>a}BB8^h6y~SSy/18x1aSc2]\_~8Sc~B.:^1_{6S^]inhCQ{yS8gTR1{x{BAeT_>]STTAh6Tc]\:iT{T{1R{8Tgi_iBQ.`~_g_~_}aMugR82hL8~:y^_.x:]]gS6S^\_~8Sc~B.eux/R]^ayS^2LxzmUg~c\18{x{Rx~}r{`ag81Rhgag<2Ci6y}y]Ry~{61g//.-~hRk3KM^{T_8>ySyx}cCAx~g1]SA18SRAAygy/Cn{B^~8h_,,18{26}:y2{RBAS{Qxe~cx,{=2SS^ccx2{xAC_a^1y^82uh{SQ_nh^R_yyy]anhyR]g8}:1\RC18~\/x}cCc>e6ih_xB__2>xTniT2~_2^AS]Si_]T8h]12\Ty)d}R]TC^hSRBB_g6unB2u~\{hT-,_y\2Teie^BTa1]{`/1C__CR^\.RTS1^^^QBS88S_cc1QxT^}nSC8~y2_Sc~T1QC8c\R]QxhcC]2]gg]A1^1x=:B12}a]BQ]._RB8h~CB_T_Tcga-~SRR}nrung_R1^_1\hAyn~ua1}h=e^c_S>y..Cgx_}2^2A`\{^C1]^yhhyScc1Qxy^cr6.x6cT21]^yT_1B]h~\R1/xhcC]]xa=~]R]__ST{A\gcC\x.xhc/CgTaLyBCha/T1,^n8]TgB`xS~gg^xRch/x~rrTx{hin~28@wuh_TyS2yi>6cgy{R{gm,nh=}SgecciuucQ^cRyL=c^xS\/2u_/Q]~T/B:hcyxASg_y6}hh_8R^A]CA61AR]6^c{^aex_{~ARhTTA,-Bc8_{Tyh~xR2Mi^B.Q\^g:_A{c_i2eS}cRRShrTh=T}h21}g}}:MrT.1}CTcABci{]cyA-Qyn=~T{nMh{R~wCBB_8}/iwgrnc]xci}~}8Q]~\]2^xhS1^{h{6ThS~8<^_nge^`BRc8_Syy~CR28i_AQR]y<>_2:{^:h16chR\_uc~=}S8h6}:MrT.CyR5TAA6y^cSB-.S>T^^}^}hgiyxxS2iRRBAy^ih_-Q_~6y6^\T6~T{^{n{A1^..g{_}c6cT_CM:eMwn}6y{_^cT]2S}engL9B/{~S^1}>A`Mi},giBQ21gS-cMihcT<{ThMn7. *?Z~Tx2_{}yeiS_i}6x{}\Q}nTAQ^_/CRAy^1hhaic_B}h2cg6S_}{S{.,CS{TT^]^S2/,QhTR/xTcC]7AQx/_uiQ^TSSyL_Tc^Th{/Q_ghnh]RM_-.rR.^M~QS8ehQ1iTx2h~_]~i_QBB^_/CxRcnnS2i<_QAcCC/2ui/^ySSyL{gh{ac^8:]`-Qx~:gcgniB.Mi2`.ThxyeSS}}{Thh}{QB].Q_yA-_y1xR]Cx^R_SBxiccMi~{SeyS{1_i_g82R/c{`C}A{}_y8C.]S6M_/g^Rn[<\/{h_}:ShA/.R_S2CC2>^82}g82]=g_:_``Qx~=agQ`gM2/}g~\^SC]T}{ThTS/Q1.Q_S-^_xx\^xC]R]1.8}gr:y<1x}cT}2{=S~g_xB\8~/2CRSy^Wz#""?w@]Sh]^h_Q1]CQR_2ex`]S]]ccxc}]A1h:ehe~QBinh~n2TT1]T<6C^nx^Tiy^<-/:SxSie}wiTSRBS8yR]6{cwLScigSaeCRA]B^Q/C{ia~^AS^TyB]=c]TT^1\Q`-C_{T]cg1}x6T~=yy=}\]]xBxTy^`2y_2gg]ce_xAxi=8hggBBhaaMMygLe_SaaR^nx2ci]BM6Qc>TCTh_^8~^cLnT26MCShc6na.Sh2CT}y}RT2Qnh]S:c}n>:hSA]y_SaC/B,,agxgMySg8~n8yh]`.B/Be>{{c]i~2iyR~~gy{1.R\]gScaxgyAnrx1*O;1@[GR^iMcS{^^}]1hcCy~^1Ryi6AC6gghMB,1c\.ShT2282xiiThi_xAx\`/TyA]cy1~/`2_Sn~6}RSuS_]16TASnc{}a6nicB^}B2=chMy_c~~S2cgyx28^.1cT]x6{]_C^~}_hg^gey~:gSS^2`/S^Tx8__^6nyy=CC9h8cy1C{T]_{x]^}c1/Ry__~2-BT_y:.Q{R-_nh]AR.y}2}/BAS6T6}yT_\_{#""?w@@?Yh_c}ii{2R/Q2SRQ6>TxAcCS}/Q]B^aMTSLR2h=c6g_1B_]/Bgcc2_cg~_h=]:x:uC..Tu>A]<7A^^{{1T]`Ccch;~{Sin1ChnSB.A6~__^1}S1c}{1xS4^2-Rg\hhg<}_^c:gyxSMMyQh{~{hS2R/Q^TxQ8SB/22QCcQB_B2c2xgQC8hh_R/161.Bhcc2_Tch{xcLhRh:xugC`.TnnR2r~RST}61`\}y{_^12ngBBc:^B/Byy5ySS8cS:{x1S2_S-xRh8cS2ciyR_MSQT~_cThS2R/Q2S]:ry\BySB]6QB_B_gLSC:7/\n961A._:_QBhg9S{c~6S=+:͇lS?L żڊwΛcw4-0bfaϓ >1;ҭɲkahϼW_W؛645ltkж E?Eûmlk ~}ή$6:5]h\KdKUUU}}~}on$#$¦ǥsrJeJt}tțčqo#."Ÿɟψff$># on᱖ޕro>t>=  %+%ۮ~{KI*q)O5YT0,+(#!`.@T?fe31=:3/# L   }|ol2d1=TP\Y94? C_CtqROBlAc_-(p6旀~VS0s-omYU,&+)4~3PoOd`JE =V<{yzwXUEB1ywom+a)  LoKҁWUblobby-1.0rc3/data/gfx/blobbym2.bmp0000644000175000017500000001724212042452377020464 0ustar danielknobedanielknobeBM6(KYl  ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"!!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#""!!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,$#"""!!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,$##"""!!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,$$##"""!!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,%$$##"""!!!! ,,,,,,,,,,,,,,,,,,,,,,,,'%%$$###"""!!!! ,,,,,,,,,,,,,,,,,,,,,,&%%$$###""""!!!! ,,,,,,,,,,,,,,,,,,,,,'&%%$$$###""""!!!! ,,,,,,,,,,,,,,,,,,,('&%%%$$####""""!!!! ,,,,,,,,,,,,,,,,,,('&&%%$$####"""""!!!!   ,,,,,,,,,,,,,,,,,((&&%$$##""""!!!!!!!!  ,,,,,,,,,,,,,,,)('%%$###""!!!!!    ,,,,,,,,,,,,,,('&%$##""!!!!    ,,,,,,,,,,,,,)'&%$##""!!    !,,,,,,,,,,,,(&%$#""!!!   !,,,,,,,,,,,(&%$#""!!  !",,,,,,,,,,'&$#""!!  !!",,,,,,,,,,&%$#"!!  !!!!",,,,,,,,,&%$#"!!  !!!!!!!!!!!!!""#,,,,,,,,%$#""!  !!!!!!!!!!!"""#,,,,,,,,#""!!  !!!!!!""""""#$,,,,,,,,"!!  !!"""""""###$,,,,,,,,!!  !""""#####$$,,,,,,,,!  !!""#####$$%,,,,,,,,  !!"##$$$$%%,,,,,,,, !!""#$$%%%&,,,,,,,, !""#$%%%&&,,,,,,,, !!"#$%&&&',,,,,,,, !!"#$%&''(,,,,,,,, !!"#$%&'(*,,,,,,,,, !""$%&'*,,,,,,,,,, !""$%&(*,,,,,,,,,, !""$%')+,,,,,,,,,,, !!!!!!!!! !"#%&(),,,,,,,,,,,, !!!!!!!""###########"""#$%((*,,,,,,,,,,,,, !!!""""""####$$$$%%%%%%&&&&%%%''(),,,,,,,,,,,,,, !!!"""####$$$$$%%%&&&&&'''''(((((((),,,,,,,,,,,,,,, !!!"""###$$$$%%%&&&&'''(((((())****(,,,,,,,,,,,,,,,,, !!!"""####$$$%%%&&&'''((((()***+++++,,,,,,,,,,,,,,,,,, !!!!"""###$$$%%%&&&'''(((())**+++++++,,,,,,,,,,,,,,,,,,, !!!""""###$$$%%%&&'''(((())**+++++++,,,,,,,,,,,,,,,,,,,, !!!"""####$$%%%&&&'''(((()***++++++*,,,,,,,,,,,,,,,,,,,, !!!"""####$$%%%&&&'''((())**+++*****,,,,,,,,,,,,,,,,,,,, !!!"""####$$%%%&&&'''((((())****)))),,,,,,,,,,,,,,,,,,,, !!!"""####$$%%%&&&&'''''((((())(((((,,,,,,,,,,,,,,,,,,,,, !!!"""####$$$%%%%%&&&&''''(((((((((,,,,,,,,,,,,,,,,,,,,,, !!!!""####$$$$$%%%%%&&&&'''((((((((,,,,,,,,,,,,,,,,,,,,,, !!!!""#####$$$$$$%%%%&&&'''((((((((,,,,,,,,,,,,,,,,,,,,,, !!!"""######$$$$$%%%%&&&'''((((((((,,,,,,,,,,,,,,,,,,,,,,, !!!"""######$$$$$%%%%&&&'''(((((((,,,,,,,,,,,,,,,,,,,,,,,, !!!"""######$$$$$%%%%&&&'''(((((((,,,,,,,,,,,,,,,,,,,,,,,, !!!"""######$$$$$%%%%&&&&'''(((((((,,,,,,,,,,,,,,,,,,,,,,,, !!!"""####$$$$$$%%%%%&&&&'''((((((((,,,,,,,,,,,,,,,,,,,,,,,, !!!"""###$$$$$$%%%%%&&&&''''(((((((((,,,,,,,,,,,,,,,,,,,,,,,,, !!!""""###$$$$$$%%%%&&&''''((((((((((,,,,,,,,,,,,,,,,,,,,,,,,,, !!!!"""######$$$$%%%&&&''(((((((((,,,,,,,,,,,,,,,,,,,,,,,,,, !!!""""""####$$$%%&&&'((((((((,,,,,,,,,,,,,,,,,,,,,,,,,, !!!!!!""""###$$$%%&&''((((((,,,,,,,,,,,,,,,,,,,,,,,,,, !!!!!"""###$$%%&&'('''',,,,,,,,,,,,,,,,,,,,,,,,,, !!!"""##$$%%&''''(,,,,,,,,,,,,,,,,,,,,,,,,,,, !!!""##$%%&'(,,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"#$$%&((,,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"##$%&(),,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"##$%&(*,,,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"##$%&(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,   !!"##$%'),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"#$$&',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,   !!"#$%&',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!"#$%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !"#$%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, !"$,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,   !",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, !,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,blobby-1.0rc3/data/gfx/font21.bmp0000644000175000017500000000337012042452377020062 0ustar danielknobedanielknobeBM6( GZI6H8!0#     CMDOjT0F5%=+#@*#I,+[6)c5%k3!o.k&h!^ckḑĭnvWy^Oy[KYQa_u`vYnNb5J'9& dje֭Ŵ{}Ǧ̨ɣ{Zp5HԻĜ7B<%& #+%&.'#)&# 766ֳ¾FJL ү``hYX[ʱxoŸŲ̓~A8Gulv䲐W=[ ϢՇ\6$:zk|pd>i "!ԫߑN57ph\+`  @3Aʼn~@#$’ISW\F\T~$')ѓp5Z\ `G_A~#$%͇`-HJWH%7)8v3i!m]4  7-7~l0qblobby-1.0rc3/data/gfx/blobbym3.bmp0000644000175000017500000001724212042452377020465 0ustar danielknobedanielknobeBM6(KYl  ------------------------------- ---------------------------------------------------------!! -------------------------------------------------"!!! --------------------------------------------""!!! ---------------------------------------##""!!!! -----------------------------------##""!!!!! --------------------------------$##"""!!!!! ----------------------------%$$##""""!!!! -------------------------%%$$##""""!!!!! -----------------------&%$$$###""""!!!!! ---------------------&&%$$$###""""!!!!!! -------------------'&&%%$$$###""""!!!!!! -----------------('&&%%$$$####"""""!!!!!  ---------------)('&&%%%$$####""""""!!!!!! !--------------)('&%%$$##""""!!!!!!!!!   -------------*)'&%$$##""!!!!    !------------)'&%$##""!!!    -----------)'&$$#""!!   !!----------(&%$#"!!    !!!---------)&%$#"!!    !!!""--------'%$#"!  !!!!!!!""--------&%#"!  !!!!!!!!!!"""#-------&$$#"!  !!!!!!"""""##$------$#""!  !""""""###$%------#"!!  !""#####$$%------"!  !!"###$$$%&------!  !""#$$%%%&------  !"#$%%&&)------ !"##%&&'*------ !!"#$&'**------  !!"#$&)*------- !!"#&')+--------  !!$%&')+-------- !!"#$$&'),---------  !!"##$&'*---------- !!!!!!!!"########"""!!!""#$&)+----------- !!!!"""""""####$$$$$$%%%%%$$$#""#$))------------- !!!"""#####$$$$%%%%%&&&&&&&&&&&%%'((-------------- !!!"""###$$$$%%%&&&&&''''(((((((*))()--------------- !!!"""####$$$%%%&&&''''(((()))))+***)----------------- !!!""""###$$$%%%&&&&'''((())))**+++++------------------ !!!"""###$$$%%%&&&'''(((()))***+,,,,,------------------- !!!""""###$$$%%%&&&'''((()))**++,,,,,-------------------- !!!"""###$$$%%%&&&'''((()))***+,,,,,,--------------------- !!!"""###$$$%%%&&&'''(()))***+,,,,,,---------------------- !!!!""###$$$%%%&&&'''((())))***++++++---------------------- !!!"""###$$$%%&&&'''''(((())))**+++++----------------------- !!!"""###$$$%%%&&&&''''((((())*******------------------------ !!!"""###$$$%%%&&&&'''''(((())********------------------------ !!!"""###$$%%%&&&&&'''''((((()*********------------------------ !!!"""##$$$%%%&&&''''''((((())**********------------------------ !!"""##$$$%%&&&'''''(((((())))***********------------------------- !!""##$$$%%&&&'''((((())))))**********)))*-------------------------- !!""####$$$$%%%%&&&'''''((()))*******)))))))-------------------------- !!"""""""#####$$$%%%&&&&&&'''((()))**)))))))))-------------------------- !!!!!!!!!!"""""####$$$%%%%%%&&&&'''(()))))))))))--------------------------!!! !!!!!!"""#####$$$$$%%%%&&&''(()))))((((--------------------------!  !!!!""""""#####$$$%%%&&'''((((((((--------------------------  !!!!!!!!"""""###$$$%%&&''''''(--------------------------  !!!!"""###$$%%&'''()--------------------------- !!!""#$$%&'(((---------------------------- !!""##$%&'(()---------------------------- !!""##$%&&()*---------------------------- !!""#$%%&()*----------------------------- !!""#$%%&(*------------------------------ !!""#$%%'(*-------------------------------   !!""#$%&')--------------------------------  !!""#$%&')---------------------------------  !!""#$%&'----------------------------------  !!""#$%'-----------------------------------  !"#$%------------------------------------  !!#$&-------------------------------------  !"$--------------------------------------- !#----------------------------------------- !------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------blobby-1.0rc3/data/gfx/font22.bmp0000644000175000017500000000337012042452377020063 0ustar danielknobedanielknobeBM6( =H ,5 9 ]$RL O= !# ? ? #!9y !!gRz> ?-  L;rM*.t  /tW@w6 <& #6&5!TENCC l 8|V/G=|1G( @XW%#V_1X;LX(6 [, Pvve>M[?YD^Z C 07_fTX)0#J,Jr`cCPT  jlKF#j( 9 $&  w|HI*/4#DfMWf@OJpo>8"# +4,lm?>577U=S>kh83  MqMfe=;3s5=\B}a]C@;;3H4xv^[_^|yWU?=6L7XoXnk*&  مpmHF6X7~}@<[[]\;a;]\VR -2,ЉutGjF0;0nl(#~|NJTmU nm>Iblobby-1.0rc3/data/gfx/blobbym4.bmp0000644000175000017500000001724212042452377020466 0ustar danielknobedanielknobeBM6(KYl  ,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"!! ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,""!!! ,,,,,,,,,,,,,,,,,,,,,,,,,,"""!!! ,,,,,,,,,,,,,,,,,,,,,,,$#""!!!! ,,,,,,,,,,,,,,,,,,,,$##"""!!!! ,,,,,,,,,,,,,,,,,&$$##"""!!!! ,,,,,,,,,,,,,,&%$$##""""!!!! ,,,,,,,,,,,,,,%%$$###""""!!!!! ,,,,,,,,,,,,'%%$$$#""!!! ,,,,,,,,,,('%$##"!! ,,,,,,,,)'%$#"!!   ,,,,,,,(%$"!!    ,,,,,,)&$"!   !,,,,,&$"!    !,,,,($#!    !&,,,%#!  !!!&,,,#"! !!!!"&,,,!  !!"""#',,,  !"##'',,, !"#''(,,,!%&'((,,,"#$%'(),,,   !!#$%&(*,,,,   !"#$&(,,,,,   !"#$&(,,,,,, !"#$&,,,,,,,  !"#%',,,,,,,, !!!!!!!!!!!!  !"#$,,,,,,,,,, !!!!!!"""""############"! "&,,,,,,,,,,,, !!!""""####$$$$$%%%%%%%%%$$'&%&,,,,,,,,,,,,, !!!""""###$$$$$%%%%&&&&&&&((('&&,,,,,,,,,,,,,, !!!""""###$$$$%%%%&&&&'''')))((',,,,,,,,,,,,,,,, !!!"""####$$$%%%%&&&&''''))))))),,,,,,,,,,,,,,,,,, !!!""""###$$$%%%&&&&''''())))))),,,,,,,,,,,,,,,,,,,, !!!"""###$$$%%%%&&&''''())))****,,,,,,,,,,,,,,,,,,,,, !!!"""###$$$%%%&&&''''(()))***+++,,,,,,,,,,,,,,,,,,,,, !!!"""###$$$%%%&&&''''((())***+++++,,,,,,,,,,,,,,,,,,,,,, !!"""###$$$%%%&&&'''(((()))*****+++,,,,,,,,,,,,,,,,,,,,,,, !!""###$$%%%&&&'''((((())))))*******+,,,,,,,,,,,,,,,,,,,,,,,, !!"""##$$%%&&&'''((())))))))*)*********,,,,,,,,,,,,,,,,,,,,,,,,, !!""##$$%&&&''((()))))**+**+******))))))),,,,,,,,,,,,,,,,,,,,,,,,, !""#$$%%%&&&&'''((())))**+++****))))))))))),,,,,,,,,,,,,,,,,,,,,,,,, !"#####$$$$$%%%%&&&'''(((())))**)))))))))))))),,,,,,,,,,,,,,,,,,,,,,,,, !""""""""#####$$$$%%%&&&''''(((())))))))))))((((,,,,,,,,,,,,,,,,,,,,,,,,,,"""!!!!!!"""""####$$$%%%%%&&&&'''((())))((((((((,,,,,,,,,,,,,,,,,,,,,,,,,,,"!!! !!!!!!!""""###$$$$$$$%%%%&&&''((((((''''',,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!"""""""""####$$$%%%&&'''&&&&&%,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!!!!""""###$$%%&%%%%%%,,,,,,,,,,,,,,,,,,,,,,,,,,, !!!""##$%%%%%&,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"##$%%&&&&,,,,,,,,,,,,,,,,,,,,,,,,,,, !!""#$$%&&'',,,,,,,,,,,,,,,,,,,,,,,,,,, !""##$%&''(,,,,,,,,,,,,,,,,,,,,,,,,,,, !!"##$%&'(),,,,,,,,,,,,,,,,,,,,,,,,,,,, !!""#$%&'),,,,,,,,,,,,,,,,,,,,,,,,,,,,, !""#$%&'),,,,,,,,,,,,,,,,,,,,,,,,,,,,, !""#$%&(),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !""#$%&(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,   !""#$%'),,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !""#$%',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !"#$%',,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !!"#%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !"#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  !"#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,!,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,blobby-1.0rc3/data/gfx/blobbym5.bmp0000644000175000017500000001724212042452377020467 0ustar danielknobedanielknobeBM6(KYl  ---------------------------- ---------------------------------------------------! ------------------------------------------"!! -----------------------------------!!!! ------------------------------""!!!! -------------------------##""!!!!! ----------------------##"""!!!!! ------------------$##""""!!!!!! ---------------$$###""""!!!!! -------------%$$###""""!!!!!! -----------&%$$$###"""!!! ---------&&%$$#""!! -------('&$#"! -----)'%#"! %---+&$"!  %&--'$"    %&--%"   &&-&#!   !&&&'#!   !&&&''!#$%'''( !"#$&'()   !"#%&)(   "#$&()-   !"$%(--   !"$%(---  !"$&---- !"$&-----  !"$------- !!!!!!""""""""""""!!  !"--------- !!!!""""######$$$$$$$###""!%$%&----------- !!!""""####$$$$$$$%%%%%$('''&%%------------- !!!""""####$$$$%%%%%%%%&(((((''%---------------- !!""""###$$$$%%%%%&&&&))))))))(------------------- !!!"""###$$$$%%%%&&&&')))))))**))-------------------- !!!""###$$$%%%%&&&&'''))***********--------------------- !!""###$$%%%&&&&''''(***********++++---------------------- !!""##$$%%%&&&'''((((****++++++++++++++----------------------- !""##$%%&&''((()))**++++++++++++++++++++------------------------ !!"#$%%&''()***++++,,,,,,,,,,,,,,,,,,,,,,,,,------------------------- !"#$%&''((((()))***++++,,,,,,,,,,,,,,,,,,,,++-------------------------- !"$%&&&&&&''''(((()))***++++,,,,,,,,,++++++++++-------------------------- "$%%%%%%%%&&&&&''''((()))**++++,,++++++++++++***--------------------------#%%$$$$$$$$%%%%%&&&&'''(()))****++++++++******)))--------------------------%$$########$$$$$%%%%&&&'''''(((()))****)))))((('&--------------------------$##""""""""#####$$$$$%%%%%%%&&&&'''((((((('''&&%%--------------------------#""!!!!!!!"""""""""""""######$$$$%%%%&&&&&&%%$%%&--------------------------"!!!  !!!!!""""###$%%%%%%%%&--------------------------!   !!!""##$$%%%&&&&'--------------------------  !!!""##$%%&&&&''-------------------------- !!""##$$%&''''(-------------------------- !!!""#$$%&''(()--------------------------- !!""##$%%&(((---------------------------- !!!"##$%%&())---------------------------- !!""#$$%&()*---------------------------- !!""#$$%&()+----------------------------- !!""#$$%&()------------------------------  !!""#$$%&(*-------------------------------  !!""#$%%')--------------------------------  !""#$%&')---------------------------------  !""#$%&----------------------------------  !"#$%&----------------------------------- !""#%-------------------------------------   !"#--------------------------------------- !"----------------------------------------- !------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------blobby-1.0rc3/data/gfx/ball10.bmp0000644000175000017500000001206612042452377020026 0ustar danielknobedanielknobeBM66(@@ ~vuwgfhzwzeceqpqhghfefa_`YWXc``dbba__poofeewvuhgf$$#rrpggeGGF~~}iih  beboroz|zegetutehg {}}eggvww`aaVWXbdgzz|eegxxynno]]^|||{{{yyyxxxrrrmmmjjjhhhgggeeedddcccaaa___[[[YYYWWWTTTOOO333 s))$%$wo8@[%$G?```(G@XwFFFFFFFG?``(344F``w݄ 222߀3(䑑`) `ZF` Xssttk]Vv(33F`ww ]] ٗt` Fv`(% ٗkVۘ3ZF㊊v`(]kܘVۘ23`v3FFF`(G)kt2Vٗٗ2FF 3Fv`(2k ٘ݘԖ25ؗۃ2F`Gw?]ij00ޘٗۃ2F`Ahhh0hh0000jؗ2F`Җh/h0hhhhh00 2Fv`GӖ\.0hhhh0֗ؗFv`(G\'RRRh0/h0֗ؗ3v`sgj0_hU9R0ۘ22v/0Ah_džRhRRڃ2FF7v(G)0fE_RRR/j'ٗFFZ7``G&---~՗11ؗژ 4v`Q/|--Rf-ٗ1ٗۘ`Qg-R---hژ``)fE.\ƅ…Q_|--g00h0ٗ7`wg-RR-~}f0gh000ؗ3F7XRQ_d^~f-}hRR/hh00ؗ4`?$R^,dd-fąRhh00ٗFZw#_f--hhh00ؗ 3F(GX:ddRgRRh00iؗ2r7Y@üPOOO|-hh0A]3F?XG>OPOOŅRhU֗25 ONOŅd.hFr`$)ǷO¿.g90i1ژr`[vMNNLLLLL{…|-fR/h0iؗktGmccccLL,dfƂgh0 '֍k{ccLPdNOPdg/90Aj1ـ LccNP}Q9A2yKzcNNLOd~RUuzOOd|eRUلtIIIIxzcLOn^eƆzIIxIxzcLOd|Uԍxyyb*IIILzzLPf'IIyba**zLmP-_9ĮabzIaaIxzcLNOgIxxIIxxx+yzNODdfIaaaaIKIIxIxccJKccNnfaby+LcLL{P:PxaxIIJczz:CJzzMCaHxIc**Ix{{IxLLx*xIaxII+Ixamblobby-1.0rc3/data/gfx/font23.bmp0000644000175000017500000000337012042452377020064 0ustar danielknobedanielknobeBM6(  G/CI  $%g "V DD--##S%%F/2!"_. VV==55~ //>>%&uG KK|ccJJ C->>BB**i '!!+wxCC EETTII,,Vaa5BBZZ^^EE 10:yw0/d&&XKKYYJJCCy::bWU4..BBGG..l__ 21?eb,+h..2277EFF{@@ ] |xp>:.+*$$&& YY MJkQG%$i2!BB99 Q 'g\81EX&&>TT  g`~n@0j"y (ZZ22 ?#v]I,))?ZYi]~gN6 [Z-, >#2spT9e##AYV  q[iV1* P _]A<=F8fusG= 6@=ivq?9-*oܐ]V "{rUMjTqq*][}F?|~D rR`\ blobby-1.0rc3/data/gfx/ball11.bmp0000644000175000017500000001206612042452377020027 0ustar danielknobedanielknobeBM66(@@ ~YVY}z}/./pnpgeg;:;lklecdywxighheeljjhggbaaecbedcmlkqqohhfggeXXW "cdb z|zgigegeIJIvwvopoWXW^a_wyxkml ceeegg|}}fgg^__WYZ_`amnoffiVVXzz|NNOppq~~~|||{{{yyyxxxvvvsssooommmkkkjjjhhheeeccc```]]]ZZZTTT@@@$$$ 9k- 'dl--- (|\-EEd l' -D",-- {{!!{ "9("Ed-"j|c,| lX}lHHll|WXX9Ck,--  3w XC" y- -bX3VXVVw4*jCC|HX4>X4ދV{!!{' *Vvu=?X434Xb(-V> UU]VV?V3x4xwV44*X{j-v;UXWߜVVޜ33WXb{ ##TU3??3jk)f)f3X ?3WX-llHHllt?; ?4 H--S::)t?# =>3Wj ---- \ϊS:T; ?VC---sefS 3-eeesS2ssW --SseSsVwx44344XꙎ'-22;# ?33X( H6Sss?3}!j"ds  ??xXb,-UssS ?33*XCu:ssuU?w*!lHg;G͊wWk58-c::ʼnRRRssvVC9Ck9"'-,eRRRRssst 4Cj!" assRRRRv3!b\țPPPQRQQQÉRRssًwXWW*k-\¿RPPQQRssS?> WC9c-PRPQPPPQSw4* +QP]VV?PÑeSuvuUv1QaˊfMN/PRő#tqp/0QQQÉ#pooLQPRf)fKoKKNpQPQK^op/FPS)..noLKoqF.Ko..nKoK1Z..oNoKKpQooIJMpq.Ion.Oo^nnoqo.nKoooKN0poKKn.mpKKopoK.n.noKnnoNnnKpnKn.n..nK.blobby-1.0rc3/data/gfx/font24.bmp0000644000175000017500000000336612042452377020072 0ustar danielknobedanielknobeBM6( - B@C@ :0 &f}"$c A %-:PSfbsZk:K ` 3"| 3IVEIz %9:I]`zBL| A( %)4)HJmim)/L!*"Y%~€<>J*1 )yzED&%E16}}SO303>A ˌ^W62k SU /65JXN-(ELL23UuqxODvEEWW} (蠏hX)7,,Nrr>?7yrkD4Xzzll55j>;Gݜ~qVBBb``88F Ԅe,!/{{``??)Ǡߊe+/(ϓff99i"әq:*<AAUꞞii;:]&  w>0Akk~߰놆QQk%#.D:HǜΤw+!,ccr׽䥣nm:9E!("*Ю̟ИpIIOדlhwJGP.+/3/6[T_յʝF5F..0ƿϒ³ȴ˵ֺYGXedh̼j_i../ZYZ}z}~y|TQS!blobby-1.0rc3/data/gfx/cursor.bmp0000644000175000017500000001546612042452377020277 0ustar danielknobedanielknobeBM66(00  QQ__#A#JvJkkjj]]k>c",D,mmdd__bakkPM H hife``bb__SQ@7$C6&`^jia`cca`HE*$7/Ev͢J}Jddb`^^\\DD&"&(Muh5*4b4RQPQTVKJ9}9#"& IR`fZ\$<82X2ORAB;;55+*'%/,IHa_hHri*7*ihVV=>''%$?9WL`_ZΊzhhii^^DE%%"!=9Z_dx_^|yɼaaiibbaaNN3363OL]~]\~UUll``___`WXDBA;MRVXl-/1Ae?hh__^_]]`_]\OGE9DnJdCSP!J!SSZZ^^^^]__^`YW`G>K{Vyr&=%==@@RS\\\]]]_[_Z\NGxc8f6B@7&2&aaCC46HIUW[[^\_Y^X_TsqON<>UU^]`\dbr?`W]]^^HH9746LMddcbdd``_]_^bbYW:855QPdcggjj.F8&6&jj^_RQAA34;=RRb`b__^^^^Za```FF33HJ_`ba2X2FoFlj_]YXKL;;54BCWZ_e_k^z^e_YcaVU22**;=Uc`{__`dmSG# p 8Y8kl`a_]^^\\XVJH7448Gj^aa\N9v'$MKef`_^^]\^^\]UWC\4[=fU^\I13-cTT`a`a^^]]^`\fZcMv9q2@I@-0LSkg < IJ[Z`b_^^_]\][\`Ut@$*,0Mom'`&>@ST_^___\_k^_ZA-L|ߚ98ABLLWW_^_[_q``W52WҀޥ-85-J-TSGIDEJJYV````ZD%CtܤUUeeONBAB?QJbld]D),FЂݠnghdeUVHFA>MW^^G''IՌ޶ 6W6mm`a\\SPGECmG:%#Oѐ,63TTji`^a_afK]+z$hښƸ5B>ggcc`\d[^c7 kذʽAPM4V4mlc_b\ZQAbl׮DWPUUkhg_Zb9h Mۣ߰Pe`kjhaS]6r#)Uۨb|u%C%khLD:8vߟ`~PE:'2X tj +b(Z.64@V¥5B=:6(ʜDJH"VɽCTU@z(d]2"blobby-1.0rc3/data/gfx/ball12.bmp0000644000175000017500000001206612042452377020030 0ustar danielknobedanielknobeBM66(@@  \[][Z\|{}wuxfcfqoqihi^]^XWXywx\ZZ|zzxvv~~igf`_^yxw ZZW$$#ppoddc\\[hif`a^ [^[z}zcecvxvopohih  []]TUUwxxoppklp--.zz|<<=gghXXY}}}|||yyyuuurrrmmmjjjfffeeedddcccbbb```___YYYWWWOOOJJJ&d<얖M[>uw{۞ ww"Kl!Z>tڞuZww!KkKlgw{9gZZ)uu?ݟ'&KLtٞڞutYtZ?wiK󗂂M)Zt t؞KMMמZgZZxsӜXu6ܟwwwwxMMMuu/6w6wwx9>WZXXX͏6(ttZwwx 86yVZZɌXXXtڞtZZwwK.556ˌZZttww&k8ȋ5ƋʌX6ʌ6tttwwb W_VʌV6Xtuw 'My__ŋ6stܟ7 MrUVVUY6ZxGLM:UyȋrUUUUřXx* %_U-./UUrXsWX@wib=MrUÊeÊU-˝Vtvvw!"hG&쉉rXĉUyXZڞuuZZ@Z{ 3SSSTҝ^>۟xR}SRSSrXs^)w{+R1SSRRSS_g^e6gYtvSQQ}RRSS3-.XfΜ(tR1p}R23,‰‰ˏQ|QQ,ˌ ooP|QS3TeXfoPQnnOoo}QTTŌO0Q|2W6mmoQSPIQ2+TmoooSmHNooQSRSTHQQRRSHQ1PQRRjmoSOmommoRmmSSIonm0ѺRblobby-1.0rc3/data/gfx/font25.bmp0000644000175000017500000000337012042452377020066 0ustar danielknobedanielknobeBM6( :@_6A^'.=$banmJJg-0<  JE_ނqRGc% DZ|\E5NhTsȖۭma;kɔuރ>< Ag|@^'c  aEeK߅*%&!"}[$JKck.v| 1!2u@^c; =*)$ %  aX s vt xl n\^C E/ 0\9^pFҫ{~J J23  ad@./8Yvd<ܥ`c78 IqO6PP $$jg'eg88<$ň N:K΃ͧVk:kA&>\+[0"km44fjuQsB3@ ** 3)g i88 UvrodjDE@4;ó?H?}3L$L #|ksvphy`NUC Zlx|ʽvbyJyO4L0"-# <27`bXpglcbTOG>ђѝڮֲ̬˼|mphfr_h`qiyowhqW IϔOOPCKKJ4ҀMLLLπOggԀ--OP{ ΓǒMLLLLLM$-O3|NLȒLJLMԀNfO^^l|edđLLӀϔ--y^&CK,]Icđ,MД-^QC&&C$,jHLNДf^P^2CHHoILMLДOQٕP^zV(|NcIHH,LMKLLДgٕ^ u"CJMH~HHLJLLϔgPP[CcJLdd~LLOhQ3傉 oH,,~~~LLL͔h5P%y{{[0½e~~KLLӀO.hQ/{<=,LJHLLLπOOyzN-g 6GGdKe>wnFbG~j,ȓ84$OhbG]]~dJ\Nw*HǐY#fE*G~LEE)E6HLEE+bj,LaxaE+++bÑE```pSc,J___EE d,6E_DD__`EF H~oEDDDDD__`F}?DDD__E'Rbb`? D_E* D_` R``_D DD_`*E `_D D_FEE D_EE `` _EaE`_DED __D D`_`D` D__ blobby-1.0rc3/data/gfx/font26.bmp0000644000175000017500000000337012042452377020067 0ustar danielknobedanielknobeBM6( !"U X^_Z \QR OP 64 $ %Q Th klpmpi lUXsuʴ# !>? ˻$! 4LRF%֧ @=ik5I"J^3\T7ƅ 4 06T R};|ZQDv q% 0#&@[]o;hO6 k]8 'Jgf:"4_>۵X B J*ߖ"  `xd#d# h2c7ؘ   k߀a |BdR j8SQҞ,6-_( K&B)=tǥC^$S|@TI Me' }nu>cccddzz& 6I(kHI*4nd#d>cccdz&'eH[IJfKgngdccccddd&QF[o4gdcDEccccdd&P ǖfautbaaDcccddd&6 {dEEb>ccdd&?{e[&C>EDE>>ccddz{'(EEccEE>cd&&F?'eHBBBB`cccdd& 'QCBEEcccd'{CB`aEE>ccczz@y BABC%aacc&zCB = B`C>AC BBCdCBBtcaBCcadCBEdzCCCCCCaE>P`CCaEBB%blobby-1.0rc3/data/gfx/font27.bmp0000644000175000017500000000337012042452377020070 0ustar danielknobedanielknobeBM6( O RH  > zWz TR 6]  vyE2 j 02g/`Xe- m  %'<>GNl88  y &)GI-HdE~=m !Z  "&AA%>#*`!O#0 K  35&XfGg&n:  )-3+ hy 62   36LJQGVn o  f`i% : /  xJNIM   3ae/6   ( _`[^+1  ~ N * +psRW2: *#/8C8G#7 #W. DEX_8B(4~&+39HHIxxy__`VVW|||yyyxxxuuurrrooommmjjjhhheeedddccc]]]ZZZTTTPPP### $H~11[11a=~)a1~~a1A~a ~JJ]1~=I|*`a1~1!Iz\|IIzD]~a~a++H|\Iaaaaz0m+z}1~xz?tV+Iz~~xp߅sxHxxs0 krkUHH$0\1ts## TkޅV$V$J~Jr#ڄikkjj#T$VmH}"qSShrkiijjFkH??Vm~1tT/g/hTUSS"۞VHV sVpH+1aaa j֜i"qShۅㅅtmHz|}111a1Մrۜ.jj/hh$U VVmHz~aa~1gjԛjgSi mk~a~ҏ.h>>fffjقgS#V klV~ԛf,h՛֜TkkVH=~>ҏԛ֜ikkksV|^^^ҏ؜f҂ԛ ksVHI\~-^hufԛ֜ TݞVIz(9ԛgq/Ղf ԛhkjݞVI.ԛR>ffԛkkjjjkVH\(].9RЛԛkrjijݞI=1 RRffԛETThhjV0ai^R R TTjhSjjޅ$Iz{ a`uXXR fԜjiShjޅV|=~:_XRXfӛjhSktD)AD)ǚR9ӛjjhhT~=IIA(XƈeeeeXӂik#kV+IJ88Xd88eefh sst0+pI{~J7eeeR م߅jjj#Vz`~1Ùd8RňRfjSՃgSiV0;`8878ef,ԛ։ittV!AddeRX9i݅s ?x68XRXǒԂ Vx%6666de8d8ee hTlr#UV0ceed8̓ h F"#VQ68eƒϛSrljEjۅsOPP555P78R#S/"444444444P6%6d8_ʓfhgSj4NNNNNN46y%6dnnςgכ.S4NNc444PP6eLjLJϛ՜N4PPNNNN444PP8d8d8deқ NNP4PONNN4444P6d f>f643NNNN444PPe52222MNN4Q666c>eN2LLLL2@MN4Qbb_ @LLL22N45P66XLLL@2NN44445Q6LG2NN44dL2NN3N44L22M4LKNLLLLL2M4662LLLLL2NQQL3L2O444623LL2NL2@P22L24M2L2MM4Թ42Kblobby-1.0rc3/data/gfx/font28.bmp0000644000175000017500000000337012042452377020071 0ustar danielknobedanielknobeBM6( - C FJK(F0<9.<&5(  i!  0Gawya D1"kK+ "0@"s4b:We[Jz5W5&b8 3(./% $c`DVbQq3I 5$U( e_^RA.'L <>*tPdC_-6|`V;&  ^AAtp~ueS:<//u-c,V-  D-ZA F)"mMJjcn`^GM+?0 \$ ) T+UFH#   %  2Z%?IO%N+' 3Z} 0*+ 0#/ (x%)4?'H5n3+$+"-&.6:POWVTREA:3?9.)q41&R'7/4$=:Qdrzgn}y_j[%-1E,]'W,Qj(T&1_9RQ"- z-,Ġlm/Љnoo oN?[R_ÿ%-iiiˇk̇mSKII\oqN'Hi,ii,HGGGHkl/Inݑ=iFG..GHkmԣuuIaEgUg,,i.-.GkV/Ф;IKO}ggg¿-Gˇ`VУSKMMgg,%i-Glll`ωpDDDggi- H/DCigggFGG _ʈIII)*g,-G_/)CDDi---.HωDdddddeiCDD->VD)dDiCDDiiHl+)DD)))iG HLJeCCC))DgG )DDdd))dCDj>)e df))CCCD-,fD eDD))DFi-Cd)Cdd)Di)Ddd))Ch=U))DDdd)gTi)d)D)))d)C) e)BC))eECg)Ci/blobby-1.0rc3/data/gfx/font29.bmp0000644000175000017500000000337012042452377020072 0ustar danielknobedanielknobeBM6( 3}Y 7 n \ ;61 EXWAAz  P22kk&' JssJKoQ00mm!#6  llLMa}GGlo(+! 2##uvKLG _`lm.0  2##BDIuweh'*q>&&rv5;5 ^bQXeo=Ack+) *NY.=U #-%; p! "6 > e+Y!DOZ7&b 0[ M E BN.Y'P 0iX O K:5 :H.[SNN[ggW|7_> +$ H"Ebelry}{uxqg`xXm@V';"t&+     0 $;)-K38N47\:?a9?c07GN>GBblobby-1.0rc3/data/gfx/sch14.bmp0000644000175000017500000003006612042452377017675 0ustar danielknobedanielknobeBM606( 0  333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333blobby-1.0rc3/data/gfx/scrollbar.bmp0000644000175000017500000000152612042452377020735 0ustar danielknobedanielknobeBMV6(    [ "        r ` > > 8 2 2 2 2  $ijjd 2 2 2  *0&0 ' ( ( (R > 2  DIIQ)3*4*4*4{ ^G"} 0:DMLUDMBK'0$]'28"*#+)3FOCL'1&d)MR.8& ('1-7'0'm-MRW_)3%.%.'0&0's2LQV^8B:C.8)3%.&v6JOU]7A9B8B8B%-%t= IQU]'1z T4%DM3=7@:C9B'1#\48>*47@-7%-'0#,$k gkip`hGPMU1:$,$-dh棐╍tzsyS[0:#,Tsw걦誤稐╍nuRZ-7i ÌҨ΢΢ǔŐrvX]5; blobby-1.0rc3/data/gfx/sch15.bmp0000644000175000017500000003006612042452377017676 0ustar danielknobedanielknobeBM606( 0  333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333blobby-1.0rc3/data/gfx/btn_slow.bmp0000644000175000017500000000336612042452377020605 0ustar danielknobedanielknobeBM6(  blobby-1.0rc3/data/gfx/pfeil_oben.bmp0000644000175000017500000000146612042452377021057 0ustar danielknobedanielknobeBM6(@  000,ZLutt *5HVdy$&d*.)GE.\Zvu   '" ++'"" )+'"" ()'"""&()""$&%"" $#" "   *!   .-,!  ..-*  ""/.-*  ..-* #/.-* ..-*/.-*! ./-* /.,/,blobby-1.0rc3/data/gfx/schball.bmp0000644000175000017500000000700612042452377020361 0ustar danielknobedanielknobeBM6(E 333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333blobby-1.0rc3/data/gfx/pfeil_unten.bmp0000644000175000017500000000146612042452377021265 0ustar danielknobedanielknobeBM6(@  00& LF . l+hK&LZHTYZ%&\D( ?DDBB77'+ggSS##bF  >>CCEEBNOkkHH)) y:XHHHHe11 ;$$vvXX>>55))55k!!+GGJJv;;8$$ff^^;;..#k66eeLL--''""::CC~>>T22 W22gg77\N22wwppNNBBPPQQKK^88-  ]]AA>((ooeedd\\rFFH33 ~??PP  )ό}}nnTTY==#:""aa((M+ ֛勋pplKK:..WWJJ >⼼鼼YOOT//``$$! ppĥrdd411 aaGG%%BֿTNN LLkk77v,, $!!ʹʷ~vv$""7$$ttcc>>4QLLٱDAAjjjjMM}xxA//蝝𔔿jj:%%533qnn䕕jHH~~~~A77}}mUU mkkUUU blobby-1.0rc3/data/gfx/font31.bmp0000644000175000017500000000337012042452377020063 0ustar danielknobedanielknobeBM6( :<:KMKcfbz~z OVO+.+^i^}}KRKaraZiZmmg}g190kh̜nm@Q@xw֖|zPmP#."}|癈҇[[6Q7   Ҍ勆eeFwF"7#  }{yymmTU3h4  ,eb~kkOP\^@B$O&3][kkUU  yzNQ15*QLJLI87=`>kp4;j% )p(=:+)%%`cW](J1/)$M*=,vz?H) &** 3 EoIpy(7v& ,)  akUa*X# ,) lxAQ% AO($ y.E5q1F $4.{-84E:WEq'@$ <$cbRQ20(Ot_l>0 C)rpLNTdm,MJ <_= (`. 1>.oBS <7 n7 ""3)]Ar  KP rL B- - H8>!Pd rlM=  -<- Z@b iNT,. /2 " s #G^Jv1? +#0 B&BWE3_/?)0 i #7-VG7(T!R27"  X -7S=ZD0 $O Y;>48# /&x1FHY*7mC -C&&b?@@C=? #o+?N7H?4 % j:;BBS:<?H$0*/PQe.L'c t/177J1[]-/ #G#+&X]fx1G'K~ #')DJK_`58$&#'8 lqbr2C4"%2mmde;<: +|]j1=($+99Zhh22!"O@AZO[(1{ $/ppSS))""nVX}=E %n  CO(7zz::&&hhgk+1z  btHX"u!!/ab-- km_c%" n]r(.XHHp~~==E"stWY(+ 1-2M  abeg$$x -LN"#NVV88blobby-1.0rc3/data/gfx/font33.bmp0000644000175000017500000000337012042452377020065 0ustar danielknobedanielknobeBM6( "+'"7#)-. !:&+j,(e(9]</!,3"))&$20?bE7  ?  )*(%CF$h)58 K  '~)::$>' [bkl,* ^  *Z/,'=gD_}g>;  h   56A[C݄QO s & n  -,#)%ԋWT   / &_*!'$~΂VS 8f"  #&  +7.}VU ,!"($w~KJ  Q% *&Za33 ( IV%'$Z6[Xy.8;IsYar#-  .\Rh.k@o rgz. o!"w||#w-+Oi$Y 2L"wny'12q9Z % C/M@}+uBc3 (NsS | xKJv!$ΈJ1R@Ze5Ablobby-1.0rc3/data/gfx/font38.bmp0000644000175000017500000000337012042452377020072 0ustar danielknobedanielknobeBM6( H E 5 x   >  /   '# \1,#40 2,p3/H ?:"c  PM%!!t  VS2/+(  ZXPM:9[ZqoJI0AXA׌kj;R; % doczB2Cʵ̸~H11fWgڞ\|-/0 xBՋCFÇ8z#%q|:A CdDgqG blobby-1.0rc3/data/gfx/font39.bmp0000644000175000017500000000337012042452377020073 0ustar danielknobedanielknobeBM6( Zm $+& } 6   _\IDA'wurp=7$";  feNL2/, on]Y:j8  ƘԎYX*6*?E?ɨXaX y|yuiv5*52-2vcCe  ~vς? Qkޞ7,-[1]^7<=N)ON.KM895!M OMN'KMor K NGJ '߳' (]a!ۢ$ 5.&r+wv+|]W@qh2nC0F{ blobby-1.0rc3/data/gfx/font00.bmp0000644000175000017500000000336612042452377020064 0ustar danielknobedanielknobeBM6( #$,.,"0";ITU G =.C4DTUTF]F?r>=;;985/, x I # /$/vlwgygbbbbwwdb~}tqNJ^ ,\Q]~~zzww  XhWّ~95 r 6 ml``PK | 3  ;P;?O>VS s . }}+:+AU@D@ g  on)MfM}z,' Q ȵee-balh ,  ji1@1 {yGC g ơon:O9LgKlj  5̯~~IeH rq:5 djqjʝ__ HiIUQ .2/ٶvu8R8 ^[6ٽWxW) XU,'D7:7εٴ{z>V>  ;9.) F xy̪өhg,<+.++' Eؿҧde*;*  =;.*$!G BFB³ˮ׭_}_):)  8B?41/,RNTNӳڲqq7M7" DB@=>:?<AILI˸ܷ_^5P5"6"('2U3IGURa][XNK695ɸ綇on[ZTQljywꈍqo8bgbν弰⯩ߨ鯴ꬋ‰-B-7;7jqjȶǰDTDblobby-1.0rc3/data/gfx/pokal.bmp0000644000175000017500000042356612042452377020074 0ustar danielknobedanielknobeBMv'6(h@'  ̬ܶĥ侼ĥ侼ܶܶv|̬̬ܶܶĥ侼ĥܶĥԮ̬v|侼侼ԲĴ̬ĞԮܶĥ侼Ĕv|ԮԼԲ侼ܴ̔v|ԲԲ༞ĥĥ侼ܤ|rt̬侼Ԯ̬̤ĥܶܶ侼̬̬ĥԲܶԲĥ̬Ĭചܶ侼̬ĥܶܶ侼̬ܶܶܶԲԲԲ侼ĥ侼ܶԲ̬侼ԲԲĥܶܶ侼侼ܶԲԲܶԲĥ̤̬Ąv|ܶԲ줊\VTԲܶԲܼ侼ܶ侼侼Բ侼Բ̬ܴ̬ԮԮĥ侼侼켞̴ܶܶԲԲ侼侼ܔԲĥ̬ĥ侼侼侼ܶԲĥ侼̬̬ĥܶܶ侼ĥ侼̬ܶܶܶܶ侼侼侼侼ĤԲ̬ԲԲ侼ļ̬Բĥ̼̬̬ܶܶܶܶܶ侼ܶԲ̼ܶܶĥ̬ܶܶĥļ侼侼v|ĥܶĥܶ侼侼̬ܶv|ԮĄv|ܶ侼̔ܶ侼ļܶܶĥ侼̬ܶܶĤ侼ܶ쬔ԲԲԮ̬̬ĥԮĴ侼̬侼ԲԲԲ侼ĥĞܶĥĥ̬侼侼ܶԮԲ侼ĥ̬ܶܶԮ侼ĥ侼ĥĄv|̬̜ܶܶԲԲĴ侼ܼԮ̬侼Ԯܶܶܶ侼ܶԮܶ侼̬ܶ侼侼侼侼侼侼侼侼侼侼侼ԮԲԮԲĥԮܶ侼ܶԮܶĥ侼Բ~T|rt|rt|rtLTZ\4VTTnl|rtTnl|rtTnl|rt~TnlTnl|rt|rt~~̬ĥ̬̬侼侼ܶܶܶ侼侼ܶԮܶԲ~|rt~|rtTnlTnlTnlTnlTnlL~~TTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnl|rtTnl|rt~~|rtĞԮ侼侼侼侼ĞĥԮĞĞ|rttjlTnlTnlTnlTnlTTTTTTTTTTTTTTLLLTnlLTnl4v|TnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlTnlLtjlLTnlLLL4v|Tnl4v|,fl$Z\$JL$JLd^\ĥԲ侼ܶ侼侼侼ԮĥĞĥԮԮܶ|rtTnlTnlLLTTTTTTTTT\TTTT$$$$~|vtvtlq bd bd bdZ\ VT VTZ\ OO OOZ\Z\Z\Z\Z\,fl,flTnlLTTTTTLLLTLLTL4v|NL <<$&$<:<\VTlbdԮԮ侼侼Ԯ侼ĥĞЌTnlLLTTTT$$,,,,,,,$,$$$$$~|~|vtlqcelqZ\Z\ VT OORT FD OO FD FD FD OO OO OOZ\,fl,fllq,flbd$rtlq4v|4v|LTTTTTce$JL$&$$&$$&$$&$,.,DBDd^\ĥܶ\VTܶĥԮ侼侼|rtL4v|$$$,,,,$,,,,,,,,$$$$$~|~|vtvtce bd bdZ\Z\ VT VT FDRT FD OO FD OOZ\,fl4v|L4v|$fd bd^\ce bd bd bd bdlq~|~|TLRT$&$$&$$&$$&$,.,,.,$&$$&$<:<\VTv|ܶ侼ܶ侼Ԯܶ侼ԲTnlcelq~|$$$$,,,,,,,,,,,,,,,,$$$$~|~|vtvtlqce^\ VTZ\ OORT OO OO FDRTRTZ\,flLTL$rtZ\ bd^\ bdlqcelqlqlqlqlq4v|$fd$&$$&$$&$,.,,.,,.,,.,,.,,.,$&$$&$<:nXK],6Mf|LVt,duTa4>T'%% ,! !!# !!!!  ! , "! ,  $!  !! ! ,#!! !#!.%( '# ! .% ,!!! ! ," , .# " !   ,#!! $ !'.'##%! ! %  ! ! (   ! "  , " ! !#!,'%(  '# ! !% !!! ,    " ! % ! . ! !% ,, !!'% #., ! ! # !!!!,,, " ! !,%, .  ! !,,, !!!'#( .!  ,, # !!!,!,, !, '".! ,,,$, . ,#! !  !!'%  ,!#.!!!,   !!, "!  , !$   !,%!!! !!'%  .!!  ,!#.!!!!  !! ". , '"  , %! !!, ! '%(#  .,!  !!,! !!, !$ ,$!,  !#  ! !! '%# ,  ! ,%! !!!!   ". ,,, ! $   ! #!,! , !! ''%(  #   #!,!,(   ! " ! !#  $!, , !!!''% , %, , !! !!! !!!    '"!! # !" , ! % ! ! '%( #  !, ! !  , " ! # . "!  !#,! !! ''#,  ,  ,!!!! % !  , !" !,  #.. $!  !% ,  ! !! %# ,!, !, !,!!   , !" .!!.."  ,% !, !!!'%(.#  ! # ! !!!  , !'"  # "!   , !%  ! ,!'%#  %  ,,   ,! (!   "# ,!,     !.% ,! !!!' #! ! ! !!! , ,, "!,!,. "    .# !  !!''#( .#!    !!,  !"! ,     # ,!  !! !'/'% %# !, !    !!(#!  '",!    *! ,! %   ! ,,''%##! , ! ( !! !! #!  ", ,  "   , %  !(! '.'#. !,,  ,# ,!!, ! " !! "!  % !, ''%   , !%,!,!  , !" !   %  !! ! !''%( .(,    !,!!! ( ,  !'", . !  "!  !   !#! !!''#  # , ! # ! !!! . ,!" ! . "   ! %!  !!!''% % ,,,  ! !!! !,!, ,$  !'"!   ! !, !'%% %#   ,!# , !! !    "., !", !, !,% ! ,! ,!''%( ## ,,!!. !,!,, !$.  ! '"!  ,!! %  !.!#! !''%  , !!! !,!,, !,! ! ".,  . ", !! ,,!% ! # !!'%# # ,!!!  !!!,! ! , $  ! !," . ! ! % !#   '%(#!   !! , ,!!  %! !  "  " ! %  !!!,!'%#  !,!, # !,!,."%, ! ,, $    '"  ,! %  %, ''$(!  , , !!#  ,,!  ! "  ,  "!  , !% ,,! !! '% . .( ,,,!  !,! , ! ! $ . . "! ,, ! %   !#! .'#(! # !  .!! ,! , , !, $,#  " .  !# !  # !'%,!% ,, !! # !! , !  . , "  !  % ! # !!''%(##  ! %. !!# ,  %. ! . "!, !!!%   , !'%( . # ,,!! !,,,!$# ! ,, . !# .$.  !,.!!#  #, !''#! % !,! # !,!!!$. , , $..#  %. ,!! %.## ! '%(  %# ,,! !#. , !!. , ! ,! $,  , "!, #.!, ,#, # ,! ''% ##!  !!   !    ! ! ".  ",  !,# % !!'%(.% , !!% ,!,! !! ,, $. .!." .! !!#  # '%!. ,, !! #   ,.#, , ,    . $ !!, ,#  ! ''(#. ##!,! !# , !,.##  , .# .* ! !!!%!# ! ''#% '%# ,!!# !!!!.#! , , ,   # ." #!, !,#  !'' (# ## ,! .!# !,! (  , , .  $, .!!! ! !% ,! .! ''#.! # ! ! !,  , , . ! ,  , .!!! #,  !!  %(##! ! ! !!. ! ! !!  ! ",!!  !# ! #!'%! '%,, ! !  !.%#! . , , # .,   ,, !#!! , .''( %% ! !, % !! ! % ! !! % , ., %,!  !, ,#!!!! ! !'#  !, !,!! #,!,  ,  ! %,! %!! ! ,#,!  !!'''%  % ,  !!! !!!! !.!    ., #,!  ! !!!  ! '''#!! !.! !,! ! . !. ! . .!!   !!.!!!!''''+'! !.'' .  !!!..  ! . !! .!!/ . ! . !'  +''/'.'/'''''''''''''''''''/'''!''/''/ '' !.!'  '...'!'.' ''''. '!''.','./'///'/'//////////////'/////'//////////.+//'//++/////'/''''/+''//. / ' '''''!''''''''''.''.'''''''''''''''/+.///+//////////////////++/////////////./'/'//'///////////////'////////'/////'////////''//.'''''.''/''/'/''''/'/'''''/'/'/'''////////'+/////'///'+/////////////''/'//////''/'////////'//////'/////'''//''/'//'////////'////////'////////'/////'///'//////+//'/////////////////+/++////////'///////////'////'///////////'/'/'+//////////'/////+/''////////////////////////+'///////////////////'///////////////////////////////////////+//////////////////////////'/////////////////////+//+////////////////////'//////////'///////////////////////////'//////////////////'/////////////++///+/////+///+/////////+++/////////////////+///////////////////+/////+//+'/////////////'/////////////'///////////////////////////'////////+///+////////////////////+///////////////+'/////////////////////////////'////////////'+//////////''//'''////''//'/'//'///'''/''''////'/'/''//'''///////////////////////////////////////////////////!                   ! """"""""""""""'''''//'/////////////////'////'/'/'//''////'////++++++++++++++++++''''///////'''''/////'''/'!!     , ! ! ! ! , ,,,                              '   '..........''......       '  ''''''''''''''''''''''''''''''''''''//////////////+''/'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''.'.''''''''''''''''''''''''''...'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/'///'''''''''''/''/''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''..'''/...''''.  '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''...'''''''''''.'''''''.''''''''''''''''''''''''''''''''''''''''''''''''''''...''''.'''''''''''''''''''''''''''''''''''''''''' .....      !!  '/ '.      !     !  !! !!!! ! ''''.  . .'.       #      !      !                           !     ! ##          !        ")"""$#$$"""$""$"""""$"""$""$""""""""""""""""""#"#$""""""""""""""""""""" ### ##$$"""""""""""""""""""""""""  $""""""##"""""""    #  #$#      ##$$       ####                      ## ### ####   !     !            /$      #   #  ##!! !        !       #!   $    ###$ # ##!  ##       #       !,        .            !        !  #                ###  '%!  !!!! !   , !  !   !!!  !  !!,!  ! !   !   ! ! ! ! ! !      !!!! !    !!!!!, !! ,      ,! !, !! !!,!!! !, !!!! ,!!,!, ,!   !!!!!!!! , ! ! !!! !,,! !!  !   .. ..         , . ... ...  ! ... .   , ,! .    '.   !!, !  ..... .   !!!!!! !!,!!, ! !! !  ,' % .  !  , ,!,,,,,, ,,,   , ,!, ,   ,     ,,,!,, ..     ,,, .,..,,,,,!!!!,, ,,,,.....  ............................... ........... ..... . .''......................    .. ... ''%$#,,!!!!,!!!!  !!!      !!!!!!!,!!!!! , !!!!    !!!       ! ,!!!,!!!!! !!!!!!!!!!!!,,, ,.,,,!!!,!!!!!!!!!!!!!,,,!!!,!,!!!!,,,!!,!!! !!!!!!!!  , ,,,,,, ,!!,,!,,!,,!!!,,,!. !,!!!! !!!   ! ,! ,!!! ,, !!!!!!!!!! ! !!!    !  ,!! ,  ,!!  ! !!  !..  !,,! !! ! ,  !!!!!!! ,,! !!!   ! , !!  , ,!   , , ! .%"# ##  # ##$$$$$### ### ######### # ## #######  #### ##   ########$####  ## ###########   ######  #   # #  ##### !       ######   !             #   !! ######### ##       ! !    #### #$""$     #%$$# #  # # #######  ##    # ##$#  ##$## # #####   #####   ## ! #  ##   #       ###        #   !            !   ####         !!! ,!   ### +(,!!!!,, !,,!!,!!  !,!!!, , ! ,!!!!!!!!!!!!!!!!!!,,!!!!!!!,,! ! !!,!!,, ,!!!!!!!!!!!!!,,! !, ! ! !!,,,, !!!!!!!!!!!!,,,,!,!!,!! ,!!!!!,!!!,!! ,,!,,!!!! !!!,! !!!,!!!  !,!!!,!!!, ! ! !!! !!!,, !! .  !!, ,!!! ! ! , ,!,,!, ,, !!!!    !!   , !!, ! ! !!!!!!!,,   !  ! .. !! !! .! . !  ! ! .  !  ,! ,,!! ! , !, !!!!!!!! , !!!!!!!!,!!! ,! !  !    !!!!!!!! !!!!! ...!, , ,,...    , ,,, , ,,    ,  .  , .......  ,,,, ., . , ,,,,,,, ,.... ,,,   .......... ., ..    .......... . ............................................ ............. ..........'..................... ...............  .. .  ../ !!!!!!!!!!!,,,,,!!!,,,!!,!!!!!!!!,!!!!!!!!!!!!!!!!!! !!,!!!!!!!!!,!,,!!,!!,,!! ,!!,,,!!!!!!!!!!,!!!!,, ,!!,,!!,!!!!!!!!!,!   ,,!,!,!,!!,!!,,,!!!,!,,!,,!,,,!, ,!,!!,.,,,!,!!!,!!!!!,, ,!, !!,!,,, ,!,!!!!!!!!, , !! . !,,!,!!!!!!!!,!!,!!!!,!!,   ,   !  ! !,, , ! , ,   ,,!!  .! !!!   ,!  !! !! . ... !   , , !,! !,, , , , , ,   .. !  !,....    ,   '!"**$################# $#"#########%%$#### !############%$# ###!! #####$$### !!### #### #!  ##########    #### !   #########  #      ######          !      !# #    #!   ! !  ####   !! !    ! #  !!!     +"""$########  #####$$$##$$$## # !### #######$  ### $##    ### #   #########   ! ####       ###      ##    #   # ####       #        #   ########             !    !"!,,!!,,,!, !!!, ,!!!!!!!!!!!,!,, ,,, ,!!! !,, , ,, ,,, , !!!!, ,,,!, ,,, ! ,,,,,,!!!!,! !,,! !!,  ! ,,,!!! ,, ,!,!!!!,,,, ,!,!,!!,, , !!,,,,! ! ,, ,!!!!!,!, , ,,!!,!!!,!!,, ,!!!!!!,!,!!!!!, ,!,,!!,,,,!    !, !,,!!!,, ,,,,!!!!,!!! !!,,,,!!!!, , ,,,,   !, ! ,!! ,,,,, , ! ! ,  , ! !!!,!!  ,  . !!!  ,   , !   ,  !!! ! !! ...   !! ,   !! ! !, ,,!(,,!   , ,! ,!  !    ,  !   .   ''+' ,    ,  , ,   .  ,, . , , , ,  .    ,,, , .. ,  ,     ,. ,, , ,,,,   , ,.  ,, , ,     . ...   ,      ,, ...... .....  ................. ................ ......   ...............'......'....... . ''' (!.. ...................... ''!!     !!!! !!!!!!!, !,      !!  !!!,!! !  !!!!!,!,,      ! ,,,!     !,,!!!!!!     !! ,, (!,!,!!!! !!!,! ,! !,   !  !!!!,, !!!!  ! , ! !!  .  ! ! ,   ! !!! !   ,  , !! /'( !!!! ,,! , !  !! !  , , , ,.,$%####%%$$$#%## ##########%###$%$$$$$### $##$$  ####$$## # ########## ## ###$### ##$$$##  ##### ''"$# !  !# ####  # ! #   #####    #  ########### # !#    ###       ''(#  ###########  #       '#%# #    ,!!!  ############    ##  ,!       !    ##     ########           ! !  '(%%  ! ! !       ! !! !! ! !!!!      .!  ,, !           !!   # ,!      !    !!!  !!!   '' !       ! !!!! ,!  . (%, ,,                 .  !   ,,, ,,    ,,!, , ,      ,, , ,,      ,, ,   . , , ,   .., ,    ,,,.,. . .    ,,  ,    .    ..  .........'''!,   .   ........ ....................  ......  ...................................'( ' .  .. ..............'.......... /%,,,,,,,,,,,,,,,,,,,,!,,!!,,!,,,,!,,!!!,,,!,,,,,,,,,,!,,,,,,,,,, ,!, ,!, ,,,,  ,,  ,,,,,, , , ,!!,,,,, ,,,,,,,,!   ,,,,!, !,,,!,!!!!!!!!!,,!!!!,,,,,!!,,!,,, ,,,,!,,,,,,,!,..,.. ,!,,,,,,,,!!,, !!!,!, ,,,,!!,,,, , ,,, , ,,, ,,,  ,' ''!,,, ,!   . , ,!,,  ...  ,      ,  .  , ,,, ,       ,, .......... ,.... ''( '!'.........'.''  .. . '''/-$## ###%%######################!!! !     #######      !!!!!     ####!,    !, !# , !!       !             !. '      ! !! !!   !     !!!!   !!,            ! ! ''.  '''''''! '''' ' '      %"$"# $$$##!  ###########  !!###   ## ##########  !!!,  !..!. !!!!  ..' !!!,  !!!!!!! !. !! !!!!!...! !!!!  ! !! !!!! ! .''''''/!! !!!! !!!!!! ,! ..'  ! ! ! ! . !!''''' .    ''.''''.'.'. ! ! ! ! , !!   !   . ! !'''///////''/////'////''////////////''''''''//'''    ! "' % . ,, ,,,...    ,,  ...'.''''''''''''''''''''''''''''''''''''''.''''''''/''''''''''''''''''///////////////////////////////////////////////////////++'!*****************/' .   .. ''(, ,,  ,,  ,, .'''''////////////////////////////////////////////////////////////////////////////////////   * **********************************************************************************************'....'' (% ! ##,,#!'' (                     ** **************************************************************    '$%%!! # !#                *******************************/        %%, #,!!,  ,!,  ****************************************************************************************************************  /!!. , . ,, ,,, ,************************************************************************************'!%%%#%%#$%%%#%#%%#%#%/   !!,!!!,!,!           *********************************************************************************/. ! , #$"#### !!, !*          *****************************************************************************************'       '%" ####  !,!!!!!! *                      *****************************************************************************/.!     . /%,%%%%%%%%%%%%%%%%%%%%%%%%              ********************************************************************************************'......... '''%$.    *                 ***************************************************************************************'  !! !!! ''$$       *                  **********************************************************************************************/   !    %$    ! !               ***************************************************************************************************'  ,!!,!, !!,'/%, ,,,, ,   ,, ,,                 *******************************************************************************************..  .. .. '' !$*"#######,                *******************************************************************************************'   !  **$#                          **********************************************************************************'      +!,!,!,!!!!,,!,, ,,,,,,!,,,*                   ******************************************************************************'     ''.,!!!!!!!!,!!!,,,,,,!,,!,,!,                       *************************************************************************/', .  .' '$$###############*                      *************************************************************************** ! !!    '$" #####                               *******************************************************************       /(% , , ,  ,, !,,!,,!! ,!                         ******************************************************************************'. !! ,   '%..,,.                                     **************************************************************************************'..... ,''% !,!!!! !!,  !                        ********************************************************************* /'!!!! !   ! ,%% ##### #### *                                 *******************************************************************************/   !     #%$% ! ,  , #, ! !                               *****************************************************************************/' ! ! .! .  ! !  ,,, ... , ..   *                          ****************************************************************'...............''''##!,,!!## ,#%!!!                                    *********************************************************************** !!!!! .!%$#  #####                                 ************************************************************ ###   /%- ,,  !  #!*                                  ********************************************************************    %..,,  ......                                      **********************************************************....... ''(,,,,,, ,,,,  ,! !!!..                                *************************************************************** !#$$$$%$$$*) '%$%######                                           *************************************************************$""$$$*#   $$ #'""$######                                             *****************************************************'%$$"""$#%%%# .!!  $$$#   ! !                                        *****************************************************************'    !/%- ......  . *                                 **********************************************************'. ......'!$$$$$$$"$$$$$$$$$$$$$$""%                                           ************************************************************************** .   ! .'/#$. ####### *                                        ******************************************  !  #!!,!!,,!!                                           ***************************************************************'     '## !     *                                         ****************************************************************  !! !!! ! '' '(. ,   .                                                *************************************************************'.. .....',%! !!!!!!!.!! ,!,  *                                            ******************************************************************'...   .'+%$!!! # ! !!,                                                 **********************************************************************        $""#  ##!##                                                     *************************************************************' ## '$*"#     #                                                         ****************************************************************    !!,!!  !                                                       ************************************************************/' .!!!!! / , , , , , ,  ,                                                   ************************************************************'.  ...... .., ,,,,,,,,,,,,,!!,,                                                    ***************************************************...... .. ''+%-%,!!,                                                      ***************************************************** !! !  !""%###    #                                                    ************************************************************/     #"""#####    ##                                                        ****************************************************       #-$    ! ,,!                                                *********************************************************' # , !! !  '%$,,,, ,!!!!!!!!,,!!,,,!,,                                                   ****************************************************************/' ,!,, , , ''.(%-!      *                                              ***************************************************'  ....' +$,!!,,, ,,,,,,,!,,,,!,                                                  **************************-******************************************/  ,,   '% !      , *                                                 **********************-**************************************/     # ##%                                               ****************************--***********************************   ### /'###    # *                                                **********************-*****************************    ' ##%$%%#%%###$!#                                                   ********************-****************************************** /!,!!, %# """""$"$$$-$$!                                                ********************-******************************************* /'  ,!, !  ,  ,.#$ '  """%                                        * ****************-*****************************************/ ,  ,  .' %%,! ,,!!,!!!!!!!!!''. *                                              ****************** ****************************************' ...''!. ,,,, ,!!!,,,,,,, ,, ..'                                                ******************** ***************************************************'...'!,,,!, ,!!!!!!!,,,,,!!,!                                              *******************************************************************.  ! ! .'# !!     #                                             ******************* *********************************************/'! ! !   $*#  # #                                                 ********************* ************************************************' !!   "$""#  # ##                                               ********************** ************************************     ! .%% ##  !                                                  ************************* *************************************************' $$$$" "    '/+%% !!                                                     ************************ *****************************************************/! ##*  ####% ' ,,!!!,,!!!!,!!!!!!!                                                    *********************** **************************************************/!!$"$$$-$"*&'.' !,,,!, ,, ,  , , , , ,                                                     ********************** ************************************/!!  //   %.  ,,,,, ,,,,,,,,,,                                                 ********************* ***********************************************'..........%$,!!!!,!!!,!!!,!!!!!!!!!*                                                       *************** ********************************************/'   ! '.$   !!!!!!                                                     *********************** ****-***********************************/'  !!! ! ! !  %"  !! *                                                  *************** *************************************************** /    $$ ########%####! *                                     )              *********************** ***--****************************************************/'!       %$######                                      ) )              *********************** ***--*************************************************** #     %!  !  #######*                                  ) )            ************************* ****--***************************************************      !,!!!  "(%. !,!!!                                          ) )                *************************** ****-******************************************************* !!!! ,,, !  %$,!,,,!!!,!!! ! ,,!!!!!!!*                                   ) )            *-************************ ***-*****************************************************/!!, !!!!,,! ,, '%,!,,,, ,, ,,, ,, ,,,,!!                                     )            -*************************** ***-************************************************************/' , ,,   '%. , , , , , ,  *                                       )               --****************************** ****************************************************************' ...'!!% ,,,,!,!,,,,,,,,,,,                                     ) )               -**************************************-***************************************************************    ,  ''"%%!!!!!!!!,,!,,!,, *                                     ) )                  -**************************************** **************************************************************' , ,  !! ,, '%$"#     ,                                        ) )                   --***************************************************************************************************/' !! !!! !!! %$% ######## *                                       )  )                *-***************************************************************************************** /  !  .' /#%$#####%#                                        )  ) )                  -****************************************************************************************** '    #     !# # # ##                                                       -**********************************-*********************************************************     #%!       ***-----------------------------------------------------------------------------------------------------------------------------------------------------            ------ -    % % % % % % %  %    % % % %  % %  % % % % %%  ---------------------       ---*****!    !, !%$,!!!!!!!!    *****------------------------------------------------------------------------------------------------------------------------------------------------------             ------            ---------------                         ----**/'! !!! !! (%, ,,, ,  ,,  ,, *                                                      -*******-**************************************************.....  ..' $%((!*                                                   -*************************-**** *********************************************** /! !(!$.%$%(  )  )                           )  ) ) )  ) )                     -**************************** *****************************************************%*    )" ''+++*                                                          -***************************-**** ************************************************************************+                                                        -****************************-********* **************************************************************** """"""""""""""""""""%                                      )                        -*************************************-*********** *********************************************************************** ' """""""""                                                              -**************************************-******* *************************************************************** ''    $*"#                                                       **********************************-*** ************************************************************/'      +*#                                                             --********************************-*******************************************************************/ !#   . $"                                )                            -***************************************** *******************************************************/        ++$$$###                                                    -******************************************-**********************************************  ! # '$$$###  #                                                         --****************************************************************************************       # $"$########                                                          -********************************************************************************/  !  !# +/$"###########                                                    -*********************************--**********************************************************   . ,  '+#%$$##### #                                                              -****************************** **--****************************************************  !    %"$  ##                                                                     -****************************** *******--****************************************************     ''#         !*                                                   --******************************* ***********--*************************************************************!    !! ' (      ! *                                                           **************************************--********************************************************/   !+%%!    !*                                                       **********************************************--********************************************    !   %$%,!!    ! *                                                         *********************************** ********-*************************************************  %#,!!!!!!!!!!!!!*                                                  ****************************************--***************************************/' ! ''(%!!!!!!!!!!!,!!,!!,!!!*                                                   *********************************-******************************************/' ! !!!!!!!!!!!!% ,,!!!!,!!!!,,,,,,,,,,,,                                             *****************************-***************************************.. . .%( ,,,,,,, ,,, ,, ,                                              ***********************************-**************************************/'!!!!!!!,!!!!!,,!!!,, '. ,,, ,  ,,,,                                         ******************************--********************************'!(%%%%%%%%%%%%%%%%%%& ', ,,,,,!!,!,,,,,!,,, !                                                      ************************--************************************'.',!,!!,!!!!!!!!,,,!!!!, .   ***---------------------------------------------------------------------------------------     -    %  % %  %%  %  ------------------------------           ----' .  , ''%( !!!    ------------------------------------------------------------------------------                       ------           --------------------------------               -*/     ,,, .%%  !                             *************-*************--*****************************/ !! !! !! '%$#     ###                                                **********************************-***************************************   ! !   $$"$#############                                       -***********************--************************************* ##### ""*$*                                           -*********************-****************************************** #########  $*$ ###################                                        ***************-*--*******************************************/       ##           *                                     *********************-********************************************/! !!! ,!+!!!!!!!!,*                                              ************-***-********************************************** !!,!,!!!,!,, . ! '%-,!!!!!!!!!!!,,!!,!,!!,!,                                          ****************-**-******************************************' , , ,  . '%, , ,, ,, , , , ,,, *                                    *************************-****************************************************''      ...'%"., , ,,,,,, ,  ,,                                           -************************-*****************************************.  .'(% ,!!!,,,!,,!!,,,!!!!,                                   ********************--**********************************'. .   '%!!! ,!!!,                                       -**********************--****************************************/' !  ! ,   ' + !!,,!# !##  !!  *                                  ************************-*******************************/ !!,!!! !!!! #!##                                   ************* ****--**********************************       # ###*                                  ********************--************************************ ### ### '%$    # #* *                                          *********************-************************************************ #  #%$ !!!                                        ****************************--*******************************************      #$!,!!!!,!,,,!!!!,*                                    ******************************-****************************************************/' !!!,,!!!! !, !!' ,,,,!,,,,,,,,, !! , ,,,,   *                                        *******************-*********************-************************************************' .   . ...  **                                        ********************-***********************-**************************************'..... '!,,!,!!!!!!!!! ! **                                   ******************-*********************--*****************************************'  , , , '+%,$ %%% $$$ $%%%%%%%%%%% *                                               *******************--******************-**********************************',! ,!,,, , !! .%$" '. ... ...**                                       *******************--*********************-************************************/ ,! . ,! ,! ' $"$####   #                                            ******************--******************--*********************************  # ,!,, $"##  **                                         ***************-************************-************************************/ #    ! '/$"$#########                                             ***************-**********************-************************************'  ! !!  !!''%#########*******-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------   ------------------------------------                      ---/'!., ! !,!  +$!,!    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------                  ---*/' , ! !,, !!,, %,,,,! ,!,!!!!!!!!! ,!,!!!**                             **********--*************************************--**************************************'.  !!' %! , ,   , , ,  **                                        ************-*****************************************--***************************************************..'' %,, , ,  , **                                         **************--*************************************--**********************************************'!!!! ,!!, ! , %(, ,,,!,,,!!!,,, ,,!,!!,!!,**                                           **************-*********************************--**************************************************'!%%%%%%%%%%%%%%%%#%%%$*"%!!!!!!!!!!!!!!**                                           -***********--********************************--******************************************/. ' %# ###  **                                                 -************--*****************************--*****************************************'    %$#%#$###                                                 -**************-***********************************--********************************************/.    '$$% ##$ *                                          ************--*****************************--********************************************      /%%# !!   ###### ****                                               -**********-************************--*****************************************    !   .% ,!,!,!,!!! *                                                  -********--***************************-*****************************************/   ,!,!, ''%!, ....... ..****                                        -***************-************************--****************************************'.  . .  '//+(% %##                                             -*************--*******************************--***********************************************.................%$%%%%%%%%%% %% %( %%%%*                                             -- *************-***************************-****************************************************'    .%*'........'.*                                           -*************-*****************************--**********************************************/!!!  %%# ""$""$"%#**                                             - *******************-*******************************-*********************************************/#    /#! *                                          --*************-****************************-************************************************** ##  $$% ####### **                                        -******************-***************************-************************************************'       %"%!,!!! ,,,*****                                             -***************-*****************************-*****************************************************'  , !!! ! , !!!.'% ,,,!,,!!!, ,, , , ,,****                                            --  ********************-*************************-*****************************************************************'..   ,   ,   .,,***                                                -*****************-***********************************-************************************************************'... ..... !!,!!,,!,,!,!,,!,,!!,!..**                                         -******************--**********************-***********************************************************/' !!!  ! ,! "$% ## ! !**                                            ****************-*********************************************************************************   !! !!+$**$ ####### ****                                         -*****************-***********************-*********************************************** ##    $%#### # # *** *                                          -  ****************--***************************-******************************************************** # #  +#$%   !***** *                                   *****************-********************-****************************************************'    !,!! ,,,,,, ,,,,,,,,, ,,, ,,.***                                             *****************--********************-***********************************************'.   ,!,  , ,, ,,,,,  ****------------------------------------------------------------------------------------------------------------------  -------------------------------------------------------   ---------------------------------------------              --------*****........./+(% !!!! ,******----------------------------------------------------------------------------------------------------------       --------------------------------------------------         ------------------------------------          ---------****/' !! !  , , //+$"-! ####*                            ********--*******-***************************************** #   $""%##### #****                                         **********--*********--**********************************************************/ #    #%%     ! ,!!! *                                  -***********--*********--*******************************************************************'    ,! ',. , ,,,,,,, !,, ,  ....                                **************--*********--*****************************************************************/' .. '+(!,  ,   ,,,,,,,..**                             -   *****************--*********--***********************************************************************'.....%%# ,!!!!!  ,                                **************--************-***********************************************************/.! ,      $%#### **                        - **************-*********--***************************************************************/#  ## %#                                 -   **********--*****************************************************************************/      %,. !,!!,,,,!, !!  , *                       -******************************************************************************.   !/+((,. , , ,,,,,,, , ,,***                   -      **********-***************************************************************** .....''%%!#  **                        --   *********************************************************************/,!!!!!'%%%# ##  ##                       -    ************************************************************  ##### . ,!!!,! ! ,!!!!!*                     -   **************************************************************************    ,,,, ,   .   ,  ,.*****                     -- ******** *****************************************************' ,    .   ''! !!!!,,, ,,,,,,,,**              -     *********************************************************************************.... $"%##$##  !!*           -  *****************************************************************************'.     #%$"#"#####**                 -   ***************************************************************************$"$"*"$"%$$$$$$$"$"" % .. , ,!!,!!,!!!,!!**              --     *************************************************************************************/'''''''''''''''%%%!  !!!!****         -   ************-**************************************************************'''',$ %%%%%% %(((%%%%(%(% %             ******** -****************************************************************/      *,,!!!!! '' ,!,,, ****               -   ******** --********************************************************/    .%$"%##########  *              ************ --*****************************************************'    ' #$    , ,! ,! ,,****                  ****--*************************************************'  .. '%%!,!!!!,!,!!!!!!,***            ********  *--*******************************************************/.  !   .'"$%%$$#$$****             ********--****************************************************       +*$"$## ****              *********** *--**************************************************/'!     + !,, , ,, , ,  , !,,*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------           --****.........'!,!!!,!!,!!!!!!!,!!!**----     --------       ------------------------------------------------------------------------------------------------------------------------------------------------------                 ---******/'!!  !!+      **    **-*-******************************** ## # (% !!,! ,  ! !!! !****    **-*--********************************      %..,  ,, .,  ****        *-*-**************************************************'..........''/%" ! ! ! ! ****           --*--********************************************'.    .''$*$######## *******                - -*******************************************************     ',$$  !,,!,.**********          *-** -*****************************************************/    %, , ,,,,,,,,!,,,,, !,.*******   **        ******--***********************************************..   ..'%#   .********* *          -****** ***********************************************/ .,!,,, ,/%$$$###**************  *       -* *****************************************/'   ##### %    !***********     -**-***********************************(/!!! !  !  /'( , , , , ,!!,,,,,************            -*   -**************************************//'. ........../'++$!!,!,,,,,,,,!!!**********         -**  *********************************** ' !! !..! '''"*##! #********* *        -*   ******************************'       $"% !,  ,!!*******************            -*  -**************************************.  ! !    '!%. ... . .  ************          --***  *-*******************************'.......%!,, !!!!, ,! !,!! *********************      -* ******************************************/' ! ! , !  . '-""$"%$""%"$$%$%%%"$%$$$"$$$**********************-           -*-****************************************       $%$%%%$"$$$$$$$%$$%$$-$$"-%**************-         -** -****************************************#     "&"%""****"$"$"$"$"$"$""$$*************************            --************************************/'.. '**********************  -          -**  --************************************/.  !!/++++*************************-            -*    --******************************%""""$$"$""""""$"-$""$ ''., !,, , ,! ,,,*************************         **    --********************************'$""""""""""""""""$"""" * '''''''..'.....**************************             -     --**********************.  !!! !! !!! ") "#  ######************************-         *   -****************************%"$ ### ### ************************            *         --*****************************++ . ,,,!!!!,!!! !****************************       -       -************************'...'.'''''''%,  , , ,  ***----                   ------------------------------------------------------------------------------------------------------------------------------------------------------ -------------'............. /$*" !!,!!!!!!!!!!!,,*----                    ------------------------------------------------------------------------------------------------------------------------------------------------------------------    ---------------------*'''...'  $""####### %#### **********************  - -*****************.!  #      / $######****************************** --     -********************/',!,  !!! !   ' %,     ************************************   -   *-************************....../, ,,,, ,, , ,,!!!!,,,, ,, *******************************   -    ************************** ... '.%%  .  , , , ******************************************     --    -************************       ! '($%.!,.',! ,! ,, !!!************************************     --              *****************    !    $""## #   *****************************************       --          -*************************/   !!!,   '$"" ### ##*****************************************        --           ***********' ... .. '/#%,     #  *********************************          --               -********************'......  ! ! , ,!!,,, ,!,.********************************          --       *****************/!   !  ,  '!.. .. ,,,,,,.'********************************         --         *-*********************** ##   ' %-" ##!,, , !!! !************************************         -             -*********************/. ,    $"*#####   ************************************       --         *-*********************/'  !!!  !  ! '#$$#  # #   !**********************************      -       -*********************'......! .. . %.. . !  !!'***********************************        -              ********************'............../'%#%$    .....'*******************************************     --             -****************'!! !! ,,  .   .. , %%%%%%%#%#(%%********************************       -           *-*****************     ,, !  %%,!!,! ##%##(((!****************************************       --                   *****************/ ###    *%**$""$!!!!!!,'..*****************************************   *-            -*****************/         $$"$$$#**********************************************     --                  -*********************'   ,! ,  //'%%. ,!,! !****************************************** *--            *********************....................'/.   ,,  .************************************************      --                     -***************'   ,!     .   '$ !!!!,!,!!,,!!,,!,!,,,,.*******************************************    -              ****************          '"*"# ##, ### **************************************************      *-                   -****************      # ""*#######  # ## ******************************************    *-             **************** !  !!!!!!!!  !'/%.,,,,,,,,, ,!! !, !, !!!*****************************************************    *                      ****************'''............''!.( ,,!,,, ,    . .*******---   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------******/'    .....'.(%%$ !      !!!!!********---             -----------  ---------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------  ---*******  !    .%"" ## !****************************                  - ***********/    !    '$$, #   ##### ,****************************                  -****************  ..   .. .  . ,,   ,.'*******************************************  *                    *********************!%%#$',,,,, ,,., ,,,,,,,,,********************************     * -              -***************** .....'.'.'$* %###   ####*********************************** ***                   -*************/   ..  ..  $" "##   # *********************************   **                      -   *****************       %!!!!!!!!!!!,! !!!!!*******************************     *                       -- **********************'..  ! ..'., ,( ,,    ,**********************************************     ****                     - ***************................./$$!  ## !, *************************    ****-                     -******************/.      "'/$######### *****************************    **** -                        --   ************/ ########### !! !,,      ****************************  -     *--                    -**********/     . , .  , ,  ,'*******************************  **-                        -   ***********'.................'%" !  !!######**************************   --    ** --                     --  * ***************'  ! ! !,! !!!! , '$""$  !  ##### ****************************   -   ****--                    ****************      "+%%%,,, ,!! , !!!!!!!!************************       -*****--                   -   ***************/       !,!,!,,,,!,,,,  ,'*********************** -- ********--                    -  ***************'.    .. ... ' %$-$####### #!  #************************   --  *****--                     ****************** !, , !... ,..,'$""     !  # ,*********************************   *-  ********-                 -    *****************    !  #%%- ,,, ,,  , ,****************************** --*******-                       ***************  ! ! ,  '%$,!!,!!!!!,!!,!!,,!,,,,.********************************   -*************                              ***************'..  ,  .   !.' /%%  ##############**************************   --***********-                     -    *****************' . ! ,! !!!!!'.,!!! ,  ***************************      -******                       -      *************         #!!,    , , !, ************************** -**********-                  -  ****************/ , !!,!,   !!! ' #%%### !!,*************************  --************-                        -        ****************/ !!,!!!!!!,,! !'',#%% ####  !!***************************** -******************-                   -   ***************/!  !(. !,,!!!!!!!!!!!!!!*******************************  ***************-                     -   *******************'!!,,! ,, ,,! .%! .,!, , ,,,,,,!!,!!!,,,!*---------------      -------------------------------------------------------------------    ----------------------------------------------------------------------------------------------------------------------------------     --***.............''%" ## ## #### -----------            ---------------------------------------------------------------        --------------------------------------------------------------------------------------------------------------------------------------------          ----******/       .#$% ,!! ! ! !  !***************************  *-*******-                -- **********       %!,!,,!!,, ,!!,!, ,!!, ,,!****************************** **************--*                       -   ****************/ !  , ,!  %%!   !!! ************************************** ******************--*                - *************'.   .........(%%  ##  !!******************************  *-*******************-                --  ********************       % ,,,,  ,,,!,,,,,,,,,! ,,******************************* *******************--              ********************'#$$$$"""""""$$"$$"$""""""&$%$"%,,!     ****************************      -************************-               --     ***********************'''''....'.....$ !'$$. !    ************************************-**************************-               -  *****************''''''''($. ,,,,,! ,,,,,!!!!!!,,**********************************   ***************************-              -            ****************           !,!,,,,,,,,,, ,!!!!,!!,.*********************************-************************                     *******************' , ! !!!,  #  ! ###***************************************  -*************************                    -     ********************/ !!!!!!!!!  ! , ,! , ! ,,, ! ! ,   , ************************************* *-*********************** *                          *****************            % ! ! ! ********************************   **-**************************** *             -         ***************/!!!!  $-$$-$-$-$$$$-$-$-$-$-$-$%%%%%**********************************   -****************** *                  ************.................... ''%"$! ,  , .*****************************-******************************                             *******************/ !, !, ! !  ! . !  ! ,!!!!!!!!!!!,*****************************   -************************* **           -  ***********************          .!!, , ,!!!!!,,,,,,!,!,!!!!!!************************************* -*****************************                  *****************/ !!!!,!!, ,!  #-$         !*****************************   ***-***********************************              -  **********************/.!!!,!!! !!! !!,,,  .#$% #   ###***************************************** *-****************************                  - ********************          ,!%  , ,, ,, ,,,,,.************************************-************************************               -     ********************'      # %%$#,!!!!,!!,,!,,! !!,, ,, ,!********************************************************************************              --          ******************' ... ..... '' $*###    !********************************************************************                 --  ****************/' ! !!, ,!  .$$########    !*************************************************************************              -     *******************/      , !  '+,     , , ,,, ,,**************************************** **********************************-***                  -    ***********************'  ! !!!!,   !  ,!,,, ,,,, ,,  ,,,,,.***************************************************************************                  -      ***************........... ''#-$#%%%# ******************************************************************--**                        ******************'        "%$########## ********************************************************************-**                        *******************/       %% ,,,, ,,, ,,!!!!!!!,!,,,,,,****------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------        ---*******/    , ..  !'%-, ,. ,, ,! ,  , .***-----     ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------           -****'........ ........'" !!! ####   #####*******************************--**                 *************        '*$  !!    ##******************************************************--**               ***********************' # ### .,% !!!! !!*********************************************************************--******                     *****************************' ,, !!,, , !,  ! !!,, ! .'+,%((((((( ***********************************************-*********************************--***                   ******************'.... ..  ....,'/#$%%$%-%%%%%%%%%%%%%%%%%%%%%**********************************************-****************************--***                   *******************/! !!!!!! . "$""*" "**$""""**""*$$"$%******************************************--************************--*                             *****************'    ## ******************************************--********************-**                            ******************/ !!!  ,,!, !,,!, !!.#.'''''''//'/***********************************************--******************-**                                *****************************  !!'/'' !  !!*************************************--******************-***                                  ****************** '!#$%%%%%%%%%-%-%%%%%%%%%%%%% % ! '****************************************-**************-**                             ************************' %$$%$$$%%%%%#%%%$$$$%%$%$-% , , ,,'*********************************************-*************--*                                     **************************+//////////''/''''.'''(% ...,!!,*************************************************-********************-****                                       ***************************%$(  !,!,! ********************************************--*****************--                                      **********************$%   #!#,**************************************************-******************-***                                        **********************/    ! !%%$"!# # ###   !*****************************************************--*********************--*                           *                   *********************** ...'%$$!!!,!  !!! .***********************************************************--**********************--***                                        ************************* /''''''''''''''''. '***********************************************************--**************--*                                               **************************'!  /+(%% !,!!,,!! !!. ****************************************************-*****************-                                                **************************/      '$$""###  !************************************************-*******************                                                    *****************************/     ''$* ## !   ## !*******************************************************************--                                                  ***********************         '$%!,!!, ! !! ,! !**************************************************************-***************-**                                                 ***************************'        ,.  ...'***************************************************************************--*                                             ********************.. .. ! ,,,,,,!, ,,,.******************************************************-******-**                                                    ****************************.. ...../ #  ######## !*******************************************************-*************-*                                                    *********************** !!   $""$#####******************************************************************-                                                **************************** '      %$$     ,*********************************************************************                                                 ****************************/    !' ' ',    '*********************************************-*****-                           -                    ********************************'........../.!!,!!!!!!!,*******************************************************--*                                        **************************************/'!   .  '#%###$$# ********************************************-**********-                         -                         ***********************************     $##$$#************************************************************-*                                          ***********************************     " !%. , . ,, ,, .***************************************************-********-**                                                 *******************************************,....... //!(!%%%%%%%%%. /*------------------------------------------------------------------------------------------------------------------------------------------          ----------------------------------------------------------------------------------------------------------------------------------------------          --************.... .... /'/" !! !%$%$$"""$""$#**-----------------------------------------------------------------------------------------------------------   -------------------------------------------------------------------------------------------------------------------------------------    --****************/!!    %"#,,,  $"""$$$""%***************-************--                                        *****************    ! -% !,!! ''''******************************************-*********-***                                             ************************/,      !!.. , *************************************--*********--                                         **********-****************......'/%# !! !,,!! !********************************************-************-                                           **********-  ************/.!   ,$ ### !!  ********************************************-**********-                                          **********-((-************** #     %$$ ,, ,!!,*************************************************--*****                                         ***********-(( ( -**************/! !!! ! '%% ,  ,'********************************************--*******                                          *****--( ( *****************''.....   . /%$    !  *******************************************--**********-                                      *************-((-' (-*************    '%    ******************************************--********                                           *********-- --(' **********/ !   !    #% ,, ,,,, ,,!!,,!, ,,,,************************************************--***********-                                      ***********-(--' ( *********************'   ,! .. .  !' %%%!!,,,,, ,,,,,,,!,,,,,.'*****************************************--*********-                                    ********* ( -('(-*************** ..  . .. ''$ ########*********************************************************-********-                                    ******-((----'( -****************/ !    #  # ###*************************************************-********-                                    ********-(-----' -********** '$""$""" &. ''!,. , . . ,  ..'************************************************--**************-                                     ******((----' (-***********+/// #$&%% , ,!!!,!!!!!!!****************************************--*********-                                 *****-( --$-'  ************  .,, !$''#$$    ####***************************--*********-                                      ******-((---' (-*********/       %! # ,!   ********************-********-                            **-((--- '' (***********  .,! '/... ,  . '****************************--********                  *             ***-(---(**********'.......%"" ! # !!!! ************************-******                                ***-(---'' ( *******'        !$""     ***************--*********                  *       ****-(--- ' *******/    !   %$.!!!!,, ,!!!,!!!***********--*********                        *             **-((----$ (-********/'.  , ! , .% , ,,,!!!!!,  .**********--*********                *        **- -----''  -******/' ! '##########**********-********                                ****-( ------'(********. ###  ,!,! ! *******--********                          **-(------$-$*' (-**********'         ,!!,!!,!!!!!!!*****-************                    ****-(---- '' *******/ !!!!!!!!,''$""%,!,,!!! !***--**********                         **( ----$-**  ******** !  !    $"  !!!!! *****-********                          -(---**'  ******' !!!!!   .% !!!,!, ,,,!!, ,!,****************                           *- (---$-*******'( ******** ..   '/%"$#####   !! !**********         )               ***-(------*** ' (*****.       %!!!!!!.***************              )              **-(---$$****' ( *******.     !!!',!,! ,, ,!!!!!!!,!!!,!!,'***********                            *-(---------*****' ( ******'!! , ,, ,!! !       # ****************                 ) )                -( -------$******' ( ****/' ! ,!! !  . '%- *" #   *********           ) ) ) )   )  ) )              *-(-$----*******'' ( ********/ !! ,      %.'/. %%%%%%%#%%#%%#%**************                )   )  ) )             ((-$****** ' ( -****/'.. .. .. /$ !!!! !!! ********             )   )  )  ) )                 - ----***** ' ( ** ! ... ! ! ' %$" .,,,, !!, .********                    )    ) )            -( ---**** '  -*        /%"$    !!! **************              )  )     )             ((--$-***** ' (( -' !!!  ! ,  ......'**********               )    ) ) ) ) ) )           *-(( ---***''  ('..............'%$% ##### ***************                ) )    ) )             ((--******  '' (      %%!  # !*************                )  )    ) )  )            -((---******' ( -'       !  .. ,, ,, ,!!,,,********            )   )  )  )           ((--********( ((-'' ,, ! !!!!!!,  ''!% ...  ..... '***************             )  ) ) )  )    )           **-(-**********-- ''......... .....  '%*"$# #  ### ***************              )    ) )  )           (----*************-(     # ##" %$"% !,   !!****************               )  ) ) )             -( ---***********---%!, !! !.%%%,,!!,,,,,,,,,, '***********************              ) ) )  )  )             ((----******----!.     .'' %$ ! !!,!!*****************              )))  )              *-(-*********--- ,, ! ! !!!!!!  !!!! .'%$$ ###  ********************            ) ) ) ) ) ) )              -(-*********$-     ###### # . , ,,!!,!! !!!! ************           )  ) ) ) ) )           -( ----*************-----   ..  !,!!,, ,! ,,!, !,,   ! !!,,,*******************         ) ) )  ) ) )           -((-$-***********--((%##%%%#%%#%%#%%%..'#$$ #   !  !******************            ) ) ) ) )  )  )        --(-****************--$!!(((! (% %#%%%#%"%%%%#%$"$$$$"") $""   !*******************        ) ) )  ) ) ) ) ) )         -(-******************--   !  ..  !! $$$$%%%%$""$%$$$$$%$"$$$$$*& %$,!!!!!,,, ,, ,******************         ) ) )) ) )        *-(---****** **  ! ! !!!  !! ''/  . ,,,,  ..'********************          ) ) ) )  )  )           -(--**********--!'.......  ...... ...  .  '.%$$! ###### !*****************           ) ) )  ) ) ) )         -(---***********-             %$ ##****************            )  ) ))           -(---*********--#              (! ,! ,   ****"************           ) ) ) )    )       -(--*********** . !!!!!!,!! !    .!  ,,!!!!! !!!!!,!!!!, .%.  ....... '**************          )   ) ))          -(----****** ................................................''%$., ,! !!!!  ,,,.***********************          ) ) ) ) )          -(--$*****   ******-!   ! ! !!!!!,!!!!!  !. ! , ,!"########******"**************           )  ) ) )          -(---*** ***********!##   #####       #   !!***************             ) ) ) ) )        -((---***   ****%             ,      , ,, ..*******************               )             -(--****  ********% .. .. ........      ..... ... ...! , ,!,!!,!,, ,,,, ,,..'****************               )        (-****   **-!'.............'..................... .  .''/$"$!###########*****************            )          -(--*    *****%!  # !    !    !       !  !!    . $%""- ## ## **************           ) )         ( ---**  ***-#    !!!!   ####  !!   ####     %"$.!  !!******************               )            - (----**   *! !    !  !! ,,   ! ! ! ! ! , ,!!!,,,,!!!!!!!!************                       -(--**** * *(.''... ....... ...... ............. .. . ''',%%%%%%%%%%%#%%%%%%%*****************              )             --**    *!'..'........... ........ . .  ..      .. . .. .#) '''.'....''' .*****************                      ---***   ***-!!,.! ! !..     !!!!!!! !!!,! !! ,      ")### # !! !***************                         -(---****    *** ##     ! !   !    #####      ## # *********************                     -(--****    **!     !!!  !   ! ! !     !  ! !  !  !! . , ,!!, ., !,,''*********************                  -(--****      ''.............. !.'    ... ..................   . '....''...... , ..*****************                -(-******    *-.................. .................. ........ '       **************                     -((-**** **(    !!! , ! ,, ,..    . ! .. ,, ,! ! ,,, , , ,,  ,! , .%%######****************                  (-****   !!    !           !!   !               #%#    ****************               -(-**    *      #####                #####  !%.     , , , ,, ***************               -(-****     **( !! !!! !!,  !,  ,!        !!!!  ! .  !  , ,! , !! !!!!    !(!... ..    '*********************                 (-****   !'.........................( ( !...  ....................  .....!' ,,,,,! !!!!,,,*********                 -(--**    *-!   ! ! ,!!  . .. ... .. /''/',!!!,!!!!!!!!!!! ,!!  !  .  ! ,!   ,,    ...'.+%    # !,*******************                 ***    **!  !!     !     ! !    !''-#    !   !!     '!##   ##  !************************            -(-**    !!        !!        !'$####    !      !   ! **************************                  -(-* -%(. !   !  !! !! ! , !!! ,  '#!!!!!!!!, ! ! !  !!!!,,!,!!  %% ,!,. ,!!!!! ,. .************************               -(-****  -!  ..!    !!  ,!     .  , '   ', ..................... ..............,'' .......'***********************                   (** -, ...........................( ................    ......'''..... '(% !, ,!!,,, !!,**********************                      --**  !......' .     .  ! . '( !         !     ,  ' $$$%#     !*************************                    (*  % !       ! !! ##  ! ! ! '( #       $$#  !  ,************************************                    - ** *!.  !   ##  ##  !!   '( !,!!,!, , , ,! !! ! ,, !, !!!!!!!!    !    !%%. ,,,,     ******************************************                 -   ('' .   ,! .. !  !!!!! ,, !, ,!!    .'''''...........................................  ........ '($     ...'********************************************                   *--**  ( ' ........................................'.'...'.  ... ! !!! !, , ,       .......... ''%$#,!!!!!!!!!******************************************                    -*    ! !!! !!!!!!!,!!!! ,, !! !,!!,!   !            !!!  !! !+#%%##########*****************************************                   *-   -% #   !!  ####    #   !!     ########## #%#   #*******************************************************                            ( #  !            !     ! !,#       # %,   .....  ,*********************** ) ! ! , !!!!!!,!! ,!     .......'................' ,.   ,!  .              '... ........... ..*"*****       )   )          '''''........... ........................................................... .....................................'$$     *******************************************************************  *          )            $-   !  !! !                          ! !   ! !         !"*$#######***************************************************** **..   !   !  !,!!!!!        !! ! !! (      %%(      #####################  !       ###########"$%     ,, , !!!, !!!!!! !!!!!,!,!!!!! !!!,,,, ,, ,!  ! ,!!, !!,!!!!!!!!!!!, !,!!,..,, !!!,   ,!,!!!!!   !, ! ! ,,!!!! !! ! !! , !!! ,  !! ! !,! !!! !!!!!! ,!  ,!!, !!!!!!!    #   !!!  !!! !    ##!  ! !!                     !! !  !   ! !        ... .. .. .   .   .., , ..... .. .    ,,,  .. ,!!................ ... . ..  .........   .....  .... ......   , !,........... ,.............., .................... ........................................................................................ ..... ... .....................''.,,! #$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"&%,,,  ,,,,,............. . . .. ...........   ,!,,!,,,,!!!!!!,,  ,!!,,,!!!!,!! !,   ! !,,,! , !!,!,,,!!!!!!!!,!!! ,,,   ! ! ,!!,!!! !, ,,!, ,,,,,. ! . , ! .  , , , ,, , , , ,, , ,, ,,,, ! , ! ,  , !! , ! !,!    ! , , ,! !    .,   .! , .. ... .  '  #!    $$##  #"""""""""""*"*"""""""""""""*"""""""""$$$%$$$$"""*"""""""""""""""""""""*"""""""$$"""""""$$$$$"""""""""""""""""""""""""$$$$"""""*"""$$"""""""""""""""""""""$*"$$$$$""""""""""""""""""" &       .                      !    !    ########     !      #%! !!   # , #%#%##$%%%#%#%%####%####%###%#%####$%%#(%%#%#%#%#$##$%#%%$#%#%##%%#%##%###%####%#%$%%%#%%### ##%%#%#%#########%##%#%%%#%##%##%#!##### ####%#$%#%#####$   ! ! $$$$$$$$"$$$$$$%%$$%#$%%##%%%#%$$$$$$$$$$$%%%%%%%#%%%$####%#%%#$#%#####%#%$$$$$$$$%%%%%#%%#%####%$#%##%%$%$$$$$$$$$$$$$$%$$$$$#%#%$ $###%$$$%%##%#%%$$$$$$$"""$$$$$"$$$$$$$$$$$$$"$$$$"                    !      ! !! !!! ,!,,,!, ,, .'''.'.'''''''''''''''    %#%#%%%%%%% (%(%#%%%%%%%(( ((((((# ((((##(%%%%#((( %(#((#%%%%%%%%%(%%% (%%#%#%###%%%#%%##%#%#    ,, ! !!! ,! ! ,! !!    ! !!! !!  !,! !!!! ,!, !! ! , !!  !!! '!(... .       .,,,, , , !, ,,, ,  , !, !,!,, , ,!,!,!,!, ,!! !!,,,  ,!! ,  !!!!!,,, ,!,,,!!,, ,  !,,  ,!!!..  , . !,,,, ,!,! ,!!!, ,!,.. ,,!, !! , ! ,, ,, ,!!!,, ,,   . ,, !,,! .  ...//'''''''''''''///'''''''''''''/''/'/'''''/'''''''//'/''......!%%%%##%#%%%%#%%%#%%%#%%%%%%%%%%%#%%%%%%%%%%%%%%%%%%%%%#%%%#%%%%%%%*($$%  !!    , ###### ####### ####   # !  #####     #### ! !!!!!!  # ############   #######  # $$$#! # "#"$$$## ###  # $"$$##   $$$$$$# !  $ #  ##   ".! ! !!!!,  ! ! ! ! ! ,  !  !,!    %$"$  !!!    , ! ,  #   ##   #  # !!!  !!!      ##             , !!!!        ,!          ! !  #              !         !              !  ##                          ! $ !!. . ,,! ,    , !,,!,!!!!,,,,,, ,,,,, ,,,, , ,, , ,,, ,  ,, ,!!!,,, ,  ,,,, ,!!,,,,!,!,  , !!!!,!!,,,,,,,,,, , ! ,! ,   . , . ,,,!,,,,,, ,,! , ,, . . ,!,, , ,, ,,,,,,,,,,,  .   , ,, ,,, ,  . .. !,.. ,!!!! ., !!!!, ,   .  ,!, ,,     .. ,                ..  !, !,  ...    , ,, , ! !, , ,, ,  ,   , , ,  ,  !,!!, ! , , ! ,,  ,  ,.! ,..,   !!!,  ,,,,  , ,,,,,,,,,,         ,,,.. . ,,,,, ,,,, , ,. ,,,.  , ,,! .. ,,!,,,,,,, , , ,,,,!,! ,,,!! . ,, , , ,,, , ,,,,,, ,,,,, .  ,    ,,, ,, ,,,, ......  , , ,..  ..... .  .....      . .       ..  ...... .. . ......    ....  ................................  ... ... ..''#$% !!# ! ##  ! #######!   ##%#%#%########  ####### #!!! ! ##  ######## ##   ############# # !!!! , ! ! ###### # #  # !  ###  !  ,! ###  #   ! ,  ########   !   #####  ! !  #   !  #       !! !                             '%$#. !! ! !!     #,,  ,,!! #######################  !!!    #        , !!!!!!!        ,    !!  , #######   !    #      ##  !  ###    !!,! !       !. !     !!    !!!  . ! #############%#%$$$$$$$$$$$$$$$$$$$$$$$$$"""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%$$$$$$$$$$$$$$$$$$$$$$$$$"$ '#$'........................ ...................... . . ........................................  ............. ................ .. ...... ......... ............'. .....!,!, !,,,,!!!!!!!! !! (###!!!####(#( (   %'-  !# !!  !  !! ,,!!,!!!  !!!!!!!!!!! !!       #  !! #      !!!!  !!!!!# ! #,,!!,!!!#(((((#% %#%%%%%%%%%#%%%#(%(((#%%%%%(%%%#%%#%%%%%%%##%%# #% #%%%#((  (( ((((((((( ((((((((! !.    !, ! !'/'''/-""$$%$$$$$"-%"$%% -""$% %""$$$"$"$""$""""$$"%%$"*"%%%"$%-$$$$$$$$-$%%%$"$-$-$"%$$"""""""""""""""""$-""""""""""""""$""$"""""-$$-"-$"-$-$%$"$-"$-$ $%$%-$-$$"-%$%$%$$"""""*"""""""""""""""$"$$$$"$-%%%$%$$-"$$$$"$$""""""""""$""""$"$"$$$$$$$ %$-$-"$$$% $%%$$$$%$$%%$$$$"$""$""""""""""$""$"$"$-$"$$$$%$"$$"$"""""""""$"$$$$% $$"$"$$$$$$$$$$-"$-$"$"$$"$$"$-"$-$$$$-%$%%$$-$$$$$$$$$$%$$$$$$$$$$%$$%$%$%%%%%%%##%%%%%%%$%%%%$%%%$%$%%######%%#%(#%$%# %#%###%%%%%%%%#%%#%%%#%%#%%%%%%########%%#!# ##%## ! #!!!!! . +$%! ##(###%#(###%((%## #%#%####%#%%#%#%#%%$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%##%##%#%###%#%#%##%##%%##%%#%################%%%! %%#%###%%###%##%#! ( ((######%#%##%##%#%((,(###((( (########(#(###%#%%##%%###%#####!!!,!!!!!!!! ! !! ! ,,!!///////////$##,#,!!,!!!!! !'%!,# #,  .,%,# , ,!# ! ,!!!!,.'$%## !%,#,,$ !,! !,". . !,!!! ,.!!! !'%%$, !,,%". ,   ,, %! ,,% #!$! ,,!,!!! !!,% ''$(!,!#,,!! !, !!! !$,  %,   # # !!! !  !!!!!,'$#. ,,%,#!!!!$ !,!  ! #$. ,#,,,!!# !!!!'"! !#.!, $* !,!#!,!, .%, , # " # !! ,, !!  $%% ! !! #!!, !, ,!,!,$,!!%,# ##,$ !,!!! !! ! .'%(%#! ,$ , !!  % !! ! ! ,#.". ! . ,!! !!#,! !!..%$!  !,, !,#,, .%! , #!$   , !!,!,! !'%%%$,%,, #% !#!!%,.",%%" %%%%% % $%%%,#,% ##%%%% ..$!,,#$ ! ,, $ !!!! $. .  #,,,,!!!! '#("! ! !! %$. ! #!, '$!!,,# $!! ! ,,!!!! , ,'$ %$""#%%$%%-$%%%%%$$#$%%,"%#%!$$"%$$"$%$% $%$%"%#(%$ #%%%$%%%%%!.% %%#! ! !# , !.$ !!#!!,! %."!. ! # !! # !! ,! !  %$!,! ,# %. !(# , .$,!  % #!"  ,! !!,  "$$$$($$%%$(%$%$$$$(#%$ $$%%#%$  "%%%%%$$$(#%%% %$%%"%%#%%%%$$%%%%% .$%%"$ %!%,$!###,%$%%#%%%"! #.#!$!#%#!!  %%#!,!'%!,  !#$ !#!  $,!,#!$# !! ! !!!! , .$%%"$ %#+$$$%%%%%$-%% %%%%!%$-%-$%%/%%(%-%% $$$"*$%%$%"$$%$%%%#-%%%$$% %%%%%"$%%%%$$%$(#(%%%$$#%%#%%%$%%!$$! ! !!#,% !!,#!, $ ,  #$.# #,!  ! ! ! !, ''/''!,/'/+'/'/+'/$$'!//.""%-%%$%%%%$%$%%$%%  !-$-!%%$%%$'"%%*$$%%%-$% "%/$%%$ %%%%%%%%%(!! #,'%$,!  !#,%#,!(,!!! ,.$! !.,#."#  !! ,!!   '"%%$%%$%$% $%%%%%%%$%%"$%%%$$%$% "%%%%%%%%/%%#$ !  ! # % # , ,!!!  .$!, #$ ,! ! ,!!!! %'$($ !#, !!#% !.!!! $  ,#$,   ,! ,, ,! !! '%$,!, !,. # !!#!! $! ,$. # , ! ,!, !! '%$,,#, ,.,,!,! $ ! #$,  ! ,!,$! ! , , %,.%$ !#, ,! !,!!! .$ ! % #$  ,  ! ,,". ! , !#  '%%$#,,!#% #! ,#,#,,$ ! ,!#.",   ,.$ ! ,! ,   '%$ !#!!,!# #!!,%!!!  .$!!, .$ , ! ,, $ ,,!  '% !#!,, #!,#!!!",! ,#.$. ,   , % !! ! !  .%$ #!!,%. #! !!!! !$!! , !  #.$. , ,#!  !!   .!'%$ !,!!,! ,#!,!!.$!! !% #". , # ,$ ,! ! , # %",,!!%,#.%! !,$,,   ,#.$. ,   $ ,, , !''%($ , !#%!   ,!, $, ,!,#.$. # #!  .$  ! ! ,#%$,,!,% !! #!!, $,,!.$  #!!!,$ ,,! !# !'%$ !!# %, %,,! , $,  !#.$,  , , $,, , ! !%$,!,%,  #!!,  $  ! %!$.#, !,."  , !!!,# !'''%($!,,# %   %! ,! ! ! !  !#.#,# , $..! ! !#!!'%$,!!%.% !! # !  ,!$! ! !-#, # ! ,," ! , !#!!'%%% !,,.% ! ,#! ,, , %  !# . !! , $.  !!,.% !%%  ,.%,! (#! ,!,$! ! #!# %,#!!,$ !, !! % !''$($!!% ! #,, !!,$,,!%!# %#,,, ,,$!!! !! !, .% !'%% , !!# % !! .%,!!!!!,!!!!  !%  !! ,, "!!! % !'%%,# !% !  #!! ! $!!,! !# # ,,   , $!!, % , ''+'%% .#.$ #! .%, ,!,$,,,# #.,# #! #! ,$. !!! % !!'$%,!.% #!!% !!% ,,!!# ! ! # ,!, "., ! !!, % !''%(#,# $ ! ! %!,!,$ ! ,#! ,#,!, !  ! ,! $! ,!! $ '%%!,  !,#$   %%   ,!%!!#! $,#! !! # !  !".  , !!! % ''%%,,!.$.! !, !% ,! #! !, ,,  $ !!!!  % !!''%%! !$! !#% ,!  % !#! ,%,! ! "!,!!! %''%# #.!#", %!!!!!%, ,#! ,%# !  , ! $.! ,!  % !'%  #$  !#% , !!!#, !!$ # .#! , !$.,!! # ''%%,#,." ! !#$ , !,!!,# %$%!!#!,!%!*,# #!! % ''$%$$#!-%%",%"  !$$####, !#!"%%"%$"-% #$%% %"#"%%$%%$%$# '+/$-$$%%'$$*$*%%- -" $%%-%%! %%%$,% $$%$$%$$$% $ "(%%%%%%$%($-#*%%# '""$%%%'!$"($$ $$%+%$%$%$$%($$ %"%% !''/$ %$$%+''%(+'/''%(!!%$%# -%% #-$%%(%$%%$% %%%%$%$!%"/%$%%%%%blobby-1.0rc3/data/gfx/font01.bmp0000644000175000017500000000337012042452377020060 0ustar danielknobedanielknobeBM6( v  L )& ) #4#/, n NJ&"35 PL z  QO,'3 5X5NJ lUS 4Y4$ W# t  J2G C I*(Z  )s'PLGC>9 : DwC{y~_\%  h  poje>WsW]Y EXEA= R@T@yx" TW`_ ] `fGF dj)*_)OY#20R:H_)oG6YL:u\blobby-1.0rc3/data/gfx/font02.bmp0000644000175000017500000000337012042452377020061 0ustar danielknobedanielknobeBM6( 6R61g08$7$GFMJ85,v*!B!  />F7H@/@H#@H@Iٍfg4l  qH\+ebܒ_1CL^GáOև&DM %'hC߉%@L2!7\7~<J &+3%.5XͲ5s4DeMo}VtC4>-4K͟,_s% 2KDSjqJ!Q(e<|%K_  vv~sU%1 m45c#q4G }j=*N Dy8\#p ­piZu=kvqĴʳqO"%&v}~zvblobby-1.0rc3/data/gfx/font03.bmp0000644000175000017500000000337012042452377020062 0ustar danielknobedanielknobeBM6( '*-27?8=E38BHP\?ES:?N+/;! *,6kpszt{Ж~ltPV;?g13N)  ϟҞӥˢݏ|dh?AC&&'/صᶸ``y ;910     blobby-1.0rc3/data/gfx/font04.bmp0000644000175000017500000000337012042452377020063 0ustar danielknobedanielknobeBM6(  HwF p(@ x)]> r [6jK & P3l.L 0 w  G#@ * / #b2" M40  K , % 0 7M&x 5 ) / 9 A@:3$  y~#     $1hdMF"  . 5(" #!$*)50))>vrMF!'2-=:QK;5!,(G=+$#4։^Y83$!E1. "ԄRO'$ B({xCA**w N(novuHH--** _utfgBCq1ØxyKL"OwwUV*,u( uuRT,/K DG,."bbrsBEU\\ylm/2 ffUW##\LKn}~PP::^UUblobby-1.0rc3/data/gfx/font40.bmp0000644000175000017500000000337012042452377020063 0ustar danielknobedanielknobeBM6( A[ <~ yW=*r^%%%% ]4J- YY{{wwkjQQ('O# KK!!b..?WWwrrge c &NNCC $$3hg}>:h &..OZZCJHi@7^aa)) g IGtrg0#%2KIFE  cYI47'@7WU 8F;aCQ9$L SQ1- ZD2fA[9"FEDOK  !<]:vHk=-L65ahc,$53M/qBdžP|F e_MD,L 7,h1%\, N7$aC'ze:|DBN4h]Xi\@.1& qR~Q}MyCEP͂H=$T<:]u_I@-8 rѴ︇p[J {aX;3#] oyШЕcCR!bobxtIK1z ,%0}k=w* 0'=Zh>!,"e8ӊ+6: tpJ@"R\80@D{`Ώc{7)0 t%~?0,LN=1C]4P Y-0.2MP*./+$!#h\6Ұ20/.ALB'uZzݟ{g`^jvh6 ##gKj|Êȿ~ÁǓ]W1Y%%blobby-1.0rc3/data/gfx/font05.bmp0000644000175000017500000000337012042452377020064 0ustar danielknobedanielknobeBM6(  $ >O [[M 4  ! s c 9  / %-=EVeelkqY`:= b. K[QPX]J^/#4;@T|^yƇtHY-!@  rv:) J/"&7-1' IdXըMŇ XC; %,37F og6D$,-f+Rm +B6IPQ d =f Nm D)#! 6[~c D/dZuL ?, D{ O ?5)''x QP6 F A 9;+LI=nRH)+  -d>98 ,Wu~b Pa-:($# %$C,r 7 <;6a|o,} O3a%M"O&h1='O8Y4P#f pyF|P 6/ 2?A`[tVj-8y "*ځUx'N:~"*8-6e 15B{Nh ;\7]"" \B={zh CBCA% G"jQCTbqK @=-&~E RF-g}=C`.  HFJEJFZTOB, ] +    !OM%U~/9b$  gckexpxsp^T9.'S"H* R@]aa#Q&9'o$O [X|yҍؒߙڗ׌y߆c_#M rQ fae_M6)-u    {jդuB!VX':f-F6"e"JvúzPt,Fk3#.+"~i{;W Cv%  IF8zXX+6 MW;ufO/  t_uPC #)tj_# *@N3rc(2 P<(= #L%I  ,A<'  e aX?L6NGPJ?9/  Ih8w{~w{en\OD-6 i\ڎv?N5ra5O"blobby-1.0rc3/data/gfx/font06.bmp0000644000175000017500000000337012042452377020065 0ustar danielknobedanielknobeBM6( )!M#@AJRMB 7 "  $ :&#! sJ )  N* =#&G)9aԀk}`lBK{@& h @+7' !m֎{RVb2  m#/J)0 OtX~ʋzNᖔQR]#KBzkߍ{KKPᛙXV_ vfߚ+):"##㡜d`fqzf ग़NHK fk СPIJ}ҡ} #)'(÷ζ|ikaJk͒ệG6LumnŶYGH."0ā}Z$ дhi ۉd7'9q]^ܣln=&&K9MrpAp 2()֐UWb76  }ZI,GuwgjKLH3HwM6!3`c^aFGjFhqٔM2.inLN'*rf͉:uB$7s8=FJ7:-. geKZ)B" @ @E6;03<xZoyK5MP +B[!')0,2+/')B0;ɁoEq1L#2$0#-$((*"h=L\;]+?+8.6&*BLcnTr?Uu.6.6ql|ucvfpQY& )  yrxy~]b26+ E-3ېꖠnvTXhj>?blobby-1.0rc3/data/gfx/font07.bmp0000644000175000017500000000337012042452377020066 0ustar danielknobedanielknobeBM6( w*s H  8@  #O52C },PeGV g )93pևAX ' GEeXr*S }-0iaяXZ7 oTj@@0=B|C[s)> DWdl!m5j6ATq;q:Y/ 1F= S / 5@mKbB]0l <!  R[po]n4E" wXP R UbmLT|[c&.    ()+<-/A35SbeW\+/&#"0%7(  9:DE&'+&H  $& +7586*%*$& FY!%! <=UUa_XSB<2)1)!F-/x/2,/-/02=>VWpp|c\TJA4-"Z!-.`xyzzss}|Ԥ洲vlZfQ*E 76AcaywtߦzXGq #!)>;KF=X;/Fblobby-1.0rc3/data/gfx/font43.bmp0000644000175000017500000000337012042452377020066 0ustar danielknobedanielknobeBM6( :-s+-  !5!#  E Ep   FD I ,  $F# M8H8IE W9  (& |  nlD?| _C  LCCaCmjD?/+  , *([  tr^ZROHfI.-,)*K)&\%'%!c &WVfeNJ-h-NL]ZHEEBDB98+h*";"0=0jhFB74\[ywjhTR@v>1K1  omID,)/poܥҕ}}b`FWF((&EZF`])&2n2jsi̵idiE=E tr:80-{x{Ⱥw_E_1%0OfN_]65-=- ״ĊÓ[T5RvsQO@\@ ޟn~Dy.,h{fywWmW$#) &ДށٲLW,Q978{y{E1CoQliΔE+& ~E~ v۽Li4W]MZɓɲRa&['$zWB/" Əlػ8r:_]Bj0JkKcr2  + ?{4\( ' ti XW E& gFk+mU2z(d+!o%&'t)Wr4[iZQO_R;z&S p鉹v7$,Sdv_c-blobby-1.0rc3/data/gfx/font08.bmp0000644000175000017500000000337012042452377020067 0ustar danielknobedanielknobeBM6(   ",)$3-$4<.D=,DA/G=,C4&6-#/  '&::6RI;_\BstNVekjgbZvJ{Q3T8$8 5/VMDȝIհQރF").'/P@RnoFqA1D DΟFݯ@1 B5CըޘhzN|C1DFۯ@.JbŽt~V~  O=!8K:*;؉z` [D\m  xa֛S ]^ 29RXӢQ 5%BzZ~5:BD9OϦH|T^QV-0  GJ#3EؽT hiptDH=?/ 246`a$1GD^Ddvu2ؙ|x}|   7ߪ<)* ^;dU1   '\ ]  TG&,Sjv|oU.#SU ]C&#NQ//+,9#;Y;Ze݋V?@_Cbn$gj`\* _ c  q4s*{ "c=g]NR22ؚ+-ebnqDF*++,km 57 iy? Ȧ *#%,,D0En{V:07FUVHk&n1$0sOt̊~O.-blobby-1.0rc3/data/gfx/font44.bmp0000644000175000017500000000337012042452377020067 0ustar danielknobedanielknobeBM6(  ):)  _^ecAl@- ߝ~DB9vv|z=<  ͩ~|  VcVGfFblobby-1.0rc3/data/gfx/font09.bmp0000644000175000017500000000337012042452377020070 0ustar danielknobedanielknobeBM6( @D@NRNHIHý\_[&*$OPOگq}oEME#(#SURÿΧWhW$1$  #%#իѪfe5K5"AEAƲ¤ܣYX%B$.  vuD~A'M&9 >V?igZX<:$V#> !$,$@N?RlQca~kjbbVTPK52X:*U_U\qZdcts}{҄މ`[A=!`B =I=|{ឮﭲ쏁~[X85!y I Ğע<];;O  n  £ߠol;|:5KxJok  B 3<3Ңc`0y/0  li83  u OcONJ!{=  #O$?;  VoV@;Q $   -) J`IڑEAx C   '4'Ԓda#  y Q 3 *,p kiPL    +;,if䈄c_FB=8E?KGB>4/">[OR" ΋  K#++Ufߧ2$ $    blobby-1.0rc3/data/gfx/font47.bmp0000644000175000017500000000337012042452377020072 0ustar danielknobedanielknobeBM6( UT7a6%E$ RQNL5q4<RQpmCA-n,a tsb^B    *~(G 8y661 O qnUP S"+H,xvki,%  a 0 ,rp@<q2``KF |6L|J}{LG  { 8 9`8zxEB   9  (D'soKG  9 IiYJ:a8yuJF s *(% @=l;tq>;i % 8KH (DxAa^)"Y I}Gmj/+v  =:GC'$0fdzw95{,$GE63(&'c&  tsNJ)%,)){'65IE7475 ŅwtRNOKd`sqmk_]\ZWyW|y:k:&c}bΡ걥ᤛ֚MpMblobby-1.0rc3/data/gfx/stange.bmp0000644000175000017500000001412612042452377020233 0ustar danielknobedanielknobeBMV6(B ʦPwlAЫ( |@wlA(%  DwlAЫ( |@wlA(%  8wlAЫ( |@wlA(%  ,wlAЫ( |@wlA(% @ @     @ @ @ @@@     @ @    @ @@   @ @ @    @   )@      @ @ @          @   @ @ @ @ @    @ @ @ @{%v "o { ${ -{$j" |  #o @ii{ #{ ${ #{ -{ "{#{"+z "{ /{ "{3{ #i{ %fv 4{ #  % $n$yv $lq $ $S { Ht ~ Cqg{ $qm /{ #{${ #x !{ #o$(!$r'(& 0  *$ L ( # % $ : 1 $ ! .l1u &f4{ #{ #{ #g7{ #!$ 4& &$ "#*#)J !$ ,%$ *&$ ' < A{ "{#{ "{){%{ #*$--##8($4%#$  $$"0/ %$ 6 $+(w "{ *{ (x !p #< $#= (E#7$L"#!/;&$"3'2%$*=+$. ! A!g{ #{7,{ "{ #{ # D?($ #@' ^AA)$ ;#0$01(7($$  $ "g${ "{/n',m o "J0#&$9&8 ,D#:4"PXa!N-%&#$ ! { #{ K{ 't  $(#(#$*$K$9%=-& G-,"-*R!%&!$By ${ 'k{ G{ (*)G$,"+,&$,%8!$B']!'$+37Z^E7F<_o]-A"C]/@"$18 /p #r{&{ A&M'322#(/5K3a&$&%%2+/XK8nSRT__jakU]Lb5X"1K+G'($6% #u. | "{ #{#k,,r5A.;Z949%1#/"3 -/s,5bLQG^fZ]gmy||hpLW1<& $V)#$1&$" 4q t { #)2=E0^0M/@&3%0$0) #<'.(5?Byv { $`2SD%"JEk0xB @ = KN?mXirbV;9#$ @'!B , $y "g{ $Wb4=;G9E0=,3%3]+P*!q\$&-#pPI ROE}txg\A,%"2(!$` fSy !B98$3(# lf3% Q+eQkuQX-:(54%#A # #iM{ "{)1>:;2?2?6>*7I4= 7#uhpKmZ:iO*jjiE<߀w]i7P"04$ ,(%2 ( 8{ ;{ "m'm.{ #{%a{ #{#f{ (x Rr { #T{ #*835LRI?(2_F/()$E%4#bs}hрLh(K +",+~LXW>ktLXOV:++:%)%'%N($2( "{ #{ "{ 4| { !pc{ #V{)>{ "{$R{ #\r ,{ "l{ Jq""{ #s! +(3)iN:'4&44=%K'5  !mh\Dq|]q;a>#(-+ [{{vah:E!6%$%$$# %{ "{ 3d. A/8*&+v  { [{ 0{ #m{'f h{ +&+"5$.&1&4:<q$7( 2~(ZtɄjN|*S~%g mC|sMX38Y%3!#$ % !$ky "{ "w !. #( *qz+ $g $/4,( " $'\&k 7{ #y8{ #l{ ${ #s "+-4$\#-5#1$#@ lcvKs^y=Y<"z} a{xw]eD #' #" ! ) #t &{ 1{ %{ #x !{ #{ "$92-!K&(C>#0(89u0N)t͂kӃOk+U5ol^fbBo~HR+9%)%  $9$$ #w{$m)j#$Z$%($"" $$$ ;1&#.$ -} "} %{ #{ =x !y !s  "j;O+0:+'*&<)"(%)! O gaBs^|=bJ.ZH \Swyz~\b\ ^a@r^t=UA-2$} ExywZb=]7-'$K #&! .z !w !{ Dk Z)!P&&4U!0@#!#\?' )'% ,%$$#{ <`{ #{.{ "$ $#=$$!#m- %$$5!'%2H#  b sކjOi,U6*. WfaE~vP[0<(+B8"#1%(%"=:<;)6{ \Zx *$8- -/\!*)!@*!6!;&1.,,'A) :$# : ' 3{ O{ "z "(*&0"###$#3"B$$"$$5#*ndY?s֊`x@]8%&"!jP}us?K#2&#'>+%$#3' # i j 3j{ %l:'5'8&3'5&/c2$C'#G?&*71I2DEB%H, $$$$%5,'n{ "o { "# *($, $#1)"/$r* "3$.$U)>+#*# Q tvm|R_.G0p\CTR;lyOh-8,'&$)F:$Bt!q=v { "/:.80VP6,9(572#K'$$+(75?hOX}DP6B*,#;%$C  $Y{ $x (|.1u,dJ3Et2!Mt/~3:Vj"+6d%+55c)] 54u/r- 21po # '*$#*.!h$$%#=E&#-##'( 5 n T\>ucvBaO2&"q@ Cyr{}ah@M&3&$:'# >} &w 2x N{ #5B6C3>0:0<'1'B9%C%_:LK]^f`kJ[N@ J?&#:  $ ${ ${){ 0.K0d.AVn*=!6}/5!6"8%;e5/"?x"B$;#7#:#:<]#F"6#8"8o0!6y7FsOi*"4u./5p+y :t!y $ #-#$ S*$$X=$ 0$$ $R (%1 J  $ 2 P qqń0RB=8.{ PLS6~pwR\1;!$2"$1#6 U2( 0{ #{ #s :F;G7@?@9k/;,:,%9$ 2")o:fsBmRwegAL@5'"T!D,$+& &{${&{&EOo*'5p,o,rKm-x"D":-:%8%;t':|8;'='=+=BAs'7(J{%=b#/)Q+=~&<%K$;#6}"5"9!5]9t".z/k/r*{ #^({ #{ 1jZ{ %kH,d{ #{3l{0{${#u4Z( $** #/$!43A$D$?X'$$#$&%$'( P( $jb?1י|CR><4%%rGkrv|oAL%E$#$"+$&z{ !u { ;{ /ZJ;FOF-A-@{@:.B0c/C/D/B5=.@,@1B~):4A*@l/>{%8&^p#:">-8z26'5r-z!4z.a.l"'`"+/p1` q&g #j j1| %|0| %{ &l ;{'z #{ #{ 0{ 1kl d` o!{ &{ #Z@p{ #x {2{ 7{#{ ){5{$p{ $v {*{ "{ F{ %{${ A{)f%.,9 77aK6 dcv_g?I/ !#3%$ " ) ${8k { I:?(=36%6,5$9%\&;:C/@,=0bE>4D1D1F8J5G=I0R6HBH6H:FBI3J-91D2E/^>E-J,A8R:Cq)N}*9"8!C6o;31J%<:r'*0~+~)z'{&uIhs !ji| %z#{ ${ 'g#{ *Z{ #{ #iq $v {${ 2d{%c{ #Z { %{ #e2{ #Y { "{ "| #i # / / $|  "s $ "! , 0 1$&#<$$ $!#" 9$ " .#?))5$#)L"XE(s aD0sbyCR ;:3}fsUufv~\n>HU6&,%-)!$# e~ ({ "Tx !8A:D=^PIYES>'5.+ # 3$lDN.ygqGk+95O$0#" &&{ $y #0O31A:!6-8+;,=3A4G/A/O3I4F=L5C7E9I:L:tCI>MfP;c8LPN=[;K9H0T(;"Gv"]#655410v(Gj"1))~'k P} $''m-#Yk0 '$ $i!p & 8& $ # F~"tG " 3 $ $ $ ( #-'}  j$$ * #6$ E+# $21{$<3 $$99%1741$<&6%%"K"4J-#S)&$(, 1#"0- I pomՅUj1S=8&TqbEO/flSf1>+:121&$$ $#mE{ #n BK]L?W@IF@R?&6)-##$N;)\stmuZe7E-@'!"J-!$| -)| ,| %| 15=4 /4*M*R%8)=.<,\1@2?TF4CEK@>APM=L:K8\FI4F1DZL6ERC3B=D$</#34.40h#-.9/:)%"['&! F )>;~ %5% $ @$#57 ! o   &?w # # n+&+&H'$& .5& 'B$  $.S$#$1& %$# %)+-95B9N5@/;;.C.?)&$$#")&15+A"!s b>9u†dzEo"S >+bs Nujyik#J*1$ 2k|)*}A#0!Q3-!v<:&4'63A0F5G2>7F9G8CK`O;K9G6F5N0M.L+=*=%6%O+6(@6$1!,*"-=*)&(/ &)5$(#J#$ A%'# +  %$*"1*8:1+  -)$#+'&.&$#<-)#:($7VR" . !/>7DL[Yc]hVEQLC&3)*1$4#&. enfpWy4eD"frq b?f7iT^T@5%$Z!%$ # !y !{#a==PD1L(=2,$5 5VJ-Q=ylvR]2D,'3(&!$ 9<&kh}){+.-3!W#3'4A>/A.;5C8F8AFC9$T#*0*#(#%&*,%.(  "!$$V)23#!35>#=%"%$B.0ECZ[mn}nxXbAM-:)+"8 #) IU!, !fV71vfրGr#F ne[KKGqiyenXPD,6&#$$-$6$ #[{ #{ "?L@L?L'A3&O<%+F'D$L'$!5$$$T%$&#:?#$%9;&",%#!$%((7( ' !,#vs*jEQH6B;aCdBQ[U\ZTZPZ[_Ua[lYcXe_p\akayg{rep`iq~_gak]o\x]iZdYd{hS^eaP|{Oi{VDP=HLK7C7D2>3t&(X-@%/$%$("" $$5+$#O(/%( $! $(Eq4%*$K*8'"&%6N#$$3"&7'iq"+LEK3Ȍ앚v^hJT3@$3(%6d'# (v ]vfyGm$Z 1)%fmStjxrpGQ&;&0$#$)-@%%&j{ )a{ #=F>L>L*!W<o^香p^b=C&;-'# glzWoՆX5gF51sQQ!Gs|Xk8B'/$# 0V, $ 8u #{ $f]MNdKE?]?D1>'6D%?" g;:Z1R<$^YYh2=C>58G5L=/<8>,9$P52:/$B%#!+H#$&$#P&[1W/!/!/4*U'%,B#,R($#,(>7<,7I^AMOYcbY]h\eg^Q\LdfP1?l:-6))'L!!|3$Bp N(z^G=~|ze{KWXZ$01! =%w e).u֌fHf$H ==/hXHr[ߘ`gFX(618%"#!$C% $%{ &v !f8B>IfF@JBa1;'^M)# ;[3T8meWIruqqSa4`"@% $""1 T6,+('*(+,!["1@;5<<=,W;A6J8F>ICSlUE]O[qWkU]_ejijoploxqzs{xyvzx|~x|xy}u|t}qoxgoisfpmlkm[gRZT_O[IbGSAcLHNMMI;`1;gd-9&3-&'+5($('$5';+$49k;FHRgegq~}}}t{pyblXdQV@E*8 @'T&I;  y'f]Rq(Q#}C/ R\Aߧ醌t|XHEA8&( "zg mZpֈZm7O;61"i]!J"㊄rdbKA4 #$$$H! $u#{ #{ 6t >KK8C6E.:)5:@'!'";[4U"Ae\Ldeqy_jpQ#08&F"#J)0,&+.A5!3-5(2)82:1@EH8HTKNlDPjVMXQ[V`g\eamfwcixs{uu}⁇ޖ{wsnx}hs|k^ideV`sbQ\dZH[pTDYXYHVOVYc`ndrlsq{pd|\]=I58?(7''&&C* .%03BCNH~PZ^hgnii[k`W;A$2#'$"!#UL$A A++1.;QUJVT\irjimgoito߄폜sqkvT7E*0-zhP;h}Qp"J4-|^TUo^~cMX=B) * x `" vhJo&R 4(/'#WaVzeoEP(6*&$-#!<<"$ a8n{%{%>K=J=K;QYY1>%H*"%  %;k4e$U%tuS1\+xomOZ/=42&!C!7L,*\$//2!/-+7+9Q>1N/4SC:FaLEQaPPXYZWa]flaigvj~qzxsx||ntdi[_WUmMJ^?BY<@^?GlJINRZ^dnmt|}|~qjnetbahohWaT]NYXXITN]q{lik{~|xxSyOgM6,C$& U(',5EBMR^`juv~낏avJW2B',E''%(*H&8,`4:#N[ ntcCiF&!s\G/ EE2ʍ囏bmvST4 g(k_r\}9i> 9>.)fL#qRZZG*%$#L"#$ .{'{ #m=J>^;FW7D/D%1+`M! ;k4f%T.&$^Gtes{\f9X>4$.T#)(FA 0$["0(6'5,;1?<]8U:A @4 %o_dp}W .[ _uՈjLq(B :0dX{cclX(7,&<&$L!-h #{ $m Y)KE9D=H:T7E-8&:*Y& ? ;k4g%T 0,+r BfI:mmIT,;!,D)#D1.!0H#2)7+>[>4AJJ=IIZEsIe^ZR[\^ci_horoqzv|lhP<>!!!!!! !!'&(122778==>BBCHEEFEEAAB889.//''( !R:=cg|~t{lhqcmkYcT]JgHU]UxeJK?(F$@r1\).b5tU`6B",!!('9,S !4-[41t*MINS=<줊ޏrLQ3=)A!8(?$$' ?2R,5$"/ ;\4V%Q 4(71_9 y}nyUe@G!/0'6,#G ,$6$0)SD42P4@7i;F@Y>GCM\Za`Wb^hcmgonvtxkjY>@$$$"!",+-GGG\^\ghhjkjfggffgcca_^_ZZ[RRSLLLLKLQQQWWWQQQBBB)((_@Hejpxj}qn^gYbPZJTOU'J$G JGB;|2j+Ӆzmk:C#1)0'(* E  "l"&]A T^=A{{}uWb:E("&8!"ND '?#e&:'&/z"/#YM5 / pkS᠘]h?T(/c }{`iwRn0Z63*T1Cx' OGZ|a듧}^a*ldrևiӁLo([ ?30dXyZweoEX%/;=*$>$! * !1%l{ >{ 0^)8CyHIGuKGRU>&4-1! ;[4U%Q?(12rClTwomH_,9'290-'(//2&4#F+35:8UF]7J>SBNCLNY`^XcX`drimyנ~MP)%'!!#324NNNdddrrqttsqqrqqqnnmkljgef```YXYPPQOOPTRTYYYZZZZZZZZZVVVGGF../($&SW{㐀zkremai\f]_MUXk*b'Y"PF@=3_*jqTz}juL]8:-)%" -\L dUclKGfeAz{`j ; @!B ɌZf(.% m,(m}^{?^7.43&}ZG;EN5|Yd'z*bnIp[o:b>!1+" x'-J(t|^lB@N  2(<$ ".c{ #{A5BAJ&5Z!+  ;k4f%W?/2,h9 @wyT^7F$6E16$."/&2'599$=1>=B8DLGBNX\KZSpY[fuqhpo|{nteBE'()556NMM\[[kkkxtuzyyzzzxxyvvxvutppnfee]][QQQQQQ\\\cdc```[[]YZZYYYYYYYYZPPPA@A&&'! W<>pᑇ|{qxroreW_QWoˀWEi.P!EE;5o/"l2s{Yc9E0+14!'#le3T"I 6#/#YOt~esO_0=$" $!(! 2##+,  23y.!FM< 22O/J$kozL=oOZlldfځMr+M+ 4)e")a8 "*~ua q%q{gsLi)N x"*#v) UhZ|h^S*B9R*$#6($ Ex!bJq { "3ufu_kHO*72')2  Ym>T~Ek+W6):1 QeXmYx;K 8h) $"U7 ?;CD@+#:!F4KCZF^=L-=LV=O4&s#![6jd}\G 5| e[=k܆Y{:bH.44*,58.j5 )$s[OMY nZ@lz[v;e9j"Yd!V#߁t}\_HE$,! "##B$$!l{4{ $5VFB4K6TTM-:#2">&"<  7W1R#G 4"#>F4*d.A&oxha6@$252!/42&3*UD5?B5A=Zq}bkއq'5-0EA'##2W'+K%E? 8<45 I#XC`^mj~gyWd>M%?B4u%(H. tf_e"*r(jҀc݀Hf%W F6)1(!(1-gM6 1?'﬛GT-hwb}Kp)L63&NeXdd~c{HT+F"")*16$$- $yW{ #{ ${ #LEvJ7LAC2?+9GP&#*}5P/P"B 0,HL6*], khwcmUL-6"0)1`9,9+8bA2E8EMJ^NCMKvMYk_Zbbkqur{yr|aJMLLNkml|~~~~{~~vxyttuxxxxxxuuuqrqjjkdefeeeiggghhfffaaa]]]ZYYZZZWXWQQRIII>>? K35pvqzflazUYЗ|ۙfG{-i TF~6a+O$}t}]f8D%V/&y K t ai\}Ep$N (&XUswia_/N+#$}_+5V1Y.G $&)(),>F,mwM}}ْhӁLi0ZO,d#.f8 F|pxFd' ]dHiЁUl4ZD#h&+ );5/&o7 3zp9R("u`;^zSv7W?,(#"zG荃y{Xb9J)$%=G' B  %{ 2jf6C9E5n4A2?/P +"$S 2_-X A .2527,|NCS2~nyib8D+L) ++8-A2?2<1;leC^SVQcS`e`tisj{vSVVUWqrr||{zz{~~{||~~~~~uuullkefgeeghhhhhgfeedaa]]]ZZZZZZYYYQQQIIJ?>@  ^6C3@6BZJTIFTKVRbpd^lrp}}z`oWWXsrsvvxutu{{yyxvkmndegfffghgggggefdcc^^^ZZZZZZWWXPPQIIJ=== !\X쌈tymv_uhdϠy_x?e(U L@>|3_nJtwOX4A*''" i%fzXs( ,}fP'6($t Y \#y\};[A %#"m^RvgR{UN*kySguPe.P7-'%4*'--0,'y=5 ^pEag4j1=e/:9MZKCNIUNZU_]eblrzzo{YVUjkkyzzrrrvvuvvvmmmgkihhhggghghgefaac^^^YYYYYYWWXPPPGGG556(%'lowochZoӜݝi֋Kq0\"PKF7n.y]h>J"/6O6# r he_rGe&I =)%9 Ns~jtcs4G.)!(Cw9*CY9b&L 85()z=AC?(#$ [ Rzm{^u=YC5(("gQ{{]g# mX2lŀ\i=YG::$)4$|$#(1=; U`0m0XS#'2A-M(HF6 X/ dUckHT3:"R&&$K1%#!={ !mn {54@2?8C9?kE1<'5!F& !(vL)4f-aM 1)*))[flS{{eoHT3?+7ED0<5TQKYrFU{YP[T]^cdmsw~{gkqqp|}|~{{{yyyqsrjjjggghhhgfgeefa`a]]]ZZZZZZWWWOOOFFF))+W>I鍃qhn_ctV}8h$V RR@v2p[fsGS*;1)'!{^X@dpRg2K941*WTgQᄘ\f9O$2W+! #Ig?g+P16'!!M^^ {mufLf*@ *-"4:077,4D8 zhX|#PCU'?88 2cXQ< <{rWa9E-#L !)+$ &{ #u"{')R3@=?3J.91;(7 /%= aP+5^-P9 (*4454)+2r(^lJs|[ffM[AP]:@5Q=KBNHTMUmgZ_a}gmqzxwxxy{{{qrrklkgggfffgggded```]]]YYYYYYXXWPPP@@A!!"ae}}mtchcg{֙a̓Ck*Q!PZL6@[9{nwb[3R!/ F"4 l gsZg>M8/(-z\-^)vxooLT+9;!  g NkFl1[2*+$!Zapug?L% bT jZGuUjXlCR.5(~axpFN1 jbBlYr9V=*" 4@B; > 2%6(_]RC \ %h4!0 6/ (,$];cQ{iwu{*:(SR#62@$$5* 6p { #s %[B,74]MAFA,@`cg=M(#M}I*4^,Fy/ wq)*5>0.`%L~|҈oRS8F3BCdSG9QDXHVP[Wc^~`mqs|~y~zzyttsmmmgfgeeedeedddaaa[[[YYYZZYWWWNNN///Y=Byw|gllalMr2S"JFD:e+zyZdBK<16+>O$sh[azIa(D 4)%qlooohp\T?B!=c WcMc:bA..*uYiexeBK@, zW f_IחpQ^0ID/icfR]a= [ nxdxHg%Q B, 4?C 5<J=B:(YRM+a24::A*(,l`at|Zd;F,8Z 9#&% # &{ 1{'4{#3?8V-82?0=0:&36@&#D7wX-3e+VA)!((+55)%ZDg[Jz~_hRcBI7D:IDeESNWT[|j`juxxx|vz|~{tttjmmfgggfffeeddd`_`ZYZYYYZZZWWWCCC msu|i|gg|u֓Xt:U$IA?A5{_u{emHU+9/.)%} dg>eSq4WN?1zej{bJmu[`8O!.6% cUTeAW$? )!zmrYt}XYrI`,M/!"UfH돜ߋx^g3Cnޔۙzj|Vx3d1$e?H^@ W nɂ`lC^ I 2g)8X=4UE\ET8P#JD5h'916C4G0?/"!Z2 Gv~_h7B(@"  6$ $w !{ "{ #3?1=4H3?/;)4$1(F-#8R  `Q40I(PD$))}s$.,1. q&nwO퉒~ejJWFJLKGPHkNZS\Zsakiory|󏇧|{{srqihheeefeffefcac^]]ZZZYYYZZZGGG4%&~|qyhuaamO}2_!WNC7_)Ҙwv~Yd>J[5)#N( tjWb}Lv+K+*/QNmfluTe8D=-"g!Om4d<-&q `#raSvfTgaNZ#wwRۦܪѕzd9X4"W5"4%] kWgtPi.K-+%}ad|-}COs^yXlD])V@-+*(*+))?767&Q; jTirnbS;)$( $, #g{ #{ $2LTi3A2?86*8'U7=%C j`2/Y'XB/4*hi .0-45 u& Mwrⓒ{odCLALDPJVbYe`aefppnryrxyyynpphihffffffhggcaa\\\Y[ZZZZSSSƉt}qrkq_gsߣvږYz:]$VYM:(v;y\{tJVbI71# K hiO6gفV7`3i(+!upbErgk@J#12!/wy ]dRl;hA-(Yh`MVP;!J"w`ፐ`{ ' |I8puP||ؒat7R0K$3|nD5֗\}=[@:0$~{'!D|l>jqfLg-UH=4&'1*5'22$yNH׆zwjAG(32$<&!$#''${ F{ "z "/;:j3M4T*6)8!3C , a?-a%[F-*+0=1'.* s% [#QcBjrQ[Jn>QOWQ\Vdmgr롆uzxyynnnfgfddeedfeee___Z\ZZ[ZUUV)))`evjo_eY\}ezDi+c[Q6-d:3b8~qzU^2>!.;)& \ip^yDd"> m%pYryhrOZ+W"&'f`QUuBn$J()!kFUrT: DDbXE{wrzJf+   L1XJ3{=.C') ! .^ fLi*L 551)!n2$5,w~ٓnyQk1SA ;3'4*)_ !Sf[DktM[/ZL?3*$ $$ $ = %y${ ${ #4M2?,754-;W9!3'!"$  LH+M#QD)!$05 - . 320- 1 /n,w|bkf]VXPX~Ydakhppyyxxxnoneeedaceeecde`^_YXXOOP///Ygzotgj|WcԝnȊOo1d]U;Z'H fbv`k`J'5!')$ [ d]EdvOk.G-*oedYt[d9D%jjcEa7zk~zR^F84  #4g fS)'-;Z%D}{w]hHI+), $$E" % "{ 5y '{ >{ X-72?hN*5-@&;,9.&# Ht7*G"NA36940=7:FC D =Am4>a)'(:"D k,=f[@~uoGPA`($7"%9$$# !x ?o*{!&YC)67>S@P=99E;'O>'.#('% ><(LC++45 ==&R.R(MS@ 3 :J$n7?"xrlzYcX\{]d_uhqpw~Ƴ~uuuhhhedc`a`YZXLLKMLLCCClEHֈy{ekgeSV”}dEl)KGP=1h(|uvq_OD<(6%%%5(o jm`|Gg&I 1/3."LotҕpOY/< $vbyWk@O D:5>4*44(10#lD cRBTfgJpzXb:Q0B-"K]`BjUq4R7*23saT|>IU  `&xc̪ןZu9YA =600)5*h26%H'xzsztfps)3/' L$,?%N- "{ 7y !{ #s $.=481=0=(5+9'3.2r?$O' -K(GC ..4* 26oj:E\=^/cM 5 ;A.V)&P3f}XT][ebxixt|ܕǻŶzzyqqqfefXZXPOOLLKQQSKKK`>B|r~gleuT\{nP1bRI1y,~-yawbnCZ/5&' ( n iZ@fRp3T0}")'RcSx\hFG$ 7 z hU_yLb+D4(44445>=0ymvulSMq_⇍Xc=J'5  ]#n`sCZ {11=-adwhgcO  Bpy؏\}:bL H=%*?0&&)l> fbB}ktOW;9,'" U<'78(7 pG{ #V^-CkA0=5>0;/D-3%)%H# -K(TS 5#);&&VlThFe.XE 3425_/yk뗧v~aepU]dxڀqqy|~οxyxhhhYYYRPQWWWYY]OORY>@샊~}r{rzT]UU֞wٗ[;j"WK:60:v:xfs^\,FQ, #J j$j΁\}@oM%,+Z0E"rd}Na"2*iZ9gX{:Z>'-?*h*@>46?(`y smRGU\@ΐ|X_Bh-5" n k[gPl.G-50qovviGkn1@"lJ yi̙z؏^;;5)"($na{zu|]g?9H-:'4(A)=A> /!+U'ZQ ;d`4jMgy[h@W"?w&n$-4[+P/ړ힙pymjhyu}ʿź~~~qqqfefcaceeddddTUU"""a@H{sf_gW]KU}ɗfEq(ZVMA4 u(zxp[d5A!.(06wT jyUd}Lx+_6(7.z?ߐsnrRg#+" l mdJo(] <3?5+AC 0 3 F0hd_Vkl^G$="ˇ~mxDK+{ gI2m\}=Uy."23|#cRyYbG%] 7u{]ݟ|_r=]U 4->76=80*)]cbD{muk[,3$2&$KH $?$e- t cO{ #=A.;/H;8(H26*1%  .3*sC$,U'VH <3'$%/ rȂlUr3XB988|4oSw}nrnlm||ŻxxxtrrrrrppplllXWX#$%nIJ{r|lvbhU[L_l˗pÈRu1XE?6{, h%{jNmiJR?%-| %hV5iӁX9j=%"%ToU@r{`j@Z#1 s gpHmYt9Z:35**A =:JU@t& "{d"sjABzoza=P- j mxfLi*M *,E-Y4 kVnxFO7(p J l[I|ŋ_u/=+8(10.#5# 4$+D&E3',552-, $xsOxُfGv(]F<DE= e8vvkotp||{{zutt[[[$$&yRW~~~_ga^NQyANȑwƍ]} &od sgLj(D -%hs'01F)^7h:Z3L%M5l(8)|KEGseaa4: v ha@l݇Yw9b='++u3 !Dyei7D&! ZJ lbIݞz]{:Z~0 36%on*z> HaY?뉄wMX8N5,  $"&#0') 4mc U-:I;L=(=*8&4 .'PW!'ZK++G%H4$#*30Fr'"h |tŇ[9iN=EJ@z<@{O栞䗛ukhnuzⷹ¹{||\[\&&'\`p{em`V_LgBEs{gGx'X5- o% [Vtlsy_|>K%G(+&!" +p fzMf݂Ru1\96:(~Q]WpyZx8_# k jwRr։^mO0'u\HQX4vdjGW!:| \wÆmՆQu-Y:% )/'1*6"mDUwgh[C`)K7!({1H?τku8F'nrexJjUz4]<]2J(4!BTN4wpsCOh9 hT ofOܜwYu6YR 2m*5>1pm^C[3|n^.?,%0#L%#$ $ ${4g{%&2XU*7?2,8)%H@( )%oFH..UxQ-1ٖs֋Wu7[ SPPI!E&zDa{`㣯쯣zyĸSSUB37~~kucmaiT^SaAG;?|w_zڕh~H['OE + \FLkSt`h>B#$ugS4k܋W7hP,W4M* K[Xr~dk?I*02"t \yymσ7XH9(&vMYU26Lh]6zˊ~ٓpV8YAD=)r^5 YYA܂qz^QHvcdUgP{.X5,(*#hveJ|dpmN) n T ncיs֊Rn0ZE$ h('~a. E?-{Wj1?#&1<"%4^Q, $ ${ -{ #{ #>9,9\92B)8(6)(@'$$+  ?G$ZG 0>;%dC64B,F0g)knY~ڕk̂Li/]b^P F$F*wF囟wzۦ~~}KLMvWdzkofamZjRrKWLK@@s04|ΏodžTn1W6 n! m!X?$>rfi^K*B/"B dvgЂEd"PE2#$c!K~zhrRUN=6# l `:3pQn,XB4;-iB > cL",$ylt[v:ZUC70*hAh|[e%+; g9-mʂ\}=]HC=6,} Uz|jrT`??# eOv~boOr+^8-5)4)%KGrh~`l 01s n l:/R*8/:B/AS%1#/6)"$' &J#OI =F:}$J9|H%A4$$|$^~ؐzݒa}Ad7\)QE>$x>)kA;gL壬̖y씖ĻsssKKLntoxaijhYdZ\PU>E7<16zku`x?\@ 5 3*b?hZ~m`^g/D'-hjeHgsR`1TB4/(gDnUyifa/1  7_ msW}ŌhӀFl$M .39*vMLr^LG 8 u^u֌[o:ZD20)xN!L fn8D, jnrf~Lb*J 958+ktZ]eBM#,"! $j WlŀIh(@-3;5'*!sEc5lQ\h>&*<"#"$$$ &r &{ $t ({ C7797,9&:$D&2<*C$'.+"'R#E= 24=-(&Bh/5{)0x~oW{5WHF?4w UjuihxNF-: Nkgj*U *&5%\EŋhqgVW**%/ Q]3vY5^<-.!hgR]˃sPY.wV?tیgJh(B 08"ju1c+kwUi@2}fodHf%?14 MPLp|Xd=JJM"0 b[& ߛ}Ǎcv?fM =,h),"$sDS0oxR]0<#,6==##" , #t7o &4A0:?-5A3'C&26..($; rP*&Y"PF;!(,/l$5A/:'O&$'#v` !yaAm)VM U$X(I-tH?wTथ}ijuq늆|u}lfp_ijdpYLWHQ=k8B/4(2~z[bـ@] L<2)"lacEouo[M=$4)U i\9jԃXz7`2(8*X/ XJ7[[BO)+-%^dhBr։Rn.YC1.|L? BRzm]g7NuyOdU8Y5)jvedtd9F V% hpHjUq4M/0#sZ0 PD0xՈpCS&1=>'!%~^U.'y]x9aA 'j*=0&"Ligu|blGN,9*#''!!&!#$_"eqVܜuۏZ;q(c S$N)U-W2S[fꨓ遇ӯy~zpxgn}w`hWbflJUBK?\-(*&]~^Hn[[b<">6 @smlkfNhA](I+}"kbGmm}XGH0-#$S ^+Ƒe~Cn$D 45'g!~PA RV#J:):2o$.'WVCOUsF9B% _nqfـoҁRk F*#A/N_aw_h@K%-1$$6?#!  T N~{|Ōc?eN < .&`'8f]wdmCO$1'$ #$ x% ) #h=$1B.'5&/)6 *$Q/30$!%$3^1\: #%-)|26-94:#1)$-=('&"b!~oOr5_%["^'W*N/N4{R񽫬v|{y}lvqa}qdRfKUG[aT=W:J;A3@*4,3 ({jX;pތ_t>X>%e&%ZLwooyiYFIC44];1$ !y fhBkZ:a'xy j$b%rtohUd#1 S_6ȓڔeDf%H7,_v |} $+,(rJ<@DjE*,q u hkAlY{9aG-43 q!N kud\4T= $#%?%! e^/(xӍ[t7XI%'+7S"!MxxMY;./+8H4 4 nr}hڃMk*N6"&)} ^iV|ttfm[aXxjN;;&'V mxdIz7Z3OQ" TkLlY3=i 'i K`WMޡhHe*MC)$(.,"-8'f60uX:%6%0%"&##] fn݆dHt%P <<&ZXMhbywW`N"@#$"/1 M eN<ȓrRn.Y9"e~'ePwWwQg4A+2%$$9#  : A+*2U7&3'Z90#.!1%) 28,k_- EA1/4'#i6'$#i,I%}}w{qrcqDJs:0hdLkӃW6a85,v_WqqgaCC* 43'`&;(-%a5*l) 1 q g`BjڄUo4[M1wU[gUX;akDPC72&&#(+ {Y ppWjӃIu%[ 4 %#a#]&YaFV1g("% !%B # $f"G4&?P8*3IC$2 J(F&:  Sy3NTA6314vK(@BJ=&H(+:(1;#?+#>=$q%^zw˗rٍU}9o&b"X&P*O.N2vOmlۦ٠}tnxtcmgUTXRVH_SO>S9F|G3D@5&@*6$2]|b$r֊cGm$Q E(^uKj`aGFrF>dVB_Ohisz~kaDEf:8oaEa": 7- 'yKjYqqWw11C*zW]+tX:_!LA0")345)d!!0E7\ *f&nԅ`pCa V0}$Rdv`jNUU;'$$ C  ! hE |{|b@e ;7=+*%!Z=H5mU7+)##"!%%H$#/i*+1&4'*/",$2#1! Q)#$ ,#%$#m"-'3FZ$0&4GG-'$5)2VK< 1e]XR:1>%000 $( /)X&/8$$)')#mkPB~Ȑc|Ec.O H$J'V+V/T3xQx顦瓆xtqzg}~i^ildU_N_KVFRQQ)7G[4."0/$pf&pÃ_yAb@CD!q\!QdIn=Wtp~KY7E+[0% #&n^@oV:%*U7 砚g^?H**F>MG +3y 2TNu暈sӉ]{Gq5M(A M@@UB w!(&;)1! zW=)pczIl.PCM4..vq4).U.' &&8#   ^aF9ٗj|SoBj1[G+0(! O  !# #d40 '$2/,7=J3%+=)'(!**)#*+.'x3 U"t`xя[q>Z)R!E$G'T+U/P@mS檪|t|qxxド|xq~kurar^hZdVaU\UVKUDYRN@Q.G0=+7/7^a#1+  m nfh|Pj-VM4$wRB_rqm|p|;H> ##YegҀOz,`H11+Y/ ObF뤩pYYWk?`44[7B {g"TjP9ޞڙrcWrNjI_E_@j5U!|2 /bW0!bg rzx΋jޅUpCa9j'P 7:t &D M  3_Z{tޤؔjSqEYkQ#  D! o(&2;;%2]8>4 K<$-`#,aP0VL2$)(-+ v"0L.BXćnz 4v 7TSm~\}0#wX/*8,F-?'# ;":%*1M'(.0*'8&r jg[P~ږcӀDw,aF!B$K'L*L-tIymulpcjlu{lrt}szy{zz}}}zwu|stu{o|luisf{bnaia~ZdWU`PY]cQddaWaVbZcW{RVR_JkISGc8E.Q&5D6A/ t_gAmՅYq8\C~e!Z6 # Q,&Z,.ē׼IfCSCIo2SRm҄Yz8P4-(.$I@o{ipNSf2@]46SeFV|qစꁁwtjYYU`;8|,.n)_QT SR TR3&%hi?(f:4g5*m}!:;!5.a" ! F! 93-HE G{{4t+$'163I@&40?[+*!#$#.1 -<EK F@<9+pg3?5)74Q$']!&&'02)%(+S(!%%~ c r|c؟x[>n'UR Z#L&D)uE,qGǠ{}mrdmlghfiojzxnxruw|xrzpsnypygn{vrvtpbk_ibhZdWbV`T`Q[d^S]t[e^h_}jbcogt\deeQ^EN?KKD=6&4#* $ d pۅd܀Ij%M -m%#a5 F0%^1?Q^,,y팪qt|V\$9Tl%? )'!kNR{t^^z=Lj@Ft ?<<4UdI]iuܐttykT`GF9H|@8D(!+z9 ( 9)* r #H) 8< 4 6$( y{{{n'$1$V$W(3$.!I)$3#Q 2! z'<AJD><7#g59-:&S - L)',E% .Y'\"') +5/&#z ]&-{zלsَTs7]$QT QF[?S'qC8kJآlutl_ipakgqkfpfruxoirkvk~}frtrzrtpamck`w^hZe{fZy]_U^QlzmR[UWajletgp|qmvoxkt`ZeX[HT=R)7P8&&%)i ikFlȁWr5^F,2+m9 5DBz23\T d'/vz0@ZGm\{Ȇ5]B2-(+l W ϏxgqO9j@2B$$6X= ??05$ !<!#*IA. 86*8*NV % &5$ 1 s y{{z-#<&A&4%W -54"(;+Y)$::N)PJ>6<;6)!),8/F,`+$ 4><)%'(+&((#,;&$nq`(*ǔm~Mb0J DHH^EV$b9&V9RwW辶|nk^iYd{f\foi`jak_g_o|^f{j^o_kass{aj]g[eYdocR[R[TlR]QWU_dcT]Wu_erj~ltnvwwu{f]fOXBN^IV?#1(% 1/rf4qՈc}Fd#F =3%M5 jP>LTF]u)WTY,5~GMa:CJqctFb#C 4)*0%l_婱䏙n{@ 9 > (V -` F'Q)1]#G,+-KhLa`l҉{ꇙ|zrnnhhbg[gtS~XY@77-<)%25) K   $ 701)(.GF0103 5; t){{ fu ,7%7P6).4$/$0#,C##PR A j5J=,&'# Ur,;;:f&3$ "&45$m-2("'&8&-/OR,Op_#g<7ڦfڃFc-C}58? A"o<$]:OrTblXck_gwS^dnԀh[jdfV_]q[eW`Yc|dYaV`T_]]XqQ[OfOy^XR\Q[V`Tszd_iakguvuw~|s}foctWmFa6B(6""<{$VkYlUq3L6'#c6 BnmNN/4}$!j YL Q%,ccyRUk\ulŀUd3E:&'4*jM%zz쭸聏`rFIL] :}8I4D -`,1%-Y#G#)Q./QT7Mqly~~xxq}jfgi^RyIt@@8[0y<8*'(*F K,B 6 3 X"[ M%:%"#8 {{3{dV4)0&5(6'>&4I1'+$$!K452 5!g`uuQ)-9*8&X-#(%$#$"$%5&(&k.,L6(/d&olU|_Ak(PHNPC q:"V6zqfr]gQ]QeYoLUO`NWndq_T^T`R\uaS_Q\P[NZUZEMTWFlSWIUM]N`eQ[P[NWc^hemkv||dXeGNDP4A#0 (" 0vd 0qaCb!8-)rcBmKmc{H&'*&k D7<$+JY[qֈapC[!N/%(eKKkYtt^{~Ywj~'YCcq&l4vI_IZ 6z +\GQ):q)X*4c39a:?V, $G:S[qis||vppjj|regkUNdFSM@9993-3'R"""|<,G-d5!=E= 7>E(T.Mg7!6L/g~6O{{"/ +'4*8n;24#1$:6$@ P! 3>E @$&**L1,N*6$% /Z7&."%5(D!(('*9*$}W%xowY}:f#YPSIAw; W5~o~tXbOYKWJskcZYNYqVNY`ZNfMpLWKXjVIQGTDODPBcENZgEVHSFOeTIcS~_je`jmwm}`n[\Ae4J$13"Wm_kӃSv1M33-)'R1D z\\C;9P|M 0 \kkzSn1a7 ##go?썢rf|`x8b[wE0/{n}vn}τnzLY 7u8sU^ ;*+3bB @<%7SA__xszߍ蒆xssmmhddy^kmdT]vP]oMCC@@@>=z<;>?@Y4:dE00>-<+((S_&&C%%%%%0%)%V6.D.%%V:.%>Q18=%Sj1%]%%Q>( }{g*{5.668,8*8YF&4 -9 !"+%!;DI=#&!s60)7C5',62$#)&$-':==7)`,! 4#'+ V*K/#*Z/ t9Mʗr‡Sz5] DJE?Cq7T1eqS^^uDMBKF\FgGSEPFQUbURCMETBN?I@L=H>NGCL`LI;\;C8B[L@cDPGPS`|{enY`PaOG)8$*%'' paB'p`AcG.#*-q6 fXiiUp+/V267%pƃ`~AoC0,/3xK革bxUIax15M뭽䌛v\mOa?S 7v>L &S1e9Bw(4b$L/4ZEMQ/3S&,IWAbvWdsw|yu}jjrgducplkqa`^^]qu[Y\[mUfRiXRP\OokOOOOX_iOOYOOOYbOYROkq[OOS^c]nMFFd8&Q( )!{w{*N)7.:6:0P'6#*+UBM9(!&" de*DDA92(%o1X ,L *&5&$,>$-% )A)&*'55+0+]R\"Ig1.<-,3.*,)+R&$mX""ʗmՇMe/CBDFI=W.M-}ktVYEQ@KRLBL?J@LAMJ>I[V8BMFgK5@7H6E5ARB2=?>2>8:/^OR햋}rykt\_U\O_2?-2&%!&)Vnmj؄Qo.b8m)#XE[.oo]jBb;' T4^djρQu.P-!29"S'[?nn뢰BiN%[[wl[CUi~EP!n?_3YRhOb @ :S`bk.B /fLU*6j++U69,%6XF\biZikxj~|~||{{{zyxxvu{|stp|nnnnnyntqnnptnstnny}noo{v⎒mm^^OF62A-{\-:0T7h.;N0)2@0%":) #Pp+=P L8;1% kb+69&3#5+!$*O(--)%3(S&6*7+=64)X'8'.*--==-#Ghb<3јۘhӃGa*D>=<A:X,N,v~zhIT>JeB& ."WImfguYMA8%PVF6,p|^y>eA$.0$fDžesCl8Lkщkk((6NLk+yZ@67`OkSlcuD_2VE < > 6w B 0i G  / 2)UIPgNU`GN\/U3@3@-8/=)6'+$.&$D(B%04v*(@?6=+(i2D<;4!/&$;F"(*G"-%2Y;)65B3?=D@D3@*;C?(5$2 ") x#bdjO~cՀBU&B;29D8b,%`5Ә}|U\LS:F7F6C4@_E4@5B}X4A2=1G4:8D:B.>,:%M*9-6'6zynvtwf_OY>i1>!?*&# qfojiOq,N751"p?f[8oo[fT; <*rT foi~Os,`A)x PFnbl\NZ&t1)=f}ꓤtO%[X+yTBeV]sى哢<[<_o 5s 3n = +@;H>JkunzPX|+8m-]%QD)W"MA? #L&P +5;_9@^;A\&.Y F*1^4:^.5_@F]#I39^6;^(QB#4!G!G&.Z= ?*/Skl}Pf=. 1Ob IAPC4MjF2B0M.8) *!A'.=H<:04<3/-8:*>(:A0"2$&Z%*,3+94>_@NrDNZRDN?L<\GG0=*L D7!)!>q]ooO{ܒ^|=\#G:BH:`)K$$P.ӒКn{Q\8A5@2?2>-9C:9>Q@l@,Y/=+7_@(5+GZ=%9$Ax{ykulmWJY3>G2-"! rgN2p^w=Z>8({~]UߏlfK(.#$B,l aY7p˅^y=\K:'oN3 6Q9[w~)^oXt%[|LmIkΆLk+yZUm\R#sTpױEg/{Bb_uv4+M (x-F !V P  > #U>LW` > /g 4n %S7D)w A M7o$8qDM1lHN^&MXryh11#5KO8E8EDE4@1>'4#'"$#f%FPJ:,3-!!w!)7;7&U3")'('>!/ +I9:C@lDMPYa\\olHTDM9C1:*U#<!%L-y#PzmwY|9[D9?4r*n+U%!Q,Ӕh|NY2<.:.<'31;,;-D+8:E+7&1(?-6$8$525usndm^fSY7Ig{O*uex<^?#Z2z(qNWp>#ZoӟZo4["U4ZN  I2gDd@S9.;  Ay 9HPN8C7I4J2>59).'$#!&=b%+4 <@.ghR,)9'C w#1%5**ZO!.&2-8j^DN`hirvvkwٝq`kVlJUAKHh)6!?+$*}lZ~ۚp։Pn/O: 9 6 1 + m% ^#:]={chBZ0=#/'=-3%4K5&=A4!0-"D;1/,.[w}smyfpYcGO;F.W5()(! X sˀjփOg+<("tmiW~aHVb4_H>:H| ])s݃j݅Oo+M,*0(S=YaJtRG;d@eI6tQpi~=6DO(u%[lLbyCZ=pm@eXsG(\0^Lk@IOb@eȵf}QRoցOo8mLmҪCfFht=Xqyaxby [l#W"b M 3OI9C+3 , =J=`=J9E3=2>,900')7 )>:79>)!%a4..Y&4Q4!E%10,%#"0-C??Enp}u}nwat]dFM?PmC(="-B,&$eLЍmKp+M<= C 7 / + W 8Z6oxG{2=&4+]44+>#<>%.:2:-+'7,o)ts{vxgoYaTZFQ4>(4J(6%(*'$K fiAr}fuI;%\yiDpaAH:w@XHn df?rʅ^u=Y>/($xk[!tvnq̫颲鞬'bUQ!\=_UR^xf@NFLkk;_w0~RRoQpLmQpTpLkby_1sRpoROWRGyQp;d_wLmQp3_D}m3_k욫8W1FkQpԒOkH_Ta /7D;6I*"$'8B=HeM5=5CBO>;83*!$8!F$NJ<.)}'yf;%O&3*!$0%K' W&$##0-H|}yqnqYeC^CfyZuwzOmN5+yDN6\6W-|e|Fh(uCfa#s}.~\R#DOoQNX5TFh=Fh̆~#sW;[wCfy3}Cmx BBa6Z-* +%7?M?J>M>KaJ5G2: .'##k_< LTG1)m%E,97(6$?L*;('2'$ #?#!/R䔆닁t~ugfXW6A&AE/X2%T"_ a4+瞀fCq%RBC7*nfp#6o3iqO8F,I+1&,T/,H$2<"7+)"]c }$)u}t|i{bjOXEQ2?A.!,1 !#o `aAr^~=eT>&_wd!QuaEEm+  efH3r֋^=e8j"`pe!VVEGv,+RwZm誸pͣ*CTZDg8bRZuRoyqLpWqIy쉚-|0^!pm^x|tX8s^xf}+y!pi~^xLkO)_Akkf}(u+Caw6ao]s>[^j7s7E;aPH $3:GCUBN@LA991&#7%  Q@8R4U%LHC( ,g1(BD5 <&</*! !O%.%$#ᓎ{r{mkT[GVZF-;cA-#I*(%y X Y1/ޟ~ٔc|@g"YH4+,+*/4R\DPJAJ2447%#B7'6##'|(|4>|&s|y}l}qYdJTOoby1LkwR`ZdMT2<'':V6 5,<  C]mS?JAM>K9@Y>.z'3X YS\p[uMr:e'N 201F*=.)=-%<*$$$'%()4ߋwln\hFY09+8('4,, ~ O l^Hݟ}֒b@p$O7-19?:52M0>"-"+"=!>3'"'@)"'&]3"# %j z#[{ Jy|pyntfjSbDQ5I!,,*%'* n gfDv܎g؁Kp0U QK)Z o0'YE  2^gV9vg`Tj DK)%'<;AM' ~^B _5:џrz[yH7b*_!qWmNOoӉ;dUbyI6Cfe|聒n\ f(>22C7775ig.d++_*4|XwӄhGo[z(U(Uo㉙hq(9((7(7(L6k¡Lqz(-wCT%fJWT?;(\GD4" GSGVPX>G?N;F.904E%'qnjy|sdO`-GESk 2+R-!#B( $ $xpaia[Ul5B+`$2 #6,+ wP kcQ~גeHm1X"H6,9~[(!L-*&&+(Y*(&#%(  "?C&! %k s 7v"{$uzn~r\ewo7M/<*K+$ #h h _{Ӊ|ݓmVzCd1\J+'& M $!#_{͆|mVxCb1[B#22#H  t UTy]pݐSx˨C)w}@eȒU6@ [zt.k?aMMMXX]^^[vyToRTRgRɡXRR{}ܞayۧRRReRRmmӔ勜rFp**aE 1"7  -=(KkLXITHLJQ=[0:&4+!  q]nԅMj-]}e !%'0.&#9,"T;)$$##ዓ|yzks{qRfLO5C`; .6,) u TlVF̛ٗnՆUo%),C#$$(?ExktiOZ@L4A&0<+*("2,9 .)$ .-9-@)8)0&+%+##焋wjs^iOfAM4K77!/M>"2 # %# +%(I4AGSUQ-SP@<Ft}a_5B .;'$##$'#$9& *{ %{ -i 5~typXbGf;Q5B0<08(S&18!/.- .)(<1*(,1&* > 6%*0"?'-$/#. 4  &V%;(%BFA+<=  02 "!.E,cTl6>̮那k~߳!o7XR5Roքawdz\q#h]jLW0"A|䊊wwZZWi * 5-7 + *1 '+! 8?' M'=!*{vlublZkR]GRsVBQJF?L?W?LAK>K:FEI>J?I;H@C3B)4$nF## %!vor^hP[ieFY)7O5+I%$#)!4*5#@n"jsj_NKxkUjrmQ8@18&## 6$ % +e !{$Z~ulubmTscmDUA]VB[ED_B@7D4Q5T.77:D@EQH>^\H7@9E8E3>3O5CQ>4?FD2=/O%%%*Lj@;;7-F(''5'j)=U+Ha7 G( 842f TZ,tt}~@e-|8NRZu5`B} +a9B~%.X/gt__>@J"f 4=4.C;,27 X6=- d 配{qyltcq^iZdcNXOV[]PZq]KuVQRqNXQXGSSM8QI;53>&"|ry]hP[CM1;A9H>)'!&=% 0(#<qm*2'&2., _P~|~XbCE!1## $%<$0'$ ={ "{ #v !썫w~mir`kYW_Q[IkGPaSOWIfWLQ]ZxZKWJUMWPWLWKTjnENSWH\ITJ[GTtZIQD]EK:JT@<>/P]""$.' <$  !bLrO[菏|||bkm՟m3a\x=emlCfT&eAa3UVb9DD%+Skv~އdTq%%9M:1"# )+ QD  +ڏvleencmbk`j_jce[]h[gWaU`Q\NM>j7C*9J+'t|}egN`>K7?(O%&'&,2"!lm5"OB,($%!zYfSgpSW*4)+C'*%B"@ $gs { &|w~ireo`j^h\q`qnefbYoZpSt\dYdYbXcTu\f\h\fafZer`[e[eU_ZdYd_fddR_LLcL҆M`NRNNNrVY^IIQC<<55,,##R@S (-o Th(,ᄤl}n#'sWUI=el_wQ83|D 6 ;%0](.SjNckIIR*%%(# 2 <$Me-# ;䌐ꄊꁒwu|oylvktxthqwjpd{b[eQYnYRT;G09/2!0{syeofGu9E-B"1%(&#)lzFMyCe.L;+W_LDu~\f/#C-+, }\ RxڠW}<<|t2_Ux6hls&m8x%B3>w'N}~}\O11%40.4 :`7ZN)刟Ɯꩵ$O=AfFCfLk8CfȄex#P =LU.5gdQcvpZK/#, )% )?   4B+@<6<3()A)+) &J&K8~ a$r܇kOs+S<?2"l>f]Kku\G9*3%&3#:(/  # ${4r { )ꅇ|tsjj_jQQPCc<))%Z 6% ,OQ `iztꆖSrkkLmby吟j}1[@ 4q )X,J>aold;0/  2 ,11.hI5$3 2R,$"%##? f&ciFs^=bG8)0@&}3HE}{vYq6A%=-c'N3*>#0.!n&o{ #YC؊vz|ccVLHir-4D!3E0qfTc-6rԄ肒{qQpRoawe&s`u<\ #b : 0a.4UJ<[℈ꠈ[U|E%  *%!$3("$() - @>,9('1\!,$F#fas}jOm+O7*0K&>lNckCzk]X.yPQ%$$$!2& 8x!Z{ "܄yqqgg[MMbv1;%X& 3.vWYǔބMuw@iGJwoF PoӤiz=.DLTy/5Y)+MԆ욋wYYJ:?%"7F# l!$V9$A&/(%!("2&$$ c#!j gVGr^y=eK9)$!eEtt^i?JJ/3&%$!U* B |%o x "{ #酉||ttk_afXnZy52*. g ")F H qqqVn9iYGRKWq>bSTx "{ #{({%{ #o #`z 6gK{ 8i { "{$z *{ #{ #{ 2{ #w#q v y #{ "q ^n }%1*x z - &y  % # #x  >~ !% $v  # #&{ !{ #mo { #`N{ #{ "`{ #{ #l7{ $w!{E{%| #{ "} "| %t'mn{ 2o'#{#{ "{ #l{ #{ (s Qt n{ #f { %y C{ # ) Dw w'# %^ ${ oMx!} #o | "v {#pMjs { +j{E{ #m++{ 'e{){ #{ 2{ #{ 8|&T! "$;+$ $ )%;K%%1%9#$$$)(X/$"fSG0sbuCT G5-8.}\wpxam@K"0(Q(#!++, #~-{ #q q!zzsrnp_{f@C^0!!$ %*}>YāuwTwh}䍝Xt6_TpUGTmA I 00+FziKK]' ) $6p{m({S  ' # $"&8' +$ $#* $ B # ) > $~ &/ !~ !f| #x#{ 7v !dz E{ #v Dy )z "{ #{ #{ #{ :{ #gm{7{ #{ #p { #w!z !d8{ #{'(z "{ #{ #`fnz !{ " + $$) % ! / 0 #ih{ # 7t'" $v # #u5: 3 \ " ,$ #o N $ # #M5$j h %  $ % 1%% # # "% / "+ U&$ OZdw .v ;!' $ "8 $ $w! $ $ #%1, #A &; " $| ${ >n{%{ (m{ "{({#_0{ N%$ #&& ",( #t+! '$ #&&w.>5}YAg6wR]1@, #P$%$ $ ${Nk{ $zqimcd}\DKMD'%] %#" T FtKzkܩJlLkѣFh.~LA`]l 8w6@y/5X3)H⇠iiGR'' &$P) n { ra{!1{{$ G#     * # # B$ %$$$$ && j) $ #  $$ !~ . $ 9~ !**ry j1s1} !( 8 $!'} / $`~ "u #{/m g>v!{ %{ )j6{ #ksdv {(?{ #{ # . & $ #" $%" :%0#&t# $%'`o $ Yz \-' B $$ $ !l0' $7}$-' !(&5$N !"#&#,%C5$8( $$t! !"B $ " " .$ /0$# & !$&  !  ##$ E $' &x pey$u {${ "l$#{ #{*1h# $ / # 5)"'$ #'   >9 =2- $ < S $k m)"{ #{ #V_{'m( $ ! *$J%,3%$#7 -5%`=5G6k:E27'6.?)%A( ) KI# l cA0ud}Ed"O ;=7&`Gn|aviO$1,!!$#: #  $]{ A{ "{#fd`\RVEJ7I((-O (D(+e DzhmmPPq}+w,zki~LkT%qpH]NZ[cx*/X50OیuicG:'!75M%0}o`Zz?c/%# *($@*$3#%!7 $$S,%  #2$(0 #  $$'& Ls  1 $, $#!1 $*+#x"x";*$~ %y 3 "{ )q #{ #{ #{ #{ F{ #{%{#dt"=Y " I  %%%<+$+&# "h !!$%%(&8/y   $!$%!#$%,($>#$B'%$)E0$/!$!,UO'=+% $3!$*(&   #D%' '#$!,$%;%# >! %A<)~m." #t { #z ${ .{ ${ +u ,{ Y~ " $/ =   *$)&P L9@!$$#$$$ ^,"@,$$$! /b n%kPw / :Tu !? $1 H!-%   $$(&/IDhU]]g[aKScI&6*"$3!H, N nmpǃWr4fM@5.!DET1|nxPY4?%C+"IZ'4  $ #{ "{(D{%VvMMCE]Kg)94a -sjK jFZppQyv :[*xNWCP<,l? 93PC[돘拂iiTG*S"a@ Qy{i{{g"&%  X $& B(& % $$<0774D$%  >#@1 ! %%$ %" %#$2'%& 4(4 !$# ! O} &~ #{%b lA{ %X9{ #{ ${ @s -) 1$ #% : @0&) &$ .$#@$"?)B$&% % .&%'8 1A1$&6!$<6  %3% 650/%&$#06'$ +$@C&> &# /!5&'$("$' 9 +%-9  $w l:j jp { ?z !{Eh& .w !| ! &=42$ A$!%#" !$$ .,'${ ,$  $,$(" j$$2E$ JKO%**44''0.V(*46c!P lFVΊrzqՉuu6i~뜭Lke|S,tK 0:?VJ;Z⎉lh^G&*.3 ( F{{ /{+{ %8& F$%#$."C$$$+$$$$)"$$3#$" $Z7 ;(%5'#89>8!&"9$!0$$$&0 NG# ( 6 ! e& T#$$<##$  &)##*$K!i&(%" #'$"bj%oUAvnFy[䋒mqvtNJ/4/ F)$/&~Lm[pXu5]<&'vYPEW4muNW4AM%% 4 ! #&A$q U{ #F>7V/1%@$?!Q#9/@ k uO c#+賾rS|FtƁcy7bWKTpכTn; IQzIQUYH\ngtFD>N@='u'jiNr 'i!#6 5" $?J'%0## ,%" #$#0'F# >b 7B($(!"&$##!, ! $#*&#=&A""%5$-%!K #^ $t  $%>" = % 8 $/' $- >(+& (/ 1&*0$#+$"6!<"2 3255##"6#*$#8 '%'!C.= +$ C"##i1$.$$$J4 9 $##(#$%#(%0 "%$ {!(1'%#$"$6I2T5 " % %m3{ #f3h{ " $k r #"! (#$$-*M$H- :?#@)%# L,#(#$!*,%6$#  %&E2"$4$=$"'Qp.oW[D];ϒ甙xfhIh/:&.4@R()!& v`(vf߃Gm$W C,fiaLzj_mz(D6(#*T"%$] (p 2e{*(33//)@2`58 ,(rGa(.sb5N LTr)v@eRo5_L $2=t7#*.'%#" #9%FB$.F #!$3%%<$$"7#C)9$ $"$ $  !$ / <$4:&!#$*+* *##'$$.12- 1$S(8!!"$$890^- '8V'+(-1%3'= ,$qB-*$5"#$$E%'2%2%(S&(#O  0A'&6 6&'#9&%##B%: ?& ( % # ( !i/O7(%B$$+#I(3$Q)%$8!#Y+'!'%,%$'"$9&?$#$!%!$%3&$ #$S!$=&/%%8a+#(@)d: =z o#WGrc祥䏈ltR[:E,>0\#9## 'g leo΄Xm5VE5'(65s?OfFࡒs~Vt3>%)F"K' $!$ O{ #d{'(W&&""=Z, 4 "# ;$$%!yu N_#+觶5MXr86aRQpUn- 4u1b%LH<[~jceu 0 >zv{{m{764%#"& @% "$##%2(('3240?FA$/$3.\;!)"CM#):E9'"/1$<###/+02p.5;$=,#1$--%,&# $%$$)!.&  TP>(4$%$*$#"$S!#&%$49!0 ='(-& $40%&)I/%a@6!2*.%;!A&1=BMCOEOE}LUJSALIL7D4;#0D.C #%O,-0,!(4$# $$+$6&'!F,-MIHcJ:492-4.CJ+1' "$-$62# #$%E7< 7$$$!(/;(* $ #*%6&$@&&(.&<&00B-\'383-%#6 $ ## N$* $%#$&"#$C-4% :;%Bi D&~a6 ?x}aiNW5G$4?,S,% t X%ufyH\$E ;40??K1e8ta{ajNSA6 $;&;###$ !s  /w { #pH]/*5$&A.  K !%<+2L |K]#5S`}}yp~UE$0#3'A/&-%$$ -#!7'53IBNLXU^b^RX>fM9)7)0;2!+%2#/)7"5Z7+C%6&4/;)&3)3J4%c$.G2 .I,+#5$$2&=&#$:'?'!3P$3&%'$7'>$*#$%&$N"'+&,92-I9EBKAMUM:F/?9E (&F/$'K$4$#$($/',J4-7dMRZseclczmummwnwfxciOWFg3O(6,;3%.83%$3%$##9L&4&4S?:jnZT^gXQ_CT=J4A;M%2 -"(! +4 $C$;0$;+>$$0$2 % :/*2")$'"J$-2v1<>HMXgSbO`ZObK2?,4R4U0(4+%$$&##"$%5#d& *$.%&$6% 3$ y b[Ro(T+"[NMUX?Ԗov[eJ^,9!$-!  g"mapՈZt7^E6/0)v!V#qU_3?($" #"3$& 58 !] e{ #,A+"' A* I.(RZ!)Sf5P iClFhI6aM jNBN9P,4,77w(4b>*(6&$ &<$):$%%#$0A-%#1!6"&2$$$%<, &7"#E2=>II|U`^hrotV^VU4A-:$!% "??+6/KKcfirnervzrуငox}|hsmslvmw}v|vlvnv~wlvkuxvktmxgpotxwiqtdpco^alaKYAM~K.@E5'B#&9'!IL$+"$/$##3-(%" 4`'$%M2AE]U}sktw|hrJV'0+21( *#2$ .XS0&31?@KMZZoNlbDAR/EU2?d:EY5ES3piUtiʚ}[eA`@?"6%($( <%08% ..;7BGTvbfph[rezj錑}zqz`iQY6`4:?(3#Q7&+ +F'7'$$&% 0##$! -% #$#:@+>--eYKVLd`eqj]emyqy`iTZ6g17%9&-$G4 #"+$*%$&$N9"'' Xonc}Co@%' pU/ EY7ߚs}V_7I%3%/gj]r\9hF-#"f!K s{rb3?-%1 $ ! =$Ow !p # 32#"$  ($43/)52 Zb")4^L(^@e+y9TpNUz -*8w[dSX00C@ u5l {{{LN6B6AKCF=$.)DDI)$&)A"B+MGJK^XK!^"W1 5 vmS脈졋遐܁낉zvwvu]ie^MN4>&/%?&#$7'F3*8[EV8o"kgUTtNIYC8 CI2qf}iq^_BC&3C309! $B7"!<,909GVfC ULPEynww{vxmJVTp&4$!<5N$#$1 #9"#3 *$R(@$$U$$'*%#*,97@GV|G!v*K3 :uj|u}|}}uiKW2q),.(*%J%$$!$#$ *S1*J'!pr bYAj̀Us2V ,), uG1 BwahoSY-43*s _(ujLq(] I)%/#X~i{flHR.C"<"((5 $%  )| !v DffA#- , Ua:1  2>4CU2' X?)"P^#+Ќ1Cb|B&!pCQ.[ B 3<*pnzoN;u+Y +&{{ t"{=H8T7Q8CeF0=&Y(/A$!#,#1>)!i!siB2 KE^8E\7=e3AX2BF2EN0EP1DO0DS2CP7EG.BQ29b5Ec;Ec7CY2ER1=X3CM4EQ3uZ|eUsgv~s|upUicP8a35!/?$38)L($,.&A$[1I2$#" $%4/^:1Q G_bO0 !?ՂgnKU(4<?#2&%$ &%<+_N-81_ lzuw|~&hl"fJ=<!T%}{kyp_)63%=#;% &+HD1l.kztYB!BG QYXt`u{ocFR1>$IG/(G(#+#"@$$-$$R%$$X0%0E&#'%)EQ1," t Y I!J"G A:CzcؐwahO^4L#5M,+!$$"& 1"$'2V3# #6"foaxDg H1</#uZP5 -E'ߒ턍pQ\%29C kiOr։\s:gR1%%"h-W+ozWeJD13$#$$%%$ %{ 3{ "{ Q65 $ $  Y(1 (  1 % ( % Z ]#+M}ĉM&(u_wl5.}RI\0B:1΂}邏kjLHs%+=(~{ {{f7Hp^}V`)+ _uɅi~Lm(K 7,{KvbzfgdT'G#(%$!$"$:#%% %{ %{ "z $ @ 1  7'= 0 QF9..qJPV:-9+((0#uD&:JwKb"3ʅŏk@JGTp-|6pUoцׁ8E7/v|~p}X 7  L jt{*{V?L9B#%$#'#/!# -%B4($$4%|}f3&mֆ^n?XH0,3&*vmD9 =S5T\15z joIpߊ[|:ZF<%#)U!;}mj?D0.*@#F%$ >'  %s"y -T JX  !5 $<(P:IISRlXysW`[kTSOOHT?=c4@ 6)+3H|ImGSҜ㳾)VnYZy5`3|euXe0e8A6Qz^^:P"7%J{({{#{{FM[PBMAL8h3@18'/(  PRBd;Su'#31/$#+,,yXx#,,!)356-^$4*n|+E Xf\XL9:EV6vh셀lpQ\VL+:#+. !#") M''$$&! $LT3e"X G4x|q\v}sNY/9fpMב{ajK|3E)6+*%#$-(!1! +!#" F& _jefxMc+M9-<*):'"k@<B}c] /q9q~gLl)O ?)&/xSp\}ipIU)6C0"$'&"( h% ${ "w { "J!A%0('N11JN__mttvvuuuu~tsqmh]rCc*- !$ / %0wUtF[ܟ卡BB6Trn| P )/5ZUA]免ttqV<8-I++it { #{FRER;JB]UB5E4<T'&;  igD\wOf4MFA)((()# 2 ) 4 ? * 1 E D D ;:9#+?)2:%[#2.*&qOBG!K"r`節zcmK^AM%3:A&"0& $4!)FE jzJT~Es+_6'( ?aWowkL</2# !($"89,@A-w- );%Q5W?Y?X4T%C586)+,"TGvuVjxIT&A2#)dPOmCf*U0$55,)!#&&ueZ<EzauckGT6A;1/O*) C$$?&-! /$# # %_POzCn*P3()4?4(-+$!WF:Ecu~^iGR+:F*@!$*8'3%#Q%&$15;$fhY>lԄYm:ZB4A)&:/"!v\D;kgO㖈`Z'" nfDlԄ[x;U6&+.[!U$z}\_2< .6$E! 1"" # 6v %l h!,+*}:),aU|;`@/0;???4)3:+!^EEDwinx^~jL+Dh<+3$*D)($,)/)+"& .[ mwcoHf%I 3>,&+ -&PK<0F)ۏFQgbKa)B962uWMk\irOT*F,2"/*+$$%))q-s!V{ #!R9,)!J$#C=S ( D9@g/5b0128W B$+Rƀ{뛇rrSg2aeL7mo T |b %qCdzq;(~D +\)6f=kkqJV"4  B\0{"v/^ KWDMJtHafQ=e2?+4*&z qhfKkwVk5_D#~$* (J[ZkE\Sa\w`}`r`w`~`{`}_y]v[~ZqWrT~OlIfAp8a.P"ONFA&(0)cpYK9jclCa1<"0='Cr3-R&,%5& #Ofh\~Es$S +%SMr}irQaG@+$ #[{- G 1$%y*@<J5^aLrcݚuݍ]{@o%M2.1."]F?fiI똖k(8&, hiqaIk(P B,.73 '92-1!K"@%&qXP0Y.ztrP[4M57& ; #7('5$"5/ r iuaIv(] ?'1<574)(/5$wmK/ 1Q+~gpP`>G%P!-$"F6f'F'$$&&s )ifJj́Uv4]><1/'q-& T'9vx]R:wpF^|So7aE-2&}hN$~rzb;O'G( $6D" % t { $m**5'''%% L_&H,(>;uGb ) /i[den2A} D.?   :i{i{{PPl\LVKYHRaP5L-9*$ f#p{buEk"H **4*7,NKibtqxy̋yĉyҍzɋ{zܑy֏yڐy‰wvs֊oՆjd[}P{Cq5g'YF9;>($3'pJ:GEi<͓}Sf9F".<)#;&6@+:&H { \fAbOs0X4~#'h@dQlT\8DL7%&# ::O9 021I*N=>@R7}e[$ܕgH^*C=681&zT2 {eySb*$}$`e?h߄Vo6UG<4-,/4>3.0+H/@&#G!:sWINUnMג~\dLTGB%2W%# (# 2%E@C kh[>hV~6fK--608?5(&0:1$eFBU_B܏wakLe4B"0 JH&B*#7$: g $mσ_wCi S B6*4<*"$*"Y6 ( nXn)69SeT?n$N +#+!|^iRyj{GSW;)$%$&8'$$$'&{ 5` 4s 1{ \Y.t4,0)Y$XU ;]L &=sqD:C3P ] / !\  NUu8>Y>iwnLL*=S0 [BnfYmMM..jO6aÆaxZuDKe8OPX24)cT//22u {ZR{W_Uxg_OaRWIP8J*79E'9) (l kxVkwTo1]F,*)$+cn&&5&nA,kD0cD/`T3T3+W?6glLdlLghLnuVxvwxΒߝxnbUvDc3N"DG/) {su-r(WGyk\g@N&*4$$$%@nk$fXt (#/K$R98\~(G '_Jh\I˗ՖmMq-aMB411&XBpl:G jmu`sDd"Q G@- )+--.4*%4)Q&L*|ybDCua悌nvXp@L.\-*.0$$ 81$! ( hl܃`Dq"X 6#.990674;;4*$t!+ aHEw`zYzAM.D -A>,; #/#+*  n ]pPgxPr.^F2/?;')4)'-&y=8 eqFce)4`/=o%1 I157{ _%p{`zApV0)'*/"+*v @M v&u m o R debHFT N`H:oqR}wԞ؜і|ӐpՈ`sMe8d%It'r!10+*r"G=Ea:]h?P$0?A$ =##Jl gk_Gm&@ #-%#n߈rkt^`3`8  3/ .P(SC 4$.73-7" 6 ~ gTZ/(єoOz.bO6-/%kQOϗ^iY+w jXgRv0R<4'&.$"+7>-.=><6/.$cNL!Z(thsOw<@)@1)F(.E)#( ~ejYg߄R|0a@,5?9%+45>;46:(+- uTA3 !A s~hrPX8EVA$*)#$/3&, { h?/mވ\y=eT;+0:*+6*)/+0m7 Q1z4U}(-M-X(K:,(0)}[}jZ}irIS+9?$$$$"$#$v { "{ #m>W8866W2,DW" .vwWp{{lvPtoh~:l:+D'F (\ G!>گdBZ*! :2=BH \hAJ__g oHN;d@?p_oU_*Xiqy__;E"A%|.r {{+^g]gY|PgZZ?W6C/2-("0!| j mbi{Qt.bA5(%(zfW1;K+   ~   )| r "d\%T T hO?wgwЋeOo9W$@88:7,peWC=Ǐ펔y]hBK75<*04,))"( {`m?dRf2I84)!%wjMpxcx=N!.7/4*26_/Y"O >/42-3R&400 w M K‰pOx.b>$!nonX1T+҃yCOi@+m]t?S8$%0/-45:? :=G G9&+70" sR@E\9ߓt|ZgjYZ: .%:##KM!( _ W1#m]?cE05??.$%1@ A2/ 6'*#wjJ;FCg<ߔ~]gNl<9 /!)&!  j-.msfLq*^ I*04!o,-%(,/rXmjSN?|Z$FA3 )h(5*WE瞛h9I(&%$ *lQC)%iZcgRb4A#!J 5 )/ ln Q T @miK|Ǜ̖ydMo4[D 40"(qYHM{|gIB"/7(4;($!% lgҀZg>\I:."l1V'|dqLU;7<$;[4]&\8,60+z!HPAc3OD' (*TXŋoOx,Q+"!# CvWIr$/ulgf}Ml+R8792359<AJR#Y$[K74-!$-'iQIDq`虞wsKs.8i7,o\**%@$"" h l kjfMn+R3/:73/!07?#J$K96615,&#gWRPwbrnMY6C&L$#$6#Nu hZClY|9cQ.h+@,$0 ) $!(/~ $bT#g7 2 56 : /0hI}hQev`U.8T2%*&"# # ){ #m { "FFDDiBAnGC,,f/'fZ0:zp""5baxfkfx=?H~AGY`Fjg\KlC 4R To][yG:_syo}Nlyju$6m28_xbj{ffnV!0  > '2{bhzohqfpbl\gWaOZ]O4>$2?2511, %Q mqgyNk+VJ)nk`nSu~liLL %!$"1 8A!,#1, "  8  v k B T e94暴՘vߏ_~D[*C@,,9.&oPR~sWb7C)-', #;x o d[amIg(K 5,qkcTkjo{`^6L-!  @\9b*^='$'#|WZQ\^K)70! $| ` XËmKh)C:7.!$U4 vozHT i`@lZ:a>8?B3+924EO3W?i>k4\"K<-3;* tbM!Z)s{[|sP&0&;%#""I" c \`>lZ:kD%/8275+,*C3e?n>]4Q"R=45.3'! UI!N$|ovfmLL'3)n&&@m fodHj%U 9#/;+/ ><A64?&##~-l/+185 ,"fM}s|Yo^FJ1&$$$ %$%_ { .m1LLkKTJDM<<2]*-' ^pVmtΎ4f``z+yStK 8x^gu8=UdIZvvXX5O97K0!F'}j SgyEonK:Wax[wmdz{Re 4t4e.aC *#xfVXjWU@%Q'("*#d a X"}ϊhӀFl$W :0/+xF!G en7.gjzdIn'K 434&$)".6],4JOm[}YqIa4XI9-(,(~mVTva間~qGSGA"0 "6! Vo݁dIv'T *#67/5==4za+4_Oz[{YvIp4T=A84((uZ?2 s`~koI,3#1>$  ifyMjUz4aI/"%!3/L+_.P(KJ 1(8%f%# )* +)%tGcR|irJU(*C!1#$*-$$! ${ #r {%kjRQQWLD_k>,8 A uF{lϔss;QpPAJWaIPq ?Zht`U22!+[!  '"bf6H^oYv,yO[N8bWqq%k  N#M5$@ziiII%%   r'`{n !lwwtgjoYpLy{H/<"1,O:#eoygyLi(R <^vY<`V|bkFU=1#M8$$G?L,4%$$"(#"8  ~ n N T haI؝ȗwَ\=m"O *#+,&K5 P$cmAM&4&$H'&g m(h\}AfH6!%|]!X"v{rpIT2<# v $NVCj2eC(6%c|mE_sl]G@0B104 E'tN N}ԑb?b?46(wP?tTxEL# fnGkW{6W2(/"!#$03fi$D$iPgs֊oׇ^{Fj.S7 )*)'$ |gJ0Q.ێ꧍nwbY@F&3+4 r YwIkW6gF/6?7,)'0f.6%iQgso^Fe.R6 34&.,%u@@1V-ߐo~R_:F'3%#$} `: n`Cq W 4")/P64XE\E`8V#<++<'_&'((h4,i"uE:脁w^e;Z'0#057<$.rp { WmYZXTTMMBBD4L;5 UsM`"+譹훩e{Wqٍ#sD)o,MMW&dLX'#1 p h]EmxYm8YF,-#C =mMX1M!,F(%0$7%#4$)H+a#:& 2".,-/ l B K'|r}cCc%87?50|[\UY=s|SiQCE.!"###! {njTbxLg+VJ+kAmj{WaGQ#)  XSIn7`A .6%QUW2bmulHK$7%A!#!{ _^+&ѓx؎[s6[B)!"~Z;xyfp"q nb|F\"E 9;+'0143+J-J1w~pXw>`&K>>:/&# aBBs`voeLL.73("fpbFk"T C@621)%+4I,L4w|pXs>U&J@2%2-nTHJp_vqf>`)7 ndVgցPu.Z51=21=E~+i\>fxn߉XxD[)@@7"m%%))(57,0(sDeUhpNXY6+"%*9#&C, $f{ #[__`_[[Ufqt>O/e'N-2;_jAVz]_\~ϥGiMlτ)^-{\LcpisJQhhrpRQ3/ 6@tLksII|\{Ai>OKGe|莞~Na +^$R00XqsggIY4% 7 mc.PKoxowsubpboka9D0<"0C&!eq|eIn%I ((9.e7 g]u}yt>I&7 QN#)D%%1C ##&!+($0*?69!!!AT qpSޡڕfրE]%@A85$yoCrúbl?J&D"$%6iG4gVx7`<+'+wGodJw{blBX)1cJNq=b"H 0.//}!g%|]tFaE5- .S!`)FS \ eQ[#&q4;OOaCczWl*I+;v IK]rnWd44 8`k* oowxumvclccQlPI/::KF.+!$ n icEuW5S1*-1t0 EP/lO[EH'=.$?#;&$ "$! 8$$D('F"/# jH ^@3ЗgDS$H I?3+sxppr Q!{myOf>=+*# ei}^D\"6 2;,d^j#u~foOZ)7,} i^;SuDo*O:.*1!askd}Pc2@nA"%.4>-!.z%&XdY~g܂C`!I=8;+m_ڠx_j+# n $n_~AnVE;48:50$jfBE@auaw^Ck)Q?4*.*'RI \'됖e{WR,9$'} e$(o_yA`N9:4""&)vH}jCCA  E"uv^w^Cq)S4,-0+%#R= ^'ȎaiIR$B_ j mqfLn*R 3i%"#2Y1=!N9yڍ~ӑn˃Qy1[G 1+44)(<'!651 = LfkI▃kNP0R"*"'A#$;M--%6$ ${ #{ ?{$ׄjkfeh]\C`11!!_6 @" oJ 㩳P`11nTr:Cl}oABZf /e#*I~툈st\UM4J : !$$a]it挜g0Lm(ukavEJU(W1}tmaJC 82 5w{oyjn`jx{JViK'3!/!(&"! e.siEY"F 2&,g8 Isgy]m=H#1'j$7C#%FO9&>'2$-3&%*!$0#"[ tT _4)~ؓcv?_N ;33Zb`'EpZmusm7C!A%% *!#riUd݁Oa.ME>&mjQeXjucc2>" #b:(YyLx2[D:6-"z0e'lMk|or9E&.#"$ 0q U yƁvZ|5SA7("nQfaCnx:F mlhPr.^D704=;3%e'b$zjMJO#+bS1(ߡڗmԅRq6YK =+11&vk[R}~lv@V'D#! _lghPn.[F6-**w|hlpjYV*! [ i8=mRv6]I :.& &"dVTr듇btgR \&u hiClYz9aE*3%", ,?/*+ ~z͕s܌U{4f? (!**&80##43PPY}{v^k;G)1&0.%E!$ I  #ir,t7Moonkk\\OWC^--@< g^#[J69kdz吠椴L[ +T76Uр֓yXXP8' % , yU ާ>Xw\i7p#LSbggrS-- ($ 6{)o|zpo}xgu}iR]N[+6;3#+$!|/^ xfkӃTg1U4*(rYRXjAirxW-:4H$4'& ;$#)G,%5%%$!9"/P3!$%%'#F'K#[ G_<)ΓxZv5W=,7.!]UY'puaOF9#lәvX7V6 '+5)&14)){_If`A{jsJZ3;*#$D )&%:) < %Z{ 0[rrs|mmexnZPV9A8`L- 0w!Lh?9rg:-|aw=eKiΊw R ([3:`#Ap}yy~w>?"G),m\譸iLD_l(\Tnn|?N>FyXCcpkn}hA;"45Pgb|wzkclUd[S~G39 .K!{ g$ p`AfC8'|e^Vrv{Zj:H -$#9j['%#"8!1.%*%5%%7 $"!5)1hY b@2nLi(O 96'%!at`lHT>9!$# ;n o ei`pGg&I .$!$yShrEJ$2 3Z fd[zFb(L =5$tfBowpvfdmQ`,&68&"6 %]ixXu]:_<58.$gfOei<;wn{gNy+XA8&!2<+|gWkc}Rl aN X+xjԃNj1UHE;)"o_Izjꕕ`mAL)$U! cpՁgցNm+R?1$*40tisV~g_{R^+$ w S \~q͜ȐjρNa1I>7/j bGug[c1OI!rU`HjփUt4Q@@2#hgMm7IV,ZscxZv9bF 9A+j0%**s(!SE߅{vjAI"06;/:!$$Z$ ,q { #{ #{ #xummdVcFF5NNN,, nZmEU߃٧(YO[f}=eNS:_9|BPX'0\ (eu~eqJJ)@H*0 3crNl砮<NRD92A)H,>w@Fl[z^[AA!&4VPr{nvlfv]hlf;F1O/&-%5 j ioiQp.]@!rUftD|vqDMMH('%$% "$D'!,""$/5'$$:&#!%0-$ "*p X[nKxÈ^x:b>.)(&eWL[kwX*>F$# )xheEfzRw3]B)"' U[PpZ5B",~m\@aQw3dH*zt!WsdJt|asDZ&D$0'$FB&'>&" v !QvjLi(E 7;4$aHpyKY/GsifEo\};^J=0221#z!pJz{^b!1 .bM rxW顈sӉXuYy@k F1/2(|_T|hngoB3"lVcMo\w;ZJ4/*)7!n}^8 {{~V`($ bT r`P×֚sXm9cU <r$!aZPviV`-  f%#ñ`wCR DE=.qkcif?F U tey\:hN C,)2'*'.rYfeC{iwHQ;<%#,#K0&K~ w%l{ "d'~vw{sldUT[[22"9) (8vM}wh@v&4|.`Pp1_D's\r{nyKT*U!Qg韓mQ]U4: ) % g\}b럯uO-|a9'f 1o7?wEZ\\GQQ( *)<+k&qxmujarS\H_KB1/"-&-& { a e>/o^x>n@l).hNLza7E".+%&$$ $#&'% Gg(L+%*"$($6%$2 1#%2's[ tgҀIr%D 062,axQ=cfKW1>&0+))P)# fjӃ\@nX6!$a0Q(rzhpBb% `g[|@b? ${HMzmxRs2?1H:!$$8   i _tKqЇ[n9ZK82#lWgY`h.oqπg{L`)P >/54%,B#E.Y0J(튍yg+ o T W"ݞ{ᓑ{#TA8=1 rfLyfyS^Z;| T"mg߃Ll)M 45**0|"#cK(A$w[`!/Z@*gT Z& ƌם{`{@n#X6{&!TiiG{ewGR q /kZgӀP^.GB?&q svJrb@ }J ygz^4)*(6z\K|v[gJJ=.&"* $#$z C{ &{ "{ &yw|kkg`QQAE/YE v  2o Sx^}>>J%]r_w!qjZqn~GZOY8?k $S:T䓎w^~GA*3&,m\-7psR}S&ye|NI 2s$7n6*TgHyU["vt|szmfp\oNY=HRL#=* '!# ` mngNz+M}$.1n> - dNfk[O*#/%,$$ &H%$:!+ +"9$#$=$$# :E*+5 $n hR?hӀQ{0Q794'hUe3WbITN\)$ $#! q'j`dLp+_8p][pmsq}wY^%3#e i`dMl,N110lQ\tbx~]qLO7<$'&!E/'"X y aqf}Ir%[ 90/!hfsBhr\E8y beEoƂ[x:c?'./(-F%I0$iDU_B{]N4) }\ T r}^fFo([C0..!fA5 y膗zAO1# rgrJo[:Z0/%*0&*'"|PU\=dg'39d\N vaҞߖfFl(K92(e$WG~vyr7:(< 'LE,mՅ\p=UAC) (oOor[eOm Q tb|_=dL @50444)(8 l^`[@}wuN_,9 &$P$5*/ 7' " "{ %{ "u #z}r}j]]NnX=G$<"s hXq$}KaTpJV! )p"T jiKݟ|Ō_)*2lna||kh=_K3&#'O'$# =6!|0y _{ &{ 2zzw~ٌqhh[[KYG;*D0F("6)hJzz]]aIpɄ8CSo&qGx%A 74Bqu┊fMM+\$qteoIv**3*zl]\Z@uwMW1>+%#'%8;3  "!rYZ{ +{~yqgYYLp99),$7&5^KK{{爘wqPpZ.~FhD3K= 9 .d.6h05SH>_v~xclu@&9|hTYZ{BW=e5z(\$DdpCLAm==YW&<)`0 pgdlgjfoFQ5BMT%5&2=% Q ihGmYz8_N2)0#hZ&}mwMV8@1&'$*$$* #####LL$*!#.=&  kn;9a+N1*%n#@~5EtYU0=-2-&$"0%$!%kivOf}Rl1[M2/0!fhZrzXd,e s cfEn[r:^J.#wD-G*u`c5FH+#(?%416! p d}QazIm(\ F9,o`e=uAM S"reIt%U ;.2;>?=1%#mS{hՐikLK  o M`7)ȓn؉Nv,ZG2.3+{RiV}q+8/c%rȂe}Il%C 6759.&, "&Y;ye~oPI( oW[/(ΎnNt,V9*16}[YtXi_!m oمdHo%T 2' R2 ogoqh+cT _hF͙y؏[v8aS 2!4)+2$bH6 ߕy^iZL&bS',(&.$ ! #o!pz"\ V}}|{wpffnh^QM7':P G {cY {}cBv)JK[k=-{9*p 1m 5q;Cx:@[nT;/."hPa:~ci@L"q qerۉg|Ld5YE(.(eTq^nxV{8=#,#0$$'": s jE:buOh1\>.0$hYsqP[ bi}LmX6]@)(507>2-,~Q@vg䐕gvBN(+!b \ MܘmɂKm)U9-63'qY݀~wgX! g[kEmԅXy6SA;9>:61(&"W?5 odfpCM)$ \XmKp)P()6(!qVӊ|ux:fv dmGjUr4W;!)+!CMW6yeoBV$ r T mjP˗w׍Y6e> m*!"4'qV= E\4ܛpyan-8)-$K%#$#* $v b?{ "wwp|qW^PG79J(#& (  . b Yl҇--k;Dl쉚pIk7TLC J6o8,*D6PgpvvOO*&RfܴT1o@b5 8{4CWG79*,  uu|q|{xcj]fRuBM3I(&=)#>- ) nekJm{Wk5^O,*+nQCS/{kW[7=n"#U%P+'+-%## '#%.$$R'#%#"8-&"&+@K: &(+%$3_${H:GF?9'-$(&.%.<  hjhcKw)U ;2,"uXrsoxZ]0A n h}Tp\tHg(L-08,cDߏbjD\(<59>!!###*{ aa{Tl:b:!+(e@_]Xe09Fs*Z"rދd~Gb$O ?.117=<82'pOF|l穙jsGT :! |_XˆєhπFl$R @83&qUUb?|`j5B] d"0rdۀGe$V @39>@832$lcV9tg肈qtIR . v _XҔhӀFk$> +',/%VN`>yPg" \!nՆ`zCa Dv,3*fNw|wWf)2"+\ T wguVx3N30566?,) ; Eyl||wFM$1("$N ! ,f ~#z "{ %{ "|wwusyVVoFOV'')$ &$l cO$矰K|1IBj[O[wY_u~r2N H1g.\.4`(.TD(ILbNeEx.S[W>9C+yFh·T` < 0iXF3=((Q_9  s}~wku]eU]IS9E'25O(,!"  Q$pފbwE^"P 6.%hvght]hOZ!3&'#6$W>&$A&4$$$)!1& "%2Q16 C =# 0-'Ia(5[3@G=%!$O$#": kh\?iۄW7X40.+R`X}^5@0{ atjOt+N6&,-xIU_@xZdMK!;"'&>2 %z dZUu?k C)1"QM|_EZhEJ-.U j~WmÀXu5^G4699?;62,'!dNyj닙}Nm'6:$$o\ W"}b>jJ/#!c[vpnvB! _ f^mXw5fG04?4#.53#g{[8 qk焊nxMX)8# ^P‹}ْb>a;t'-"SJmf'5$wjWgԀPm.Y/$/-"fvjHwdnAL 3/ n DuisRi0NA6<:53.#aJAa5~txM]U@$1$#1-#+) # !s u!{ )woodhVWzU5;&&&   YGwײnʌ9cÇz6a‚厞.p = 2q2C8B7" + .hQ`)oN\x8lWOax,y.qHY 4p3B6.$0!#~0ov}utgv`iX\]HKEA4$+('+%"5! l"c^kڅTj1[G1!"#hUjAxisNZ/;)%$ )$Q(5$% ,%8(H16 ?!%$.3%!Vq$+58*'-(\2-;/H%M#, /*   cl`E_"B:4,c[!a&yirL}* k)hyNta?_A$,+]Bzmt|NVF .!& E" )"  ilATzAl%J 55'wrfp>`dEZ z c$%sڋe~Gk$M 52,1=<63+$!!hFonR߈uU1?' } _^0)ޕvX{4T3%0+ qdjBw|Xd42} R"seGs$V =8>7!)73-yo P( ucQv~S]0<- W ^3'͐vXz4R3-1(aQ\lBvwAKq\C3mօ\w=mF+*$|\OۊozR\2?(%&  l G vhoOp+P>@0$"vmPmg~akfO(9&="#7P#.'z +_!l {;-흎}w|w|qdUVGU;5&19D6  {jQ Wnvvvq>s(@87_yf}@XtKN!eM ,g *a0G;M 9}nzƔ+wKQpTZukmU OM_*B2?V&64  rzjvhzXcTQ;V,G'0)(!H'{ d'pՈ`zApV4u$ \Zyws|Zq1M!0(&C:!c*&#P=&&"=$"#$67$ *>1"55 -K)r*65' /.Y.M9LH 3&#, 2BOY-9 hg\gڂRg1XB5+|}gm_z]r5B| SułoɃTi0Q960*tSX樂wyQX/8*>g#"$qT:%RpCk*R=/*)#=q5ZrCL 5XihZo}Xo5ZC4.64..781))%gPfoR砖鍓|^h6W&!r\ ^N;mLn(H 0(loh qt\=@ il\oߊXy5aK:)4*)=:8%&uJ<dbI\p3B""\eL;mՅLi(F 9H=u^n'moS]!i nsf|Li*G 93%mSB[RtbjYO)%%  $iU̍lIl(YK5&)(r!sAS0yzKSRF8$"$""%'' 3n{ Q{ 5}xtddwEg55&&1oT !aB.$G&'-$$ .)84k,9!/-)*#)4:% .0&:4DDI;',"$>5%4(&+$ a%k\{>k@-,'(y>h5ynRS(k `]Ky͋gӀGo$T G, jnedgEU8sLa-:/-"&:+)|OgBi,U?4,#thxIU+8!+xb$uˈf׀Gm$[ F;;/$*8:46/'!pO/[.ߓt~QY! U mzYx_;\>($ nfTcmDQ#x b"ufyGj$R 0 0',2/;53,$!jS1P+ߑhBN 0 p\ fzUxَ_;[DKB eagXcc(15}hfBlYm9RG<.wxX:ykbb-<'2 e JˑgҀDk#F ;=6,(}"LgdyoeAU.3J-&^*.0%$h 2%{${ "xoowi^UGh55:') uSb#4nSo:G&YSqRq>fǍUZl-|8bK0G9MPQN^xTN+yelM\uu:V+ O / =5 r{rzeloR[HT0:)N';;798 ef?/oՇ^>i;$)}vwN퉆nv]gHT\[>N:F:W>A5<+I0761%*!0 .U-(2+6/A9CCH9GLZ.1&w#*5/00+1U46,I=+)%.#0 $p5##;. ihqdLq*P 6'$-"[pp~[e6L7l[ tpvڍ^}:WB4 sY]c'@f]FevIU;<;!@  3 lIh?i,UF2) q|t_HS@? !d j]pY|7cE04-*454/,) &(oPSwq|MY x Wu݇j߅Li(R 9-*'|viAakrY-  d lZpYm7T93+#+014?*'-#c7 Etꕧ{NX2 ar~jLu([ @.pkkZ<\f6BxKoۅdzHj%Q 7$ fPokv|Yq=J!02# y#PK}ōc}?_N ?%!3'rqXBL0nvT]1>,:&R(&*&  y ( 2| "{8.}xyodlhFF66+3  ;z\PٍzaQZz"M@my}A0^6DIk=ICf5RFh=)^axO`8^ux O=3 / "1Z.r{qzlwppXaObIK*5$Q(&(  goqhOv+H4<'!&\zY}pyirs]v`w`gZgTo\\JT@VgQBN@LGOBXFRImITEPBRNQ1( #1*h 4<7*8!#%3%-*!3&"$($8$ (" % nfbOjX{7c>'0$P\e?|kjfU' o ^.%Ғp݊Si.LF){"#cFhMX4A!3')K"%'OQ9a)XE&%+&}N]GQ6C dT$vg߄Jm&P 440%0*.90('+0'~VGvt`_E& icCePr1YB1*# Es4]hNX+< x a$&vÇgtJ^&F 0%'(3@+#9319-S>=vkU\d*l`?eނPw1\=1$E[-thrL" ,gh]CyɊU4U;51$vjUf=s}爦KW)2&(#!=^Z-'y؏]9jK *!42}K`Ge~ajD`%2'4$$%0h.$$  Kw { +s)$~xpp|kYLN7=60]$ #N+p fOo;Hp7mk瀑xlsOCfʂ0^.~%[9TNAcy$qXDCc*@O%*/K/"4s|lyem`j\_EPGg':-"$" s g\Ao\$$E"%6% $ #Nݍ{y|ffX[^I88-*+l % , u$sFY tT?uo⏝]3auIkax冗Xs.~)wRo֮Fh8Yuvb{c|Rq&`-|! $ 5,ps|owlgoV_MX8?0@$A7Z 1({ e qugLq)C 5)f"'TEW5Eb8E`:rYlXhWsyu䄊|sxs|lvcd^Cx$&~*#0& *8--;!!$.@S7'$&! $*$ #' Eo dhR{1Rs&)0,gVr^FfoDP$! h Xyz{c@jI3)( cR?~a}nmU_8~ %+APG 0/9.'$EQ>S,5*(M b(t҈jLs(U ;(#'2: @DEB9+++$dLJ@tmSѕjp(6iz6-M%QC47&xSXKU2< j `3uDŽjׄLw(Y D1#/;6 /=G?76;3>0g'Z SvoVcm%/i`.-O%Q;15&!aR]DZ&3u_ csQgڂPw.Y./;*#lvdGwfeTm&-&"&u C mnOpOn,Z9':)$-!Grnx}inIN&4=D!&$** ! y0 ({ "ڄy|gjZZVQG3,T0 Z  ETh-8||jjW|l55w(-wCki珟^x[-|4~Y&aJqYxjထ!O()b7)  Rdqrwiq{jL_JPUC&5#+()3 q gS;nهZt9Q="d$1s`c[]MMI1 / GLEL3EZ4voQhW`RqjaOvbH0p-ZQ$."u%%. @>4 5#b])(RF$ $)e#7##/I##%" a. l^~@_9*$lMz~yVazE!{k ]4*x\9dSC-$ ^QLfjL斞ﯜ{mlWM$$7: <:658##FL9aT?05?#t iiqKr\:kO4)!):>E"L(W%OB 0),"!`< 3 Ir^^i)7O" :B 305"u~MYF`5BU  a`hGr݋\:cN68C977B"Y(V%GD E52$&s2.[:M~q\^i*8B 2= 9=7%evX?I+9 { f>.m\z=eC5?/ UTnzTi082%/J !+r H wsjGi%J 67&2'QES5r{ja?7-@$## @$( 'w{ +zs}hhpMo>>.."" "0F" hKO|`tԖ~{``QQjDDr:Zo)?+H8s BILA .Q\$B "DV(%( #$##N%!$E2(  [lrfނNo+N(&)^@fNz|czLk* 3 +`ibGuW4fP0+4ieOS%yj扁YklUtl'9/3</-*z#=OTA+J*J$0 w `tiLx(Y =.(1*up-%D7V=[7Z'O?4.7*w_QYYUe8Z;1*.0(#%vxFRHM8AN( j#`tևiLs(U <AD779w8%R7Y=Y7Z'[I81((r\WBUUb7Z;1%"'153%yBL;G@=Oj nufLi*O -43zB Rr]jdP&7(# E~4l Wy|c?eJ 7{(/KGdcmAL06%!$l#  &! $냃㖙jjwfSxI@22%S %kA P cwznnpeee^lbώmQQPPbwۦjӗ9@" <#/*pltjs݆k[t\V@L x X rbsTp1X8-<%_ziK<!=u[xmfnuT7(85+11.3(C|)+58M3F^=\1"/*p k gyTq\:bK6*,%#/s.:TPhUtJt4aR F83*w~!z##%)17& #u{>I?KR=(&# hhkJq\:aGA4).9=U.UpJp4_Q 8-1"imO`}|%**+*'!%9BRG.> khV,"~!n d'weQ[/:2%<$2! 2e Z1%xڏ[7dL*06tZd!d&kRY/<+"'A!N $ $XꁒmaaSgbD@6?U0C+%!M8&* j SToHW넄||wtrjeee_aX̌^ ( -nmviaidgMXAM1;PA!2R% eqăcFh#> 2 *3%%)99261.21(%zS!*v$&&<(g)<+++v'k_!94!($-) 0<$$##%,/#%$$ $"h$nxd߁Ic'K 7*# $kpZ_hDM*#e Rxm͕rψSm2KAC)m$#ooWG_q^v*0.441f-"F#[).''4%-"})X+uhLp(W @62)/4m)6'\cgj؄\}Dp*_I;/"**&,,.4<=/&% $%.*5%T,)!" <u b t̅hLq(Y I7+&)+]%<5|ug؁j\{Dd*KB<)`# .#'47) '$!!0=f@;24%foyd|Hf%H (g&,|!hpcqz[i;G9O C'<qN eM<ۘrRy.Y:=A&#mgSirQ`bG,$&(*%#v % !'烃xxoodXrIK;T-6&"  2%/x ^^J Y {Mc|Ϥ懤Ҧ鲼oU|n6O$e  !"=4B jss|cm]fn_GV4@+8F.$! m zekUt3C:.+*#/8GI H D D 740y%%c''}%"-4)***)11u0 !! !$D$(#3;(>%$30#2#8&?0.. sbcCkWw6`@.1&&z!a&{mzl^A?# 'lKt֋Vm6UL 7"*/*)&z!$o+.,*|؆)R52*2'4$E+>$F'L& m hwLp\:dK875*&2+2pdzю{ܑmUr<^%M@-$-5558:666,,/U,".-;!.&'1"& 1m hhHpֈ\t:cRB=2(%-3pcz{m݇Us%3!58H  J q gaDjUx4U8!>C,vgUl -y$+嬇rrq\\YPAAZ3'a!0-6 QN/1%)h_J W@HSC X[`jw( 0-"R4QcolrlZeJRGc5?+7.?*Q+ | g'!pauCQ E =/!)-. 3<"O,W1[1L.P(M#=<=;9+ /5'0))90'/(d=DPCz7D'=,&'!$$D$0I2$+%"'##)"$L$W1 foaEr"F 1>)"_tb[g@G'& _!Xݞy؏]s=g"K0/7:96,#-9:2*-5+Oi""!'%$'/"+ ~PshLu([ H=:$%fw2R'7wvMݗ|ؑhҁRs=e*TBAE=:?: 6:@ C/)A#5%.1&<'G3u(~btɄhyLl([ E<82~`7U#wO욈|hRw=e*QN6%6,-< ?L; (1&4 )a+!"-x f!#nȂ`Cj I MH7etmj_afU'1+( #$9$( o T |z|בb@n RCB=$Mo ;I7d,D @6"&!$1n %j|xzzvbbUVGG::.97"$WN! @B +"_ifs&w3p A) T  ( aYfW``RZ]U-:&0'%$ W lajvRg/YE)"3), >A1RFlRnYgYkUqQsLdF\Ah<\4D+N!N3 5/+9:*(-%yhQkrcnQ[9C,"$"#$h2$M#J$(##/$$#! " X bZjсSz1R9?( kBh5l{NY)4<):$hRN~sМדgJf0LM A452+&*-0D HG >, ##4R$;  +$#!.hhuPp\:jQ@;,%xIG ;m& |~zjXHs;h2Z,P)P(S)G+M.P)LS:<&*#;''!% ( m hhHpʄ\:hK<;6t!uM+ c%}zjXzHj;W2N,R)C(A)T+^.Y)H;7F(  $3/$#'n k}Sg|Pe/OE;;#}|v`:LU?I,9J# !+*4 hZ.2čzDr0b%P=* #2'B3#1( 7$'*$) =i(yqqgot\rPlC@K++&!. C 4( ;3V/M %2&  "11YbP^LqEO1F.;01%$$ "= /x f?/o^@h<)5>)#<E1VPwgՀsxvӋsoՆlxiӁe׀aoYtMj=L+MA *96*(--`FknyLz-82,/3#$9'/%2!$"(kE&($#!. &8} f#n^~@_LD,")z\ogh?["/#*:sZOvv]릆s\}En1Y"NJ>?F<?72A"MSF2{Y#!I$/9#!{ btiLp)S B=5+!xfdKCM #f dM6}qf]{VoRlOlNiLqFf:S*PB5a$" !"$")*&!s bs~iLx)S <81"|~fjGGRb dF7yई}qދf|]wVyRnOkNqLwFd:U*Z9A""-)#%# &z gB2p؈cvIa.ZUL)$'v[.NH6A&4V+3%6%=!2*~ \!eC9өjS}Ba1JL)-0#> ,8"$ 4'$' ۫|vummccX\MJB66hS##E38 B.+ -.  <!=D!P"R\NZQduRH&D'& )$$$# 9R$%# #2%3  V hogԃOm,Y9.HCzZU_:zhpLa-H#:#"%( X%T jVA۞p_OwCk;^7Z4_4d4d6V7Q8T5W,\HoR%1.#:>$3KB&$$9 l!btLr]y;aM=2/%z]pBan T YE6{sנ柂{uplgakY:*!#( I$J'" cfXCrދ];\A70*|fAebfWn LeC6t՟埂ߘ{ؐuplgaWtLbdx9 "8#!@## &"Vtπx׎j}UzCl9d'K ('kn$'-%V'9'I$T%# 8 &}R {i㢀jSoESknB) ":J/,$J<$!#$ ! .%&rrzjf`aocMf[@Fn؇\tloTywtptpkslkhiIlZ:&((!++*'RO;A t hiIܢǐq^{RjC[.DMd'>!!&-,r-%Q3><$$'AH$ "r HŽq߄dX/  3%$T'+"&$$ *&衡wpދhaaZ_QXIB<<7W27R@N,I*((''&&&:&0&&^:4X2&&&)b&&'20'(`))*-****5)%1 Cj\<{BL>K;K?P>3"..$%U')*& V qҀg}L^)K H)97#)}~L-?" y ,} v n dV TXpjVѢܤu֋Y~:^< ,*%!?7r@dYz_i>JF4$+#J3&W$9&)"%,)%#. 00 gofLh)E =-*"mjf^CzhrIU+5', <3Y OT \& vcݗ顄~yvsoߊj~b[mjp>t @""#   ,AA  ~ Z htNta?kF)#"^GqgxFU` jj aaadhp }  @*+##E'$P) ''* k hbGtیa?j@'/2y^Gi|EN5   t fd] ad M b }  !"!9##!!B##$4 b ǘmՅdWfP7i% 0#<%!"$%A$!#*-$4%"7! li*drr 0($G%N## #0%%&vwpj~cf]VQdPQIrDYAAM?F><^0#c% ' $"$ #&."<-/ [O99.#  J umRq.K2( mdM\`T쑖txTP/( 5 :  " |(Sj S2: #'*BO"-#"{_u~mRz.X/*:*!P!DoyYV'  *  o    '!K$##8##< * +} j@1fE=_L1d/$} o 6"#,$"%8$$9&TB +&O-   G%*#.%#,. $'4/%#zz|vyqktoac\XXWZSRPP^cdMStLLxTVKKLKKciLK|JcJLIG_IMVYD==MaD@,,%5Mn6C:E:E9[2r0HA)#-'9&"(!O8-)rf q˂euIg&M -/50iOlWnS]D6-K"&= x \T glOݞxYq5NG0$0$wbjagqV[2N626C$ '$42:*.%*#6& ($0%+$! $f*ap΂dIv%K |"*' &NfUxe]X$f'+ " !- (} t%n mm n rw  (E## )"*D3#$i+#$0 1ma^Av܎dCf K?4+( 1d8m`aj=m %/""$4*$)!T#48%$+#$"m [Q7J:/(("D4A4@/94BV@;O'5- >?,& "#  n ]iLm̈́Xt6W;;74uI1^+|irQ`08*#$![#  gT k\NpՈOf+P. *%YvbAfp`^7D% %" # '$!!8%>5$")A&$" o ehFlW5i5,'#+_Cx|mtWm6D.-&%X# $21/ %   #$C,"1%$(Y)##B!$99%!$-2,`ojpԊV|2Y<($%"oPt{\vM@%$  -(#T5;&#1QG$#"$4'$,&#: >r` rmpVt2T<7-(N=덃`NP,A&#"#"(2<#$$&: =+  6;g !"!A #  "++*6A4&$:1=8*"&)(/(!LE](/#&&%8"4$';,aD#1F",##!<#&B%%ↆz|w{soomjkmffzedg}ccckqccbbv``boekVXQStFFe@99d22?5S>s5H/02;)5+)&+4& 5#";"2gYq։c|Fl#L B:/tU^ofsZc=H%a,< %$;W,! / a N gkVzؐ`p*=($:%""% "#4 !T4#-$>!$%Q'"  c#.cD1wg{Fb#E 5-('eNwiQmwNY<=%'1$#9'F&#'##U"D>%"!$$""$$%#$<!+, E%!*"Fb7-+`)56J.&3(/6)7*8'4)7(9=7'0a9(>&4#/86'9/6)7:F(1-9.:B=:3"0#%#"$}yyvvsqtymmqlkkklwj{pjjjihhugwcc`mocwVPiPODC4e.97;2?:I,F(5L1'1%%&$%$$"%(r/mk[k~Uy3aI=(drvC]5yjK[-k$0#$! '5*,10oMpje{F_#> 2/+'5\%YGOPJ(& *2$;&2! :%)"$%/8%OP6$# l h[lUp8]B(#HH X#悇pxT_4W%%! $N%%$2!R=&5 %/F) &(*(';%,''0|6$q&$2$1#1*\_.29!)1!#)$~ bplrZx7`OA65*jO|yjfBL%3& H%)"$ #@*%/6%MM(%!%)C/% +$$ ,blir։Zw7V72*#o[}}ymb>H#03)& # &!g9%$%)'&$#!#%0,&*-!.&4 -Q: !/!0"/.$2&3'69A.;9?IC7[Ua9DFLOD:d>K=J>EBK=MKF=4A2h2>2>&6J0!FF(.#-)- +% [$"p̅aCk TD4&t'g!vx|Yz9C 1?'.$#4%" ++lfS9\~Ei'? )2$!"ROX?F5Q"0L($$$<)&&&%&2$%6C'#C1{ c'qՈavCs!Z*'/kejq`{`jiN%2'$3$#7-#!8965]'522,-"-$\&4)6X93<9C0:.{aD5A7D7E9P@B3@-8C2%1$2 .9&#'#L(Q#R'xiJp&Y AJB$vS]bCꕈ}za_4A&+C<D&%?h&*&!"&6P7& &-,#4$! T _.xiJi&F =.#nWcbDpxS^(S,$ ( ( $&8$%%%T(!);* +(L(5,9-V=<3@6@=BG:5H9=6CaL7D?D\F:C>HOK@P?uGSITKXSTP\`tX]g}i]KTXZOZOZ[TO[MWVqOXWfR[JkEPEQ>O?a15%3 ,Q!/Z6C7C?A2B43K2/$%$+/Y?]FQDyIUpRLVDMEUOYEM2<3@.981#1*(%G:  S m^s\:hLNG%hRPtn}dpLm'A/*_<%$7&,$B'+$%,'*&#)'@')'C'!%&# z em^s\w:dL:7.s\]vwxmnFQW2TG%V#$#=" %Z.*- *<3)9,8=A8E^KFLUepTQRI]HRT[GUGTBKIiGRBJWVQXUYW[R]btUnZfZm_l]r\d[yajs`pfi]m]gth`[enpmdeaOVJV@UmK2WE5"0#(8`\FCU3ACX-:<1)G&(%0=EXY^eoW`SIg1OpF\.R:v*$~ Qxyr{Xm9E B1$@$$&$.U$  &6U)Ly. n=9u~poS@K5B F:!#*+(''*)(;F.D*-(E';%?L"jP6*pՈ`}A];6-Q0A $ipgt]fpM#2$DE'%('*$30$7//*JYD3KBMQXN_b[cUoXcpbXcYdxeiiXcgcY`WyW^Yh[e[v_n`zckemk{cigzntmukk{j|itnvlmkqgpceb^facVHPDN@L6E8A(<7A .%@"#F'Q1)$(&,2/*#1!0 .K,):'B #$ 1 gkojQh.A6+V3E}SAW0yjtJU4C.*E,' &H"4-"A/<(6,WIE>JNSU~S^[[Y^^h`{crdjagxqfpntio^V_LViR3<6U(5!+(%#' _biHt_=bO<47-}\sli}JU,:&&-'$"+,(4" .%&5/5LD4"0-+%' &$'' h dyMt_=mT=51%gRj^iHS,9D)%" $#$68(?1%BMb4?9EDNjWk]\bXx_cbpdnclhpqoaiinjpclulttbnemeo}tirlwyyv|nutzzryy蕄}wnwo{hnelisdm\dj^_M]KQ8U=WJUAF9J4G0:64+)%-E!$+U4U`2RS3t[|l~ws|NY'h1JD88<($%p\覆{fneQ4X+9N46/-1A ,!"!TO/D:,2&#&_6^8A/<'$-"9&W/*.)$1!,*8$027&3T41M(&!(( b eH1p^v>NAD1eVW}x~hFn".*""$$'!%$' (3+1130=KGDPK^S_녌[daja{isov{oxyst|xsxhusyi_XT>R,=(6V2,6'  | _ wދmRv.\F450!iY'憄w]g,&!! b g\@p׈^y=mM=2 cPxws|S[FG'%9F0%$DV(G&$#6h,$2F83@?GMY|d^tdnksjr||xltakS^CNH@*8!/;+% _uypوWw3^7 73"fT߉{bt&1$#3 &+o8&5J:+6\IC`HI^M@H0V4?-;".//&#"  Tq{pWy3[5#22 JI߀~䢃`ifs&3'S$$>1%X!%&/$08BZRNVg_hisqt}xyy|u}{s|qw{v|?yGJ>KgK@b(0'7A4?)'-k[)9-'/%%P#3754)''smb_Ftgۑ}u}dpTHH x!)$1))?)&1"n*"/.264'$2%$(.*3*70A4C=YDYGWE]>J^d??.D(6&>'+"= X rxjփOz+_H?6.iOvsKwn^tP7:$ *8&#,%$5<*2yylz\fQ@L/9*5C/'-7"( 1gbC0yُhHn$L :3)#{dUa$݌t|itybEMaJ56!/s$,H% andsߌ[y8aK=5/&pLumgqEP'4&5-$#$+$)( *(42/22WYQwUeWZNVEUKGIG,L$'"#*Cv P ois[w8S;@3$cGGs~foGR)7%D&h* %-9$"$CNB@fP;FE]1>)+#/  kq9Fd?n,Q>B1 .)#843=)(=)'60)nandF/ EPWwu*1=)i!34'b3$%$+)3$#0$$*&2}D8E[d]kdbQ\yS;E7K05"0(&(< b swj݅Os+Q1(' JeQtxDPbMc-M-1 $$'$C3~xnx]cOZDQ=D3i *'<"" sU6zЍkՃMn)U<46."Z1S/vYd6B!1!$!V#2$2-LbecjYaYcGPHSGL4A&4'%$?$-\ R4zkMq)PC9"r o1W+Yd4?))<9-5&2F;D$%!$$1QRDP?P>`9YN@%`59 &lfCbu_Pn9Z(R@>D =(*#(=42<)(;84 'S; f!r*?=?+j#45#!*-$$!($&O-,5T@4@kuirdq\fVxMVBL3C&4 ,=.$[ gUK9rۊ^=fB1,,&N!N և|wvTjG@"C'$ "@(%$$:tcj]qNY=HB8B1+(/!e #TlGv`>cC541(nQkT{clN^%2\+4%"$$#$2._9gqupwi\dQbIUBK,9'1!$%  *XckHv`>gL8')#yKxcPzfpBR'D## &%0 )#^aGQNNKY;G3L'E+8  #eP:v||ؒtdUjLaAg7h,_"Q92 >:8A>(!4<<'%.!kn(1-"t.8*70 y! ,+$bH?$-.&%;)X)8K6T0#b.IoKrowr\ePYMS;O/:!/"+( P qӃjOr+Z;/9#FOtVnwR[6jG71&(%$W)#%%#- 2~s{oyoWjGS7E)O#.$Y2L=  ` WyoRr.M456*PMЏyP[5@,T2$$R%$#grqmvjshn\qbBN9EL?+$($"" y #^x֋oRp.ZF80~~kL抂kxP\1>78 )7r #+ $ ,SDLJ\QUEvMG6T?;,2!oۗzs~kӃcZP{Fc<\2e*L"FSO6 $+8A**)(2160!)6/ >96? +$z!11"!$8)"!%9&= 2"$1-G5@xpy`rsi]UCO/;&4H.'#"  o a`Ar^=kL04%`n*2u3s~O['8!/(.&4$ mQqxjPr-XD/*""6(8[XFR7DE+< &83G%$$!~{wqclgkGP8X+>(I*$.!r M purXw4\C164'~ c!Q[D1?G%5&'$"$!.(rxt|mvgoSmIUXGd;)//"&#) z [ sorÄXq4aF*/*|uo$enBM,7D- "%$ %9 "!bVcriXbPWMWDN?V$0$+B*! 9 r T QO H^VBi\~y뢈ܛؗŎy؏tnhb\~WrRtLiFUA`=m:Z7W4f4U3P3[4[5b7Z:S<^;[7O.J=G7!4V%5''*$1'#K)+}v}kr`jS_CW.=FP-&O  n gR?vg~Ke0_ K9&'0H:1C(432*!$ ! %!# !ztw}l]_MJ1<'8+&/3I ZQ6zjԃMl-YIE>,!!y"953@&2i# $#U*+3}w~umsakPZ@^,9&4,)U*5 bb>0zՏjvMj-P2?3(! v!3@+F%0S(($$#$ @.$ $[j^h_iXnT_KW17L0%0*!4 ". z ec[ K P^K5nqSua|xܜ͖|ؒy؏tpk؄}`z^y[[vYiYmXmXqVkUdQpJo@`kZ1 55#&6(/2-%I/%$2&  CCxts\fQQ&4**$ " L ghGڝԕqZFl9Jmk. ;%,'*%$$&(% %"}u~p{dU`DNX?%/ 0F!%G u Q%瘂qZ~Fo6e%Omj.'5!0-H$=%B&#"O $%vzer`dGQ6DU;:)5+ ,d P昂qZzFl6W%?hk/1#'$!%K$!"#<" mszpjsajXaSS9E,9%2!R)8&CW& M .#     j yp R _Y X N R R V:.a:0faDi~PepLhrWhgJgUEe`Fi_G_4+b!d'-4!#R$5)$X=%%A '#$"ovgo{Q\?JmA&3$*% Q |y~gۂY}D "$m+ $e"$$x爕zyiR^HF1<$5$)@; gmqQ墂lT~VTk"/% D#*&$!.%#$ $&#~kt`jj_?L3@P72,!+( My g mpQ壟וlTxVI%$# $ ,$/$ &$xO~z~yqzjuZdKV@KWC/:$1<0!(?$ &$N* #5   " sc S rml m"mgQ o t%z _ '"!C,MO+$5"&/-+ $"?>wxqzhs]dJZ' 0 &|u|llW`SP@P9L2<,9;G$2@.+Q-!(#-%+%"@!)1!/ :#   '    ' 6# d0%!&)/- ."0!/B//0#vodnU_GR8E*8,'",(/)# ~#  !!(%& :H$$$-b%(|yzzguU_H[9J,9#0W/%'  t r (~ ) @+""3,5'$##U  ",~uqrVHSBX,9">"+1+ tr~4#0 "7&.D$%) '(! 2た|s|el\eVhP[BGHi;F8E2C)469%13-!/,+6'""$!-235!!5/,/ , !!0.)F**-#F!,&4n31)?&D&xks^j\`EQMx)7!/*W'O13020%!7@*)/%,#c-4 N偅xpyemT]FR4=)W**/&"" .%(>K ( $"9*/+6' %'*-"KC' #- #撍ꂌwpir]diW{`VKtSUB[ANOK7C-H$1&-(B/$/%Q&M'H(*p .-$)&*+"#3&yxnS_GQ7ZI2#1%' 5- M&O(:)()*.=$([*L %Cxz|clU_DZ7D+9#7%/( '$!$#$ZU),;$?'+ ## ㍅wrlygpcu`j[ebcU`R^SYL_HTDP=H=N6DKM2?J9-84A4A8A6g@:8Q7D7K;A1>*D#0 2/!%}yr{cnkdLW78-5.:+Y&,P5511>1!6G4%2%-&2'u&187D5$2('862 훁qzdsVaЀQ7=/8.5;Q0!.*W.!/*+-'B-!/$($M'2&0O1C'=!) $؄zqzdvTsYR9E0;l@42b40WDUC&"))*,, .5i)WVP;K9PQBC8E7C2I9E8E:E=HG:)1@&$$ ꇍ}pwhpZ_R]FP?Z8E3F1A3;-:'0-[19A=/<078>1?0?1>$.&3,#Iy|ir\hf]GS=JP@BA06B0+%=$2&%'VQ'[핏zv~tpyvymursmqfpct`j]edh`eYcX]YffWaT\hvWbYaWa_bXbXc]cWhWceSvW_TaS^ZY`[KWZSeEC0D)7$2yldl_nvfUxOWX]UUTZJZFo[VJUH}HSGSD|AM5?/;I=&+$H*腌xox~}^eV_O_LpRPCOH`i]=H@v@>oAM@SUL8B;F2<-9"V* ExoxaiVcSdQXdWEQdQASBN?XHCM@LGaAMLQS-G톌wjqtbvdhbc_g[eXaWbccWnR[p`QiKhISbu7D,1$4% !∛v{v}ludm_e[hYaU`P[R_PmPZS\OZenPYSWKTOPTF1?'5.9"(هut}ևpdm_ZbYcS`S^P\R^Q[MWS[GNNn^WOLEV9FEG'9/>y重~yrzsqxr{pyxzo|ktox֢voxnw}tvmo}ytnzkszxf~uuirhkjnYjgU`OZڈTB_MHցu~qzxjueoctjp`r_h`jmjYaYgPYNYDU;P3A-8~{pwpcjooak^fW^Ze^gYcYs|dx^IRJT^L5F.9%3V%|מrlxwaaiblff`j^iahgZgbj`^Lb@S?L3A0Z$T!Ea풑꓉큈퉈{z⣁ێ{vux|u~uur|t|yxuts}oxqvhqom_gXaR]\艏{~zw{r~pznvkswirencm[VgNVEP>N:G߃}}}ovqzltdlzisr{gleoirfiWeMRKWCSEO*5ܟ荱덏xswqnyiz|jrirxncn`hZeZcR]KuRMq1`P嚜鄊y|{zz|x|v}yzxxv~vuzl}en^h^h쑘폕}z~}wysyl|sgqˈfV^itLS펒克}戆v|s|otxpymjagenXVGPGSOV~wqynuoxmwjxgptn[eWpLVDJ;s܊~x錓xq~wj}hpnꘘzw|xutqkkudm`nzfOX쀆|yxzwszxk~mhigR^e}~zzxs{wxhww}v~mtwxms?blobby-1.0rc3/data/gfx/font48.bmp0000644000175000017500000000337012042452377020073 0ustar danielknobedanielknobeBM6( ###klk()({~{LOLUVUĿ^a^*++Ֆ   blobby-1.0rc3/data/gfx/font49.bmp0000644000175000017500000000337012042452377020074 0ustar danielknobedanielknobeBM6( sssWWW333}}}HHHppp===rrq000(((尰WWWDDDޚLMMqqr|322۪\^]EFFԌ7:8ltq-0/͝V^ZGKKƵɿ/50 ǼEUI8=;do2D7 |ʊMnV""&%θڮbo1N7foۯt·B|K)#*'ͤ{ٍM[-_4`kXi070Օcq.8^  Qv[|:D% 4 ~ԇip'-" ^eW[2;1B6ޑrz7w<blobby-1.0rc3/data/gfx/blobZeichenWeiss.bmp0000644000175000017500000000146012042452377022206 0ustar danielknobedanielknobeBM0(  ..---------! ----------------#"! ------------'%#"! ----------'%$#"! ---------)&%##""! --------&#"!!! !  !-------&$"!  !"""""#------$"  !"###$------!  !"$%&------ "#&'------ !#&)------- !!#####(-------- "#$%&'()+,,-------- !"#$%'(*+,+*--------- !"#$%&'(())---------- !"#$$%&'((---------- !""##$&'((---------- "##$%&'))---------- !"#$&()----------- !#&------------ !#'------------- !-------------------------------------------blobby-1.0rc3/data/gfx/blood.bmp0000644000175000017500000000224212042452377020045 0ustar danielknobedanielknobeBM6( l    !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~¼ſÿǿblobby-1.0rc3/data/gfx/btn_fast.bmp0000644000175000017500000000336612042452377020556 0ustar danielknobedanielknobeBM6(  jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjblobby-1.0rc3/data/scripts/com_11.lua0000644000175000017500000001756612042452377020752 0ustar danielknobedanielknobe-- Combot 1.1 -- by Oreon, Axji & Enormator -- Flags und runners wait = 0 naechsterBallSchmettern = true -- evtl Variablennamen wechseln -- Weltkonstanten CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 323 -- Berhrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS -- Charakter CONST_ANGRIFFSGRUNDWERT_MIN = 30 CONST_ANGRIFFSGRUNDWERT_MAX = 55 MIN_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MIN MAX_ANGRIFFSSTAERKE = CONST_ANGRIFFSGRUNDWERT_MAX ANGRIFFSEINSCHRAENKUNG_HINTEN = 10 -- sonstige Einstellungen servexVersetzung=-6 --Wert ist so gewaehlt, dass der Ball nah ans Netz fliegt, der Gegner ihn aber grade nicht erreichen kann -- ***ANFANG*** function OnOpponentServe() movetoX(130) generatenaechsterBallSchmettern() end function OnServe(ballready) servex=ballx()+servexVersetzung naechsterBallSchmettern = true generatenaechsterBallSchmettern() if ballready and movetoX(servex) then jump() end end function OnGame() target = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,1) --X Ziel in Blobbyhoehe targets = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,2) --X Richtung (-1 oder 1) bei Einschlag targetNetz = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_NETZ_HOEHE,1) --X Ziel in Netzhoehe (Netzrollerberechnung) targetJump = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,1) --X Ziel in Schmetterhoehe targetJumps = estimImpact(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP,2) naechsterBallSchmetternFlagTesten() -- schaut ob der bot angreifen soll oder nicht if (target > CONST_MITTE) then --Wenn der Ball mich nix angeht movetoX(135) --Dann auf Standartposition warten generatenaechsterBallSchmettern() --Angriffsstaerke neu berechnen else if (targetNetz > CONST_NETZ_LINKS - 10) then --Bei Netzroller einfach schmettern naechsterBallSchmettern = true end if naechsterBallSchmettern then if (targetJumps < 2) then sprungattacke(angriffsstaerke) else weiterleiten() end return end movetoX(target) end end function sprungattacke(p_angriffsstaerke) if (opptouchable(balltimetoy(CONST_BLOBBY_MAXJUMP,2))) then movetoX (CONST_MITTE) jumpto (383) else p_angriffsstaerke=math.max(p_angriffsstaerke, MIN_ANGRIFFSSTAERKE + ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so hoch spielen (kommt nicht auf die andere Seite) p_angriffsstaerke=math.min(p_angriffsstaerke, MAX_ANGRIFFSSTAERKE - ANGRIFFSEINSCHRAENKUNG_HINTEN * (targetJump / CONST_NETZ_LINKS)) --Weiter hinten nicht ganz so tief spielen (kommt ans Netz) movetoX(targetJump-p_angriffsstaerke) -- Bei der Sprungatacke wird die Strke des gewnschten schlages angegeben jumpto (383) end end function naechsterBallSchmetternFlagTesten() if (touches() == 3) then -- falls der Bot einen Anschlag Findet der Direckt punktet so wird der Wer nicht neu berechnet da er dann nciht auf 3 Berhrungen kommt naechsterBallSchmettern = false return end if (ballx() > CONST_MITTE) then -- wenn der ball auf der Anderen Seite ist soll der bot nicht naechsterBallSchmettern sein naechsterBallSchmettern = false return end if (touches() == 1) and (math.abs(bspeedx()) < 2) then -- schon nach der 1ten Beruehrung angreifen wenn der Ball gut kommt naechsterBallSchmettern = true return end if (touches() == 2) then -- nach der 2. Berhrung angreifen naechsterBallSchmettern = true return end naechsterBallSchmettern = false end function generatenaechsterBallSchmettern() angriffsstaerke = math.random(MIN_ANGRIFFSSTAERKE,MAX_ANGRIFFSSTAERKE) end function estimImpact(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding ntigen Angaben bgrav = 0.28 time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) resultX = (vbx * time1) + bx estimbspeedx=bspeedx() if(resultX > CONST_RECHTER_RAND) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FELD_LAENGE - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = 2 * CONST_BALL_RADIUS - resultX estimbspeedx=-estimbspeedx end if (resultX > CONST_NETZ_LINKS) and (estimatey(CONST_MITTE) < CONST_NETZ_HOEHE) and (estimbspeedx > 0) then resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end if (Frage == 1) then return resultX end if (Frage == 2) then return estimbspeedx end end function movetoX (x) if (math.abs(posx()-x)>math.abs(posx()+4.5-x)) then right() done=false else if (math.abs(posx()-x)>math.abs(posx()-4.5-x)) then left() done=false else done=true end end return done end function jumpto (y) if (blobtimetoy (y,3) >= balltimetoy (y,2)) then jump() end end function balltimetoy (y, Anweisung) --Zeit, die der Ball bis zu einer Y Position benoetigt time1=-bspeedy()/-0.28+1/-0.28*math.sqrt(2*-0.28*(y-bally())+bspeedy()^2) time2=-bspeedy()/-0.28-1/-0.28*math.sqrt(2*-0.28*(y-bally())+bspeedy()^2) timemin=math.min(time1, time2) timemax=math.max(time1, time2) if (Anweisung==01) or (Anweisung==00) then return timemin end if (Anweisung==02) then return timemax end if (Anweisung==3) then if (timemin > 0) then return timemin else return timemax end end if (Anweisung==11) then if (timemin < 0) then return nil end end if (Anweisung==12) then if (timemax < 0) then return nil end end end function blobtimetoy (y, Anweisung) --funktioniert in Ermangelung einer Zugriffsfunktion blobbyspeedy() nur vor dem Absprung :[ grav=-0.44 time1=-14.5/grav+1/grav*math.sqrt(2*grav*(y-144.5)+14.5^2) time2=-14.5/grav-1/grav*math.sqrt(2*grav*(y-144.5)+14.5^2) timemin=math.min(time1,time2) timemax=math.max(time1,time2) if (Anweisung==1) then return timemin end if (Anweisung==2) then return timemax end if (Anweisung==3) then if (timemin > 0) then return timemin else return timemax end end end function weiterleiten() moveto(200) jumpto(estimatey(200)) end function netzroller() --Ist der Ball gefaehrdet, an der Netzkugel abzuprallen (0=nein, 1=ja auf der Seite des Bots, 2= auf der Seite des Gegners) if (361.5 < estimatex(323)) and (estimatex(323) < 438.5) then if (estimatex(323)<=400) then answer=1 else answer=2 end else answer=0 end return answer end function blobtimetox (x) --Zeit, die der Bot benoetigt, um eine X Position zu erreichen time=math.int(math.abs(posx()-x)/4.5) return time end function opptimetox (x) --Zeit, die der Gegner benoetigt, um eine X Position zu erreichen time=math.int(math.abs(oppx()-x)/4.5) return time end function balltimetox (x, Anweisung) --Zeit, die der Ball bis zu einer X Position braucht if (bspeedx() == 0) then return nil end strecke=x-ballx() time=(strecke)/bspeedx() return time end function ballxaftertime (t) x=ballx()+bspeedx()*t estimbspeedx=bspeedx() if (x<31.5) then x=2*31.5-x estimbspeedx=-estimbspeedx end if (x>800-31.5) then x=2*(800-31.5)-x estimbspeedx=-estimbspeedx end return x end function estimatey (x) y=ballyaftertime(balltimetox(x,3)) return y end function ballyaftertime (t) y=1/2*(-0.28)*t^2+bspeedy()*t+bally() return y end function touchable (t) x=ballxaftertime (t) return (x <= CONST_NETZ_LINKS + CONST_BALL_RADIUS) end function opptouchable (t) x=ballxaftertime (t) return (x >= CONST_NETZ_RECHTS - CONST_BALL_RADIUS) endblobby-1.0rc3/data/scripts/hyp014.lua0000644000175000017500000002725712042452377020716 0ustar danielknobedanielknobeg=0.28 pg=0.88 v0=14.5 v_p=4.5 pj=0.44 r1=31.5 h=31.5+19+25 estt=0 p=0.654 s=math.random() function reset() est=0 imp,imp1,imp2=0,0,0 nettime=0 touch=0 esto1=0 esto2=0 esto3=0 esto4=0 esto5=0 esto6=0 phase=0 valid=1 active=1 nettime=0 y1p=posy() end function yb(y,vy,t) return y+(vy-g/10)*t-1/2*g*t^2 end function move(x) if (posx()2.26) then right() elseif (posx()>x) and (math.abs(posx()-x)>2.26) then left() end end function tb(y,vy,height,typ) local sgn=0 if (typ==1) then sgn=-1 else sgn=1 end if vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g<0 then return -1 else return -1/10+vy/g+sgn*math.sqrt( vy^2/g^2-vy/(5*g)+1/100+2*(y-height)/g ) end end function time(t) return 1/5*math.ceil(5*t) end function pos2(x) local x1,x2 x1=4.5*math.ceil((1/4.5)*x) x2=4.5*math.floor((1/4.5)*x) if (x1<0) or (x2<0) then return 0 else if (math.abs(x1-x)146) then return (v1p/0.88-1/2+math.sqrt((v1p/0.88-1/2)^2-(144.5-y1p)/0.44)) else return 0 end end function time(t) return 1/5*math.ceil(5*t) end -- function collide(x,y,vx,vy,objx,objy,r2) local t1=time(math.max(tb(y,vy,objy-(r1+r2),1)-0.2,0)) local t2=time(tb(y,vy,objy+(r1+r2),1)+0.2) local t3=time(math.max(tb(y,vy,objy+(r1+r2),2)-0.2,0)) local t4=time(tb(y,vy,objy-(r1+r2),2)+0.2) local t=-1 if (t1=400-(31.5+7)) then return -1 end if ( (yb(y,vy,testimate)-objy)^2+(x+vx*testimate-objx)^2 < (r1+r2)^2) then t=testimate break end end end if (t<=0) and (t3=400-(31.5+7)) then return -1 end if ( (yb(y,vy,testimate)-objy)^2+(x+vx*testimate-objx)^2 < (r1+r2)^2) then t=testimate break end end end if (t<=0) or (t>150) then t=-1 end return t end function estimate(x,y,vx,vy,height,typ) local collision=1 local tw,tn,ts=0,0,0 local tr,xr,yr,vxr,vyr,n=0,0,0,0,0,0 while(collision==1) do ts=collide(x,y,vx,vy,400,316,7) if (vx>0) then tw=time((768.5-x)/vx) tn=time((361.5-x)/vx) else tw=time((31.5-x)/vx) tn=time((438.5-x)/vx) end local th=time(tb(y,vy,height,typ)) local t=10000 if ((ts>0) and (ts0) and (tn0) and (twt) then if (ts>0) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vy=vy-g*t xr=x yr=y vxr=vx vyr=vy n=1 collision=0 elseif ((t==tn) or (t==tw)) then tr=tr+t x=x+vx*t y=yb(y,vy,t) vx=-vx vy=vy-g*t collision=1 end else tr=tr+th vxr=vx vyr=vy-g*th xr=x+vx*th yr=yb(y,vy,th) collision=0 end end if (tr<0) then return -1,-1,-1,-1,-1,-1 else return xr,yr,vxr,vyr,tr,n end end function impact(x,y,vx,vy,xpos,ypos,targety,jum,move) local x1,y1,vx1,vy1=0,0,0,0 local x2,y2,vx2,vy2,t2,nn=0,0,0,0,0,0 local xpos1=xpos local ypos1=ypos for t=0,7,0.2 do if (jum==1) then ypos1=yp(tp(ypos)+t) end x1=x+vx*t if (x1<31.5) then x1=2*31.5-x1 end if (x1>368.5) then x1=2*368.5-x1 end y1=yb(y,vy,t) if ((xpos1-x1)^2+(ypos1+19-y1)^2<(31.5+25)^2) then local dx=x1-xpos1 local dy=y1-(ypos1+19) local l=math.sqrt(dx^2+dy^2) vx1=dx/l vy1=dy/l x1=x1+vx1*3 y1=y1+vy1*3 vy1=vy1*13.125 vx1=vx1*13.125 x2,y2,vx2,vy2,t2,nn=estimate(x1,y1,vx1,vy1,targety,2) break end end return x2,y2,x1,y1,vx1,vy1 end function playto(position,delta) if (math.abs(est-esto5)>0.2) then esto5=est imp,impt,impf=0,0,0 x1=0 t=27 typ1=60 typ2=120 if (position<400) then n1=2 n2=-3 elseif (position>600) then n1=-4 n2=-11 else n1=-1 n2=-8 end while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(math.ceil((tp2(posy(),vp))))1) and (t2f>1) then for x1f=pos(x2f)+n1*4.5,pos(x2f)+n2*4.5,-4.5 do impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,x1f,y1f,220,j,0) if (impf>position-delta) and (math.abs(posx()-x1f)/4.5+1=0) and (x1f<=360) and (x2f<=365) and (impfposition+delta) and ((x1f-pos(x2f))/4.5<0) then break end end end if (imp>position-delta) and (imp15) then t=t-6 else t=t-3 end end t2=t2+1 end if (x1>=0) and (imp>0) then t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else if (delta>60) then save() else playto(600,100) end end end function save() if (math.abs(est-esto3)>0.2) then delta=30 esto3=est imp,impt,impf=0,0,0 x1=0 t=33 typ1=60 typ2=120 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f+0.1,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) -- if (y1f<316-h) then break end -- (t1f+math.abs(math.ceil((tp2(posy(),vp))))1) and (t1f+tp2(posy(),vp)+1=0) then impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,x1f,y1f,220,j,0) imp=impf x1=x1f y1=y1f t1=t1f x2=x2f y2=y2f t2=t2f vx2=vx2f vy2=vy2f xa=xaf ya=yaf vxa=vxaf vya=vyaf t=-5 end if (t>15) then t=t-6 else t=t-3 end end t2=t2+1 end if (t2>-1) and (x1>=0) and (x1<=360) and (imp>0) then t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else move(200) end end function attack(low,high) if (math.abs(est-esto1)>0.2) then h2=900 esto1=est imp,impt,impf=0,0,0 x1=0 t=27 typ1=low--60 typ2=high--120 while (t>=0) do if (t==0) then j=0 else j=1 end t1f=t y1f=yp(t1f) t2g=math.floor(tb(y,vy,y1f+h,2)) y2f=yb(y,vy,t2g) vy2f=vy-g*t2g x2f,y2f,vx2f,vy2f,t2f,_=estimate(x,y,vx,vy,y2f-vy2f/30,2) t1f=math.floor(tp(y2f-h)) y1f=yp(t1f) if (t1f+math.abs(math.ceil((tp2(posy(),vp))))+21) and (t2f>1) then for x1f=pos(x2f)-20*4.5,pos(x2f)-4*4.5,4.5 do impf,_,xaf,yaf,vxaf,vyaf=impact(x2f,y2f,vx2f,vy2f,x1f,y1f,220,j,0) h1=yb(yaf,vyaf,(380-xaf)/vxaf) h2=yb(yaf,vyaf,(420-xaf)/vxaf) height=316+7+31.5 if (impf>432) and (math.abs(posx()-x1f)/4.5+10) and (x2f<361) and (x1f<330) and (h2>height+typ1) and (h1>height+typ1) and (h2432) and (h2>height+typ2) then break end end end if (imp>0) then break end if (t>15) then t=t-6 else t=t-3 end end t2=t2+1 end if (x1>0) and (imp>0) then t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end else playto(600,100) end end function wall() if (math.abs(est-esto4)>0.2) then delta=30 esto4=est imp,impt,impf=0,0,0 x1=0 t2=26 typ1=60 typ2=120 t1=t2 y1=yp(t1) t2g=math.floor(tb(y,vy,y1+h,2)) y2=yb(y,vy,t2g) vy2=vy-g*t2g x2,y2,vx2,vy2,t2,_=estimate(x,y,vx,vy,y2+vy2/30,2) t1=math.floor(tp(y2-h)) y1=yp(t1) x1=pos(x2)-10*4.5 t2=t2+1 end t2=t2-1 move(x1) if (t2<=t1+0.1) and (y1>146) then jump() end end function OnOpponentServe() reset() if (math.abs(pos2(posx())-posx())>1) then move(400) else move(200) end end function OnServe(ballready) reset() if (math.abs(pos2(posx())-posx())>1) then move(400) else -- -10,-5,+8 if (s<0.3) then b=8 elseif (s>=0.3) and (s<0.7) then b=-10 elseif (s>=0.6) and (s<1) then b=-5 end a=198+b*4.5 if (math.abs(posx()-a)<2) then jump() else move(a) end end end function OnGame() s=math.random() yp2=y1p y1p=posy() vp=y1p-yp2 if (math.abs(imp1-imp)>0.1) then imp2=imp1 imp1=imp end esttold=estt netold=netc toucho=touch touch=touches() if (touch438.5) or (est<361.5)) then nettime=10 phase=5 elseif (phase==5) and (nettime>0) then nettime=nettime-1 else if (est>438.5) then phase=2 elseif (est<438.5) and (est>361.5) then if (est<400) and (math.abs(bspeedx())<1) then phase=99 wall() else if (yb(y,vy,(31.5-x)/vx)>220) then phase=6 else phase=4 end end else phase=1 end end else phase=0 end if (math.sqrt((ballx()-400)^2+(bally()-316)^2)<(31.5+7)) and (math.sqrt((bspeedx())^2+(bspeedy())^2)<2) then phase=3 end --phase --0: Spielstop --1: eigenes Feld --2: gegnerisches Feld --3: Ball hängt fest --4: Netzblock --5: Netzblock weiterführen --6: Netzball vorher abfangen if (phase==2) then move(200) elseif (phase==1) then if (p<0.5) then if (touch==0) then playto(320,30) elseif(touch==1) then if (p<0.2) then attack(60,120) elseif (p>=0.2) and (p<0.25) then attack(30,60) elseif (p>=0.25) and (p<0.275) then playto(420,20) elseif (p>=0.275) and (p<0.3) then playto(480,40) elseif (p>=0.3) and (p<0.38) then playto(760,40) elseif (p>=0.38) and (p<0.5) then playto(380,20) end elseif (touch==2) then if (p<0.4) then playto(600,100) else wall() end end else if (touch==0) then if (p<0.8) then attack(60,120) elseif (p>=0.8) and (p<0.9) then attack(30,60) elseif (p>=0.9) and (p<1) then playto(760,40) end else playto(600,100) end end elseif (phase==4) then if (tnet<=tp(393)-4) or (nettime>0) then jump() end if (math.abs(posx()-360)/4.5+2<=tnet) then left() else right() end elseif (phase==5) then right() jump() elseif (phase==6) then -- move(200) save() elseif (phase==3) then if (posx()>300) then jump() end right() end if ((x1==0) or (imp==0)) and (phase==1) then move(est) end if ((x1==0) or (imp==0)) and (phase==6) then move(200) end -- debug(0) -- debug(est) -- debug(imp) end blobby-1.0rc3/data/scripts/axji-0-2.lua0000644000175000017500000000610412042452377021104 0ustar danielknobedanielknobefunction OnOpponentServe() moveto(120) end -- Flags und runners wait = 0 aggro = true -- evtl Variablennamen wechseln aggroservice = 50 -- versetzung zum Ball je gr�sser desto tiefer fliegt der Service 0-64( 64 ist ca CONST_BALLRADIUS + CONST_BLOBBY_BAUCH_RADIUS) -- Korrekturfaktor f�r die Flugberechnung korrekturfaktor = 7 -- Knostanten CONST_BALL_RADIUS = 31.5 CONST_MITTE = 400 CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 157 -- Linke Ber�hrungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS function OnServe(ballready) moveto(ballx()) wait = wait + 1 aggro = true if ballready then if wait > 90 then jump() wait = 0 end end end function OnGame() aggroflagtesten() -- schaut ob der bot aggro werden soll oder nicht target = estimImpact() if aggro then sprungattacke(aggroservice) return end -- Zielbestimmung --> optimierung weniger CPU power n�tig ;) if (target < CONST_BALL_RADIUS) then -- ball wird hinten apprallen vonhinten(target) return end -- nun k�mmern wir uns um die B�lle die ans netz prallen if ((target > CONST_NETZ_LINKS) and (ballx() < CONST_NETZ_LINKS)) then netzappraller(target) return end if (bspeedx() < 10 ) then moveto(target - 20) else moveto(target) end end function netzappraller(p_target) --moveto(netzlinks - (netzlinks - estimate())) moveto(CONST_NETZ_LINKS - (p_target - CONST_NETZ_LINKS) + math.abs(bspeedx()*bspeedx()*1.4)) end function vonhinten(p_target) p_target = CONST_BALL_RADIUS + math.abs(CONST_BALL_RADIUS-p_target) --das ist der fall wenn der Ball hinten abprallt moveto(p_target-math.abs(bspeedx()*bspeedx()*1.1)) end function sprungattacke(p_aggroservice) moveto(ballx()-p_aggroservice) if bally() < 580 then jump() end end function aggroflagtesten() if (ballx() > CONST_MITTE) then aggro = false return end if (touches() == 2) then aggro = true return end if ((ballx() < (400-CONST_BALL_RADIUS)) and (bally() < 200) and (math.abs(bspeedx()) < 40)) then -- der ball k�nnte nohc dr�ber fliegen ** noch optimieren aggro = true return end end function estimTime() bgrav = 0.248 -- standart Realfloor ist 144 -- warscheinilcher Realflfoor f�r Ball + blobby ist 144 (der Ball muss schon berechnet worden sein) realFloor = 144 by=bally() vby=bspeedy()-600 time1 =(-vby+math.sqrt((vby^2)-(4*-0.5*bgrav*(by-realFloor))))/(-2*0.5*bgrav) time2 =(-vby-math.sqrt((vby^2)-(4*-0.5*bgrav*(by-realFloor))))/(-2*0.5*bgrav) if time1>time2 then time3 = time1 else time3 = time2 end return time3 --ist die Funktion nicht auf folgende aussage minimierbar ? -- time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-realFloor))))/-bgrav -- return time1 end function estimImpact() --diese Funktion sollte die einschlagkoordinaten auf K�pfh�he Rechnen bx=ballx() vbx=bspeedx() time3 = estimTime() resultX = (vbx * time3) + bx return resultX endblobby-1.0rc3/data/scripts/Union.lua0000644000175000017500000002456012042452377020753 0ustar danielknobedanielknobe--UNION --27.3.2007 - version 2 --Enormator -- 11.1.12 - insert game-provided physic constants where possible - by ngc92 --Startwerte setzen --set starting values OOSinit=false OSinit=false OGinit=false decision=0 funcs=4 oldtouches=0 oldbspeedx=0 serve=false --Weltkonstanten definieren --define world constants middle = CONST_FIELD_WIDTH/2 wallright = CONST_FIELD_WIDTH - CONST_BALL_RADIUS blobbyheadheight = CONST_GROUND_HEIGHT + CONST_BLOBBY_HEIGHT + CONST_BALL_RADIUS blobbymaxjump = 393.625 blobbygroundpos = CONST_GROUND_HEIGHT + CONST_BLOBBY_HEIGHT / 2 netheight = CONST_NET_HEIGHT + CONST_NET_RADIUS -- height is just the rod. for total height, we have to add the net-sphere-radius too netcolissionleft = middle - CONST_NET_RADIUS - CONST_BALL_RADIUS netcolissionright = middle + CONST_NET_RADIUS + CONST_BALL_RADIUS --Hauptfunktionen --main functions function OnOpponentServe() --initialisiere Funktion --initialise function OSinit=false OGinit=false if (OOSinit==false) then --einmal pro Aufruf von OnOpponentServe --one time per OnOpponentServe decision=decide(1) --Entscheidung faellen --make a decision OOSinit=true serve=false end dofunc (1, decision, true) --Entscheidung ausfuehren --follow the taken decision end function OnServe(ballready) --initialisiere Funktion --initialise function OOSinit=false OGinit=false if (OSinit==false) then --einmal pro Aufruf von OnServe --one time per OnServe decision=decide(2) --Entscheidung faellen --make a decision OSinit=true serve=true --Aufschlag auch waehrend OnGame weitermachen --continue serve while OnGame end dofunc (2, decision, true) --Entscheidung ausfuehren --follow the taken decision end function OnGame() --initialisiere Funktion --initialise function OOSinit=false OSinit=false if (OGinit==false) and (serve==false) then --einmal pro Aufruf von OnGame und wenn der Aufschlag schon vorbei ist --one time per OnGame and when serve is already over decision=decide(3) --Entscheidung faellen --make a decision OGinit=true end if (serve==true) then --Falls noch beim Aufschlag --if still while serve dofunc (2, decision, true) --Der Funktion sagen, dass sie immernoch beim Aufschlag ist --tell the function, it's still OnServe else dofunc (3, decision, true) --Der Funktion sagen, dass sie im Spiel ist --tell the function that it's OnGame end if (ballx()>netcolissionleft+CONST_BALL_RADIUS) then --Falls Ball nicht mehr erreichbar --If Ball not gettable by Bot serve=false --Aufschlagende erzwingen --Make an end to the serve end if (touches()~=oldtouches) or (math.abs(oldbspeedx)~=math.abs(bspeedx())) then --Hat sich die Situation gendert --If the situation has changed OGinit=false --gleich Entscheidung neu fllen --redo decision oldtouches=touches() oldbspeedx=bspeedx() end end --Entscheidungsfunktionen --decision functions function decide (funcno) t1=1 chosen=1 chosenret=0 --Alle Funktionen abfragen und die mit dem grten Return whlen --test all functions and take the one with the highest return value while (t1 <= funcs) do temp=dofunc(funcno,t1,false) if (temp > chosenret) then chosen=t1 chosenret=temp end t1=t1+1 end debug (chosenret) --Sagt, fr wie gut sich die gewhlte Funktion hlt --tells how good the chosen function says to fit return chosen end function dofunc (funcno, actionno, action) --Weist jeder Aktionsnummer den echten Funktionsnamen zu --converts actionnumbers to the real names of the functions if (actionno==1) then return std45deg (funcno, action) end if (actionno==2) then return takelow (funcno, action) end if (actionno==3) then return wait (funcno, action) end if (actionno==4) then return twohitserve1 (funcno, action) end return false end --Ausfhrbare Funktionen --executable functions function std45deg (funcno, action) --spielt Ball aus der Luft bei maxjump im 45 Winkel an --plays ball in the air at height maxjump with 45 angle --funcno(s)=2,3 maxjump = blobbymaxjump distance = 32.25 targetx=estimatex (maxjump) - distance if (funcno==1) then return -1 end if (action==false) then if (funcno==2) and (action==false) then return math.random(10, 100) end if (funcno==3) and (action==false) then if (bspeedx() <= 3) and (math.max(balltimetox(targetx),balltimetoy(maxjump)) >= math.max(blobtimetoy(maxjump), blobtimetox(targetx))) then if (bspeedx()==0) then ret=85 else ret=math.min((10^(-math.abs(bspeedx()))+1),1)*85 end if (estimhitnet()==true) and (blobtimetox(netcolissionleft)<=balltimetoy(netheight)) then ret=190 end return ret else return 0 end end end if (action==true) then if (funcno==2) then if (posy()==144.5) then moveto (targetx) end if (math.abs(posx()-targetx)<4.5) and (ballx()==200) and (bally()==299.5) then jump() end end if (funcno==3) then moveto (targetx) if ((bally()<580) and (bspeedy()<0)) then jump() end end if (jumpcase) then jump() end end end function takelow (funcno, action) --Ballannahme ohne Sprung zum Stellen (bspeedx()=0) selten exakt --take ball without jump to create a ball with bspeedx()=0 (sellen exactly) --funcno(s)=3 if (funcno==1) or (funcno==2) then return -1 end if (action==false) then if (touches()<=1) then return 1 else return -1 end end if (action==true) then moveto (estimatex(220.5)) end end function wait (funcno, action) --Auf guter Position auf eine gegnerische Aktion warten --wait for an opponent's move at a good position --funcno(s)=1,3 if (funcno==2) then return -1 end if (funcno==1) and (action==false) then return 200 end if (funcno==3) and (action==false) then if (estimatex(393.625) > 424.5) then return 200 else return -1 end end if (action==true) then moveto (180) end end function twohitserve1 (funcno, action) --Aufschlag: Stellen (bspeedx()=0), rueber --serve: up (bspeedx()=0), play --funcno(s)=2 if (funcno==1) or (funcno==3) then return -1 end if (action==false) then return math.random(10,100) else if (touches()==0) then moveto (200) if (math.abs(posx()-200)<5) then jump() end end if (touches()==1) then moveto (estimatex(blobbymaxjump)-45) if (bally()<580) and (bspeedy()<0) then jump() end end if (touches()==3) then serve=false end end end --mathematische Hilfsfunktionen --mathematical helpers function estimatex(destY) --gibt mglichst genaue Angabe der X-Koordinate zurck, bei der sich der Ball befindet, wenn er sich bei der angegebenen Y Koordinate befindet --returns exact ballx when bally will be given destY if (bspeedy()==0) and (bspeedx()==0) then return ballx() end time1 =(-bspeedy()-math.sqrt((bspeedy()^2)-(2*CONST_BALL_GRAVITY * (bally()-destY)))) / CONST_BALL_GRAVITY resultX = (bspeedx() * time1) + ballx() estimbspeedx=bspeedx() if(resultX > wallright) then -- Korrigieren der Appraller an der Rechten Ebene resultX = 2 * CONST_FIELD_WIDTH - resultX estimbspeedx=-estimbspeedx end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = 2 * CONST_BALL_RADIUS - resultX estimbspeedx=-estimbspeedx end if (resultX > netcolissionleft) and (estimatey(netcolissionleft-CONST_BALL_RADIUS) <= netheight) and (estimbspeedx > 0) then resultX = 2 * netcolissionleft - resultX estimbspeedx=-estimbspeedx end return resultX end function balltimetox (x) --Zeit in Physikschritten, die der Ball bei der derzeitigen Geschwindigkeit zu der angegebenen X-Position braucht --time in physic steps, which the ball needs to reach the given x-position with momentany speed if (bspeedx() == 0) then return 10000 end strecke=x-ballx() time=(strecke)/bspeedx() return time end function blobtimetoy (y) --Zeit, die ein Blob braucht, um eine Y Position zu erreichen --time needed by a blob to reach a given y coordinate if (y>383) then y=383 end grav = CONST_BLOBBY_GRAVITY / 2 -- half, because we use jump buffer time1 = -CONST_BLOBBY_JUMP/grav + math.sqrt(2*grav*(y-blobbygroundpos) + CONST_BLOBBY_JUMP*CONST_BLOBBY_JUMP) / grav time2 = -CONST_BLOBBY_JUMP/grav - math.sqrt(2*grav*(y-blobbygroundpos) + CONST_BLOBBY_JUMP*CONST_BLOBBY_JUMP) / grav timemin=math.min(time1,time2) return timemin end function estimhitnet() --Wird der Ball das Netz treffen (bool) --Will the ball hit the net (bool) safety=5 if (361.5-safety < estimatex(323)) and (estimatex(323) < 438.5+safety) then answer=true else answer=false end return answer end function estimatey (x) --Y Position des Balls, wenn er sich an der angegebenen X Koordinate befindet --y position of the ball, when it is at the given x coordinate y=ballyaftertime(balltimetox(x,3)) return y end function ballyaftertime (t) --Y Position des Balls nach der angegebenen Zahl von Physikschritten --y position of the ball after the given time y=1/2*CONST_BALL_GRAVITY*t^2+bspeedy()*t+bally() return y end function blobtimetox (x) --Zeit, die der Bot benoetigt, um eine gewisse X-Koordinate zu erreichen --time needed for the bot to reach a given x-coordinate time=math.abs(posx()-x)/4.5 return time end function balltimetoy (y) --Zeit, die der Ball bis zu einer Y Position benoetigt --time needed by the ball to reach a given y position time1=-bspeedy()/CONST_BALL_GRAVITY+1/CONST_BALL_GRAVITY*math.sqrt(2*CONST_BALL_GRAVITY*(y-bally())+bspeedy()^2) time2=-bspeedy()/CONST_BALL_GRAVITY-1/CONST_BALL_GRAVITY*math.sqrt(2*CONST_BALL_GRAVITY*(y-bally())+bspeedy()^2) timemax=math.max(time1, time2) return timemax endblobby-1.0rc3/data/scripts/gintonicV9.lua0000644000175000017500000002123712042452377021712 0ustar danielknobedanielknobe--Gin Tonic v9 - Still no comments, sorry :P CT_ServeSelf = { 152, 163, 180, 195, 205, 240 } CT_ServeOpp = { 140, 200, 240 } CT_ServeIndex = 0 CT_Tolerance = 5 CT_Action = "" CT_ShotDecision = 0 CT_NextGround = 9999 CT_LastTouches = 9999 CT_LastHeight = 0 CT_SkipNextBlock = 0 CT_WaitCounter = 0 CT_WaitName = "" CT_WaitMoveTo = 0 function IsAt(position) return (math.abs(posx()-position) <= CT_Tolerance) end function Wait(name, time, moveto) if (CT_WaitName == name) then if (CT_WaitCounter == 0) then return false end end CT_WaitCounter = time CT_WaitName = name CT_WaitMoveTo = moveto return true end function WaitQueue() if (CT_WaitCounter > 0) then CT_WaitCounter = CT_WaitCounter - 1 if (CT_WaitMoveTo > 0) then if (not IsAt(CT_WaitMoveTo)) then moveto(CT_WaitMoveTo) end end return true else return false end end function ResetWait() CT_WaitCounter = 0 CT_WaitName = "" end function OnOpponentServe() if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,3) end if (not IsAt(CT_ServeOpp[CT_ServeIndex])) then moveto(CT_ServeOpp[CT_ServeIndex]) end end function OnServe(ballready) if (WaitQueue()) then return end if (CT_ServeIndex == 0) then CT_ServeIndex = math.random(1,6) end if (ballready) then if (Wait("ServeDelay",math.random(28,90),CT_ServeSelf[CT_ServeIndex]+math.random(-150, 150))) then return end if (IsAt(CT_ServeSelf[CT_ServeIndex])) then jump() else moveto(CT_ServeSelf[CT_ServeIndex]) end else if (posx() < 150) then jump() end moveto(40) end end function OnGame() ResetWait() CT_ServeIndex = 0 local timeJump = timeToHitHeight(380, 390, 20) local timeGround = timeToHitHeight(200, 222, 40) local timeBlock = timeToOppSmash(390) local estimhx = r_estimx(timeJump) local estimGround = r_estimx(timeGround) local estimBlock = r_estimx(timeBlock) local block = 0 local wallcoll = willHitWall(timeJump) if (timeBlock ~= -1) then timeBlock = timeBlock+(estimBlock-400)/13 end if (timeBlock == -1) then timeBlock = 9999 end if (timeJump == -1) then estimhx = 9999 end if (timeGround == -1) then estimGround = 210 end if (CT_SkipNextBlock == 0) then CT_SkipNextBlock = math.random(1,10) end if (posy() < CT_LastHeight and posy() > 150 and posy() < 330) then CT_Action = "" end CT_LastHeight = posy() if (CT_Action == "NetBlock") then if ((posy() < 150) or (timeBlock <= 8 and oppy() < 150) or (ballx() <= posx()) or (touches() <= 0 and bspeedx() > 9)) then CT_Action = "" else jump() moveto(400) return end elseif (CT_Action == "JumpPlayFwd") then if ((posy() < 150) or (touches() ~= CT_LastTouches)) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (posy() > 300) then if (math.abs(bally()-posy()) < 18) then moveto(ballx()+bspeedx()) elseif (estimhx < 200) then moveto(estimhx-50) elseif (oppx() > 600 and oppy() < 150) then if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(4,6) end moveto(estimhx-10*CT_ShotDecision) elseif (oppx() < 600 and oppy() > 180) then moveto(estimhx-40) else moveto(estimhx-60) end else moveto(estimhx-70) end return end elseif (CT_Action == "JumpPlayRev") then if ((posy() < 150) or (touches() ~= CT_LastTouches)) then CT_Action = "" else if (estimhx == 9999) then estimhx = ballx()+bspeedx() end jump() if (CT_ShotDecision == 0 and touches() == 2) then CT_ShotDecision = math.random(5,6) end if (CT_ShotDecision == 0) then CT_ShotDecision = math.random(3,6) end if (bspeedx() > 5 and touches() < 2 and CT_ShotDecision < 6) then CT_ShotDecision = math.random(6,8) end if (math.abs(bally()-posy()) < 18) then moveto(ballx()+bspeedx()) else moveto(estimhx+5*CT_ShotDecision) end return end end if (touches() ~= CT_LastTouches) then CT_LastTouches = touches() CT_NextGround = math.random(-20,20) CT_SkipNextBlock = 0 end if (CT_Action == "") then if ((ballx() < 400 or bspeedx() < -2 or bspeedx() > 10) and estimGround < 400) then if (touches() >= 2) then moveto(estimGround+(posx()-500)/22) elseif (math.abs(bspeedx()) > 8) then moveto(estimGround) else moveto(estimGround+CT_NextGround) end elseif (estimhx < 650 and math.abs(bspeedx()) < 6) then moveto(215) elseif (estimhx > 650) then moveto(250) else moveto(180) end end if (posy() > 150) then return end if (touches() > 2) then return end if (timeBlock >= 23 and timeBlock <= 25 and CT_SkipNextBlock ~= 1) then if (posx() > 210 and estimBlock > 395 and estimBlock < 650 and not wallcoll) then jump() moveto(400) CT_Action = "NetBlock" return end end if (timeJump >= 17 and timeJump <= 19) then if (bspeedx() <= 7 and estimhx >= 65 and estimhx <= 420 and posx()-estimhx <= 90 and (bspeedx() >= -7 or not wallcoll)) then if (estimGround > 400 or bally() > 250) then CT_Action = "JumpPlayFwd" CT_ShotDecision = 0 jump() end end if ((wallcoll or bspeedx() >= -7) and estimhx <= 250 and posx()-estimhx >= -90) then if (estimGround > 400 or bally() > 250) then if (CT_Action == "JumpPlayFwd" and (touches() >= 2 or math.random(100) > 15)) then return end CT_Action = "JumpPlayRev" CT_ShotDecision = 0 jump() end end end end function timeToHitHeight(minheight, maxheight, depth) local i = 0 for i=1, depth do if (estimy(i) >= minheight and estimy(i) <= maxheight) then return i end end return -1 end function timeToOppSmash(height) if (bally() < height) then return -1 end local i = 0 for i=1, 17 do if (estimy(i) < height) then return i end end return -1 end function r_estimx(time) local estim = estimx(time) if estim < 31.5 then estim = 63-estim end if estim > 768.5 then estim = 1537-estim end if (bally() < 330) then if (ballx() < 400 and estim > 400) then estim = 723-estim end if (ballx() > 400 and estim < 400) then estim = 877-estim end end return estim end function willHitWall(time) if (estimx(time) < 31.5) then return true end if (estimx(time) > 768.5) then return true end return false endblobby-1.0rc3/data/scripts/reduced.lua0000644000175000017500000001037112042452377021271 0ustar danielknobedanielknobe-- Reduced but very effective bot. -- Borrows from axji.lua, com_10.lua and com_11.lua as distributed with -- Blobby Volley. -- Copyright (C) 2010 Soeren D. Schulze -- The terms of the GPLv2 or later apply. -- See the Blobby Volley distribution for the license text. modeLock = 0 timeto = 0 target = 0 naivetarget = 0 estimbspeedx = 0 direct = true servrand = nil -- Knostanten CONST_BALL_RADIUS = 31.5 CONST_MITTE = 400 CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_NETZ_RADIUS = 7 CONST_NETZ_HOEHE = 157 CONST_FELD_LAENGE = 800 CONST_BALL_RADIUS = 31.5 CONST_GROUND_PLANE = 100 CONST_BALL_GRAVITY = 0.28 CONST_MITTE = CONST_FELD_LAENGE/2 CONST_RECHTER_RAND = CONST_FELD_LAENGE - CONST_BALL_RADIUS -- Berührungsebene des Balls falls er ans Netz kommt CONST_NETZ_LINKS = CONST_MITTE - CONST_NETZ_RADIUS - CONST_BALL_RADIUS CONST_NETZ_RECHTS = CONST_MITTE + CONST_NETZ_RADIUS + CONST_BALL_RADIUS CONST_BLOBBY_HOEHE = 89 CONST_BLOBBY_KOPF_RADIUS = 25 CONST_BLOBBY_BAUCH_RADIUS = 33 CONST_BLOBBY_KOPF_BERUEHRUNG = CONST_GROUND_PLANE + CONST_BLOBBY_HOEHE + CONST_BALL_RADIUS CONST_BLOBBY_MAXJUMP = 393.625 function OnServe(ballready) if servrand == nil then servrand = math.random() end moveto(ballx() + servrand * 5) if ballready and math.abs(posx() - (ballx() + servrand * 5)) < 3 then jump() servrand = nil end end function OnOpponentServe() moveto(100) end function OnGame() estimImpactHigh() if (not (target == nil)) and naivetarget < 400 and (modeLock == 1 or timeto > math.abs(posx()-highPlayPos())/4.5 + 26) and touches() < 3 then if (timeto < 30) then modeLock = 1 else modeLock = 0 servrand = nil end highPlay() else modeLock = 0 servrand = nil estimImpactLow() if (not (target == nil)) and ((estimbspeedx > 0 and timeto > (target-posx()-10)/4.5) or (estimbspeedx < 0 and timeto > (posx()-target-10)/4.5) or naivetarget >= 400) then lowPlay() else -- HEELLPPP... if not (target == nil) and naivetarget < 400 then -- This often saves your ass if you're standing inside a -- corner and the ball bounces from the wall or the net. lowPlay() jump() end end end end function highPlayPos() if estimbspeedx < 0 then -- safety againt fast balls return target - 50 - estimbspeedx*5 else return target - 50 end end function highPlay() if (target > 400) then moveto(100) else moveto(highPlayPos()) -- 33 time units for jumping to max height -- Regarding naive target here. -- If the ball really bounces back, it would be a bad idea to jump... if servrand == nil then servrand = math.random() end if naivetarget < 400 and timeto < 28 + servrand then jump() end end end function lowPlay() if (target > 400) then moveto(100) else moveto(target) end end function estimImpactHigh() estimImpact2(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_MAXJUMP - 25,1) end function estimImpactLow() estimImpact2(ballx(),bally(),bspeedx(),bspeedy(),CONST_BLOBBY_KOPF_BERUEHRUNG,1) end function estimImpact2(bx,by,vbx,vby,destY,Frage) -- erlaubt ein besseres Estimate mit ein paar unbeding nötigen Angaben bgrav = 0.28 if ((vby^2)-(-2*bgrav*(by-destY))) < 0 then target = nil return end time1 =(-vby-math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) --time2 =(-vby+math.sqrt((vby^2)-(-2*bgrav*(by-destY))))/(-bgrav) timeto = time1 if (timeto < 0) then target = nil return end naivetarget = (vbx * time1) + bx resultX = naivetarget estimbspeedx=bspeedx() direct = true if(resultX > CONST_RECHTER_RAND) then resultX = 2 * CONST_RECHTER_RAND - resultX estimbspeedx=-estimbspeedx direct = true end if(resultX < CONST_BALL_RADIUS) then -- korrigieren der Appraller an der linken Ebene resultX = 2 * CONST_BALL_RADIUS - resultX estimbspeedx=-estimbspeedx direct = false end if (estimbspeedx > 0) and (resultX > CONST_NETZ_LINKS) then direct = false resultX = 2 * CONST_NETZ_LINKS - resultX estimbspeedx=-estimbspeedx end target = resultX end blobby-1.0rc3/data/backgrounds/strand1.bmp0000644000175000017500000165346612042452400022050 0ustar danielknobedanielknobeBM6W6( X  k/ 9<„WF*H#DP Nė{JM:EfS #$#uGO%)JHcdXgT.Fl/):f-iR0LbѨ#&3{ine[ *+)mrR&'SN+XZqlokRHuFIkjGG)'÷o:*0JGSw5pjhN⪹H0>>>>> 1zJsx0 m 0 x x>> >> > > > >>>> >>>>>>> > > >>"">>&> >> > >>> >> >>>>>>>> >>>>>>>>>>>>>> > >>>>>>>>>HHgXAAXggggX/lHHkBHHkk___HHfNN|*HY*N|gXg_AAA_HYHYkf|Xll_HHYHHXXgXPYHYYHYHll_llllllBlBYYYH lA/AA?__]]*](KRRRRRrMRR K2^S]2]] RaRRrK^oIaIIr q2{2{2{S22{MRMMRRMrM2nSn2nnnnqrGr GM]2^2] raGaaaIaaaaaar  krGr* rRr rHr GQa]((2^2]my1m0mm Y)m))kuggXBHkkkHk_l)GGyyym01B yy0mmmymmymymHm)))m)__Y)Ys)yyy)ym)Ymxm0[smxYBsYxxsXsl Y)))YHH)0ymknnAAkl_**{{*H HYxJz1mmm)Y)mym00)YYQk{iS GGGmy nqqk)yGmyyym)Bsl??myymmykH0ymyyym)my0yymGHl_)mmm0)Ym)k)GYl__ )myyG_[ x0m0y0y0000?yyym0mmmm))y000y0y0y00mmm)myyyyy0B%xxmx)&6xx)x%l)00ymy0ymyy0mmmmmx)x)m0my0mmym)x0ymyymm)m)mxmmmm0mm0mm0y0my)mmG[lll?g[A x))YslHYNNHG)mlX88/zlg[Bzz?l0)m00mmYOg̭Yxlzsxmmmm)mPXgXAHmmmm0m)mlBl Bsxs䶨zBBz[[[?g_HYY%HHHGHk?XAAA?lY)))YXqy))sz/[[/BYYY%GB ``TTs1xm)H_A/XgX?kl%HHr%Y_gA/llAAs뭭l0)m00m0xAOgk_kA1000)smymm0mn{B)H G)))mGry00xzxyyym))yyyyyymymm))mkllx)mmm_myymxsm0xYB xmxBsBBmm[BBBx)YmGm)ml_mm0gfnnlG))Hn*HsYxxBzBBsmmxY)mym00H_)ym)HS %GQyGYkHmymm)))szmYlB)0m)yyymyyym00m)m0yy0)_s̭)00Hm)Y)y)Hll_lx0yHXmyy0myyyym)_Xyyyyymm)))y0yyyyyymm)m)))yyyyyymm)YG)))YB&&!)YsY%kmyyyyyymmm)))))myyyyyyyymy))Y)yyyyyyyyy)))))myy00ymym)lAlY)_g?[[_$x)mxmm)YHlll_-8l%)%*?/[gg z_Bm0xmmss _/ggxmxmmmmyXlmym))m0m$/gg/?/=[sz/gBG)GGHHYH_BXA?HYmY?_yyyx [[z̨lBxm)x%z̭ W````T1sssxB?[gAYslHYHG))slXX[$Y)g̶lAsBzlB00x00s Yz/Xg_10 0000xsYx0m0xYmy)%)yGGm)mm)))y00 Ymyyymm0y0ymymyymm))mm)[mxm)))yyyH0YxymyyklYym/[/lm%)mmGYmm)5fqmymG 2XGG)))x%YzzzY0Ymmy))Y)y0ymm))% % %GGmGH)G0))G0m0000m)Y$0H)m))mmmyy0yymmmy)myymmY)mx_?)mxm0)Ym0m)AlyyA )x)x0))mm)mmAmy)))YHy0yyyym)))PYyyyyymm)YHx0m)sB&+YYHB)myymy))))m)sHYmmymyyy0)))00yyyy)yYHHYGYYYyy0ymG*qlYsllH)mlXBHl)Ymm0m)X/g˕f|NH))%lBGx?=/g zBY0AOsxmmm0mm)HHm0ym))xgA/Xzx0msA/ls[s[ ̶8|Bx)))mmYHk)mOX/A?lY)yylAṰ[zxzzHm)Ys$TӔ```T 1xxxsYB[/Yx)BHY))))xl/[[zYsT̃Ϩssz_BsxYml[/8g0y)l== 000000sBB)0000)ymx))mmGGGGmYYmmryy0000m)yym))ymyymmm)mm))))m)?xmYmmlg?0HymHx0sBYm)mGGykPggg=?mYmG)YHk_yYAqnXы))mmyG Rv2 aGGG)m)%YlYmmm0)%)m?gl)yy0mm0yy %0mmmyHGmy000000mmxmmYsmx?Y)m)))mmymy0ymy0)Gmm0mmm)xml[AYYmmmYmylmQy)__Y0mHYmBY))mmYBlmy))kHYHHyyyyyHkqG)Hyyy))YY)m)mB&HlBGGymmmG)YYYlmyy)ymymPPHHHlmyyyy)nX)PHymm)GfnYYH km0Hmx譭YsY)0m)H|NNNNf*GG)m))Yk)1lնOss[?lYxm[[O[[mx0mym)YHBm0yym))xY[T̶s000mm)slAsx0x000xxBYTTY/lm))mmYHHmy0Hsm0 lHHls)0y YB[̭sz[zHYmmmYT$x YBHx/sm0)YHY))xGB/B=̶B ըgO[s[[BYmxՃ[O)sz= x 0000000 1x0x 0 )yymm0))ym)x)m))Hy00yyymyyy))ymymymm)))))BmxxAXmkmym0xBPGrarGP\fAlxYG)mGY)mml5k*H)yyrr rrrRaGrrYl)0myyy)my)))0xmxmmxH?nkGm00mYk)mmYm0s0m)mmsYx0Blxmm))mmmy0mmym)myy0ymm)msH//)xYY00m0Al0Hl0yyYYm0xssxx0mYlH0y)fff_PYPHmyyyyyyyk*GHPmyyym))m0y0y)Ysz&&lYHBlm%Hym))Hk) HHyyyy)y_lmYYYl0yymyXgmYYkHyyyymym5)HHmmqGym)mBBYYY)ymknnXNff5N5yym%q[AO/sB/նsx sx[[ضlx0)mmm)YYYz/Yyy)mmm[[[zxsxy0))m0mGsm0m000mmAslzYYmY)mmHY%xxYlx00xxmY)HY%0l/0))lg̭_?z_?Y0y))TTmx))0m)lPx0x00BYm0Y)m))%sllsÃsB[OYz[/1)sx[[ )s m 00 0 00 000xxxYxHmym)0mYxYYm) yy00ymmmymm0yymymm)Y)))))Bm[)H)ymmlxHHaaraA0BlYm))))m0mmH|ff5yGP)mm)RPIRIRGIPPBmmm0ymm/H00ssx0xGHHx00HmY0Yy0A_y)mmm0Y0_mzlm)))Y)mmymym0ymymmymmm0m)YlA?)_/0mlAlY))m))yqmmYYxmxYYY1)x)xm_m))yfgf_YHHyyQHkyPyyyymy))m))mmmYs&Nj%HH yG%y)mmGml_m)skHyy)yfHmYYHy0yy))AnXA)Plymyym)m*AmHHPy)mGy000z)sH)myyA5NXf\fHGy)/H==g[T8Yxxxs̶Bxx/sx0[))mm)Ysxllyyyy0x[[խsxmY)y)YYsBsm0mm)s/XY%BlBx))))YG)xsBxmxm)s)))sY0xyY? )m)x[A/xY0yy)mxO`Ҁ`T$sxxm0x000xY)xxm mBBxY)))Gx)HlBHs/s[| sxxxs/[BxxՃsx0[mz" m000000 0 00 00xY)lPyy0my00m)xmYmG kyyy0yyy)m0myyy)yymmmx))))))0g 0[)lXH)Gymyyym))YYRrIrrGG{lmll)ym))y))mmGn_PG)y)GRRRrPPHY)))myyyy)ykBYsmx BBm%YkH%xmmmmHsxPyyyyP1/H0l0))Y))Y)mm)0y)mmym)m))y0))BYlll_[gBBlA)0)Gny)Ymx1s1sYYx)x)mx_0ym)AnHPHmyyyyyyyyHGymPYYyyyy)))_H0yyGY&&sGYHBGG)mGm)Gy))YsYyyy)yy_-XH)YYsm0mmmymmm)m)y))HH)yyyy)ym))yym)m)yHy)mQqHyY?_YHY)mymyn5X\5NSGGmG_A뭃 $lA[l1xm0[Y0zT_xՃzz))))mmHx0s?yyyy0[zx xymmyymymHY0mmYHX*HBk%ym)m)mY)Y0lz0 xxxx0_?mBxmmkH myyl_m0m_lxHBx0yy00)AWBY)m0)mxmHm)Ak0xxxxxBl0GmGxmxxm0l?l/[ T[BYm000/x0zzxm1ZD 00000 x00 0 0 0sm00mYxmxGm yymyymG)mm))yyymy))))))mmxs)/PmGymymY%GRaar0y0))?Ymmym))ym))yyyGGmGymm)yaIRGaaRPsY0mymymmGGP[YYsYxsYxmkkH0 m0)ym1P)mm?x/XmYYmY)mmy0mymm)G0mGmmmm)x)HHY0mmBx_A/H0))PyymxYxs1Y1xmx )m))ymy)Y)yyknPHPknA)ymym))XlkqAHsB&G%YHY yyGGmG)P))m))))YHY)yy)y)mGm)YYm)))mm)))m)m)))))yyy)mm))y))mmHyqXfBl)yyn5f\\\5kHG[s0xAg/Bx0B϶ 0xm zzz$smym)ms0A0yyyyy)zzzxsmmm)0m0))YHx)0mymHB)XXllH00m0y%mYmYBsx0xxm0NYY0)lH0yl_m)mm)0ym0mx00xs0mmm00)syNmxx xx0 B m)HmG%y0GxG00lAgxl[s0xA=[Bm0AsY0x x zxD1 0mx00x0 00 00m10yyyx)m)G)myyym0Yyyyymm))))Y0YYGHaGQyGPYqYG)araarRrG)))HyyyGy)))))my)GGymYy)mGPraaa_mmmyGYs0BNH{Al0m00m)))00BYyGPyY0s0s))Y)mYm0myymyYmmymy))YYHm0?8lAlY))m)yQy)xx1sxY)))mYYmmm))Y)))YHmyAXXAH_XYHHyQ)ymAglB&Hy))))))))YskHyyyymmm)my)m)s sm0mmmm)m))GYHk)my)m0)))))Y0mYPHGy k*B)mY)yn\\\f5\nnG5gl0mX|?%m0l8s008xضz[z)0lmxBY0Yyymm00 sm0mYYz)slsx_mYmH)k8N8{ffikGHHkHGGB_Bs0x xsxYB?YYx))m)YYmmym_)0ymlYx0myyyyyyy00 x0xm0mmxY)%0%_lxxx 01 [l)B?GG%kYәXXN8B_B0x Ng?Yym0l8 00Ox䃶lzD" m000 00000 0 x0)HyY)yxmmx)G)km0yyy)00)yGyyyy)0))YY)mmx[0xYGPkPPk*PrIaRarGG))kyyym))YYyyyyyarRarPkyHAg_yYGGYYmz1Bs/8s)/Xn)000xm)m))mxxyyyyPyyyymx0/m0)x)YYY)))m))0ymy)my)00ym)l뷷H)lgg//As)mmm))yyBxxx1Y))0)B)y)))YYYY)Y)0yymnlX̷XHmYHmyy_NHYs &6HHHBGHym)))YBYGm)m)mGx)s0Hxm)))YYYYYHkHy))YHHHmHyGQQy%/YHmyyy*55f\55\fnϕz0)yBAG0B8[00ssz00Hm)xlY x[0mm0̭?1H0XYBBBYxY)y)GHHNiqyQ_smB 01xx x[mxxxmmYYYyyyyHmym)))))m)yyyGmymmm0lsm0)x0m)xm0//mxx1xxxxsAxY_yy BA[g:X|l_g?00B[X)0B8/0g1YOPksxD 0 00000 0 00 00kxy)x)mYm)mmG))%_lmym0xylXyyG)))))))))00[mY)GayyGkkkPH*H*araPP**Pyyyyy_HYsHllHHyymmQaaRaIra*_XGGG)B̭l?1l88mA?xm0xmm0x00)mmy0m0xlmAgB)xmm)Y)))))myY_mxmmym)ymPkHH))yHXglxx)m))yyy)s x1x)x)00YHBHl_l_$B_)gnA?Bll)myPHHnX*뷭_AnXm)GGPYPHHHHHB_k)0y0mmYYYYs m0lXsYH)kn*mmm00BzYx))0YxYHyyGYg/?y*Nfff55N\i5Nf\5q8_0mmmXGy gYm0g[Yzzm0mmmm)) _ g[)m0Yxm[ss00mlg8Axz뭭B )y0ymyyLN8qL5Nfnq yG?)0BsxxxxxsY0mBBmxYxmBHyyymm)0yyyyyx[x0m0mxx 0BH0xxx x xsz0)mm)A:`:XX%lg?0xm)AXGBgYm0g[s001> 000 0000000 00BAmBm)_X/)mm)my)ym0_my)mmXXy))m)))))Y)mmmxXlm0ryGGH kHrqk2k\qIaayaGHGyyqPy0y)m))k0yyyrraaGRaMIrq5fXNNyPYyl1BBA|Y0Hlmxm0m0mx0)m)mmymm00x))ml)m0)x))Y))Y)m))yX)mmmymyyy)sl Ymmmymyyml/lx sxYxm))m)) lBBH_[_ЭAmyyymAg/AXgg_By0mGyy))Hl_l_mm)))H*HyyyYYH)ym)lYP)Bzs00Y[mmmm)_XXX̷ym)[zm0mmm00zy0y)GyyGGG[8y)yyn\f\55Nf\nn55\\L?Y)yB/ly?)m [䶃[x0)mm))xsxmXAl_AAzBOح Y0ml8lxH/=Bl )mmmyyyqSqkq5|5iinXq*?mmBsxxxxx1x00)1xxYHYY)P0)_YXHG)m)yymx00x0Y0)))00mx1xx xY0m_k̙:``:nXN_8Ax0H/km?)mBl[/ s"> 000000 0 0zs0sAgl)xm))m0yyy0m1mP))mm))m))))mY))mmGGGaym)PkG_2rGGyyyGGHmYklm0?sm0xmx)mA̷m0yPGGmyyGaar{-HH_ggA)myaGa G0B=s1x8xBxmmmYxsx)0sY)YHHYxBm)mx)YY))mm))mmY0Yx)00yymys_sx)mm)yy)Y? sY s)x)mm)ll)Ys)m BBmx[l[ABmxyX_H_ggXl)))yyyyyyymmmymym)mm))?lsY00mYqqHllBBs0[[ 00)BkngXmmYxYs00xx)mm=l0xmy)ymmGmyAlmm)yy*Nf5i5if\\fY88[AlXgX)lY))H/=[BBB sY)Y lsx|=OXAx0m_gX_/lY)lyyyyr5ffq NNNiqmszYxx1sxxx0m)xyYflYmY0Hly_NNfXX0myyyy0zm00xx00m x111xssmlԺg::A5XHg/H0lg)lY))g[z=[0 sD> > 00 0000 0̭smHxx)00mmPB)00Bmy000)mH0yy))m)))m)))x))))H/lGGGyyyyPkmna)Gyyn)m*lmy0ys)?Hmmmx)AXXkgPym)yaa(qn/mX|_0yymyyG)1 zY8?YBByl*y0llxm))Y0))mm)l mx)l Y)m))))mm)m))Yyy mmyyYyYs)mymy϶ssxmm)Ylz))xA/sBlmmlA//lXHY_*yyyyyyym)!yym0y))yymmymy))m//1=)Ylf_?Jzm0[m0lXnAA_ss10xxmxm0Y/smlsyyy_n[Bl?HmyN5NN5f{*q(ffG8)ggXgl)))Ylgg[Oz00 _Y)l[zBX̨g̭=/Hx0g=-̷BG)5qfN-**{Ymlz11xx0 x0mmxx)XglBs)y))))HYXgfffG0mm)yyAHYl10m0)YmY0x0xxxx11xszl0{Hg::TgTANXXmg| mgg̋)))YlXgg[Ozy D>>>x m000 x1x000zm 00x0Y0l1")y00m?1)))))m))))mY)0_gN)ymxmPXmmli_m̋00xmmBym0|Gmx N= 00/Hmmx0_n)_zGG XH))0y))aysx0 s1J|/ls1mAky0 lPY)myyyyyymY)/NOl))x))))))))))))Hxm)yyY_x1Y)0xxmxJxxxmmYBsg[s s=z$[[ly_X_Al_AXgyyyyyymy+yyyymx)Pmyyyymy))0zs1Ŷ$BAkB[ JY 0B惶l gXqgggXBzzx0Y sm011A0m))yH|lYlYHymGyN5Xq q(*BOgY/l)y))))nX[=Bx B )sY ?XTOl/zz1mN=0z8-/Sn k*Gyyn|n8{Xq **q%s/ 1s1sx0mmxmxYY?H%)ls))XXPBlll̷x?=/zBY))Bm0)mm0)xx1s111 [YB*] ?A[-ii5Nn_yHg8O/Alm0))))gX/lym00E"xxx 0 sJ00x x)mx)xm0y)mg؃lyy0m00)0sB)))))mm)))xmlmXNHGGy0)0YPyXnymG00)?ll/mx00)0GYmx_lN8xYH m l|zYxYnlA_)0)mm0Yk00)m)X)y)yG)H[J8zJs)yl100A)yyygXkHlY8NXmk))Y)))mm)))))YlX m0x)myPPysBxxPymB0 1 xxxxHB)m00z8OzOzϭ[g yYll/gXyyy)yy0yx)lyyyymmm1/zsz[[lB[xm000/[ A/nAf|fg涨 s=x [00mXmyHNXY)myy)HHPnXfGrq* 0B_)|zm00mmym)y8gYBxYYsY lz m00xA[x0xsxxX Ym[|8 -/ r%lGQyyfn)%%_{{AA[/zsssJxsxx0m00ylzYBBHsYYXnAPgHl)Ys/s0m?mm00mm11Bzss/{{*HYYs8NN5yYBlN|z)m0y)mm)yXOgzYBymmy0zzxxx0 zzAsx x[_s)m0)mmm0z00ymY))y)))mPy)YB)|Nymm)000Y_mllzsz_m)0xmBY/NsX)1z ?gglzl_Y0))xx00_ lzl_BmxY00x)00ymGlXN8?YsYAYyHyyyyyXmNY)))))mm)yYYyyPyA)mm))y0mmYPYmyklm))m))))m)))s[8gHy[Bl_yynGk**qqkmyyyyy)m)mG))&m))l)yyymymym)PYYH0$z_mxzxm00gf_=OO=zs szs ?lx00)x)0ym0y_/mmmYBlkyylB%%xHlYGxmy00BYss1x0)mkN|B111x)00mz00BzxszxmYB l|_-**{0myy558lB%H{q[A/X111s000NszlYHlBHYmk|N?X_)y)H_H00 xs1?1H{SSqn*nn2qsxss/NgnHm0HYszsx0xm)kN|z11ym000x 1 x 0σ涨1 s0m0mmm0mm)00000xzYxB0yy)00)))m0yyYGmYfHyGmYmx)xs Yx _lx0mmx00mB=zz8[1YYXzgAHBYmA)m00mxm0mll_Bxl[B00m0)m0myyy0%gNsx0mYm))HX00)y)mGyyyym0kNgPY)0Y0mm))mm)00yyyyyHl0))mmy)YYHmm)mm)))m0)YYHB$[T/mmm) gkyyP_GGGGGymym)yyy)Gmy)GY󌓟Um)PsHY)yy)mPH)PH00z$؃_xss sxx0BNnlzm x 0[?BmBY0syy)yym)AggY_lYHllyy*%l%%H)HHyy0Y 0sz s))ym)PyH/sYsxszsYYxm0BXlBxsB)[|//8XXn{%B{Y0yyyyG NNiB%lHynB/ 1sxsx/Hm00z[B_Hl))yHll)000ymxx1s[A* * *r_1B5X/PyH? 0sls1))0y)PyYgxsY0x0000sxxmm)BYslBxxz[0my__l)sl[[Aymm0))y0)m)mmmyH)Y)G)y%myy)mmxm0 YsY)00m)mm)Ag8_%0Yl0OYmmmmmmyy0)000m00000xmxmmYY)y)))G)mmm)l x00)0)m)X_)YG))yyyyymyHlm)mAYmYmHH0m0m))))0y)mmmm)ymmGGy))))myky))Y)ymPG0k)000m_lYBB0)))ABY)Y0)mmxmYYHBHHHY)yyyyykXX*)%lHYYYYH)my)my/XXYHHmz _HPlYx0x)mm0 Axx001A̷z$1x xx x H?Y))l)mmy)ymm0__Yl)YYHHHmy))G0)BH%̷m)YYyyy0lAY0 ssx)myyY0yl0x01xsY AAlm[sxg_xmxYxBHx)?[Agz*|8fXHm%_ymyyyfNg?xYBGX5N8JYx?0)0mxs?_BBsxY0x_zyymyyy0y00myymyyymmxY??*r]qQGlH xJz8gfmY0xB Ys1x)myyPy0[Bmx01Bm0y000s[B00m00Yl00000xm00)y)mH)A[Bm0mm))yYm)m)0mm0Y*HlH/|HHGGmmy0m))0))mmBlsYx)0m)YHY)YY?smm)xxY00m)Hl)my))mm0mmx)0sx0smmYmx)mmGYm0)xmxsYxx0y0BmmmHHHyyyym)y_l8_qHY0))0)))mPyyy))mmyy_Pmy))kmykHyylYyHPGYnAY)my)XAmYYYT樨msGx)x H0)BY)Y YYY%Y))Ymymyk5|qmH&#nPsYYlmy)mgfm)H0msm0HPky00xxxm0l[s1m0mHlsxss 1xx 0J[s00mm?m0my)y0)lB))g?)YYYHmyym))B)x))YHyylxx lz )0mYYYl xx0)/0xX0Y_m0m0x00_xx[/AlBHqX{lY)m/ym)yyy)nXX?z xYsqfXN|8ϭYxxY??smBm01 lB??lY)m)l_Y0m)))))))))))YP)YYBsxY?%m*I2rQBz?X_)gffXP0Hglxxslz )ymYHYYXlz 10x00000HBYyxH_mmmmyx00m)ym0y0y)yy0YH0mm)H_Y)mY0)0000lYg888_Y)))m0Gyy))mm) [A[lY))Hll)my0sYYzmmHyP_))GmramQHx0z 1z0mmm)YYy))mm))Gm))0mx)yymyl*myy0ym)_lAN8=Bk)m0y00mHHmm)HlHPyyyH)0)Bs0nqkHymyk*yyQ*nA*Y)YsA[YYH)mx)m)B )0 [[zA_B%m)yy)mPg\gf)H!kY0mAlmm)ymXnY lyB 0y)m)00zO1"1 0m$11 m0000=Bmy)mxmmym_/Yxs_H Blmmyym)HY%sYGlyyyY lBxyy)HHY0lsssx)m0?Y0m=YYmy0)xy[?mxgl*lkkaH)gmm)y)mm)mm0 [[xy_N55f-_x0mHX)x$l$B_/A xxs xm 0))m)))))mmmmmm)mYm0))%lGMoIKrQr*aYm%z[AYmymONly1BzzBxx)yGHYzzsYssmx0m00/)0GyYly)m))mm0yy_kmyy0yyPH))H0)yAAB0mmmYXf|||8Y)m)m)Y)y)m)mmmA[[ 0l_Xg)ymmHs0x/JY0yPyXymq2mBY0sxxs)0mY)))Gy)GY))m)mmxmm)mm0m__lyyyymy0ymy)HAs/NN8|8Y)mmml)mymm)Byyymyl0x0?m0)mlN5yYG)yy2q**AHmHH)Ys[xxYHYPlY)xGs)z[$T$ [X///mmyy)0GPHH&Y)HH0A_0mm))y)ymm)mYl /BxmPf*y0mm00zϨ1 11 xmmyzx xx0 0lm0y)mm)mmymy)nl))x)YmYYYHHmmy)mymsYY)YYHmyyy0_m lsl_mmymPHY0A[lm01)Y000y8?s00%YHmyY[sA X/*inqHHk/NY)myym)m)xxzxm mNNf55ff?mm000G/NmYTX[sxxxz xmsm0yyyyyymyymy0mmmm0m0%a*oIIrMaa%)xzYyy0y8Nȷmy[m lslzmmy)H0A[l0x/x0my00gGmYH0ymmmmm))mX0yyH)yyylY0ym0H8Nxmx0myHggB Y))_HY_Yyym)0mx)s l Y)s_m0)/8lAyy0Y0sxPknPy)q m%lYxmxss1x)xx0mmyQGH_HH?H))xYx0m0)yy0H_Y{H)yyyymy)yyyyY_l 1A8XYmymyx||y0y0yY_)yyym//s0Oz_)nf\GQyGyyyQ2*QG*nqyymm)Y )YHHYYY)Yms)lYAx_?*AYx)0ymm))Gm)Hl&&4ymYY0llmmmm))mm)m)xky_[sHy Qy)m00 z=D>x 0xmm >1> x0 00l=my)m0ym0mlyxyGmYYYslYmmy)mx)mYmyyyAYYYYs)myHY0ضlYx0mm0))lB00/x%)___X-l/XB=?*qnHl)%g/Ym000)xY)xlyqNN5f\\5Nf0Gm0mn/sz[OgX/sxm x[x xmmy0mmymmmyGQv]eIeK %)Bzl)0yymYNf_mmAYYs0yymHYY00[lB0x00m0X_HY)ym0ym))0mm)y0xY)mHymkGmyyymmշmyyy)l8xymygHm)))BnBmHB)yymmxm ssx0HY0mA/_lX|n)HYl[B)))y*GGrPH%_Bx)xxY1ssxm)mmmyyGBHmHl)mmxmss0mm0y0l_Y_XyyyymyyyyyymYsm00 XAYmymm0l8l)m0000n)yyyymY=0x=[BmyyQyQ)mmyQan*Hm))))Y/TmmHHkHY))%B0g[Y*** Yym)0yyyy)G)P&))Y0B)m)m))G))YHHyB[xY_GPyym)m0 Ń1 )1"1 x0000mymmY0mm)))000y00mHYH _ )mym)_km0)H)0))Y)mmGYY%yyHY0[zխm0yYg 0x/m)Gy_fgll[|NfLnSn{qlkkY0_mm0m)xxBmmYN85585mxm Q %[Ogg[xxx/xxxsm0my)))ymmm0mymmmQKIMeaamB_mmm)|NNgmmmY)xm)YYHYY0[zխYg)0m0mlNNfmmmm0mmmmmmmymmm0_BlmmH0kkmyyg)000 /[A00yy_nm0m))ms)YlY))mxYxssxsxxlm0_ /[[gk)mmm AByPyGGYHsYszz1mxsxYs1YYxmx)%lYmy)YxYx0)xss1H0ymmmmYyyyyyyyyymBsx0lm008XHm0ggmyym8s 1=[sm0)yaG HYG*ryQ*qG)HYHB[x)BlBPHHH)))*0)/AAgkY%mxmYHHYmHHP)))&ɉHHk_H00lBx)xYYHYl%HkqqrB{BsHHXmmymmxm ŚE1 s1 >x0 0 xymymm)xmmmY)m0mmymYH?s0x0))XAHP mym))))mx%HlHP))?B/szx00mmkB)0XXl00//mg{)mlX8N5\\X{B)Bg_m)BsBlxl[Y)mHP5NNN5f5fH)0m%GQ*q/X/ gJgO/lm011sxmyyyyGHHHYmmxm0yQQ)GmmyyQMMo m%)1[=lxyyG|)))m)HlH) [szxgAAlmm0mmkX||XY0))mmm)mm)m))0)BlmlH)HH))ylX_0xBlllBxk/AB)mm))xmYYyY**0)x)xxxxxxYmmx0xl)))m)0H)yyPGPyGyYYzx1x)xm)?_myGYx))0mxsx 0xx0myyy0)YY_Hyyy)myyymlzxmx0m_y0A88))mlg/yy)lO8歭s8=Bm0yqHkHHGGqPGaakPHPkHHBllHAxBYHHHsHHYY))xHHkY)Ag[g**G))mlllyH))&&!#4YHkl1Almm)))mmHH krq*l/AGGHYymm)mxs1lH >x0 00 z)0mymyyyyxxH)0mYx)0mmmyl__0A_Y_A_/A)0yGymYYYYYG%l_ll__Bϭ0mxm0_NԷ)8[m0Agm )x?O/XNfffff\Nn)000|X_XOzsBxmyyy\N|5555ff/lq*0\{qNN?lgXAlmmxms zxx0mmHHY))))xGH?kyKeoMRMG s01ssHHN_myYYYYYG_l__Hϭ0mx̷xm00)x0ym)xxm)m)))0)x0Bl0msgX/_YHYyyyyyyylBBsYs110y0yy?l)mm0)))myBly0G)x1xxxxz0mX[sx)m)Y)mXXmkGmyyYs1zxxxxY))0mBkYY)0mYxx0mxxmxx00mYmmmm0?*lPyyyYHmy))xBlxxx0lBmm0z/g8l0)xmY[l0mmy0B/N=s Ozmm*HGm)lkGGaHyQykPy0BAl_PBHHHHHHHm))))Y[ YYmmm)0Yy0yyy)&64B0x00Y̶lmYBmy0yGQGGHA_0yxmm0xm1#"ÿ"[sz x x 0 zx0x)0my)m0BmHg)?gXA sH0ggAHHl/)000Ym0mmYBHBHHk__Hx?sxmxm0YYm_lm////Y00m)0_ggX88NNf_xBBgOBN/)0yyN5fN55NN|i{?L8NN8?8/B000xz0xmmHlHHmxmyQGkqGQ ooaIam?x0x[k)ymmHHHHHk__ )?sxmxmx0mm)_m0Ymm))mmm0?OsH8|?lHmyyYlAsm0m)yPGHmmmm0))xs mmm_?0m)x)x)xx1xx mm[=[zYYm))mgymyym Y xxsx)xm)m_H))m sxx)mxxxYxx x000)H)mm)llPGyym)HH)y0YHBzm)yy)zszXն?l_[Ym)m)[OOzOOzm{2qmG?HQHyGyy0y0lխl_Hll/lHHHH))xm)mmxx X[mm)0m)mmm)l0myy0ym&&[[)mx|gB_mm)Qa Gm//gHy)Yy0mmsûû1_x x x0 0xJ000mm0my0[8/lH))?lAmY)_gA_ll)X88g8XgH00mmYmmmm)))mmmm0m0llY1|g_mm)ymmm)X[g/lxxx0YN|gXXX8fN|n*kssA8X Y8=lBx0mymyyq5fNfNNNX{qSnLg|N8/88?̋%%mG00 Ym0yGPYklk)Y)myQrMoRI MBi?lB 0zm0klP)mGmyG))ym))mymmm0lzY1?s00mxm*/xyy)YB0))myx0NglA|XHkYykg?Y1 mmxmmlgn))))mm))m)zmlXl0mm0m)xYsss xzlxglmxYm?mXg/)mmyyyy))Y1zxxYsY1)xG)0xm?)x0YzY)xx)m))Yx00xmxYm)YmY_lHPYmlm00yl/ll/0)Xnl0sY [[ϭX[xm00xl8=B[OlYmyy/*k0AB)88yH*ymymy0YAAHB_ YHPH)))m0mmmmϨx xmm)m)m))0)yxy&B=0B̭_/Ymy0kPk0 BG0kXgOϭ0mxHm0sxO##1>zl 0 0 xx [=s0000mm0y0x0sN/x_B)Am0_/A/?mf|gg8|NB0 lxml0)m0mmy0m)m)))0_gAss8_g|AYxs)0m)yy00Yzs1s00mmPAA8gglH%HsBT[YgOx OxxH00yyy55NNNNN88q*]q|N8LnNg?BB*k)1[1sm0PBll0Y)m)yyyKKKoK{sYs sxB BX?xmB)m)mmmy0mm)m)))0_A%s8B?_mxHHBg mHY0my))0x0gfXHmHXHklyy)B xxsmmY)m)ymYHxmmm)mm)xzlxmmmmm))msYxsBxz̭YmBY0m)0mxY_)lAgggGY))YyyyymB?[z1 sYssYmmxmm)x0Blxxm)xmmxYsYmx0sm))x) mHlHk)YH0m)my)H?gXXHm0))B/zO[Y0YO J[Bzs_Bn?)m/HmyAyyyymmmmxXgg__AYBPPmmmxm00m0zzszm)m)ymmy)m)y0y))myy&6zx O0m m0sglPkkPGHymkY_A)xOlsHs0 #Z[A0xx xmx x xlm0xmxmy0ymxmBzm/XYlm)?lY[gY00YAgN_/X|=_YxlY)m)00mY))my)BYgzYBsBxY00m)ymy00lsYx00)0XO88Nm)GYHlg[YYlxxO YY?Yyyq5ff*GqrqqnlX|/XAg?y%حsmyyPHk_kHmm)yyQrK]KKK {XB sgX/mmm))00)x)mmBYgzYBm1O[_ylH_mm0)mymmHmm_PyyG*g8lm0xx)m0mm0mm0Hm)mm)mx ?0))mmmYsY[[)yy_))mm0)00XgXgH0))0y)PBYxϭ Ys1)))))x)0zX1B)xm))HBmxsBB)Y0s)m)kY)_Bzsm))H%AXiXXn%Y)*? 1zzxzOJz=8A/OOYYHmmy)kHHm)Asls)0G)mPYmGP)))yymmy_nԷ?Y)xY)x))m[l))my)))YlYyxmm&&s1x[Yxx00m0mlA_HGQy0xyyYBmm_/%Ys#DꨶYmx 00xxx0x1)Y)m0mxszm0mm_/?m)BHY̶H0mmyg8[Yx[glxmmmmmmmmmxHy_X[zB0xH x0myyyymxx1mmyY s))G%Yz[ Tx0x=[ 1B)myPYy rqN__|Nf|f%lH)PA̶ 10xyGPHk_lgAlHlm0QyQaK]KKK l)/==樨=0[zYxx)mm)0)mm)Hy_[zB0m00s[YH0m AXAmY)ymsY)HP)kkymy0yyyyGHXX-BxBx00m0x0mymH_)m0)m)Yssz/glm))Y)m)m)xs[0yykHH)x0mm YA뷷з))xyxyYH00x[xx)mYml/lYYm)mmyH0mσ0m))H )xYmlymA뭭[BsYY%H %H8yYY?/xxsY0xz̨gABm)mYX/ggzA_BkHYY)Yyyy)mm)))NgA/_x))PBH)xYYmx?O[0))mmGkklHlPmyyymyyyy0&Bxxحxm000myQyyy0yymx)s?/X/z?l00h >0xx0 xm zzssxY Bxxmm0x//1m00ml0Ymm0sx0m00m8_x0z0N/l))0)|mmx))H?nlm_B00m0Y xYyyy)m)Ysxx YYlyy0xmxszlBG))l[[ YOO[B [ sz[m)myk*fqGG%)ykf|AlBNNlB%nnx0s0yyPHHl_HNlylHYQQQo]K]^S]{kB=[s xJN/Bm)0x|?mm)x)H*km_B00x0xx0x0mmm)yymx)mm))mHGy) rvqrGmHl)mm)0xm0)5NnGxkKQQmmy000yyyymymmxsx8/xm)m)Bx00 ssxsmmGGyNfqX//[?mm)) s)xxmYxm00xsmxx xmlB)))m)/g881x0myAzx1 xxmxyG򋕋Az$xxx l?q*_XXX|BBBx0BY1xxzB0Y"Z=z/sxxm0|O*k?_HHlHyym)yHkH)PNN[lx))n|xHHYHkm/xxx)XNfNnHPYymy0y0my&+smm000yymHsxxx%%%% mymm?[smx0mmmlBB1)x Ymm00 0000϶ 0GmyyHYyXm00)|l)HrPHl_BHxmmxNx)xxlx0ymy)m)zzs1B)0)yymy)?zzYx syyyymgg?BsY%s[OB:TB%0mxyqNglx%s0s= s1 ss1 m1>1BxxykPP_kAqHYYmyQym*ll?Y0BB0mlzJ?_N_s)xm)=Nxxx1x0yyyms1x00)mm00myyyYYx))mmyk_^Py[XYmmm0mnn*)m{S]]SSqY0mmy00xxxAzYBxmmm0))0)xxx Hm0yyyNnPlmm_xxYxYm0ymm0B0xxxxxx)))x))xmmml B?AxY1x0Y[s0)xxmknNz1xx?nLnXgN/A8AsYzYxxzllXŃD"#[=gжA==sx08/l0yyy)%mmly))mPA*yyP_xYmHffYHHHyHmYxmmgN|n)yyyymmyyym)&&!Ǩ=[sxmmyyyy)m mxx %Gymy0Y[xxm0x0mYgs BBYxHmx))0000001)mY)_yAlm000x̃Bm k*l)l_B8O?BYxxmYNXxx %zBYm0mmH_zBxH)m)y0m)Y x0x lymm)y_YYsY_жW:ә`zYx0 0$=sy58glB1 TT=Jzss 100g̨z1 myyGPPHk\fyyAYym_HmylX01x0B 냶_l_ )xmsgNx)x %BYm0m)m0 ssmx)mm0yyyzHmmx0)00)0)msXnq%)m)l[l))m mXgf{PH Y/q{qqqmmm00ms1lH0))Y)mlxm0_XAlPmxmmm)mxmyyQyyykNgA_100 1 00x0my0mxymxm00x0sBxYm))m)0y00m)x)00YBYmx%yYg smxsmYB{Hgm0xssxxxH8n|_?[xYsYx)zggAsm"DDhZ=A_xσz x0glm%mGGG)mmYY)lHk0m))))my))YlBm)NNX)YHBHHnHl)mmXyyymyy0ymmy&!Tض[?mm00yyyyyyyyymmxxmmG GQQy00lx0mx0NNNAmYBBHl)m)))m0000 0xBx))ykB)[x0y?0%kHPHmBYgN x)m_|Y)xBJY)mm)yHXHBsmm)Hmymyym)y lxxx xx)s))m)xAzBsBsB[= /y$Wgsx0m0x[Bmykll_lxYՒTcc=W [zzs x0BJJzssmkkHkHH_lNfXHAlxx0ks=0 ms=XHsmxmzgNx%x?s))my)ylz0l)0mmm_Ym)xmxmxmmA*2Y%Y0YYsG0ml_Pqk ?S qSy)m0))0z/lxYlHmy)mm)A /?lAYy0mmyymmyyy5X00 0 0 00000mm0mm0m)0x00xs x my0mm)mmmm)m0m0Yx)xyyy0xs))0s1xxmm0B8Osmx1m1m)%lqXqHB_B/[/ xmzl00000mlY0xZE"gl̃0g?))mymG))YmHYnny0y))yykyYmmq_YHHHX_k_)000xxmymyyyyyyym)ymymym&!zB= x00)yyyyyyyy000000Q0QQyQy00xmx)x)l_ msYBBB)mymmyx00 0 szm0mm))nH_00gY00BY %xHs xx|x)ssxJ%?Ymym)yG_)msm0x)y)ymmBlYs1xl)mymmm)m)0)zBYY1?Ogx yWڙT?00xxYm0y0)?Amxx =W؃1=ssBx1x0z[/JssHH_n|GyyY0ymm/_x0)0000x=8NBBx)8Yxssx?J%?Ymym)m sY)00m)00yy)0m00m0Y8AN*q*%HYm0zBx0x)HNnY[HkHm0mmmJsB_*)mmm0_)YnY0yP/HyGyyQyynN 1" > >x00ymm00m000x xYHXnPyyy0m000mmmmm0 yy0m)G)yyyy lxY0xxl00)HB|XmmmY/z%Ym___TxmAm0mm0y0xDD"hJNzxT Y AAyyGGGG)YY)HkYHkym))Yy)m)m))))G)))Y)YYkHk_Yyy00yyym))ymymm)WΕO/YmB?)00yyyyy)00my QR%G%QQQ0sBm00m)m0xm)YYl)))y0x 0 1 )m0mxHylH)Xm00̋?*kqBG%BHzNOlBs)O|HsY)0m)m)mym)Ymyyyyyyymmm)z s BkyH)mmm)x?Bs%ssym:xxm/_x0)m)m)Ym)y0xmxWWs/xs1mmyH[kHkHH*NX__)yyym))m0m01zsm0yx[= 1xzs88z s)XONs)Yx)0m)m)msA_m0yyyyHm0000x[A%{*{HY)mmBlm0m00xnqkAHmlmmmm)z?Yyy)mmm)YYyymHnX)YG)yyQyyl| 1z""> Y m)y)mm0000 xx0lnXmm0))m)xm0m_m0myHyyyy/lx0mz/y0sA/nA?BH0ymlHH{n_/?/=xAAmm%YY=dDs [/sx1=z [X_)y))%mmY))l*mmm)mm0lG)y0)m0)))))YYHHHy)H_))yyy00mym)m![xzOҭYl)yyyyyyyyyy00 %%M%GQQyYsl0))0)Y)Hk)lPHm)0xx 0 B))ym)k)yHy0|Xm/Bxnqq_Hx)BBs8Ns s[gNg xB[Bxy))))myHm00)yym)0l? zBBl0_m)mmx0l_zx00B=l`:`TTTzxxz)0)mYYmmm000 O`WWs1xm0s0xXmGGPHHnNfXHlGyyy0my?_ Ysx[Bxsy= z1sxxNs s/gNg xB/Bx0G)))//?Ymmmm0y0mGmmG))lXYYl/_H*gGm%)0)B0000m/gHXl%YB000mmysk)))HPBHxx m_)yyyyXXnAP_yymy)yyHfJ > 1"" 10s)0000m)x0xx z[BAnnH0ymmm)m0)m0)l)HHyyyAAA_0 ?0Yl[AP)y)mmmYl kBnin{nq{zBgHngg*B_B%)OzŃxzض xx=z 0[=lmm)Y)y)YY%mY)Hl)l)lmm00yymHHHYHkGHngXkHk_*nn)mm_))GmmBT1000sB$YB00Y))myyyyyyQyy QQ M % R%RmQyBm)x)mmHlHBlHHl*_mH_mx)x1 01Y))0y0mPHP)0m?1)?-/q_n%)Y1B/N=lss/gB%1Yml_lH))))Gy0YBm0y)Y)mYm0_zzzH)GYYYHH)m0))YmBkl?=B[c=O?YYYl)y))YY)mmm0msc`cccOTx xssm0)/mmymGPqHnN5NHyy0))yymYX/B00[z0y[1 zsz1x1N=lsY/gHY1Yml_lH))))zBm)Y))x)m0yyyGGG))m/XlYm_XHki*%)%m%Ymmm)g8?mgH0? H0)Ymm))mykyBks19l)myyyP2nAGGyyyHfJ>ZZ 01BYm)mx0x01x xlnHm)mm)mmm)m)x)xnXqHrym/8XXsxsBsl_lg_y)xmHkqni(fNN??=/[klggN))ml=ؽ1z=խsx$ x _BmY)Y)YY)Y)00Plm)HYmmmmHkyymmlklHlHG_gk)m5ffmx)Yk)mm))Ws 00000x_mm)yyyyyyL0QG %%%QyyYHYmmmmsHBllYYkPyk_m)Y)1 x 0Ym)yyymGyy)YmBA_1_/?B?YHBz88sBgNlsls?sY?lY))Y)Y0y0msl$ymXL)mY))lYmx[YԒ`O`$|)myy0yy00 $sc`ccs0s0x lsHYYk_BmGyGPGqP|NfHmy0myyyyymB)000[sy[zJszJs0B88 BN_sls?sYkY))YHzYx0mxl m)smm0m)m*kyy2aPqqXkPGm)m0gN8sxx0/m s)xm)yYGPyBBlyyG*_HlnGyyy0yY|ŶJ==O00 z1 xx100)mx)xxxx[Y/Xk_Y)mmmmmx)x)mm0Y5f*yy8|gAz00000/AfnkPY)0lnXL\fNl/ m)gXg?x%/˕X[Xgg歨=zxz zBx)x)s)YYyyyk0XX_Alyyy))myyYmm)mm)H*XnnH)?fNf5Xgym)lHY)Y:0)000mx0)BlmyyymymyGG R %QQyQ*Bm)mymmH[s111> z 00x)xy)yyPH_H zsY?lBYYssYBNlz/=8l1xmHY)mxm0B)0x000)y0)xmxB_sssmm0Hk5knxJh[Ox0lHGGyyyyyyyPPmms1cxx)x)xlHBlkH*kmQyGPyykGr\)mHBm?gs*m[$ZJzs 1sNz/g8B1xAYm Ymmxm0̭l)0?_0xB__)k{)yy2*GG**{aGry[/%0z8g% Y0m)m%)YkHGyyYB1_HHHPmyHq_*nn2kGGyy0yNJ==z10x?10 xxmm)mxxx[s_/lmm0mm)))mm)xYf\nayyyyyy0|8Azz )0mmxkHlAm_k)0m5\f5N\fNNLA[̕/?)mmH??g=/JJz䃃sB)Y0m)m)))mmy0)PYl[msB0y0yyyyymymmyy)HngnX?ngAng0YYYAH)))Y xmxxy00)xmxYYmyyyyymmyGQ K RmQQyY)mmymmmmyyyyy)/B1 1 1zsx0x)H)yyyyyyYkH _B?_YBszls[zzl/Bxs/0_l)mm00/[l0[O/_k00llYxmmgg0x[B y0Pqf5|2H00=xm00m)yQrl`cWz xm B0xHHHlkHllyQyQkrqqk*Py)?kmyym01̶mm̨Z11x xBBs/=lB/Bx𭭶/_l)mm00O/llA?/0m)my%qR2qn*rrryyyG s=/HB_syG)YkHGylG%*_HPPmmy0yQ%mmqrkyBlmygϭ[m)0z1sxxYx)mm)xxx B000m0mm)mx))mxm0mB_Hyyy0m Bl/̶mmx0YY_XH00\|558XX=l0yx%X/8?A//[JJ sx[lxzxYxYYm00Ym)HXAl_00A00myGymmmymyyy))myH_H*nXXABsxBBYAYYY ӒW BO m)0myy))mmlm)mymyyyy)mm)yyyy) Q% %%%Q0Qyyyym))mG)mm0yy)yyyyym/1 >"smx0lm)myyyHPB)xJBJz xx/樶sNz敕8|gl0x0mx ̭x0[Bx0y0_lx0[%x0sHmmkH|NN|NfGH1 DB))yyGyyGGl/بx0m GlmmHkkkkkyyQ)yayar2qvryymymyymmm)ys xYx涨BNl敕8Ngl0Ax0mx 8gBsmymym)nrkGHGGarGGPPyH//Bm)mm0mm)G)GyHAA/[?m)myyyy*kqkHyy?lGym[s0Yx)xs[zs11m0m)xx0s/lxx)mxmmxmxmmYs ?BmQ))yy00)Y0z=Y0m0Y)HHkyykB *nNN5XX|Nsy)xx)///̶mz J=g0 0mx0z?//)sBxY YmymYyyPHsllx))H*kGG)mmyyyyym)lAGl5f5f_x Bl_nX̷YYHYBl_AA$Bx1Bmm0))000mmsm)myyym0y0yyyy q% **xGyyyyHHHk)yyy0m)yyyys "xxxm%mmkPyy))mQyHHH?/J?Bxs 0xxY/[zxBx[BymHxmx[01=[Hm0l/$xxAzz10GkH8|XllklGxxEEx0sxsYx0mGGyyyG/z$1 /{X/mYHklHkkkkGmqkQ 2raaGGmmmHHyyyyQy)Hk))m)ym?浽 x m x///zxBx敕[k0mHmmOAA_klmm0m)sH0mm)nlnn/%aPkyy2fXg=0%)mGGGPYGYPyGyy\XP)))0yyHkr Gymyz_xmyyyzxmm))/zs0xx)mxmxx008Yx 1m000mm)mx?syGyysx)Q%) 0x[l0my0yyy)m̷GHHkAOgggXg?Ym)x00%1%m0y 0 ?=lx 00kB*XX[B mmyHkfqXHO[x0zg0fLkl%ymGA GGPH)A))NfffNf̭sY/gAlBHHlH_BxGxm)Gm00yy000Am))yym)lYmmy k{*))0yl?Hmm))YYyyyB>=xYmm0m)klHy)yyyyHH=̶x0)mYYsX/zzB1xxx0B0m0_s0sz=zzsz=|=lxm0xAsO[z[B_lx0Y)Bl[lszl0myy>"UEj=ssY)mPPGGyyyXXH?sxxmxgLqHkkkPyykHQer]2]rryQymyyyyyyyymkkkPmmmy0m_/ = 1 xxxszzB1xxx s x0m0_s szBmYn___l0ymmmg_Ymmm2XlyNf8OxxxH_?%mPPY%YyfXXnyy)x00YyQXkPqnrGa)ymm0Qy0BBxm000m[88[x0xxmmmmx)x0[8Bs0000000xmslzxy**yBAmm)HlYYm)1xs B0m)00m%)0Y88O8m0m)0xYsB0my00 00sl0x0YX/_kBlsxx)m)PmYN5nY_00gsqX\5?HYmmP*qqq_klklglYkf55nqmxY/XXXgXll_lAlss x)y0ymym0k)0mGHHkxmmmQG%l/kxmmmyyymmm))YlYyyym[=J1 s1 mmG*kkG)yyyyyYG xϭsx00mmsss1x z1x)0m0ls0z8zO8 B0mmmBz_ s00BYmmHz_?gO[ xyy"dN0xY0y0y)PPHyyyylAx0Jsx0_f|k*Hmyyryaaa rIav\(22kGGmGGyyyyQnAkH)))yyYmmx0xm0x g? ss1x1 01)0m0ls0z|=P)yY YmmxmBBsxxx0sNBmG)QyyYBmm0X5ԋGGPPPYBaPyykGPXnPyyx00xHyG22 vPram0)̭l?ByyYx0%)glsx xmxmxm0m)X|[z0mmm000m)x lzY0l)mx)m8?lmssxx0yGY))H)y)Y)))))Y zgAHymH)))101%m00m)my)0xmm)x0mi|{qkH0mBxxYxl_)0ymH5gYAO000YYslf5ff?_?k5qk0)A)m)mmymGkH0σ[[m/|g___$[s0xmmky0yymmy00)yAfs)))mmy)mG)YHB Y))yQ))mHl*))HlY0ml8zzs1x0qq*kBHlPyGmGH00B?AxYY)yxmB[zss xxx?z)00sOO[ O_mxՃzzYs00000x)0[88 =z10zZ 00)Élx0mmyyHkkymymm)myyyX)slBsxmkg5n **yy0lkyQrraar2(vGYHyyymynlP)my))xz0x0)mxssssx xxz[)00B OOyPm0mymx_zx BYmYnfg/sG)yy0)xzzx0kNlm%PPPPYPYqPPGql)Yx000ya*22qr*R*Gym%l/N|*GPkG0 x xxmy_Bx0xx0mmmxm00m0)[sx0mxm00xx)xY?*Aqklls0m0)lP_l)mYss X00_H)lymY__Hm))Y1H?zsxm)0YHYYsxYB)mymyyymmx0YHsm0fN*2qkHy000m_XlyH5H_x0s BYfHllHHXX2kH*k0mYYHyyllYHnfXA)Y%x0000NLQmy0m0m0XX5)mm)0m0ymmyHs0myyyymymXG)Hls0yym) [xx)?f52n*H%lHyyy)ymHHH0B?_zs0YY)0Bxx 0x1xxH0YBBT8TBH0mm$ϭsxxx0mxm01=[zz J 00>îdJlH 1mxmmmYGHkkkmyyyyk|B1Y0YgA\N( * Hk***HymlQ RQQQQQ QyyyyyyyyymnHG)y))mx% 00m0xBxx0 x1 H0s B=OTmm))H)00 zml[Al_)?Y[BssY[mQyxm0mmss)mmXPGPGGPHl)yq_mmmYykqyQ2v^varGQ0ll|5qqHBHxxmm)kqgx 0 xx00000000x mxmmmx00mz̋ln5\2k)01xmk_% krGmxzOmXn0ky_)m YsBJg)mmymmylBss s?lxm)m0)mmx0xs mm0 gNHn Hr*Hyy)nkGyYyNgl0Ollnqk H * rrk)yyPryy)m)XgnAlY_%%G/NNGkyyyyynPGllmxxmmm0yyym)msBx)x0)ymmyyN/mYH0mx0)yXX101zzxHfX/|LkHmyGy0Y[B0 xBl0YYxx x1s J%s Ymm_xOO?[z ϶H_x B[AOzH)xmx0s=B sxsxsxz1 y껞d A?Y 0xx1Bxx0H__ymm)YyGkrqNYkxGRrraarr yy_yQrraar rQyym)myyymy))myymxxxx00y)0xYs= 1JmYmm_)[OO?gl0m))m0  sA?YYl0xx1Bkmy[ABYYxm0mkYHPHP))GPGl_ym))))m0_yn*Ha2222ryymq[0iqqnq)lYxmxx0YrHB)xx0[ըsYx01smxm)mxx0̶l H nv*kk%  0x H%rrrPrsxsHYyyPYY)sszYy)mmB x0 mA)0000)x lmm//A_qnnPkNllAAAPy))m|Xg8zl_|*q*klk*k*rkk_HHHyyGkHHyyyX_mXqB)lsBG%%H HrHk HyGlY))x00yyy)))m)YY)Bl0my)myy)HlBBmHkmym)yY)yxYzz0)XN85Gy)ym00BX 0mxx01symx)x0xs敕xzxml )$XOO/8 =B[??[[8zHYyx)01϶x B1%s1 J1x0yym4z_mxxssxx0Pyyyyyyy)XPlzsHyQQrrMQQQPayGyQqrM]M*aQyyy0Qyymmyԋy)))y)xYzY0m0x sϕ zmml $O/XBm)y)kyy 00ll?kHkHYYY_ssxmxY)yy)y)z mx)kHPPHHHHYkGmmmm))mxmH HNGrqr2vqv*QmG/?ly)f]qG)mmm)0yyP*nY0mx0x[XXOmxmmmm0B[8gPHkkkrH*kBBxmy r rBm 0/Y_yyyyGYH1sA)mG)y)lYxx0xxlsyymmmYsxBx0l00xl[Hkk*NfYy_nXPyymfNgBs|l_5-*kk* rklkY_YyyGmyHkyyA8)HA*BY) HYgNLq*l%Gk kHk_PymyylHYHlAs00yy)))m)m00)y0mxmmym))lYm0mm))y)myms)G5XNNf\N0mmy0_lYYxx B_/AsylJ[ [88 z)mBzz=حl[O=zxB[ [J=Hxy)Y0x=sz1=zxxx m00E> xBx0m1x> 0yyyyyyyyyn*GBY)QrQaQQy yGH^]vQQyyy))yyyyym)yy))xHzxHz[ zz88=/xz xmBzz=?zHlHP))Pmm01BY?_lkkHHBY[_m0000mAY0yyYlsx0m0y)Gm)H%YYmy)PyPm)00000/gPQ2*r2qvrQy0_)m5qrm00y)y*)mx)x/ ))YY)YHlsmxm0000msllHYkl**Hrszlm0y krP0 xmyykyyyy)YYsB_m)))mlsxssllmy0mmsm1x0000/ԷrH*rHflY0AnYy0lgBOggBHnq*Hkkkkr k_lGyyHPg/HYHA?lY)YHsf?k klH * H kHmGqnk)_000ymy))G)0Hm0mmymyyyyGm)m))PGm0mmy)m))))BlmBsHP5|NN0))mm00B0̨Y0xzBlB0YkllJs sxz敜88 xz x0BTT歭[[__l8=zmy)x=Ϩs/ =01xx000 û> 1YllH11>>x0yyyyyyyyyyyq0B[lYmyGaQyaaQyQkyPyGarvrQyymyyyyyy*Xm)zB1mms sx1z=8 xzxyBխl0___H)kyYm0Y_mkkGkHklmyl0m0y0ylmy)m 0HPyyHY)%BmGP)y/[̶{l)B8NgXqrarnPrn2*Qym0/l)m5rQGymyGnBYB0y)gl)sm0mmmyyk*kk k_Hl ykrPrP0mxxsBXPyyyPG)Y)YBm)kP)HmBlY l X)yyyy Ysxx1?Ys0mYHkk5NfNgXmy0l8[AX_AqkkkHrHrrkkk_A_)mGY)HGyyyH|HHkl___HYYkX*kkkkqkkk k)mYklXAlml m00HH)ymy%)YlHPl_l0mm)))YYHY_|k0G)PN)yx00)s0̭Ym lH0ymls$A$1x/xs1J?% [=σX/g[lzzO=_YHmGY=Oz1/xxx0 0 0 x 0 x1D"1 00yyyyyyyPyaqGsls))yQQyQyQyryyyH2er^rQQQymym0yGyyy))yX_k)Gm?l0sl0zըYzsxx11JJBB[=ضOYl_kP)yyPmlBmHlyyGH?_ m0m))myyyH0yyymY0x)YPY)YmYH0akHygl8|[?f\qqH22k2qayyQy)l\\ HrGrnSy)ylH%lmmmyHgllsxm0Y)Hkkk* Hrk kYmmAkrH)mxxBXgfYPPyyy0m1lH)YkXqlmYlsz zymm00=BmxX{gHQykHkHHX|?_l_Y)y)y00?x8[?A)kkkkkPkkr rk B%%m?GyPP)yyyyymmgX_lqAHYm_5Lq*kkQ0Gk kk%yyXHmmymPl)m8| Y00yy0my0mym)myy0Hmm)m)YH__A?)ymY)m)fX?qy_HYm%0mYm)mm[[ xslB)0mszBYBBxB?lzg1/Og?z1zzB)y)Y)x1z1O8zzsx0x0x10 mض"> 0 yyyyynm)lg0QyQyQyQ kGyyPH22rr2rQQQyyyyymyyGGyyyy*X)G))_B0Yss% Bxl?z[[zz[=[g_)yyyYAHBX_yyyk_kymYyy)yy00_BYxmmY)))m)xm0)ynn)/)0)%_)m)00)xl_ykrykH*kmGym0?0y\vPRGyyylHBG)myyyP*AYm0kHkHkkkrr*?)mm0yHnkHPkHmm000_ggHG)yrkmYx00NL*qHGmYsz حxlmy0Yx _GxmBq*GQy krH Hy _B)_YJN ?/?mHmq*kHHHkHGkPHrk _)0%/AH)GGGG)yyyyyyyYggA_/Y)YYYYH5nk_mG%G0mmzx Pl0sAzx0m00yyyymm)mB%m0mm)y00m0)mH?kYHYmm)))nyy)mmm0)m)k)m)mm?)0[lY _s)m)yBzJ/?//[J/[zx=[̭ z[ m0_XHB_sYzBBYxY)yYxYx$s ص x0ms > xx)1> 0 )yyy)yyyyyym*vGy)nHyQQaIyQqkyyyGPv2rIeayQyyyy))yyPyyy0m0m)yk%GGG)G)m/lsmB[[T /?//J zx=A z[?_ll_Hmyymyyy_̷lyyGHP%kyyyy__yy)mymyyym0_[sx0x)my)mm)xmyykGm0B0ymyy)QyQy0mm)))mirkPP0Y))myyy)yyllABH_kkHkkrkHr rkrHm)0mmkn kkGmm)00y/ggPPHmNH)0YxlNnk*lmm)xBzY)0BA)mY)mx0Ynqrk*k0Xs0n_y[|[BmmHf2*kykHrrkH*lBBgH)HPyy*|XlXkA))YHB*55qq*ymm ***my00YAzz_s0s[B0m00yyymmm/X_)YH)BHxYlY)8*fl)xym5fNNN5yy)YH ymm)m)m0l0 szsmx))y0B8==̶̶?[=/J[N/zz؃[zm0Y|gO8 xx)y)YYYx1zz xxls1x 11xxsE> 0)yyyyyyyyyy*Gkyy)lHyyQQaaaQykyyyQyyQvorraayyQyyy)myyyyyy0mmy)nkGm)yym)ml/llzO== ̶//J/N/zzՃm)YAyyyy)GlgXAm)y)YH)lkPPyPH)0y)yyH?B0zlBmmm))))))0)0mymyQymm%0mYyyQm)000y)llx)nnPaH PkGyGym0?Ym_myyyGyy0XLXAq*kk l)HHrkkkyY000msYHlBlm)Ymylg8lA?nN5qmHBBm0xx)s0GkB??zB[ssA[[)0YYYYmPHXf\k* *kqk0) As00)Asm08Bs_)m|)mmmHkHr HHY_8|PY0YgfXqlnHYYH?lk/qnXy)mmGl **)x00zzz YBxx涭 mmyyy)yymmmlXA_lAsY/[sx8/H|8|gzm00m8-|n0szBll)m0yY)HX[sx))mm O==[s0 [涭Hg/x1xY|AOz0mlH11x Ϩ Jzxx B=Էl  zsxx 0 0yyky*yyQQaRaaaaaayyyQR2rRGyQQ)YkyyQy0xx zYyL*n{GGHlBmm0lO8===϶Bx0B[涭 XX/0ymm)Y)mmyyym)_g[m)myYH_[yPGX_YmmymyyH_Bz XlH))yy)m0y0Hyyym))Gyrmyy0)))m)ykYy?N{q kyy0))sl0))mGm_)ymmmsXlnnq*G%B rklHHY0Y?HxGsHHmxlsl?Hl/ql%*0mB趭 1m0)m0q?fNsx [ AOlxxm00xYmYNNfffLrkrr*qqG)mYzY_g 0zzs 1B[lB)Xf)y)HPqqHYHlHHkGyG)m0mYY|nBYYHl_*Af)Y)) *{H%H00s[?Bsl xxsz mmyyy)))mHgXAlYOX[?mB/?HH||O8OABHm-_yms/)ymmy0m00x_m0xsxsz88 s0x)xs/̭sxA/ggϨ x)H_nAAXX)Xҭ?BYs0mNX/?s1zZ1xmB) 88=J[$zsyyyHaGyyQQorraaayyyy2kQ22MrorayyGyyykmyyyyxlsx_lmff\f55kYGm001O88m01xs[̭Yx[/ggϨmmmmm0))mmmyy))0Xmxm0YYHmYyP_00ymy)yyymXz?AkqqkP)))ym0mmyyyyyyyYym*mYlm00G)G/lmgNX5NqqqH*rGyyyyllHH0))ym0Y?m)0%HynfnqX%mm0HrPkkkHl?lH/B0Bx0Y))0yyyy0Y0 ?YmGlGm)sB xxBlXBksYs$[sOY 0x00 )m_)n5NNN*mGkkHmHxmBs0mlsm=sBN/A[N|%0mmGHkPkgsm))_Alm)m)mmYllBmN8fH?_YHl/qY_N)YmyGYk*? )B00s[z===̶Jzz1 xs/z l)y0kq)Y?[XlsH/lxxYy)88 G%BsBJOlmY0)m00Ymmylzss0 00 1 x[sm s[ 0mx0mz/X8NN8BxxmqyNmyY/gg[sBJ 111xx00[=JJ[[lyyyyyyyyyynPyPGQyao,rayyyyQQa2]vrryQQyGGmyyy1s? l0f5fLf5ilHBHYB0sx 1m sAB0xm0xlg[X8NN8B)mm)))m)x00 ))xmmx0Pymm0my)yl/l/k**HY)Y))00)HHPy_yyy))ml_qyqGlm))HA_YXNX{5nq*PqHkkPy_?z00)lyY0mHm)))yyHny)0PrqH?AlBx00BYxPxzlxmHsm)xBmm[[s?yy%0))sYgO zxm0m)00gfyGGGHHmkkm)0l0l0l /[_g|fYmm)ngXlx))HABmmlllBmm/8N/_sB)l__k_XXklNY0mmHGYmx[[x1= ssxx0 )A|fN)H)yHA/?/z%Y_BYG)xGk0[T_)mxm0km00m0Y?s1Bz1xxm00xxJJsxz1xxBϭ000Y|f8gB11ByPyB0xYxy/[1s1xx00 zzx1"s1sYx)xmym))0)GyX2HQkQyarooayQyya22vvoroQy0yyP? z0555555f5XqH_XAm00B1zz1mzzm000g|/fg8AHm)mmm)mmyy0m)x0m)mm)m))mPyy0mxy)yyH)myPXmqqq* mmYyyHYmm0kmGPkQkGmmmyG{fq*qG2n rHrr*Ag/_0ymkyxm0xxm)H0y0mm)XNXNGm)mxkkHHgfYOlxxxx0z/ )m)m)mmx mmmm0xHmm)y=?YBBx[l[ x)x0xm0lmmAg?m0xmlAA s0xx[x00B0gAYx [$ /X|)))mm?ng//[BBYm0sBmmx)xmxY s0[A8N8g/_[YH_*q{[Gl)[[[l)m*k%m) mm xJ ss1sx0x0z 10ymqNgm)YH)Y[/ggXXA_z/lymH/sY))%%H0sglxm0l/))0BBBszm0xY 0 Z1DJJB 8x0m00 N8z1kykyyHz?=xx)mH̭ mmx0 x 1x0)yyx0 x000 0 s mmy)Hsm))yyQn nPGo,,ayyQPI]2vKere aQQaH*Yyyly\X5fq25ik|ˋ/Hmlz1 BOx0m0B8gN8zl_))mm)))xmmyxxY)mYm0yxmGykaymYmmy)ylGyHPlYX2qHGmYgXmnYYg))yGQl_0HnLnX\2Hn{q rrHk2AX?m)G0y0x)m0x00mHXg||mm)HPYl[8[xxxxx0myyYlmxxmyygl?xy)YB xmY0xszxxxx)x))mxmByA8zmmmmmYsl_[Ymxxm=s0xBm[ ss)B$sO_B00m0mfNX϶ Yx?msHx ABz0 [BNNX[/llX_s/Yzsy)mlGm)Yxl 0 ==zs1x 0̭m0m)ml_q_GYYlglgXlx=gyYgϨxkBHYYB/zx=gl [00)my)%s%zxx / 00 s"JJsx0zO[s0)z8gBYBBzxmByNnBmsB0HA 0000001sx0x0x00000[)xmxyyyY)yyyyraPnHyar,,,IaIyyyyQ2r2^vvvqraynkyyym_g[Yyyfq*k*fn XXY_)mJsx0z8[s0)08g YBz)m0)mxmm)mm000myHms)YG)ykr0)msxy)0my0G\\ov* ))l8gAnPkym)[B 0mmPkk yGx/[Bm5\fnn{ oX{HX_ylsxsxmxyyml?/B?)mm))lg[?BA?ly0B[s0B_A)0s00)HH)yHyHg8Ylqmy0mY [Bm00Yxxmx))x)xBBA0xxxxl[[sss)Y0[Ox0xAxmxm0mgm)xσsxz8s00m0m s0[m0϶0sxY Ym[yB8?s_BBBqn(n_?Yss1s0mk?YmY 1z =J10 0s=s0y))m0m)YYHxO[AgnkyB= xsYHxYy_O=0O[[$xxmmxmm)%00xmxsB 1x"D9zz YsmYx0lBq**l%1xPyyykNk sJ 0ymyymymmzszY0H zxxy0y0mzmmm0000msyyyynrXyyyQ,,Iay*M22]vKKrQayay*XnHyyQykyrfiA*q* qnqqX)y0zz10=smY0lkqB%))00xm0m)yym))m)mmy)mYBl쁳ay)))m)) xm)y25H)H55r*G)lNl*rQmOs)x)mmyPqqqq*qPG0myr5(N5ffkH2*K*2n_nkG0 mx Hyym/?_l[AY_0)mymf8lB)sA0)mm0Y01gs0x gx0Y)Hm0lHmmHkPlsBs)mmmmsH s0m0m0xxmx)0mxmxB?0YOm0mxxzzsxxY0z[s[X/ xYY)sgOg00)0 OHYm0000mlz/z 1x00=mxzYxmzsx0s)H/Axy0H?n2lsBss xyB_Y)m)B[ s1s1x 0zOx0ym))))%)GYmmYsl_nXqH) _m?Jx)0)Y)%xY)|=B10mssx /smxmmm)%%B00x000z0mx1" JxxYxB0[_?mszxm)yy_g ssx0)Gyyym00s000)m*=J JY00000Y0)mym00x)0)yyy2nqmmGae,,eyyGQQar]2vvraQmyyyyyn2*k * *n){kYxJzJBx /Y 0[̭??msB̷))m)))yymnXky))) sHGy)mBszx)ymyyQy%qY*qakka*PQGxx0A*Hqq*rryyqPQGa5(5Nf{Qv*q(q̋k-kyml0m0yyyy?Yz//sm0yxglYxYlXN?)x00[z0xxsY)m)BBYYx)0Pk*yQyQyyGGllYx0Ym0m0mm0mmxm)0lXlHH0m Y)YxY|l[[[AB1 sY=ggBA[?BY[s))Gmm̭sm[s)Y00/0m) m)mY )xmYB/?xx)*S{*Pks z)Gyq)%)Y0 BxBxx0 Ys1x0z/_0)G))B0zgg[_Bllkgq_Hx[?xx[z)?A)mmmx))%T szxm0Y01z0x0xBB-%QmmQl x"00YYx0sJ1xz1x[g/00zHm0xYyr\l0  m)myy)xsm0myk*k /?x00000yyyrkGyyQyyyynrnGmyzk,,ee,QyQyyrGQr2v߳ryyyryyQyyQyyyH{*k qHqnrf8*0%01  0sY_k/gym_XAAAY)m)y/fgnAqHys 0)yyyy01z0)))yyQyHHGq5qH**Sq)%HPa2QnxxxYml*HPaPqPkGQ PGGGG55\5LX*aGv**]*qL*{n˷)%l)mslxY0_?zl/lY0ym0)Bs)H?lgnB0mY00xYmsO ̭sz xssYxm00l)mmmHyyyyyyyyGBHx x0m)xm00)m00B[mx|NNnkBBB[gH))_[_z Xح xx Xg/zxYB))myllYzxm0l/ly00m0 0xY)_[zx)_qAnqHsBzBBm00mkGmmms[0ŭsx1zBx0[0)mYH_lB__k)00?[A_?Xgl%slx[lB)))mx))m0 zzY)s x0s붨m00s[?-H GG)xxz"1x01l_H0z/X888?x)s0mYy) Pky)lsBm0mGyyy))xs̭Ym0)yPYsBB 0000)y)Gnqq_yyyPnPyGl)?BRww,oQyQrQyev2vaaQyayQPyQQyQyyyyn2{q*** *qkqX*X0x xzs00YBlHAAgOO?m))YX/ABHmymmHA8̷kkH)s0mYYPyymmz/xm))HGk*XmyGH2)QkPyyB)m))QyQayPyyyQyQ55\rrv 22QykGkyyYYyz zBxHY)mmys/lYm*?k0)mmmYmO[mz Bm)Y0)ll)mmymqkyyyyyyymm0Y000y)00mYm%/|\Xkkl_z?BYYY%?/l[[[xx1 $z$XA/X sY)00/g_xAlxBx lGmy0YAl)mBY)PY_B)xXnnninSrHss)m))Ykk)YYY0x0s[ب1szz1x Bxmxxlx0m0m00YY_Y)m/l/gAAkHYxBYzO?_BYxmmm%sT[s/Ylx zzYxx)mA%H***%mmx1"1z xx 0sJ1m[ggXOBYm)))Grr)m)_s x0)myyyyyyymmH[lH)myyHHml0000ymPGyqnq*Gyyy)ynn)y0i_eweryyaya22SSGmmQ0QmQyyayyyyyyymnn{**kk*{k\lkqmGm%?Y0))Bl_lXXX8HHH)[lHGymym?|Ag[_s)))yyyyyml?HxYYHyyPG)G)yqqrrqlGyqksYxmmmyQPk**akkykPGP Pyrf5fffqGakraqeaqGPyPmkYl_H)Hm0??x)))zsmBXkyyym))Xgs szlxm))YY/_))mm)mmqHyQyyyyyx00Ymm00)HlBqHB_/lYHs%0lOض$s z=[=ggggggYsYYm0_8ϭYxmY[s)xBsBzffXABlgAYxmYxkllmx)XniHBBzzsBlBG%l_%))00Yz涭zz zz1m0x0x_B0mm00mm0YmAg/_AX/ԋkkYY_BB8/Bs)0)x%m%0Y z xxm0B0mkkLBGkk1"JJ1xxxY00xJ10 z/XX///_BB)YY raGH%xzlYm)yyyyyyHBl)yGPPH)m)xBBx0)0yGPGq**r*kQmGyy*Hnmxo,eeoaayQyQQ((Qmm0xQyyyyyyyyyyyyYlq{q*k HqH2fqk_ԋ)mxms00BY_k_ll̶[_BHm)0xBsYxmmm))m//m/g/AH0y0yy%*qnq8lB)yy*PPQy)ykky nkGqnyA)maPrPQPyngBkmGkr* GQGH)00)PY_)m0g[/Hm)qyB0mB/lyHHHgg[ mm))YHH)_ly0m0mxmm0AmyyyyymmHm00smm0G0BGHnXl%YH%HHx0x=z1x T؃BOgggOO))YsY)Yn_XO8Y))x)mmx_̃[|NNX/)0))YGl))))fSqlHBxxllmmmH)xmx0mmmmm Bl_ m00000H˷yy0gAgXXHkkHPklYm)|z1YxxmY0YY[/=1xsBx0mY8fXX)%H%xxZzxxmx x zzYAgX_YHHHBk r(rGXXlGgs0zB0)y0_kmykH)l0m0m00Qy22k2PyYmHqqPG2qva,;,oGQyQayyyyQyrrl%xmmxyyyyGyyyyyyyyyyy)H{qqqkkr*Hq5ik_))yy))))YYPYHAXXYlY)00x[x)m)mHYY 000g)0mA[H0myn-q?mmm0nHr*HyyGH*nkyyHY)mmyyyykgkm)my PQQyyY0ymPy)/)m0m0z[[Almm{l%s)0x//sYyyXgO[AOg_x)YYYl0mHl)xm0%*yyyyyyQ0mm0_lBYYmQ0m_8ff58N8=H)H%)00mJ[11xT[s8|8ggAl))l_YmNO))x)sms|=lzXAYym))x))Pmm))xkg{q?**BHBzlB)m%lmH/[[=[ x0mm0m)Bg涶sYY%l_Y8X|gglHHHHYl)0Als1mx%/?lx/zO=sxxz[Bx0mm_8ff|XGyH{kmx%JDJ00000xs1zsxlAgOOggYYYYHkkkaqѲa_g_ym̕ m?lmm))yymm0)0x)m0yyQ2q222kyYHqGqre,aQQyyQQaPaQ%l0x0yyyBnqn{qq* *H*5*k*k_Xnm)HYPlkHlxlzs0lYx00XBXB)sAm00BN/0)00mlm)0mByX(*)P*yPHkHHGyymyyyyyymyyX_m))yyyymm0Gyyq_)ymmxX//B*_)mHl)0mlkyRaHggTO[sx)m)YYYl0YY/_lBlBm_HymyyymkqBm)0yYYlBYxmxm%BH0N|NlxYm?0)msxxz1xx =T m|ABxxmB[)s__|xmmYYBYlB0B_gAYmmm))llkl)mylXABHs**HHBB[%)xm)B/[[$smmmmy0Y?mx_lY%s_mymy_NggggGlHYmmxO?xm0HN0??X8B0xmY//lm))AN888%)x_BxJJJDx0m000 zzl0XO|OAsm)YkY \2nG[G8 l0yym0))))mmPmY)y)m0yyyYGyQnq2yn8|lHy*yQr e,eraQQQPyy)_m)myyyn{qqqq* kN5ikGynPnXnymP_*l)0)l0?Ymmx0AxmgN_)l YxxX8smm0x)x0sX_G{\̋Hqym*GHyyPGyyyyy)Hyyyyyyyyyyyyy Y)0yyyyym))m0mHP_kQY_m)m0s/lygHmmm/Hm_kGQL| z= )x)mYH)HBYg?lBY%zlBy)/my)mHnYmym0)0G%G yx%Y0m?HX|YxYmBBms[0xxx[Ox0A1xmlAm00 YxYYYxl 000))g_m)m[YlX[)yknn0YgxHqlY)mx8/xYxmx/g[ s 11=[xmmmyYOs0000000Gmm0H|NNN_H))HGGYYlkmxmzmxYYmA_xml=?0x0 00B[?A)mNYm%m%Y001 ZJD0 m000 0 z/mAgl/TsHx)YHYQnq__An)O?0$Onm00Yxmm)y*H_llY)0mGyq2*nry)X8gmlPqQarMeo2rayy0yyyYBmx)myyyyyyyyyyyyyyyyyHXq{qP*nkL5NˋHP*nXuXn0yAgfXXlmmmY)0)m H)?g)YsY00s/m))xmxBlm%2\{Ry X*my)*HnyyPA)Gymyy0yyyyyyy))yGG)mymymyy))mmmyyyyy_Y00*kk)my)glsBzG*ksxll)%%GN8[s00z1mmmx)xYlsgB)Y%)Gmm_/)0))Pk)Y0YH)mxm00000m%0xm0x%YzH0z[Yx)0m_0H0_Yxm00my$z[l0g l xBBsx xYmYs sx01[l) /XBm)m00lgY00Yl z0lg̷lHgOz0xxkH_0YmYx))_XBx譶s01x10m)yHBxxyQyyy))yyHAXNNY)mB/smm0YY)00m))x0000AsAm0)m)%YB00%s1ml?Bx00B0 mm0 J[B)g[ssx xzsB0m)xmGknHY[1zA?xx z_ x s1mx0myyyyHlm0ym)Y2nv2kq8N?kq\yGGam QQyyyyyyyyyyy0yyyyymBm0yyyyy)yyyyymyyyyyyyHYng*H2nk kkkqN5N*lPXnXnnYYXXkXA?YmmnY0mx)H)Y[sx00gYm)xmxBX_yGaGRrIGalymYGiGyGyynyYAPH)myHPkPyyPYm0mmyyyyyYH xmykYPkk))mmYYmmmszGHy%s8%%G%k kXN 0sBx)xxYYYsB0BXBsYBkHYHYl_YsY)x))YHHG)GymBlYy)0mmmmxGxxGxGxmxY0B0mlBB1xxx000Bms0Bg )x)00y0xxm=00zsY1Yxss x YmsY0Ys00[xmYY)mx[m0[l[xk_gXO|[sYYYk|0mmxYY0ANgAxsبYs0xs)m)y0_0yyyymymmmGmmyymHXmY)kY0BB)0000m)YY)000BH|l[0ml_Bm_mmzBYxB88Y001mxmmmm 0z1Y)g8lحxx s mx)l)yHqY/s$sبsx0 B/gBBsm 0m0ymyyP_?/As00mmHYm))yQGʡnq2G_gNm0_kn5yQknryyQyyy0sBmmyyyyYyyyyH_qa*nnq2Hk5*{nkXY0)PHknqmB˕H0H)m0)Y_mm[zxx_XXXx0)lm0m0)L? narra)y%GGyyAy/HYGyy?)ya2yyyyyyyQyyayyyyyyyyyQyyyyyyGYG)mm *PyyyyG)_8BHYmGG%s%϶lxx0{{0msYlx0Yx)0xm slY[8_%lB__*HHkY0000_kGG0H?HBYlx0Yx%xxxxmxx%0 m0B0YYx)m00)0x_YkGGy m[xO1BBxxYsBB1xlsmYx0sYx0lsY)0)Y)mx Ax0/?A?0mlHnX_sxx)YkYyy)mmm%)__))xzx x)mgByyQyyQyQ)Y)mP)ym)0l))x1xmxXԷ)mmx)0)A m)y)YHmH0Y%mB 1xmxA[000Yz xmm0y0m00xmmB/z1xsx00)x)y))_zs1TϨmx 01=Jsmx0))yym0?Ys00x0)mqkyG2XqkkgNNXymGy)mm0mYmyyyQyyymy)mY_/_)0ymy)GHmm))mmyyQynnkk nԋq **n5N8nHqXXXXYyYkkg*HN0yymY8fgY$sYx_//)0000m̋kfL22] m)HmlPkyAmAlY%y%XkyrayyayQyyQyyQQyyGmmm)qyky)y%8))msYGG%%%Yz/B 00 L)y)YBmxx)xm/sl/[l/lB_*_*kYY/XlmxHn_HklH?YYYlzm00x%xG0mx10sxxsm0)xmm 0)A_)YH[lY0O/ss xz Bs)s)0x)msYY)00xslY0B8x%myYyxss1xxxYH)))m%00mm)Yl)[x[1Ym0yyyXXlyy)00mm)Y)mYmm)s0z xYmmgNH)mmxm0)NXgl0Ym)GGGHlHyx%x sxxxxxxHm00Y 0xmm0mxxmmmlAB붨s 1s1ym0)ym0zبx[sxx= gB00xx z10mmm)yyymm)msx0xx0m0m0l*lHH)ynX0//myP0)yms_)GPPaaPyyymmmyyyymyyyrfff*qq*2q2n*kPHf5{XXXy)qkH?HmyXYNN_lHA[TJlYxHXnXXggsxx0mmX 5X88m%m0HHm)HmmnXPqQlY_))y?A)yyQF3FFFFFFFFFFVFFFFF33FFFF3pbbbbbbߥ߹rQ\ *)H0m000B0XA*BG0m0G%%Gx%)l00m%)HYH)Ym0m_8/l?8O8|gH_***__kHH*Xglg*k* qBlB_lmm00m0)m%%Gx00mlB0Y 0)_mmmxmxmY)0))Y00Ysm|[mx xssxsB)))/k0llY0mxYYY00[mH)?N%mY))ymG)Yxl)Ymm0mmYlHHlXA)0lls Bxmmm)m_yyyyyQyQm)yYyyy))mHsYm0s)xmN)mmx0mm)5fgXxY0mGG%k mymG))xzsxx0ml )YO mm)m0)yy))mmg[_[sx mxmx0x))0)xY$s 0mYsx m?smm0ymGmm0mm))) s1x00xYY))x)%)llqgX)lny2P5vymayyyyyyyyyyyyyQyQQQyQyQ5fqqqq***kq Gff5NLfXnXXyyqHymX)H8Ngg0[ )lgXXXg80YG\*{%GG)myyX_)))*kqPy?AlHmyHymyQ3VV<< x0xm0)m0m)%mmGP)YHHY)gB05y)lmxBm0xsYYx)))y)l)G0{B)B_m))y)YYYsYsYxm)mm)0n_)m)mQ2 752ppN\vpp2|p5vfKv5257 ,m)y)YG)mmHkPg|=J q*kkHyyyyg/<QYBB11ssYkH_* QmH)fgHynqH%mG)mX5N)l) nnX/Bm%)G*n**V\f55\VV<\\\\\V\\ < \f f\5V\\\Vf55\\7bK2m*kyyk-nk0)_0)0)Ymmm)BYm00GGGyy0y)SnGQAHmYy)gl-k_N{GB%YYBmxss z[[0xY0)%l sxssgAHyGm)YmxB0mmmx0 x_xx0s0O?H/AzY0 lYlXN|fXnyHAnll_Bymm)BH))fkYHXBOAz敭)YH8N8LqX\5****qlz̭s)0NkmmqymHk**Yym0yPYYHll*_lB%_8[Yx0mmx0xx1Y mskYyH2G0mz[ϭsHxHm)0000ymmH 0xz1 >>> 0x0mm0mkPGmx)mYPP0_l mkX2yYm YBB Y) 000myGmGyH?0Y0s)mmPYPYH)gmx00BHYmk*Ba\p25p2eNʪvvf52vef,ymGy)Y))Yl|zHv(n?{Gk H Qyy2rv3FV01Y00xm0Y8N5rrm0)GqnLBxGmGN_0nH?n{l%/[HlYxkHGqrQepp..FFFFbFFF(FF333333FFFFFFFVV<>>>>x00xymmmmq00mm0mk?PymBk/?%0ssH _0AAkHXXky0G)l{BB0kmm_?smmN))mm0mAXnlg?mQ V\f55 V <\\ \\\5\ < \f55\\ >">>11x0ymm0%l*qXN01_Yyml/[[_HHl)mxsYlmBgNN|k)GBk_0l0?HXX?lN0mm00lg)Ymar F7777777737FF7VVF7<<<< 00x8Nm)?lsm00Ylmx?zBy_n\i*xBx)x))PHGP)myy0YYHllH0mm)ymy)llYx zxm0Yxsx1x0000000000xlx1[1 xmm0m0))mmmYHH)ll))m0mOz1Yے$T`_ϙ_ANf*xmyq\ *N55nii%mq_q*qq?l?s_0y0yyyymHJZJ>>"zs))yyy)m%0Bl[Al%x%Hs%x x11l )YHHkH%)Hq*All/Am0mmY=?_XԶxxmsxAHkkH ly)yky)ym{GB??O0y0Y z91BBx00zg?_NNfnqYY/̶zzBx0g8?BBY_glyy0mxG)kYYHkH)yy)GGHHHHHHklPyyyGHHHkkl_l0̕YAX̷Ym0ss?B_%mmHkHHBl)m))yymmmy)Hg_lY%0000Q*lH/YBg8AymkŨ1[[s0x)0)X{L_/sxx)m0x)m)*y)_xzYYk_HH*k))/0xB10 lyy)ym0mlx0xlYx0mymBmxlBsYmQk*mky0 %)myYXAsA/)0BBYxsYmxx1"1 Y0x0 )yH/xmxkgX_)B?lx0qi{*qHBsxmmPyyy)m))))l_Hllmmmm)m)HHk)00zx0Gy xxx00000mx0sx0m x1xm00Yxm0ym))ymmlll0)myHJs1BgOT$TϭOg$$ 1 s8%y*\n*fXffn{Hg̭8/inS{ Y0H)0yyylD >9 >10myyyy00 xxlBmYAl zB)0x%Ym0slB϶1Zzzsxmymy0m0)ymXX/mYH)y ?XNNnB 1zkHHHmGmY%BB)B0J̐sY Ny0)zz1sx /f\nkqHsY00m0mG%m0xlYmyxxGHslBBBBBmm)))YHHHkABBx0x))GPHkBkl?__0gs[=gYm0ls0lGmkHBl?Hmm))mmYPH)_G?mm0ymx//11%gg_m[B JJ/[x)yN55\55kxYYxm00)lH_kH)0Bm0sxlnHԶz01sx0xgmmy)y0)yy0mx%lBk_0 m1m0Y))s0Ysx sx0xmkq*kHH0BBY)_5g%g8A_0Y?sBsxx"DD 10 z0B)xmx[x _sX{HBl?sxm*]]]GYJBx0x))HGyyPyyym)YYs_?H_?)))YYYsH_*ms/ x00k x0000000000xmmm0)Ym)xsx0mmYy)H)yy0m)G)yBlm)m_JB1xBӒ`T$WTTOO $$Y*|NYxxmGHr*X{f5\fS***GgnNNNNf5Nq%x mmyQBB>">>Gr m0 01lBmY)00x000000l_ss00mY1 )xmm)000)m0yy0yHHHYl)l)myy00BgXBs) lHHkBkHH)YPPmlOl0yg J/ssYgmm)JJŭ [mxHL((\gsx)x)mymYmxA0m 0xxmx)PHlByX lmmmy0Hl0mxmym%m))ymYYm0x?s[=[[X[HllYlYmYHGxm0m000mmm)m)Y)yx000y)ym0s/sB kymy s00 lA xG\\̋xxB%)mYq*k0m) [/lnk*XB s1xxxAyy)y0YyH_0B/00m1B_x0y)))y0xH lB 0y 2aGQHABy[YyX|[BBYx/sl x0x9"1xsxAs[xxxy[lx mH)f\?xxmk**v*lBx0mB_ll_kykA_P)mYHlH)YHY))sYYH)y0B sx)myy0x1xx00000000xxx0lm)m% zxmxm)_m)l))))PmyGyyHl0yY)mmzxxYڀ`` O`gTs $Yk)x%rQ 2f\\\f(Sq{{GL5-kY))yyG>E9>" iXnGQ0x szsm)x00000000xxmx Y0xx0x1 mxmsm)0y)myyy00_ll)yyxxym)Hs l1lkHmmHm)BX0xz0/GBB1gOyy0m x [?m)mmv55\ffNgHX%xmmyk_H*kH)ykm0m0)YH?_sx[[lH0ymxmH)00x)yym)))Ym)yyyHnnnnXAl[Ymyr0ymxYy)00GmQGGQ0mG)0ymyHsxxlByyy)xxm0x[m0y)Ym)BX/-GBm0ynXfX)8_****qq_mxYsz1 Byyymm00Y0N0z88800/lx) AXx)mmYsszxmmmyryymyAPQY000Yg|OgBsBH%Y00s1 x1xب)yy 01x0xssx5{*BYx)*qqv***m0m00AglByyyfȺ**kyy)H0HBB Y00y0m0ll xmx00 1J0x00y000xx01Y)?HQG)lk)xmy_)m)000)Js0xm0)0mm0yGm0mx`ӑ6O[նA$A$ xHy mBBxmy0%GH*f5˪˲2]qx?l[Xl_̋G*)0mxsy000E>> 2k%m0m0z11xxm00000000 x x0)m)1Hm)0_)mm )mG)m0m)mYl?zxs Ym00yyyyABl/ymm0mlBx008/BBs_ymms%m 0z ==_Bm))r{\XlX)BGmmy%{n{XnHqmxBA0my0)_ms000mm0m)_zzH1l)GYmm)Yl)y)Hl*AlHmGr ))lkm0mmmm0ym0x%P**HABm0mmY)0mx1gyyllHYxmx00y)))YGmm_˕gf__kykLLN_myyY_BiXn*qHBHBxs/YyP)0m0ϭ001O?xxHY//O8YBB11Ym))ymkX)Hl)l8N|mYYB0)xx)lsx 1J0=mmm01BssYzYY\\{q_[Ym0**]kn0)m)0ygyyyXXnXq*k|Lllzlm0mY)0YO?xmxxsx1m1x1 Z10m0mx0mxxsY0sXkYGP)0yyYkym)mmmm0x 0mxxY0x0Pymmm ``T`OA[g  1[)mQyYkxxm)myki\LN5fN]r]]q{*0O?gX_ll000BB0y 1">>J2nv%00001x1J x)x0 0000 x 1x)m)BB)mmxl%)_x y0mm0Hlmmm)mxx1sxYx00XXByyHl??*)1zXHyyymYxxm0xJsm00my)yG//gA)?_0mmBn/iffX0)g_y0)m lm0ml/A_[g/Al0mGmYHmkH)ymH__HYHqHyY/A/G {nHX/mmmmYYBx000)y qX_YlYymxmm0mx0m0ymy_lYYx)xmmmy)yPH)BgN/lmykkymGHmyf5Nnyy)HnNf]qvʷHHmBsBsx)yy0m0 80x[l0l=?QmY?g[A0HHYzBxzlxmmYmyyyB*m)m0?gYBBx0l0011 xBg?y00m? xlgAH_lY%k]*PG)mm0/ggYyXY)lk*HBH)Ng?̨YmlHmYxgxY1JDz m)Y00xm00%N-ky|nx0Blz)Y)l)G)ym0 T6``TTg B)yy*lmm)%)y*5Nf\NNff2q]22HfX88/l%YHHG)XYx0B/ylxx0myxjEddE>00mG%000 xsBBJ xx0000m0mxx?ym))xllmymm0my)_xs)mmmms)0mmm)m0y)x0szx0XN[YYmXg8NNlYm0 myy0)llsYYx s0x0ym)mPG)8g8_H XXN*ymH0mmyy0)m0BsHl)ymB)001?_mymy)HY)mmmH_k̷)Y_Xgg̭[gGGX*|_yymx0mx?/BBYHBHl nBY?mG)Yxxx0ylYmxYBB)0y)mklHG0x%sl)0mm0)l)A_%mNNyl))kXXNfq*/_BssY0mHy08=mBxz/xH?zxymHl)HYlBs Y)yHHy)H)_Hss?l00lsBB1x s B)my%Bk0xsxzlzNfnlHHl**K*{Gf?Y)Xy)y_XnmqHmYm8/[0mAlA?0OsBsxx)x)YZ"Jx)0xx00xmNX)_lYYyymXHyl[sxmx/[Bx0sml_)y)kBm0l ``OgO kyHm)my)myy55555552*vqvnmnfggYm%Hm_gx0PXB0m0yy0 @@dEĻ 00m0 0zJ[[1m0mxm0mx1xsXBH)%ymy%HyyY0mmm0)0m/sx)))mmyH0ym0xz[H)ym)NBxmmLX)mmmm x))llY)0mm0y)yYH%mxl8Xl)kmkmy/8|/H)y)0))Gm00_y)00Y0mmmmY/̷m0m0x/l)mymmmXn_Y)_88myyGmH-Xgk)lm00yms0J_GlmQl)0lY))x)00xx?lmykHsy0)Yxm0myykY1slx0mm0my_YGmml_my)Hk*|Nnfflml[)0lBkms8000000)%_*%{|gA8X0HY?0_Y)0yymyyyH_m0A88g?ssxs?BY0s sz11[mr R HxxBYm) lB0x[Xf\f\kHlk Kkm_ _AXAx0y0H??lGq_{lm0l8NNm)B8N/Ys[sY1x)BYDJDx0 xxx00m00{N8Xs%klkmmkyyP//zx0))yOXAsmm0llslsN`OۀOO` _yrG0yyyy5NNf?Yk*qq{2yHf|f|BxGBmg8Nl[0my0y94ddEE1 0000 1[Jz 0x00mx)xG85k%mH%)?|qyyAxm lsYxB)YxByPmm J?Hy8HNXH_**_?GYmxxxm?*0ky0lHmHBYmyy))klH)0sYBz0myGyYlm)N0yBy0Gymmy)))m)0ms)H)m)sy0y0mlXkm0m)/mmxNfGmGGXfnGmy)_nlm))xm1 ))kkYGm)_)mYm0x0xgl)Ym_Yy0xm000mmGYHYm0xxz)xmmm)0YPH0m))0m)mXN8i k*q]\XH)0000_l%Hgz|O8///lHlL yii[|_mmB)mA x1[l lYYPPyy)m)yy__yX8fNNlYx1/x0BsxJ11x1[[Y)Hr ?%%Y Bm1Xf5N%mm amy0O gOAyym)slHBH%kHm0ml[?[TsYlYYm0mBsxxYklh9xxx000mglmYH?_?YXl)x /ll y)O?lHYYՑ҄`OO` $[HyGHH)m)yyyyNiHy)Hqq yyq|/_s%B?H?gBNYmNmm0yym>4E 000 0 >9ϕ81Js xm0ymx000%|ik% YsyyyXXkyAO[xm[ _)ḽ)ykH0lBH)YlyyylgyX*q{q*{GYmm0xxB)GykmymYY)ymyHks)sl0x0y*Y)))mm)l_)HH)yYy0000_Bmm)mmm0Y?00llmm0m )0mmxm00mlmm0Blmmmmm)xYlmym_XgXBmym))x000H_HY)Ya)r)0_0YYmmxmxYxYY00mkyy))GkY%%Bm))my))HB)m)YY))0mH|lmmG **|fXlYmm00Yk/HO[0g8=g8NN//))YkXG8g|/XsmmsHx[xm00YH0lGYyyk_008/B10xJs//)xx Bx1zs$__k mxxHHlTzgf55iHml_Hm0A8 l0yym_zB̶k{S/BYB0ml[?/ zNNB_xm)mm00mxxYBl JD[s00000Gx)Bl %__00)ykzslB?AYmmyl sszz%HYBxAO``s s[ շHqHHHyy)yy_|B0%B%0Gm0%kX=[lBB0ANY)8xmyyxΗ" 000 x1zz8gxDJx0xm))m0){N/H/%kklmmHyPl[[ Y[z)xm00[̭Y))yk_HlXl[)ymY/?s*NyyGqLLLfG)mxmxx_myPkYym)my)HHYm)xxm0x)mGGkHH)mmm)mmm)myYy0))mym00y0B0m00)0mm00mHB_?A_mmmxm)m0m0x)mxmlBm?l0YYmYm lg|Hy)mmm)yY_mxmx0mXkHHYHPHH)HY)GlY)m0mY0%xm)YYGyymyyH)0ym))ymmy_X_Y%%xmx)m)m)Ym*/H%GGGaGH*gYm)_X̋)=x0z8888mY{NX|/8XY?km0YsH)0xmllG)G)HlHYGm)GHHH_lm?Nsss x00xgB0ll z xm00Gxl%%Y O\\BxAyml)[ҭ $AҶxyym)B?8%H|Nn/BB)_[z0 s[z000)m)l)0mYxxHB hJ=l000)0B/n̕{?k0%)H_[01BBz[))yPHHYxx0 %Bgl 8:`O$s $0[AA**XLBymHyylNl11B_?H00m[[BY)%ym)N/HB8NmyPx0 ED 0m000 1 80l 1)mx00mmmXglm%YqYXG)z[[BB_0m0OOgHYHy)Pmml/[yNka\5ffGm0YmY̕By)y0yG)yyH_y))YBG0mmGG*klkym)Y)0mk?Y0Hm)m00mm00x0xlxxmm)m0mmmxmBH/Y0m)xmm0mxmx0mH?_)mB?)Hm)lBB0xHBYmmm))m00))y0yyyl1)s)GHnl0H_Yymm0)xYmxmmYkymyQ)GGyGayyYYYm0)mm0mYyy)YGB0Bss1m0m))Y/X00B-_OB000/8/ly0QH H)mGAN88sxB/Hxm000xJ ssxxszl|0GHB%xYYGPPPk__Y 8sx 0x?m0s[xl1000m_ll)%%$`O_X\N5l0x[)m0_g 虙[[OX ))ml%s/nk\nlB)AYm0x00000 0mx0)m0Ymxmmm0mYsl1 Bحs0xYkl00Hx _*?l?kYxmmyyHlmB_%B_BYYmm*lB s10m0sBl)ϒ[T[ s xB/qB_?lY_yYY)B/Bxs/ Bz/8X{H)Bl%00m_mNYz8|myGm xxm00m0)HgBJDzz10000ymmlllsmx-*__0m0y)H[/BHY)ml s=Y1Hsm_Xyyyy)m_NBkkq5ym)xm1g)yyPyyyyymmHY)mmmmGyHrlH)m))mmmm)Y0kmmm000yx00xxsB0mmmmmmxm0x__/B00xxmy0mxxYxm))0mB?mYg̶X/k0mmxm00YHm0mxmyym̭00x%0ynHAlHBGmmyx)x)Y00xB?8nkyQGGkqHPyyyGHlY0mxB_/m0m)G%z0sYYHYHlBl?_?lmxmm|X_)llzsxBls0mBXk%)OsYl0?00xxBBss Ym0l)_nnny0Ozsxzz10J0Y)0z0?̭00mm?Xls/ xx0ms0BXTgT5fS)xYY08O__N[lkHHlAYX{n2lBBk/sx0xmm00x00mx)0lYH0l Bz000x1sx xm0Bl?_)0Yzm1llll?l%m0))0sY y0mmxYxG_HAY Y1xxxsHYYBssgNՒOsmӨ[Bs/ml8|_GB?lllBYxH?BYx1YxmgNf-?sx)lmzN8/BlN8myyPm00x00myy/gY|0 z000YY)0H/̕_YHsm)y_zm%B_?s)ymPssxx0xxxlX?HXyyyyyymYl8=zXyP*rf|G0m)Y)Y00yyy))0ymyyQG GQ)0xm0mm0X8Bl)0mm0x0001z=mmm00x00mx00BAYm000mxxmm)0kY0)YHAHmsm[)m)HY_Yxls)YmH_q_?000m*_YXA00)))mm0yYH%/*k*qkP 2n\kGQynnyHHBY0m0m0?lY0mmYmYm0lzYxsBB0HgAlH_0m?f|8H)m|NA)YB0x0g̋Y_xxx)xY)00xzsYxx//Y%mmmms_mx_qkym=OzB0Bl0%Y0zl%0%mGm0YXAm)z0%LN|gg:_$qf))%0xx%Bm0g|gAHHYG)m)_l|PP2̋Hm?ymxmm 0m00xm0xx0BYmgiqPsmxm01mx)mm)m0/x1l?0%__)xx?nm?/YYHmmmx)YY)*llssY%YBllYBH[T$`O$ssxO=[ب_0)0lOll|gl0BBl)zx)lBBl%0y gfnYmB8NsY8|/x)myyyllxmm0)ygHX0/1 %0xYlBm0ss lA?_lYxxYmm_$xxllB%H)mm_lk Y x x1B_YYXyyyyyyYl=/yyaq*qXNxYm000kmy)myy0yG0yyyyyyG%GmyyH_kY0Y)xfY0m8?m00 0/m0my0mmxYYx00zg? x0mxxmm mmmm))0YnOX_00xmB8/)BlBl[llҭB0mHq%) 0 0x_Yy0000m)_kmy0mY)BBssH88qf*k\n*Py*qqPGYHk00xmY0)mym)m))Ym?sszxYAml888A?X?gfH?xmYBxm?AB_BmmxYx000xxgX0xxBBxBx000mmBBm)_y/N s=8/zlmx%HY)նXgk gYm)mm0xԭB_m0Gn`A_000B%mNY gY)xln0m?*G2X_Y)qm0m000xmm0m0xmmX0NNnl[1ym xxmm0m0)ml Y_%0x_?)000_gl_B))mm))00B)yni/gAsH Bz?H__BBBs_[`NO 0 OըTحzJ8llY)Bg[A?B0lx0lmm)l _5fN?xYNN%m|/mmyyQGGQ_Y0\N|0 1sx 00l_l0szm l%l__)xxx_)msB%00mxYmm_Asssxx s1HXmyyyyAkmy)yH%0/Nx0x0mm0m00GHyy%x000000y0QG2ymk*GXAYm))m0lHmNmmm0)m1x0mm0msը?B00?xz[0y))x000)Gmy_nfgXm0sz /X_l_[[Oxs00 0 0 000 xm0/8s00 0x0my_)__Hs?%YN*nX{qHn*mmmY)YPYHY)/l0YGG)%lBY)zxxH[lXNfNA|g_Yymm???)%HmklxGBssx0mx0)Hmmmml0m0mm)BmYyHl))aQQ_f_mmmm_[XN888lm)mG)nLqmnԪ|5Og%%HBHx)gg )?ymm/Gqnn{)5X0xmmx0mmm0mxxBxmYffL-zAlyyyy0)yymy)Y)0xAs01_*xmx_H xxAX?ssxmxx0mmsHBlsB__llYH[Tϒحxm$$l[//YBmmg[Am%Y?mm H58B/{0GBn5fAX|s0z)GmGyQykY)0fN5f|8/0xxmxmmm))Y)0xzxHzB0%?lxxxmXYmsYx0mxm)Hm)__XlYsYHlllHYXyyyys))yyyyG)%B[000))yG)mNXlx00000000GQmG *kyYkXHmYXNgm8N8lm)00z=0mymmmsBzBxsm0m0)xm00mm)Ymm*gNXmll x1B1xY_/?1z x mxz x0 00m)B00m00YyP)lg|YHBlzNqHq*)\iqkkkGlm)HHHlPHl_)0AY0)mmmG)_B xY xx?/X/H)%mkNNXXm)yyY??/?Gmm%%BYx0mymmBmm000m0HH0klHkmmyyQkgg88zmGXXOY0)mqfS*Hkkknqn5m|f_%0g/_[HY)lymlNi)*qqnfX0mm00mm0mmxmx ss0_lN{B/xyP)my)H)xs x_0xmk_B0xYgBx)xmxxxmmYlm?g?YB__?*_llH ӒҶ mxm0NT z0s=/xHmm0Y|NY0000HsB%0BY H)qfllng?_Azs%x0YH)aqOXkNfg_00)x)x0)m00)m0l0_%)*?)0x0Alzxmxmmmm)H)[sBBl_lBYHBHy_/= )mm)y0y)0mJl xs0BAnGy0l)mYyJ% 0 m0000000{GPGmGyPAYY_ggH)|g*0f8gXk0))lxxY)0m0ms0z/zBx0Ymm000m)))y)X5_PYxzHHY0=zOOB= >x m0mx ?x0xmxx00myy)PX/YllB%sXnH*GN{kH)?zxxmH__l0mlm0)yymymYllY _ ))l?Xqk*yqNXYxHGm)l/8BB)s)0)lBm%mBHlYGyGmGHmmYmHHkym)yyqnnSNmmmyyygg|00my2^KK((Xqaro]^^(|n)YX|LqHmgg))mn_)GXH%*{fXr5Nk00mm))mmmxmxss0G2km0lAYyyyyyyyGHll)0ml0 n0x0%?0 x?[8s 1xxxm0mx[HlXqklBHxҒ`ҭ0m0g`ll__/iYymmm/N|smBBBXG)%mz 10l8Yss?|fi=?sx%BH{lGymmmXgg_8NX˕i*ym)y0mYmx?m%*xGyBH0%x)X_1)mmxmx0YBH/glYHl*q_kHYYX)yyyyA[B))y)yHqxlJs xlN)%kY)mG0Y00xx0000000lXkGrmmmygm0m?lN))ygfg0m)x=k0 0[zxss81)0000m)my_lGHXmzx O[̶_YY==sgOlm > 000)A[Ysx0m00yGHY0lHs1BH/flkHN5**Ys[?y0mmA)m0)m0ymm)A[Asmy* *a*q2AA HgkyyYNNs1B)ms-BxG%x?YYHH)ym0HHGyyYYHnHykq*Q2qrqLHYxY)0yymll 0sf2](((]Rq5n o](^l*85_mH0y_gNYyH_H)ymg%B {rq5NfH0mm)m0mymmxxY=/yQmxY0yyyqkPkl)0x?xxL0x)_Y0xmg_x=zzxY0xxx)Yg8YAXnYGlk_YY O:O0m)mTB/B%0l|8Y lY8N8_Hlys[=YmYABYHm%lXGq _NBl//YmBGmmyy5gggkH8n|N?{8fl0)y)Pkm0x?s0m*q0mm_H0xm0/gz xxxm0xm)?m??lqkYHYmyyyA0HY̶0Y0)0myml[lB0x1000m0BHlkHPHBH_))))))))Y_)msmmHyH*l_H_mmyymmkH_qH*A100yGQ̕Ylxxm00YHBHkqkkglxgNA[Azs = Oz8Am1/Jl1x00xx[zX m0y_smgYssx1BHY_/̭slYmY Y00)lz 0x)HY0%y0l=zz[kGaoar22qqGxz [A?Y)H50ssm)kq5ˋkx=lJHx)m0BlmyGyy m0mBN_HYy*nNfGykQ2ʲv2zs%GGIIM)0mXgzmx)k2L*GyHAnl%BGrn^\iHl-_yBls H GG)x ?HYHB?HG55f)m0ym000xsO )0Q0Q00yyyYG)HHHl)/080x?ymyAx 1Z= s 1x)m0X_)Bk_GYYnn  )mY0*k//HY0BOOz 0XXgHHll)8As)lAXll)xAx%lYss[H_?[L{_|g/AnG)|8BlHyyyyyGPBlB0mBm0/?0mmsls0xm_ANs 1 xxxmxxmmnSnH̭llkyyyyy)Xl0sHQy0_m0/0%x0m0m)mYmzBs xsYx00xxxmyy)Y%%Y)ymm0y0ym))Y)Ym)yymGmHYx)myG)qXnы)Bz000lGGl[/10x000)_l_{q{*q_GHHmmN[g[x z8[gB)B8/%B0000 /ŕmmmxsḽ%1sxB?zll/Xggg[BYY0_sym_/xs0m_xx%0YOs0lyrrrq2S*G*)msxx0)GNqYxx2(*ks0[m0ms_)myHyHHy)BB|l)PXn|5Nmy\]2z1PaIrry0qH_/xxG\(qQyyGPyymG(NыBmPm0mY ) 0vyaGG0m))%lHkyn55f)mm0m0Ys [1mmQQQyymmyyyyG)Gmk))YH_)gllgYsxB)0)0lsx1ꗵDxYxxxmyyn)g8Bx))YkH*B52qgs_))H0Qa*BB%0)A/=[l0mm0)))H_YNgBY?lBBYG//Y0mm_/XYxYYx=̶l_q_HY)m_Gyg%m0%BkyHHHl0m/00xs0mm= zz11x0m)Yffi/YBkkkxmyyyyy0ss)YQqAB0l_Yymm)YYYBssY)xmxmmYHPYllHGyy0GGG)GPkHyy)mHHPYHH)))mmm%yAym_8J0 08l0mG)0/ 0Y/_8N8fk_)00̭gN[ s8B8_l8H0YmgAg[B/mPmlBlzlx8ggXA/AgAXX/Y)Bmmzz0mBx0ly)m))8B022oorv]vnGysAlm)Y)P)f\)B0xG2*km=sYB0l)xPGBH)YAYYkk*5f5\k2ʡeI]B1)xHaaG 0xX2rrngY0m qk )mmPHYHYH*%a2L2mxx0mHy0xxxBB1xYin00mBG0HHGyN_m0Y_zz[Y0%karaQ0ig)0ymymymmm)H_)0?|=lmx)l)0/? 09sx0x1Yx0x)BlYm0%ll%f-z YYGkrQyG)B0s|/O[O xm)mmYYHlY|8mY%xxmsԕ x)B)m_%8?Y)GGk)xml_xyl/B)xHlH)PGHYHlY0Ax)80xl0m0/m J= sxxmxxm00XglxY%kk*l)110m00yyyylsHHBYm_g_BH)YmmmmYBBBllAl xxmYBl) HBlkA--ABmymmHHHHPA*P̋m))HklHlql)Y00mYm%k*kB̭0YHmQG0/?0H_/fN58n*mg_gA[/AXlB8/lBm)HN|gNOXgO8=1 ?J))Y)/gzBYm00x=OX?X|XXH)msm0Am 0mmmm)myOBAiXN\(\(2e2]qylYlmx%)YP_55q)yYxmm*HBmlB0)xlAlz)))mmP)ml)m0YlHHH_H55f5f55n*r(rrMaHxx%Qa _[Y0Xvq*gm)k)GPkYkYHs?yyQa*SqHYYH_msmxl_Bz0BfNP)mx00yx_)mmmHXNlxxG*/H0z$Y(qMo m??y))))YHYB000Y?lB1)lB1 "xmx) xHmgsm0H{X0f/zx)YlHBy2**H/?sl8g|mHYYY_lXq00x)xx0Bm_ON00mxm)lY/8 /HmGYaGG)H)Y?)m)?mHH)GGGPYYk)0BllYs)Bm0mBxx1Jzxx1)x00yy{g8lx)kll1)lH)YGyymm0)smBX/HyyyH YHBymy_m0)xHY0YlmH_YNX_n8-/i0m)GGqYmmmYm)%)HAHk-m0mxx[?0m0)y0?xB)m_-̋*_q5L*P|Nfgggg[xYBYYgAgOg̷))nf555XnN8g|gAs)Ymy)x=Yx0x0z/ /gXgXgԷgNg0zm)m0lyymym[lX|\((vvqHlHmGGG\NPy00*%[O/_Ymmm[_mmGyHmxm0B)2X\5f]a(2eamyyyyagG*ReyXg80P0sHxx0yQ)H)Hs)sY0 sBll0HNN_YHmy0m)xm)00mmxzlzB%0%B%mzz[m{r?l0mm))))YYYYlH0)sH0xA0xY))8x>90 m0 1JB00sH0%HYH/ qYx)YraR2IayGGl/YxB8gNO[szslPkHyYN)0yy[[xYx[8A00mBHHHGlN/mGPkPaHGGln%xBXB)0GGmym))y)Y)Hl)/N=lxYxB) "xxY)sxxm0%BNOxx%klss0mY))0lgyyyXAշmmm_/m00mlgX_m)??sBNNgHBlmyGQyN*Xy))0m0mYY_*HGmGz$zxB_%mG%m0B000l-N88N*NNL*Hg88/Bm0mxyXg||O0myfn|f|NN8?smm0m0N%zsmsx0BY NN8NN?_A*nms0mm)00A***PmyymXk5Xnq2nvv]]qqn)g8qyQy5lP0y0ymG k_/0mA)))mm)yyymmm0)0G2*rk qrvevovQmQG*eCrkHN8GmyBB)mymQGl)Hz0m0϶0BlB)xgXlABm)m0_Al mmxm0xl mxYsBmGk]QG)y0mmyg8xYzxsxmml_BB/09x0mH0% ?0/[slY))x0)gX\Bsl%xraaaaR0%smNOO[ϨYYlymgNB0yyy B8XXHsXkHB_B*l*kH kPPkyyg)g/_00y)mmGYkm0msmxYBYlϐ01x0xYm%m0?/sxm0B_11smmllYmYyBAm/)myQQggB0))[BYYs||NX//l?s)Nf?qk*nXGmQyGQ*n Hl)Ylymmmx%lXHm)xmO= 1gX/)0Hs?%0XN8NgNiq55ymsYs/sx)xmyy))GH_8Oxx10G-N5|ON8gx%))mH0N/xxxxmx)Y0N|Hk_n*ylmPPmmXqqy0y)y)Ykn\ r2]]*lH)8NNNXyyn5 lk%k k*X/_lAA)xkBXl0)YPYmHY0)PHfqqkkmGGqSn(ʲQaGaya*^5fkXAyx[ xm0)A8[/Al0sBxBYmmy0nks0 /mlA*ḽlHYx0$ B m%?)z[l)q*kry)G)yy0m)m)x)00mYBYl BBx0l8?m-80B z1sxYxYy0mm)/Gnfl)m_HSQQymQ0mYB8N8/xxyyN/yyyyHl/-NXfnXBq0PGyyQyqrn?///lnYGGmYYklPYH0x̭smm)YH)m>000mxm0Yzl00s lHY)HYYss ss0mYm0m)ll_yyGsABgmX[xYls_A?Y)lg[sx 0)_gXXnyyyr*qGHHHyy0_BmmlsXX{Gmm)YzO8B/8?)YB)mN8X_qL(P)lY00 mBYmHm)s00mnNf2H_/X=lmHPkl%0 /mxYxm0mY)0sfN_HHkl_kGPlHyPP)mgHGmmyy)y)*NN*22]*{{klNPGk2\q*HllHy?NgXglA/XgAlkllm)BYlϷm*5kYm˪\(q5qaaGQyQakl?Gmm00kl[s0)XlXN[0)010 ?Y%myH/n_G00OXHn)l[[smss[xB)00%H%xx̶ [_Y_XX{ Gyy))m0mm0my)YYm0zgAx0ssYm0% NNXn[XBxx x0xmmYmmym0mgH\flH)*?qq{q Qy?/glz[mmmyyyA)?Hk_Xl%lLffԺX_)YHH_*HHnyyymQQy GlgX/[?lB8N||yyG0mm0ml%Bxs)0mxH_HB 9xxY%[xx [BsH0x)ymD1sszBs0x0)0mmAyyyymmB?AlB 1sJBxY)lxJ mX\2n)0yyf5H)HHHy0Ag_lBggY?gsy0mmx1xz϶8m0mH0m)?-|NAy)k*qnnk_B0xmm0gfn_lY00 s1m0{*)BY B1Ym)gPHHY0[Nxxmx)m))?BY)B*)my)PPPngBkmmmmm)mOGPXfXHG)**q*[g|lqN_NNXn*HGyGH kq(XnGA8|g*X|NNklkkmmBHBHOg0)_///BlY?n*arr{G0yy2]\qq2n_)m)m0yfQyym= 0sBl_m0000϶s x0sl)y)PHl_?BxxHsynH0XX_[=x$zx00)?xl[zg/Y))mmmyymyG0mlXgmllzm0/g˭q%N8gYx0 xxmmmy)0OXf\XB?kyH*___y0)BHsmm XB?myyy)y_llXHBx%m0nGai8)sYHBmkGQ00myqqrPXXsBgN8)mmymmm)))0g BBsBYxml8g? 9-880l/BBzxJymm) _s Y0)x0Ylxm1sfX)yGm00B=̶?붭?/XA) sx xlgg/qPG*qmymy*XXHPYPmy0y_HYXAs?l00GmB%mxx 8?m0mm0Ymmm%G/?YGmrk\*yBk001Bl0mXqqymHksx l?* kGB0sBx1mY5\Pkl)0?/1YmmBB)H%my_qHmkf)yPkXX00)m000yl[_Nf-m닋*/A?Grqf_)G k {X*yy_8g)HAglk__ymBll|Am0m_m)̶BYYx%)yraa2kGGG)yyy^(v2{2*_)my2GyyyGYl[XAHmmx0xxzxYzl)ykPY?10mmyXNXlBAllms[lY00lmxz$lmm)y)mymyH2AHY/g/NσBs[xlAkXAJ?%xXk)msYx00xxx))mymmB|*f\|-my)QmGYGH)YY[AAggHz)))Nf|XYx)Yx0mxQmQr G%Blm00n5XqQ)%yQQq\gXl=ss/NN)mmm)s)m_XA0m1sϭ00g[?/8gXl1Bxx0mm)mmmyys s )xAA[/lslyYgYyllx0?x?lB11[lm[A[ sx z_X22 2H)mm))GYGYHHHyy0ymy)m)_xmB?Bz0x0001Asm0%Y%HlBHkBl*0lmmymyGGGymPBl0_??B)mm)HlH0YBYY)YPyyP_mz)lAPPHlm0?J0xx))_/sBHs)mHly5x0m0PPmlBx)0xxPHH/LNNB)lX[A XggAmxmmYYHq-NNHGG q\5\LqyYmm)YG)l_l)%)?? |m?)z xs sBlGGkHYzxBz 0mmmX*\*qq)myyyyyyG)y [=gXAsHl?lzs00BzYYmGmmmmx?Y0mm*Y)m0lm0 )s)lxx00sz[[zxxmmm0ymy)G)l//ggO8[ Bx///x HPA0B10mmm)0)0mgXq5NfBHkPm))ssBYym00H/B_A[0kymyygg__Y%1%xx00m)xεks00xx)0Gq\NNgmy zBslAH)Hqk00Yl/gAz̨zl[lmmng-_%BgN8X)0000 xxxxm0)))y)kssY0/x000kfgGmX8?)0 =xxg8BsY 0Yx0YxlAXg[s sxz=kmP\5\\)YPmm)mmmPYHmmmy))mY)[0)0lA/l 0 0 0BBxx0BlgN?k_B_BYxm)yyH00lxmyGPYHYmY 0xmkrPH)mBB1mmyym |x1xmmymmzYls)m)0ymxmyxx000BXBxxHHmmX_YHYBX sx0 km0)YH_X)GG{nS2k*0)m)kH)kHlxx?l g|[)0x0x 00x000my**GGsHx%_m)00i*n\qH0)GyyHGPmss xs sl_BYsYs x B ssgHm)yHmy0xx0 lx0myyyxkY0)x sm zYs)00B_m |T [xxm0))ymyyXgg?8H lzs [=l)Y[_BNg?%%Hmf*y?=/حx)x)mYm)m)?Nin5Nl%YBkr?YYYmmB)mm00Ol_lHl[lx0myym)m)Ym_A?H0YzYm))m19Y)0yk51x%kXfHk_kYYzB ))0Hksg淭AO|[Bs[sm0H/HX?8=J?%%/XXHmY0s00x )))mym0)A[sYHsx)x00xY 0000sm0Xxx0OBx[=8/BY0Y|00s?[gYYx1sxB8%qov2qrQGym))))HHk_k__llY)))))HkHlkllXxx0001B00xms0))*Y0_Y)m)mGGPllkY0B/YBm0mYkHlB0)mg|fXlB?Bx00mBYHYYYYBmyzxx0))m)Hmmymmm0y0yymm)0yy)y0msz000)B)%HYHBkB*?HY0mxYB)xm0l8m)lllYmYlBYslm)ymNN(S2qyHH00lsm8xB00/s00mxm0xx0x)myGkyBs)m%z00ylx_Xqn*my)yy)HHY_Bm[1mxx000000mx0 1xm)Hm00m0mB0mmmmyYHlmlB0B sHXm_XsxAzsx Y)mm)mxyyN8BOOlzssX_xl?Hg8i\_)xsXyBgH0/m)mmm)m)m%|NY) P2{_0Ylzy)Y?YBOBn[/xmmmmm00ymmy0))G)mm)0O[ lzBmBl YYm000yHHlm_ymmml ls0ymy)yGH_/g/=gOO lgm0/H{mm0kB?xls0xm)m)m0ymAAzA Yxm0m000mxmm?O8_m_|_l0[N[0zB?/xxYsx8mlBsllB sslnav]rGGyym)GPPHkkBm)YPY)YHllsxmx00xm0x0000 l0x)Hm0Yklmmx))k)yyPHkl/ks/Y)m*_lx0 Y0)k*Xk?szY)l_l?AAY0m0lzBzs)yyy0)m)0mmm)my00myy)0xYx0Y_l%BHBH_nˋ)s)00000Y)0xlB/XlBzABHmyHN52]]_XHyH_lY00=x 08m00/s0 )xm0xY)mqk0 YJ10Yl?zH_X*nnqmmkkHY)HmYYYYzzxxm000m000mxxmmzBs01[sxmmm)x000xx)xmHY)y0H_B0B?BY1YYxlgf/_Xg xs1xg))YmHXlxBOO /zzA[)Bls0lfNNG))0)Ymz0x[xmmm)YBm0ykXABBHxk***n[m ̷lxG__H0sOY/[/ sx)))mmm)Y)YHYym/N A/s0sBY)x0Ylmy0GsBmmy0mG)))YYsYsm)mm0yy8X̨| Y szx)/_H5n*%H?yB00zl1xymy%%xm))gll)xxxxmmmxs8YYm_OA?/?/lY))s/88/?/H%YYsl(q2Gyyy0y)yyxx0xx0xz0 BYm0mzYx)%my)0y%H)x0ss)0lyk8?_00m0ymmmxx0 zBgz x0smmymmBsY)0y0 [[s))mmkHmy))y)yyyxxBB1x?Xni_*Hkq%Hym)xlBxlm))))BNN8)xY?B00)mNN55^]]qS-|XyG)m)00mzx1xs0m//)0)mxxxmx0lyqXnGm%/%y2%*{nByH?{Yyx?xmYY)xxJ0 xx0m00m000xls [xmmmY0 x0m))xm00yyyYy_YXz?sxqq_nXNNgYxxYx0g_*_mx)0m0000x8[[xxBgX Bs00l*P***Py0m0zJBmsY0s_xsm00)_0x0xls%kGn{*nksmylXB0Oqnls mmH YBl)PGAmmGXN-% -iiLmG%)m0Y/BmmsY000)YHl) Bx_lx0ymm)yNzlOÕz g_BB)gNn\ %/ll0[lsmm))mxmms 1xxmmx))m0)BB[8?AY_HsY0_g_Y8NB)YYHH_XgYYYssxXH2\\(S2 yGGy)m0yy)))0yym00B00 l x/Y00m0s Bl00Hm0mGmHmmm000mmmnmG)NY)000)mm))Bx1z?gN|zzmxxl00ym00Bxym0xmmmmH_lY))mmy))Hyyy00BYmm0/fffNN|8/i)G0mzs)lB)YY)/N8HmBxOl_HmmHNNNqv2]]*qH)_YymyYmx)0z1z0BB_xxx)xx)m)kXnXflaGQ/%q8AXkG%_?HYB??ml/_xxJ10m00000m0x0mxm0zsxxx0xxsY0mmx)m)0mYHYlm0YXgg/1sz0)YqNf|NgX[x10m[gXkmAkm000N8[m 0llx)X5*mm/B1H x0YYmx)//x0m)BBsxxHkH(2{PqqY0mmHA?YB ?__A0[Y)[YYHYHH0_qG %% Hx))xlmy0ls)mmm)YHxm0AB0m0)0YX_1s8A[1Bsy5N5*)m0mGmX00B=[xmmmY00)yyyXm0xx)x)mymA|zBBYx)sHH)y//8_%)%s0||_%%HY)GnXqn\n((aG))y))yymyym))YyyyGm0 01zxmm0x0xYmYysmxmxY)ymql_Y)nX0Yxk|{))H%H̶#h==mxx0sxxg/AnXAm00lzmyy)mm)kHYGm)m)0lsymrGyyym))0 l)0%8|glNN*Ym00A[[mPPHGgN[[BYXX?qG)*5(]]2vqq000mm YY)%mmsBl[mxB0 B1xxxx1x)mx)m_XH*aQGmymN|Nnqy*qk_Xzg?=xlxx xm0xxxxm0x x0xz 0x00)mYBy)m)m00000m)[0gg/l/0YYmXL_XgNymQN[0 [ mBB))A/l1xx0sgg)zY1011Bx[5Nlg8Tzx Y10m/ 10_Yyn2{{* HkGG*H_/ 00xzxs0m0_l)y*X{kRM}Mw o Gmgxxl8%)ys0 sxxBmmYHzl0))mm000y)g歶0lO/Y ymB*lY**mm0_l01x YxY 0m0ysxxxYm)m?8Nzlsx0m)mHg8N8B)YH%YmH|NO8NҶsmHYllH%fLq(2]qaaGrGyG)m))HGy)ymy)m))YHymym)YyPHP 0zsBsm))00xxx))Yx)0Ym{gnYmYX)BsG)mNNB)m)X|XHB[1"O))x $?Xs/ 0YYm0Bz)yyyy)x00m)PG)Ymmy0xlBmm)HG))Ym0mmx0?NNX̭)%8iYm0skPm?Aحlm0H{̋Q52v]n2*gHxmx0)00mxY 0[Xs BH01zsxx1xx)mmnX5\5q {lmnXf5f2HG*_*ضg涶Bxz mxxx)000xmxxx s[z[x00xmY)mm)m0mHY00mx0g-BBl%lmfgNyGG[ xx [8mBXYy00xx Ygs101Yl1x/NN85AYs_zss $x)mmmxBYxJB x)m)q^S2n{HGGYymyHkl[[Y0z[s[?HHxx)x?** ]o}Ko] Gm=zN{m0sY[[_XA0x/YBl000mN8=0m0 gsBzsxmYgNf*myY[gz xx0s 00xxy0uY[sx Ymm)))GNX)B[HBsxm)mkN8gX8Xsm))GlXYBOYmm)GB8GGGGyH)yXH)kk0my)yylAqk?s ym0xxmmml/Y00A/Y0m0)m5nAk)l*XXB%XGy%k|gmm|Y/ z">"=O x1A10000xz0mmyyyyyy)m))mHP_)mm)BYYmmHyymmm0̕?_Xg?_m)mY8Og))mYH)00[0nHf|SG2qrmgAxx0m0AYmB0xx0s_0mzg[sszsxm0m))A|NN\5NNmAng8qX\qq* 8gll//00/s x xxmm xxxxxm0xmx)mmxmxm0)0msYlx 0mym00l?1mmXXXgl)mylzBYz[AlXҶxA8Y0yy_BsmYgNglzsmxxBYxl8B0zAgsxzsxxmm)mY/=[/̭00yyG{2X{qyyyyG)y?_?l0m [//_Y*A?Hg?)QQQRKowKKeMQmyy_σ?)mxlX̷[gXXg/lAH)lzmmg?_mll))[zxY0HxmYBsm[f8|gBgǪl1xmxssxmxx)ByX zxx)x)Y0mB Y00)mXY?AB0mmm%mNN|H)B)zlxx)mfNX)HnqGG*/mm)kyymm|f|YHlyyym)P̷l??zxm)mxm0x)m)AYxmmymqn_)8_N_Gyy*??H))mHl_YHl_=EDDOm1xzO x=))mmmg0xmmmmy0Hmm)HHkkA))mzmxm)yHlmxx[?m/gX//_sHxlN8mmH_0BxzB AYx0m_?%{ԋGqar)s__s0mHYHym00mm000x_Y B xm0mmHNGyyGG*l)5f5fq**q**qnglHBl?m0Y/x Bsx1x0mxxx0x0x00xmxmYm0mxx[XxYYss0)myH/|N/lHBfXklNN_BGmQzAX|[10Bm0y/lYxxmxgY[涶/lll%Hl))xYm[g?ssmx)0l_Bxz[_Y*yQm*Ln̋y0yyyym))A|N8_A ?[?/Akkl?B)0ymGoKwwoQQQQ//=OYyn5nBmH/fNgXg?_)Bs0//|0 ym0xx0)0Y8Y0YHHYmYNNNgAYz [ 1xszxx0x))YmX[ϭ l x0)mmYH)xlYYx)mmG)))))xYYY)Ymxm))lHBs)0)))lklHYHlgl)0)GHlY))YHyyy_mHyymyym[ggOmmmmm0szg[sY0mm0m)))ymGHHHY*Y_nqqqqyXG0smm)m))mHP "#c=Y011O m0mmm)mmmym l000yyyBmx00kHHqn)m)llmm00yy)xls0lXg_qHmmmx[nAlmmsB0xs0zz)mxy0m?HYymkgg8HYklHmyy0m0x00000xH//?x)mm0)Ykmm)yGm)X8\f닋_k*58kl*_??HHA=x s1xxxm0xmmY)lOX[Yx)Bxx0x0z/BxY?Y0)mm0)mmH[z01YPmP**55gHGQ{ //Axm_0ymym))mmym[k/{YB*Bm0m0sAl%*̕mHY)00XNXGGGmmHl)m)lHym)mkH_l/H?X_%YHl *X_BH0)yy Ro]MRQQyQYX?Jσl lg*\5g?Ys[/[?A|/AymYYY0YNgz̶xmxB1 fN_0Bl sxmm0mHl00XY_n[zxx0xx)mYx%sm0mm)m)m)GYBmmm))))mmmHHBx)x)Gy0mPY)YHBY)mQyy)mYHGky)mm)YHy0)m)mk)Ymxmm0B?/l0xBx))0YGPHHPYHPH_klX8gGkl kqq*HQ0GlYHlkkl__"=N[x1 zsm00m0my0Ymm)Hm)mYlqqXm)lxm0yyyyyyyy0__0)__lXAl)))0Y)Hgmxmm0B0x xmYY)))0HHxY)mkBHl_HH_B%0yyym)000000mm0[/)m0mmmYH__/yG)m))gNf5N{)H*{N{ll_l__?*{*_H = xm0 0mm Y0lBB0x)sH0000l00xmx)xmmxm))0YY[Bxm0x0my_XfGm%Hq?/l=?/lg[0l)0yymyyymm0ymm0y0)[[Y0xYGHHH%mm)lmsBHA/zsxYsy)00y_N?mGmm)mmBmyPkyQ))mmy0)mm0/N̋HkX(kkG))*GmoMIooMo]my_N xxms_mlA15f0Bz̨/[A=f/0l0yXBY)m)g /A[[BlB%BHm)0l[gsm0mmYBl)Xy0lBx1xmmxYk0Ym0mYHHYkkYll0m)mmmmYYHGGGHlBx)x)HYHkHHl_YY)m))Gmmm))yYH_l))))Y)m)sl0y)m00))msYm))x0)g[xBs0xY0x))mYkl_____)XNXYH HymHYGGPPHH PsO#cx" J=x[Y0Pyymy00OY0yyBBmxmmYkAgym)))l0m0xm0mymyyyyyy lYlHHHHYmmx)xYlXNYmms0O[0O[Ys)YH)0llsmYYHYqAm)GyyyYY x1x000mH0Yx0)YYHHl_X0)YGHkHHO?sNNN8YmmmHkB0Sn***q{* k0z x m_YgYmBm0g[xx0x0zx0Ys)ymmYBAYsx?Nf5ym {*L/[mm|[Yxmmmmymymymy)mmYYHBBHB0 ?)%k{?*smx)00YlyyYymyyNNi)GPYl 0mkknnym)mYmxm/nk*q**GG)%rRr{_x)ms=t>"D0 EE?HA555f5f_lzA[[?[0?0ymy)m))m)mX[ B?/BqB0xslxH_/z/sxkyy00?0)m0mBBx0x0Ym)_lB_A_)))))mBH0))mm)HBHk*qA_smmYYkYHk_lHY))myHYlXAy)))_YHYlllB_mmmmm0)Hmm00mYx)0x0)z_xx mxm0)myy0ymmyYNlG*PGQymymyxhD促Ogx z=z /mHk)0mm8l0y)_mx)sk_gf)))m)x00l mm0myzx̷y)PHGm)mB?lnBy)Y0Y0s $YBYBYY)y0))?lm0mY̷yyyyyymlBx00BxBlmm0m0?8/BY)0)HlB_?0kmxm)G)Y0N/YqNGm)mQXNfnl**{*]*** m0sxx0xs?A/0BBm[g s8mmxx0/B0YYYYm))s00A/Bslzl_m*Nqf*yiԶ//B08g/xsm)0)))yyyyymmgsB_%_ YmY)H_* ?ˋ0BxYl?yYm))kqH))G0lm)mG2(nyHHHYBmyqNk{n{G **q{GGHr r rGm)xmm0>">E1 NNfN5f5*knl[_00 0myyyy)ymm0yymx[T0msBsHHYmm)zGmHBBzH)ymmx?YmYYlsmxxmlylmNN|˷Ym0mymY0x0mmAYkqX HH00sm00my0)mXymylmlYlHyymymA00xY1/%mxmxY?zBs0lB s0 )00[BBlgmm)Ym000mm)0mymyXkGGk GQmyYmQQ ֚ح|/1 =z0slmkkm0y0yxgl)Y/0)Al0)m)Y [llllBGys_BlX_x))s_AkyYGy)H0[[mzBzHm00mmxllxmm)qHQyyyyyyY0 xs000mmBN/Y))m0s0mmlsN8xmm00mmiN* **?**y)0xHxmxxs0mA0lAB1x0xxA0YYYYYHyxxz_sxBsxH0_/)Bi25NqGq2\i?8N |?mYYHmymG)0AllY Bk%m){**%%G)00km0yxySfqGPxlBmmmykn2nqqX/_))GN5i]nkq**q**//rr HGxxYx0EEdddEsmxy1sY8N|ik_A[smx?)0)mmyG)mm)0)XXH k BHBm0Bx%B *{ilsx)))y00 mmY)mY0xy0Gq\mlkmfXlH))mmy)m00mAXAqHHlYx0m)yynXYmB0mHYl_BB00x/smmm[lxxH̭000x?A00mm0mmm0mYHHm%kHGyGPlyGaQQQYOZ϶ ?>z mk0))H|O/ylgzm0x YlYHAY))sgXlBYBY?YmlAyyyyyy0_gl?/Xԋ)mmxxBkymymY)Y[s A̭ zBm0mmYBlxxmmy)PyQyy101xsBsx0)xm0lmYYym)Y)00YgXYBy0ym88?%)̶)0mx00m{q*q****k)Bm?lx0x x00[00x000sY Ymm0mYYHYHYmy0s)m0/|-zmH5f55\2nf*AY0B88xYY[mmyyyyym0_XYl **lYsA/m̲**ly0myHAlHmx)m)G)qfqPP*ll0my*(nnqGXNA?X-Xkfq(*qqX%rrH)x1m0EUĬE1H00 xmyX|GkHHA0ANg[mm)mGyymm)0gHHk%*lY)YxAAB{kB*-BxYm)y0xm0mYy0y0*X\yyynNfY0)0ygmYlHHHlHHmGk/Y0m0ymm0m0mGq*Hmlm)G))_[/?Yx??00s00z/0xx0Bsx 00 0xllBHB_)0))mAY)m_Gm*l{%)myGXXԷHHGPrQQQx= ON=s0 x [B)kY))ym0g00yl?0mm0mHmx000glmYY))%Ym)mm0BgO[X_Y)%ymmH/gmyyyHOgA[Xg[ mmYsm_Bx0)mm0yHyQyyy)x[B 1sxxmmy//)0x_B)00B//A? m0)[)0s00zm8xxBBmmm00/N2q{_k*** PylYm00xY|000=m0mm)00sm0xxBxm)sYPmx[$s[[YHHy/x)\|Nff5ѳqn/)xgBx==_mxYxH/Y0_ym)[XlHql*?**k*____[? {S*_*qyymmllAl_0xmmGmn52A225\X*|8-l_XXLXNfin***qn{m Q)))))mx@"4HyyyYX8gmGGmg|Yx0/80mYYBHmmyyyy0) gk GkHmlA/**SHmmm0ymmm0xxYsyy05n)mmlH0Hk0))0lsA/mYkHmH_B%smyymmyyymnnAY)_)))mm)YHHsY0?϶涭BlB00m00x0zx0)x 0 0 000HgXAl%xyyXXA?BB_˷)))y)HH_GxGmH5nHH kaGGm)sB8=zY100s/llYlmm0m m0yml ?YsXX/?_s0lHxHHlllYYH)_0y)0ym ///LXAg0G)0x̕))QXgOA8_x)YYHlm0Hlmy)HqGyyx11zs00mm0mgB00?)00B/[l00x)0my)mxAlx0mx00%_00))mxXiGy*HkynXl0_s0X0Ys0mmYxx000AY0__)Ym)0xzB/%z?S5f5N|NqnNiBzx0xHY0 lnYy\qyH)YgXl*kkk_?AHX\ **nm_H*k0xxmm)GmynXn{_HH\qG8NXlkB**5 *vnB%rokyyyQ D44DlPyyy)lYl/Y%Gkk)H0HXNN0Y[mmmyy))llY*k**k_q_YB//_)/**k0mxmyy1ssHByyyYGG%kq*)mmYxl0mm)lAH0Ylk)0GYlH))mHyyymG_NAlx 0y))))sBB0_OḽBmm0Y YB̙ϭx 0m0?|fX/qk*qggXXX GGyz)P̷YHBHG rl*yBG/xs% xmH[YmYAYHm0y_)yymnA_BBAXgAYA8_YkB_k%YYH00l)mmm0X/sm0mm%A?xymBB)yyG gg|Osxx))YHYHlmxYB/__lH_Hm_HQyy))yBXmm0yHklXBx)m0/lm))0YYBzslx)?XXqng*HN5fL-sxGG *lHlH|Nfn?l[s=0 Ymx00Bx0z0BH)))m?XBYy0%)%fNNNN8-_*qN%0xBYsAl)0m Y1BNH)lBgg2XG *kH-?gkG_*qPmBl/AHxm)YknXqrXy**r%8?lkHHG*ffXGGk nqrf8A rraP Gm)my2ssJ /?llymmH)Y?HHmmH_YlkHY[BxB0xB[00y*y)myAX__?***k***___/s/{{{]qkymmHyB 11llym_)ymm)X[XX_lBYl/l))HkY)H08/l%m)Gk_GmmmmG_x))YYHslYx00mHYm̶zYxz?sm?fff˕fNNNffX/kGkH%mGPyBlmxmmyYHlHmyG%GHYHkkB00xs1xmxBYyY_/Allmmmy)0Xyy00N/AAng?_88lH?**lBHYYm_Ym)))0[A/Xz0xxm%i/{)0)/k0__H%QQyX8[ Xzxx)m)Y)sYg?lHYkllly)G))yHlY0sB/[x0m))H{8_0mmm000xB[lYm))̭BzBsYxx 1xsNfm|Gxm kk5GBHfnmmzzzB[xm0gzx00xgm00)m)m?) Bsy)myx)0yyyr5NNG**{XB0 xYsBYY0xBBY%kA|lH)XiXˋmmHHr*k)//X)G GmlllAYxx)l*fiHX**nqkPGGBXHHHHmmf0mG q88 alkkPyPqkl x1Bgm0mBYm00Ys))HHxYsϭg_x0 YlBY˷)Y?q *qklklqA_H_Xn*q2X?%))yxx111HByYYHY)myH0Ymm_mlBB)000y)Y)0Hll/HGY%?X%YB%H*mmym))mmy00mmYx))PYHYYlg8_HY))kYmsl8[00==?sBBf85NN5\Sf{yGG aGHl)ms100mm%HYBHmy)PPq*GGx1x10mkGyx_[A m))0HmgY)0BxfXlkA*˕|Hl*?*qk**H%sl0 mm0Y-As)Y10lxBmssY_sGG%NNs00zssYmxmmm)Hl0HgBmYs)xYxAH)m)0m)m)00)m)*]kGyHl))0sAx00000xm*g mx000000)m0m%5N2Pyqk))kxxmqqHYHBmNgl[Ճ[)mmxH?s00xzlxm0xm0Y0B0yYY))x0G)QyHf/Y%G*km0H00 zxxYlHYsx /BYBl{N_?0YXN5/m))mkkqg|8g)GmQ 2l0mB_Hx))YHlG0{\kym0B[G%HH*H%GGmNm) qylgH)y_Ym {?lHkYlYz00xx)Hm))m0)sm)m)0xs ̶1)s/kY0s1l*55 )kPg/̋qYBlH?/gLlQmqq)m)mm0011BYykAsBsxYmGmmlAxYH0xmx00Ymml_*??%)%H?*YkkmyG))Y)HlBklBlllB_Ykl_*nA?Ag8gg|?m)Y)H0s8gm00x%lBXfN8N5N\nqn5nqYHkGr *m2))00x000m_??ls_llHHHHllk*)xxxxmHGyym/mm0)PYkml_ Bm n5nԋHl_|N8l_*{**BHYmxmm)X/%01xm1Y%y%Y%gJBG%% H|m0x0)YY_lgsBHlBHsYl_YH)m)HHHYG0)0sm)GG*QH%mH0Bgl00000000ykg_m0xmx0)x)Yxy0f5(yQyG)xm0mGH)HBlsm0qPBs1 s x ) [zx0m0l0ll00xmHHm0myGay*A0BG)m0%10? _BB [)Y**X/BmkXf5mxHnq0NN)G)mk2\8Ym0_xm))HmHH%)%rr**kPGGmmy%_G0mYGymlGymlx)mGrrrQ0sX?mGGyXAyy***ql0)H)m)0B 01lY0B_Hm01Y%m%s00 JHxsYBYYxmB zHlqNP_)g̕ikmm)l *km/f[|)Ga YGl)x11ByPXX[zB00Y)x0[f)m))mm)YHmy̋_/LXG_lkkHnBmmsBBYHYq8gNX/m0yq**q*OggAY0xsBxxOOxlBzA1zz%ANff555N5]q^f\LHrk r]kG)x)m0m))m0YY0)Yl_)_H)Y̶l_lPqym)mmyyyYB|)xmmlgklHm_N?l)mͺ0%k{_XH%H_lH*k00yy)xHGkYxxYm?0xmHYHBm0Ys))xxxxx00l%[*kBq_*_HY%HHmm00*kayYx)0G*qGxH0Y 0000xx00x)myYmmxxx)x)m0?--%YY)YBlm)x0mmxH2Hkf\fP?ssszxxBLH8s0xzx 0xmymy0BxmymyGmY/gYs1sBxxsm0[ls)0kXYY[/)B_-H*_?sHAgkYmxmY_XfQXNH)mm{nsmYYBGm0mYkkG5Lr]2rar rGHszBBk%G G%BkY0mykHymmG%HGmYm0mmymqkPm zB1G)/z0zxYms/ 1lYAs0xsxxxsBYmlB[BxlN_mN|iGmG)%l*kQk|N-%%mQPrvLkYYxssss1ss 0P0G[/zlss)0lY/xH/?_YBBmm)Y_fqyXf8Bmm)qs)x0x00Hsz)0e3 vmm))H)Ym?m_l)mm))mYkHmy_NXnLXX*k**%0z/BgB8XAYQ*2**qiXmNm00xYs)x[z/Azxs_ngXX|_)q*q5in2PHr kH0_Y00m)llYx0m) 8gx)*knXXkGqyQyyA0lBl)YH)mH0H_YyYmyB%%Gy/X8Nf*m0G/nlB0mmx-N5_m0mmGmGQ??Ylm)H%%%x%Y)Y)0lYBBBgNl?***? Yk*mXul)_k{H0n{ rmGkkxmxXl0xxmx)m)lxxxmx)mXB%BY)[XlYHAm0y)lAsm)myqGQQP)) _$s0mgXHYmx0/Bsx0Y0mmyyyyY_yyy0)A8=Bs xxm[s)Y0N0mx0HHB_//AlY%zkl_sxH_HPy000mY)0))mm̨_lls))G_?lYlmmyyrQ2]n2IrrrGy1gOB?|?NBsH0?XQykHHGBq[))y򋋋m /y0zlH000 91myYHYY0gxsY0HB)B/_00mszkGk)H Hy%mmx)BHXY%xmxGkLX_BB00x=J?m3b;\ > m0?_)m?N)Y00)mymmxyyYmmmy0m)lX)mHH_n_mm)0l/zBlmYgB000000m)lB0BnYf5fNPOW1 1 0lgԋk kk*_Gm)yyym_)yy_*H__HkPyykqGy)mkX m?gg/?YmXNf888lBmX[BsB=__G))_m))0mlYmmm5N(k kqq G**nylmmm)y糁 F VnrayyyYB)m00Gxs10m)yyGXym))mHHY5 m)xy0 gX/_AXNsH/OYxgg8A8|YYlxBO88=1??ymm)m)))YYmB)Hn\qx)xG\|qqqq%)0%l=/1B00_l0X)BBx00YsmxYm0x0m)YYm_8XgXk)0)ls0Bgy0)m0_X)yymm??qmq*H)lHm))YHymymGAXg 0m00xsx8z0)PGymsHBlsxx)s00_g)0mxxm0xGPPs)m0)yymHmymyymy0))0GyP*PymHf|*kkB%0_A/OHgxxBmmAAYzxB_Y=x0m0)mlYH0m)0m" 1111 00m_l_?__m[B00)yxH0mY000m0m)00)kH)my)mnXы)HHl)l8[[0Ϸ00000Ygg[smHfN|NNf-qkAmOOs1 s  ԋk kkkkkk*YymGyPNNXm)H))mmG%yy/XlxYsm0HX[l0Ns?m/O/Bzmyly0mmmmym0HBxxx0Hf5 qqk *kGyyy)kmyyy\Fyyyyyy)sY1m0))0myyY)Y z%xx0yyy))Yyym_Hy*)mm0%2Xq))XX=̶/8XY|gX8|gN8_smYmg8g|?mYlxyyy)ymmmyHPyn**]**qSmmH8|fX* **]{0 0涭1mxmB00mlyxm)_/m00xmmYm))xym)m_gNHm)_X xmBxmlXg_)m%)myNHlm0mmykNNN_))m)0yyy)NB[A ssxm gHAmmkG)yYx_Bx1x0mx0yY)mH)*)mY0_fYkYxm0)mHXnHll0ymGGGy%))HYHkyyy0 8 1B88A%0)0)mm)z?Y0sxB ?mmmy)Yxm0m)E>DD xx00ml/sX)ym0)PlH0HyHy0mYYlHyyH)mY)yyyyHn5n{YHl?YlA̭BYx/O=00x_gXBllYYx)Yy0f8lGPmO$s s sNH_kkrH**ymyyymy)f5HyyyyQyyyQymy)y/O|BsYH)0))[ z00ml%lH)yyyHm0yYmyYkHk)HH0)mH?S*S*k**PSPyQmyQP \\ yGGym*XnHHm0mm)))HPlP_? mmyyyyyy*k_kP0mQ0mxkXyy)_8/A/mX||?J|8?Hm?l1H8?_)m0)))H)mmPGPfq **\5/NnXqk*//H000B==[_m)Ymm|Nmx0mxYxm0mx)mym)mHAmymg m0YA)BY)xAԷkm0mlB%HH)yy)yYnHBYym))yXX)l[ Bxm)*XYYGyyyym)B=zsY)0mY)GkkYH0_ffP)BAB)mmyxBNklmyyP)lB0)mm0m00X**zz[|8/HYB%%00ymgB0xBBB0/Ng/Ak)mY0)0000#D x1xmmx /_m0mmmmPPlHxBmmm)HHYy0))yyyHXfgGYkB[zBsx̭ l[sB[Bm)y0*|l)xky)g ss sm|fn*{H qHy)mqqGyyyyGy)_Aqy)yGyymmmGym0YNxBB)ymm0xs YsByzzmY)yy)ymPXg_mmy)m|f8m)m0XA5n{2 kk *QHGrn kyGqn)mnNYHY)Y0)XPl_ByPN5˷yy0)m0x))GynX/z)YYY)XXBm[NBH|/zB[gXY)mm)ky0yyfki|5AXkmHk G//%)m m11xO8O=_YYxsmgX0m)0mYxmmmym0xxxHH)yy/YxABxBY_nl%x)))yGPHlPym)mymm))Hkyyy)yyyY)lxsxxBk*%myym)mY?zglzx0mYHGmyy)YY)m)HYHGYYm0m)y)?HHlYmy)m)B[m0)0m>괵Hl_zlZl?l0mGHq*G))HmAA*Ykffmm0mBYx0m00> z xsy)mlmBmyym0))PPlHm_0)yyyHHk0)myyymyy*nnHlB8ḽ?l=O/mHA_lXyym)kXB))_))Ts sHyXmHGyGH0m)X)H0y)m)Gymy))mm_YYBGyy0lYsmn)??1)_k)myy)qgXly)_f)y)mBgBkg|LXXSGG H *kkPG5fn*mmm)_08Nx)yl__/?Hmm)))Gf5HmPGyyyfPyy))yyGym0))yyA8gBsYH)y)/s1?0H008/zl̷_ql)Ylyy0yHqH2 yGG knf|mBlHBGGGQQ%%)ymm%sB[ll_BHYYmxlXgn0sx))mx))mm0lY)Ymy/0H) xm0̷Pymyms?0sm)HYHHlPmYYYYGGH)YHHYYxx/nyyyy)mm/lx[[8Axm0my)HHPyym)))Y)mm)YlY0m0m0Y)yyl0mmyP)GY)x000mx>J_zHmmklzDDm)mGmHG%%l_0s__HHmm)0mmnN55fymy?/Y00xm0B11x000myOB0XmyP0m)0)GHl?))mmYHlYm0yymyy)m)ymm)Bl| O[z8=z[)s)0))mm)ymmy)k0k0y0OW$ss HNXyym2Xk)Gy)yymNP_ymy))*Y)sYslyymm[ x ?lAfmmxJs)%lymy))YNfHyyymmy)G0)0AYYLX8) kkq Qnq* Nm)G/XAB)0Y??HxYyymmYHAAlHY))GkkPH_0)Gyym)y)_yyymyyy)m)mHGyy/NxYH))yymmmm 1B0mm?l[lm )YYm/gmY)YHAkmyGiXYYym)H_HmklH__ssH)QQyQml/l//1xBlH_BBlsHm0B?mmY)0mm0Y[xlxym_l_lmY/0Y)?m0Hlk_qHG))HYHkAP)GYHYYx0m*Pr2mmYm[BmyBzY)xYHkyymHlHl)Ym)yY%HHPHmm0yyl0[00m00y01>#DDx  0Y)QHamxxyHkmymGmyGm0ml= 0xmmP_H0B/)0lmkyymymm)y))mYHH0lBm))YYYYkYm))ymPyyY)YmGGYBl/Nz8Bl8X0l̷_))lGPG)Hl)G))k)yT[xx) A)mN?ymNXPmy)yyyymy0y))_HPy)_kmm)mHYY_l))mY)0lYml_Hyymm)sslBg)mmm1B)y0ym)mx??ymykXx8Nlxy%H*yyGHX/N|/??slYx)xm0mmHHHYHH_H))PGGGkG*yymy_XXHyymym)q_Ymlyy)mmzs%l)mymlz1ssmAgyY?As%B%0)X))lY))mG{NXG%Y))kmy)sHm)GQGQQQ _lBlBY)0-NiX8x%)xlY0)mlsm0lBB[l/P)Y)00sm0yyym)m)y0yyyy))mm)HQ*%mm0m))kH)yxx)B_BHHGQyQY)ymHH00m0)0YlY)HkHgH)00yyml1Y0 00my EW#Dx$0mmmmQa G0 Xll)y)%mm)ykXnlHmmf//-x%x11z x0mll*O_)m0x0Hm)PYYGYHlx)lmmYYHll_HmyY)HHkHyyy)YXg/HN8[8?)sl0Hklk)ymPHGy0mkkmyy`$$0xmxgA)y)yHHml*B)Ymm))ylP)0))_l0Hmm)))YkHyy)Y000mmYly0m ssY0lm BB0Bmy)))))0yPyHPyA/xmYNNlGmGH GGGy_Hy)lX=/A?lxymmYHY)Y)XXAmmYPHkkkklq)GQyy)mNgP_mfA_Ylyx)sxBlyyyyml[Bxss?l/)mmmYABm0Y_AY))lH)ym_/Lg0mYmy_mmmmx)x)0GQyQQQQg?/YmH__?l0NNNHm)mm00%lAm0s0msk)0x0Y[APm0m_llkymyHmm)yPlPymmymyymmG2*aQH0mmmm%mglyy_B)mm_HyyH)mm)myxH)kHP_myyyyB A/xx)xH) sƵxx E0000QGRGaRGG0%s1l%00mx)xGAX?sm%X*BGmml?B sx000B-/%0)mHyyyGyy)kmyk/km?xm)00l_l)llm0)myy0)yy))gl|0lN8/8?8NϷBYYlYAq)))GGyGmmmy)Yxm0)X)mY)yymmnlm0l))YYm)0y)m0)lAlAmY)YmY%H*m)yHmHYy)GykkB_B s B0lg?_x)xs0ymmm0_y0yyyyyysmxx00YBsmm)myxY0NsYy)N5Hf8Pm0BHG %ymmm_lY)k0y)y)A*kPH_y0)Ym0%YYB_Hymmlz xYym0Y[BxBl/Xg8X[l))PYymym%/_m)0BB00y0YY)000yQQQQ=xm)Y/A?H)X8N|fL0ymmm)AHyglsm0kH0m)BAsy)yYm%lBHkmymkkYH_0y))yyyHyyymmyymmyGHqqPQrsy0ym))?f?m0m0ly)yyGGQ_A_H)Bm0)00mBHB*qPY_)yy)kXl _1mxHm0ÿƌD1xx >ÿdxxyGIRaRqBs?J)00xs %s/8_YYB0nyymmBssxxx001kH/XHyyykYykm%mPY?Jm0)mmml0)m))_y)mY8NN8̶?HHkmkn_PymkPyymy_y))Pm$m0mlyym)Y*mkHyYg_HAm)mlgY)Xll/Y)YHP_ymHB|||)mqf|f[Bs0mNg_lsl Y8[m00)0)__08H)ymyyymx)xmBsmym)Ymm)A|YAXkPPXkYYymyAqyy0GHyyyGym)))yY))yyGPkk))0x0mHk)mymBl1Y1s0yyy)) Bs00zllHmm)Ym)myNgl)yy)mlY0mm00Hx)0m)GmmQQmH88|mm)%YXgH|NgHyQ00lHkl)myyY_%mGHGlXmm)_glYlPymYffHl)m))yyH_Hy0ymyyYkXkkq_Yyyy))%A%G_lyy)ymy_qyAgx0B_0mYmy*fkrkAq%)yyyym/H_0HyyyHx#>)m 4E x 0mG%GR*5/Yz˶0m)B%xm?N0xm*y)m0B/z x/8B%lHHyyymHll?%GPHk BJl0xlxY[Ym0000mym)Hm)mm)m_N8||8m0yHHyn|nP0m)mHyGyknqk)HHkHy0YY)ymml_ymyy/g?HH)nX/y0YlX//A_AYY yy_XHy)qBN80mmAXffgXzzs10lXlY0mlY0)Yx)l[8N)yy)Gyzx0x) l B)Yy_*_Y)))0mY8 55f*GkymlH_m/)_yyyy0P)m0m)_HyAAx))YGHPHkm)Y)Ym?YH)mBlll YlABY)xx[Bx_By))kymymmym)A/_)yymHYHB0m/lsYY)QQG)g880xGm%_HBAmmmGGy)HYmyxlY)))Y_)%XggHlll0)ynX_)llyymkHHPk)myyyHlk0yyG%kqyY)myqY)msl0Y|NX?k)Gy)yyy/BYY)8fmYs_H5f\X_HrGyyX_lmmy E">00 41 0 G%akѷ)zB)0mlBxYxYYXBHxyGkysH%zsY0xzsBl_/?xmyylnyl_ys0[XA)Ymlm?00000yYy)Hk)mYmHllgXx)YqlqfA0)))0GPlPyy)ylmP_kP)Yly)kllmy)mnN8H)HXymHm)0m/gXg8?Yx__0__yy_kQmymNXNNHfL8X A[YY/NO_x0m00)mABY|l)GG*Gymm00Yss00yy*ffgm0H|A)mf\mlYy0mYlmyyHYP)mlgY0YAY)XlyB_)sGGn0y0kBNNfNHGm)fNggBBY0)/NgBYB/sY8AmmmY)YlymyPHsm0)))yY0BY0mmmzH%mGQy))0)smx0mmGmnXH%0Y%Gm*m%s)myymm))m)yGkyl)BY))0G))))klPm)))yy))HlYm)ymymHHmmy)m)mH)%xmmxnNHH)%Yx0XNyl)mGyH0m0NAmBg*f55f\q*Pyy_nq*PyyyQyyGyy0>"">00 Eÿû1xGa H_nHy[)ymYsly nkqB x  lly*{YlmyyPlHyf{*)*klB mxm_g_Ymm1B0 %s)m0yml)0Y)0Hn*ykYyH)))0)mYkYyHmymy%*k*HmPlym|NNHy)ym)y)l/_HygYmmmmxY8O)lNg0q*GqkBHBmm-|0N0Hl)sggkyk*Y  ONNglm sO8z))0_fGH_B00m?xmmmBXYmy_llm))lgAx)yyyGkrl)syYY0y0ym?mB_ym)lg[[lsH?Hy0Ymm0*qkmmyyHNNmm_/gfglx0)llzsm0lYyPlG))lymymm)HHmymyyk000000YY00G%myY)xmxm1mxym))mym%)YHk0)xmQk)m%0yyyyym0)))mym))mYPlHY)m00ym)Ykkyy)YHHHmnmyG_)0ymHBkHYxxY)Pfnm)0m00YfgXyYmy|_z%0gslHXq\k*PHqkPPGQy)0 >4E>0x xd4ExG]kaHH*fNNqY_Hxmmm?Ymy\ 2*nXl/l_s1ss))yyyG?mmyPYHH)qfqG?rPslz m)Yx00BY0s[_Yx)YB)0k)myHmyY)mmyPl)0m)))HH?m)ymkHqyn5|*yy))yxmmPHyPnGHY_H)0gg)zgmX5*H{5k*2_HX)m?XmHnHBX*GH _B xz8[AXXXX/0B)Osg8l0)_N*yyBBmmxm/Amyyyymg/lY5XHmyGQG%sx)ym0y/yYlyymgX|AY0l*lg[0xylq*Hyy)mXNX|NyyHg8X/X8[s)l[8N8=zm0yyym))HH))ymymm0smymBH00 > 0%xxmmkm)HBB_1)x mGYkyGxGyH)HyQ0))ymm0)m)yY_HmyyHHPkHmy)mHHPkkk_Ymm)mmmmlm0)YPlBxG)yPqll)msxmm)0mHHmYXHHH000n]G?00YYlHNNq***yPkHHkyyymG00 >EE4U40  q*HlxG)mlxN)0(*P_l_yyGmGy)l0xs_Gy5G00%?1zxPPmB_sG0GYkyyyA0Y)mx0Y)Ymm)YH__Hm)YPHYPHH_Hym5nHkqGy))YHHHBl*Y\gHXkXXg/YYlNL5Nffq**lflyyl/H0|qH_sg_l[g YlY0z[|lBB00 0mq%mmBn0m%BXXYmy)xm0m?m0mmmm)mHg8g/)f?ms))mslBx00mmyY_my)yyyyyy0Xx0BN0qX2n k HlmmXNnHBxsgky*n{ 11 8ҒNgBx0xmmkmy)my0myymx0 [H0y)l*x0s">x 01xx0GH_HYmygABBx0xmYlBHABy0y)Hnnqkqyy00mmy)y00)HHHHkY__*_X)0y)m)mYHP)myyyyAy)0mmy0)YHmYm)QB01mx)GlYmm)0|fX)0y5q{/%0BmmX5˪v*HyHPqHPyy0mm)lY00xZD4E" xy*qHk0xmB)my))YsmgBHyq55ѲnY))myymyYGyBx00BY0YyH5NPm1mxrP1Bz P)mm/HBlB{YmyHXHyP))ym)mmmPHlYHy)))0)))PyyYPk0yyyy5Gyyyymy))mmY)fqQq{*{XgX?%)mXBnN-f85Xqq*lNXymY)mml|nHnqqllzz1_O[T[AA0sl88/lOB1msniX*HG_lBg)mm)xs0mm)HllmyA|mG)))_[?lyB)))yyy_?myyym/X_m|k5* q Ym)%BY8qGGGHssx z8[XgXЭB[s)YHmym))x1x_sXH_ m zDx0mm0mBX_xgl)m0y0llmYmAgnHm)GmlAggnkyyxx)0mmmymG)Ak)yHky)Yk_kllP))ymG)%H̷m)00y)mYAsm0?Bym0yGmm/X%)0HHyymY))lm0)mm)mG0)fNy)00HNˋ*B8YmXNXL5ff]q**kH*kk_0Q0yg_%00zŨ´DDD1xmxmg NnmG))yGG)BHY_OlYBf**q*mym)maGmyHmm0Y_)kBHX0yyl 0PPyyX__yyGPAkyyHqy00)GYHk)kY)mykk)ymyyyyHkPymGyymmyyyyAXmmyy_gA0m)f55NfqHqqq])X8X8sX)mX/m0gH*/?lBz zAgANOg?sYH%حNN=8=H[0)0k|%mGkkLymGmg0y0y)_l000)lYmm)0ssl 0llH0ymY_A000))0y)XXXg/HxHl?\n5N5q**nk8_Hx*/l0A8\ks 1 xl8B/AXYzY0)m)m)mmymm)yy0)O_0z  JŃx0Bl00_H8?y0A))_[XXk%)y?XA_YymYmmA̋_)m)mmYHXXnHqkym)|NPHlHymg|HmYY0))Y_yy)GmkBsl))mYGGyGmN8BHm))mmmmHgmmmxymNkBH)/X*|fNq** PkX_mmyxGm)m01z ø#c= 1s x XN8g[ONqmm)mm kkHkHlmA_smm{q*q*q_y))y0yyGGkGm00YxX0kGBxqnPYHPPPyYymym)myyyQm)HHlHm_ykgAlPyyyym)m)yyQyyymyy)yyyykGym%HYGm)BHYPNf85Nnn*P***qk)XgYlxBYmgԷXkBYzgXAgN|OBYxlA[[|[ gm 8/Yl-?YmknXmABymHnY_m00mmm00xY)m0m 00m_0yyyl0lG00GGGH_Ag%xm0[XlN55NfLqq*ԋNym_Hmy*g0B|nHnzlzls?Ol[NA_lsx0)Yyyyyymym)y sxYx//_Yml xŃ0m00[xky)mYsANNg/kHmx/8O:Xl0?mmylYyy)))H|Nn)mkymkgHkl0mmGgglY?_)m00)[)YHHmGGHmYP)))Gym_8XYHmmxmmmmyyy)_G0ym))Y)ANN-nkGm/NgqPN5Lkq*qHHykgBm00mHkmG_X))yl0 x [y)x :TOT|nmymmk_lH_H0xlBmx0y*qXGHyyy)GQyQP)y0mml)0Blm)00mm%NH)YȷPyyPPPHqkPyyymymmy P2Nf_Y)__yyAyYH*lk_YGmymyYYPkPPyyk)GyyGGyym_q)kyyBl0xmlmG)%HYYkl)HHGf8qHk_XG%[?xmxA00YAgxBslsX8gOXX/_H%_100)xYm0000s|m0ms%G)k qnmm*yG?NlB00000)ss)m0sxx100Y))))ymxH)mGarQ_g00m0)̷H|555fq ]*naX||/X)gmmXH*Xl _g[N|OggXYBYYYYY_H)yy)GPYyy xxx g|N_B[1JJ= 0mA)HY0xx0[s/nH_YlYmY/[Xl_Y)mOAAOggg__YmH/?HYX*)/Nk))yy)HHHymy)m))HHymmm))m))XgX[A/A?lm0m HHYYYHlPHyHm)G))Ymmm00m0mm_Xmm)mmm)HH|NmmmHNlmN*%**llGY/Y0m%yGkq)PGx 0 xxm0m0 WW$f)yrkkklHH0lz00HY)yXHHkkyyyyyQGGk*mm)m)mll_)m)0HkPPHHyyyG)G)yn*55n_Y)H_mkAH))ylkPGmGX_)H_HHnyy*yy)P)yy)yg)ky0H|fB)0ly)y_lHYAN?)BlPG|nPqXy0sB1xzH0?f/gXmx/mN|XlBlsl) ϨxxxxxxB)mOnlB_BYm]yy)y*GyHqYYl/s000Ys0BB0x000/Am sym00YGmY?[m0YYBYyGGarHsxm)HH5\N55kkq2k)/gsxlmkXHmgg_XX_ sBgg[?|NONOBllB_yymPHklPy/Azxxxz xxXB0l B B0000B/[xmx0x[B=8XnnnHlxxA_PAOOO`طm0HYgg?*kqGP_)y%HPHkHP_)HYPHll)y))mH))Hg?lHml_Y_)0mGHrHqlkP)G))))Ymym)yym)y_n0yx0m00B)0g?)Gmy8kHlqllllmG%GGHkPmylY)mQmmyQPyGlmGlPHPk)?ՑTTTfHYmyHq%*lx ضm1 sHBXHfnlHyyQyG*ryay0)x)YlkAmyyyy00xGNmmPnA*GmyyyyGPkmG|NfQQknn )YHYY*_yPmPPm)YPkYmmg||)Hk\yyHBmyHYmm)mH)Y)yyGy)/?)mx)lPyYYmHk)Hlk5_Y)lnHmmY/ssxly_YB? xYzxmg88?lkY%G0 10x[8xYsB[gz00mP*XkyyQ*rGmm)YBx00YBH0m0/0xs x0))000mY)xHBgx0_)mGaGaamsYlAHlmXfN|H*k*2Gy)[?xmx%/?yyO8|xHszYBBgg8XX[_slY)HlmymmGyg0x x//mB/B 00BA_x0xx=8NLmaHX)Ys8lA)̶sgO$``NN0YXl8g8-ыXY)H**qH_ԷYkHkl)y)Ylklk/Y00mH0)GHPrkk HHkkHm)y0kkHHkA_m0mmmm)mnXmm)m00m00Y0)sm)m*NXYHH*kH0k*kkHlPY0Y0y)yayGGkgGmY)HPG*qH%8T`OXNNfk))yHkqHq?)xx1 sYlm%)mqyQGqP* )_B))?mHyY%Yyyyyknk?ymy)%kQyyyya)))Pk))y)PPHPHlqHmmG))))HHnnfGmy0G)H0Yy)my)ykkmy)mls0Glm)yym)YHlGGyyGk))H)kQmy0?[x0_Y)Y)YX 0Y BB?_/qB|8flllY?H001x0xs0 x_[l B00z )0Hff8lGGGkyG%)YHlzA[/[/)000x0zبzBlsY0)_my0/AHm)m0)maaaaGQl/|Y*GiNrqmYsBxx_/k0AXgXxxxmXg8|XlBBlYmm)YHlX)yymyy)xm zBs[8))x0A100NN=1xY0xxxϕN8Y)mGm=80/AmYTOO[0?)X|N8N{l{Xm)mG)P*yy)Yyyymnkkmy)GG) rr kPkPk__GkG)mmm0m))yym)m))x00m0m0m)xY00myG)G m00x_)%B**mmHmyk)my)m)Gaym0*GyQQGg`OO?Xgyymy))yymmG))xzxxHmYyyy*PGrGrG0Bm))mHlGy)0))kP/glymmGqk%))yy0yyGGymmyyyyymG))y0G)mm))YHnXy)00HB0Ylxx0mxY0?lYm)y)k_lHy)m0yy0YYYllPmGykHGH*ymmJx00m_YmAxxs1Bms?_?|/HHllXGm00Bxx000YlB000x0 ssmGyarNB%Hnm))YPl g[_s)%%Y))X100mBG)0l[ mm)))mm0)aGaaaIGG%x0kkPP_)Gl)lnqHy%/lzssx?_y*sB mslxx/XN8_HY)HB?Ymm))Gmyk)mx) z ggmY0x00Bz00xmm zs[/)m[Bx|=8z0[l)Y [ҒYB0lN|f8*0Qmk/kmyn)myym)HPy)yynkGyYyyGGG*kq2r kHyGnNAyGyG))Y))ymmymym)mxm0mmxmmxm0mGyym))00y00H0m)yyymYm0yyyyG%mGQ0y%QGyQk2nO[YH00)m)ymmymG)YxY)mm)mGyGGyyy))?/x00mYyY2q8NHyyy00mmyy*mym)H?GQG_{ly)lmy)PyymmmPy0ymyy))YYYYHHkY_mfq_%mGlY0)l_B0[l/0_m_B))mP/_Hm)yymyyHHHHGqHAN5AYHkHaG)00l0m0mPy)llAx0xsm0B?*BLGH)mlxxzlxx0ml_YlYxmx[s xxP)QkXf_GGl*y00m))ls000mHY0x 000s0x)mmmBm)s0y)GGaaaayx00)mk))myl_)YYGHH)mm?s0?))HBx)BBYBB//8fB_B)))YYB_l_YH)m)yym)0xxBz8||/xmY_H)Y)10xs1x H0z/s00xmx)00msYB0mBsYB_H_mYYy0nXXHx*HH)y0)y)GYGymHkyy)mlnGkHyyyy)ykHkkyy)GyqqSq k k)m)BXmy))Y_m)mm)mymYxm0xmxmx)Yml_ymmymG0)m0ym%00mAGGyyyyYY0mBBG0yym00BkyQv ϶zxsBYmy0)mymHk_Bl 100)Gx)HH)*kklkHGGHGylggN00AnnmG-5? /G00y)yYf/Y)0y%k mmq{XXmmykXkGyy))HPllyyyyyy))YYHY)mHn_lHlHlHNmmB%m_YBxBx0B1%?mmyYYmyym)mmQkPmHHYGqHymm)Bz0HX00s0mYgNff8GH_xkly)Ymlz000?Bl0xx0xx)mmyymHBs0YHymy))m)PHmx8l0GmyyQyHYms0000m00m)y0Y/H0)0)Ymmy)GaaaymHPPkl))GylA)GYyG0xxx0llmmxssxH?%??GX|/kYHY_k0y0%)BYH*GQym)xm1szYxlgm0)l/X/B000011x 1lYl/xxm0x?Bmm s1xz HH _mym$ xgYBHmm00mH0)BYkHYyy))Gkmymy_Ngkkyyy*qHmmyGQkS\Xrkk nH0H?gY)GYYNX))mm))k*ym)xm000m0mYx?gXyslGYxYl))GXHHmy)GYxylxQQsxB)a5{yQQRF҃ [m0myym)kl_?l0ABmmmmm k)gkmHGlBm0gnq52*sl/J_yy0_lY0y0mmGlHg˷y)myH_HkkyyyYYYYl0ymy))lHHHl0lkYkYqYy_N)/y?g8xs0[l00%l0myyyymymmYHlkyyq*XqGG*m00YY0)myGyyxXOH000m0mlHHgkBxs*mlym0 ?lsxlYyy0mYx lxymy))mYsYmmkyym))YPk_m[|my)mQmY[_x0m00x0mmxY)mm)m)ymmGaayGymY)0YHHkyGPlAN/YkklkG)0z00mmkHl0xBsm0sA?lXfx)?B)HYyyy)HlYYYlkk*ym0s0x_PPlx̶?BG)00l/ 0 //lxB_B)myxmm01Aq*GGl//lYHYkP%m)lsmYHYY)yyy))Y%0mHHBHm0ymG)HyHyyyy))_YYHHyy)yy)ynXGmmGyG5  qqN%m0 O yymx%llBmm)yym)ym)m00sz)0)mx)m)g?%yHAsHHkG%_l0G2lG_X_GyBm)yAY0)GG%Hm0XN\2a3^yxxxJOsYqkP)))yyGP)lYYYHHy)8/H)yy)H*f_mmyy nL{8Nn*%G0))yyyyl)0)Y0yyHGQqfl0m)AyYq)ymymmYYlkym)m)ymHYHlm0yHBAB/)BBYYx?m 10BX0ymy)yH?)mHyymyYng*HlyLgXHG*q*inHBm0Y)AGymmm%_?Y)xmHXHm))%z)_)yHAlsm__ym0xx0/[_xmY)yYBs0kYm))lk)Hl_8XGQQyHY[s0xmmmmmmm))y)HY_z)))yyQyyyymmmrN?gmHHGk*k)ymBlsmm%HXm0m)HN|fX)?mYy0mHH))HHnыQ2001/gl[xl//lHl00z?x0x?l00%_B)Y)0mm0)000P%Yzz%xYYHHkG[sxmmmGYlmyym)?x)mmYBYHHm))m))Pymymmy0)Hlmy))yGHqn*rB)Gyyk k{L|xY8lyy)1Yx%lY)xymmyGHym)%lsHz[xxmmm)_*)XlmmYGHH)mH_lAQkyl 0mlmGrY0mmmmq|5*vFFF(mxs101O|lqymYyyyYmY|nm)YYBkY*NN˷my0yNyq*q /8Hl/0GkG_myx00mm*y*m0q)myy)0)lkm)ymy)H_0mym))xm0YY8/0m[1xlx01Y))mHnN0Hy)*NN_GllGGN5fNN_G*qnqN/?)mg|BkH/m)HlHYY_/YYGx0)H_BHY)mBlBs B0)y0m00A|O[lH)m)sxxxHyym)mm)PklQyyyy*BOxxxm0mxm)m)YB?_m0mmmmy0myyyy)yyyy))YHHky*XHGHkmm0Yx0m0mmy0/8B00mmym_YHg˷lHsl0ym)my))0)))YHknXqQ m00xY0[nAk?H%JH)ymBs)00xH0mm?k)GGymyxx*Gy0)mkl2%r?x00l*_ym_zY00kH_l)_Am))H))ym))YHkHll_)m)yy{_%mGmkkNnG*5?BB18 mB?xJ /Bm0mmymyBs)mmm)Yk8g?)mH Hk*5km*ynyllxm0ymm))yra0ym%L5Se^bF3F0x0Ym_0k* GGyyyHmykm)_H0YH0)my)_P5fG */X??mH0yPHm_H0Ym0mrqPGm_B_Hymmmm)x))YYkHkmm)))YYHlm0mmy)))y)?H0%mmmm?08JB1%%mmYH)y%l%0Hkm_lY)fqqkG]q{Ng8mNHYYYmԷmY/YHym0YYlBH)_PyyHY%)y x0[0m)0BY00kymGmmymGHkk) *GQG)0Q)H00Hlsl0xmmm)))Hl[Y0xY)mymmy00yP-%B_H\5\gHa *q*{H_00x/G))y0mm%_?Y)myPPym%)0%yymyyGkrrn]H000s)A5y0 lHH)_YxmlllH%_XG)yyrPkY0x00mYPrrIG%%m0HmyymmxmBklY)glmGYlkmkmyy)G))GHHH_H)yl))m_*XmXfGr]5?/B=As)0x0myy)GGHB򨶨zm0mx)B)k_mymXNg*kHkk*Xmkrmllmm0m_l0)mGarHGm?l055< 7 <55?0mmY0BmyyyyQkGy)HlXBm0mmmy)mm\5Q\\ *-8NN8Bm%HymmPHkX_AB)0H)5qnPaGkAY_*kHHly)HHYYHllYmHBB_)0)))mG_m0?B/̷Ym0x=)))ly)GG00kHyGyy)mQm fkkk*{G8_ON/0H__kHlkmHAlm_Xl)m0)YHHm0)0m0mq)m00x00)llHkyG0ml1x0_G))YGHk{?%mQ0y0mx[ب0)mm))))xX[Ym))%mmmm)Byq|mklmr55f|ˋ*qS5/˷Y)8_lklH*m)H?YnHPyy0mBl%yGP)G))PHqn2\\/)YH8fmm0xBxH*lHHHm%r**nng*Yy0yyGmyaR*q)aIaaraIaG%QaaQyym)mmy{gBm)0Y Bs000mym0)yymm)Y/X0qY)xm101̶HGm0/{Q/X8/lnXAx tjj xmxmm)mm0𶶨sm0mYm0xxxmsBY/nGkH rrn00m?f8Yllxm0xl0BmGG-$s zXqqqnXXXAY)m0m0/yB_0m00y00Y0)rы{X̶1s Okm0A/ymN\55Lq * B88fm0BlYmqYHmmX?s[Pkq GQYnrHAOBAm8fQQQ)lx)0000000ykXYY)Ym0-=̕ϕYm)mx0BY0xHYm0ml 0BB0)BklY000mn5]iL*]r*8ϭ8*kQP*Hy*)Ylz00[01)0xmB/{_lB*k)))Ym xHm1xxm0x0m0yl qn2qq%GQy*yyGyGX_y))*nkk mmmm_H))x*/[0x0mmGYYYlYy0mmmm)QP0mm%_H)H)yy%-{/{)yH*]{Gng[x |gklkGXGHYx_AYY)m)G)ss?*XX] ]]PG?Bx)%5f ymy01BqHkH_G kqH2Hyymy0G l)yaRaIraaGGm0Qyaym)yyGGy%Xkm0z=B/ 0m0)m00mGyyyy_*Y)yrx)x 1 xB?)0)B)mG_XiHm jj@@@x00))A)xB/8s x0YBxYlmxBl%Ni*H r * 80lHB*?Y[xxYmBY?%GHQ$AAkqqngXBls00xky)/%Yy0Y_yG\NNB00000%Ym01/)mmGk5\f\(n* qz[/NN_0)%A8H *lGxzmy*n*ryGxXXrrr/XglHs*_%QQxk00xB?Bsx0xx0)mGl000YHYB?00mmY??[B0lmmszm))ygBx)y5{_**]a5NA)qqyԲ)ksm0m0ml/x00Yg8_yll)y 00s0xx0xmmx0HgS5ky)HymknqXmyGykxm0Hlm)sm)mx%z100mnkq??0gXH0xx0k))mmm))m55Lkll%*2{N|0NmHkyP*kHHGY[B0XHH00m)0B?%mn  ]2Gx%mHXы0m[_NqHkkkkk *qnHm)HmrIak**rrPrIIaR0GGIraQQyQkYyyYGm_x0mmxY8zz|Y)0)mmmm/)/A)mk00 %sYmm)m00?x%y%?_Xm00Jll*nl[BYmx/sm1sB){* HHH k*H_Alx)0gl0 00mx8yGGY0xmy_BYBsxlAs xxymm/AY lYY)00yl_Gy?NNx 000ygmm[)))l{N5\*k*Bg/HYY00Yxl/lmk8/%YkBz?[gmlN*GGa[gXN*kYnnQyy0yQ?)XXXAYsmmGXBBxYxyyYB0xYYx0l/8 zgmmx[smslB)xAf5g8{)H*rQNOl[NNGGkXkmYl ))xlY)0m_*l mxx0xyxxxmz0xxmgNNNf{G*ykX*yH2nXyyymQG)l%x)mmmmx0?qr*HkH5YYm0_Y0lkH_0m0552nq2 Gq8B8NBH0m_B_Bmx/mY0ms[l0_/kG nLqaRv*0xxBxHyyG08/GA55{klkkkk**q5i?lm)y)PraaGPPHrIrRrraaIaG%GayGYYmyy)m00Ol|mym)y0)0mmQyGy%HHHGYAA))0kYxx)l0mm B?Bm%Q)%Y/Ysk{ gnX/Xn/ql_?l_AxH 8?slxNflyQ% r q*m)lAlHsYm0m[sH00lmmym * yHHHmm00gz[_HBl0x[_Aly0Gm?s0 0 00)0?msOBm)/Gk5iH GGyQ0lB0))000m)|/0%q/z gfH)_HqGGk5A?s/mH*2GQyyQyHG?g_lx0yy0Ys0ymHsYxxB?xBx0l_m)xxf_s0mA_5N8k00YkG)/N//ymQyGym)))YB0Yx)XAAzY)sym)Ym0YmxyBxxm0xms*/n5iHqG{yynn̋mmyGmymakXX)%llBkYl000msx00YA*HN)y5nX/x0my%%?_))00X)Yy*\qqaQ5N?Nym2yGk)klY%xxxxl?m0Bglm/8nkXnraaa*qmxxY0%)*SqBYxnX G 2{*kBHkq*fnlm0GrrraIIIaRrIrrraIaGRGaIaQyyQy%myyyy0yy)Gmy0g/gXmmmmm)Gm)yaGymG_mxl/X/ %YBmxz)GGQkgmfN\gg\2nnnn*qgXnAXA)B8ss))mG_g)mmkBYB Hlm)0sg[AYslBmyGGyGkmBlmxmmmx 0m0m0?ϭO=Tg[?00x00) Hmym%1 > mx))?N?BlOAm)Gkk%mQQmB[)xB)xm0mm0m)%gf8sx/l?H)mG%HG8X/g|HH *PqQ)mQQHffX|NYkXyQGH00xx)myy)_mxx1g00zgx0zO?mxml%)X_B%0xA_?sss?x1O/xY0m)GGx0000000mgggn)mGGGkm)0Ylxmys? 0xsx000xx00sN_kmyqyGq**kHy)Ymy)ymNX/AxlHm%B%*q{nXa5l??x/As)YlYmY[g?YB_N58/G) **PyN8?NNG0HXSm)?B0mBY$Yx00%lllk_*r(iSaaaa*k x>>)0YGqq{ y /%YYl%GS5({Xq l H*vq X?HmPrraaRarrrRaIPaMoaIaaaRaaIGQQQ)00yymymmyyXsY0yH)m)mGGGGP HG%mYg ))Yl%m_X_m xxxymG_0mx)Hf\nXnnAklf8fX/g-/xBJHGm_)mm)%H*{*G)00lsm))my0mmxA0mYHmyQym% r*)Y_0xm)x)mxm00BOOOҒN 000)mx0x > > xsx%m_N?xgmm*y0HY)0yyy)l?mms0m0)YYBx0xlXX*Bx0 l_Ym%lHGqNg)GS*y22aQyQyy*q*H)kymm0xx)m)Yl x0xxs00B=s0m[8AGmn_BYm1?ly0zBYY)G%ln?0m 0)GG%00000BglmGmGGG))_m0x00mymzxxm0x/?B0mkGkP2nGGGm%0)0)Gyy|/?B00yx/xkn2*GG *arqN8A)/?xY8-l?0xH_f5f|HmyrHGmN[?g/yyymGmyGmGHY0Y/A[llYY)00m_?Hq*IaaI{l1> myG2q]rnk%GGG 5fL0y%k]qrQBm0maaRIIIRIrIaaIaaraaIaaaaaaaH)G))myAfllNfm)GaRrRGGGHXA0?N8Yx)0_AlBx% 0x%G%rXn00խ/*%ff8Xgg(XHByHqPyYBJ8YBGH0%Bmml/m)m10mxxmm)0)018m000Y00xYyyym q*]q**g/0mmx)xmm00ҒNO )[Y0x00000myGG00 9E>">0xYx)mlN_[g8myH*H)_YYYkHPr*8=_mmmmY/m0?glx/lH_l_lGrGXX_xaar2kP22raQQyaQaGQQyQaam00xyyGQyakH001Bl%mG/z|zmmymB/N8*%G*_0%BzY)mGl?B00xY)0)Yslx00z_mmy{8_qPraaarGQvXN??x00QQQYYxxxl?=xx))GHqqn*q˲mymGm)Y)_kmHf55l%B?m0myy%{kQ\Sqa G aaaQrnqy))0?X?m)8̷B)0mYzg?̜s%xHm?)sg[m%yy))xm000000mH8AYm)yGyrԡrqraIa**1>>))yGni]rG m0GGaRq]XN0mki*vqRG%lY0xarraIRrIaIarIIaaRRaIrGaPaaaaaGakGmHQ[H8Y%mGGaaaaag%mX8H0mm)?_)%m xm))nXHHy%i-8g5NfmQB| 1x0)myyH/xmxHg/l0Yzx0x)ym)ms8mxmxm)HQy%**]*q2?m0)xmxx)mx0BY $x0x0g 00m0mm0m)00 E4E" 1xxm)%8ss8lyGrkHYYm)YkkP_/-NN|/*m0)GB//%l?HB)BB|HB_llkkHrGGa)G%l_xmav(]Qyyyrr a PrrPrPGGaGaIRqYm0 arayYl0xmmG%A?=8zxs8mm%myBNNG0Gx_?Hx)GGY?xYs0m0)HB|xB8BGm*** aGaaaGAg?)0BGGxxmmm?/%xm)mG\nGy)m)H)kGHGmB%0mymyyGGGkq Gy2aaRaarm0/̕n/{_llYx?z/m0Bzx%%G)%/_xx0x)Gy%000xxBgglA%m))GGG) *rarl* GQʪer]5*GGRGr]*]kH*G0Glnqqv Ga %)aRRaRrIaIIIIaaIIIaRIaaIIaIaaaRGG %mQQaB?0yGIaaIIaRRq)GGxl)x0GH%l0000 xx%%RGnl)mlq{-nGmmHYsyymyyyXs)0yQmmH?A%m00?)mmx?szxxxym)m/)xxYxmyym q]qHGl)xmxHmm)m km0Gm0mmYx00/l0xxmm0m0)my xx s0))%X_)0ANkky0mykrrHkX/*)0G)GGB%[gBYYYH)G|XH_lkk*aGBYsyyaRIIIq\ qaQQaRrRRarRaaaRaaIIaaakH)mGGaIaGyH00mGGaGA8xx|BQGymL8nnSaa nnNN/l)GraY?Yx/B?%GmGGG8s [8X%yG*L2r aGaaaaaRaanN_)G* IRRal)mmX/n/0O/Y%mf5L/qaGGyy)m0mGHakB?x0m0Gm aQ*qGaarraaaaRK aQmm)X*%GGB0BxmGzA 00xY)yY?000zmmmyNkrRaRGayQrvrr**%%lGQr\(vor2f*aqvqGy0) qq]*eaaGBmmIRIaRRRIIRRIaIIIRIRaIaIaIIaaaaGGQyG%)QQG GsBlxmmRaaaaIaIaIaG*XfYH)xx%l?000 0 0x%GGrrX8Y))m )*nmy)gs%)0yyyQyyxxmyY[Bxx0m00xB Yz_Yx)))myg0Ym)YGQqqq]k0y)m_[B)%*yym Y00xB1xx0mxm0) Ed x10G%)Hl)x0Y_Y k))yPrr /i/00HGBXHAg88XX_)q*?kkkrkaRr_)mmIPaaaQaaRIaIaIRraaaIIaIaYGaGaam0B?yyGGaGm_O/1zGGGGGQkGmyaG*{N)y0Ga)??s/BB*mGGGn8/8zxn GG%XfLGaRaaaaaqNg_mmyraaaa/qyl%gB=?_0G%G GGmQyaGG)lmmGyyG PaGkn//%0m)yPGrrQyGaIaIaaIaraQQyG)0GNNS)yG)?gY1%0GGY%xzsm00y)_8x0l8H)my*q_*HrIaGaqav*]q yQYve2eoev22\552GarRGaG0yyG rraaaa%HGarRRRaIIIaIaaaaRaaIaaaaaIaayym0QQrRa%%00%xyaaaaIIRIaan5q* Hxxx*-Bx0mxx00%mGIa_BYaPm%z)0mymYH?YmmyQyy%Y0yQm)H0m0y00Ax0Ysmm0)mm)[sYHyQyQQG **Nf*x{HGn QyymlHYmYAYxmx0mm m))0xd 0xmm0Gmm%yyrrHrkq*GPrGmmGffHslG5nnHkkraaG GymQyIIRIGayQQaaaIraRIIIaIaaIaaaIaaaGaGaaRaRaaGGG))8yyaaaaxLB|-aGa)ymGmGIIaraRmnyyyRrRmk/Hl?m)GGGrB/NGaGakGQyGaRR HYIRaG**2q mG%%?JsGyGQymmy0aaaPq*GmymGaaa*nG0y)GaGaGyQyaaaaaaaIoraQQQQyQmyQNnaanX5NB?GaqYx[zz_YGmG)88O=088XG*f2r RaaarqRQv*v*]raR]oeea  raaIaIRGa%yyyyaGaRaaaaaGq aRRrIIRIaIIIraIIaRRIIIIaIIaIaIaaGGmyQQarGB%0GIaIaIaIIIaXmGG k%))%m???x0%zx0mmRRPaYBsY)PrrP))yym)Bm0yyy)%0ymmm_x00m)0/[?smmmG)mymNglxx)mYPyQyyGGGrNNY)B%m%q{rQyyyH0)yyyg?sx mxy00)m §4m x0m%mykH *rr%?%%xkk%/x0krXmHHSraaGG)myIIIryGarMIIrIIIIRIaaaaIaaaIaaaGGGaaaaaamGm0H_GPIaaGsaGaGQGHGaaraaaGG)))GGaRRmGX?0mmGrG)B/xGGGQQyGGIaaaRRrRPP%%m)GPRIRaRG*qG0%x0liHGRymG)m%rraakymGyGaGaGrq0mqQRaaGmGGaaaIaaaIaIroQQQQyG )yGRaqqXxymaras[/B̋)Gn8=[x8GG 5Lraaaavav*]raaaRqoeooeooraQaaIIaIaaaaGQaaIaIIIaaaGaaraIrRIaIRaIaaaRaGaIaaaaaaRGGGmymyyGrRPar_BsmmaaarIRIrraGG2Gm0QQ%l?N10xmRarRa0GYGmRrrGXmyyyHH)yQyyyymQQyQy)mmmmyy)8_0mmyGyQ2HYY*nQQQ))Q5NXmHqqn2Qyy__0Q?smm0m_yBD1 xx)) arGGyyyrr kHkkrrkkxx0xBnGrP XA%1ayr\5NNNq)G{aIaRyaIaar)yy)aRrRIaaaaRraaaaIaaaGaGaaaaaaaaaRGaGGl%m))aaaaG_s)HyaaaaQGY)0GaRaaR%HY)GRGaraGB_Ymrar?/̶H0Gaa%Gm)GaGara%l%%GaaRIaararRGaG)YBmyH*kGaa%)mRaaaGymyGaRaaaGGGyGHGGa%m)mmGaaaaaIaaMQQmyQQ0G)maIaaraarnA)yGIra /k)xGGk%zO[?lGar]rGm0ayQaRraIrorrMaaaaoooMrMIaIaaaIaaIIaIaaGaGIaaaaaRIaaaaaRrRRaIRaaaIaGaIIaIaIaaaaaaaaGG))yRRaPGGB% mQaRaaaaIaIIRaaa*yyGPG?0m%m%rrIarrmy%))myaGIaaaQyyyHXYyymyyyyQQy)YGmQyQyHHmmmyyx_lY)0ymym)xm0mGQyGynxG)l*rQQQQG)yQB/zm0x)H_0x x mxGG*GGPrk HaarHxx)xYk)PaaHrHB%kXfmHf5qrRaaaaIrrrI)rrrrrrRraRPaaaaaaaaaaaaaaaaIaaRGmm__ yyaraIaaYm0GaGaaGGG)YmyRrGG?)0rIaRaQyxs))%PaaG%[[lsyayGGGQmGarRRrRYHByGrrRaRaaaaaraa)%l?lmx)aaayy0raGyQyyQyaaaRrPyyyyG*kaGaGQmm0)amaIaaaaaoGGmQQmG)))GaRarar))mxGGaGa[00GGGaGGGs ymGGGGGmRQQaQaaaQaIaaaIIaIaIRIIIaaRIIIIIaIIIaaaaaraaIIIaaRaaIIIrrrRPrrIaIIPIGRaaPGaRGGm)yyarrPaGaYaaaaaGyGGX[aGGmm)m)yGGQymmQyGmGm%%yGa)nqyyQyyGyGyyyyGGHmm0yyG)xmm)yyyy0mxxyyQyy)GmyyyGm0G*kGQymmGyymsxmPGGmmm00m)yaaa PyyaGrmGGGaPPHGxY1xYGGQarGJz1yyy2kmH)maaaaaaaaGQG)aaGaGGGaGaaaaaaaaaaaaaaaaaaGGY)0NNXGIaaG?B0yaQ))00m0yGaaGa%GzB)0mGRGaaam)BmmGarRIGXBmy0y))yyyaRHa%BsmGraaIrrrrrraH)YlGyarraaHmmIIaQGaaRIaaaaaayQ GGmGmy000xGIaaaaaGaaaa x0yQG%YmaGIRarHYxx%%aImHl_B))rrar?//HGayaGG x)mmGayGaaaaaaaaaIaaIIIaaaIIIaIIIaaIIa}IaaIRaaRaaIaaaIaaIaRaRaaIrrraaGaQQyQQQQayGmymyQQaYmyQaQaaaQQQyGH_PHQm%Y0mGYyQaaMrrPyQyQG yyXPPrrrQQyyGyyyQrG*Hm))yQQHkkHmyykyHx)0ykqq2qPmyXn-00GmG PrrHayQy)Y00)ayGk000GQQykqX22*HHnn5\?Bf5f55\nrPXXJB ykn)m))yQQQQQyQQQQyyGHGmQQQQQQQyyQyyQyQQQaayGlmN|NflyQQklaQyQG)yaaaQllaaaay)?Bm)GRQH/ myQayymmGmQmyQmGmyaaaaaaaIar%YmaQQH maaaaayGQaaaayyyGayyQyQQm00mGGaaaaaaaaaaIr]qH0xG))mIaIRRy_0G%RaraGammsGm%GPraaa%sHmaGGaa00mmaGaaaaaaaaaaIoooeIIII}}III,ewww_q^^vkk^KwoIaIoaaaaaaaaaaaIaaaQGGyrraIraIMoMIoooIaaax)Ira,eeeoeaaPR rYBn RaaaaRrrrraQIeIeoQQQop GyQIooQqmmayaw,oeeowGreem)aa eeaavp^verGQeee~CFCQpppIIwC~CCC]2^oMp(C^pFC//뷋?nnAn2PqkHYyQ]ovrRrGaKvK** qvRaIaaaQQQQIrMoaaQQQQaaaaaIaa000ygYn˕NyyG%B%pKaaRoMrIaaQQQIrMvaaaQaaaaraaevp^p^~wQyyyyQyIRIMIrrQQQQaIIaaaaaaaaIIIaaaaeeMaaaowveeaaarerRraQQQv22{q* QQQQaRQyH)yQGGGyyGGaGaBB)yImGm)Bx)GRRG){lkyyQmmyQyaaaaaaaaaaaaaaaCCCCCCCC(~(C(C(~(~(C(~CCCCCCCCC~C~CC(CC.~~Cqppkrekk]~C(C(~CC(CC(CCC222v]v]]]22HyQaP v222^2(((CCC(((((]raM^C(FC2qHmmxQ^2C(~(~FCCFFFCCC2S(?nS22*2^^^2*rQ2((CC(Cae(((CCC~(p(aQ2F((C(nrG2^^CC^.n r(iFF(lGl]{]qGev^^2FC(F(F^(( rnC^CCo(^^^C(C(~C(CC~CC(2]ovvvCFC~^BA{*qAq*_lky^^C2^n]2(C]v*qq*v*v22v2]2^2^2^2^2^22]22222^2v]vvvvvv^^^2^R)YYXYXNy)GmGG%%xwS^2222^22v^2^^^(^^2^^2]22vv]v^2S]]]^CCCCC(^oPrHaamyyQyyyPv2^^p^^^2v22vvv2^^^^^^^^^^^^^^^^^^^^^^CC^^^C^^^^^^vv]^^2^^^]vv]]2^^^2{Sq{**qrGPPrmyyyyGaBlyQaQyGQ0BmGaQyQ̶%yyQmy))GGaGaaaaaPrRaaaaaIaIa~~~~~C~C~~~~CC~C~~C~~C~C~~CCC~~CC~CCFC(~~Cq*v(n{{?HC~.CC~~CCC~~~~~C~CC~.~~.~....~....~CCC~C~C.~~...C~C~~~~~~~~~C~~~~~~~~CCC~C(C(.~~~~CvRyG^(C(~C~~~~~C~CF~C(~eya22qqq2H0x x 0I.~.~~.....^C~~~~....~(pIe~C..~~(C^IaQQ(FC~~~(C(/JJ/ --.~~CCFC.(~~C~~~.....~~C~C~C(C~CCC(CC~CF~CCC~~~~C~~CC~~~.~.~wC~CC~(CCSi(SiSq?HS((CC~.~......~~~~~~~~CC.~~.~~~..........~.......~.......~~~..CCCCCCCC~~CC~~~~  "9>D>Bsxxx xx w.~~CC~~CC~~CCCCCC~CC~CCCCCCC.C~~~CC.C.p~~CCCCCCC.C~.....^~vp^^^(C~CC((~C^FCCCCCCCCCCCC.~~~CC.CC~.C.CC~CCCC~~~~~~CC.CCC.C~CCCCCCCCCCCCCCCCCCCCCCCCCCC.CCCC~~...C.CCC~~C~C.~~.~~.C.CC~poowvIaorrIaQQQQIRP%IraaQQaHkGyQGHyyyQQQGaarrRIIIIee~~~~~~~~~C~~~~~~~~~C~~~~C~C~C~~~~~~^ppwC~CCpqq^CGGxmmm.~CCCCCCCCCCCCCC~C...~~........CCCCCC~C.~...~~~~.~~~C~~C~~C~C~~~C~~C~~~CCCCCCC~C~C^~(^ry^CC(C~C~~~C~~~~C~C(r2nvq2q2q0000 0F~......~~~...~.~~CC.C.~~~C(^~..~~RCCC.~.CFC?vpCCC~~CCC(~C.~~~......CC(CC~~~C~~CCC~~~~~~~~~~C~C~~~~~..~~CCCCoGRr **q*_ RMRIICC...~~~~~~C~~~~~~~.C~~~~~~.............~~..~..~.~.CCCCCCC~~~~~~.~G>>>>x xxxxxxw.3C~C~~C..C.C..C.C.C.C.~~~~~CC.CC.CC..C~~CCCCCCC.........FCCCCCCCCCCCC^CC.w.CCCCCCCCCCCCCC.C~~C.CCC.C.~~~.~~~~C.C~~C~~~~~~~~CCCCCCCCCCCCCCCCC...C.C.CC.C.CCC...~~C~CCCC.~~~..C.C.~~CCC~CCCC(^]q]v]]2^^22vvSn2q^]K^*Hr*GRrev]vqkPk222S{{]*]22222^^^^^^^^C^CC^C^^CCC..~......~.~.~~~~..~~.~...CCCCoIo^q20 >x}(..~~.C.~....~.~~........~.....~.....................~~.~~~~....C^CCCC~~...~..~~~~~Coy222vSq/>>> > (........~.........~....~.~~pCC..C(D@@@§E9"E""—JSii(~F~~CC.....7......~.~.~~.~.~.~.~~~....~.~..............CCCCC~x11> >x1 %%C~~~~C~C~~...........................~..~.~.~....C.....~.......dd"1>> ~~~...............................p..........~~.~~C^o]]wvvvvvv*qqra...~.....~~....................................~.~.~C.C.~C~CC.C......................~.......~..~.C.~.C.C..~.~CCCCCCCC~CC~C.CCC~~C(Cp ) > 0 x HCC...CCCC~C~~C~C~CCCCCCC~C~CC~~CCCC.~~~~~....~~~~.~~.~~......~~~~wIIae2^^22]]%x > x I(..~~...~.~~~.~~~~......~.~.~..C.~.~~......~.~~~~~..~~~~~.......~~~C(.~C...~.~C~v2222{q{ >> (.~~~~.......~.~C~.......~(~~..~.CC@@—j@@@D"""%%]CC~C.......~~~~~.~...~.~~~.~~.~....~~...~C~~~(p1s1111"11"11111>1G((~C~~CCC~C~~.~.......~~~~~..~~~~~~~~.C.~.~.~...............?%"EdEdE>껻>w~.............~.........................~.........~~~~~~CIoooororrr*aQ....C......~~~.~......~.....~....~........~....~~~~~~~.CC~~C~CC......................~~.~~.......C.~.~~~~.C~~C~Cp~C^CCCCCC~.~~C.~C(..~.CC^m > > xx x > GrCF....CCCCCC.C.C~CCCCCCCCCCC~~CC.C~~~~..~~~.~....~.~..~~~~~.~.~~.CCCwIIaS2S2S222x o(...~.~~....~~~~C~C~~CC~~~C~CCCC~~~~~~C~~~~~~..~~~~~CC~FFC~F~CC.................~~~Cnnn22{A%> > >Ii~~C~~CFCCC~~CC~~CC~~~~.~CCC~~(C........›@4@@j@E4E9Λ/~~~.~C.C...~...........~......~~~~~~CCC~~.~CC~.311s111111"111"11"xHCC.~~~~~~~~~~~~~~~C~C~CCCʾC~C~~C~CCCCCCC~CCCCC~CCCCCCC~C~~~~C.C.C.~.~Ļdd">"p(~~CC~~~~~~C..C.C.~~~~~~~~.~~.~~~~~C~~~~~..~..C.C~CC~C~C~~pMrooKwKooMroK]ao~~~~~CC~~C~~~~~~~C~~~C..~C.C~~~~C.~~~C~~C.C~~~~~...~.~.~...~.~~..~~~~~~~C~..C.CCCCCC~~C~~CCCCCC~CFCC~C~CCCCCCC.....~.C..C........~C...~C^C)xx 0 x xxx 0 0 > >>%CC.......~..........~....C..C.C..~......~..~....~..~.......~~..~...~~~~CMIIaa]2222n2* >> >xK(..~.....~.~..~~~C~~~~C~~~~~C~C~CCC~~~~~~~C~~~~~~~~.~~~~~~~~~~~~..~..........~..~^;nnq2{S > > C~~~~~~~~~~~~~~.~~~C~CC~~~CC..~~~C.....~—jD@@j@4Bev~~~~~.~~C...~.......~..~~~~~~~~~~~CC~~..~F111"1"11"1111111(.~C.~~~C~~~~~~~~~~~~~~C~C~CC~~~CCCCCC~CCCCCCCCCCCC~~~~C~~~.C.~.~.~{%dC~CC.~~~~.~~~~~C.~~C.~~~~C~~C~~~~~~~CCC~.~....~.C~~CCC~C~~C~^IaoMrwoooooRI2(2KrKC.~C.~.C.C.~~~C~~~~~~.~~~~~~~~~~~~~~~~~~~~~~~~C..~~.....~~~.~.C.~...~~~~~~.CC.~CCCCCCC~~~CCCCCCCC~CCCCCCCCC~CCC...~.C..CC..........C~....~CC^H0xx x xxx x xxx>> %~..~.............~.C.C.~~~~..C..C..~..~~.~...~~~.~.~..~~~~~.~.~C~oIa{22n22S*xxxxC~......~~~~~~~...~~..~...~..~~.~..C~C~CC~C~~~C~C~C~~~~~~~~~~~~~~~~~~~~F~~~.~..~.~~~~.߱PnS2/G > >aC~~~...~~~~~~~~~~~~C~~~~~~~~~~~CC...~~~~~~~~@DD@@@ç4C~CCCC~~~~~~~~~~~~.~.~..~...~.~~~~~~~~~~~~~~.C~~~%s1 11s1%1s1%11111*......~.~..~.~....~~.~.~~~~~~~~.~.~.~............~CCC~CC~C~C~C.C~%""""^~~CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.CC.CCCCCCC~....~.pwIoeeooore^vaaICCC~CCCCCCCCCCCC~CCCCC~CCC~C~C~C~CC~CC~C~C~CCC~C~~.C.~.~.C.C~C..CCCCCCCCCCCCCCCCC.C~C.....~..C.C~.~~..~~..C.C........C.C..C.........C~....~~CC x x x x xx m) G%~C~.CCSCC~........~~~~~~~.C.~.~...C..C.~.~~~~~~.~.~.~..~~.~..~....~.~.CCoaIGR2Sn2n2S >xpC~C~...~.~..~....~~~~~~..~~..~~.~.~~.~...~~CCC~C~C~~~~~~~~~~~~~~~~~~~~~~~~.~~~~.~~~~~~~.~߱nnn{) ~~~~~~~~.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~—@DDD@@@@j@@4EE~C~~~~~C~~~~C~~~.~.~~~....~.....~~~~~~~~~~~~~~.~~C~%s111111%1111x11]~~~.~........~.~.~~.~~~~~~~~~~~~~~................CC~~~C~C~C~C~~C~{%">Ꝼ^C..CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC.C.CCCCCC~.....pewIweorIaaCCCCCCCCCCCCCCCCC~C~C~C~C~C~CC~C~C~~C~C~C~C~~~~C~.~.~.C...~~~C.CCC~CC~CCCCCCCCCCCC.~.C......~.C~~C.~.C~~.~.....~.~..~..C............~~..C~C~C^1xx x x x )m > CC..CCC^~..C..C...~.~~~~~.~..~.C......~.~~~~~~~~~~~~~~.~~.~~~~~~~~~~~~~~CoIIar22n2{ xxxxxpC~~~~C~~~.C.C.C.C~~.~.~...~~~.~....~~~~.~.~~.~.~.~.~.~.~~.~~.~~..~~~~~~~~~~~~~.~.~~~~~~~~~~ߘnn2{ >> > a(...~~~..~.~~.~.....~.~~~~~~~~~~C~~~~j@DD񧧞@@@@@@@4E"%S.~~~~.....~~~~~.~~~~~~~~~~.~~.~~~~~~~~.......~~~CC~~(~""111"111"1111]C~.C~CCC(C~...........~..~..~..~.....................~..~.......~%"껻1C~....C..C.C.~~..C.C.~..C....C..C.C..C.C.C..C..C.~.C~~C~~~.~~^eeorear r]\\RRQM.~...........~~.........................~.~.~.~~C.C~.CC.C.C~.C...C.~.~C..C.C.CC..C......~.~.~.~.~.~..~~..~....~~C~~~~~C~~C.CC..~C.CC..CCCCCp9x x x x x x> > GC~....C~C..C.C.CC~CCCCC~~.~C..~~~~.~~~.~~~~~~~~~~~~~~~~~~~~~~~.~.~~CIaa 222Sl xxxGxpC~CC~~C~~C~~C.CC.C~~~~~~.~.~.~...~~~~~~~~.~.~.~~..~.~....~...~..~~~~~~~~~~~~~.~~.~~~~~~~~~~~~.ߘ̡G> (~~~~~~~~~.~~.~..~~..~.~~~~~~~~~~~~~§DD@@EEEE"Gi~~~~~~~.~~.~..~~~~.~~~~~~~.~~~~~~~~~~~...~~~..~~C~C~~~C(1"""1"1111 {~C.~~(C(C~~~...~~...~~~~~~~~~~~~.~.................~...~.~.....~.~9p~3..~.~C.~.~~~.C..~~~~~.~C.C.~C.~.~.C........~.~C~C~CC~~~~~~Ceo^\Q q\^rRrRQ w..~~...~.~.~.................................~C.CC.CC.CCC.CCC.~~~.~~~.~~~~C.C..C.......~....~~~...~.~~......~~~~C.C.C.C.~C..C.~C~~..CC~CC^x xx x x x x >>> %q~C....~~~C~CCC~C~C~C~~CC..~~.~~~~..~~.~~~~~~.~~~~~~~~~~.~.~~~.~~~.~~~.~~poIIIa 22nS2/ x xxC.C~.C.~~~C.C.C.C.~~C~~~~~~~C~~~~~~~C~~~~~~~C~~~~~~~.~.~.~~...~.......~.~~~~~~~~~~~~~~~~~~~~~~~~~~~~.~..~e;Pn̡2G> >> a(~~~~~~~~~~~....~~~~...~.~~~..~~~~.~~@@4DE" S~~~..~.~.~~~.~~~~~~~.~~.~~~~~~.~~..~..~...~C~~{?%%%%%%%%%%% K~~.~~C~C~~~~~~C~C~~~~~~~~~~~~~~~~~~~~~C~~~C~~~C.~~~~C~C.C........~~...~.%껝1x1......~~.~~......~...~....................~......CCCC~~C~e\]QIK^]rIMRaaC~C~........~.................................~~C~.~C~.~..C..~...~..~........C~~~C~CC~CC~C.~~~CCC.C~C~~~.CC.C..C.C.C..C~C..C..C~CC.~CC~.~~9> xx x R{~~~.C.~.~~~~.~~~~~C~~C~~~C.~.C..~~~C.C~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.~~poIRan22n2n x %xC.C~C~C~~C.CCC.C.CC~~~~C~C~C~~C~C.C~~~~~~~~~~~~C~~~~.....~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.~~~~ߘi)> > >a(~~~~~~~~~~~~~~~~~~~~.~~~~~~~~~~~~~~~~~F@@4EEEEDD"ûS~.~~~~~~~~~~~~~~~~~~~.~~~~~~~~~~~~~~~~.~....~~(i-?????????J????{?]~C.~~CC~C~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~C~~~~C~~~~~~~~.C..C................{%>껝껻껻1.~..C.....~..........................................CCCC~.C~Con\2^MMKraeraaIrqw.C~~.........................................~C~.CC.~.~.C..C...................~~CCCCCC~C~CC~CCC.CCC~CCCCCC.C.CC.C.C.C..C.CC...~C~C.~CC~..~9> x>x x x Rq~C~.C....C..C.C~C~~~~~~~..C..C.CC.C~~~~~..~.~~~~.~~~.~.~.~~~..~~..~pRan222/%xꝝ>xx.~~~~.~.~~~.~..C..~~.~~.~.~.~.~~.C.~~~~~~~~~.~.~.~~.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.......~~~~.~.~~C~;inS > > (~~~.~~...~~~~~~~~~CC~~~~CFCFC~~~~~~~~~~...D4@×EDDE4EĐp~~~~~~~~~~~~~~..~~~~~.~~~~..~~..~~~~CC~~~~~~~~C..CC~F Xnnnnnnnnnn2n^F..~~~...~.~.~~.~.~.~.~~~.~.~.~.~.~..~~..C.~~~~.C.CCC.CCC~~CCC~C~CCCC{ sxx19CC.CCCC~C~C.~C.CC.C.C.C.C.C.C.C.C.C.CC....CCCCCCCC.C.C.....pwwoe((KR^IIIoIIIIveCCCCCCC~C~CCCCCCCCCCCCCCCCCCCCC.CCC~C~CCCCC~CC~..C...C......C.~.C~~C.C~~~~C.CC.....C.C.CC.C...~..C...C....C..C.C.C...C.C...C.C..~C~~..CC....9xxxxxx xx x 0 mmR2~~.....C.C.C.C..~.~.~.C.C..C....C..C.~~.~...~~..~..~.~.~~...~.CpII(22n% x >>% ~~~~~~~~~.~..C..C.~...~..~.........~~~~~~~.~.~...~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.~.~~.~~~~~~~~~~~~~߱\X{) G(~~~.~~~~~~~~~~~~~~~~~~~~~~~~~~C~~~~~~~...~~D4DD@›D4@DE~~~~~~~~~~~~...~~.~.....~~~~~~~~~~~~~~~~~~~C.C~\ Xnn2nnn22222^(~.~.~.......~..~~.~.~~.~~~~...~........~~.C.~~~~~.~....CC~CCCC~CCCCCCCCS*11>>>>xx1111C~C~CCCC.C~CC.CC.C~CC.CC~C.CC.CCC.CCC.....C~CCCCCCC........wKoKoo}o2IIIMIaIrw^~CCCC~CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC~CC~CCCC..C..C..C.......~C.C~CCC.C~C.C.C......CC.C~~.....C...C...C.C.~~~.C..C.....C.C.C....~~C..CC...J91> 0 0 0 0 xxR2~~~C......CC..C.C.~.......C..C.C..C.C.~~~.~..~~~~~~~~~~~.~~~~~~~~~~~~.pMMIvnn2% x%C~~~~~~~..C.~~.~.~.~.C..C.~~.C.C.~.C.~.~..~~~.~~..~~.~~~~~.~.~~~..~~~~..~.~.~.~~~~~..~~.~~.~.~ߘinn{) > ~~~~.~~~.~.~~~~~~..~.~~~~~~.~..~...~.~~@4E8-iLh@DDD@"ė^F~~~~.~.~~~~.~.~~.~~~.~~.~~~.~~.~~.~.~~~..~.~.~..~~.~~~~.~~XLnn*BB2n222^~~..~~C~~..~..~.~~~.~..~~~~~~~~~.~~~~~~~.~~.~~~~~~~~C.C.~~.C.~C.~~~~~~~C]*]?q?*kq*qq*********CC~.CC..C..~.~.~.~.C.~~.~.C..~.~.~~~.~.C.C.~.~.C.~.C~.C.~~~~~~CwoIIM]aoRaaIe^C...C~~C~C......C.~~..~.~.~.C.C.~C~~C.~~C.~~~C.~C..C.~C.C.C.C..~....C.C.C.C..C.C.CC..~.C.C.C~~.CC.CC.C..C.C.C.C.~~C.~~~~~~~~CC....~~~...CC({q* H r H kk^CC~..C.C~C...C.~.C.C.C.C.C.C.~..C~.~.C.~~~~~.~~~~~~.~.~~~~.~.~.~~~~~~~.~pRRRvn2% >G~~~~~~~.~C.C.~~~C.C.C.~~.~.~.~.~~.~~~..~~~~.~~.~~~~~.~.~~~.~~~...~~~..~~..~~.~.~~~~~~.~~~.~~~~.~;; XLn2G> Gi~.~~~~~.~~.~~~...~.~~~~...~~~~~.~~.~.~@çE"|-LfhDDEEΝC.~~~~.~~~.~.~~~.~~~..~~~~~.~..~.~~~~~.~.~.~~~.~~~~~~~~~g\((1G2222nn^CC~.~.~~C~.~...~.~~~~~.~~C.~~.~~~.~~~~~~.~.~~~~~~~~~~~~.~.~~C.C~.C.~C.C.C.~^q2{nqn{n{22{{vqCC~~CC.C.~C.C.C.C.~.~.~~~~~~~C.C.~.~.C..C..~C.C..C.~.~~~~.~.~~.pweooI,]oIaIopC~~....CC.C..C.C.C.C.~~C.C.C.~.C.C.~C.~C.~C.C.~C.C.C.C.~C..C.~C.C.C.C..C..C..C~C.CC.C.C~C.C.C.C.C.C.~.C.C.C.C.C.C.C.C~C.C.C.CCC...~CCC..CC\Lnnqqq2{qq2{22q22^~C~.C..C.~C.C.C~C.C.~C.C..~.C.C.~..C.~.~~~..~~~~~~~~~~~.~~~~~~~~~~~~..~pMMr]nn2/%xxx~C~~~~.C.~...C..~.~.~.~.~~~~~~.~.~~~~~~~.~.~~.~~~~~~~~.~..~~.~~~~~~~~.~.~~~.~~~~~~~..~~~~~~~~~~ߘinnA) > G~.~~....~..~.~.~~~~~~.~~~.~~.~~~~...~.@@D4E4hXf9@D~~.~~~~~.~..~~.~.~.~..~~~.~~.~~~~~~.~.~~~~~.~~..~.~~~~~.~iB11{n22^(~~~~.~C~~..~.~.~.~~~~.~.C.~~.~~~.~~~~~~~~.~~~~~~~~~..C.C.C.C.~~C.C.C.C.CC^^2222SrPk2{2q22~C..~C..C..~.~.~.C.C.C~~~~~~.~..C~C.C..C..C..~..C.~.C.~.~~~~~~CpwooeopMMvIaaapC~...C.CC.C.C.C...C..C..~.~.~C.C~.C.~C.C.C.C.C.C.C.C.C.C~~C.C.~~.~...C...C..~C.~~~~~~~~.~C..C~C.C.C~~C.C.C.C.C.C.~~C.~.C.C.C.CC....~C~.C.CCV\LXS22222222q2]2]^~CC..C.C.C.C.C.~~~C.C.C.C.C..C.C.~C..C.~.~~~~.~~~~~~.~.~~~.~.~~~~~~~~~pMr](n%xx%C~~~~~~~..C~C.~C.~~~~~~~~~~~.~~~~~~~~.~~..~~.~~.~~~.~~~.~~~.~.~~~~.~~~.~.~..~~.~~~~~.~...~~.~~~~..;\XXn) > i~~~~~~.~~~.~~~~~~~..~.~.~.~~~~~.~~~~~ç44@Z\fח@EEE~~..~~~.~.~~~~.~.~~..~~..~~~~~.~.~.~~~.~.~..~~.~~.~~~~~~\n{s%1x%_n22n^~~..~~~CC~....~.~~~~~~~.~.~.~.~.~.~.~.~.~~~~~.~~~~~.~C..~.~C.C.C.C.C.C.C..C^222{2kPG22qq{22CC..~C..C.C.C.C.~.~.~.~~.~~.C.~C~...C.C..C~C.C.C.C.C.~~~.~.~~~C.pwoo,oooIa(\]^MaC.....C~~CC.C..C.C.~.C.C.C.C.C.C.~C.C.C.C.C.C.C.C.C.C.C.~C.~C.~.C.C.C.~.C..C.~~~~~~~~C.~C.~C.~~.C.~~~~C.C.C.C.C.C.C.~CC.C..C.CC....~C~C..CCLinn2{2{22qq]q]qq2^CC.CC.C.C.C.C.~C.C.~.C.C.~..C..~~C..C.C.~~~.~~~~~~~~~.~~~~.~~.C.~~.~~~~.poMMMr2(n >xx~~.~.~.~C...C..~C.~~~~~~~~~~~.~~~~~~.~~~~.~..~.~.~.~.~~~.~~~..~.~.~.~.~~~~.~..~..~~...~.ߘ\XnG> G.~~......~~~~~~..~.~..~~~.~..~~~.DD§f\\5Z×9~~~~~..~~.~..~.~..~..~~~~~..~~~~~~~~~~~~~~.~.~~.~.~~~~.~~~\"11Ji/{2n2CC~~~~~~~~.~.~.~~~~~~~~.~~~~~~~~~~~~~~~~.~~~~~~~~~C..C.~C.~~C.~C.C.C.~C~(^222v kkym{22{222C..~C..C.~.~.~.C.C.C.C~C..C.C....C.C..C....~~.~..~..C..~~~~~~~~Cwooo,ee\KvraawC......CCC...C..~.C.~.~.~.~..~C.~C.C.C.C.C.C.C.C..C.C.C.C.~C..C.C.~~.~.C..C.~C.C.~C~~~~C~.C.C~C~C~~~~C.C.C.C.C.C.C.~C...C.C.CC.....CC....CC\(222S2]22q222]2CC....C.C.C.C.C.C.~CC.C..C.C..C.~..C..C.~~~~.~.~~.~..C.~.~~~.~..~~~~.~..~poMMr2(/G%C~~~~~C.~.CC..C.~.~~~~~~~~~~~~~~~~~~~.~..~.~.~.~~.~~.~~.~~~~.~.~~.~~~~.~~~.~.~~.~~~~~~~..~~..~~.~pߘ\\n{m > i~~.~~~~.~..~~~.~.~~.~~~~..~..~..@@D×D=f\\D@4EEx{~~~~~~~..~~~..~.~.~~~~~~.~~.~.~.~~~~.~..~.~~~..~.~.~.~~~~~\B"zJ*{2n2^~~.~~~~~~~.~.~..~~.~~~~~~~~~.~~~~~.~.~.~~~~~~~~~~.~..C.C.~C.C.~C.~~~~~C.~C^2222rP*))*2{222......C..~C.C.C.~.~.~....C...C.C.C...C.C.~C..C.C.C.C..C~~.~.~~CCpwoeoMeMvf(IMaeCC....C.C.C.C..C.~~~~C.C.C.C~C.~C.C.C.C~~~~~C.~C~C.C.C.C.C.~C.C...C.~C.~.C.~C.C.C~~.C~~~.~C..~~~.~C~~~.C.C.C.C.C.C~C.C.C.C.C.CC....~C~C..C.\V(S2222222222q]vCCCC.Cp.C.C.~C~C.C.C.~.C.C.~..~.C..C..C.~..~.~.~~~~~~~~C.~.~~~.~..C..~.~...~pMMM ^\((/BC~.~~.~.~.~...C.~~~~~~.~~.~~~.~.~~~.~~~~~.~..~~...~.~~.~~~~~~~..~.~~~....~~.~~~.~~.~~~~~~.߳\n)>> R..~~~~~~~...~~~~~..~~.~~~~~~~..~~~@DDD=\\\\JD99xi.~~~~~~~~~..~.~.~.~~.~.~~.~~~.~~~~...~..~~..~~.~.~~~.~~\"11{S2C~~.~~~~~~.~~~~~~~~~~~~~~~~~~~~.C.~~~~~~.~~~~~~~C.C..~.~C.~C.~~C.C.C.C.^22n22v YmaGnn222~C..~C..~C..~.~.~.C.C.~C.C..C.C...C..C...C..~C.~.~~..C.~.~~~~.C.CCpwwooe^aQR RwC......CC~C.C.C.C..~.~.~..~..~~C.~.C.C.C.C.C~~C.~C.C.C..C.C.~.~.~C..C..C.~.C.~C~C.C~C.~C~C.~C.C~C~~.C.C..C.C.C.C.~.C.C.C.C.C.CC....~C~C..CC(n22222222p.C..C.C.C..C.~.C.~~C.C.C.C.C.~C..C..C..C~~~~~.~~~~.~.~C.~.~C.C.C~C.~C..Cpoor2\\(( 1%C...~.C.~C~~.C..C.~.~~~~~~.~~~~.~~.~~~..~.~~~.~.~.~~~.~~.~~~~~~~~~~~.~~~..~.~.~.~..~...~~~.~p߳\\g /G > i.~~~~~~~...~~~~~...~~~~.~~~~.~~~~~@J i\\\\ Z"Gip.~~~..~~..~.~.~.~.~..~..~~.~~~~~~.~~~~~~..~.~.~~~~~~ -9"1111%%q22^C~~~~~~~~~~~.~~~~~~~~~.~.~~~~.~..C.~~~.~~~~~~.~~~....C.C.C.~C.C.C.C.C.C.CCC222raaky)xG22{22~...~~~C.~.~C.C.C..~.~.~~~.C...C.C..C.C.C.~C...C.C..C..~~~.~.~~C.CCwo,ooooKanQ Ie^.....~~~~C~~..C..C.C.~C.C.C.C.~C.C.C..C.C.C.~~~~~~~~C.C~C.C~C.~C..C..C~.C...C~.~C.C.~~.~~.C.~~~.C.C.C.~C.C.~C.C.C~C.C.CC.C.CCC....C~~~..CC\\\(nn2n222p~CC..C.C.CC~C.C.C.C.C.C...~~.C..C.C...C.~.~~~.~~.~..C.C.~.~C.~.~~...C..~~~oMoMoS\\1%CC..~C..C...C.C.~~~C.~~~~~~~.~..~~.~~~~.~~.~.~~~~~.~~.~~~~~~...~~.~~~.~.~.~..~.~.~~~~.~.~~..~C߹\\> i.~~~.~~.~~~~~~.~~~~~~~~~..~~~C@ 8\\=z9"i..~~~~..~.~~~~~.~.~~~.~~.~~~~.~~.~..~~~.~~~...~~.~~~~.~~~2A/"E11%m qn2^(~~.~~~~~.~~~~~~~.~~~~~~~~~~~C.C.~.C.C.~~~~~~~.~C.~C.~~~~~C.~~C.~C.C..C.(2nn2rqH))GHn2222C~~.CC.~.C.C..~.~~C.C.C~.~~~~~C..~.C.~.~.C..C.C..C.C.C~~~.~~~~.~CCCCwwwooIiiym{%%r.C....C.C.~.C.~.C.~.~.~~~.~~~C~~C..C.C.~C.C.C~~C.C.C.C.~~.C..C..~~~.C...C.C..C~C.C~C.C.C.C.C~C.C..C~~~C..C.C~.C.~~~.C.C..C.CCC....C~C~..CCV\\Ln2n2222p^~C.C..C.C.~~.C.C~~~C.~C~C.C.C..C.~.C.C.~.~~~~~~.~~.C..~~~C.~.C.C.~.C..C.~~poMM2\(̋1%C...~..C..~C...C.~~.~~~~~~~~~~~~~~~.~~.~.~~.~~.~~.~~~.~..~.~~~.~~~~~~~.~.~.~.~~.~~~..~.~~..~.߹\\G>> G~.~~~~.~.~~..~~~~~~~~.~.~~~C~›8f8\\\-=9"Gi.~~~.~..~~~~.~~.~...~.~~~~~~~.~.~~~~..~...~~~.~.~~~~~~~~\\ԛE"11122^C~~~.~~~~~~..~.~~~~~~~~~~~.~~~.~.~.C..~~.~~~~~~C..C..~.~~~~~~C.C.C~.C.C~~((n2qr H?_m(n{S22~C..CC~~~~..~C.~C..~~~..C.~..~.~C.~.C.~C..~C....C..~..~~~~~~.~.C~CCpwwowwMooM qGG p.~....~~~C.~.C.~.C.C.~C.~~C.~~.C.CC.C.C.~.C.C.C.~~C.~~C.~C.C..~C.~~~C..C...C..C.~~.~C.C.C.C.~~C.C.C~~~~.C.~~C.C.C~C.C.C.C.C.CC..C~~~C.CC\\\nn(n]^(.C...C.C.C.C.C..C.C~.C.~.~.~..C.~~~~.~.C~~~~~~~~~.~~~C.~~~~.C..C.C~~~C.~.CpwMo ^\\\\1"%%^~~.~.C.C.C..C.C.~~~.~~.~~.~...~.~~~~.~.~.~~~~~~~~~~.~~~~.~.~~.~~.~~~~..~.~.~~.~~~.~..~~~߹\\gѺ/%>> > (~~~.~..~~~~.~~~~~~~.~~C8f\f\\˕\\V =B99mi.~~~~.~~.~~~~~~..~~~.~~..~.~~~.~~~..~~~~.~~~~..~.~.~~~~.\\""x{n]n2^(~~.~~~~~..~.~.~~~.~.C.~.~~~~~~~C.C.~.C.~~~.~~~.~.C.C.C.C.CC.C.~~C.~C.C.~~C(nn2{rq2k*qkqn22n2C~..CC~.~~.C..~~..C..~~C.C.~C.C.~.C..~~..C.~.C.C..C.C.~~~~.~~~~~C.CCwwoeoMoK]qiJ%G]2CC~...C~C.C.~.C.C.~.~~~.~~.~C.C..C.C.C.C.C.~.C.C.C.C.C~~C.C.C.C..~~...C..C.C.C.~C.C.C.CC.C.C~~C.C.C.~~~C.~~~.C.C.C.C..C.C.C.CC..~.~C~~..CCV\\\L(n2^C~C.C.C.~C.C.C.CC.C.~C.~C.C.C.C..C.~~.C.~.~~~.~.~~~C..~.C.~C~C~C..C~C.~~~~~poooM2\"1.C..~C....C.C..~.~.~~.~.~.~.~.~~.~.~~~.~.~...~.~~~~~~.~.~.~~~~~.~~~.~.~~~~~~~.~~~~~~..~.~~~.\\>> i~~~~..~~.~...~.~~~.~..~~CC-855\\9"i~..~~~~~~.~.~~~.~~.~~.~..~~.~.~..~~~.~~.~..~~~.~~.~.~~~.~~\"J{nS2^CC~.~~~~~.~.~.~.~~~~~.~.C.C.C~~C~~~C~~C~C~~~.~.C.C.~.~.C....C.C.C~.C.C.C.~( n2 2n**n2222~C.~CC~~~~.~C.C.C.C.C..~..C..~.C.~.~C.~C.C..C..C..C..C~~~~~~.~~~~C~.o,o,oweoo]{{̶ {]CC...~~~~~.C.~.~.~.C.~.C.~~~~C.C.C.C.C.C~~C~C.~C.~C.C.C.C..C.~.C~C.~C.C.C.C..C.C.C~~C.C.C.CC~~~C..C.C.C.~~~~C..C.C.C.C.C.C.CCC....~C.C..CC\\X(\((2C~C.C..C..C.C.C..C.C.C~C.~.~.~.C.~~.C.~.C~.~..~~~.~..C.~~C~~.C.C~C.~.C~~.~~pKooo^\\\\?""CC.~.~.C.C..~~~~~~~~.~~.~.~...~~.~~.~..~~~.~...~.~~~~~.~~~.~~..~~.~.~.~.~~.~~.~.~~..~.~~.~~.~.\f\%>> > G(~~~.~~~~~~~.~..~~~~..~~CZZ8ff\\\ (""~~..~.~~~.~~~~.~~~~.~~.~.~~~.....~~~~.~~~.~~.~~~~~.~~\\sinn222C(~~.~~~~~~.~.~~...C.C..CC~~~~~C~C.C~~~~~.C.C...C..C...C.C.C.C.~C.C.C.C.CV (nn(2q2nn2S....CC~.C.C~.~.~.C..C.C.~C..C.~..C.C.~~~...C..C..C..C.~C~~.~~~~.C.C^wKoeeoeovK]?i {̲]CC~~..~~C.~.C.C.C.C.~~~~~.C.~~C.C.~~.C.C.~C.~~C.~C.~~~.C.C.C.~~~..~...C.C.C.C.C~.C.C.C.C.C..~~C.C.C..C.C~~~C.C~~C.C.C.C.C..C~C...C.CC..CC\\\\\L(C^~C..C.~~C.C..C.C.~~C.~.C.C.C...C.~~~.C.~.~.~~~.~.C..C.C~~~CC.~~~~C~C~~C.~pwooo]\f\-""9CC.~C.C...~~~~~~~~.~~.~~~.~~~.~.~.~~~~~.~.~..~~~~.~~~~.~~~.~~~~~~~~~~.~.~~~~~~~~~..~.~~.~.~~~.\> GL~~~~.~..~..~.~~~.~CCZ-85ffff\\ \("i.~~~.~~.~~~~~~~.~~.~~.~~~.~.~.~~~~~~.~.~~~~~..~.~.~~~~~~\\\-g1?/{2^C~~~~~~~~~~.~.~.~~~.C.~.C..C.~C.~~.C~~C~~C..C..C.C.C.C..C.C.C.C.C.C.C.C.CCC  2nn2....CC.~.~~.~~C.C..C..C.C..C.C.~C.~.~.C..CC..C.C..~C..~~~C~~~.~.C.~.,,eoooooeMo]{?/=0Bi*pC~C..~~~C.~.~..~.~.C.~~.~~~C.C.C.C.C.C.C~.C.C.~C.~C.C.C.C.C.~~.~C.C.C....C.~C.~~C.C.C..C.C.C.~~C..C.C.~..C..C.~~.C.C.CC.C.CCC....CCC...CC\\\\\(L((CC~C...~C.C~~C.C~C.C.~C.C.~.~~C.C.~.C.~.C~~.~~.~.~~~.~C..~~C.~.~C~~C~~~~~.C~pwoKo^\\\˕9""%pC...~..C.C.~~~~~~~~..~~~.~~~~.~~~.~~~~~.~~.~..~.~~.~.~~~~..~~~~.~~.~.~~~.~~~~.~~..~...~..~...\f\gX%> > >Gʢ~~~~.~~..~.~~~~.~~~~.~FZZ=888\g\ - 91"(C.~~~.~..~.~~~~~~~.~.~.~...~.~~~~.~~~~~~~.~.~~~~~~~\\\|B922(2C~~~~~~~~.~..~~.~~.C.~~C.C~~C.CC.C~~~~C.C.C..C..C~..CC.C.C..C.C~~.C.C.~FXnFn22.....C.C..~~~~....C..C....C...C..~.C.C..C...C.C.C.C..C.~~~~.~~.C.C~pw,peew,oe{̕)k KCC...C.~~..C.~C.C.~.C.~~C..C..C.C.C.C~C..C.C.~C.C.C.C.C.C~~~.~C..C.C..C.C..C.~~C.~.C.CC.C.C.C.C.CC.C.~C~C..C.C.C.C.C.C.C.C~CC....C..C.pC.f\f\f\\\\\\\\(C~C.~.C.~.~~C.~~.~C.~C.C~.C.C....~~~~.C.~~~.~~~.~.C.~~C.~C~CC.C~~~~~CC..~pKwoo]\f\\f\˕/99"1pCCC.C.C.~~.~~~~~~~~~.~.~.~.~~~~.~~~.~~.~~.~.~~.~~~~~.~~~.~~~~.~..~~~~.~.~~.~.~~~.~~~~..~~C\5\\>>> GLV~~..~.~~~~~~~CZ=LXi\X{{{J1(C~~...~~.~~~~~~~.~~.~.~..~.~.~~~~.~~.~~~~~~.~~.~..~~.~~~C~~~~~\\˕ΐ222^C~~~~~~~~~..~...~.~.~.~.C.C.C.C..C.~~C~C....C...C...C...C.C.CC.~~~C.~C.C^2v옳vp.C..C.C.~~.C.C...C..C.C..C.C.C.C..~..C..C.....~..C.C~~~.~~~.C.C~pwwv(^CewoK2]{i8/x% eC.~...C....~C..~.C.~~.~~.~~C.C.CC.C.C.~~CC.C~C.C~C.C.C.C.~~~~~~.C....C.~..C~~C.~CC.C.C.C.C.C.C.~~.~~~~~..CC.C.C.C..C.C.C.C.CC....CCC...CC(F222qv*owCC~.C~.~~C~~~C~C~C.~C.~C.~~.~.C.C.~.~.~.~~~~~~~.~.C.C~.C~.C.~.~.~.~.C.~.~..pwKoK2\58i4EEE"1(CCC..~~~~~~....~.~..~~~~.~~~.~~~~~~~~.~~.~.~~~~~~~~~~~.~~~~~~~.~~.~~~.~~.~~....\5B>> G\VVVʾ7ʾ~..~~~~~~~~~~~~(CZj@DX\99"9"""11(~~~~~~.~....~~~~.~.~~~.~.~~.....~~..~~.~~~~~CC.~8XXnS2{p(.~C~~~~~~~~~.~~.C.C~C~~C.~.C.C..C.C..~.~.C.C.C.C.~~C.~C~.C.C.~.C.C..C.~..;;;;;;;;;;;;;;;C~~C~C.C.~~C.C.C.~C.C.C.~.~C.~.~.C..C.~CC.C.C~~C.C.C.~~~.~~~.~~.C~Cw\\(oeC-Z"{]2CC.CCCCCCC..~C.C.C..C.~~~~~.C....C..C.C...~~~~C..C.C.C.C.C~~~.C..C~C~.C.C.~~.C.~.C.C.C.CCC.C.C.C.C.C..C...C.C.C.C.C.~~~~C~..C.~~..C.CC.~pKKweeeooIIoIII^~CC..~~~~...~.~~.~C.C.~.C.C.C.C.C.C.C.C~~..~~~~~~.C.C~.C.~.C.C.C.~.C.C..~.CwKo]\f\ff8EEE""9KCCC.~~.~~.~~.~.~...~~~.~~~~~~.~~~~~~~.~~..~.~..~~~~~~~~~~~~.~~~~~~~.~~~~.~.~.~~.\5\B aVVVʢ~~~~.~~~~~~.C^j@@@D—\\LX"111~..~~..~..~~.~..~~~..~..~~.~.~.~~.~~.~~~~~~.~~CC.F-g8\\Lnn^~~~~~~~~~~.~~.C.~.C~.C.~.C.~..C.~~.C~C.C.C.C.C.C.C.C..C.~.~.C.C..C.~.C.;;;;;;;;;;;;;3~~C~C~.C.C.~C.C.C~~.C.C.C..~.C.~..C~.C..C.C.~C.C.C.C.C.C.~.~~C.C~Cppw\\ve>9%C~C.CC~C.C.C..~....C..~~~.~~.C..C..C..~~C.C.~C.~C.C.C.C.C.C.C.C.C.~~.C.C.C.~C.C.C.C.C.C..C..~.~~.~~..C.CC.C.C.C.C.C.C.~~C..C.~~..C.CC.CCwKoooweoooKMMooeMICCC~.CC...C.C.C~.C..~.C.~.C.C.C..C.C.C.~~~~~~~~~..C.C.~C.~.C.~.~.C.~.~.~.~CwwKwKv5ffff\f(4EE99o(.....~~~~~....~~..~.~~.~.~~~~.~.~.~.~~~~~.~~~.~.~~~~.~.~.~~~~.~~.~~.~~.b\5f8-l> %\\VVVVVVV~~~~~~~~.~~~(^@U껝> i~~~~~~~~~..~~~..~~~.~~.~~~..~~~~..~~.~~~.~..~~~CC~̜˕g==/{](.~~~~~~~CC~~~.~~.C.C.~C~C.C..C.C.~~~~~...C.C.~~.C.C.C.CC.C.C.~~.~.C.C.~~.3;;;;;;;;;;;,p~~~~~C~C.C.C.~.C.C.C.~.~.~C.C..C.C...C.C.C.C~.C.C.C.C~~~~~~~..CC~C.w\^eeKJ"1m vC........C.C.C.C.C.C.~~C..C.C.C...C..~.C.C.~C~~.C..C.C..C.C.C.C.C.C.C.~~C.C.~C.C.C.C.CC.C.C..C.~~C.~...C.C.C.C.C.~C.~C.C.~.~.C.C..CC.wwooooweooooKoMoKo,C~C~C....C..C....C.C.~.C.~C.C~.CC.~C.C.C~~.~~~.~~C.C.CC~.C.~.C.C.~.C.C.C....wwKv5f5ff5f\\@E99MC...C.~.~.~~~...~~..~.~.~.~~~~~.~.~~..~~~~~.~~~~~~~~.~~.~..~~~..~.~~~.C5ff8>>>>V\VVVVVV~.~..~~~~~.~~(^@L{Ꝼ"껋iC~~.~~~~..~.~~..~~~~~.~.~~~~...~~~~.~~~.~..~.~~CC~p??{??zllBl BHHe~~~~C~~~~~C~~~.~~..~.C~C.~.~~.C.~..C.~.~..C..C~C~C~~~~C.~.~.~..C..C.~.~.C.~b;;;;;;;;;C~~C.~~~.C.C.CC.C~.~C.C.C.C..~.C.~~.C.C.C.C.C.~C.C.C.~~~~.~.~.C.~~CCwv(ioepi˕=h]2.C.~.~....C.~~..C.~~~..C.~.C..~~.C.C.~~C.C.C.C.C.C~~C.~C~C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.C.~.~.C.~.~~..C.C.C..C.C.C.C~C.C.C.C.C.~..CCC.CCwwwKoeoeoKoMMoowoMICC~C..C...CC..C.C.~.C.~.~C.~~.C..C~C.C.C.~~~.~~~C..C.C.~.~.C.~.~.C.C.~.~..CCwwKwK]55ff58N5DçErn~F.~.~~.~~~~~~.~~~..~~~~~~.~~~~~~.~.~~~.~~~~.~~....~.~..~~~~3\5ff5\f\=ddd>>RVVVVVVVʢVVVVVVV~..~~~~~~~~~CZz=X/?1s"11%S~.~~~~~~.~....~~..~..~~~~.~..~~~~~~.~~~.~.~.~~~~C~~i{@@DDDDDD×E""11H~~~~C~~C~~.~~~~.C.~~~~C~C..C..C.C.CC.C.C.C.C.~~~~~C.~C~C~C.~C.~~~.C.C~.~~3bb;;;;~~C.C.C..C.~.C.~CC.C..~.~.C...C.~~.C.C~.C.~~C.C..C.C~~~~~~~~.C~C~Cpweewweeq˵82qpC~~C~CCC~.~.C..C.~~.~~C..~C..C.~~.C.~C...C~.C.C..C.~C.C.~~C.C.C.C.C.C.~~~C..C.C~.C~.C.~C.C.C.~C.~~~~C..~.C.C.C..C.C..C.C.~C.~~.C.C.C.C~wKooeweoeMwooooKwoo,C~C~C...C....~~~.C.~.C.C.~C.C.C.C~.~.C.C.~.~~~.~..C.C.CC~C.~.~C.~.~..C.C~~.~.wwKvv555ff||5fZDDRS~~~..~~~~~~~~~~~.~.~~~.~~.~.~~~~~~~.~~~..~~.~~..~.~~~~.77~~~~~. 55|5f\=Jdddd>>(VVVVVVVVVVVVVVVVV77~..~~~.~~~.(=i(̶s1"111"%{.~~.~~.~.~~~.~..~~.~~.~~~.~..~~~~~~.~~.~~.~~..C~~~S]@@D@@D@@@D9""1 nF~~~C~~~.~C~~~~.~~.C.~C~~~~~C..C.C......C.C.C.C.C.~C.C.C.C.~~~~~.C.~.~~.~~~.;;b~~~~.C.C.C.CC.C.C.~.C.~C.C.~C.C.C..C.~~.C~C.C.~C.C.C~~~~.~.~.~.~C~~pw,owowe,e i{8J-K]pCCCCCC~C~C.C..C..~~C..~.C.~.C.~..C.~..C.C.~~C..CC.C.~~C.C.~~.C.C.C.C.C.C.~~C.C~.C.~C.~C.~.~.C.~.C.~.~.C~C.C.C.C.C.~C.C.C.C.C..~.C..CCC~wwoowwoeooKKKoowwo,op~CC.C.C.C.C.C.~.~.C.~..~C.C~C.C.~~C.C.~~~~~~.~.C.~C.C.~~.~.C.~.C.C.C.~.....Cww](555fN5f55חU4 i...~C.~~~~.~~~~~~.~.~.~~~~~~.~~~.~~~~~.~~~~~.~~~.7~.~~~..\5f55\|Jd>VVVVV\VVVVVVVVVVVVVVV>iV\VVVVVVVVVVVVVVVVVVVVVVVV~~~~~~~.(^@D4D %%"""1111Gq.~~.~~.....~...~.~~~.~~..~..~.~.~~~~~~~~.~.CC..~.i*çD@@@@@@@D×9SCC~~~~...~~~~..C.~~C~~~~.C..C.C..C..~~.C.C.C.C.C.~~~~~C~~C.~C.~~~.~C.~~C33F3~~~~C.C.~C~.C.C.~C.C.C.~~C.~.~C.C.C.C~~~~~C.C.~C.C.~C~.~~~.~.~~.C~~pwMKMKoKweo{LX89{S(CCC~.....C~C..C.C.~..C.~~.~.~...C.~...~.C.C.C.C.~~~~~C.~.C.C.~~C.C~C.C.~~.C.~C.~C~C.C.C..C.C.~.C...C.C.C.C..C.C.C..C..C~C.C.~.C.C.CC~pwowo,eooKeo,wwow,,p~C.CC.C.C..C.~~~C..~.C..C.~CC.C.C.C.C.~~~~~~~..C.C~~C.C.~.C.C.~.C.C.~..C..C.pwp(5555NNN5=J@@]CC~~~~~~~~.~~~~..~~.~.~~~~~.~~~~~.~.~.~~~~~~~~~~~777~~5555558EdVV\VVVVVVVVV\VVV\VV9"11111*(~.~~.~~.~....~.~.~.~.~~~~~..~~.~.~~.~~~.~.~CC..~~i]›@››DΐM]..~~~..~.~.~~C.~.C.~C.C~.C..~.C.~.C..C.~~C.C.C.~C.C.C.~.~.~.~.~~~~.C.~~F33b.~~.C~~C.~C.~C.C.~C~.~C.~.~.C..~.~..C.~C.~C.~C.~C.C.~C.~~~.~.C..C.wooKoowoKenq{ʜ-i]^CCC~C.....C....C.~~.C.C.~..C.C..C.~C.~~C.~C.C.~~~C.C.C.C.C~~~~C.C.C.~~~C.C~~C.C.C~~~.C.C.C..~~~.C.C.C..C.C.C.C.~C~C.C.C.C.C~~~~C...C.C.C.wwwweweevweeo,,woow.C.C.....~~~~.C..~.C.~.C.C.C.~.C.C.~C.~C.~~~.~C..~.C~C.C.~.~.~.C.~.C.C.~.C.ppwpp](V555ff5f=J@@@@@@^.~~~~~.~~.~..~.~~~.~.~~~~.~~~.~~~.~~~.~~~.~~..~~777~35555558EE9 nV\V\{`SDԗs]W1n'48A-9lc;(F@I;KqLx,XZZFFOH4Ǧ裕{D+%n6neU]#yH" wNhɴgzgpDfYw.<nĻxx~l20I.7SUW78ux@tZH LGKbedDJ,Mw\.霎l:X[no<0-yFl6hnx{d0gP4-g:R{S09ϝ/;ΐQwSc8͸p 0C?x0V9Z:ebaFEEE(!FBEF|z{h9GXTz)yzzDEFzN|14nZZkF]]0pp%pp%$S]ppp-,:kpp%00p]]]5]5F5FF+F+++++++F5]p]]]]]]]]5]5]5]]55]]]]5]]+5+]55Fpp0p]]p00p]00p00000p00]]]]0]]]]]5]]]:5]]]5]]]]]]]]]]]]]]]|FjSFF]]]]p]]pp0]]55]+FFFFF]]pp]]]ppSpppp]ZZZjjDDDDDDDD0]]]00KZ:DDDDODO0]]00]5S5+5Sjjpj0DDDDDD]]S0]0p5]00$gDD000000pSjj3j5XX]0]]5]00j000000000]0]X++++F]]ppD$00+v+XNv+]]00]]]]]pp]55]]]5]]]+5]]00]0%]p]Xvv55]p0%]F]FFF]]+++++++N+++5]5+vv+5]p]5555+5]p]]lX55]]]Fl5555]]555]]5l5+X+X+X5]]]]]]]]5]5]05X++5+5F5X+FF5F]]FF+FF++]]]p]]F+5+55]vN555l5+55X5+5Xl5l55+l++5Gxx\;~555XNS5555X5+vv+v+X+Xvv+55X5l5]Fp]5pp0]l+X+X555X+v+X5++]555+55]+555]pp]0p]5FFFF]p]p]]]]]p]]]]]]5]]FF+5S]]55l]p0]%%ppp]]F]+++5NNN++5SSFFF+]]]]FS]F]pp%ppppppppp0%p0pp]S]]]p]Zp%p%ppp0pp$lS]5JZZ+F]]pppp0pppppp]]]]]p]55Nv+NFF]pp5]]5p]+FFFF]F]]]]]]]p]]]]]p]]]F]F]]]]]]p]]5]]]]]pp0p]]]0]]pp]5]B8Z]]]]]]]]]p]5+++]p]5F55]]]]p5]55]]5FF55555]]p]pppp]]]]]p]p]5]]]]p0rZZ:k000OOKKOODD0]005+5vNuu++ZZZ88DD00p00ODDDD0]0p0]]0p50]]5]0]50]55]0]5+]]]p0]00]0055]]00jjj55XX]]05DD00p+5]55F55+N++]0DD0DD]p0p]]]]]p]p]p]]]p]]p]+5FNN++5++555FF+++N+5]SSFj]0]5]]5]]]0p]]]0]]]]]]]]]5555555555+l5l]5]]]]]]]55F+N5F+Xl+l555+5+++++v+l+F5555+ll+55l55]%pp+5++++++5]p555ul++5l55l5Xl55555]X5+5]]]550]]]؅{{x;1j]]]]]]]]5+5+++55ll55F555+55F5+5]]5]5+5]]55Xv+X5v]+X5+55lXvl555X55]]Fp]]]55+++5+NN5]]p]]p]pp]]SS5]+N+FF+5F]55]]]]]5+555]]p]p]p]]]]]]5]+]FFF]SFpp]pppp]Sppppppppppp%pp]]]]]F+]:]ppp0pp0]]ppppZ:8]]]]]00]]]55]]]]pp]pp]p]]]0]]p]55]]5]]]]]]]5F]SpSjSS]p]]5]]pp]]]]]]F+]]]]]]]]]]]]]]]]p]]p]]pp]]p]]5F:5]S]]55]]]]]]]]]FSjS]]]]5]]]5]]]]]]S]F]]]p]]p0pp]5]]p0]p]]pp0pp]͓AZZr000쀀0000033@ODDD00zZZZ:3+v5DDD000000000DDDDDDD000]]55]50000DOOOK0pSj@jSj3jwjg000vvvXXXXX]j00]000000000p000]]l5]]0OOO0OKOOOD0pppp]pp]0p0ppppppp0pppp]5F55+]0]]SF5j5j5j\g]pF]+5+NFp0]]]]]]]]5]p]]F5+++]FX+55]555]5555l5+5+55S5]05555555+vNXX+lXlul+l+5555]5]]]55p]55+5]]]5]+5+5+5|__uu+ll5]5llXl+X+++5]55FFFF]]5؅{{x\;1|]]Fll_u_uXvvvvX+XXl55+55]F]F5v+NvN+]55vv++XlXll5X+55]555+N5S]]p55]p]p]p]]0]]p]]00ppp0ppppp0p]p0]%ppp]SF++F]pppp]pF5]5+]]pp]]p]]55p]5p]S]F]F+F5+p0ppppp$p$g$$p$ppp]]]55F]F]+pFSFmZ:Zp]00ppp0ppppppp]]]ZZ8Z]p]pp]]]]5++FF5]]]]0]5]]5]]]]]]]]5+]]]]]]5S|]FjS]]]0]]pp5++]5F]FF]5]p]p]]pp]0p]0p]]]]]]]]]]]]0ppp]]p]5BZ:-+FF55++]]pp]p5ll5l+5+5]FF5++SF+F]]]]]p]p]]]]]]5F55]]5]]550ppp]A:ZZ:ZZ]]550OODDDDD000jD]550ODD8ZZZZZ0]0]OKKKKKOO00DDDDD]5X+5lj30j]+5]]]0]0]0005]00]]55]00]vXvvvX]5j]j卍p000]]ll5gjwjgO$00p00]0O]0]55]]]p]]]]55+55X]]5]]]]p0]pp5XX55XX5XX]]]]]]]5]5]]500p]F]]5]]]p0]]p00p]+p]]5]5]]5]]]+5]]]l+55F5555l5X555X]]FF++5+555555555+l]5l++X5+v55++]++l]55]5555+555]5X555v+55+5]5+5++5+5+lF++5555+55؅{{x;l55X5X]Fllll+vv+Xl5]5+]+++l+5_lll+vl55v55]55]5XluNF]FFSp$SF5u5]]]]]]]]]p0p0]]]]]]]]]]5F]]]+l5F+]]]]pppppp%pp$p]p%p]p%ppppp0pppppp%pp]pp]]p]pppp00p0p0]Sp]%]]pppp%]]]]p]5]]]0pp0p0]5]]5p]]~rZ]pp0]p]F]]]p0]]]p]]]]]]]]pp]]5+Xl55]5]]]05]055]]]]]]]]p0p]]]]]]FF55]]5555FFFFFF5p]5]]5]]]5p]]]]]]5+J:Z:5]]F|FS]]]55]]pp]555FFFFFS]]5]]p]]FjSjSF]]]55+5F]]F5]]]]]]5555FF]\8ZZZ+]pOOO0]]]]SDDDOg:ZZ3+0500D00gKK0]]]ODDD]]]]5++55FmKKOOD0DDguF]55]]0000]]]5505XXXXX5vvX5S0pgppS+++5+500]JJJJJKp0]0O0p5++Nv+]]]]p]0]0]]]]]p]]]0%]]X+pp0]]XX0X0X55X50X5]0]p]+5F++5+555+5500]5]55555555]0]5]]]]55]5]555]]]]5+5+l+5lX+ll+X++5+55555+55X55555555+55+555555+55Xlll5X++5lX+X+X+X+Xll+l55ll]555+555]؅{{x\;1X+X+v+lXv+5]]5Xl++5X+5+5X50]]500]5XXXl]5+5+X+ll5+55X5F555]5F]F5l+5]]++]+5pp]0]p]pppp]p]]]pSp]Spp]]+]]]ppp]p]ppppp]]SF+F]5]]]ppppppp00p]p]]S5]]F]]]]]pppp]]FF]5FF5]p]N+++F]JZ-S]pppp]p]50ppp]p]0::]+]]pF]]]5F5]p]ppppppp]]]]FSF5+5+5+]5l5]5]5]F5F]S|F5]555p]]pp]5FFF]]F]]F]FF]5F5]]]]55]55]]5555]]0]:ZZZJ++FF]555]]55555p55+55]p]]0pppFF55]p0FF]]0]p]]]p55]]SSppSS]Spp]pp5D4ZZ0D0OOK00OOODD0]555ZZZZZkODDDDDDDDDO0DOOOg]]55005DOO0DD쀀]]]5++++5X50X5X555X555555X5]+XF]5]ppp]F+++5Nv555DDDOKJmmO0000]55]005++ÈvS5]]pS$S]ppS]ppp]]p0p]+Xv5]]]X55X+]5550]55X]500%pp]]S]]]]]5p]+]+++5pppp]]]pp]5555]]F+]++N+5+l5]]]0]]5+5]05+Xv55l+X+XX+X55555]55X5+l5++l5+55ll5l5X+5555lXl+X+X+5+X555555Xll5F]55l55؅{xx;X+X55X55+5F5]F55lX+vX+Xl++X+llll+X5+5555X+X+55X+5]5555]5]]0]55]]]]]]]]]]p0p]p]p0]0p]p]]S]F]ppg0%055]]p]++5+]++]]ppppp]]0S]SSSS]]5]]]]]p]ppp]]ppp]ppppp]SSSS55]pp]]]F]]++F]555SppppZF]p%0pppp0]]]p]p]]::Zj55]S]]]pp]]F]]]55]55]]]]]5]555555+]p]]]]]55]5]5]]]0]]]5]]5]]p]]pFFFF]F|@jwjjjS]]]]]p++5+5]5]]F]F]]FFgZ555]]55555]]]FS]]]pppp]]S]]]55FF+5+5]55]]]5]pp]]]]S0SgggpS]]pp0pDbeZZZZDDDDDDDDDD0F5555X]0D:8:ZZZZOOOOODD05]]0]]]]0]]]00OOOODDOD0ppp0DD000]]5]+FjSS000000]p0555X5X5555]]+55]+l+]55v055++l]KK000jj3D]0OO333300]p]]pF]]]]]0p0pp0]Sp0p0pp]]XX5XXX5]55]0XX5555]550]j555pS0]]p0ppp]pp]]]]]]]]]5555555555]+5+FF]F]55X+5555+555]5555]]]+v5+555XX+X+X+X+l+X+l+X+XlX5]55]l5l5+l]]]]55l5555]+]]5vl5]5555+X+5++5+5+]5؅{{x\;1иN+]]l5+X+X+l5555X+X+X55555]X55+X+l+ll55+lF55p]5+5+5F5l+5]]]p]]5]]ppp%p0p]]5p]F]]p]]00ppppp]5]]5+F]S55FX+F]pp]pppp]p]p+++]Sp%%%pppp]]]pp0pp0p5]ppp]$gpp$pppp]]]pppp]S]]p%p%0pZ:Zpp0]ppppp]S]0p0pFz:ZZS]5F]FF+F+N+]55]5]555555]FSF+++l++5+]5]]55]p]]]]]ppp]]]]]p]]]]p]]]5]]p0]]]55]p]5FF+]+++55lN5g@FFF]]Z]]5]]S]5]]]p]]0pp0p00]]]S|]S55]]+X5]p]]5]55+5F]p]]]]]0]0]Sgpgp]DDO48:88:Zv0ODDDDOKKKKK3gOOKrZZ:8:ZJKmw3F]0p]0]00]00]]]000p5F]S]0]]5]+jKJJJJJOO0]0]055]0]55Fluu5+5]j0j]0]Sjjjjl]0]0000]0]00D05]]00OgF+5]]]ppppp]p00]F5F]S]0]Spp]5XX5XvvvXXXX]5]]]]5X550]5]0]555+]+]0]5F++]]]]]]]]5]]5+5++5]5]555]p]F]5+05555++++++++5F5FF+5++v+l+555]p]0]5+55]55l5555555555]5+++5l5vl+]55+55]5ll5luuX+XlX5+55+X+G{{xƲ5l55555X+X+X+5+X5l55F05lX+X+5lvN+v+X+]l5]]]5l+555+55]5]]]5S]Fp]]S]]+5]p]p0p]]]]]5+55]p0p0ppp]]]]]]pppp]]p]p]pp]0pp]p+0p]S]]S]]pppp0pppp]ppppp5]]]]]5+5+]pppppp%0p]]5]]]55]::3p]++]0pp]pppp]]p]+B:Z]]]]]]]]p0]]]+5+5+++F+++++FF]55]5555+lFF55]55]5]]]5FFF0]F]5]]p5+55F5++N+l++F5]]5555]]]0]5]FS:Z:+5]pp]]]]]]]]]]]]5]5+]]50]55555555]F555555+]5555]]5]]55]]5]pS]00pp00:8:::ZZKD0O0000]NvN5]]55]l+++]F5K:8:8Z-0OO$000]00O0jS0S0KKKO0jF000DDDDOKKKOKOD0]0]]XX5+5]]]F+X+vvlF000O000+N+]5++NlNv5v5]]]0D]00000ODDD]5]F]+F5]]]0pppp]50pp]]]]]Xl5p]N+N++++]]00X]Xl5l500p5]p]5]]]]]+5F]]]]]]55]]55]]5l5555]p5+++]+5lll55l555555+5X+5+l+5+5+5+X+l+5p]5+5+5]]5555+l+5]++]5]55+Xv+55]55+++5555555]55X5]+55555X5+X]0؅{{x\;|vv+v++5]]]5Xvvv+X+lX+lX++X+55+5]555]%0555+5]lN+55+5+]]]]p]55]pp5]]]]0p]50ppp]pSSF]S]ppp]p]0pp%%pppp0ppp]p]pppppp]p0vN+++++++]]%0ppF]SF]]]]p0pp]p]pp5+55]]]pppppppp]]+FF+Spppppp]p]p0pp0ppj::(F]p]]]]]FFF]555]]]]p0]55]p]]50]]]]]p]]5l5FF]FF5555555]55]]FFF5F55555]55+5]]5]5]0]]5F55]p]5]]]p]]p5]:5+]]]pSF+F5p]]]]]]]5]]]]]]0]+5++F+5+55555]]]5p]5555]]]]]]]X+5]5g3eA:8:0DDOOOODDD50p]0DDDD0pO:8:ـDD000DD0]5l5++++X5+]0F0ODDDOOOKOOOjgOKOOOp0p0D050j5X55555XXX5jj000005]]]0p000]D0XS]X]0]]50DDOO0$0g]]]]0gOppppp0]p]]]]p]]0p]p]p0]Sp5vXX5Xv555555555]5]55]jj]]5]55]]]]]]]]+555]]p5]FSF5555]FFFF]+]5]5]5FF]555]55555555]v++vNv5]55]+X+++5+++X+55+++]]5XllX]5]055lXlXl555l+5+l5+5+5p5FF5+l]5X5+X+X+Xl5]G{{x;+55]]5l]555++5555v+55555++X55+5555Sp]F5]]5]]F]p]5pp]]]]]]]]]pp]55F+]]F5]5]p]]SpSS]]]]]0]++FFF++]++]F]p%0pppp]pp]0pppp]ppSSpSpp0ppp]ppp0pppppppppp]p]5S]Spp0pp0pp]pp]pppp%pZ:]pppppppp]]]+5++vFg::5p]]]]]p]]]+5+5+F]5]5]55]]]]5]555555555]5555555555]5555555F5F5555+5]5+5]]5]]5]]5pp++5]FF++]F5p]]]5p::]FF5]]p]p]pp55]]]]Fjj]]]S]+]]]F]55]5]5+lll5FF555]5+p55ppppppD0SƕrZ:0000DOODD0D05]DDz::8:8:0000DDD0000D00]55]]0OOO000S]5F]]000DDODODKKKKKO305]0]05]XXXX5X50X5]j]0000D00D0D]000N+p]]5]0]000]]5j0pDDDDp]p]]]0p]+NF]]pp0p]p]p]]]]]F]5555Xj]X]555X5X00000j@SS]]5F]]]]00pp5]55+5]5]]55]5555555]]]]]555ll55+5+X+X+5]55l+55+X+55]FF5+]]]55]]]5X+++]F]]55l]+]5+Xl555++]5]5F]]5X+Xlll++l55FX+lX5+5XllF|G{{x\;v+X+X+v+]]5]555v+55l55++X55X+5]p5+55l5+555555]]]55]+5]]N5]pp]]0pppp]pppppp0pppp0pppppppp%p]pp]]F]]]]]p0pp0p55ppg$ppp]]SF]]p]pp55pp]]]ppp0p0$p]]]0p]p]pp%p%$p$p]]]]]]]]]F]F++:ZU]]p]]]F]5p0pp0ppp]]]3Z:Z]p]]]0p]]]]]5555++5+++]+FF]]p]p0]F55F5]]555]]55555]]pp5]XNl5FFF5]55]]5F5+5+55]555]]]pS55++F5pp]]]5S::Z]]]]]]]]]]p]]+]p]]]]p]5+++]]]]]]5]]5]]5l555FFF]]]]55FFF++]]]]DDDDD:Z00p0]+55]p00S5]500jK8ZZ50OOKg]F]F0OOKOO0pgj0DDOKOOODDD$D0]555]]5v+55000]55]0j0]XXXXv5X0]00]5]]]0j3]X05]]555F5FFFSS00j000]jl5S5Sp0]]p0FSp]]F5F+5]5p]p0]]]5]5j55]55+X055]5]5+F]]ljX0]5]5]]F5]++5FF+++F]5]]555F5F5l55+5+5l]lXlll55555]F+++55X+5X+X+v5l+5+X5]]5]ppp]]5pp]5]F5+NÈv+55555+]5]5555]5]]5]5v+X+l5555X]+5X+]0]]؅{xxƲ|+XX+Xvl]]]+++5+l+X+X5+X+55FF+555+X5l5555+5]]]p]]5]]]]]F]F]5]]5]p]]pp0]p]0p0]]5]]]F]pp$0p]5]]]]F]]p]]S]]p]p]]p]p000p]pp%pp]]5Fpp0]5pppp]p]]]p0p0]]p]55]FFp]]ppppppppppS]pp%p5:ZZkpp%p$pppppp0p0ppp$Z5]ppS]]55Fp]]]]]5]]5555+5]]+]]]l5+l555+5l+5]]55F]555]++]++X55]]55]]]55]F+55555]]]5555]55]]p]555]]]F5ZZ+555555]55]55]]S]]]]p]]]]]]]]55++NN55]]+]]v+X+50]55]5SF5]5++FSpp0DD8Z8DOO0000]50]50D8:8:ZDDDOKOOKOOOO0]]S00OOOKOOOOKODD쀀0gp00]0DKK5X]XX]XvvvXXXj]0]55XX5X+X+55]050]]5+]]5l]S]S0j0jj]]0]00]5+l+5]FF]]0p0p]pp]p]]]FS]X5XXl55]5]55]5]]]5]XlXF5]05]p5]5]]]]]pp55+F]]]55555]]55F]]]555555]55F5++5F555X5Xll]]]]]5555]X+X++++5l5vN+Fpp]]5]55X5555+l5]]]]]]SF]5X+55555++v+]5l5505N5+5]5+55]؅{{x;]]555+Xlu+5F+X+l5++5+l55]p]55]555+55]]+5p]S]p]FFFF]]pp]]pppp0pgp0p]0pp]FFFFFSppp]]]]5+F+FSSp]ppS]]p]ppS]SSp]F]+5F]ppp]]S55+F]ppp5+5p0]S]]]SS]]p]]]pSp]]pppppp]pp]%ppp%pppFF]8:Z]p]]ppppppp0pp0]]8]p]]5+5++]++]]p]]]]+]5555]]5]l5555]5F]]]]F]55l555555]p005]]555]l]]555]p]5+5FF555]]]55pp]555F]+]]]55lZ:55F5+]5]55F55F55]]]]]]]]]]]S@j5]]555]]]55l55]]]]5F]]555]+5]p]]]kJkJ Z:8::8KKKOODO쀀DODOODDDDDDDDJ:8:ZOO0]00ODODDDDD]5DDDKg0j000D0DDOK05FS000pODDDD000]0]X55XX5j]5]]]]5X0]05]]00DD0]500j]jSj]j00050000]]]0]0D00p]]]0F]]p]p]p]]5+]p]]]]]5]pp]SF55XXvXX5j5j]X]X]]]]j]]S]]pp]55F]55]p]FFF5]55]p++F+5]]F555]55++555]5++++]5l+5X5555+l+l+l+v555+]55++555l5]+5]55+XXlX55lv+5]5lX+l5+5X5+55]5555X5555F55]]]+v5]G{{x\;55+5l55lul5++5+l5+XX+5+l]5+]5+]]555555++]]5]F]]]p]pp]pp]+]5]pp0]]]pp]p]]ppp]p]]pp$0ppp0Sp5]pp$pp$555Fpp0pppppppp]5pS5]pp]ppppp$$0]p]p]pp0Fppppppppp%pppp$$$ppppp]p5F]]55]]]]@:Z:ZS]]]F]]pp0]ppp0$pp0p]Zj5]]F+]]]]]FF5]p]]]5]555]555+]F5]5]FF]55p555++l]]55]]]+5+]S55F]55+l]5]]S5+]5F]]5]]F5F]]555555]555555ZZ5]5]5]]]]]]]]]SFF]pppppp]]5]5]]]]5]5]]]]]+5]]]]p]]+l55F]F]p]]]]ppzZ:8::zBUz3pj0DDOOD]B8:::::8DDDDD0쀀DDDO0O$DDDDD0]]0pD0]DDODOOD0DDKOK00000]0]0XXX50]055]0]50X]]5]jjOj0]0j5500F0]]]]3KODDD]]F+]p]]0pp]00p]p]0p]]0pp]5XXXXX550]0]]X]XXXj0j]005]]F5]F5555]]5]5F5+55FF]]5+555555l5+555+55l5+55+5555X+5lX+X++55+++5+55++X5+5+5555+Xl55+555+++5+555lXl55+5]]555X+5l5lll5X55+55++]]p0]F5]G{{x;1+5l5v+X+v5555]F55]]5]]+]5F]]]]ll++5++55]]]]]]]]]5pp]]F]]0ppp]]F]F]]]]p]]]]]]]p+50p]]p]pp]pp]]]]]0ppppp0pp$p]pp]]]pppp]]]]]p0]]]]]pp0p]]ppp]F5]p]0ppp$pp0pp%pp%p0ppp]]]p%p]5pZZppp]pp]p]]]]]]]0p]p]z:::gS]p]]555FXl]]]FF555F]]]]]p]]pp]+5+555]]555]5++F+]]]++5++FF]]5]555555]0]]5]]]+5]ll55]]5FFSFF]]55+r5+5]]]5]]F]5]]]]0p]0]]]p]]]]]5]]]]]F5]+55]]]+55F]5]]S]p0ppppp]8A:8::r1zrJJK0D쀀0Z:88Z0쀀DjKKOODDK0]OOD00]D00p0FF0]ppDDD0g]p00S]]]SSjjXXX5X5v5XXXXXXjj005]005DD0K0jjj]]0]]DS]pDpp]]]]]p0]p0pp0]FF]]]]]FXXX5X5]]lX5555j55X]5XXlj0S]]]55]55]SS]5+]]]]]55]55]55]p]]]5]55555]55l55]]5l+l555XllXX+5555X55+555]F55F5]5+v5+55++X+5Xll5X+Xlll555l55555+55+55555+5555+5+]]S]]]p5]G{{x;~+555+X+v]5]5++5++]]55+]5l+l]55555]|F]]]5]]]]FF+55]]5pppp]p0pgpp]pp]]F]]p]ppp]5+lp]]]F]p]]]5]]Spp0p0p0]]p]]555]pppp]F]p0pp$pp]]5Sp55]Fppppppp0]p$%ppp]p%0pp]p]]pp]]SFZZk]]]]]]]ppp]S]p]]]p]]]BZZ::g]]]p55]]5]]5555+5FFF]5]555]]55F]++FSF5]5555]]F5+55]pF]]]F5]Fp]+5]5]]]5]ppp]5||SS]]]]]]]p]]]]]0]5F5++:55]5]555+]5F]p]]]]pp]]p]]p]]SS]]]]5555555555]5F5]]+FF]]p]]p0p]:8r48Z:r88rz1JB:::Z:D000000]000쀀DDODDDSDD0OOOOOOB0]0F00000]]]]XvXX50]0]0]5]]]]5F]]Jg]00000X5DDO0]0]]0j]]0jD0]000]]F5]0p]5+]]p+5l5]XXXXXX05j]55]5]]0]00500S]]]5555p]]0]5]55]pp]F5+55pp]]5FF]+F+]+]]5555]5l5X5lX+5ll+55555555]5+55]+5+55vv++55X5++55+X5]5]]555ll5X+]5F55+5p]+5]5X5l55ll5]5+++5+]؅{{x\;jX555]555l+++55+5555lp]]55++55]0pp0]0]]55]]5F55]]]pppp]]0p]p0p]55]]]pSF5p]S]Fpp]%p]]55]]]]]pp]F]@p5++5FF]pS]]pp+5pp$gp]$0ppp0pgggppS]]ppppp]]]pppp]]F]F00]S]]pp0p]0%pp%ppppp0p]ppzZ:Z]pp%ppppppp]p0ppS50p]]gZZZ5F]]]SS5lp]55F5]]]]]55+5FF5FF5]]5+555555]]FS5+]]555555]55FF]FS]]]]]]]]]jS]]]]55+]555]55]F++ZZZ+F++FF55++5]5]p]]00p]]5F%p]]FF+5]55]5l5F]]]]]]]]0pp]]5]]p]]]p00Z888:Z:::::rr:8::888ZzkkJJKODD00DDDDDDO쀀00033000550OKKOODD]0000]00]505X50XXXvXvXXXXXX]X000]50000DSjg00KO]lF0jO0+5Sl]S]]0]p]0]]]p]pp]p]]5]55]]]]]]5X5]X555X]]XXXX5]Xv55]5]05]5]5]55]]5F]]p]]]p]+5F0pp]]]55]5555]]]]]++55p55]]5]]]l5XlX5555555555]]55]5l+5+55F++5ll+++5]5lX+X5lX55+]5+5l+55555l]]p]5+5]5+5Xl55+X55X+X+]؅{{x;j55+v55555555+lv+5]]l55llXX5+55]]]55++]FF]]]+5+]pp++5+55]p]]F5+5]SSF]p]+5pp5]]]]]]]]]]]p]]+]]]]p]p]p0pp]pppp]]]]]]]p0]p]0pp0p0p]]S5+]F+]5]ppp]pppFF+]]pSp]F]]]]pp]pp]]]]+]ppppZZ:5ppp]]]p0]pp0]]Spppppz5]p]]F]FS]]]p]0]5]555]]S]55+555]5]]55]5F]F5]5F]55]555555F5]FF5555]]5]]]p0]p]F]]]]55+55Nl555Sp]+55]]]5ZZ]]+]F+++5++5++]p]+5555FF5F5+]5]555]]]5SSF5]]]]5]]]]F+]]]S]p]pp:AZ8::8:Z:::::::Z:8::::88:JKOODDDDD0쀀OD0000S0]DD쀀DDDD0j0]0XXX]]0XX]]]]0]]X]]5]]0]j0]0m0]0]05005]50000jSp]]5pp]0]p]]p]]5]]+]pp]]]l5]]5XX5]]555X55]55jlj]5j]]jjjjp]]]]]]]]5]]]]]F]555]]555]5++FF+F5F5F555+55555+Xlll5555]]]]]5+X+5X+X+5]55ll55+555+5555X5lX+Xll++]+5+l55+555+5]55l5]55p]p]55XlX55]5]]555G{xx\;N55+v+X+X+555l5X+5lX+X+5]5lXF+555+]5]5]5]p0]]]]]]]]]]p]ppp]pppp]]pppppppp]pp]%p%pppp]ppppp0@pppFp]]]p0ppppppS]]FFFFpp0ppp]p]p5+]p]]]]]0p$p]+]5]%p0p]pppp]p0p]p]p]]]]]pp]]p5ZZZS]S]pppp]S]]ppp]pp0ZZ]]]]]]]p]5p]555]F5]p]]5]]]]0p]5]555550p5+55555555]5F5FF]]]]]5]]5l+5]]p]]p0p]]]55]]]]]5+F]F5FF]]]]]]+ZZZZ]]]+]]SpF+]]]]]++F5+5]5F555l555+]]]]FF550pp]]]]]pp]pp]]F]pS5F]]:::AZ8:8::8:Z8Z8:8::::::::::88kkkJJkKJKOK]0000DDj]055XXvXX]X5XXXXXX5X05XX5]X5]]5003S$00D00쀀00000]00]p]]]0]5]55]0]]]]]]p]l55v55X5]55X]j5XX5]]]0]505]50]]p5]]]]]5]]]5]F]]5]]]5FF+F55]5+FF+]]F]555555555l5X55555]]]]555]5+5X55]5]]555]55++X+llX5lXl555+5+5+]]X55X555]55555+5l55+55+5l5l5+5]]+5]؅{{x\;1j5l5]l5]]++F5+555l_X55555]+]55X555]]5]5F]]F]]]F]]]]]]S]]p0p]p]pp0ppp5ppp]pp]]]F]]ppp5]p%p]0p]@0p0ppppp]]gpppp]+]p0ppppp0p0pp]]p]pp]Sppp0pp]p]pp]+]p]]pp]]pp%]ppppppppp5Z:Zr5]ppppppp]ppp]F]Sp5]]]Z]ppp]5SF]]]]5S]]5]]5]5]]]]p]]]]5++++5p]5]5]]+++5]5]]]]]]p5]5]F55p]]]pF+5]]]]5]S]]5]]0]]]pp5]p]]]]]Z]5]5F]FF]]]]5FF]55]]]+55+0pp]]]]]]S]]5FF]]ppp]]]5]p0p]]p3pppp88y8rZZ8:8:::::8:Z:::8::8:::::::::::::8:8::rrkkJJJOOD0XvXvXXXXX5XpO00000Ojj]00D0500D0pp]]0+N+p$pS5]]p0+]5F5]]55XXX55F+5]5]555055500555]]]5F]FSp]]]]5+]5]]]5]F5F]p]5]]]]5]]]55F5]p]55+5555l5+5]55]]5]]+]p555+55]5F5X+l55+]]5]55X+X+55]555X5X5]F++FF55+55+0]+FX+5ll55F]555]F]5+5]؅{{xCXl]5]F5l+55l]]]]5Xl]+55555]555+F]]5ll]5]S]+pp]]]S]]5]5]]pppp0pp0p5F]]p]p]]ppppp]]]]]%]ppp5S`@p0p$p0pSS]5p]]pppp]pppp0pp5]p+F]F]+]]pppp]ppp]]pppppppp5]]p]]p]pp]p]pp]p]0::]S]ppppp]]F]0++F]]0ppp]kZ8ZZ]+]]5]FS]]F5]F]]]]55]]]]]FS]]]]F+]]]]]]]p]5]p]S]F]5]0]p]55]5]]]0ppp]]]5FFF]p]]]p]p]F]]]@F5]]]]5+]S]zZZ5]]FF]]]p]]]]p]555FF|55]]5]05]pp]]]p0pp]0p0p]0p]]]]p]p]pppp0]8:8r::Z8:8:88::88888:888:8888:::::::::::::::: \ϖϓ{xxx͕x\D00]000000D00050D0DDD000]]00]]]]]]]]]]]555XX55XX55XXXXX5]55]55j]0]]]]]5]]]5pp0p]]]]55]+F55]]550]]5]55]5F]5]5]5555]5]]5l5]5]F+5]+5+555]55555555]+55+555Xl5]]p0p5X5555+555l5F5F++F555]5555l5l5555l+555F5++XX5]{{x;~j55]55+5+5555+5]]]5555SS++l]5F5]5S]]F550p]]]]]5]]5]SSp]]F]F50]p]]]]F]F]F]p]5Fpp]]pppSp]]+pp05+]pg]p]]]SS]]S]ppp]]]p0pp0pppp0]$0ppp]++Fp]p0]5+]pppp]]pp%]+SpFFp]]5]p0]FF+]]pp5Z::Z]]pSpp]]]]]]]]pppp0p]]@l]]p]]p]p]FF]]S@j]55]]p]FF5+F]F]]]5]]5555FF]]F]5]]5FF]]55]5]55pp55SS]p]]F]Sp]p0]pppp]]]]5+5+]ppp](Z::ZFF]]]]]p]F]F+]F]]F555+5+]p]5]]]FF]0]p0]]p]FFp]pppp]p]p]pp]5+]F]8:::rAZZ:ZZ:::::8Zr8:::::::8:::8::8:::8::8::8\ A{{7{\\\\\\ƲR::88881 18׺KJDO$p0p$p]]]5]]5XvXXvvXvvvvvXvXXvvvXXX+XXXXXXXX5Xv5F]]p55]]]]+]]+]p5]+pF]55]]5]]555]p+]]5]+5]]5555]+555FX+]5555lX+5+5]55l5]5v++5+5+F++5+555F555l55+555l5555+F+55ll55+5+55]5++5055]FF5G{xx\;1+5555l50]5]5505+55F55++]]]55]5]55]F5]+5]pFF5]]]]]p]]00SS]p0p]0p]p]pppppppp]pSp]p0pp]pppFF+F]ppp0pw@pppp%pp]pp0pp]]ppp0p$]5]p]]pp0p0p]pp]p]]p]]p]]p]ppppp]5p]5FSp]0ppp]pppp+5pp]pp]]ppppp]]]]p]ppS]]]5B8Z3]p]p]]]]p]]]p]]]]]]5]]]]]]]]F5]]]]p]5+]]]]p]p55F55]55S5]]p]]]]]]]p]]]]FFF++5p]]]0pS]]]]p]]]5p]p]]3Z]]]pp]+5]p]p]]pp]F]]]55+l]5l555jS]5+F]]ppSp5]]]]pp]p]pp]pppF]:AZZ8ZZ88::888r:8Z::88:::8:::88r\Ʋ7xxxxx૕\\\\ 11:::::r1r:::r:Z~::::88:m3wwȓȓȖ7x7774xw0S]]5]]]]pS]5]5]p]]5]]F55]5+F]]55]]]5]p]55555]5]]]5+55X55]+]5+5]55ll+55]]]55]5l555+]555+5]5l55555]55l5555+555X5555]5F55]5555]+]G{{x;l++]5lF555F]5555]p]]5]5]5]]5]]]5]]]]5]]5]+Fp]5]pp5+]]]5]p]]]p]p]]S0]5]pp]]p]p0]pp0p0p0pp]]ppp]pw`]]pppFF]Sp]]]pppp0pp]]0pp0pp0p0p]$pp0p0pp]pppp0pp]]5pppFF]pppp0ppp$p0]pppppZgp]Sp]]F]5]Fpp]Sgp]]FF5]]gZ::Z35]55]]]]]pF50p]5pp]FFp]5]5]p]]]]]5]55F]F]]p]]5F5]]F]5]5]p]p]Sp]p]]]]]]]]]pp]+Fpp]p0]]]pp0]Spppp]+]3Z:F]]]FF]]]]]5]5]]F]p]]]]5X+50]]]]5p]]]F]ppp]F+]pppp]]]p]pppS]]p:8:8AZZ:888::::8:8::88:8::::::::::88:::8::::::8:8:\ƚ xx\ƚƲ1rz1r8RR1r r:888r1r8:::8j3z@5FF]Sp5x{xxͭ୭\\\\\ABp]]]]]]]]]]]]]5]55]5]]]F+]]S]]]]]]]5]555F]]]+F55]FF555555]55555555555]5F5++]]p]]555+555]p]55]F55]5l5]]55+5]]]]5l55]]55l+5FF5+]55555]؅{{x\;155555l]5+55555]p]55l+]]5555]]F]]]5F]+5F]S]5]+F]]]]+]]F]5]]]pp]0p]]]pppp0p]Fp]F]p]]p0p5Fpp]]]p0pST]p0ppF+]5ppp0p]]pS]p0gpp0pp0p]p+5p0p]pp]p]]pp0p]p]5pp]]p]Sp]]]0$S]0p]pp]50p]5ZpF]]]]pppF+]p]S]p]pp0pp]FrZ8::]p]5]p]F]]]p0SS]p]S]]p]]5pp]]]]5+]p]]55F]]]]]]]j|F]5Fj]p55]S]S]]]F]]]ppp]p0pp]ppp]pppp]p]]]p]pF]]Z:Zg5Fgppp]]pSS]]]]]]]5F]0]]]p]]]5]]gppp]]+5pp]pp]]5]pp]]]ppS]]S]:rZrZ8::r:8::88:88:8::8888:8r\;Ʋx\\\\ 1r 1:R8r1r88: r1 18:35F5zZ8S5l5F]5X+550]xxx\x\\\ 35]F]p]5]@SS]pp5F+5]]F]]]]p]]pp]F]F|]]]]F]]5Fp]55]]5]]l+55]55555+555ll555+5F5555ll55]55]555]]l55]]]5]5]]555F]]5555]5+]5]]]+555]]]5555]7{xƲ55l555+555+]5F5555p05F555]FFFFS]5Fp]]]]]]]5]]]55]]pp0]F]]p]p5]5]p]S0p0pp]]ppp]]pp]pp]Sp]pp]pppp0`Igpp]pp0p$S]p]5]p0p]p]p0ppp5]pppp0]p0p0]F]p0]F]]ppp$p+0]pFFFp]S]]ppp]]p]pp]pp$p]F5+]ppp]pS0p0+ppp0r8:]pp]F]Fpppp]]p]]]]0p]S]]]]5FF+S]5+]5]]]]S]5]5]]]FF]SF]F]]Fp]F5p]S5ppF]pppp0p]pp05]p$ppp]F5]p]gZ:Z]]]]]]pp]gS0]]@]]]]SSg]]]FS]]F]]]pg]]pg]ppp]F]FFp0pppp]pp]$$]:8::8:r88Z:::::8::r8::8888:8888:88:8:8::8:::::8::: \;Ʋ xx\\\Ʋ18Rrr rz::z1z 1r8zS5]p]B855]0]]5]]]5]]pcxxxx\୭\\\\\r]]]]5]]SjF5]]]5]]F]5SS]pS@]5]+]]]5+5F]]]5FF]]p]FFF]F]]F555FF5p]]5]]+F55+]]]5]555+55]l5555F55]F]5]]]5]]5]p+5555555+55l55555555l55++|+5؅{xx\; j+5555]5]55555]5+5]p55]]5p]FF]]]]]]S]5]F5]]F]]]]]]p]]F]]]p]]F+]Spppp]F]pp0pp]]5]pp5]pp]F]pp5]05pgT@pp]55+]p]]p]+pp]p$]pppp]]]]pSpp]]]]ppp]pp]S]ppp]]pp0p]pp]ppp0ppSFFp]pp$pp]p:pp]]]p]S]5ppp]p0pp]p]pp]558:Z]]p]F+p]]]p55S0]+]Fp]]]]F5p]55]]]F]S]]F]]]]F]5]+5]Sj5w]]]]p]55]@g]]]p0p00$$pp]5ppppp0$]p]]]S]pp]]ppZZZZp]ppp]p]p]p0pp]]]]]ppp]0p]]]]5F]]]p0pp]Spp]]]p]p%p]pppSgpp]ppp8::::Z8888:8:8::88:::::88::8:88:88:r\\ \\;\1zrrrrRRf818z111:1\1r1zr::5]5p]+5]F]]5]]]5]]]]]]x୭\\\\\r]]5+SS5]]]]]]]]]]]]F]p]]]SSF]]]]]5F]5]S]5]]55F]F0]+]]55]55]555+]]]555+5F]5]Fp]555]55F55]55]FF]p]p55]+0]55l]5]S5+5]S555]555555l55؅{{x;5+55]l5ll]]5+]55]]]F]55]F+]]]5SS55]]]SS]F]]S]5p]]]]Fpp]p]p]pp]]p]]]S]S0pp]p]]]pp]]ppppSFppp]p5]EISpSp]]ppSp0pFp0p0pppp]]pp$p0]0]ppp]]Spp0p]Spp0]p0$%0]]pppppppp]p%]p]]p]Z:::ppF]pppp]pppp]pp00Sp]m]]p]p0]]]p]]SS@S]]pp@F]p]]]FpS]]p]]@]@5]]]]S]S]]]0]]j]pF5]]p0pg0]]Spppp]]%]]p0p]]]]]p]]pp$0p]p]3:Z3]p0]Fpp$]pp]p]S]p]]]p]ppp]]p]5]p0]pp]00]Fp0p]pp0p]FF]pp]p]p$0:8::8r:Z:8Z:8:::8::rZZ::8:8::8:::::::::8:::;\ \xx\\\Ʋ11zr8r11r8rR8\8r1r111rr:g5]]]S]]]]]S]]]]5j|S]]FFx\\\\\\;;1m5]]F]jS]0SS]pp]p]pSp0]]p0@]]]S]]Fp0]]]]]F]]]]]5]]]]]]5F]55p5F]]jFF5]+]p]5F55]FF]]+F55]55]55]F]+55]+F]555]5FF]F5]]]F5]|555555S5]]]؅{{x\;|5]]55555l5F5]55]S55]F]]]]5SS5FS]S]5FSS]SSgS0p]]0p]pp$p]0$$pp]0p5ppp$p]p]]]0pppp]0pp]]p0pp0pp0ppppE@]p]p0pppp5]pp]ppp]p0p]S]]Spp0ppp]ppgSpp0pp]]p$ppp]p0p]p]p]p$p]$pp]JZ::Z]ppFppp]pppp]pp0pp]]p0SpZ8ZpFFpSg]p]]p]@j3p]5]]SS]]SF]SF]5p]F]]]]]@F]p]]0]S]]p]SS5]p]]]p]]0pp+p$p]]0ppS$$$pp]pppppp]p]pp]+Z::Zp]ppp]0p0]pp]]pSp]]]pS$]gpS]pS]]S]]]]0ppp0pp]p]]pp]Fpp0p]ppp8:r888Z8:8rZ:::888::8:88:8::8:8:8888:: \\Ʋ;xxxxx\\\ 1zr81r18RR\1r11r;88:r8r::8]]]p5Fp]]]p5p]]]]]0]SF]0x୭\\\\\\\\\A1rS]]p]]]p]]$p]p0F]]p0]]p0p]p]0p]S]pp0SF]SS]p]F]]]F5]FS5]]]]]F]]]]F]]5SF]]5]]FF]55]5SlX]FF]F]]F]]F55]]]5FF]]55S]5]]5]]F5|5FF]|F5]]555]؅{{x;]55F+5FF]]]S]F]SS]|]]F]]]]Sj05]]SpSj]]]SS]]]S]S0]p]0Spp]$0FS]p]]pp$]pp]pp%0ppSp0]]$pppp$pp]0p0($$$$$$$p$$p$$p!p$&$pMfE<WRT%p$$$$$WRRRRRٳRR::88:8:88r8:::888r:r rzrr:Zrr8r:8\r:8:88RRRRRRRRRRRWWWaWWRrfRf8rrrrryfWW8rR:y1RRRRRRy11118:8:+lluluuluululuuuuuuYHRHuuuuuuuuuuuuuuuuuuuuuuluululuullllluuuuuuuuuluuuuuululuuuuuuuuuuuuuuullllulllllllllFF|FFF|luuuluuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuul_lululluuuuuuuu_uu_uu_uuuuuuuuuuuuuluuluulull+lulululllF|F|SFSS@pp@pp$p$p%p}}66a%T%$$fRRRRIII?##h#}L}6R%ML%$$$UUfRUUURR&RRI<a`MhhW##RR!<%$$$$$$$$$$$$$$$$pppUSRRRRRRRfRp`<$$<fW!MaaW<66RfRRR::$$$$$$$$$pp$$`ppMf}QR%p$$$$$W8RRRRRR8R8:8:8888r8ZZ:888r1rrϕA8ZZZ:118\r::888RRRRRRRRRRRRRryR8zrzr8fWzr8rR8&:RRRRRRy 1 1z8:8:|+uuuluuluull lY!I+vNuuuuuuuuuuuuuuuuuuuuuuulll+l+l|+luuululuuuluul_uuu_uuuuuuuuuuul+uuluulullullluululll||FFFF|FFFluluuuuuu_uuuuuuuuuu_uuu_uuuuuuuuuuuuuuuuuuu_uuuu_uuuuuuuuu_uuullll|uluuuuuuuuuuuuuuuuuuuul_luuuluuuuuulllluuuuuuuullFFFSS@pSppSpppppppW}h^%6%T`$$$fR&RRRRRR%#RC}yRWRrz8:Z18r81 rzAS]]FRRp]]Rf]ppRR]RRRRRRRRR%U(%(cR-%$%L6Uf&}}}!(2aER}}}R#R&WRRRRf%RRRRR%%R%%R%pR&R&UWR%$p%&ppS`TS`&F5`5F&EWl|FF|F|F|F||luu|lul|F|F|F|FF@FF@SFF|FFF|F||F|FF|FF@FS|S|SSSS|SFFF|F|llllllllllu+uullu_ululuulll+lllullulllulluuluuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuulll|llll55+"V###}##}R#}##W}RRRRRRRRRRRRWwLhRLI&gppppppp$$%%2V}2 VW0M#6&6ERRRWs}W}RRW?}R6WRRfRRfUWhfE¿Rhf-Whh^RWV(IWfRRRRRRRRRRRRRRRRRRET&RfR&&RM%$UR$W$$$$$$}R>#}yRR8zZC8811rr|]&F]]R]]gRR]]RpRRRRfRRR&%%~A:ZRWZ%%f%a&}a%%(:ޠR?@%fRRRUEERRRRR6RRRRRRRRRRRRRRRRRRRRRRRRRWIfRRRfRRRRRRRRRRRR&RRR}WR&WR&IRR6WR}W}RRZޠWWWRRRRRRRWRRRRRRRRRRRRRRf&RUfR%$p$%$$$$$%%$$$$$$$p$pppp@S@Sp@SSSSSF|FFFFFFFulF|FF|FSSSSSSSpSSSSS@`]F`I&@FFFFSFS@FSSSSppp@pSppp$@ppppp$ppppp@p@SpS@Spp@ppSp@pS@SS@FSSS FSFSFFFSF@FF FFSSFSFFSFFFSFF|FFFF|FFFF|llll|FFFl|FF|FFFFFFFFSFFSFFFFF|FF|FFFF|F|FFFFFF|FF|FF|FF FS|SFSFSF|F|FF|FSFF|YEWWRRRRRRRRRRIERRR+hWRRRRaEEEV}R1VaWfa}}}WWRI?}6^[LWWWWWWWRRRRWR}RaIIRRg0`L?V##VV#"<%%%(&RfIE}}}}}}RRRRRRRRRf&ITTpppSS@$$$$$$$$$$$ppgSSS@FF@F@SSS@S||uluWRW#R}}}}WR6##Wf%RRRR(RRfRRRRRRRRRRRRRRRRRW}WfUERRRRRRRRRRRRRRRRRfIRRR(RRWRURRRWRW}WR}WRZ}#}RRRRRRRRRRRRRRRRRRRRRR&U&%&RU$$%$p$$$$$$$$$$$$$$$$$$pppSSpSp SFSFSFFFllllFFFF|FSF@SSS@SSSpS`]]`zۓԌ̀͵rOO0P/0pIrjS8A~DpDr|\}{ղޣuk@mD,E&'#qqK9'()$)TKTѸײػdN3O.- sGt{P0i%l" "ǥɐhV=W,, _/y,|   Բ|e~H6G|Q~zDׁ023mkm¼ãnco)$)҄nCP,Q uxv212dې޼ehAj !xv||͝п^=4>$1$;G;zRRR&7'N}Nlkvvyv(2(<7<Ɵgh:J9"/"' "%D#7j6QOa^rpBsB̦XW>q>8k6;w9;:@>CBFDJHMKOME{C282ةێmkSPFDNLfdnlsoni_ZQOA= }}vs_\XVQPsq֎䘛WT:7.h->[=~ifKH@=#/#bab_0-+)(WULH-)2/*.,%"!%!W  \$\-m,KE<8D?>:)$  [ > ; ^   d:r9yuvsxusoeaJE -(+%&" &DxDqqх쓘xu`\MIHCQM[WXUNJR  (5N4{+\ ]PDnpTr% ]ZX^+܅)(m)O5Q}I 56kIia+ ׈h0cdbB_A'=sSsG߉(//Д|>`"^  sYroܗ8=:by3o)&2+1ڸXa+T ZJUסĨGT$B q\hߦ|FsQ$9 {ᬽGcU'4 }gj֥։[^e531 cRSٯԟvXxQ.L81'  E<9Ͳm:ky'Pk=\1W;Ti_SďsOc2JO A>6imWzxSb5blobby-1.0rc3/data/gf2x/font13.bmp0000644000175000017500000000337012042452401020131 0ustar danielknobedanielknobeBM6(  l%kNM2/%#  3ӵ#]WKED=@7?44*   ^K,ҭ !y).~1ql+SH3-    r?pjlG3Ŀ79[g\Lwt;MC%)* 2"/nQЦ7/&S1G]x֚_[W>10$ wyT̲5(jX`׷bZ]/24 D+>^Ǣ8Q!BfR{)+B  qt–GV*J pmcϠ_?#Q iJ`b[SOJE)$    )!A9O;Jg,;9.|s||ql[UGB>9F?WPocjX=_,8Z5}uΜ➟喛㐘㋜Ҋsd?W5 blobby-1.0rc3/data/gf2x/font50.bmp0000644000175000017500000000337012042452401020132 0ustar danielknobedanielknobeBM6(  !   "lqQSFI?@JLZ/[P(QR3TA524>AźHQTZ   }{|~""[F[ammdbdNd?0?blobby-1.0rc3/data/gf2x/ball01.bmp0000644000175000017500000000341612042452401020073 0ustar danielknobedanielknobeBM6( xw{fehcbe |{}lkmdbeb_b\Z\vuvqpqifhgefpnnljjgeezyytssmlldcc[ZZyxwxwvhgfeec``^rrpggeuutbba\\[ vxu ^`^z|zoqojkjefertsehg z||eggGHHwxxopp\]]suvkmoz{}[[]ssunnokkleef}}}|||{{{xxxtttrrrnnniiihhhfffeeedddccc```___]]]YYYWWWTTTPPP222$$$ -%EE8*6D$V*+{F)7a%&+FE- 44 l)-993ق)E-44 قEE_33؂D+,FU4A)hɗP؂p\-4قSz'D7+¼P9y&Eff×ׂp%zddǖfPՂ)Cd/gPb~/ccbb~c0T˚bbb}// b}c0IIJcLbdfJc~~dIbIBLGLblobby-1.0rc3/data/gf2x/font14.bmp0000644000175000017500000000337012042452401020132 0ustar danielknobedanielknobeBM6( )8 8v Y:2+ &*+12\S1 wc^dt  2 ?o.arQN/!.6(5(6+8,>2@6;..Ai;7.ajKlcR0 $=S1pPjsustmql\74 +HOnBnOD2#;    &)|hVK!,rY`Q 6='p[% =x^?4 e AS/_I  & X? A9~"'] &@$ v'-@G,;$ f B?<>A?:3#2'' .őgq:CE&nL8uGK)X/ 5$nOU9a4$)4&"l̈qyaP?R0'/3$ 2$#4'%." 2'%+ $( 2)(N:6ڞۦ՚paUuMDtTMgaxs}}sqgehf2)(aOI轳າΠ~spgyϢ溵㿼ԱϭNA@*&$sc`i_\Էėnkpnxvts:22Һܾծ~{-#"-''ϳẸ|{D87ԫ_KK     H??ڶԥ||oo]]kEEtJJlAAnEEh==d<<<22ⳳ譭ԖɆԊуuullRRD;;ܾبڧਨߡퟟ [[blobby-1.0rc3/data/gf2x/font52.bmp0000644000175000017500000000336612042452401020141 0ustar danielknobedanielknobeBM6( 0 1sx^bOQHKBDABHJR&TX.YR)SQ.RF+G?@8;4236<@BĻGLRTWzL{$$  E!Ek||~|~Ȅ>*?>.>aJbz\|gmmhdbrXs^I^E5E.".blobby-1.0rc3/data/gf2x/ball03.bmp0000644000175000017500000000341612042452401020075 0ustar danielknobedanielknobeBM6(  gfhfdgusv}-,-qoqRQRyxyifhfdetrsb^^$##gee~~hggywv eebvvtqqpmmlbbagheoqn cecZ\ZVXVegexyx_`_ eggNOOJKKfggcdd()*`acrsueeg::;wwxuuvppqccd__`WWX}}}zzzwwwtttsssrrrooonnnjjjhhheeeccc^^^\\\[[[YYYXXXVVVTTT U\?8''>@@n1[19Q@@d?@@10/(q.(%.??ΓΓ?q,1'>>?@za1LJȒL9aa,L1?a.''[\+^H.:|OGa/m[OtJ,M|EOOIeɒL*sE=tEBrrBBbB E8Fe$0    L!%KVo.<1!&\''g''q*-<>NTtCHU^GRO,7 5EBKJ[\]`哙Ӊjwb?L# MLfdwwpq~XYghҫ踿ҟ]gH6= ׀~3,.ȰiUZ痢E00ƜA88᪩ݥ&c\S֭||@33ljU--#¥bPP,)%cy{YAH/ ޺& J<9D86@71?4-2+"oeIVTf6&.aWWУWEDR?<}vxkzǟvxat=Lp&& Ͱ񿾹W?<ÈȍСڵ׾Td'"F YNN堞gf#2&#G95ZLCqdX~lj^yE-Mpp]X    sjD=3((ΎzK?0#+{w{qE32X$  a]wcJC<n; E132&) E21i`~h~Uq1nvpL\T?9nQ?qL@cW%blobby-1.0rc3/data/gf2x/font53.bmp0000644000175000017500000000336612042452401020142 0ustar danielknobedanielknobeBM6( sssWWW333}}}HHHppp===rrq000 z}lo<>(((尰WWWOR" ΋  DDDޚLMMK#++qqr|322Ufߧ2$ $۪\^]    EFFԌ7:8ltq-0/͝V^ZGKKƵɿ/50 ǼEUI8=;do2D7 |ʊMnV" z}lo<>"&%θڮbo1N7OR" ΋  foۯt·B|K)K#++#*'ͤ{ٍM[-_4Ufߧ2$ $`kXi070    Օcq.8^  Qv[|:D% 4 ~ԇip'-" ^eW[2;1B6ޑrz7w<blobby-1.0rc3/data/gf2x/ball04.bmp0000644000175000017500000000341612042452401020076 0ustar danielknobedanielknobeBM6( dcfcbe [Z\tqt~a_agegpnomkl~zwx|xx\ZZljjgeeecbihghgeqpn ``]$$#uusggewwvlmj susoqojljegeghg eggsttoppcddqrsjklmnp^^avvxeeg~~hhiggh]]^}}}zzzxxxvvvsssqqqpppnnnmmmllliiihhhgggeeecccbbb```___\\\[[[YYYSSSCCC000 tRK8PntD7~"#t&0/7pi6aϗcח65t|b%ϗחdt`՗r$$(Ȋ{c՗ý{{՗Q$]y\yy&חc1$yy&ӗ@xxxyyyח֗sRxIxxadd?jwv\/H-xwxyD,+[++[-v|]J+[[-x{)+Ig'+--v|AE+-vEEX-Xblobby-1.0rc3/data/gf2x/font17.bmp0000644000175000017500000000337012042452401020135 0ustar danielknobedanielknobeBM6( x1v^^BB & !}vYV<9V8\$\43ND W RnIkf}C{K&J  >y1+" vfGa*'LFӴdT {ztskF8> Y2Lm3o"EB?|nO\B$sV ~U;qQ>4=%_uu7sK\cOxXA8e&. "ֈU}'N=!  yX@3$>pshq7HW, Oa=[@-#V<($pYG;_''" U6't ) Y5T+`5 5$="! G     dN"vc]EC( iDQ;4"VEE#N -W 6qvfD5 /Lr>awڇڑٖыXbBP   j`>-/"    zs.Ew/8 ^wN{nC8 N jyQOH 2YoOE u %`L1~-PAbT   qR`9+(V ,:"wfE;$b Ge6z_A"(#lwm3(-rlY398:-q`=ygdMGNgBHp9#1'1"O|G?w6/H!blobby-1.0rc3/data/gf2x/font18.bmp0000644000175000017500000000337012042452401020136 0ustar danielknobedanielknobeBM6( {bneIB(ѲtnC9* f[N"?Bݜ@2T1lV;nO Ɣt/ !H'7\)ĸW <WE Wa4H`bM - g; ml = x?T\rF+$k6xMN %YkYQx7e?FsAb%?5׃lw;Ka%.geމ][v36%/%%՟jI($ yyMDK0`nRҍibG$/7*Ֆߊ~eOoA젘ZK?N;zq!=blobby-1.0rc3/data/gf2x/ball05.bmp0000644000175000017500000000341612042452401020077 0ustar danielknobedanielknobeBM6( usv}z}hfhgegMLMsrsWVWdbca_`mkl|yzgeeRQQ}||wvvpoolkkife~utszyw PPNmmkgge~~}rrqddc__^sur "$"orouvututpqpjkjVWVeigwyx bddeggyzzkllhii_``XYZz{|nop ffiddfccevvxjjliijffg|||{{{xxxwwwssspppnnnhhheeedddcccbbb^^^[[[YYYXXXTTTOOOHHH???000 "tO OOLL%b'Or:%%Y( oLU怀cM?nqce 65no~&>!on6Uc46noUO;4nn6Uc@̈vn7rU'43wП7or`MarUU%c&F֟Ib' //00ϊ5ru9'iRlj;n'H0}ialnW@3ni-j)‡))Qj))i,)i))))blobby-1.0rc3/data/gf2x/font19.bmp0000644000175000017500000000337012042452401020137 0ustar danielknobedanielknobeBM6(  |abIG?80' ȦxxMI-&   ekgU:a_72 sAgWsOeRvun*jg/* jA`k'`\ I{a2-#j<c`PY ! g.Q MXOt rI)Ej-41 oR*WUPv?x(wF0DcߡB55 ʌci(7h|Z2dI~KɏӐ$2ȏIZ)12cU1VȏeIpʏ38lGGU¼ɏ3(Tzɐ{'Y$`/``z̏p3__`Gnh(r//E/%~((..Ebŏe{~,D,E/U^Dy.***D,E.,\qA@***D,AA@DEA@,,*@Bblobby-1.0rc3/data/gf2x/ball07.bmp0000644000175000017500000000341612042452401020101 0ustar danielknobedanielknobeBM6(  dbfwvxgfhfeg||z|~}~popzwy|z{ebbhffgeepoogff`__ywv ddbaa_hhfgge bdbz|zegewxwopoghg}~  z||eggIJJhiiWYZbde^_`~~--.zz|eegxxyppqnno|||yyyxxxuuurrrpppmmmjjjeeecccbbb```___]]]ZZZXXXWWWUUUQQQOOOAAA;;;###$Ok7 T"t_ N /  .HېӐYGH ~T3QoԑH3[ "VV7j,2YY#-,YkV[vmE..NU?Ìmm.֑"žmYYVȽEmӎ2~Wb-ˎQb)*, CEmmg!AWR\QW̍'B)bs%B);@A;AC@Bblobby-1.0rc3/data/gf2x/ball08.bmp0000644000175000017500000000341612042452401020102 0ustar danielknobedanielknobeBM6(  YWZfcfigigeg767QPQNMN{z{rqrjija_`zxxnllgeekjjhggbaaqnldcb~fecggeaa`xywklj  ^b^egeABA~~vwvpqphihegf bee;<<`aajklvvxnnpggieegxxyhhi[[\}}}tttmmmlllkkkgggeeeccc```^^^YYYWWWVVVTTTIII...%%%  lG>-deUe 'U+ V+'G-T*d 33TjTT`;S45l)Cjl!ΗҀR--$N˗OOjd P˗d OOOjd--b(}pPOP4c V|K}}Ci>o{{–S$ +³0}QjWmTg=}/{Ỳ{D{LK0blobby-1.0rc3/data/gf2x/ball09.bmp0000644000175000017500000000341612042452401020103 0ustar danielknobedanielknobeBM6( vux`_a|{}{z|$#$fdfecezyztstrqrqpqifhheftrrgeewvvpooa^]~}yxwvut{{z qro "{|zwxv WYWegeKLK|}|xyxuvuopoghgdedcdc {~~eggVXYstughi eehcce^^`ooqeegNNOrrs}}}xxxsssmmmjjjhhhgggeeedddcccbbb```___]]]ZZZXXXWWWUUUQQQ>>>000'j'*5Bf*id6BTj'?1iׂ1a!MXׂBBr7ցuޝḟ:9K71/قfp}TrNn!BTd5 .Ё:.f2v-Hnn.9[m"nFHH"-.ws~,FHʇ,EFqD,FDDC,F,Dm[,D~Dblobby-1.0rc3/data/gf2x/abbruchbild.bmp0000644000175000017500000040373012042452401021264 0ustar danielknobedanielknobeBM6(,! JHJ[KTf>JI9=T@d46i9;f&(u23V""JY++B&&T22nBB =**D66 )4#!:&# [>:fFBeJFnRN~^ZvZVfb~b^njjfvrzv~zL2.E.*V:5pZVzb^t^ZrnzvgVS~z~nkl_]?*&R72lNHvVOZC>S>:M:6YFByurnɻN60^B;bE>hJC.!~^V^F@z\UE40f^iPJe^mfunhSNTC?~f`_NJ~3+)vqtc_|jf<42KCAóZ>6rTL9*&nRJJ72vZRbZ_JDjbnWQrjngzaSOugczr~vL?;zgaJ2*dNFv^V<0,ybZjbvnrjzVJFg[Wnf~vo^VWPMWC9D<8B@?<97jF&^I71/-+*([ZXTE$wb.SM<;5#~rN bT540^X;sj;_\KFA!FC.ifEOM0YX+vv$$#JN fjR[e3:>*TXCYi5>o;]x.=I(-3"CI8E^ET06I L\:WiEV!HPACu7bB`1*HQ98D3/=*X<(>"#. 4"?14Y--z )g!(Y!5m./L+8,9R6%M"9@`> "EAVIzgFbZ+1'06,4;19C6>G333,33,,,+3,03,33-03-330-03,3,3-0-3+0,-,033300-303330-,00-3+0,3030-`3300,-,33-303-33+03-003,-,3333303`300,303+0,33300333330+3-3+3333,-00333,3--0333330-0-,,3+0,`,30-,333,3--3+0,30030-300---333,3-30,-3,33-3033330303`,0033,+3333,30,`3,33-`333`,30-+3330-0--,-33-30|@-33.B/|k||VkXm|./mkzQ./B.W).//kUdzWkWX|3/WWXW`QW`zIIV/.dBzk|-0WjW||`-VVzBXW3XX.|B-/|.-.dVVB.kIW.-/mkVmOXBzzQ|k|d/-.zdW|BQVd/VVWkXkVmmB`d/dmddV|`Bmm/`zQ-.Q.BVdBWVkmd`.k|`.--z-B/`./XmWBVXmW3.ddkd.B3.mmX|..|dQXV`0VVVk/zm||V-3mX.|B-/|.-.dVVB/k|.-/IXImmdB)/dVddd.BdVkkWdWBkWdWkWWXjm/-B.QXdBV|`Q).BB`/d/|VWXmIVQBX)Bz/)`WOdQddmVdXk|X|3.|dkd-.B3.mIXBBkOqVWmX`(,`,WXkB1SSceS\\xS:yQBxu<{y_b_y}}}LS._g1\inT^ni6__~_xARC2g6~8cTBhT6~inhTT_x}he6yx]a{}h_T}2S^^c{^{c~:yQ1]Sa_S6h}2:rn62S{{c8h}1x2}~R1{nnnS~^]=yceh:xC6g1R_Th>a^1A]:n~x^y~r_6h{yyS^22~{x~~6rnS2eechceeR^6ch1RcT.B_:aCC1A}8}8TSiC-_SAQgLh2A=SSScny6<}R{_B/.RSg{xh]{T]A\\B]_AQB2\^6S]CR~u2C_}Ti8~=S]{giS{g_~<_R~Liccc[6]ccTha_<~MnS2eech}g8C^TT1\6~C\1}81^xBS{c=}RS_AB/.x}h}^i_c~_^12^Ryc^Cxc_}iSnh1n{S=M_\]{{~:gg~{A^ygr~8~6_i}TW-2/aST62c~__^2egehyR],.g]1aSA8r:}yC^i8~yQ`C]Si{_uST_.S}xy2}RBwy~T^g~2CQ2_Sy\^nh]}cTun]h{,-:L}`ChSC]_in}_{/1c2RR`\c]BS}1y}^]x1c~gh{R^,`{\Ay]Qc~{S]S>L:}/1]Sh21=1_x`{6]yT2`/1yQ/6uy]_Lg1~ac>}C}_]}CQhS~T^g>h}_S{{Sx81yg81yc-.<>e_T`B]AS1QTe8STr:gyQ`Rx22]L]_1.c}8hcunR_yQ./2i^2C^Ji_^<]x_\QxCB]:=r<1,,B_xT:B,xcCCc1h2B/2SAQ,,^rA,C9A^cy{ngx26c^AQ2=<}{cT~i}\Q<~c2^Lh8{x<{rS,,hn\,28S}TSgcRRT}^1ynLi2xih1xR1ihhSQ]yhe2^8AA]Q`\69xC_nnu2-,B2r\_gc::B,16R{x:rS\BSTCA-,y_/{i^}u>cyScR1TSCQ`Rhhy{cTa:eCA8661x~SR2eghMiR1}1\]1_8a}~R\c6^CS^y>]crnh{S6L8T\TxQ]gc^huac6hg]i2]]_SChcaRC8:}^gC/\x^8chiaS]rRc]{]B^6hR{>8{u~B2:-1>6:yy=hCC{T]Agy}B}n8y1{:hiy}:y2ccgx.88{y_S2]hre~n1ST^yTA/B6BBcu2_~_\6R.xSTBT_ARCB6g2c-Ch_\]hg^6wwLeR__2_T5cTA^8S_6gS1SSR_c:nc 2Sxci2Ty{M^T{iMSA8yy_S2CW-giC_>_,Qn:.,6ngihR}hae9Lcxc2B]T21S6]A.B:{8ySS\]S]c6{hh_T1Rgx,/~:{{^\^}Syg1_^QC,.i2R_^S/AiSraRyygxR{^}g1]a9ayARSn:^}T}}C{g1Rc62}i}g//1~Lnx-x^ynaxgn,^:hcR-]~<:B^R_T\Ry8gcR`\Cy_c}c~^}2Rc62}n_:6..Cc]`^c:yrn]Qc8yAB~i].T6./_\-QQB{T]2i}R{TRRxSucu~2y~<:1cnhhgAcc]2AB2CST\Ry~6T1 ky7xyg^RcgS~Lh{ic/Q]6MM].]SVF]2:RQR,{hT~_B`A:CQ,,C~y~ngx}]2~Th_SCQA8i}S2ah}L]x}rc,`~hx{8Cchy1A.RcyS]]~ya1-_nn2cy5ghM]i]y__2BA`_8:{c_1u_/S6hy\/Ci^RQ,,C6{yyCT<:RxyT{y1R:6T_~>6S~R-ygB.a8n~g2\}h:8]y{{r>:uw_]c=nS,,26yBC{B_T1\B`y_^i=1R_x^B,4c]iun~chLiuL\^n_6cCC`^}y_R~CAyhy8<^BQB^~iL}Q]:yC2i6neCC=c^_e~A^1BT~^C{y}hhL:1\BQ_~8n_Th:x.S=iy2~r]-}y]{xQCS8Lu^S_h.c/Ah1Ryg{i]B:_;E?vvvvs5{kt^egR_^}82R^2_eg8neRRABSghuSzwL_{S2C\_i>icR{_R{6}~cc{..{}>^gT^M}h>hhinT>TSn~6xRg~MhneSS2`{hC_6A.2~yxCgwi\6r_aihL~~}<{Cg~ir}~ha:h{{2`{wCS~\/_gT1C~gB_ggB`]}]C2{\R_y6]yc:]/_BBaSx^~1`C^8~Sc2}ghrgTygu::rn8y=.,8n^_TRQB2]B]hx_eTc:}/1gc}h}1^AS=T6iS`2M8Q8x/{{a{2}TiTAccScQ`.Rniy]S{c~2c6_6gS2~{xy}>Sx_8_Q^Sec_LyC}^QC2^c~gSM8y=c_TMe6gnMng{.,h:2{cCBA_]B]hC_e}a:g_.R6Tyg{CCQ^:~STegcx.Su:BB6nx/{{~y^}y:yIv̧v}:S{hc^Tc>y^S8^.1_~T]hSR~_B1y .t]Tuc^yngc66a/r-Y~:h:_x]2:uc~^C_Bch^_^B_^xi:2_~].AQQgh}A/\2h^\_A/yT4cxxTycyB/Qx6TnSwi/xxc~2y}xB{cC}L=_y=g{~}nhg8T1T~Sc~e~}SgT/]u{12_RhgM>C]yi6Cy:a1^}2..}:i>C/x}gh_CCC8hS]{Aah^_LSCy:1R:h2xyx.AQQgCBCSi{1c_Rhg{~BB2^S^Q//]8{_6~{x{iT]{c~c_x{_/2SACh_RhgrR]{cWvvDMihiy]y\gi_{_R{xg]^c1.\B _RQ}c{6BB]12x`-BTS>Sw/Q^hg]BB_^^R-\}SC,/y/Rh{26gT~yR^c_^h7aT{ih^Tnh\R_:SC:S,B8>x-]<]1{B/cie~{_Byy//]8vv~]^aA.g=8Luy}yQTS^hT_\`cn61__._y6Q5mvLM1SSQA{c8ihS~cRRACahC1\F:urcx_eT]^=r_R6C,/RAQ.SsxcR~=}eh2T~n8{~2Cg}TeS{T211]RcaA/Q\TMnTT2`^]/cnhx/T=MwM^{_:>T\R2{BR}SaMn6~6=eih=6Rgu=SA^eice6]_RQ6Q\>uueyccTQTh8~caT..yh:{R\R\~yMyBR}1n}~TR]{iCA{~_Bxgng~ySy]\AAcaA/xaMnTy^/{S\ri^QyL[u_}M{n{Ax^/ByyerhhhL:=T\~u>1QQx~:y6c]_RQ[c/=hM:c}Sh_/Tih~cTmiCC}SR^h~6y_y]RA\RgM8RQ1x/6]Q{n>C]{9y-Bh~2~7shTgnhB.}gA^>86cg{_i{RI_._ygne/`\g~Q]}]]<{^S_^:]/3wB:}2]ih1:n2ygTin8yB,{}-\ML:hhSAa6a:hc_2hMrL2A-x6hT2RQcng2~_._rhgB/RAQ^TcSy~icSg<=g}]B_e~e]CM2i:y}hR_>SQ:6S_eAchR]ThM=yB,{g/]:~~}SAa688T_:Mu{C-`^8i}_\QaMiTTQyL~cec~AB^xA{gc~nuT8{B_9iBQBR1Sh~RR6hha}~8yR`R<{Sx:}:~Sg\/}_1ya}{cxB]]cg2_h2={Tx{gSS_=KR.xcc6~^,S_wQQS_c=hhi{~M>Lecvvyy^Mx,}{2ShLc{i6CRRtT{cgS\-AhSS]\i}:~x}\/}21T~TB3uLRg_{a_rL_]A`R~2\]c]cg86R\T8A_RM2\QQC]S]%`R\QC~nn^xT]Rx_1_BB>nh~]/QBS_S::n~SS/ATQ,SixRgMS\c_.c8]^cSB/}~C9eBAR^S2~n]A1}x_}x~S{cC1c}xRRA2}QSg]hy}MgSL]xB`_~S}^w~_gh:h]]8]}^T]BRC]S2B`/11B]hL2xT2Rx_1_8B\rn2`./]1^=rh}y/R6A`{R\~gL{C}xQ`Tr~Cxgh~T_B`_yR~hC]^Tc{hMxBxTC2V5x[g_g::11g<]}^=}2R|.tCQ1n]Cy]\]S^{hRC6x`./]1^hX,?Z\:Tx:hS]AThSB2_BBycyS181acRB6ga2Tin>_C~R`hSS^i2RA{1,B{^~<^~rTRgTiigTxRTBA21g_CAT-\{^S8RRg{C:TS2Ci~1y_BgaT1Shi1BT,6<_yyLy1_R,/S^m=_hiSS:cxQc2.~c~6)S{ASh_a>_{S2T}>],2:T\AT-AQ3H/STR]1B]Ty6~{26RSCThBRa_B]6hBBi8A}u8SB^{\_hn:]^T2xSaRyg:^>{,/1_T8g]\^BAC.RT]Sy}\2yB_iMwMB,y:]A.xciuwi16}/TiCSc>ng}C~gxy2}]1i8gS8TS:h8STgcS}yBT~1S_2}T~~6cn{8{c\xgyR_h=xCL=]h}A^:SA__SecSygxc6gxB8},\{ci{Ci>A`.B`Rhy]=_y6\]SQ2}-AygiTgn{^au:x]1xxc<}~y_h~iSI k{hi{xh^`gihx,^MX3^xSSxT8RRRBS\_T^hn>e}{2{.22Q/~>}~{Sg2RA/^nT]e^R^__{gnQQc~g^,,B1\R~TQ}iA/`2nM]B1RBCce\8n2B]hMgxx2~2{x:SR:Lca}iSBch_S_R}6_hiT}>RQ}~_Ac:Byc2]6;S^TT1{~ARRSR{gcr:aTS}RBy{RBhL8ig~i{xBB`x{1h_1{T2yhBA{T}x,,Q_^_ihR8\/`x:`B.-.1i6\:}xy^xghi:A{Tc2niS{gS]x}yB{n{Lr:_TcxQyh~i2Ay\Bh}-R~{{T6S{a2i]/.R2R^i>y_y]CQ,xi:cy{_\`-SayMxSi=:2/_^\^xccA1:g~chcRR_^2}:gahvTiy{yR/S8cg:rS]mUSy^2iS/_=_B1_C_hh^]SxCQ,xm,x{B_u}__QxTA8~A/ChLiyy{\,A6~M<~B~gyc2Ch}T~e8h}_=hLSA]ST6{C_81_=gnM:8~gTy~n~.Ah1hRTyA1]TSx}cruh{yyRcaRScinTC{r8xgcScyCc{^}RB8~^}\^rn> *E__y}c~8{^<>hrc^2_ST_\^6\xX3?H_TRxwrn_^cy8B~h:cA_8MA`R1yu^g=2r_By8iy2e~6nMr8CAxy~~S]S\:gS>hinrh]]S,`:BCT~CC]}S_S16c{uihSh>CQQxig`y]Bi{Txgg/Ag6cS]{ySS2ScCCM>^xgTSg6Q.T~g}RyMr:B,RSLigr2=e>hSr2B{6hySg6yir~6\AC_}c2x:]Ah~2c8y}{\1y/QiQ\yQciCBRCS^S2A_2x6yT1<2RR_i},h2gu>n/,CSMi$F1/2cg2CTTTu:^\CS}c2x<{C8|3JQ.{yyih2}8_BA8{Cgy{neTi<}AQA1Th~6{Q5BQTTgi}BQ2y`BCBQ2~c8n:~~cg_{CAcg_=w>1Q-1`}L6]1QyhyRCyR18~}]S=hc}_2TT}_1My/_SReg1~Laxch/QcS_8_/1e1he/`Syyh]y~^i/Qc^R~TRTL:Th:SQ.B]cTS6S62QyBQ}T>}B`C],QRQ/1a6TSThi8T~}B{g2{RyyA6eQ/`S/Tnc1]Q_<~_A2_h~_BCe}2^Rxyge}SLSQ]_<^8={e_Q.BR{hg6h}!`ES.`Ch],/\Q/1c8a}g8T681{_-N]2.-xhiS\88y]y}`^:22C2M:Thn:]Q]8h^B]AQTa:r6/B}6S_^S]yiy~L:Axi_\xSyx~2SMc~e~Q/~ha}2AQ-`h=RCy^h^2e~RR22{nn~ig}L~1a~A]7xynaB~gg}y_^yh1_{C2STS}riR]:~h~C/Bx1BQ~a>_`1nS]]6^i~{{}R,_R__SMB^:ny8gCS:cc^Sg8hnB.y:a{{^AB./n1]iTR}cnRC}cRR1S}ygrn]^:i}8h^R_SB.5)RnT]]a^hh6y_}g]-C}]}8xyhXL6Sex1hgg2}2A]]auy/chTi_{}Sa{2ci{SeMC,}ugT6]C1T~_{:T_{cTC8:]x8cg>T~]y.}_h6yu^Rherh8S.72{2/^^n:BBSaRBB}8S:hT_6TSyS=~SaT>hS{2~hrRRgc6}xSe1116cBchnhy}6Rx]C{{_ay{rC,ThT^ThT]__g:yS8yxhn1SSR~CR~}~<:{612AgrM=g\S_/\^2r2S_n}TiM{~]Rcgy]_u[nxQ^11TT1]Tn8i_AMT}hch}R1{C~M=SgMShCS2Q}c]2hSR8uu2S_u2B`8c^>]\RxAx~r:A]hx}=~^2gSC1B{h}=1Bcn}B]^/\^2___:nT2g6=h2~i^CaeT^S[CQ]1xc:_S~g^Bu_T8Ti{RSRcni2}_:C__B6~M2_h_Agu^y={=C/Tn{]L_xC]4BCScrT}SyTiLx_}%tIbS2Rxc<{x]y:{RSyeni6x^^-0?K2S1ShSxhiSy:6M[A]CxyC_8c{_]Th82Tr]A_,B6>ghru].^AQC]B.x}y]x]R2h_Cg^_8:yn6Q1CCyR_e}{2]yTC8hiMhiu>~R.C8nh\.-,Ax6Tyc>6SQ,_n~]yLe]^B^2RCx==LL2//C8{xSg~B^Q,R8{^]R~L~:%cch<=1~1-Q2CRg<1R2xQ1a]/{h=c6hTng2\\/^r6y11c^C~a2TgT88xyh6Mhh>hS2Q`2g~~x`]R]c{2AS}C-`R6n^`{>rTR2ux,ARxrrS%B]:>T]_6aA_,Re{]]Cgnng{~.,TTin:]~c22~T{iiTghTi^ST_gi:h2/^C2c\Q_iCC>={S~y{a}{TT{g~}{Sc:h{S{c:x^SSSSheT~hxBBC=8/BS8in8R]:i8cA2nMi}C.]h2^c~Ti{-A1cTR1aMc,\y_xTi~cv]]M8acy8~S21xSy*c822y{]1aT_ci2RC]~.B_gic-3wu{Tc]_:2{w~6gSS_^\>h}h>e^Ay6xRB1hhy:iy_h6g{_x28S__2S^_igS]Q.AB.^y\C6S1}B{gge^_{R]gx_Ty}_y{_Cnc<:cx}8]R2hTLi_xcc~nS]~=g1S~}>:e{Bgcx_ghhLLn{CT6\B.\}y1p}_:]S8ySh~c>{_\Q}T6gSxc_\n8^4hcTr}C~TncSTL1SxCa}~yTS1gxghig1{~TTecyBT>i].c6,.CRTa^aB1{CxABQRgTy_yS}~Sx]S=xQ2x\c2BQRhSQhuhh~1Tc>c_8R/_ghn:}yxBL~g~_\.S^AgaCxcT2i<{2i6{>nT_8xSa_]n}yg}cy]]:6:Q/]Tn{SA,/8/Q>`\QSgCR]TSc>h^Rx_6icu6iC,{Mg~{A2cxB_ciiMw8gigSRn_/_6{S^Rx~gxSn}_y1. S2}nS]2yihMrc^R-/SUo'oV_xAR_c1y{y:]~TB^Mn~6Mn{ceB,^V,x,S<}2M}.xC}c]^R1gihA``C]yi=}xQB]^Lg:8ge}y1CRCSxci22heC]TC-2~]ThcTi}/^RT~Sai]A2R1C1y6i1Q.`y=n=A,]cS/Cn~.R2_hSSx^h<:R-``^_cneSC]T}w=ccc{_RB/RQ^62_gC]TC-]}C6>gA1_xcygn2RSx2]_aahn:y2xQciT}B}r6Sx2hy>eQB//6~^LuTWkR}=].Q/1]SegSR/B1. tb2R\RBRQ2~1x8^{8_.{2ac}=c_aehxg<^S_y]Q^aB\TRcg~ech~e6]xS=h:>ec}R_ACa__~6^Q,{yhB~iy_}Su6RRy^_{h6~hCB]6c_12CSe{cC,/yhnrgx{:{/~:8cn]1e_CcQQQC}_]rcS~^hue:>S1yTTQS1S2y^Q^aB\yA{h2^y=iMSRyc_2c=CR~TT\RS_/.BQxSB]:]C/SRhTiih:h_x~c^,`{}8CRLhA_L2{R8hS__T2A]c~B,1~~^x]RRShg2RAS~1/R]82Q/B]1QBCRS6R{nSx`^h8}:y_n=}.Q8:^^{i8/ByCBx]~a~L{{Cxg~}e}~_ATig}gTy2]A^cyT]/]c_in}2S{hBA2nx_ngBcM8S_chc2}iR, ]-]CiLg==i81R~c~}V${<}/ARBShhnM\RR~^\^g=hS_a_x~w8encgycBu2xggg>h}6g>}Qc_{c]_\S}C\Bx{yc}:c^Anue=TQ\yhi^^:Mh1]cS.-yS.QBA_<}B}8Q]R2:{^{CThrc}T~i6S>8~=Sg}`Cm-HA\hu}8rSRa8_,2>c1Sy]QTgCc>yAST]Q_S\AyyA]L_Ry~icy2QA~a^1x=S~{S8M8>C/aST_Qhuhg~g8Cx1C~R^:c\C>i=RA{RAeMhyhS\cg2,2~^{}2B~hx6T\y6_A7a]Ryy\1in}A.C_c}TTR1:rhy_2n^}__gM2Bg:a}ih__:82y>}xc_6MT_nr~~y/gT1S8].guhni8~~g^{v\~c^1C8C{]]cnuS\Xu/Q6r{h_x_1r>_S^ST]8]i>BCrrinhxA/A6SAS_B^S6CS_1ih26y-,-Sgcc/c1T6cc_T6yiyxc_B.\8{A8MgyS/QxSg8a1u={{cgSSg6a{SMa._2\1y{2_chSB/._g1{u8SBBT>h~y2cB%yLhTi_C2xM_S^yiT]_A^<2xBxhcC__B1__Tg_.\RCyhe/.c8B6BCMrL:]RQC* {.RR\:]cT-,-S~}T/Th_SST{6h~c`zA`AThia]^SBMh~xh>]}~nw^-}M_}C`_6cneB1]R]h~6_}6y_`/y_]yh8yx]y^x{ghe8~,.~gaT8}y:<}C-B^}6_na}x,,2^RTTAQ^6~Ti^^_Qy/A_chCThMLmnc^h~1T{g-Q_n_ST4 TgnhR_]2c{ga\Bgc^2TQ]SRCA{cQ3wi:]_TL{{c]S~CBSABA_i6B/,_>^y8_R-/R]gi6~:yyyny=nhR,B1^SCQycSTTAe>_yx/CSAB2\A^^/B6B.T]y>rygT]Q]a_h^S6^{8{hQA{R`/xy=ng^aR`T}ryciShhgh;eRCh2Q8{ySR^68r]\SAB_QSABS8A/,_n2yhC,B]]_R/{:cS}}Chx]A.1.3wuh<1.8QTSx6}CC1y>2.Q.Q{cT.BiTy:~SSS{ycgyA^8SB\gQyiSQAS\TAC}2A1~h11~TgcC2xT]R]B1_1R16]^ia{:S8:T\C]B2aQ-g:\Q2}^e8c8_nT_2}hh~h__axCn}2hhT::cahR-Tc.2_xc{A\]}n{.Q.%Sg/A}T>c~6~8h<^}i_.Bi:\~nc\]c1c=RCy]Ax6xxc2y_1S]a_1y]}gy__~1xgS]62:h{A\1RSc.,a~BQSn6R_hh:]=rS1_~f }A-`heBT=i_QA_\ShCT2\]8={2c2y_R_Q3Mai~\~iBR~6yyTcyxC6TS_BCggMy]=y{6SSe:aT<\_hR.=-{6hhC^cA_6_R\aa\_inc\\]TCR_^Mg~1y2^_g~CC_^:<1]BCyxBT1x_T{S~]R:>T~h__hc1RT>c1Cxxyc{cSS_28}B{~/Bc6y{{{SRCghyS2Bxg8uc^>Ty~T~nn:8_aiAiQCc=]Sa2c2R\cc\_:cRC2c^^6ih_}gxRxc}AC_2:CCB`1TRQ:ny2]SchTc>{1L=cei{yTC\}6y_B]h8M{]>yy~y5T.,g.\y68h8\16\{gS1]2SiyA\Q3wM~h>{A2:e=T_2~xSx,,CC_~xAycT{cSS:6}_S~/-6/w6SC]6iu6_Sc{^]_SB^{Bg8SS~h_B.1y^]i^{C,,Cx_~xATa}y~yf-}:_,{gy1xci}^_g}_2TcxTrg\cia2Sk3[:L8n2xT8_/]~y_^R{2A_~}1e6/A}1B>nB2TRA_^gB,-2cRCggx{]hn8e:~{iag<{.QThy6c6_^~c{c1`]hy{i~>1g~=6T{xAy>}TT~<6g<2xcT::__2A_g^`RSThC{6Mnhy\yecc_a^6Q/>L1-.RiccyC~c/c}2u_a:{x{T1/B62^:2c_8a6hgSMhhThn:_.\:unih=aTi:S.]c~:in6Sh^2~={_y}g2yni^S62 :9^S\-xhx//`^Q.2TraLCcuy}_BQ.L^6~CSTTB,.TMn6e~_cy_1{cSMgB-2ii_BR2hS_{6cS{{8S}yQ}}]8hcRa~~8~\6r}1{B]}T2CgRCA_g2Rc}-R~]\cg~SAR2TnSRBRSS2RciThy}{=iABgh^_SB/2Ty,/6hTachT1S_^C2y2uh\`_=Sx_cMne~ei>hT_S:ry6ng\cyxg8cLy_L_=iT~xSMggS]Cx\_8_RT:c]8n6Tyn/_T2hi~22S6u~^C^cc{Lg~gia2}T_1{cSM\.S=So}~T211}=^y~hS/}M~2i8n_1nhcggV,L^8{cg~:{A2ch{C~M=_T~k-w^T2]:nr8R`,B^B,Aiur~x}6}c{R-By2~iSa>iQAuwn_R{h{S~T}cT~6\Q\]^C\y:c{6h<>>gx_6{R_C_cL]-2h_{1AhLcS8Q/]{C{>1^{Qygxc>}7~Tg~]{i}==]/1g]T^^:r1.\2Q,{B_S^^xQ,By_yg]/C}{c<]y~CSh.RwSC}8ea~6}gc]]{6~{^~c<6ii62}haR1{1SgM^-Sc8i2Ag~y2c~B{c^}LyaM1>TihM<*x/,B:uMyAyac6Tx/xgST8]vt}R^~`/M=]B_:cT:gh~h~R^T}_1cgTUc/,C_y{]{{ACg}\/Q]h:Syc=TgCQB^}aTncTR{}122-Shi]A1QRSx1y~nBQ^2xAc_]RS:h8iMch}BR~{\Q~wSyr;S>c1y~~c8nyBxcycM/,_82-Si]1yC{y_{aS\]{R-.ce6hc^_g:cQx_y}ScaR]8}RQ/18~x2Sh}]S/Q]hT}neyi}S2/1g^BC]{i_CyC]T]^c8wLx\yy_C~{2]}h~cg8>8A1hTC[uTc>gM2yc}{aiSA]gceL\.~}QgMa}hy2ge_vRa{B./x8~]Sc>}]RC_:Tcnoc1B\gR,behgxB2BCc^^}8BBycSxgy2z3{R]RRR`1cMg_/B{h}{RCS^\crLS8nnS6>>S{~^AA2iic}B\TCCC^2xhh]\giyA,/^}28^68RTT2R_8cxx8aCSy~h~g:ig^2ygyB^8~A/RRx\..QB^_{T]xAQ,,`BCSTc6yR]RCR._MeuS.Q{~~e^iS\R_]ATn6]}=}__Sg]/^hM>y6\..xyy{^\TcRBR]C2TxCRx1R]{h6}>:h]/2S^hh~_{ST8}cMh1iL__8aQ`^~~T}ru]]}aM^6n8Sge8__c1RS]S>eS{^_ghTCS}~:^/SyR]_/BT}T{6}B,\c{CQ`TMihSxx]g2BSMr:h~_cCQThTCSy2x~>~{]S_{8}gScc6B`^Wy}M1T~2ThT}i]2^\_]2ic2nS^SghTB3\Tc}=8_^B`R~i~_hS\:6_RScr~~\CTggn_S8_28{}}Ax^\y22ag1{aQ^i~Sah{T6T_6i_y{x26~T}M\R:{1`.{i:6S^86]Scgc~Q_Tyc_h66_2CvS\/]ia]2B~S1nix{~uhTBA`RSMxChc2{{/C^\aahgR_n6BS>ih}>xT:8in}S1]\-x6iRc8\y2ySx-7rL^,/TcC.2a_x^LT^c2a6{LcSxSS,,-xnigCC1x\_n1^T8{`Q{\CnnyB^6TTT~gTi~aR{cA2h8i}T__]._hnx}eAy8C^c1}MLn^,.{ax`1hSRBAu{1hT]cn6{LgT^ySS1chy~{_S8{h_6cTRQ^y_~1ynna-,/xng:^1]1Rx:Cx{:~/\8__SBvv4_1`2=\T~S~RS:{].gw],AQAi^\~SxSiS]~eygr_B8Sx}y6ua-_}g}S}h:}T9R}y{ryxh2QniB^=cLx-{g}SS~2{2B~:h_]S8~Shy./]rL_xA]S}~xn}Q~{1_^g:u^C{h},`e~ByL6y:h>1-.Q]g{<=66{R\,,yi{S=~C.TyB{6Axc~:8{>^]cc~8x_cTSc:gg]8~cu}xh^/~iihB]cLC-{hgTchSS].T^C2~T^~nc/Q2]R2}8hC2M~B~y1_]}anLS]y:c,`~{`_nT_SQBB^yigyCR,,^g6_BTn2Be8A2}Axg~:]e6}ryR8]/aehA_nnc<|o2C.{M66xCy_CT:hARTu1RA_}ehR^M~Vu{S1RCxTgh{cCQnR_cR2TLun_}~_c_6w^~=<]1SR1ce=]2~2BR1Srr_B.\n{xR^}cA^iT/BS_CQxrc.xue1.\_y{`1h8LTRQ_iT_hjvmhJLc8^A^y]_yg{}2cwN]_RCS}cACS\`QBCcxx>SC1S~8x{>cB`3K_^{S]CBRRQ^:_Q{i~2~:Tcr:TC_8eh6yn_a}\Ragyyy{1B~6ci{gcSi~_`SLTy}]CB-]nix-1Sy~x\c}/`1y_T~e<_Rg=Me}My8~_6BB22TSSag]TLhghnh{2S_Sh]T1QS6{R-Be_}{ynh2_cc_`,\y1S2ahyS=ec}12}ehh^B}:}SS=c]]T{/BgneQTa{i~S_:SCA]8<~SgSScc6R_gag{yyx]c~gn^_A.Sgc]`B6iB3cgLh6,xg}]\B]ihTg6CB^_{S1CScT_gy.\BQ.QC]cB]Sei{_8h12axT12uixc~cnigT=~}unSecT./B2y6gh8}yxRTiiy_:y6_gy~^x6ny]^cS16}2_cnSC^~:gcy16guh6\`28S]CS>ia6RQ]C2SCR_cgyS8Ma\SxB.B\C:yQ1Sg21c6e2yh^6]1Mh\y~6n:6{iy_i]gyhS./_cgh_h{]Q:LuLuw=n>hTg:2BAg}^y{gTAgyiQ1}}\{]8RhL_C}88y.~:~cce=acgg>:]>}8iLw>]{c6n=y_T]26g,,}g2g}\,]hx-x:x,A{_RC2yCgh}u_]~eh6y{hMnyC:~C6~y^B\iMLunM8z}T_.,Acc]yh~a:~2/]86c_x}^__28cS8Mm3cRQ1Ax:6_/Bn_cxCS^_Cc:T22}hn=]\g:^]1.A8>uc-`..`.__R>}R{_C~TTcgLiS_grnSR6xCaaT./yigy{11{yyC.CxS~Sc~6]Q~n^}gxQ%{22chxRSC]i]/]gQQ}].yh{g]}LyA2h>h1A_x^=a]/BnMy6Lx1T{y^ay]]T>:\/g:^]x.Aeg}``..`.S_xhnTR__2g8~6^x}n^AT\BSS~2-.{hc_]A]TyyC/2_~8hh2/yA2T,CcR1yCCSACC.Rc.`4aR-_g_a1TnSB]g6C_x^62.Bn_{S}_yriR1R.^1]CRnT_2A}SCc8g}\gM~rnc2xTT11ABchc1y_`^icM2xSA-/CyT]rgCA{],Ani_ci>c\,,B6gSAQSheTBS~66RRnLS{6A,iurir>_Q/1ga1]8gC,-_{/_2BQ=h^S1_nRx\.2]^RR:{1hR/Sc]Rey_I`N\{gn62^{_c:Tc~hugx2TyT{]RA{yx_]`ChgTgy8>y_xBQ_hcR.,1icLS{>gS8/{>u=hugx6iBQSy8c=ML{ghSg2}{`Rc2RcT1c<8hu}_`/BBC2_cr8xBS{_Lg~6ngh1~Mih_2~rh{_{_c:T}ch6R]yT}{1ABSyx_].2gST]T=c{^B{gx.,]iT82_:}8/_:c=Mc\T`-L_{~yhLL}hS={~RSy^8g_e~:]CTh}S2QR^]T_R\x11x8{Q^^]SS]]{6{Qa~/\2,6r\TCQyTe=ihSCA2c~yb8hyygn}_BB^^SaRQ1Sn_]8c]>~S\Shcc=iQ`_cSC^a}6rL8SR_M]Sc~6{`CT_nTx1}__~R] W~6TS1_]{2ARC]1gRS21Sy2A1_TnM{Qag-0O\RxgrnxA~g{_y6c]9c1:a2xgT1A2QTnc__\^6n>a]Ay^~~1^6{^iu;C-_CxyT/.Sc6xB^2SRA6gS^Scr{1~c1:c^\gT_hC\h8}^inS^yh8Snc/BCx:__TC]xcn]CTAAT{BShcrTccAQ.STSh_-\8~_R:<]Thi~A/SnM__]\L:~y}^^iMiB/^A.}in,eT^~y2}].ARQA]y~r\1~yT1]:h^1cxBh{iS^i_ThaS2_}n2\81]2_Q`]5S{y:hn1A{BQQS_/_r:~TccQ-12x6^-CgSR2}:nnRQyn__]\ha{T]^iB.^B-{A,igT^gT^yx`\RQACS8~rnC_h6g__=S_eRBTRTuS]2}~{2x^]Ae1S}cR.Rgh1x1_he1RTAATyQSr:gu}66\B.]2x6^`Lr>_A{^^x~}S{xxhQ`]B-yMr.0w{_2hy_S{2Q_h-1RxLrySTgTgMi^_~{]gaBC}6_y2,,R_h6~A^Lrn^B2:nx}i_y8_aA,B=<}yy^T~ah{_1/x{2^_]_=_.1cM}\T~{]ng]xT}xC_6cAccRyT{^BATLcS_:y^2~_x/^e-^C1M:MuTSST2Ti2_hgT^86BxcgTS,,R_h6gA2r>:]Q^:nx}]_6}h\,Bi}yy2T~~1ST6{c{Ch>aS__A^}{Sy_yn^`C2x`1{S1>~x1ca]RxSTBTc^gg~yxS:_]2{:i2S{2Q_h\`]C1i}{{T2TiU _:~nQ,A^8cgaB1c8C.18gRTh]_6c/0B\R~hMg}hM{hc1TB,]TC=Mr~cicSirg1]_CQCc~]^hSyanS_g}cihQyxAcL\A_6TxxMLS{2hS_~^B8{R]_11_gwh1RR^i~_CQ}a\CCg:Ma{eh~2g}]cA,^TCnn{Se6{]^SxBC6hSyr}{{CSyS~2`QSi6_cw<2]_A^~BQ2gc^C^n2_]ahSCCc]QgSA]S]Rx{rC\1yc2B/S{Q\R6h=~}hMSc^},_cxh=ny_8a4 1}2ThS{cx]{1__^~:niS]S:hghc{J\]^/2_B{c22]A/]^yTR{i=~TerhM~~C]~S_xA^cih8h:nc}hCBc8~8BRT^Cx8yxCh_C\AS<~cinM6~6\R2122]c=T2ya:uiLeac\xc2]\Bxyhg6ehnc}ixa8~nXS1x{~~h~BRcaL:_iS6:ycrB_~~n].2=_CC^1CQ.8i}c_2eSR~i:cS]g>y1}e1^}~6^_1`R>CT}STL{C_AS^C\[:}SS8Sa~x_TT^_ac6ichaci}a>{x^y]]}8y}{2c6^_8L8cy^1{gghc/ByT~{T8ycB]{{}hca3s:~]1cy]TMrT]_]cgC66]cgchwLu=u^-^~:T^1_SB,/~a{cynMyx]guhC_n}AThhyyiiT1CR,,\{mJx_1BcRSeTg1S^By_\^\1R-yyA.-BcTQQ,-Tg21:2^TBR^`/2_~T}iR]8].Q}n:6cSc:M6\^^xTySx2c_}2B_:hS]T~86hxyLw>ny]}1BRhc.\a=LniS6g:RheS`6Sc}cSC_1,/~}RS}8n]yL{Ra_A]\2^/\yyCQ.\~cQQ,-ThTSyygRC^`/^]c{{>hA18ex`.{=8Ty^ch:ryA11RT}{:^y8}g{R^gi~x\2{cTgy/x~8^26^Ax6/xM:Mg^Tc8QRg/BwTcTncT^{2`BcRSTgxS_A}21x\,BS^B.-. oc]Ch>}TcQAx`.xxy__:\18g]./T>h6ec:nnq`?pB\]2]S~_y6y=gnTCChiT~{~i__Ma_^8r~c{RcLS\Sh2C8i^]~aS}xBx82cM1..2ge6T}{~QC_T6}yg~\R6g{CB.C:=T2_QBx:rc\82/aiB,Bx}{haRx2SBg}TaC,ALi^_22R^B\]2]_a_TgT:hMa]^iiT~T}S:}{hgc{R}M6]c=}2g/\]y~a}hx]ha]Q,Ba8ghyC1`/Ag<{A8_QeR/xS8{h}AxS{ghe{_ahh],/g<8yS~=cc~ARR2{yiSSTSS}SAygi}2BA1R_{{y6>QA6}1:8x_}]TiyySxxQ~=]Tin2x:__Ty}aTgc1/R}agcgyT{{ng~g_]~;y{k*!inc28hg~nh9A`BScT8M>:i],/8=ihT{VwninaBTMn_R]S]~}xTS^:{C2~_Sn_TTRyTC8gCyy1y2./RSReA.`QABMgS{2c~}Cx8:TS_2{SSh~R{}QC{{h_Cgy]hn~S^.~x6}\xhCSn=in6B6r_R^{Se8i~\BT<2/S8STTR\B{ax{__MgS6M}ShxSSQSSR88x\{{x{2.Q/+[:eTg}C}6xz12CT\`^g2B^Ly{c_h{S_y_1_y:S8ngyTS^{uyy:e/^T]iA]8{Sg{:aTRTg~_T^\]c{Qxe:e~ehu~]-Shy{ccy{~hg{xryx^e~c}C/x]\T>B-}TByy]yy]x6i2Rc8rnCx6eCQ]h_x}R.^SCyr~~gTca}gy2Ta=iS~>~S{S]2r]CTnSCSChiAA]S_M6_8~B-8R~gy8h_1~2C66{~7x~xQ2S]61/S^Q]nSS}{iS_kgn8yTS_TTyi.C^A~hBQC}]]M~_ich}1cU,M}RB\C]_{}2Bxh{/x^S8cTTn~yT_::TTcy~iiMT}][Scuec}R^6ihTS8:}_n=M}=5S{__^_c8c_CS8y]Chgh6BAh8]g~hi5hBMTR}c{_2/`c{cSxggScT6hT]RQ{gRRC1:;2]_S2{}~SAxy/12{6gM:~eTgghM~nhy}RiwaR^=S{y6]ai:T_~y^9~6RMTc1n[i{aue}SQTCT~21T~y2g>ihcX?p~}SxB\2~gT<2Sh6~Sa_{{QA}ycg_{SB\2xCB\22]Sn{^xgC^T>ww>i2M^C^{SCR2}ie1\T8cx2^]8h_\S],,\~_]B8y/_ggLeT~/_66i_6g69}iTBQ~a}8}_]}yS6S}{]C/xTRBQSih8y\CS=hch_{hchT_h}~~R^h6h}a}R]T_2CC2^1_=S]Tu6QA]8<1iM]R]_2\]}8h]R}he}x2^]:Tx}_,,A~=_2BR:aQS8M8y6.1TT1{hhingxBB}y{6{^2g~}:a;c2\/C}RB/C}ySxB\Sh>hgLSTi8~Sc2SSQ6cg_SW t2^Sn{^Aah\1ywghgRhaRC]1\\^T=8hQ3PTny]]AR]x]2~{_^1_x]iTB_2ScRc6CB\xB,Q^\/^_/^rScSShnS~^T=aRQ]~:c_:Mc~_S:6{gg]-`2_S2A{x,/_xxnTcach~TTcyyS^ShT2RCR\_^_S_c}{{_2S^Cug22RC_]2h]}_^C\T^STBS{}]82R]_\-B^\/^n_-RnR]RC{c2~i~2{yB.xc6_h>LT~_Shc:8^`]:>_{S\}],Q{]^LyyyS6yeghug}2]_8S1R]1CyST}T8cS{_2y4QSny]]C1S2SwiSe}{^x_x^iTBS{}6\k.o2RQ2_Ci2y22~gi8ni1}hTC288_B/xc|Z1cg^{1BBBA2Tr:]i6BRc{xy2y}iy,Qh{Cy6]RggB/h=2_n\x:ScM~/BC8Q2e{2TcRTi^,^y}Sr}}~e6yAg=}a}Rx6}TSTeSxS=e-^y~^/^6hTg.TTx1cM_8\Ax]1yehieCxT22cSBQ8h]/Tc]cn_{Snh}B^icR^gi6AQcLg~{xRCQ7T.1A *AT=_,_hyS1gy{{{_1/Tu_S_QB_]]]Sc^A2hgW3w}xBSa}hg_\SScgQa=_6~BRR8A]_C_}>cQR_1,.{\{{_hc~:MT^c:/Riih{A2^ycB/h~yi]QQ}cBxxhix_Tx_c>aBC{1,.{\__]6T^_}h^A<2cy`A88hg:}MgCT_hAAT_a<}.B:h{B2:\2h_xCS{{^A6AQ86x_:c,/SgC}2tgg]Tnx~M8a_yRB_6gnT]}cRwh~=SA\~8R^2]A=n_~}`8cchn:gad[L_{Byc>h/B66h_^c{~T2^]S~}^8}BaTS\,Cen=]]c2\`^8B`]cgC6S~6-]yB\1xAxT]^~{~g]~Sa1-/Sh}cTr82Sec\_gx]a^1^CC_{66cTCTCcn2]=}ciT1_c~y1{c_wh2{QShc/Qy{~xRgS_6}S__y~28}Ba>{S,\a=2_gStq\/2e:x6S6a-]h2Q]1A1Ty}g{Tg_.Bcge~i\_gx]6_^_xR1_y6ayTTxcx6L_2<{yi}x]{S]`0ach_]2x\_{^cR^h_S].^8_2}c_xSTy81^hgx_LThATn{C2Lc:hx{22gh}/_c~ncTncnRcyx^hygA2]]cgS/Sacha~g~^y=c-Q{]{AB5gmmg{8~:2gg\-R^2}e}hy{_Q\2ARSe2_>}Sh6cTX.1/y;i>LiecB2^1x:<}Tn~LhBS<^B\g:im]AS>rn<6}AC/CxS^QCS^iMgyCcT}T:hS_T2S6ig~h}ggC2:{x}Tx,QihC]TS^yc}]=uLg/Qx1R^_/2>g_hMr2T8h~RT:agg/x}C_8in{e886S1C1Q.ync{8A1u:~c5xS=i8S:6xL^B1x{^BCS2r8c]86c}:__:a^]y8TThy6~R^hT]~9x`Rn_{g}Sa6nwa1ia`/RCAx]BS>g_h]Sc6}xcMgR{h^_ghcn>SgggcSx1V h}Sh:1Sh~c^B1h:g{i{B6iR/\R2xRygTrc]86XtFTxxS9SS8T~g2yy\{SB,Qih]26TS6hy:S8:-uSARRc]cy{n]2RCA.\^/\TTy,,xR_gcchcTaccg2]:}BRx{<2~{n>],1{.^]^A..~u2:cn_BT{1~}}:::TA2>MRC~S6-/S6~ixeT}~6a8hSR8rnh]]hg\MT/a,^i=hg]\\Ti}\S>6cM_{xCA.R2/RT}y,,R1}{T}yccacx\~_.xxS2Be\y=2/S6BxTS_C//enxn8yh>:_BQ_^C6g~h_]Mx]\RB\_aC]}}T,-R*5TyRc^-ACSTxhA2g\,CS.\S_Sx.-T:/R~B3HcT1^6]a=ig1x6y\{h_icC2{g~Sg_]ci}R,Qca2,Sicc=c]:L}~e6SS~igSSg]-QR4.RTShugnic1S}Q,AT2]:g­qy,Cn]]gT~}yhe5B\}yRThaAcMgS^22a<S{T<=ieSx1S//1Ryy~igc//_{~nxByLT,\ihh}QB/^^B^CAATM:^B,Q}rSS:~S`R~{A^~Mh}R1.2{{2-RniTLc]\a_By8hg{1AC2SShLhyhhhaA^Tc{T{RB.1iyx-T~r:a:S,\=}xQAT1y .]]TcgMi}}LiQ/ygxBC}MR,|1Q\^]{T]a}_Mu{_`x{hSShw\-/A_],/{QRnLn:Th:rLi__~Mgxhi21QCSM[{c~ieBx~S^_R1SccSaRAT}nRBxgTy21:QQ=BS}Ry}2]AQyRghC8],28rcSxCCQ]h8~^\S{_gxAy2A~8A`}ic_h2-{hnLux./^x,/^`Q=n=e_~8gne]/1cThM]hi1~iA-/\i_y}hc/C~{Sy^_~ii8_CySLR1;}T21=..:u`x_Q^:_C]CAaw^2<{-_y^gR\C/t褬vk^CcT]<]-_g~_{i_`1Sh{{hwAQx_~T/B{.B<phC-Cg~=uS2hC/BAi_yy~5/R}21^Rx_~V3uOQ}r:i/giQ]_.QxTAC~c/BT1.gn}~STTSy>car2:r}A1{RSnhyQRhgh/6:xcMeRCSyB\}y/BSR.hc~SSS^62iyy8_:}>82/dﲬOjC_c]cr6/Q6Tg/ehS._T2ch_{6M{B//_cR16Q2>n^/x8g^\xTga68Trh}ƽWvi{y_2g6_h{-/1_gaQ^2Qxn1QB]aMhBy}R~t1TM21uu8S]:8xRyhML=yAx1xSg6^ynyRim3_x\x8hT}S``_:}ccxyi6gnie_~c\R6ec6nTy:QQc82\]S~{\S^\2a=i]BRBTn8g{}h1nRTn{h:Q_exAS6Q6n6g~^Q_61x{6>yB/6g_c{xB\c7xRThg]1ha2CBAy1Qa=S1^c~_]RBQ_~{]}S``_6g1y:Syihhc^}TA1=:T}~=__8/`}:{x2yc^Q]R.26~]BBR/^hy{a1y}ruyg8B_8xASAA}ccecc6}\-xS]TncR`icc]B1~~1R}h8]CвIT^C1cc^Ryj,w[uLyyn:xBSx{=^gh_c~nc1ciaxx}yA]MyT<_2Trry12{hgR12\.C=:^x2C:iLhc<_~gA18h_gy}8SxRC:iS{T8S~Sci8]RyT_{CTCQS__n81`,xyBQ}ig/,\8_RR\S1Qx:yxx{_Bgun_Rgy:xB{1{iC6]T{}yCye^^~_BC~S{h]1Tuc^ST8TQAR/,Q8hSA~6cMh1``_aR8nB,OĬ^B^cc1QS2Q~r]]ig2R6S6]~g^gic6^68 x]6R\TycSyxChiy]S~}^1xT{8cTS~2\^yA,C6R`BxRyR\g]1C2LihABxSnwhC2STSTy]ciCB{STBT_1SM=\Rh8CQx}/yyu~2RhLga8{2_gyxccnSxgicce6~urS^xQQx{cS_T}]CS]^r]2\Ag:_x{c_^1cy:wMc~2xS6C,^h].ѯʴb{=~{~8g/C1>eCy7ecggSh^\cTTBTy_cx1:g k2yRBhM6}6ieShLni_A}:Ty=6SSRB`++?O,CihS__icRxCy=}\Cxgx/:C1r\/_S{g~<{=aLaA,Bcc6{}:nM86hx/B2ya<2B2}^RATcB`Tn~S^.B\Bch^STC2{-,^[}^Mc=r<_{S2xQR:{x}=arrc{8acxC_R]CA{,,nM{yLw>~ihc2.,Ah^\ATSRxR_8_QAR~x/hhARnBBT}}g:_i:yhTB,\~g8}ch=cei]Q{a8nyASy1A{TA\iT^.BRA_{~2T~.,^_R>6{h_y{_]QChS]Mhrrrr<}T:e_2T]V{fr_h:T]`,\h6xCCgc]_]}TRhh~S<8STS_`{>_\cMC]}y8Li2^S~cCRS~SR\2S1BTcxex\ncS:{}c22gh6]_gcc8_]hR\gSQR1BR_xSTST1-AyCA18i:h^~h_8c8TgLeB./iSAiih~y2}haRTTCc}ch>SR>iicnw:cc^{\\^g6x^_Q/xSBxRT=y.`]^QS_2h]`2]\hya8hixScc}]B2hTyT2y_C_}AS\A6A{y1CQa}]haBC/Q^^Sxyc.-B6SRc}CB._{2~ic2h:~:>MS/8}66rn~}SciaAB8g:=T2gT}>ggScx2c:^yTQ2cA^xThi~/.__B}{{_/21A8{7~g:]ycc}]B2ncccS}}2CS}A2QQ{Q__R~1B6{AcS.1BB]12\SyQ`Agy18^xAz⺭ܭty>{Q68e=n~}ThnC\:hi{^ga6~na)12_Q/x91S_8=uh\Bcc|ç.]RB~_yc<_ahh~Ly:r~~SS\y_AT~^]n:eLrS^icTBC_c_Ba>T1S=}R6e^~xQ_ghayC/`1AB~hiy`,ղoݬ|1T=x8u}{Ty_T\Rg.^{chT/,/C\_6ScS.C~x/Ry2/R]]:gIýѬgg8g66{Tx~}xgk-wya~:SS`,A8h6}.\iyQ,Q]C]y_yBh_ia-x<2x]8nec\`]SA]ic1_cggh8hgin8}c_]xc^`RTLe]_1~ihcr2yT}x^iuR]~x^g}]_n_28h~]ScgR`]w[hS12/R}/QS__~T5xRhRCua8g:SS-,Ci~Qx~A,A{^y6ycAx}`\2nLT^SiLrgT-2}CSg^yg:hh8hg>~T}_]xc^`Cy8S}_igui_SSyACenS1~L<~c2y~x._Tʭײy^^]]riQA~:rc]h8S~S~6S8_1xcuT1xTi>=y2}S.Q^B/ACST\AA-T_{T{{_]A]gi8SA{_\12T]{hy2AB]2^_`.gh_xggguLy.`yc}igyrhA/2Lݶj{x=}.cLg6rn8R,,.=.B}huc]hy8ykMSC_8nMMh}cS`o|oѭoBQ-Q{2SySy{LSRB3+upy2BS~yya}hc2S{S1T:_A^>{x^_RB^S2~62c~_ch8^e<,B`-Syg~STy8={QQ]h>gx]xABRhAQLuS22..S^C:Q8nniC^Sxee{xBTinMyce~x,Q]eƦWb}xQhL{2c_BS~<T{~hTvtu<_^yc2xT~7i62Ħv֭B,Q--2S~6_T}:}B-0Mi~AR~h_y]cMhhT_h}^}SQ\2S:g2^1hn~}~h~xCc2}g_rTyT8_8g^:TQ]ST}ai8a_.-B2^]_a{R^hA,2ur~c~1/2xALxC6c^ch2a~Scy_~{>~cc~^Q1BAx/\ra]S_h=\]h6xB]{a}BA~hcy:i::}_M_aS/A]Shnc1xxhn~Tca11c_}Le_~c6^6}xgS`RSccch:cS]B`.C{{_S6_1R,{~}C.]RQhnxRh8~_ch26~{6}yMhTh:{A_\C2B|W86xB^}h~AR~hi{c_8uh88T_hLnThMo~}Tg={SchLe_vѬϬ18S`A2{y}h6y^3(+ohri8^8hhgAh:c]62{ynM2Q}n{_8T}=e{^ecT:uhCcT^c].cnS\S{Tu:nh6}}]x6rhah2Q\Sc^{_1h=g.`~=~_}aQTg~gyh:c~}Su8{1Qc:cTTixB6]S~`,BC2RSM{AS82/\TT6c~]h<==]1>ng^~_TeSQan_]gyTcS2cTh>x6T2c]/6n^B22SM6gicTT~2xchnaT:yAxc8]^1R~hQ/nc^{}Q}uecgg}_hT2AgnA/1^C_:c]{hS^]^hS~wnB`h_8~Rcc8Rxg_TS~>ccTc8]Qy}\QAy_1i~ciy,QxT>cicg8~{8=6]}cchy\2c~AQg8]R>uc1Mh2AQ_eyT^>~h/`/1hnyCT6_cgrC/1]\1~{C_~S2^2_~\/oĬBBTxS26nhh:kttս*:g6i~2,Qxcnc->~.:]QC_SgyS]]n2cyS~~SBA~SC{Rx~yMS`68^Ci8cTc:]Rcag~S]}hc=SiQQB/in2MhS^a:i__c]eMM1R1ge1^ytpe^x2SSuS1:.x2Tc]QQ:2A/c2xh2AShc_yT/C8SAS:n:{CARx]RQg9/^n}MTRxScha6xBch_S>y./xexSSxx^C.R{2C1xR//^c~_^TyC/CS1y2AcT{^x]^8c_R_:~Cx_\S>_xxRccx\]^2=r_xuBQ^S6g_B:],AS~>y~n^TR-]ggei{8S82^Jrc6h>ihie6{_]._~/S~2hx`A2{}aRScxQyMgA.^82Shc}:yBC]2:6^xST_^~g]/\Q`y=CRTcx2c^\S{Riu1\c>2{8cg6_QQAh:r=x.1}8T}nu^ݭ~uTQenA,Svݲ/,{8g5yc~1{c^}C\h>3++PNBT^Q{:c]_C:w}-{]TgB-x{8egxBCR6iB]y\_8hi6]g[@wTgaT{6LcTcS_26ch2^1R}CS~h>8T6eS]}e1{<~S,,x]cgTS_^R,,Rin_AA/C6:h}Ay=8}{:12^Qce{A,Ax-BT^Q{hS}yR:~QT^,_LS~:-R^TT}xQ\{hQxguc^TincRTwL6x_21C26}~S]B]6}h^]xRTR2cenhc~{ygScnT`/5Tih}^x,,hu]BB.\}g~SB_:68gDvOto-\gS-B}iT~rnc\^8B~S,yh_cd驓{Q1g_\2̰ձ~]{_^x_hS_y]]Q]cTk3(,,y}ge]C}gc6=~BShSL_\~T^6TA-`^hiT6gTB]axQ1CA6]\x2626y_~M2T6hx]ccB,-Qy6]{cT~CR2{R.QR^8:_\ST2h]gy~g]]}Th}ch]CSTSSgn_.RSR{icRA~y]c{B-`^hiie_CRia^cw~1ARyhy/BgMwu}CR6cghc:h/1cBCQ22x:Sx]_g_gTSgS_y81-Bxc6gT8}AB1~ni_A.QA1~]26cکݺo~{1TcTyhMT.1TCTaR\g>}_gXǬd]=~2}wfooݛ9{:_RSg_y}_66Qx}B.,,uewyT_2{{M~T}grMrua8nhhrgB.^}:h]C\2irnR/{B\8:Q/^x~S.gu>8~=Sy~a~yxScyhe_AA^]gh,BrQBSxQeg~A,`1:ec8SC68g{]R]irnyC^_..:cy~c6gec1Srw:aui]21x]x{<_C1_=nT6geL~B.^}:h]C\_unR/:a\`C:~/`CAcry.~uTTy}>hLgc6{]}h~>i{xC2^e\,C6LSR=:i]`BTLnga2\T6~{]AxhLS\x^/BέݬǭD>S{^]2]}y]2{>LcMMi)*xcLun\.hrF)sx,_g6__8^_hc~=TT<~d3ݱﴴNB{L~]2_yacTgh6^2ehTS{T.,]}c6~S_ScTRTC/1}=SAxx]_2T6hg]^gycxyh}RQ}>_QB/x]/AAB6y_=hncR]SB.`^=B_={]1{>{ehy~<>5/Q_~xC=S./u{RyrhBTgh\CS>n_{h{^A~S~h~^]~ig{__{.,]-޾詤Q^6>n}]_Sy}Tag:8__h~xxy.B_yA-_y]]\]R\xAxR-AaM8y_2cy_S1S~uu<~n{_y21]ASMyx/B8gQ21]c6ycx,`]_6M1/w_xM@rSx8i{6iyiCAQ]SS^CCS:^g^6:^xTxg<_``ݭ.QS~:a2T]2c2_~~S2/Q].A}g]QvodoVx.\c8uF˵ݓg1yTx/B6y`CcB^_1_w81cycS_hSB_S^RSgn}AaT__gg^c{]Axh:a~c}QRin2Ta^2c6R]n1S6>~y>`Qi{]{hc{Mg^1B/^c_CT~_QTwTT:gxCR_\ASBQ^6hyx}]_2`\h{16c_^CB``]Tc6_Cg:S~R_]_8CRcx`RC\/RS{h]`]S]]ccxTg_xAxh:c~c6CxKi:urygLg21SgSB_L^{yhh^x\]B2Q/Ry-/h2C2g}{t+[w}Rc^Qng\19{ur~c6u]{u}cTcacy6>~^1CceRAyccguR-_8x.Se_xx7i^1hhy^_^_~_2i6xy}1C\T8C~nڻo:C^gyT1]}]1cCyM:yS1Wʬx{`fୌv^cgxRAx\`/aTA]cUSc}2c]`CaTT6crLycx\yRBA}y1xC\{^RT~}]^}T{{-]x8hh>~S1Th~^/CryB<{6Ti}SCQBS{RQg_B/__Q]c/B2B^cMcyCSM~66]A.1g2Qyy]^Sy{i2^=c\`.S<:QR:yQxi8]SS2~{/^eac}{1^:n~Q}^\R~~S__^6y2~~}]^y{kϬǴoM_`ahyg6S2R/Q2SxBh*`ʲӬtdo/acc2_T7}]cLd8S^y_1iySx^{S<_A_x^2}:Tx8_1_]T~]ARh=]QhuwM{C^}{.,gg21\}rR,/SScTR~ngRBgT`]g_hhRS^/_~xgM}c6Rc>g}c8T{y}hT{{R.Q6a`x=hgR1gSSi_\S]_Sgg_u6yyxSh\2n=]SDvSTS^TS:M2A`/8/^k˫ׯ.ܬv*|v*/6nnnW|_`,yoٱ+?HcAxcL2ixChc^-_n^C~a8{y]2Syi1\Q.2TB^:uMuy21n~_-_>1\a}~SS:h~h8y6hh{~:}]6n^^_~<_]T~gcS]Bc8c_QB}6y2S~ga=A/A^R16Se>SiB1hSi]hxa2^LcQRy_26fS/zDzݴ+PB6g\`QCy:hx1}.Ch~a.BT~SS_caS_]S1BR{_6nhTCA_nLcT262]RCh}C:n8Tcgehi:gThT{_{Q1{S2{gTuTC]C2g}8_C2nu:~SB\2}rhQx}gS`^2ii8/Bg{c2^^yT_}ycSxS8Teic1R_>gTgyT>aT1C~y\h=~a{{c~ghec{~y2^8yQ1{S]_6y>cx^x<^8Sc:]BxLg{]R_c:BxcgL~B}MhS~\1S]˭dSg=1R8y.R6hQ\n~ĸtv䵫8S]A}if *Y/5aac]BAS]Q^c\BSThBQ8T_gT]C^]]__TR,.B1]~c\x_\{=2T~_^R^2x}>y.B{yS]>=eyS<:QQc]CrS_iS{c]/1~2x}nn].QSai:yx2gSAy:^x}6ARhT]cSCRxxCcy6]/AR^28gC]SC{nii=i]yc}}_}cS~>=T.Bi\`1}}<~}S\Syi}`Q2A/R\xyh{Q-`cy~<]\yxBQyySg2cyhr_iS}S/QxS{Sx=]CBCQS_iB-__C^h>_A]_2Tncy8__C-R8}Q.xT1]icMh]C\`{^2=_2{cSBS6Q]n6>i^C^^_u{CR.,Q]SThc]Bgu}CyA,c^]SghC`^=].}66cy8S~g_ccSx{^-Rn],^gynL{:uhSy1^2in_.S}S2_Qx~ASS,xx\]h{~h^SCC^xc=_]nTA]]/,QC^Tehc1B:cCTA,}^]{:x`_]QyTT~i>h{T}h:Tegy^~}BSuT,^hM1/^ٲ*yygceyM]*tΧʵϷĺ*tWie\\xotĭ)TTTwMhTxx{LSS}x_]x]xT8]}6/`cc{hc_Bi[R18~}=ehABC2T].\{]iCAxThg1yM8\Ty^^_1A\TchM_Tc8a.`^CC\T].An2^2TSBT8hQ\_]c}]grn~y=A6R,`{~i~112BSLg1T}~_A]8Mge8]{^CxC{e]T>C\8n~]BhwARTg}TghS21S^/Sce~B^c{hynnn:]__2hi^^~}_<}TccRh}A/areA{7QS_1AR}g>TQ^r6Mr8~i^R:8C_nnytܬݴby1QAB.1BCFtħׯ̶ᵰ˴F|kvFQ[>cA.Q]6{R/^>TT{R\Qax^{Sh}CcaCSc_RAxS}yB^S/Sun1\{T]2T}TS^~c{8g^/BS6c{cS6T^x/B^RC^ycR8nh}2}T_\]:]6h]~:8M{\ARy`,]ccTihhyih/Qyhhn61/C_^y{=u{`B28c]/]=<{S_A\Q:uuyce88xc}B1SxRAx_TyB^S.2^RT}_{:{]6}Sg^/BShh~h}g}2C/B2C1{~h\C8nhc_c}2A1hM]T~R}gac2]2gB.yn:~:~~>8x:g`._a~Zپ头W={{_A\Qhβt*tdt˭h|3|@iiaS;icR{gyCx{y]x]^a8CBx_g8cQ]~]]\/`RT1CCQh~\C2AQx_1}hh~ga~B\^A.{S^868yC1SS1Q]:^BB}Tah::_S_\Ccih=iSSc:2QBxe~Rxc~2S:{Qx_}^A>hA\_ggSgy5hh}ihy^8cRyeTCxy}_]^_giS2y^~h:9]TAC\BQ`Ry]1C-/r8C]{CB_}2ch~6c6B\2xB}T_h~gyC1{{]AST\R6Tahh__2AR6=hT=ThC_Si7}yRenicgTCc~yh86c~c}i}2g_/Qc^TTB.Ax^<~\ShAQR\_]MT8_a}]hhSC^_}T//c~SRTgT_SC]2y~6={C1ScayT]^h/_rMh]QC~^-]i_]ghh~xB1cCx~^}e}x,`]gc~SRy:82\S8hThL~}hi`ByCyhSaMϾݭjn{:SCyiitĭú˺ŬjLB]I,p\h==C,.cTx>CB]~ghinc_c\}u8hM<~cg^\ACShyy}S\R_c=h~8^B]cB,\S_21\_[SQ}MTiLuy12y]R_ghi~2T>S,S_x{i<,^i_,.R]xC1_{h>~2{]/Q}r6T]~c}nc~hC,`{i]Agh:A,.gMg_1.R{Tae:2x{Byn::}^{SRAR1Se=yy}yx]TL=hS\SeQ,]xC\B1[SQygM2ernSC2c2\_~hic]{S,_:_CShhQ,1LM}`Q^S_]2ya=McecCxh}{B1a}T=IݭgM9_M2Q1cꬭdo[S.|3wuA2T8ec_C2}:cR_gTc6_1aT{hxcinrMegic_^xC^=n~g:aB.R_2c~~S_^Ax1}8]^8yBg>S8rMh_CxS2RThc~RRLg^~MB_iLuBBc~ST8g_2i8]c].\acT:\^S}c{6^x_~]TLT2{{1\_a1]6Q>~i>MLa{c6c2]]A.ng8i8\Q]}y:hcT^BRR_h}RC:c]/T]}n61ARS^Ry::~8nRRMcx7n~Q2:iM>\R:c~hiccyin}]=>e^S_R1ܬId}M6S}սحtIڧ鴦^_=n6}_C^i~R26{{_1a~AR]TT{TS2~72ng{gygi1\CRTLuTTy--A~_{:_ax^T6~68g^hMhhARc6^A6R]hg{Q\~8un}\./Ta]]]-,y~2c~T<>}_CxghTR2hiSC_Sci:hA2yShy`2_xgrn_x82c~/SnhC]_~g6~aT:TL:ye_}8\B\\_<1]],,QTg]^g^yh\x{6gg8>h^8ug~~BATg=Lyxh^_igx_216e]xcy^6g~nLgB,,S88_A{T{^6yg~hca]-R~cTy,/~/˹XhSh~voϴg_\C}2QT-uKri{~nRQ~>~SBaR_{M6B6iTa~c{Q1]_TTg^A2^cc\SL:QBcT^{g\}2{yAB_:urc_h\Re{a1Ry6cR{gT>hA1~M:_i^~<_].,^>}_ghcQ\~cc~S}h6,\ahgS]S8he6~}\Bhh_ySB]}e=i{~*ݴvn^_S٦ϭvtou:}_iBRW3uN6iS]6xC=x~R\/ghgS_r}cgcaQ2_^ARSBQcThLhSgw9,{h/QcB.ii^]Tx.C[hCcS2y2rS:<~_/]~Mna8u:AC_c8h:R1SiiTQC]{Mc]RB/}=gCx1^h^26<1]hiTRC6}Shn=xC=:\]gxQ_iTc_RA6__ySS`xxR:}BR_B/TSS>a]Tuc-~BARQnny_}1B_ui]~:]1_1>RaMcT]B/x}M~hQ\]y6g6~~nx2T>7Qx2TrM~_xB/}==gC1]_i_SgThL鞭obرxĭVr[R}hxRQ0GT^`C8QBTchgC^h~}2~~nRR_^SS8_~8{=hx]}R_i>h]_n]=]^\Q:8AS~x,QLcCS~8gyu{`\n8_=}]y_QS::~{:r~B.S=hcAC}{]~SQ2wx1A.cih_RCxy}i{c<~}^CTnˬܲt`߰ܭV`AcR_cST|3N{Ln_C_SAx_xTT{g8hSy:]^RC_R}M^C^2hy]c}iTA_CSgR2SA_=nS^cB,ca6h2^T}6`,cTMT6{QTgx_S1g-`A}^.-/T^\xTh:nS_:\A]B`\8RQxh=A_:gn=h~2^gMhc1guuyB8gx~T]gMcTccQxu6BQQAA/}:~^^=g{:wMC/Ax_eSagSxA9hT]}_A_iyQx:nc2}S:-x666nh2]{Tt鲲vU6_eM8y}8<1|uS}-C:{Rxg6xyrTB]xCx_Tg~]y~yhh}_y:]Bx{hS~]Q_Q2hy]T8hSQiyL6a>iT=xy~chh^A\{y`-2_{SBS8QB.C^.`_hy1__xCTe{,A82BCgcx_u8iMS.\AR]_TS]8TSg8y]S:]Bxy^6yBS{6hcce>h_8}hTAT],-_acTThTAShx..,RyB-RhcncAa^6\2_,/e`,TK8uhCci6g~8_Ay1/xS]\]yxB.QhSg{ByTR2{{:<8>65}B/y,-}>g_y2R_6yTex^x.,/=Sx{__h-Qh2~MTA{TT8yT6h_c}_~n8TA}1,-}h6S_c]a>SBQ,y2Sh_hh_t~RBa\,-ynR-x2}Mg1g:B-y>>h2Qx<{Bge/QSR`Q/Q2SRxh5ByS_ST~_nn_R^{h>iMeA_:T}S/^2CigTi:i{6x]8R_hhA,`SC__S>~:2x{cRR{TySQ,B21c6~8_~}Sacg^T--Rcr1-]_ynaRc=B,_ig1`Ahn_BQB{x.BQB2_\Ce{BSTxC^TM6_{]{6r]6h~\gne_{ne^6c~C魌2}nh{ic--A{u_Q2STI3Jx2h\QA,2~T~S.\]C\-,^h~eLR1T<12}_~]^\/cS]c=}2cR.ahRQ=M~yCB_c~cx{21T^2gg,.a<\2cA{igS1R.{_]RR6yTB,yLMSAcS_cnMBRgCeT_{ncx]Q{gc1^h\Q\,26ya2Q`A:CQ,,C6{yiyQR{ihRx{_g^2A/Ba:{{S8ngc^B=x.c:g}a2A}:L^T2C=g{n1xcc,{1S~AT8{]C/Rc_]:RR{^SB,{L_gr=cchLSnMi8M~BA`1ykˬgB.6i:Su::cy\Cc~*oT~^\]2Sg~=~R1x\cu_d3wL_{S2CA2:9SABy:{Cyh~Tcccy/QTc]gLT]LLThhnc>T_hg^1hh_~=gh{<6y>}{=i}~hc8>8:hTT_/cw~A1yB-1}SR\ceB_u2\c{2T6]]yTc^c<=2B2\A~=xB{S^cgRc:nyg_grc{_TMe6~68>T>nB.h=6x]_/QxR/R6BCa6>Rynh8~i6]1Q^:ay}hg^/SMeQ\8M^BT}h6{e~cR~~}hCAC{iySFN\i~nScu:i:ic_i.,8Yg::2\Rx8hS1SBT8x^xB]6AB6c]2a^/CB~}.\2h2\_\Q}c{6BB2^S_\B.Ac{_w/CCca]{yCQ{6]~nSSighh~2}T]{}~cS^}{Q^2A]i{xh~M^Sa=g1ci8_T8}BQ}:i>^|tvtB~9TAxyTcyB//A{2i]:/BC.0uO\}hT}6S^B.RT8ni]^}Q-Th}cg^_i^/S]2>c\SR/:Q`//chaS{c2a,B8M>R^iLyA\/R:urcx_g:T]2={181,/xRB.2i:~2_y:_S2S8]{h]B]6n1i.`/}<.B:[M2^x1]xSnS/_{i8BRcg^]g=e}cMS^=i\-Q]CATTgS./CSCB__/BSTgig2TyRABRT<~\C._rcCCQ1cx_86^Snu{x^.\_^CBT8c1]Sx1C/1TCcSxS~h:=TBcrn{\x~:}h9^SxBMcACL8c}c2{{S/Thhg6~T.-_8c2]C\=u{2c]6C~{T=cC2y:RAyg_A1=}T8_]y2\Aa6A/QRccT_/S_8ML]QSinx_>~]86x]{cR]T2}Mcac=icghah2/bgnc]]c=>g:g]_RQ[y`M9yi{12^2.y:8acc~T/.yh}1C\Aayj˴do.2^B8r_Bcnu[uS}r{h2BBx^/BySW-ZB:6Axc8yhrTA_gLhT~a/-STQR:~gT{TgySMTC_]^gh~:g18{._ygneQ`Bac/x}1_>}STySe]1i>g86~c7h~}.CLi2--B2~8~~~x]CRy:6hM6x~6hTQgcSa>{^SS2~1Q3wLAa2^eC=]{~~nric:hQ,_y`A>ic6hgi}Chh6gr8^}A~__<~A~iC_TT:}iiB,Tc.RM:}SAa6c:cS4=urT]-]g:T^\Q}n~2~2`]:iMB/RAQ^yc_y>r=ccyc>^/_A_nic~ix{VR״~riaSBB{i>gT^CuiT}Q{na}e6\-1ihi<]TCBc__hTyS1in,_h]C]}i__hT>LRBARxSh~\Rahhcy~8S\-Ah22RB8_~Ly1gR/~S^cg}_T\B2^6~]]hAS>yyC_h6T_=R`Ry6e2,S_h,,R\^~}yc1}n1i<^T\/T]^eSS_C:R,y_]Sr86>SiBQB\]~Tx9iga8_A-\hiSSxBhnSa{xg]TS~y^S/RRSy1_hASmccLr^/xcc6~1,^]6i,A1cSST]6~TT1T:__h_QA{g8_{:h~6Z鴭ݴy5~i:e=e_A_n6c_1>uagS8\/c_1y6d3]:yc~SM_^\.1hT1SRTxT~6}\AyC{C{xCx^S_-/1xB]hn]CyxAR]C^ABLi8]./Q_2_iL86c]gASixRgr{R6_Q`yL}\Cc:c{B`_TR~u6/QBR1R{igCQRSA2}Rc]2g_C_eg^]]B]T._g]hycMeSrn^1-R~SC_\wc2~g_SnSe^8M2\QQAR]xQ.Q2_BR~x\S1Rx_1_eBBn:~^/QBSS{6SS/AyA`SixRm/g11~ha52.,^_B}u~/QBR1R_gxA]6_c2hSScx]~~^1]C{~/WϬܴR]xQ`S{1Tc{hy^_}ScxCix`./]]{m-H1=g^i:5ic^.{hg<^c~h]]eyR.cMT]-}yc7>^A6TR\5TRC6yg_{22~6^,1}:6Cxng`RSg_~=2^c1ncyS11}yB6TSC2h=>1TA`811RghRQB2R,/2xci1}n_B{gecin~xA{/B]1~_TM:yx.,2ac:]~h_Si:yCihyBc2S_}n:CQTiyCR__B2g]T^{2]{SiA,Ryh8]Ce.C2a1SRR;c1incyS*|hna1SiiR/_Q,8n22xiCQBS_/x8}i>{hu~x6^A6cR^88_^8~iTcySc6n^,28n:}\Aic.xA3JBy}C^^Q]Ty6aS_:^c^6iBA}2QxcA:C~<}2i{R{=r=S{6{T]6r~2\2A`\yMr~Q__`S:CSc>~{CiS8}S_>nh~2gTSi:T6e{6tݭ豫*2i{R{>}ci~ych^g:SRiwgRy6:~1Ai1B`0rQA}2S6AQ}i8g:SACBR~S1ec2_i~Sa2TurB]McBA]8iaT]1g.]@chah{^enS{S2ShLh<~T8A/_]xC2r_,Q{2B.`]{ShgSRBa}yS]{\xi6B\~S16BBMg-\i^{a]{i21aSyy{yM:aT8\Q{S21дd/.1__8Lc^^ChM=68}hSyLi^Se^T_A8urc]2y~tñv魭YB^McBA]icMugy11:/1@ygi}gM1\}u=_W3LAQy]R~_`^c~=1_~}8h}1]2RSSS{A\]c.TS]yg\.~1A\_t׺|ܴoyT_]:ni1{g{h~g_{Q:>=],2LS^B1LX,2]y{C{cRCB_\S}_LnT{^SB.^^QQ:Mea~1hn1B_x`.-/^=h]:>]B1~>c\\^{cx9^R{yS_/_cRCCQ^T^e:hSSB`2yxQS6/Si~y]CTLT{6c]Te\RCASC{aSiM:6T{}RQ{SARuh~chSCA/^ny]:_x_S1^TT`/y6~],,Qx\Rg6B}i/BSnM]BxA/Sn]M{\S:na\AɭvnhSS^61/T**WAyia_in=ggx`Sc]BTB-}>gT]CyvtjSS6_xR\Q2n}^<{_}cST8nQghhS,_1^iA~>CQ.xiky7^8:{aT]CT{.xh6_gn~C_{\/T:~hrS]g]Ai~.1}TcccnML{/2:^B]_1Scyg{SR._8TySR`-Th]TyBC~gxQy_C_]~gAR88hgyABC]\]h=ySc_^B,CiX3uL]T]MLi}{TA]}R6/Ch{{_z,Q}cn>hRhag21iSSagh:S]i~M~_c~~}^Ti^2>_\2{T6{RS2_=gnnayyyTc6hMBxi~AT6\aoWa2ǬcitvC6VIoSRg>6S]ydzt`Ƿݧ8i>\hhaeTSacg8:ic{nMhLT1{}a}1{Cxk3?YS}RAnL{_:g}8ABc~g}A_nng:e/,ACShnu2]^}e8c{ny\hg]TTchc2y~>,-ci/{Bg=1AC]y^]cCB_^C8r:gg2RQ/Rhc,{]B~cCeLSA}T`Qc}y^C_yScTa^CMr__:~ch\Ac~g}AS<:Q,B\2<8Snhnwh_L]/_6hS16g~>uncR\1y~~S2yAhc1yg{T:c1STn`.8h`B_Bg=VAo`tLXz*魤tt}j.BWWVko*ox8nnn{h[}nST\1iyC6yS>gSg8_/.BR{:h~:cT\cAB}Tgii{/`1:_,/\Q/]ag86=n>ighhxe:2SR/_hS\~iC\Q=x`2>}1xQ{h{\AS\1=g7e2BCgTy^]{TT^RS/STC{_Q2SAS6`.y2]~2/^MSJiB.{yy:_6h{A\6^R6yB{n8{ehSQ`/2gc{aS6_BcA6c8<{/`1_,BxQ_~~T{ci:68~A6~CxQ.]g^AgiAB/_/T{CCQ{h{AĬdDY~SXŦv4o/˲t秶oV6ahThyA6RC8C{_Sh8\Tc\2}_^1_T1x]y2Q]ncQxB/]ii}x-n\BS2BB]9~RBgn86]h8SA^hS,1ncc]_~_nnRB^hSSA`SygnT.Qy~ccyc{C~1S6~}}}C]T1^rx8S/TgQRr:R^^.`1i8Q18R\T{`2na\_cS2^y}1BAC2R`A}yB^RA\Q^cx`nLCAySRA_~}B/6>i~hSAc8{11e2,C=yyC^M<~inRB2h<{AkŻtbڲzS`˭o˭ˬĦddoCg6\21x1A{_QCr]R{2R\S86\B8=iMy\~:cd3s8u~cTcySgn=^ST26g::M2R=T22~^h:aS]}g]-RT1T}c\2ic:c^}TB]:STr}~hu~Q/cT__n{<=T_y6x,\{Cy{{L/Rc:c]{SBx:6~n>^{hi~Q/c=cTUoշӭtvٶǭ̓ׯ̭ײݩڧBcgr}/2MLeT5Mwg^hhgcS}e^`R{cTL>Qxi8c:_ch^^i~g6^eT^}Tia.{6g_g]_S]S]RS8]1~rC,T:a_acC\RST]2c^M_{SC1Ry^_~}~~^cr8_}`{g2h9T^\hnieS.}^{2.]2:BB_nexA~n:TTcyxag^_{T{:ch~eM^Rhi~Mui˺嶭贫˴ٲڭ.ǭ٬]C{iiSS:2`TiyCARy8{T6_WSBc~BT{W-w12A{ha>S\~M<6]}cC_}6r_^^hna~nr1{~\QSy^A2uM]Q]xA]8niSRC_ia:_Anu]2achTABxQ2hiaRSun_8^]/T8cn]]hT1ww^S:2i]Q-gyxn_1]2x_nSyn=c2_Q\^_M>]2^h>yyh]{~A/{cSCS1B_^C_8n}]^Tnh<_n_S6{h6^/BxQ2h6xTMS]T{C=L2^h_\grM*.կܶv˴ײ˴Ĭ7zVmX1yr2\UWUD:SRCSnLDqX**a{h~_QA_`3?Y^_x_S]:_SicwixTS{hS~6{SSg=niTnc~c~nh{Q.]iM<]AQ`1_wn2]x^8cSQ,_T\2>Mrh\\.\c6],,ycA_8}S61,`^TJ]\S1\h{L{xy]nia1Q^SiS>{1cx-C8iyyMx.2AQ1_B.xcT2^_xS{]=2^~}w8B2]]cxS}{21{i~^M6~}gTAQ]hLCQ.,xSiwLy_2{ieTB,{cC_iu>yR\.\c8{`,~xch~iS.{gL^^\/Qg~^T1*ڴz̲鬭XskRB_ir]o*l2]xTtt_iu>cC]-3uungghTg2CRQ2ihTT}__cg^x\.CyC-`Rcnnh}8/,yy8SnS/Rc2C}caL>]R_]B^g2Q{=geia2RxB_uc22~S]h:{c8{gL]y~Th6a8gTSAQShe]/{yT=h{2xQ]~1-`C~x,_cyn},A{2]6iLuh}icA1xBR^}y}i1huMn6:=en~CR{>]]nigch}yg6yy_2cc}{22~ni}yTgn<2}8y^6}_y::1\xic-/^~:ncx:Sy:2Q2~<~Q\^yhcgA/~TR/yy_x`/exB1RTuTy\c-\_2}SA{iA16_S}yaT}nu=~TRBxB.2}\18T^~Ay8h:{ya2Si_Swac~ycT{]=niy1~h]RB1~S8>_]ggLT28nTgyhL6}:nyAAcSA1}~gA]{cy2{nB^c_Cn_S6{TSxh6Ra8hcxShe_1]6hg~]]uLyQhe,-BQ_SxLy/x{1_]CA.^=c8rycey^\_Tn]B21xgiy1C2}Bii<}S{~eac\Ac_Ai:.xRy}^Ntoʱ϶ݬ筭`籩ĭvcS}LhBR2RBhnQ3Ps,/h6z-/hB`A/]yBQCTS~S]]_a=~y./S2{ha]CS1.Cyc_C]Te2cTc\TSQ]niy{2aC,1=ac_B]TR/x{8g8>rui~g=c_]r_/S~yS^RAx~]_Sx_1B,,BT:yrg>B/2~Liy{R,Q>\/RnLR/xB{~RA1y_6y2Sci>=gyBR6T6{^}]/]T}^R^}:Tc{C}{/]r6cMyaA,2n8g{\_6CQ^Th:6hyxRhn^QyLc}S^C2::V*cT^{cQ,x<|3,Cg~cQCc.CCcc]21^8i1``-R]gnn^QB^2M8h~6_^1A]AS_2he^{8_.ScC{8y}c.B1RT~Sa11AC\RSScha^xAB~ix:u~{x2:TneQB/Q8]c>Mn=ic_1AB2i~RRy8{_2Q..y=nMR,_:~RSQx2^8g_Sx]gn2//Q_SgnnnhyRR{ywnh:~c2^^xS1a<_xg~x2c].yh_chy2CQgc:aB~M}S^S:cLARB]cvtܴoݴv|_~1}cy:{.Bx`0cCcn~Q6ehayg~1xgS]gBQ`Q_^2c_CS\ThngLnM6_6~6\}i^S_TSAyhR]aAScc~Tg7a}CCSihh>gTyBA_\\_RR__-,2:>gc}RSCxT^]}}x.,]:yhhQ}8h]x2^MaRR}S{TuPZDͥݬʩݴ鴭o{8CaCce~8chg8e2Qu6y}^28:S]hhyyR~6^\/ScA>r}}S168R{McT22/R2Ry6rT{8^bh6Sgn]AT^{=y{c6i_TiTcc{y_S_Rcgc~yeLgRyn2QR}yg\_a2i~r}~\_ehcRSiCa>:>8ccT_yT^e8/-xT::~T12iTyn=Ti}Cgg2RQ}h^rTc{1hi_g~}y5B]MrT_8Ma6ir{{=~h2}RQ{_y6c~e>TCg~gcTy_S_Cc~c8}h^~yQAS{T8.RrT1}guR2gh}\Sc/2vtttttttod#_R6_{c}gg=yRgh-=cCAxy{Q.B]T22A-^h8T8~~2xhigh~S,`ceL2_}>BRa2R22~a6Ln}~_2:~ScSTRQ]~y_6}2xR_gcaS/^M2\ah:Tyg:21A^nh]_MhQ}rMg{yyc_R2>^/cy_{^15h~2\\S~1QC18yA\2c}C\]2cA_2\`SRh}:h{1:<8hT`/~hn}ahrw\]hy^y_Ty{=_}__hT~Tc]BSy_cc}S]xR_gc6yB_uT]hiSSy}iA~Qx=gR]n88uhT{yc_A2gh,^hS{c_1_8~2\\{g*`7gr~S_Tgych1Q{gV-T>8TSc_B]R1e{/SM}RA_g68e2S\SMhgcnL2\S{{~cA`T:^^~Q2Lg~>:Sc}_6~c`,}]R11\QyL{xc:Xڭ宭ݴk}~2~c6SxS\Bh~x2edu6_M>i_86yT::12yL/R:ae^QB/^8gMR11>h^-\hha~}8hnh>].cc]Rx]C.R_]2_hC`2^Tg{gni^AA1c^`C{^2TRB}~hg8>~gcCCei<8\.A8y/2TxcnMr\Ch6}ccS~hScc^~MThc^guy[w~T7ih:1SiauQx:gcS]hBxxAC]Sh~:hx,]\,^c{yew=aB]g}gxB6e8~}^B_x1]^R}ghTQ.S]QSBSh8cyAc>iny116o㭭ݴ,ܴ-.t.TM[x{{]]2{hgC,_1,x|M25=1A}iB-_6A`h=2_eT/-}S.QB^cBchQA~cQR_C_hncy62C~=w:hiL:}inc_c2,B={RChhh=6_yTMu^`^RxS2{1a:^]CR_6ggc=c]>~SQ\}8yc~>}Qyx^_\/1}CRB]Tc6T:c^Anug=TQA{]]gy~Lc}86_1ckoﴧ˯xgw::M6g=i2cyAmKRxichSBSTnx,R>~^{72~~\{2B_c2TTRRccRA2~\/1{8{SgSR]iT{_2cSShn]BhL8Tii{{~}~x^c2Q2RS=~TMhgCi6]{ih].~eh:hgx1]CghRCe>yBR><\{Rxi8u}1eh],Ca]{6S\i]~cA{c2TTx1ga\A^LaAQ2}:S]c]QAg~2^]:]y2^6iC/Ti::hLhhgh_yh}x{C{>~]Cia8cQA:}1S8].gu8hi8aci6C]Sy>yyM{Rvt˼̺}R^iuicyS_}_2~u_AkLBB~riy^BxA>:x]R2hT^_nBC8]RQRey\{SB2Sy6hy/RCC:^6}--/6hA~=]}6a~:nT`2}\/\eLSAe6{S./A]cgcCQ6unMccc^_aTT_2:}-]h]Ax}}SS~:]Q/.S>6\^Mr6^/Byc{]TB~u~T]5_1]R^hy]SMC{h]R/\e{CT_B]SSc{B1]1h]}y-,`yh~cQa\Sy{cTc~yS,Ry]]R_e2h6cA]Ti~1Ber_iSyyC]y{{2^hT-]:^\1y_1]{g2B\C;S~~^/ٺݴvto`oI;:\81yTycT~~n~.)3u\`ATh~h\AC`8>c8{g>_~rhr[2-T:_6x-S6cn8B1^Ayc6~^/CeS{TaTu6T~CS~_88CT_6<_^hhSA.C\~:{}~y1Sc{_./y{}hiyRx_x\S~6g},`Tccchac8_Sc_7^^Ryc]cehCB8hB]62=6yxCSg^B,1h]{}Tc~TTQBSihr=_8iMMiS.R/2T>hia..6S\g_S]Ti1}MirrS1y8^Q,\T~M>R_>hy{T6yy/Q_cnCTenCSxQ{T.ӶݴO*彴h:~SSa_Mc]_1T~d3:h]_yQ8^^2A1{}in\TRA\yx,Sn_Th=c2BR_T.Q]c>Tx7h1QCggMyc2{]B_/3}hC.86.^=2Ry_AR_^.\B/B}n8Q\c}ii~g~g8ic\26R-.g~QyiSQAS\}x^g_Ax~hxx6_T{_T2c]R]Bx2C\Ccxxhy2e<_hi:}RC]ASc.,a8B/2=}]~i~g:ScSSaihg}yh^Cinc2hT:ic~hR-agBa6Sh~1\R_i^.\Q/BT8hA1n66SSSSTa8T\_~R-`g~Qyi{BRyRyiACT2x_:=__hTgcC_172x_A^S1C182SaS8i_hhiTA\CB_}-,}~Q.^yB1ccy61=rS1xS~~~hcg^]ftܶ7c紴̶d16SC2h_2y~g{aA,p]~}cg/Bcc__2_2RA}a}yAb:Lc^n>TTgcg=nR_n6.,8i-{6hh\1exT~2R\aaR_c]^{g]\_^hhaga1{g1C]e~C]S_]]`]TR/nS1x_T8<__8_xic8__hhS_8r:y\RxxTc{cSS_2}}A]~}SB6<~~TB.1n{}T}n^=~}c_`]}R\c~6y2=~h1SuMr6ABR^]CSC2gcnTgiT^6M:S]]i8]y],,]]{8_]~~}S}_{n5ygQ`~=T,}~SC1~La_{c{^2TcRSMcRh:y{gyA.2}n6{gxy^A]{1^Tey}n8B1n]g82]}}n,-^TAR~6RSg]8h:___ni\{h_/]ca;C/]S>i~_CT<~a^y_\Sgh--inA,B8{y>T1e8BC8_CMMCS~xR__:B,`_~x]hh2a<_e6~hg{ui~hn:_`Ag6h~~216cSTCCe_2giy=ott孭ܶfur_{6{BxiMC_}S~B2>cS6gxR2Aar6xx\T8BSx2=}8hhhrg]yTCy8R8ax8Mi=_Sghy]gcCxc~>y16y^{c8}BTuT{A\SR1~xA{~n_^{nTg:iS}nwhx]Sr8^]iMccihSyC-1i>C//`_Q.nCSraL\Trhc8c\QC/SyC]1gxRc_Ty8a~g~Lc1yTRye\Bg}RgMi8^_}chh2Cc{AS_8h_/vtXy6yR/?pBSyAQS~6B,.}g]8n6}yn.]Sxg8~S\AxyhrrTCCS{_]ThcTcy=iB~h^22B/^}~]A>}a~ic^TyS^T~yi6B-]y2{6Mn8~:ey^]a>]Sch}BRcTR~}]Sy6h]^6=SAaLi]{6Sc2-Bhy/Q_~SC1\Sr~:hhM^`^R/~r8]SLR`,Bx/,uc\y}yS1Q,Bc=ych_R26yTi~\{~CS-QM]B_:}}6gcT~TAQ\]^RAyi~T8i~h1_n>}6i_hM8gna::SRan:c<]/1cR2RR6>Mi_/]{B,B:uc\{T{y^Q,Q_^Sa{x_gTg_c]T=`/LixB2hTyec6~e8xA]{y^C}i6y~h}~hc^y6C^}2}hr2-_iSy~\Bg~y2~CB2{CS>1_n}Ba:^~ncy:}hcBxhc1A:[}6}ncx_}}y6i{Axc{cM.,ShS/}n{S~2C}c{y~yxScx`/686:c2_cgS`,x^_^gh^Sh7.Q^82STig_SB//x8Sy=e=nh~}\2hS\]i],^hi_A]QR{1]c:r[RAS{2Rch{^C{g}yc688R^~^Ri[6g}>}C_Tc}8ncx]}S}/,S:{/cySyC__^_aS˶٬Ƕp8gnn8{eTSڬ˴ttf*.r_\xAAR`]ni~uTB}h^_A\^]\crrh_g>nixSh1^yCQQ1hT~x6^11__81Ai_]h}\,Q28h^\ceSh_]hhy]yie2_i^y}hTg8Tc8e^2{~{B_h8RB^^_]BQ\_Sy6_]CB,,-QRS}ehhS2A\^x{uy]yii2Sc]ARSe8BRc1C^{S:n]\ii^Ce6R,Q2e^Ac{hi1\c6{]c=hSS=:_T5~h}h~8i_^S6{B_hhC^2_1../B1^S}|d٭oV]ATLncCyiWt魭Ħtݭ068=:n2.^SS<8T6T6~grgRh^2ga/S:h~~{2ccr]T62TT}2_c]C{2Sg_n{2Sge{xSTc8x`2{R2{BR~gcygcB,RayR`,_{]12g]/2hL=:c8CQB_g~~_x}aC\xS]{}ABA]]Sc=c~~gi1/2S^h~ScTc:~guxhn]2aTQ`2ih=2_~gu_68yg=e__}]xy_{62>_1SiTCSTc8x`2SA1^.Q{yyygA-1e}C.,xiic__{h_.^u==hTxBBSecS1Sy\BݱڌݶXS6Q-^6cyy8ru^bݭoJBSccry_xBShTyC:}^hcQC2nyn6AR}h=:~^/CyrM1xc2S{Q1_Cc{yh1SeB{n}8hT}~T2Qy8_}T_{h66>;RCi=yR,,2>ri~{]gTC^{c:g>}gyc_ghT68Ty]CST^_T]S_yy1^1ecB_{ShhcS:cR]S}:8hc8Thc_gh{{mUkWADZn}}BSc6{^_6h~QA~]]nncASh6y_y=}_i:]~R_ec8ay}__1`S=M]~hC}:]^yCQ,_u=h{RgSxce}rgT]yny\h82Cyh6a>_{iy]TTcR-B_c_~unx{><^,,\<8R\R\^^_~gQRVoǻ˶oninC,`S}R`]hyx\h̽٭+1Sgc5c:}T~1gTx:_Bi:6.Rc^`QB^i~_h~cS,,{h}RTn^BegBS6nC^8~gc2:C]gMi2S}S^S~hg~h]h:h]iSB~hha.\88Sh>A,_6{TeSy2Qcu~g1\]g~S:=nc/Q_M2C2y8]SuRy1S^caM_1Tc,/T`]S]~gi:R`/RhTng}_BB,,{h}C}nIVmttĭݬvy:nA,_h~{}hSy^.{Ko,,M^^xCx]}ag>{8S\nR_~x{TnMh]}a2c2cwCgLr}TTCx{cTARyC`BB]{A/i>SC1{8h1y{/Q2]RB.B}L}Q{r[RQ}i_]~T}i~6rhgy8cx{c{Mh6R`1Ta6/]h=8n{CBy6Tic2c^1~S/\<<{_CARR{=L}T]^{6x^2hx2{xS1ynx~n22T]^Tchc\xyC/CC]MSB.AinSRx{:^cLg\A{2C/A6r~.]>B/}i]Cy__~yScTT^cg2ceyiL8}\-1Tc6Q]=ڴڭපtWy]ynxg=S_yRR_T:ڭ˩,K_]yT_1R]/1:_Qy<~_8icAcL~SR2gg8ay:CycR^>=h~}_A.{_2g]yS^LS`{LT{}S2R.{CC__cR/ByTQByuy6}aih]ALrha_y__icx2ca_`,xc2cneCST].xc{SRCTSxy6_\]A_MMS;ݭݭש<6_ayx}cS{cTycc8^6Lhghƿ(~h=TB,Ra{x\^ia:]2]__CR2Tg{R_L].\BB/B\R8S/\]Tcxxcc2y2~2S]~:i~yi=i_^hxcy:cA\]x{ca~gT{xA{^R~:]{TCy2}9RB{=^C_gy]cgy261{g_A_gx`BT2cT6c2]]cyA/\y2A1T<^xTy\/~]hnSA~Th^AnRa]6c81B{STy^S~T6}^]1ccRQRcSCyi^x}T`uT<]iyCc2aCAnR~2yyAQ}S1T~6xyy/QS811i]B]2hT_,,1hnn8i{xg~ѹ׬oI^B]cC-BT>_c{8ySSh2\]6yAxyݴ˴説Ǿ,+PN,Q__112_\cTbnnxC}gh6y{:uyC:~Cyyx\/BhnMi<6_}6]BC:g{cTcxr}=iBxTLTA{1h6QTR9xc:r~Q6e6i~ha{__c\8_TwR.1_Sehn{^{x]}}g,,_yRy_A`_i1`11,Ay_x1Sc]c_^66{TBA>{cM^-/iM>_B/1g~x2],-^_.B}6]CST]SC]R/_^_CBc6_^2\68SC~i1B6Lcy~xQ^]QBBAgLia1y_`2ic^S~\,`rhna_Qo,-^_.B^]ȧ`ǭŶDni^{RQ]_R\AReLigS~y`^ic>nhx_ey_S]^:n8T1{1c~x]y2c6BTMng]A~SSm-ZR}ny_{2Th{TTgu8]_~6TS1AAyTxSx`\a:uy2{]yc{2AAT=h]Q-xThSS~{h\/SrhcM6R}h``r}~g>=]yc2ch]ycQxa2Cc}2ghruy_A./BC__cM~CyySMgyT:=]aL:2]~iT{yx_~2__T}R]}ca}_xR}yR^1`Rhgy6S~6_BB{81/,]icMyT8SigB`^Lhch^B.i~^y8}iMn_c6]yx_{.Rc^R}Tx}8uTSR..BQ\1]T=qܴ-tŬ 1MyS}2cn~}_R\}gx.,1i}:r}cLyh\/_:MT~.uHgcchrLe_RS>_y~ehg]B^c^6{\C{^_6AC_Rych<]RT8}{^QC2_c{xRCx]]hTQ]11_y^\^T9uyQ}6/\2,~n1]82C8Lh2}8a]BB^cgc_h__Tuye_A___c.x2gS2h~2e_AyniL=BQ}hc^{h8giurh{C2h>}R1SyT{R/xT_Tx1T1]}Ax{]68S^~iic/BRR_2yRxxc~1^^BSLB_ix^:/yuw~\iC]~^1x2nnn{C8u1}_Axiny2r}^:hSRQ_]jjWU麵ɿokˬdCLiey={_}R/{^^xAngy_SRxhM~c6{ghnh]]^{^_~2A6}ySRS6yy}2^i^`CSx`Cb1\:{AATc1\1yc\8h2ccT_Tn6{Shy__8S]QS<1Q21^icyTaSTS}=6ShR28h{T:S,,\_hg:R_rh]Q18Ry8x_a62,1rec}_aeg1Sya_{_eTh}QAycSaT-`x{gh8R^enMh]Q^:nx}h]_cT.+LRR~hc_6g>c]~y1cA,_yA6hec}icnh]]SCBCc~^y6_S\_S_6_./_::y1Sg:ahxC1^M}y8cyiSS}2R^h1^L}S{CSy{~_/QSi6_Thne^12R_QQ22:MiT}gy{:cTh8.S\Qy9Q/26^R2r2_]ahS\4\.yh1Qx_1\x}utt=ymvRjyTL=T}y\/\y}xS>~T}x{Ty~_./_::c2}wi2]^B]k^_S2~in_x28a8ySL\]^.x]/S~{{Sx_>]{{\{:hTy8ecc\^e{_CB]y8~}~8y{g/y9~>C]gy22iy]]R.ARc8ye1~u~xSB_=S^ncTc~>Myc2QC6}~_RRRT=>g{>SSTC2c2S{26hny2yii>aR]_Q_n{A}~S]x.]<{66x1a<{^T8M~6C1~yS1A^cihg8n}}RQc~:R]e{]x8>c]1\.\CcT~Ccr}B]h.-Cgi2xhh^12_hMyc2Qx6T~ig2RCCTg{jt:h*|ooB.SnTecQB28gS2~:8gx^e{_C]}g86c:CBa8g-wr>c2]ye8T/BS{cg{>yei2{aQ2TcnryBynh_Cx_]xRn8g2]~_CghT_Ry2ASc\Rhg2ceT}S/]i^gyScrT]yx}i]R\[u~i:^_~2T~^}eey}:hha]ciSS~_S2C^\R}{6c{gg^2gh62]{gg:aQA}8cT8T~uh2T_=e^.2iL}22y]R/.gTc_2eSR6cS]~_CygxxiSe:yTS.1>8\{cycMT1{Rc^xC>u{~a\C{1{cxy~S1]}Ty:6]cS_62_i:]Ax2Rxc=Tݭ4c鴶x.crMc]x^C\//h=c~S_{Cgi>hT_16SR{~1^yhTcd3p:_]6}^SL{R1RS=}1e8_g{6uLMiiryQ_~L:T21_{CA:ha~yM}C16rg1i_/Sag2^~gec42^-`]ahiTS_gige^ThB,_ry_}Mg_2SS{cTgy{ecyhySB_hc2T~T^RS_2g{2SSQ,.6cS}ynMyRxgLhR^_/Sag__8:g_2]--x{:y6Ty_9i~g^c,_rh8}^C_}^]B221^]ST_yhTSg_2~B]gyS6h~S^}c7ݬd\2G˭׬ZC`cnhc{T}A,Qg~yaTTx]~rR2{/_ch}}heS2Q0ZgiS.{_R~=yRC-R]\gQC_xC2c8gTxQx8-BS~cgS8:1A}~}:c_h^_SihBB}2/\_>y}Tcghiiyx12}{8}SaT_6g{S1^Lw.R^{c{rwh{T_Syx]~:S{inSgyx]6:}7^6Mg{CQg[y}aTg}^{/{S]8yxSQS{1gT.A1C]y6T_B`B{>h/,BAy~hTh~._{_8T_g]^ShBBT^/\2STag:ihS\R\]CSTScy^T6S_]_MwA.C^_~8~S]n8_{^]]Bx}8STiie_6{x1cg8{::gSCtשּׂth1A5ܲ謬o-]2g=gxQSTScSh2_{<\Rg_/R_888iK1S^`8T_~y~\^_\c_A]BAAyyCBQx:hBQ,-Tg2]>yy=gC]2`Q226yygBR}}\`.Sw~hu>ha_}S/AaRSeTe_}}RShxQRQ\B,/^^B.-B}ncQQ,.6:h{2:]]8yBR^`/^^cS_gBR}T\`.26SyS~i<~R1xCSS_e8x^}S6}x_hh21{aaTeTQCMn:L6]yeC_Jg/o/cSBVoڶotBx.Bh>=aync2h}C2.Q2_~}Tn:\1g}R-.TLgg{~g2A`\h=g]_SB1nugxy\h>B-B1cLS8TBx_TAg}T6C,\Mc66a_yC^{T__c^{cT>iu8CRgg2y]{g1x~R--Sh8cac-unT}{g~6:}x.\6ii6gyT{y~ggS2hg~cyi:i^\M~_gi~ySceh>hhx`B^{_T>~R,.aaSSggh1^]]2Sih^Tcyyg7xCcgcS1]S2}a6~u\]he2u}Ax_\_yT{^^BgCS{a\Bic]aMege6h6T8{R`R~8:}T_2]^:i}yT1Ccy{_^gr:=_xh{hic{S~hnnihA`B^S_T6gR,.}hhiTS~TSyBAA12_h]]_^Tgcxxc=n8S\1R_S{chMiBR61u6R]SRSgy{~hin=1/R{cT9~gR,:h}yU0[>ng\~n2CS~}ig\BT<^Q{yc61CR.2g_}1.T}R__C8iSR_eS_i1_2/B^h2\~6AB_2\_1-/]T]hCB/QQ6n~yTSghgxx:TSyyccc:]1~rhC_a}hun1BT2RrMc{BRchy_g^/^MSguuM>cB}]BCySh8geyB/_x.^}^{TRAA`x6Sa^Qc~1y_^SxShy{nix22/B^h^\ccAB2^Ay2./CSC6Q`-QQ6y]]x8hg^2ay_2_22ecAAc~A^}Tn]}2CMM}SQRr^nDt˭IacM2xݭݭ˴/`_g{yRhr:^}S^={xShTTL_TTB\_h_A~cCxcc^c2.Q/+[i}hg\1~h^Ry>a{hSB_e]Q]yS}{TT5a{2yc:_~>~S{2x]xxc{-A2\~8QQRT]]}^~hT8yR}:hTg:{gcC}6\.C{]~]/S6C/R<^]_^8SS_y21{}hSeney}y^_r^]~MT-R2AcgQQRc^]ny]ce}h}xc8SchxBR{SQxg:g69wh{/6r=6c}{_2}gc^RrTC28ga^Q]2CcnLB-g81hvvgy86Cg:^)WtUm~>6MhTcy2_n]]6y`xSx;iQC}]]>n~Tie:}xckMcRAC^_Tg<~]{nR{}xS6{{{>MgyySiia:agc>LnySSBchxTg~2^cg_]~=}=6ca}}Sy~8~}_ae_^ih6\iM:_~i~h{e/cngTC}cySRQ/\~T~Ly_iic8~8i~_^Rg_^2{:cAB\x]_yc2BRcB^SC}y22={^_^hh}}~c8=ir>}cRi~1SnTS_Qy~RTeS2c~2x6TyT{{1x_a}2C_gyCAgnhiCCrS~h}g_~8/eMhTRy8c{SRQ.\~Tc_]hhy;k˴kRAx^_cghToooF}ĭsnMnc6C:~1_nTy5a:C{g~2]}~{{:LM:aXY~T]BR{rM6gngh}]T^{TBRaTg{TyAC{^]\RSS2ynSxQSr6Q2u88C:Le2]2S2A]{{}<=gSTx]^.QyS}gSy{A]}2xAA2^]Sny2A}ugB\_ggRh~x\x22RR^Ti:^Rcha11R\ch2\S1,,B}i{_ARi~Q2i}caS}`]cai_ceh~cAR8~cT_2c}{e˺t8gc_R]Ti<ړ.BV2CCSS_ynLT2RT~Q\_:Mugig\cRAR]1RR]{:Q?scM}^^\]6T}wncS2xA^R^}ASSy>gx~g]AC^,SxB_L^,Ah1{__eg]c~S7}B.CTghuTMug8:C]i:~ih_./Si>_SS\T],/2Cx8n_{{S66n~~gMcc{_}6Sx]1xS2S5S8c6T5}SABTL}^]A\^x1grw{}y2x_\Rg^/]]_aRa8_R]2B,Q^RB_LS`Ch1_1]T6eni1}h}1S::TA/1c~Sirrc6x1ecSgcC`x8<_{yRc^,/]RRgS}TTcn66gMcc{_T:T2RCR\bot\crrgS{]^y_SQbt_\-ASxBSL{`Rhx_xxST:a1T8yC2SQ`A{6hW3p2gM2T]]]_ch_c/BSSxuT{}~c,inT1T~]]\Bi11iSg~c_/QBghx2TT86]RRA`R2^cnc__2T}iin^A}~6inh6_C./C6Lr8c_x_heS2SiugMhRAyTyhpS212gi{~{CCC]U鴭_2Xܓ72]hR<>]1g1yS^R`/Q6hC^{T^Ty_CA\B`AiW3wKB.Tn~68}{SS\g/_BAhh{6rgBBR8/_hy^y6]6S-S:cT_iM}}~e}SQ}hS{TCx6TT__T]]h:R/STg1-Re~caR/6a^1yu]gC1x{6cgyBB2^]}TA:=inSAeSgLyy{nigC_}\]~_/`{ng~SR\CQac.]=Bx:_S{.BR8QSc_c=cRc_-_:}{^}__{TS^/yu2S_BS_{2Sc^A^:8h\.2S6^.]:gSe8S}Q`S_RCyi288BBC1x{~g}A{]]}yAAh=i2Q}c]cݬoUiyB/cMhLic2^boo}/AtɴϻXg]~>S-Sc{^e>}2]_S21/Th_{_QB_^]x^{CBCghV,wwa^{g6cx{{~8AB:gTh2A\g8\RRge1SC^Tc1y2,Q}1c}y:hc~:MTCSg8Q]ih~g~yL~RS]hiQ/2xc6/RncRSn:\22\A2_^x/_8/.cax2{,Qc<=]6=S1Cr6CSLC~Leec2iTCBye~>h{\2_}~B/:h~=_BQSyBRCeh\2T1SccBxy^,Q}Cy{2~c_y~:n_\n2cy-hgh:h:ghch]a{ABS]}hhhS`BihyR{L1{]\A2_2xQy:Q/ggC2y,Bcr:RTh1\9-٭g{6^\chLa]}tܭ`cB]^k~1y_,Q}CyS26T^_}8i]RnS~c`a6~~=gc6ghWrSQAxCBC}_Shch_hB.]~8\c_cc-xriSBA1xAxT2_gyg8^g2.QSg{ySgM~n>_{e}A_h]2eS2SxAxS}ggcc~^STR}n2]T~c1]yTSRg8{gc=__/_}>h.Qyy6^]iTT}^]]_~}xcS.{iS_\,Cn^_~^\t˛eg`2ry\C2^x2~yT_;Tgchϴ~CSh81^gS^_1C_{acyyT\1a1~LS^i{yhS\C2yy/3Lgg:cRB\B/]S^6C2hSS1.1hyyeSxS}y8Lx]~1SMr:}ag}}}2Sg}}8TcTny\hB,Q2y2R\2RQR^n~gA^8_T]/CAR_Bxihg{T}___Cx{{Ta^:^BggB,B_Ty]1y]A]{rhhR{h^T^QCBx.x>8h8}S2T~y:}]]{Q,1Tx,662^hfVQ2cQ`cegig]C1R_y_X譻t2a~}_cxSLrh~STc___RC_^]c=hcSye_i]/}T.,/]}}Q3?Z_2cg}y{{B\:^-C1ThS]MM6y8}{__yur]Cc]Q}:<=>gig]]ccTy66Mnr=C~M}\C~8gh1{_B21]cgS.]}cc}8iig`QhchcT:{{cgc\gSTT~_Ay>g-B}S61A}̶~]g8R`1SS8:~6c}\]IӶ{2h:g6}TʹbQTi=r=}~TAA__]]uySxQxxS]B]}S6^8J6}cihS_h=}]]y8TTg{ca\]{_g1`C_yeaT88wraRg~i6c_-/RRAx^.2hT.B^^C^_/2r=T]e\]gcCci:~8Q^aCx}8}}nnTi}2une}aS\^ihTcCǽ׌V]r:~2h>g~}Wx{gyyhc8gR^hSCT{B,QhgRxyS2Th~>wLaxhMhm-uLS//2y\{nSS2_RCA.xyB]6~c,C]cychcTccccxRgS`A\_:^Qc\cn=_/S~1y{y^BQhACi}^c:~^By_]g6aie8{B_nnx18y=yB{hg8=A]hc6~{{STS]MMSS=]xucB~B,xh:~g~cSAQQ{axTn{SL]]\\B.xSBx6aa`/^x_ecg8TS}y{SAB}]-CxSi^BhaB_>SBye\1x]\..gR]ng_ggcaT^^TgT]]ynnR,/An_B`Ry_M}{<6_A]TQ,ReTX˶^.S~]S_Sh}\RcTxT鴶yT_ShR.B\_~h}Xy6BBASn6Ch8]_c=_CSR\S{R,Qc6n_,^8d>n_RQr~]B6T}_\Sc{nuMMheT~2hhygSyaS}_-`_xB:hn}T{eh6~n_xT_:i.Sc_{a{^]gy_S{nh2x^{yScMM}_SxT2_2RT>y_a2A}]:iQAc2/2h{^Sa{SRx6^{TTn6]A,B6~Gvķ鬌R_8=g12BS}ctt tc2/S6gc_1AC_S{hMiyhhcACB3uZh6Thu62/Ry~r:ciirT,.}_A-6]hT5/A{T6]^6~uy{8-,]yRB1h_..x^BRiyyhrrg,.M{B_6Ty_S:hhiuSA6_AexC>h:CR_A6ihaSi8ARS2yaCRCCx_y]A-Ac{26T1`\ch8iL{,.g:_-B}xhncTQR}aLSBccSnnM}\1{RS:y/QT{c9/>CA{nTQ2}R]hgQBT1.hc~S{{C{1gn2SL{nh}62-]e_C{i_S~Mu>T/B{g1^nR}u_.RacCBAScTTa:hygLiggT}}S~n8^2]16}^~_-/C^cTQS{]rSAA_g=LrhBQ{yAgM6^^{_}]eayu6_Ra^6uSVŻmESR^2^\Tng6^yrcC=m3CB/B}gyh6/`_:}cTA^gSyihhc^}TA6ha2_{hr]xgQ/6Myx2T6_B_1BCy=h_A\1Q^hyya1^g^=CcrLc/]cQ]iAx8n:~g86x.^TRR_T>TB-Qhhy6SC/A}TCAy~^]:nhT^Rx~n2Q6SC\CyT1:i/`/_~c{c//}>8gR_hTcinCBQ18}y./6:tv=hch]2^]nSC}1y:RycxT{cyCyhTRR{1.y1]e^2~g_{c~R1^-:S2y^~L6i}_6^6gA_T:n{Sc^RRxic~~8S~iS}ig]_:}^{CTn1B}y{ng\,,]}Q~8/,\h:c^^]c1Qx{SR/B^]/}hRC8g_R6Sa=1ag]6:ya}1ciaxxT^/AcS}<_26g_{a:aAR.,/gi21_xchga{hT1Sn~rc~T]1^}66g_~>Tc8T\/x~_]SCyiJvnB,2nn~RR\S1Qx~6]BRySuLyynnLS1y禊~8~Sh_S8yA]8n__1xytXǴ^{]c~T:~}B.^_S~cn^h{iL6\-xa~inr8n1QB_ya2B^S2C\caB/~g71_1~d0[x\1y_AC~<62]TT2B_SAT_Bx_6.Q_2}>eRACS^\cic]]T=}^ByyRa=ABn^C~h68e_yT^Shi8_Ry~6{_i:2SS/ACQAx\^__c2.Cc]BBcnncnT=a{a~BQna]r>7hnWkl6lqVA9cwTTS~=_`S]B1c:BA}y~h]1_6~_ShhS2aMc*tݤC\MS^hc_xy82]_g~{ynghSRa]_r<~~cR\1SSCxST:^R~giu6CcS/A=hCS_R^B,]2RhgT}8g^]Thy:ic^S8h}yh<8_Qhc1}c^:^aMڬtt4{~}T.{6yCR6h2ghg4yunh2]Scc2xS6g=CB}ch~1a_`QhvWBcy2_a~_2chgk,y{Sg}Tc:nx\:nh2xy2_h6_:}^{R]Ti{a~x\yh]yShL>nuiC\~ya}T{Q_2x6ih81}hh=8{\{=S__x{T2xcxyAcS{Rg2\TTSQ2BQ1x^A_{/-Bc2\}cCAB.S}{6uhAQymdB72{c~xSaahW3wSx]1y{xS\Cn8.^yc:y.,`B/C_xT2`Sh1y_xc{Ly\1>T_/x^_nnihnr}hiia}x~TxyS6hc~rS{neA^n{6SQ}c2T=n6Rc8_~xQS8hc{R.-RB/T62-,.,Ch}iT}{^A\_gA}^Rx]cc^T\CMhQSce=gQ,-B/CSxS1]gR_]A_x~:x/Aha]1`AC1>na~}n~_6~iuhTyRc{Rghn{_c6n_Sn8\{rchcxirhygt~8^cR/]6eTSA.-RBQc~8^-,-Q,A8Tiyccyx]}h\6MS2S_66{e^_uh/_iT6B1T^TS/yi2cy^g{yB\:cC,/BR>:hh~Y6cc22Ay_ATV[L}cc622-,R:e~Q1cR-,.n:/6L_RaT2~{g6{S_^SM_\R{=rMhchg\C}]A12c=~xCx/Cg}~86~{L{x{iSAS]Bx]T]{hyS1CRAS_2S`.~]A}ccMnS.`{}y}_hn6B`x[wyTMBCTR6c]cSg~{e^hxRSM_\\_8iMTSh\C}]\BR^g_BBQ`Ac{}6TyS=n_C_:ig_5SRRxyxSh{Sxx]1}g~e\igxTT}_-_{ShcSL8B`x[wur_Si\.S8c~8gS\{-y>~cL}A,,-g.A~i~_cS8~{hTM{_ea]]}:i>nS]{yB_\BC1Shy\B-Qb]_{__^=]A/Yc_BB2ch~A`S{ir{1^]{2]Tg=yTa}2~{Cxiu^Ax_\Qx_]68yThgx]CBQRAQLuS{yQ.S2CiBQghAx^}T^\/Sn:2ScyA,.\}i=Le^~c2Tnx^{a1/8n2xT_BS~in_y~cR,.\Th6x~c2}^_c~]/8n2x7SA_}hgyQ1]g]Rx]˽Sc6y}6}_e}]yMny12{\Qx21}i6_g{gh^8B,B-2_a}2ySg=yQ-0w=cQ}g=}g{>hy2>gy}BR^Si8^R\aiy_{6TCRT^Tn~2M:hhTi{=aB]}gg8c_RQ.C{{__cSR^\,Su~e1/_xBinR\ac]{cA_S]yS2ncS6~8_B^A\1Q]>8_}y\h=R]8]2}e:~AR~h<{T]cMhge8Sx6ic_cBx_Sh>cxCR6g{6gBT~6gy:cc2C6_CQThcTTi=]Be^{`,Brn]y<1y{A{:{QCc6g8^hAhhy\{cC^TMSr}S:}{=hch2CTS2~L:C6T^a]/c_RSSyu8incT621a:ngccx_hQ/nT8hR~heS8TT_]MT^Bch6}}]Ah_S~`,BL2}=]}u}Cy:{QCa~gnc6C~gegAh}R}j|Vm{Ar}S:c6>hnc2aTnuy:Rc=}ehB{x2^}}}Tcgx/ScCBCcy_:Mhy,B1cnaww:nh2_i6]T}ch~2}\B8h2RuT1ih^A/^6S}S6g}`,hS6T]gB,/^iLc]}T]Scn\/x1AC}_A]c^xC|jXXuRQy]88>RC~_Tyn6ca~<]Q{~1\xcy_ihgc<^,QCy=TO-Mg.:]B]{The6y{nMi^B}a}::yBAc2]BRhy2^STT\Be~c{}x]g6g6S^6~_}xhBBQ.^u:T1ca\BTiT{S{g}{T1^x,/_y]6_g<^_y>a}BB8^h6y~SSy/18x1aSc2]\_~8Sc~B.:^1_{6S^]inhCQ{yS8gTR1{x{BAeT_>]STTAh6Tc]\:iT{T{1R{8Tgi_iBQ.`~_g_~_}aMugR82hL8~:y^_.x:]]gS6S^\_~8Sc~B.eux/R]^ayS^2LxzmUg~c\18{x{Rx~}r{`ag81Rhgag<2Ci6y}y]Ry~{61g//.-~hRk3KM^{T_8>ySyx}cCAx~g1]SA18SRAAygy/Cn{B^~8h_,,18{26}:y2{RBAS{Qxe~cx,{=2SS^ccx2{xAC_a^1y^82uh{SQ_nh^R_yyy]anhyR]g8}:1\RC18~\/x}cCc>e6ih_xB__2>xTniT2~_2^AS]Si_]T8h]12\Ty)d}R]TC^hSRBB_g6unB2u~\{hT-,_y\2Teie^BTa1]{`/1C__CR^\.RTS1^^^QBS88S_cc1QxT^}nSC8~y2_Sc~T1QC8c\R]QxhcC]2]gg]A1^1x=:B12}a]BQ]._RB8h~CB_T_Tcga-~SRR}nrung_R1^_1\hAyn~ua1}h=e^c_S>y..Cgx_}2^2A`\{^C1]^yhhyScc1Qxy^cr6.x6cT21]^yT_1B]h~\R1/xhcC]]xa=~]R]__ST{A\gcC\x.xhc/CgTaLyBCha/T1,^n8]TgB`xS~gg^xRch/x~rrTx{hin~28@wuh_TyS2yi>6cgy{R{gm,nh=}SgecciuucQ^cRyL=c^xS\/2u_/Q]~T/B:hcyxASg_y6}hh_8R^A]CA61AR]6^c{^aex_{~ARhTTA,-Bc8_{Tyh~xR2Mi^B.Q\^g:_A{c_i2eS}cRRShrTh=T}h21}g}}:MrT.1}CTcABci{]cyA-Qyn=~T{nMh{R~wCBB_8}/iwgrnc]xci}~}8Q]~\]2^xhS1^{h{6ThS~8<^_nge^`BRc8_Syy~CR28i_AQR]y<>_2:{^:h16chR\_uc~=}S8h6}:MrT.CyR5TAA6y^cSB-.S>T^^}^}hgiyxxS2iRRBAy^ih_-Q_~6y6^\T6~T{^{n{A1^..g{_}c6cT_CM:eMwn}6y{_^cT]2S}engL9B/{~S^1}>A`Mi},giBQ21gS-cMihcT<{ThMn7. *?Z~Tx2_{}yeiS_i}6x{}\Q}nTAQ^_/CRAy^1hhaic_B}h2cg6S_}{S{.,CS{TT^]^S2/,QhTR/xTcC]7AQx/_uiQ^TSSyL_Tc^Th{/Q_ghnh]RM_-.rR.^M~QS8ehQ1iTx2h~_]~i_QBB^_/CxRcnnS2i<_QAcCC/2ui/^ySSyL{gh{ac^8:]`-Qx~:gcgniB.Mi2`.ThxyeSS}}{Thh}{QB].Q_yA-_y1xR]Cx^R_SBxiccMi~{SeyS{1_i_g82R/c{`C}A{}_y8C.]S6M_/g^Rn[<\/{h_}:ShA/.R_S2CC2>^82}g82]=g_:_``Qx~=agQ`gM2/}g~\^SC]T}{ThTS/Q1.Q_S-^_xx\^xC]R]1.8}gr:y<1x}cT}2{=S~g_xB\8~/2CRSy^Wz#""?w@]Sh]^h_Q1]CQR_2ex`]S]]ccxc}]A1h:ehe~QBinh~n2TT1]T<6C^nx^Tiy^<-/:SxSie}wiTSRBS8yR]6{cwLScigSaeCRA]B^Q/C{ia~^AS^TyB]=c]TT^1\Q`-C_{T]cg1}x6T~=yy=}\]]xBxTy^`2y_2gg]ce_xAxi=8hggBBhaaMMygLe_SaaR^nx2ci]BM6Qc>TCTh_^8~^cLnT26MCShc6na.Sh2CT}y}RT2Qnh]S:c}n>:hSA]y_SaC/B,,agxgMySg8~n8yh]`.B/Be>{{c]i~2iyR~~gy{1.R\]gScaxgyAnrx1*O;1@[GR^iMcS{^^}]1hcCy~^1Ryi6AC6gghMB,1c\.ShT2282xiiThi_xAx\`/TyA]cy1~/`2_Sn~6}RSuS_]16TASnc{}a6nicB^}B2=chMy_c~~S2cgyx28^.1cT]x6{]_C^~}_hg^gey~:gSS^2`/S^Tx8__^6nyy=CC9h8cy1C{T]_{x]^}c1/Ry__~2-BT_y:.Q{R-_nh]AR.y}2}/BAS6T6}yT_\_{#""?w@@?Yh_c}ii{2R/Q2SRQ6>TxAcCS}/Q]B^aMTSLR2h=c6g_1B_]/Bgcc2_cg~_h=]:x:uC..Tu>A]<7A^^{{1T]`Ccch;~{Sin1ChnSB.A6~__^1}S1c}{1xS4^2-Rg\hhg<}_^c:gyxSMMyQh{~{hS2R/Q^TxQ8SB/22QCcQB_B2c2xgQC8hh_R/161.Bhcc2_Tch{xcLhRh:xugC`.TnnR2r~RST}61`\}y{_^12ngBBc:^B/Byy5ySS8cS:{x1S2_S-xRh8cS2ciyR_MSQT~_cThS2R/Q2S]:ry\BySB]6QB_B_gLSC:7/\n961A._:_QBhg9S{c~6S=+:͇lS?L żڊwΛcw4-0bfaϓ >1;ҭɲkahϼW_W؛645ltkж E?Eûmlk ~}ή$6:5]h\KdKUUU}}~}on$#$¦ǥsrJeJt}tțčqo#."Ÿɟψff$># on᱖ޕro>t>=  %+%ۮ~{KI*q)O5YT0,+(#!`.@T?fe31=:3/# L   }|ol2d1=TP\Y94? C_CtqROBlAc_-(p6旀~VS0s-omYU,&+)4~3PoOd`JE =V<{yzwXUEB1ywom+a)  LoKҁWUblobby-1.0rc3/data/gf2x/blobbym2.bmp0000644000175000017500000000255212042452401020530 0ustar danielknobedanielknobeBMj($  --       ',,+  %,%                       # ,,, ,,  blobby-1.0rc3/data/gf2x/font21.bmp0000644000175000017500000000337012042452401020130 0ustar danielknobedanielknobeBM6( GZI6H8!0#     CMDOjT0F5%=+#@*#I,+[6)c5%k3!o.k&h!^ckḑĭnvWy^Oy[KYQa_u`vYnNb5J'9& dje֭Ŵ{}Ǧ̨ɣ{Zp5HԻĜ7B<%& #+%&.'#)&# 766ֳ¾FJL ү``hYX[ʱxoŸŲ̓~A8Gulv䲐W=[ ϢՇ\6$:zk|pd>i "!ԫߑN57ph\+`  @3Aʼn~@#$’ISW\F\T~$')ѓp5Z\ `G_A~#$%͇`-HJWH%7)8v3i!m]4  7-7~l0qblobby-1.0rc3/data/gf2x/blobbym3.bmp0000644000175000017500000000255612042452401020535 0ustar danielknobedanielknobeBMn($  ..       $---  '--      "                '-* $--  '+ blobby-1.0rc3/data/gf2x/font22.bmp0000644000175000017500000000337012042452401020131 0ustar danielknobedanielknobeBM6( =H ,5 9 ]$RL O= !# ? ? #!9y !!gRz> ?-  L;rM*.t  /tW@w6 <& #6&5!TENCC l 8|V/G=|1G( @XW%#V_1X;LX(6 [, Pvve>M[?YD^Z C 07_fTX)0#J,Jr`cCPT  jlKF#j( 9 $&  w|HI*/4#DfMWf@OJpo>8"# +4,lm?>577U=S>kh83  MqMfe=;3s5=\B}a]C@;;3H4xv^[_^|yWU?=6L7XoXnk*&  مpmHF6X7~}@<[[]\;a;]\VR -2,ЉutGjF0;0nl(#~|NJTmU nm>Iblobby-1.0rc3/data/gf2x/blobbym4.bmp0000644000175000017500000000255212042452401020532 0ustar danielknobedanielknobeBMj($  --       ",,,,  +,'     )*                %,," ,,$  blobby-1.0rc3/data/gf2x/blobbym5.bmp0000644000175000017500000000255612042452401020537 0ustar danielknobedanielknobeBMn($  ..       '-----  $---$ '--,# '----+ #!                 --- &--   blobby-1.0rc3/data/gf2x/ball10.bmp0000644000175000017500000000341612042452401020073 0ustar danielknobedanielknobeBM6( ~vuwgfhzwzeceqpqhghfefa_`YWXc``dbba__poofeewvuhgf$$#rrpggeGGF~~}iih  beboroz|zegetutehg {}}eggvww`aaVWXbdgzz|eegxxynno]]^|||{{{yyyxxxrrrmmmjjjhhhgggeeedddcccaaa___[[[YYYWWWTTTOOO333 )%w8FFF?`(22 ٗ` `٘2FF`()2k Ֆ2ڗۄF`wh0hh0חFvG'̂Rhh֗3`E_Rj٘F`G/|-f1-R0ח3^dfh0FO-0A]FGNOr$mcL,fg 'LcP92IxcO^xIzLaaxzLOgbyLPC+xblobby-1.0rc3/data/gf2x/font23.bmp0000644000175000017500000000337012042452401020132 0ustar danielknobedanielknobeBM6(  G/CI  $%g "V DD--##S%%F/2!"_. VV==55~ //>>%&uG KK|ccJJ C->>BB**i '!!+wxCC EETTII,,Vaa5BBZZ^^EE 10:yw0/d&&XKKYYJJCCy::bWU4..BBGG..l__ 21?eb,+h..2277EFF{@@ ] |xp>:.+*$$&& YY MJkQG%$i2!BB99 Q 'g\81EX&&>TT  g`~n@0j"y (ZZ22 ?#v]I,))?ZYi]~gN6 [Z-, >#2spT9e##AYV  q[iV1* P _]A<=F8fusG= 6@=ivq?9-*oܐ]V "{rUMjTqq*][}F?|~D rR`\ blobby-1.0rc3/data/gf2x/ball11.bmp0000644000175000017500000000341612042452401020074 0ustar danielknobedanielknobeBM6( ~YVY}z}/./pnpgeg;:;lklecdywxighheeljjhggbaaecbedcmlkqqohhfggeXXW "cdb z|zgigegeIJIvwvopoWXW^a_wyxkml ceeegg|}}fgg^__WYZ_`amnoffiVVXzz|NNOppq~~~|||{{{yyyxxxvvvsssooommmkkkjjjhhheeeccc```]]]ZZZTTT@@@$$$  (|\-Ec||X9- 4>ދ!!>UVVxw4X{-#U?kS:) W-- fS2;?3X sxb,;GϊW8eRs 4!PQS? 9-+QPȊ]wX~qQRRS) @üQ? xafq0#op¾K.no1IJponKoKN0.n.n.Kblobby-1.0rc3/data/gf2x/font24.bmp0000644000175000017500000000337012042452401020133 0ustar danielknobedanielknobeBM6(  - B@C@ :0 &f}"$c A %-:PSfbsZk:K ` 3 "| 3IVEIz %9:I]`zBL| A( %)4)HJmim)/L!*"Y%~€<>J*1 )yzED&%E16 }}SO303>A ˌ^W62k  SU /65JXN-(E LL23UuqxODvEEWW} (蠏hX)7,,Nrr>?7yrkD4X zzll55j>;Gݜ~qVBBb``88FԄe,!/{{``??) Ǡߊe+/(ϓff99i" әq:*<AAUꞞii;:]&  w>0Akk~߰놆QQk%#.D:HǜΤw+!,ccr׽䥣nm:9E!("*Ю̟Иp IIOדlhwJGP.+/3/6[T_յʝF5F..0ƿϒ³ȴ˵ֺYGXedh̼j_i ../ZYZ}z}~y|TQS!blobby-1.0rc3/data/gf2x/strand2.bmp0000644000175000017500000070206612042452401020404 0ustar danielknobedanielknobeBM66(@  TTUUSUVUVUWWVXWVXVXXYYVYWX[ZZYYZY[[[ZZZ[Z\Z\Z\]Y]\[\\\[[[]^[]\\\\[]]ZZY[[\[Y\ZZ[YYYX[XZYYZXYYYYXYYZXYYZXYZY\XZY\ZXZZYZZ\X\ZZ\[\Z[\[[Y\X\\ZZ[YZ[Y[Z\\[\XYXZ[WUrt{WXXT_dTD~3_d%DJ16.026272737373747-QX=s{MYZVWWWXWWWYXXYWZYXXYXX[ZYYZ[XYZY[XXZYYXY\XZVWYXVVVVVUVTUSTURSSRSSRRSRQRPPRRRQQRSSRRSPRQSSRSQSSSUSTTUTVTVUVUVXVWWXVXWXXVXXUXWWXWWVXXVUUUWUUTSSSUSVUVTSWUTUVVVWWVYXWYVYY[V[YY\Z[[[[[[YZZ\\\[\]\[[[[]Z]]\]Z\][[ZZZ[[\Z\Z\[YZ[[\Z[[Y[\ZZYXWXYYXWXYYW[YZYYZYZ\WZZZWZ[\ZZ\\ZZZZ\[[\\[Y[\\YZZ\[\\[ZZZ\\YVYYZY[X[VtsoVWZT]bWZYXRB}1[a87+0.3373717370406#>D2[aDU]XXWWWYYXYX[YZ[XZX[WZY[[ZYXYYZZZXZ[YYYWYWYUVVYXVUUTTSSSSRSSRRSQQRSRRRRSQSORRQRRQRRSSRSSSSSUSSVTUSVUVUVWVXUWWXVYWXVWYWWWXWXXUVVUVUWUTTTRTTSUSUVSTUSUVVUUWUYUUWXVYXXYZ[XWXXW\ZZZ[[\\[Z[[]\[]]]]]\][Z[][\[\[]\Z[[ZXZYY\[ZXYYZYZYYWZ\[ZZWYYYXYXXXYYZX[[WXZZX[ZZYZYXZYZY[\Y\ZZYY]X\Y^Y[[Z[\Z[X\\[ZZ\[X[YYYUsqwWXXV\_YYWZWUY\RC.10313375817,1-1&EM:lvKX[YXXWXTYVZWXYWWXWWTVVTTSSSUTSSSSQSSRRRRPPSRLRRRPQRSQRSQRRSSRSSSRSRSUTTTVUUXTXXVUXZVXZWXVXZYVWXYZXXXWYXWUXXTUWUUSVSSRSSSSVSUSUTUVUTVTXTWVYVUVXZXZYZ[YXZ[ZZ[Z]Z\\[\\[Y^Z\]\\]][Z]\\[Z[\Z][Z[YZZ\\ZZXZ[XZXY[ZZZYXXYWZXXZYWYYYX[[W[\V[[ZY\[\Y[W\[YZ[Z[Z\\[]\YZ][[[[\Z[ZY\\[ZZ[[Y[Y\Z[XYproWXXW_bYXVUXXVWUYXXYYXKUWYYC}ZUZZDWM@z'QU15--05473758/5156=,RX?wR\Y[ZXWXXVYWWXTVXTUVSUTSTSTURSRRRRQSPQRPMRPNQSNQMQPRRSQRSRUTTUSUSUTSWVUWVVWYTVWUVVYXYWYXUWXZYXXYXYXWVVYYXRWVTUTSRRRSTVSSSSUTSVUSVVWUWVTUVXYXZWXVWW\ZZZZY\\[XY\\\\]Z[]]\[\\\\[\Z[^\Z[[\ZY[Z[X\YZZY[[ZYWYXZ[WZY[YXZXYY\ZXYYZVWWXYY\X[YZ\\[ZW\[\\[Z[[\Y[[[[\[Z\\[[[Y][Y[YZZXZYZZZ[YUrtyWXXQ[cXYZXXVWXXYVYVUYFPVWXF[XYVH\WVNVL9pw(KN550315176807/3,06<3YdCSYZWVWVVUVVWSUTTUTURSSRQSQSRRPROSQRQRMRQQPRQPRQSRSRSSSRRSUTTVTWVXWUXWTVYYWUXXWWXYWYXZXYXXXXWWXXZWWWTUWSTTSUSSRSRSWSRVRTTVUTUVVTXUVYTYWXWZ[YYYXXZ[Z[ZZY[\ZZZ[\\\\]Z]\[\]\\[]\ZZ[Z\\Y\[X\\ZY\Z[ZXXZY[YYZY[YYY\Z[YZ\YZYYYZYZZY\W[[[ZZZ[\\ZZ\YZ[Z\[[\\Z[Z[ZY[Z\\[[]\[ZZX\Z\VZZYXSvtu[WUU]`VZWXUYWVUXWUVVXCVRLKEZ[WLUYWZDYXYXGG6jsEE/1,047371746-3385ckYVTXVVVUVTVTVTSRTTRSSSSPQQRPLNPPSQPSQONMRPSROQRSRSPSSSSUTUYXUUTWUWXXXWWYWVYYXYVXWWZXYYVXXYYWWXUWWTVVTVVSSTRPSTSSRSUSUSSXSTSTVVUWVXWWXWVWVZWXZXZ\Y[YY\\\\\\[Z[Z\\[]\ZZ]\Z\Y\\Y^ZZ[ZZ[Y[\YZ[ZY[[YZXYYZXYZYYZWWZYYYY\YYXZ[WZYYYY]XZ\Z[Y[ZZZ\Z\ZY[Z\YZ\[Z\[\[ZW[YZZ[Z[[Y\YZV[YUqsoWXXR[cYZXWXYZXXWZWYTZDYUXPLLKR?|[YXYCZXVOPWZ^FC4ah4925.4.27:?t{UWWTTUUTSTSSTTSSRRSQOPQRRRQRORRPOOQOSQSSQQRSRRRSSQTTURTTSVTUUUUVWUUWVYY[UYVXVYYXYXZYWVWWYUYWWWZUWSTUUSSSTSSQQRSSTRTTSUSRSVUUSXSUWYSTXTVWUXWYYX[XYZYZZY[[[[\\Z\[\X[[\Z\[Z\[][[ZZ[[Z[ZW[Z[ZZZ[\[YZWY[[YZWZX\YZWXZYYYZ[Y[XYX[X[\Y[X\[[\[[\[[[ZY\][]\ZZY\\YZZ][Z[ZZZ[[WZYZ[ZYXWqrvWXXR_eXXXWVVWWXVXXZVZFWXWISXYWBRMMLPXVVEYXUVGXV3569FJTXTWRSVSTUSSSSSSSRPSRPPPPOQLOPOOPRPORPSSOQPPRPQQRQSSSRUSSUUTTUWTYVXWVXWWVXZYXYWZXVXYXZVWXWWUYXVUWUVSVRQRSRRSRPOQSSSUTUVSVTUVUVUTWUUWXUVXYVWXZYZXW[Z[Z[[ZY\Y]Z\[\^\\]]\[[Z]\[[[]\\]ZZZ[Y[\Y[ZZZZV[Z[[ZYYZ\Z[ZWY\Z[[ZYZY[YZ[ZY\[[XYY[Y\Z]\Z][\[\\[ZZ\]Z[\ZYX\ZZZ[Y[[[[\Z\ZZZYZWrswWXXW\bYXZZYWWXXVWWTWSMZXWEYYYUKWWZD~OMQTEYWVKRV?u,26glVUUVSVSVTTRSSVUSRRSRRNSRQRQQQOQLRONNORNPNNRQRQQRSTRRSSUTSUUUUVVUVVVVXWXYWWWVYXWYVVXXYXVXUXYXYZTWWTWUVUTWVSSQRSQRQPSSSTSUSSTSSSTTTUTVUWVXWXVYVXXXXZ[YYX[\Z[Z[\\\\]Y[]\Z[[^^[\[Z^[Z[[Y\[Z\Z[Z[ZYZ\\[[W\Z[[W[[\Y[ZY][ZYZ[[[ZZZ\Z[ZZZ]ZZ[Z[X][Z[][\[Z\[[[Y[\[\ZZZ[Y[ZYZ[ZZZZZX[UXYVsswWXXV^bZWVVXXXVXVXXUXHUUYYCWVZNPWVWEXWUKIOMSEUX'HM16MUUTUSSTTUTSRRSSQSRPROQRLRPPNQNQQJROPOOPQPQQMQPRQRRSRSSSTUSTTVVUUWWVXWWVWVYYYXXXVYZWVXYWYVZXYVUXXTWTTVVTSSRRQPPRORPSSSSSSRTQRWUUTWVTVWUVVTVVUYXYXXWZYZZZZY[Y[Z\\ZZ\[]\\[[\Z\[[[ZZ\Z\\\Z[Z\\XYZ[[ZW[\Z[WZ\ZZZZ[[\Z[[ZX[[YY[[\Y[\Z[[Z[\\[\[ZZ[[][[[\][Y[Z[WZYYYXZ[[\\\ZZZZXZYYZZYVrtqWXXR^cZVWXWUWWWVVXVXENNRUFZUVGVWWRHXWUCWWVP=v~OG27(KSSTQUTTSSSTRSSQQQRSQPOOQNPPMRPKOMQQPNPOPNNPQSPRSPPSQPSSSTTSUTVVTUVVVWZVXWXVVUVYYWWXWYXYZXZZXUVWWUWTUVTTSSSSSRQROQROMRRRSRSTTSSQTSUUTXSTSUYWUWYVYVVYXYXYZ\[[Y[X\Y\X[[]]\Z\[\\[]\[]Z\[\\\Z[[ZZ\Z[[ZZZY\\ZY[[[[ZYZZ[[[ZZ[[YZZZZXY[YZYZ[[YZZ[]\\[\\\YZ\[ZZ\\Z\]YZ[Z\[YXYZZYZ[Z\[ZZXYUsswWXXX]aWYXXWWZXWUWWXXD\YVMGNKTGXVUMRTUVDVVUFTT5bk-1@xSSRTTSSSRRSORRPPPOQPOQPQQLPNNOPQOOMOOOMRPPSORNPRQQSRSSSSTVUTUSSYWVVWWVVWVVXZWVYZWWVYWZWXSXXTVVXRWTSSSTURRRSQQSONPPOSSUSSUSUSSRSTTTUTUVUTSWWUVWXVZWXXWXYWZZ\Z[Z[[[[[\[[[[Y\[[\\Z\\[\\ZZ[\X\[Z[X]YZYW[[Y\Z[WXZ[YZ[YZ[\[ZZZ[[\[[[YZZ[\[[\\\YY[][Z[[Z[X\\ZYZ\W[YY[Y[[[\W[Z\ZZYVWYYYTrtsWXXR_eYYXVXWXVYVVWXYDWUXFXXWR=w~NPUEWTWMJSSTE~UU5966VRSSSSSRSRPQRRMQRRRNQPQNPNKKPOQQMLQNPQMQOPRMSOQQRSRSSSRSSUSVSUTVUVVYVXZUXWXWVXYYVYXYXXUWWXXYVUWTYRUTTQQRQOQKOLMOQOOPRSOQSSTTUTSSSSTUUUUUVVVTUVWXXWVYV[YVZYZYYY\Z[Z[[\\[[[Y[[\\Y[[Z[Y[Z][]\[[\[X\[ZZZZ[Y\YX[Z[ZZZZZ[\Y[ZZ\ZYZZ[[[Z\[\ZZY]\\[[[[]][\[[][[[ZY[\Y\ZY\W[[[ZWYXXZ[VYZXXrunVVXW\aVXZYWWXWWVWVWWIWWXB}ZUWNPVTR=s|NMSGVSUNHTB|)13_eSPSPSSSSRSQQRRQNQQQRONQOMPPNQPNNNPPNNNKQMNPOMQQRPQRSSSRRSSUWWTWTVWWWWVVXWXV[[ZVXXUXXZWXYVYVUUTTSSTTSSSSRQQQPQQPPLQKPSSSSSSSSSSTSSSTSVPRTSVTSWVTVVVYUYWWWYYYWYZZYY[YY[Z\[]\[Z[[\\Z\[[\\X\[[[\YZ[ZWYZZ[[ZYZ[ZZ\[ZZ[WZZ]Z[X[\ZZ\[\\[[[Y\[\Z[[\\Y\[ZZ\[\\ZYZ\Z[Z[X\YY[ZZ[Y\WZ[[ZZZXYVqs{YWWR]dYXSXYWWXWXXUXQMYUXFUTWBWSVRIVUP;tzLMNFSU*NU23KQSSQPRQOQRQMRNQQOOMNNQNQOLOPLMNNOPNPOOPNPPMQRQPPRSPRRRSUSSUUUUVWWWTWXWWWYWXVWYXYYWXUYXVVVSVUVVRUSSSOQNPQPNOQOOPMPLPNSSSSSRSSSSTSSSRRSTUUSTUUTSWVTXVYTWXWYVWXXY[ZXZYW\ZZY[Z[\Z[\[\\[[\Z\\[]\\][ZZ[[ZY\[ZXY[Y\\[ZZZ][ZY[ZZZ[[[[ZZZZ\]Z\Y[\\\\\\\X]Z\[[Z\[YZZ[XZ\Z\[\Y\\\XYXYZ\XYZ\TqsqYWWV\`ZWXWUWXWVVXWXOKOOSKWVWDXSWESUSQJQRTv}UVYCWVUDUSTHSRTHMNLE~H~M0Za/5DQONRQQORPPQQSRPRKQLOOMMOPQNOPOLKLOOPONOLLOPQQQPOSRRQSRSSSSVUWVUWUXTVXWZUXWWVYUWXVUYUTTTURPSQSQRPPOOONONNOOLMKLONLMOPMOSSSUQTSUUUTUSSTTUSSTTVTTSSRTSSSSUSURUUVUUUVVVXUYYWWWWWXWZWXXZ[\WYX\[[Y[[\]][\[][[\[[Z]\Z\YZ[[\\Z[ZZ\ZY\XZY[ZZ\YZ\\YZYZZ[YY[\YXUZYZZZZY[Y[YX[VVYWZWWUYZWYXYVWSvtsVVXV^bWVXVVVUTUTUVXETOMA|PQWDTRUA|TSU@|TORBRM3:'JNRQRRPMKRQRQPQONQOPQQMQRPONMNOLOPOPKROOMONPKPNOLPPPRSPSQSQTUSUSUUVVWVWXUWYVWWXVWXVVUUSUSTSSSSSSRMPQPPQNONNONMMPOLNOMPOPOUTSSUVTUUTSUTTSRSSTTSSRSRSSSSSTTSTSRUTTSSUUTUTVUXUXXYYVX[XYZ\ZZ[ZYZ[[\\Z[ZY\[]\\\\\Y[Z[\[[\\\ZZZZZZ[Z\\Z[YYXYZZYX\WYYYZ[ZXZWYX\ZYXZY[XXY[YW[YYYVYXYXWVYWYWXVTrtvWXXU]cYVVVVXUWUUUWMNVSPKUPL@xMOTFTSTDTRQC{P>s|)0?x~SRQROPRSPSRRRQORSRPPQPQNKPOMOOPLMPLOOMPPQQNQOPQPOORMQRPSSTUUTTWUVUUTWWVVVXUWVUVVTSWRRUVSSTNRRQPRMQLPQLQNNOLPNMOOPONPLQPUTVVVUTSTSUVTSUUUUTSTRVSRSSRTRSRRRRURSUUVTSSTUVVTWUUVXVUXYXXVWZZVYZYZY[\ZXY[\[ZZ][\Z[]Z\Y\ZZ[Y[[[\YYZXZZYZYYYXYZ[YXXZW\ZWZXYZXWWW\XZWVZVWWYWXVZ[WWWXWXXWUVVVQqssVWZS_dTUVVWTVVUSUUHTUSHQTSGUSKBLMLCMPRHPU&FL47QRPOSSPSORQOOSNQMRPMQQKRQPPOMPPMMOPOPOOMPOLMSQQORPPNSSSSSRTSTSTUWWUWWWXUYVVVTWUVUTTTSSSSSRSRQMQQLONOONNPMMLPNPKOOLKONOPRUUVTUTTVUUVVUYRRRTUTSTSSRVTRSRUSTRSRSTRSRSSSTUTTSSUUVWXWXWWYYV[UYWZYYZYX[ZZZZZWZZZXZ]XZ\XZ[[ZZ[]YVZV\YWZYXXXWZVWVWVZYWYYXZWYXWWXZXVVZXVYYWUUWYUYVZXWWVWVVXUXRsqwWXXV\aTVSVVUSUTSUUC~WRTCTTTBSRRA{UNTA{QMK>u}PL-11\cTNORPRRRSQSSOSQRRPNRKQOLOQOPPMPMQPOPMPOPOQPNPPQQQQQSRQSQUSVSUUVVVUXVWVXXVVVTUXSVUWVTUUUSQQSSQQRRLMQOONOMQOMOOLNKONPPPPRQTXVTTWUVVTUVSRVUVTSTVTRQUSRRUSSTPRRPSOSSSRSSTSSTUVRSTUTTUVVWWVTYXXWZW[XZZ\ZZZZ[[[[[ZY[[[\\YZYXWXY[[ZXYXXWZXZYVXVWWYVWYXVWUYVYXZWUWVTTYVXVWXVVVWWWWWXVXWVUUWWSrtoWXXX[`XUVSUTSTSSTUDPNP@xRRTEURSERSOHQRNHR0Zb/4IRQRSQQQPQRRRSPQRORQQRRPORPOONPPQNONOMOPPLPNQQPMOQPRRSQVTUUTRWVUWVWVWXVWVUXWWUVVUUOUVRUSSSRRRRQPPPPPLONLNNOPMOPOOPRQOSRRRUUVUVXUUVUUWVVTVUVTUVSUWTUSSUSSVORQPUSSSSTUSTRTSUTTVTSTUUUUUUVVVWWYVYYXZXZYZYXZZZ\\[Y[YYZZYXZZXYZZWYXXZVXYUVTVWWVXVUTVTUVUWUVWXWVVXWWVURUSWXWXUXWWVTVTUXVVUTQrtoWXXS^dTSTSSSSTRTSVHSRRGQRIB~LLLISRILNQGNQ ;@'IQSRSQQRSSQQSSSRSSRQQQQPQSRPPOPONOQLPQOPOQMPPRQSPRQQQSQTRUVTVUVSUXWWWYXVWWWZVW[TWVVUUSUSTRSRRSQQPSQPRPOOLNPMPLOOPOPRRSOSRTSVWWVUVVXTTVVYVWTYUVUTUUVTUSSRSSSTTTRUSRSSSRSRRRORSQTTUSTRVSVWVUVWTVWYUWWWU\WXZWYYXZXZYWZYVYYYYZXW[WVVWWVXSTVVTVVUTSSVVTUWWUVUVTUVUVWURUWVUSSUUVSUSUUTTVTTTUVRrrsZXWR\`USSSSSRQOSOQFRRMHORILOMFKMM@yNQTAzTC*-B{SRNRRPSSUSSNSRRQSSQRROPMOOSPQQNPMRPMQQQOMNQQLSSSRPSSTTTSUUTSVVWVUVWXUWWWVXXXVWWTUTTWSTSSSSOQSRQLPONPPOQRPOQRRSRPSRRSRSURTUUTUUVWVVUXQTVUVVUVXUUWSTSSVUUQSTURRSSSSSOSSSRPTRSSTSQSTSSUUVVVVSWVUVWWYWVWYYW[YYZXXYY[W\YXZWZXXVVXVVYXVUTVTUVVRVUVUTTTTTSRSSUUVTURRTTUSRSVUSUSSSSRSSSTRSUSSPqtwZXWP[bSRSQQSQPRRQNFKPJNQPFQNRBSRSByMLHER+QW36RLSSSSSSSSSSRRSRSRRPPSRRRROQOPOQRPQNQSLQQRSRPSQRSSUTUUUUUWUWWWVWVXWVWX[XXXXWUXYTWWVUTUUTSQSRSSSPRPNNRPOPQPQPPPQQRQSSSSUUTVUWWWWVXXWYYXWXYWUUVSVWSVWUVUUUUTVTUTSSRSTUSRRSUQSSRTSSSSRSQSSSSSTUVTVVVUVXWVZYUVXXY[XZXYWZWXWWWXXWXWVSVUUSUSURSUSSSSSUQSSSUSQSSSSSSSTSSSSTSSSRTUTSSSUSSRTSSSOqtsWXXU[_PRPRNQSQQQQILRP>wHNPA|SRREQPHMRREUO+13agUSSSRSRSRVSSSTSRQQSQQRRQPRRSPRORORQQMQPORRSQSSSSSSUVTTVWVWXYXXYXZZXYWYUZWWZYXXWXVVVWWUTTTRRSRPPPRQRRRQPQNSSRSSSSSSSUTTUWUXSWTWVVWYXXWUZYYWWWXWWWXXWWWVVVVVTTUUSURUSVSUSSQSRRSSRSRRRSSRSSSSSTRTUSUUWVWVTXUWZXVYYVXXVUWXXXWUVVVUVWRTRSSPUSURSRRSSSPSSRSRRSSRSSRSTSUSRSSSOSSTRSSRTSRRRSSRNstyVVXX[\PQPRRPONONSGQMSB~PNNA{MNC}IQTC}SSQET;ow.3KQSSSSSSTSRTRUSSSRSRPRQRRSQPSRQPQROSORQSRSSSSSSUTTVVVVVXXXWYWYYWX[YZ[[XYVZXXWXZTVXXTSUTTTTTSRRPRQRRRROQQSSRSRQSRSTUUUVVXXYYUWZWXXWYXWZXXVXYWYYXVXY[VYWZWUTWXWTVVUTSTTTSSSRUPSSPRQRQSQRRPSRRRTTRSUTRSVTSUUUWWTVWWVVVYXYYVXWWUVWSUTTVUSUTRRQSQQPPMORPRSRQSSRRPRQPRRRSPRRSRSSRRQRSPRQOQQQQLssrWXXQ^aPOMNLPNPRNPC~MRSA{PNKMPRDSMK?yKMJKR%CH)LQRSSRSRSSTTSUSSSRQURSRSQPRRQSRRPSQQRSRRRRSSSSSTWTUVUWUWWWXYXZYWX\ZYZYZZWZXYYWYYWWZWWWTVTSSRTURSRSSRQQQRRPSRSRSSSSUXUUYWXXXWXVVVXXWYXVVYZYYX\XYZYYXXWYYYUYXWYUWTVTTUVTSUSURUTTSSQRRRRRSROQQQRQOPOSOSTRSSTTTUTTUTUWVXUWVVVVVXVWVVUTVVSRSSTSSSSRPROSRRQMNPQQNQQOORQORRPRRMROPNRQQRPPQOORKSQLrtvWXXW[^QMNNQMQLOPQ@wMLICPPCQMQDPRMJRQD}MD|).?u~SRRSSVTSSURTSRSSSSRSSRSSPPPSQSSQSRQSSUUSTSSUSVUWXXXWWYYVYZV[XZXZXZYXZZYZ[VYXVX[XVTVVVVVSUQSSRSSQQPQSQSSRQQRSUSUUUVTVWZXYYYXWYVVXYVXXZXXWXZXYYYYYZXXY[VYX[ZYYZZWZYYVXWTVTSSURSSRSRPPRNQRQMPQORRRPQRQRRRRSTTTRUVTUWTSWVVUWXVWUUVSSRVTUTSRQQQQRRQORRRPPPOPRQOQPQRPNPNPKPONOQOQNNQROOOONQNPLsstWXXS[^ONLOLONOQIQ@zPNILKO?wNMKGRODSPRDV1\d48QTRRSSSRUUSSSSSRQSRSRSPRSRSRSTRSSSPTSTTTUVUUVWXWXVXXWWYXZXZZZYZ[\ZYZYXXXXXXYWYXXVYVWTTUTTSSSSRSRORRRRRRSRSSTSSTUVUUVWYUYXYYZXWXVZWYWZWYZZ[\YYYZYY\ZZZWZ\XXXXWYYXXYXXVTWTVVTTUQRTSOQSRPRQOPPMNOPKQNPNPPQRQRTTTUPSTVTVTUVVWWVUTUTTVUTTTSRSUSRQSPPQQSOQQRQRNNOQMOMQOQPPPOPQQNMPNPOQNLPPPOMMNruxWXXS[^QMNQOOPMNPOEPRCPQPHQODHMPB|RSINO253`eSQSSRSQPSSSSRUSRQSSSRQSQSSRSSRSSSSUTTVSUWXVVXYWYYWVWYZ\YX\[YXZZ[[XZZZ[X\ZZY\YYWVXTVSTWSRSTSSPQMQQRQSQQQRQSSUSTWVSVVWYXYZYXZZYXXXXWXVYZ[WZ[WZXZ[[\YZX[ZZ[]Z[[YZ\ZWYVWXXWVWWUTUSSSSSTPPQPOPORQLMNMLOOQPMPQMQQRQOTTRSUUTUSUUVVVUWVUWWUUTUVTRTSSRSSSSQSRROPMRPNPPOQQOMOJOPNNPOONOLONNOPKPOORFtsoVVXS[_QOPNOOMOPQLEJQA{RNMJQSDSQLLNM@zO=s{.2JSROSRQSSSQSRQRSSQRSRRSQSSSSRSSTSUSTUVVUWVWXXYX[X[[[YY[Z[[Y[ZZZZZZZ[[[[XY[WWWYXWVWUUUTTSUSSQSSRSQRQSQRSSSRTSUTTVUXUWXWYYXYZZ[XXVYVYXWZYWZYYX[XZ[ZZZ\X\Y\\Y\[YZ[ZZZYZYZ[XXXUWVTWRRRSQSPOQPPLOPOQNMLMJMMOOPMONRRRRSQPSSTVTUUXVWWUXWXVTUUWSWSTSSSSSRSRRRRRRQSQQPQPPQOPMOOOPPMMOPNNMPPOMQPOPRLqtwWXXS[_ROQNQPRRQQFRNPCPMEOMTERSEQPPJS)JT&IMRPRRNQPSQNNRSRQPSRQQSSSPSSSPSRTUTTVVVTWXUWXWWZXZYXZ[[Y\YZ[\[[Y[[Z\\[YZY[Z[XWXWVVVVTWSSUQSSRPPRRQRQQRRQPSSSUUSUTVVUUXYY[W[YZ[\WXXYXYYYWYZYXZZX[\YY[Y\YZZY[YZZZZZ\[\[YZXXYUVWXWUTTSSRSMRQNQLQMOKMMOMNNPMPMMMOOQQQRRPSTSTSXSWTVYVVXVWYUYXWTUVTSUSTSRSQRSRRPRQNPPNMOMONPOMONLPQNNQPPMOMQPNQPPMrsnVWZU]`SSSROSRRQTEUROKSSGRQGIMPDRSIPJ/0@wRRQRQQORQQRPQRRRQQSSRRSRSSSSSUTTVVUVVWWVXY[XYXWYYY[ZX[X[\[[[WZ[XZZY[XZWXVZYWXWVTTUTUSURSQQQRRPQQPORRRRRRRQRRTVSUUVVYXYWZ[YZ[[ZVYYWWXYXYXXYWWXX[X[YY[\ZZ[\Z[[[Z\Z\X[Y\YYYXXXVVUTTURRSROPPNONLJIONMIIKMMOJNQLNLMQRRSRSSPTVVVYVUWXYXYXYXWWXYXVWTVTSUSSSSSSSOSQQSQRRQQPOPPPNPPOPOOOPOOORRPRNSPqsvWXXU[_SSSSUTTSSUFSQLSSSHTTHTRPJQP?yP6fl/5PQPNNQQQNRNMQPNRQSQRSSRRSSSSTVUWVUUWWWXXYXWXY[Z[[[[\[\[Z[Z\[YZ[[ZYYYXX[XZYXUZWWUVUTTQSQRPQRQQSMPMOQPOPSSQSSSTUSUVVUXVXXZWZ[ZZ[WZXWVXXXUXWXYXXZYWZXYZXYYZ[[ZZZZ\ZY[Z[ZZ[WXYUVSUSSUSTQRPPOKPKNNJOLKNMNKJNJNKNNOPNPOORSRSSVTWVUXYXYWX[YWWWWYVXXWXXWUUUSTTSSSQSSSSSRPSNPOQLLNPMNMQQQQORRQQSSRSOrtvWXXT]bWUTWVVXVXWLXWKQQRIRSGUSKQSOJU5:1]dSQPRSMOOOMPQPOSRPOSSPQRSRSTSUSUTUVVZWYWWY[Z[ZYXXY[Y[ZZ\ZZZZ[ZZZYZ[Z[ZXYXWWVVVVTUTTSSSTRSSPRNORQOQQQQONRPRSRRTVUTUUXXXWY[YXZY\[VVXVVVYYWZVXXXVXUXVXYYXYYXZYWXZY\YZZYZWYXXXVYVURSSSORQOQNQNOKNIKKOKJKLMLMNIOLOOMMOLOPSQSTUUUVVXVYXYYWXYYVZYYWWZVXWWXVWUTVTTTSSRSSOQRPSPRQPQRNQRQLRRQPRRNRSVTQrtvWXXT\bYXWWYYYWZVPXXJXWLVRQIPPCPTHRF+.FQOQONOLNONONNLNNOQQQQSRRSSTVUUUUVVWXZU[YYY[X]Z[\\\Z[\\]\\[[Z\Z[ZZZX[XWZYXXVUXUSSSRRSRQQQOPQRPNQROQOQRQRRQSSTTTTTVUTYXYXW\VYYZ[WVUYWVVTWVVXUXWVYTXYXYYWWYYWZZVYWXXZVWWXXUWTVUUUSPRQQPOOPOLKPMKMMHILKMMIIMMLLMJOOMOSNQPRSTVUUYVWXWX[YXZZZYXWZXYZW[WWWWVVWVVWTTSSRUQSSQRRRPRQPRQSRRSQSSSTUTUUSrswWXXT^^ZZ[ZZZZ[YUOW\IZ[IYXNUUUKTOD~Q-PZ(KPQPLMLONPOPNONQPOSQPMQSSSTUTUTVVWWVXWWXYYZZYX[Y[YZYZ[YY\Z[XZ[[[\[YY[YXYXUWWYVTTUUTTUSSRQNPQSQPLPPPQRPRQQRSSSSSSVUUVZVXYYXYZZ\[ZYTTUUVVVVTSUVWVWVUVTVUXVVVUWVYZXXUWVVWZWYWVUVTTTTSPSQORLMLKOJKMJKLLLJJIMLKLMJIOOONNPOQQTSQSSTVWXYXZZXZYZ\YZ[Y[X[WY[WXVWYTYWVWWVTUSSSSSSSRSQRPRSSSSRSRSSSUVWVXTptsWXXW[`ZYZ[Y\\Z[SVZZMYVHVXIYVIUTNMP15=u|QONPMPLOLONNPPOPPNSQRSSRRSUVWUVVWZWWYYXZY[[[ZYY[Z\[]Z[ZZZ]Y[Y\ZZZZZXYZXYXVTVVWUSSRTSORRRRNRQPQNPQQSQQRSSRSSTTSWUVWXWVZYYWX[Y[YZTVVTUUUSSUVTRVSRSUUTSUTVSVTVTSVUWTXTWUTTUUUSUURSRRQPQOOOOMKNKKLOIILKLNKLLKLLLMMLKNLOLPNQTTUVTVXUZXZXZY\X[ZX[ZWYZVWYXYWWVWXWXUVWTVUSSQSRSSRSSRSORSSTTSVUUWVZWVusnVWYV\b]\][[[Z\ZQZ[TW[XP[\JWTFSSHRE2`dPQPOPONLPLPQPPPPORPQPSRRSTUSUWXXWXXXY[YYYZZYZZ[Z[X[Y[\[Z[]Z[Z[\YXYYXXWVWWYWWUTUTTSSSRTSSSRPRSRRQRRRSRQQRSTSTUVVUVUYXXYWXZXWZXXZWRPTRSRNRQOSSNQQRRQRSSPSSSURSQSSRQSSTTQSRRSSSSSQQRQOROLONKMJNKMLMLLKJMIKJLLKONNNLPPPOQRQRQSVSUXVVYYU[ZZYXZ[XX]ZW\XYXYYXWXWYUUWVVSWWTTUSSTSSSSSSSSSSTTRVVVWVVVSusyVWZT]cZZZ\\ZZ\[M[^L][MWWGWVKSUNQL)-JPPQNQNMPPQQNLQRRKQRSSSRSTSTTUUUTYWXXVWXXXYVYWZ[Z[ZY[XZU[ZZZXZYXX\YYWWYXVWWVVTVUTSUTSUTSQRQRORRQRQQMRRSRSSSSSRSSTVVUVVXXXXXWYYZZ[QRQQRPPQQPPQRMPPNPOPQQRQOSNSORSORSRRQRSRSSRRSNPORROMOOMPMMNNJOLKJKKMMMLLNNLOOMNRLNQRROSSSRRUUVWXV[WZX\ZZZYZZZ\YXXYZVVXUVYVVUVVVUUWTUUUSUSSSSSSSSUSTUUUTTUWXVWrswWXXT]cZZW\XZ[X\KZ\L\_KY]M[YIVTGU4ci&HMTPRRQSRSQPOQQSRNSPRSSSSSSSSTSTVUUYVWWUYXY\XXZY\WZZZZXZYY\ZZ\Z[WYWWXXYWVXXVUVUSUTSSSSSSRSRSSRSRSSSRRRSRSSSSUSUVVUTUVTVUVTWWXVVXVWURPSQQORLOPOMPPRQOOOOONQOONQRQQRQRRRSSRSRRSQOQSSPPQRPPPPKRPLNONIMOJPLNJOLQOOLOPSQSRRSQSSUUUSSYXWYYXZZ[[\\ZXZYYYZZZXYWYYWXUVXZVWVVWVTUXUUUSUUSUSSSSUSRSSUUTTTTRssrWXXV\_XYYXWYZXYQ\VSVUPWUNY[NYVNV5:A|UUSRSRSQSSRSRSRSRSSQRQRSSTSTWTTWWWXXVXXZXWYXYYWZXY[ZZZX[ZZXXWVYYXXVYWVUUWUVTUVUTUTVTTURSSSRSSRSSRSSSSQRQSRRRTSSSRTSSUSURVSURWSUTUSSPQNORRNLPMPRQOQOOLPNPQPPPOPRQQNMRNRRRSRSRSSRRSPPPORPQMQOPPKPNMOOMONOPQLOPPQNRQSRRQSSSVUUUYWW[ZYZYY[Y\\[[ZYZYYYYXWYXWXVWZVWVVVWTVWUVUSURUTSSSSSSSSSSSSSSSSTPqsxVWZX[\WXVZVWYYTQYRVYRWZRTXRSTOTC04XTTVTSRSUTTTSSSSSUSRQSUTSWTTTTTTVUUTYXUXVYVVWVZXYXVWXXYZVWZZWZWXWUYXUWXVVTUVUTUSUSSTVSTUTTUSRSSSRQSSRSRQRRSSSSSSRRSSSSSSTTTVTTSUSRQQRSRQQQQQNPONPOPOMOPOPPNPMPOPNPPSQSORPRMSRPRRRQRRSQORQSMQMQPMMOOONNPLQPQNOPRSQSSTTUTVUWVXYYU[TYYXZY[Z[XZXZX[XYWXXY[WYWXVXXXUWWUWTVVVSUTSUUSTRSRSRRSRRSRRQSQNutnWXXR]cUSSUWVWUNXULSXJZZMW[NZZN[(HP5cmX[XVVWXVUWWWUUXVUTUVUTSUUTUTUSUUUUUSVUUVUVXYVVWWWWXWVWWVXXXWVVZWXWUWUTUUUUTTUSUUUUTUTUUSUVTSTTSQSTSSSSQSRSQRPQNSSQSQRRSQQRMSSSQOTURRQQRROQRQQPSORQNRPRLOQRQNRQQQORRQQPQRPRSSQSSRSSQSRQSRSRQRQROPRPPPRRNPRQQRSSRSRSSTUVVVWVXWWXYYW[W\WYZX[ZZYXZWXXWYXZUXWUXVVUVUUUUUSTTUSUSTSSUSRSRSRSPQRPQQRQNNrtoVVXQ\`TTQTUUVTJWYLYZKYZJWWLYYMV,1NZ[[XYWXXZYZXWZWWXVWVXTUUVSUVUUTTTTTVTUVTTTTUVVVWTVVVTUXYWVVWXUVTUVUUVSVVUWVUTTXVTUUUSSUVVUSSTSUUTTRRQSRPRRQPPRNRRRONQPOQOQROQRRSSSUSTSSSSSQQSSRSRQSQQRSOQNQRQQRSQRQRQRQPRRSSSSSSRSSSRSRSQSSRQQSPPPRRPRRSPOSRRRSSTSRVSWWWTWXYXYY\XZ[[YZYYZYZZ\Z\VY[XXXVVSXVUXVUWSXXTVVVUTVTSSSSRSSRQROSNQOQOQORKsquWXXVY\SRRQUSUVKTUIVUPYXQYVWYPY9kt(KP\XXWYZ\ZZXYXYYVYYZZWXVWWWVUTTSTVTSSSRVSTRUUVVSTTSVUVWWVUSVVWTUVUUXWVSUUVUWUUWTWUUXUWUWWVUTUTVSTSRSSRSRSRSPPPRQQQNLOQRONPNOQPSROOPSSSSTUSRSSSUTSRSSSTSSRRSSRSQOQRRSSLQSSSRSOSSSSRSSSSSSSSSSSQSSSRRRQSQSSSQRQRRRSSRSSVTTVSWWVYYXWYXXYXXZZWYZZXYXXVWWXVYXVTWUVUSTSVTUSSSSSTSSQSSSSSQRRRQQPOPOQPPPLPqtmUXXWZ]RSQRSSSVLVVNWQSXNXXNY]P]$@FD[Z\\[Z[ZZ[Z[Z[[Y[YZWXXVWVUUTVUTUSSSUSTUTTTSUVTVWWUUUUUTTTVWUUVWVUVVUVWUUWXXTVWUUUWUWUVXTWUWVTTVSRSSRSRRQSSNPSNQRNPQOPOQPSROSSSSUTSTUTVTUSTSUTUSSTTTTTSSUUSSSTSSRSSTTRUSTSSRRSSTTTTSSUSSTUSSRSRSSRRQPSNSOSQSQRSSRSSSSTVVXUWVXVXZ[XYX[[YYZXYZYXXZWXXWVVSUTUTTSSSSQQSSSTTSSSSRSRSSSRQPPNQRPRMQOPMOKqt}WXXQ\^RQPPSRSTKULVYNY[M[\P^TQN04]\Z[\]\\[[\Z\[[Z[ZYYZYWWWUWUVUSTUSTVTTUUTVUWUVSTUWUXVVWWXWUTUWTWVVVWVWYVVWWVXYYXYXXYVWWVWWWXUVUSUTUSSRRSSSQRSLPNRPPONQSRSRSSSSTUVWWXUVTUXVTUUTVWUUUVTVUVUWWTWUSSRSSSRSTTSSSSTSSSRSRSSSRUSUPSRRSSSRRQSOPQRNRQSQSPQQSRSSSVTSWUVXWWWVYWXXY[YWXYXXXYWWVUVUSSSSSSSRSSRTSSSTSSSSRRRSRRQRSSORPPPPPOPPQQMrsqVWZVZ]SRSRSRSPTVNZYLZVL[SU]T[1]c4ek^[]]\Z[\[\[[[\[[\XZYZXVXVTUWTWUVUUWVWVXVVVYVXVUWVWWWWYVXUWVYXYWYYWWYZVX\XYXZZZVW[XYWY[XYYWYWUXTWVTUSUSRRNSOQQRROQQRSRRSSQSTTWVUXXXWYXVYWTVVTWXTTWUVUXUXVUVTVTSXUUWUWSSSSUSSTRSSSRSSUSSSSQSSSRSRRRQSRSRPROSQQRPRMRQQQRRQRRRTTUTTTWXXZYVWWV[YWZXXUWVUWTUSTSSSSSRRSRQSSRTRSRQQRSSSQSQQSSRQQRSSRRSRROuswWXXQ]aTRSTUWVOQVLXYNZTX[P[\J\23R\Y[[[]\Z[[\[\\Y[ZYZY[YZYYWYYWVWVWYXXXXUWXWWWXXWYXVYUYXWXZYYYZXZYXXYZZXZ[Y[\ZZY[[Y[ZY[YZYUWXXXXWTUUTWSTUTRSTRQSQPSRSRSSQSVUVVVVXWZXYYYXVUVVVWWUXUWXVUVTTWWVWTWUUXTTUVUSSUQSSSSRURRSSQRRSRSSSRRRSRQSQQQPQSNPQLONOPNOPRROPSQQSSURTUTVWWVVXVZWYWVXWSUTSUTSSSSSRSPPMPRPSSLRRSRQSSRSSRRSSTRSRSSSSSSSSRQqsvWXXU[`USUUVVUOXZPYURXO[YOZXXD(IO\[]\[[[[\Y[Z]YZX[[ZYXWUXXZYYYWXXXYWXXXTZWZVXWXYXZYYXZVYVX[YYY[YZY[Z\[ZZ[YZ[\Z\[[Y\Z\ZYWXZYYX[YZYYXYVWVTVUTTTUTSSSTTSUUUUTWUWVXWYXYXWZTWWWXTWUWWVWXSVUVVVVTUUUTTTSSTTRUSUQSSSQSRRSSSQRRSQRRSRSSQQSQSRSPRPQQQOKPOQPSQOONQPRPSSQRSRRSRUUSUVUUVTVVTTVVUUSSUTQSSRRSPQQMPNPTRQQSSSSSRRSTTSTSTTTTSUTTTSTPqsqVWZWZ`WVUWWZZMVTOWLZ]M\XS[PZ%BJB]ZZ[Y[YZZY[Z[ZYZZVX[YZYZWXXYZZXYWYXVUVYWWWVVXVWXZXXWZYXZ\X[ZZZ\\ZY\ZZ\\\\[[\Z]\[[[Y\[\[]\Z[ZXY[XV[UYWVVUUWUXUUVUYUXUVUXWWVWVXXVXXVUXVWTUUWVVYTSVVUVVVVUUUVTTSTTUSSTSTTPSTSSSTRQSSRQSRSSSSSSSQSUSRQRSSRSQSROORQSQMPMOPMQRPRPMPRSPSTSSQTSSSSTSTTSVTSSUSSSRQSOQRRQSPQQSRSSQRRTRSSRXUSSSUVUTTXVVWWVTWTsswWXXS^d[XYXWYXJZOUWL[XPVOVXOT28Z[ZZWYXY[[[XY[ZYXZYY[XY\YYYXYZXYXWVVYWVUUUWXVUVVZTVYXZYXYYX[\\\\Z\\\]]ZZ[[[\\[\\]][Y\]\]\Y\[YZ[ZYXYYXYXWYYWVWWVUTUWVUUXWWUXVWUUVVVUVTTXXVWVVWWWUUWTVUTUUVSTUUVUTRSSSSSRPQPROQQRQSQSSRSSSSSSSRTSTUTUTSTTSSRQSSRRRPSRRPSQNSRSQRRRSSSOSRSRSNSSRSRQSSPSRSSSSNRSRSOPRQQSPORNPQRSRSUTTTTUVVVUVYYXYXWXXZYVuswWXXV\`XZWYZXYNWLWZNUNVXNXTV3_h2ae[XVYWZXYXZZYZZYXZZZYZX[XYYYVXVXTVWVUWSUWTUTUTUVUTXWWYWZZY[[[X\[Y[[[Z[\[YYZ[Z[\\[Z]Z]YZY[ZZ[[YYYZYZYZYYXYXXXYXWYWYWWUUUVUVTUTVVSUUTUSSUVUTVVWTTVUSUTSUSUTUTVTTRSUTSUSRRPSROQQOPNOPQQPRRRSSSSSSTVUTTUUVUSSSSUTSTSSRRSSRRSSSRRSRSSRQTTTSSQSRRQSSPQSQRQQORSRQPSSQRRORSSSQORRQSSRRUSVTWTUVWVWXXWZXYZ[XYWqtoWXXS]dZZZZYWURVNXTTVMUTQXJY8AMWWVWXWWUXW[[XW[Y[YZ[XYZXYYVXYXUUUSSUSSTRTSSSUUTSSTVXWXXXWZZ[ZY[Y\\ZZYZXYZXYXZYZYWYYZYZXYWYYXWYVYVXXYXVVZWYZWVXVUTWVUUTSSSTSSSSSRSSSSSRWUUUXVTWUUSVVSTVTVSTRTSRTSQTSRSSSSQQSPOQOPOPQRQRRSRSSTUUTSVUUWTVXVVVVVWSVTTUUTTSSRUSSSTTSUTRSUTTUSTUSSSRSSPPSQRSORRQRQRSSRRQQRRTQRSRSRSSUUUTUWVVYXYWWZXXZZ[ZVstvWXXV^d[XZYZWPWYOXORVKUJTSNC&EJVVWUWXVXWYZWXXZZYYXYZZZYVXWYWUWTUUTTSSRSURRSSQQUUWTSVUTXWXXYYYZXXVYZW[XWYXZXXXYWXYWWXYYVXXYUZWW[YWXXYXWZWYWVUYWUVVTTSVTSSRSSSTSRSSRPNOQVSTSVVVUVUUSUTUTTSVRUTTTURTTSSSRSSSRSPROOQQPPSNQRSRSUTUUTVTVVUXXUYWWWXWWXVVVVWVUVVUUSUUTUVTTUVVVXUYUUWUTSSSTRSSRRSTSRQSQROSSSRRQSQRSRSURTUUTWWVXVWXVXWY[YXZ[VrtvWXXU]c[ZYZZZKUXMRJUQRVHTMT+LW@wUTSUSUUUXVTXXWWXYUXXYZXWYXYVWUVSTSRTRSRQQQRPSQRRSSUUTTXWVVZVZYXWZXWXXUXYVVUWVVVVXWXTWTVXXVXXWYXWXXXWXZWWWVXVVTUUTSSTRSSSSRRPRRRQSRSSSRQTUTTVUWUUVVXVUUVUUSWSVSSTUUUVSUUTRSSSTRRSRPPOQQPQQRRSRQTTTUSUUUWTVWWWUWWWXVXXWWXWWUUXSWUWYWWVWVYVWWXYTVWXWTYSUTSUTSRRQSQSQOOQMQQPPPRPTSQSSTVUVWWVWXWZYYWYYYXXrrsVVXV]aY[ZWYYNXOVYKUKSPNUIN6;TTUUUUVVTTYVWXWXWWYZYYVYXXUXTUVTVSUSSSRSRRSRSSVSRTVUSWUWVYXWYVYXVWYXVVUUUVVWVTTVWUSUXXYUWWXXVXVXWVVUUWUVUUTRSSUSTSUSSSSSSSSSRSSQTSSSTTSSOSSTSSUUWUVXTYWUVSWUVVUUTUSTUUUUUWUSSSRSQSRQPMQRQSRQSRQSTTUSVWUUXVXUXWWXXVWYWWXYXUXWXXXVWYWWXYVXXWZXYXYWVXXZWTWVUTTSRSSSRSQSPQQPRNPPQSRSSSSSUTRUWWUVVXX[YXYZVqssWXXS^^[XX[VXOXIZRMVHSOQQO:nu2]dVSTSSSTRVTVWWUVZXXZYZWWXZWXVUVUVVTTSUSUSSSSSUQVUUVUVTUVYWXYWWXXVYWXUXXZTVUTRVUWUVUUTVTTRUTTVTTVWRSUTVRTSTSSTSTUUTUUUSSUUTUTVSUSTTUSSUSSUOQRQRQRUSVUTVSUVWYTUWVTVVUUVUVVSTSSRVTUTSRRQRQMPPQOQPPSSSSTUTVVTSUUWXVWTWXYWYXWYXWXWXYYXXYZYWVZZ[[[[Z[Z][ZZWYX[XWWUUWUTSSRRQRRQPPPPOMPPQNQSSSSTUTUVUXYWVXYZWUrtqWXXX\aZVVWYUNVLTMUQLTIRJU!;AKSUSUSRUWTUTWUXZVWWWVZWZYZWXYYXXVVTWTTUTUTWUUUVUTTWVYWZXWYYXXXYV[WXVXVVSVVVVSQVWTSTTRSUVTURRSSQSSQSRSSSSSRWUUWWWVYXYYWXYYZYYXYXXWVVWVWVUUNNMOOOOQOSPSTSVTTVXYVTXVWWWUWUXVWVUVVWRUUSTSSSSQRRRQRRRRSSUWTUUVVVWWWXXYXWXXWXXWWYXXXZ[ZYYZZ[ZZ\[\[\[Z[Z[[[XYZYXYYXXWWTSSSSRSNPOQPNOMLMOPOPOSRRSRSRSUUUTVUUWTrsqVWZR]cYXVWWTPVKUOTNRSJSII%@IRQSSSSUUUUUTTWTWWWVYXXYZXYYVVUWTVVVUTVSVUWWVXUVXZYWZYYXZZZXYZZYXVYWXXVWVYTVUVUSTQSSSRSNSOQPQPMOQPMQSSQSSTVTUZYYYXYZZ\]Y\Z[X[YZYXWVUUWTWSTLLMMMLMNNMPOQQRRTSRSSUVWUUVWVWXVVVYYWUVWUTUVVTSTRORSRRRTSSSTSUXYUXWXZXWWWYWUYYYWZYZ[[\ZX[ZYZ[[[]Y^Z\Z\Z]]][\Y[[]XXVWYXSTVUSSRQQPMKPNONOMONPPPOPRPSSTTUSTVUXVTuttWXXWZ`WYXVYRTUQUMWJVOSQP/Y^>yUTSSSSSSUUSUVTVVTWUTWTVSURWWTXUTVWVVYYVVXYXZW[Z\[Y[ZZZXZYVXYZZYXYWXXXWWXVVTVTUTTSSQRQQQNPOOOQQPPPQRRSRUUXXZYX[\\[\[]XZ\[][]\[ZZ[X\WXVWTUUPPQOLLNJMOJMMMOKOMRRQRSRSSUUTUTWVTVXUWWXXXYVXXUXTVUSSSRTURVUUUXVWVWXYXVWXXVYYYYUZXUWYXYZ[[[\[\]]^ZX\]][Y\V\\[[X[Y\Z[UVYYVVSSSSSSQQOOPNPKNMMPLQPQQQSSTSVWVXVWYqtwWXXT`eVZYXZSVQSRQUOTLVLS 8@TSTSSSRTVTUUSTTTTQTSVSUSVTVTTUTUVTWVXVVZXZ[WZ[ZZ[[ZZZZ\ZYZYXXXXWYZXVXXXVVXWUXTUUTSSPSQSROPRPOQPPSRUXUXYX[\X\ZZZ[]\Z][[\ZZ]YZZZWYWWUXZXWWTVSSQQRQNNMMKJIJHLKMKMNNOORPQQRRUTUUVWVWYUZXYYYZXXWVZYWWWWUWZVYVYXXZWXWXXXWVUWTVVWUXXWZYZZ[\[[[\Z[Z[]\\YZ\\[\\]\YZX[ZY[YXTXYXTSTRRRRRSNNQPNQRPQPSQSSSTTWUVZXYXStsoVWZW\b]Z\\[OZSWQTTQSNTL>v1_dWSSRSSQSOQSSSUSRSSRRSSSSSTSSSTTRUWVUXZZYZ\Y]][Z[[[Y[[WXZY\YX[Y][YYZ[WXWYZXWVVWVUUVSTSTSSRRROTRUTVXVUYXYXZZZYZ[\\\\]\\\]\ZXZXXYYXVUWVUXWXYYUUUTSPRQLLNKLLLHJJJKJLLKNNPMPPQTRTSVTUUVZXWZWY[YZYZ\Z[ZY[YYXXZ[ZZYZXZZYZWXZYUXWVVUVVVWYXZWXZ\Y][\\[][\Z[\[ZXZXZZWZYXXXWXWWVWTUVTUVSSRSSQQRSRQSSSSTTYTXXZYZ\ZXrtqWXXW\b\\[[^R\Q[NYPXRSTT(GOJTRQQRUPQSSSSRSRSSSSRTTRSSUTUWUXVWYWXXZZ[[[[][\[Z[XZYY[YXYXZZVXYZYXYYY[WYXXZXYYXYVVVVWTVTSUUUVYWZXXXY[\ZYZZ\\][[\\\[YZZYXZVW[WUVZVXYZXYXXWYTUUSSSPQQRLOMNJMJKIJLIMNHONPQPNOPSRSSUVVWXZYWY[XZ[[X[[[Z[\Y\[[\\[Z[[\[[[[ZXZZYXZXYXXZZ[\YZ\[[\[\X[WYYV[WY[VXUYWYXWWWXVWWXVXVXYVWWVYVXUWWUVUVWWVWY\XXY\Z\Z[Z\ZsqwWXXW^bX]]]]R]Q^Q\Q\QWPO&DLVRSSTSTSSSRRTSQRTSTRTSSUTTVWVVWWXYZ[[\Y[Y\[[Z[YZXY[YVWZVVXWYXXYWYXYZYXXWWZZXYYWYWYXYVVYUWXWVWWVXXVUYXXZVZ\ZXYXYZZYVYZYXV[WYWXXXXYXWZZXYY[Z\A}DKMTRSSSSPRPNPPMNOONNLNRRLRRRRSSSPSSUTXWWXZX\Z\YZYY[[YZ\\Z\ZX[[YY[Y[Z[\X\Z[[[\Y[\\^[\\Z][][\\[ZYY[VXZWTWTVVWUQVVSUWUVUVWXXWWWXXWZWVZXXXWXXZXYZZXYZZ[\\[\[\ZYrtoWXXW\b\]^]]TZT[QZOZLXO6do?xSROSMRSPRRQQTQQSTSVSVVVVXVXXYXXZYXZYYYY[Y\\YYXYZYXWXXVUXWXVVXVUWYWVVWZWUXXWYXYZXWVUXXVUXXVUUSSSUUTUTVTTVVUVWUVVVZVWXXWZXV[XXWZVZYZYYYYYY\Y\NFw~3hl,\^&PP&NO3cg>xNSTXXVYYVWTXYWVWUVURWUTWUUVUTUVVWUSWUWTWTUWWSTWWWWUXVWXXVYXWXXVVWXWWVUWTWVYTUWTUUUUVTUUSWVVVUWXYWXXYVXX[Z\YXWrtwWXXW[`[YWVSPLKFEC@zB|A{E$BGLLKIGGIIJHJMLLPNRSVXZZ[Y[[\[\Z[\[[WYXXVUUUTSSSPNPONOOONKMMOOPQPPQRSRRTTTUTTUVTXUVWUXVVUWWVVYYWW[ZZXWYZZ[ZXYYYYXYYYVZXYXXWXWVXTVUTVUTSTVVTUTXV[[ZZ\\[ZZZZYZWWPZMLK9qv6jn1bf'RR FE%LN"JI$NN,X[4hlCEFJKPUPTSNMF;vz:uz2cg%QO"IG#KK/^b9s{>y~KOVWSVTUSTSSUUSRSSRROPRSRRSRSSTSSSSSUTTTTUTUTTSUTOLSSUUVW[ZXWWYUXXUXTTTTTTSTTSSSSSSWTTUXVWUVYWVWWZYVYWXXSrtqWXXR]cXWWURVVRUTSSQNLFGLMLLLLMMMONQNQRSSUURXVZWYWYXVTXUVVVSURTRPRQOPOOOOOQMOPOSQOPRRSSSRRSSSSTVUSUSUUUUTVVUXYXXYZX\ZZX[]][YZ[XYZ[WXUXWTSTQTQQSRQTPUUQSUURTVUTTVUVUWXYWZ\\\[[[YZYYYVWVUTPLDA~:pw4gl1de)TTHA FD@>#HH*WY4fj7kq6jo?yANQ@{LB}@z;tx4hm2ij+XZ$OM>=>>'QR4hk?yEQRTUTUTSRSQRRNNSOQPOQQPNRRRQTRSURTSVRPMOL@~F=t}8mt9swDNPTUXXVUYW[VXWVWXWVTTSTSSQPQRNQQRRQRTTUTWVYYZWYXYWWZXVUsqwWXXWZ`ZUWTURSURRSPQQQPOMPLORNOQNRSSSSQSUSTUTTUTTUTSQUSSUQNPPQSNNOORRQRQSQRSRSSTSRQSSSSQRSSSTRQSSUTQTVTUUVTTWVVWXYYY[Y[YXYYXZWYVUTQSQNOLNLKOLOMQPQPRQOSSSSRSTVUUUTVW1ae0de4hm5gm7nt9qw?yBKKPRWVUWVWYVWXTVNLT?z=u|>y7lq-\_)RT#II DC"HH!FE(SS,X[B/]`E*US2aeG5jn>x~6io5hm!JD$NN ED>9!CD(TU3gkB~OUWUVTUTTSTQHJPSRRRPSRQRROMLRKRH>{O7jqC9pu0^a$KK,[]0bdw;sy9pv7kq+ZZ-^_)UV'TT(UU,`]3ch2gf4hk4gl:rx;tzBOLOTOHRQWGFJO9ou6no/^a)WXF@=7#LK$NH!HF!IB9ns&LJE'QP;uw(RM2ef*[[ HDA@!GF!GF#IH+YY;szB}IZWXXVWSK@|8lrDEPRPMIJHLFD)NP&KM9jtI(OQJ/__6io8nrD3gh@|8mq&SR#FH!EE!IH@?&ON!DE"CA%KM$ML*ZW99@>C=>=><><$OM.`_6lo7nrB~HKSLL@}5ko#JK&PQ8lr:rvzHKRTWZYZXXXYVWUURNRVWUUSTTUVVUUSRTSRRQSSSTUUUTUWZVXX\ZZZ[Z]\\YYYssyVVXW]aYYYWXYVVXVWWVYXXYYWXYYWXY[YYYZ\W[WYZWVVXWVUUSSRSRSSOPQPPSRRRSUUUTTTUUSUTTUUTUVUUTUUTTTTUSTWVTTTVVWVWVUUXZZZ\Y[ZX[YZYXV[XYYXZY\W[YWUVTSSRRQOOQNNQQNRRTTQVTXXXXROMOLLNLNLONOORRSTRRQEy".&-;+8jq4ls2df3di+X[&JM"HG98>;87$HH34&FJ;;">E#EF-Z[&PL&RK$PL:5#FG:7@@5599 GEBA!EE;:;:<;>>CB??CA(RT4il4hl4ik3eh-^`*VX#FD?>!CC!FC%KM&MO%KK+UV'RS&PPDBFDCA"GG%PP.^`7mq:uyJNRRY\ZZZYYXYZYWXWUUVVQGDWWVWXXWUVVTUWTUUTSUVVZZZYYZYYYY[Z\\]\\\]][ZWrsoWXXW\a\[ZYXZVVUVVWXUUWTVWVYWXX[Z[Y[Z[[\Z\[ZYXZWWWVTTSSSOLORPRMQNPQOPPRSRRSSUSSUUSUTVXTWVUVVTVUWVVTTXUVVWWYZY[ZY][ZZYZZ[[[[YX[[[\\\ZYZWWTURSRPQQNPOOQRSSTWUVUYWWYYZY9io;ouAyCxEFGIMOPQQRTTUVRUUC}!+&3!'4 $-#*YVSOLBA~5lr86"ED DC!ED*QT6ikt{5inRSWWTTUVXTWWXVWWZWYXWUYYYW\ZZ\Z[[]ZZ\[\\\Y[uswWXXS^d[XUWVSTUSSQSSSRTSSSUTUXTVWYYYZXZY\YZZ\X[XZZVUWSRSTTSSSQSTTRSSTRSRSSUUTSUVUUVUTVTUVTVSVRUSTSSTUUUVXWYWWWYWXVYUXWVWUVWYXXZUYUXXUTVTUTSQTQTSURTSWTUUYVVVXXWWXXTXTSQMID@|?x=nv:ou7en3^f.X[4ch2bg2`c1^b3_g8ip8iq;pw.OU%,'1+ '3*3= BB@>9:::8:"HH4533:9:9>>569:9:9957 BB>?99>?6767>?783436363625472737343737471747176807379927==:<<;"FF%LM;;&LM)YX,Z\,VY0^b/[_*UW$HE'PQ0X^1bd:nv8lr@y@{?yB|D@EFFGHHHHIIIA~8ns-Z]AAHOVUVUWWXWWYYXYXXZ\YZ[WYYZZWXXVUUTSQQMPJOqruVWYUZ\SSTWYWYWY[[YZZ[ZZXZ[ZYWXYXYYYWYZY[ZY\\\[[\[]\YYZVYWWTSSTTTTWVY[ZZXXVUVUTUUSSSSRPRNPLMNILKJJKLLIPNQQRSSSSSTTUTVRSTTWTVYXXYXVWWYXVXUUVWVTXUVVSSUTTSRQSQSUSSRTUTY[\ZZ[Y\][ZYY[YYURRQN=js&!,$4=/_c*UU-[^+WY+WY&QQ"JI,Z\-__,XX-Y]-[]/a`1ac7io6gm4fi3ej2`f2`g2cf0]a0]a-[]+WZ)WW)VV)TT)VV,Y[.`a0bg0af&OQ>@473769475847374737377969790347585879;: ?>!FF DD!HGBBB>?<#JJ%NN(RS(QS=>==98?@%NN8lr:v=t|x~1_e-]b,X[%MN897925475837999989=;=;9='OQ+YZ*VX)QS(NQ"CG!BE!CB63#GG85"EE+SU)WU$LL#GG&QR DD'NP>;@A6:14!>>/Z_2ah4hj2`c8r{=u}A|A|=v}B}AA$KL$MM'KO<<#FH#EH:ot>t}=x~At{9or1`d/Z^+XY*PU!@C#IF!BC"EF-Z])UV,WX,XZ)RT"EG;>9>>@%KK(RT'PR ??!EE?> @A"HH86::4297)PRDLPSUX[VUXYSP]WXF;syB~JUVVSVUROQMOQPMRPRTSRSOorrVWZR]`OPOPPMRMMQROPPQPPNOORPNPOSSVWWYZZ[Z[\YYW[UVSTVTSSVSVTXVXVYW[UXXUWUUVVTSPSRPPQONPNLNKOONRORQRSRSSSTTTVVVUUXVUWUWXVYYXYXXWXZZXXYV[WWSSRSQPPMOMNKKKJMLMJOMRRTUSUXXUXVWVTVUTTUVVVWVWWW4V^& )"/(9bqVUVTURWUVUUSTQOKEFAECA|:qwq|@{HJNMRSTURsttWXXS^dYUUUTRSPORQONNKIHKJIMJKKNMLOONNPLRRSSSUVZYXYZWVVWTSSSSSRPRSRRPPRRRSSVTTUVWYXXXVWXWYZ[[ZYWZUWXUTSRSSSPSRTSSSVXVYYXXYYX\Z\ZXY[XWVYTVTSRPRQQNPPQRRRSTSUSSRSRPQPPRRSTSRVWUWXY[ZZ[Z\Z[\+DP%"G~XVUUTTWSUWVWXWWXVYUWUTSSSRRPSOQSSOQSNPNJA{:qx7or/bb$MK/`c;tzGGLPOPVWWVXVYVUTRLJCBCEGMRVYXY\ZZZZYZYWTOOHJCGEB9nq4hk7ls4jl2`d/[]3bi/_b1bf7lq9ns;qyCEHLMNRUXYVXXWWUYX[YZ\XqtoYWUQ\c\ZYYZUYVWUSVTSRRRQQRORRRSTRTTTRTUTUVXZ\X[\]^[\YZ[WXWVVSRSQRRPSRRRUUTUTWVVWWYW\Y\\\[[\[[\ZZYZYWWXTSSPSSNRSSVYVZZZX[\Z[ZZ[\\\Y[Z[YYYZXWVVYXZY[[YXWVVTSQPOIMNMKPTTTTSTTUUVVUUUTVVWXW[*>E  !,,HZYXWUSSRRQRSRSTSSUTUTVTWSSSSTQQRSOOQRPTUTXVSSVZZX[WXYXVWXVSUSTPTSQRUVSQSSSRRRQSQTRTVTXXZXYXZY]\[]\ZZZVQVUUUPRLKGDBA|CCC~EDJNRVYXYXWWrswWXXS^dYWXUVWWUVUVVWXVXWXVVWVXVWUTWWVXX[XX[[\ZZ]]YZW\XYWXVTRSSRSPOPRPQPQQRPVTUVUWVUYWYZZZZ][\\[\[\YYUVTSRRQNQPQQRTSTUUWYWY\\[[Y[[Z\Z[YZZZZ[ZYYWZXWTUTTRSNPQPONNNLOOQUSUVVUVWWWX[YX[Y[YYWX-JQ'#MSRRQPSQQOPQQRTSUVUVVWTUTTUTSRSSPQSSRSSSQSRQSQOPLNONOSRSWWWXWTUSVVUWUSTUSRQOQTQUUSWTSUTSSQRTSSTVXWZYWZXVVUVSTSUW[ZXWVUQSQPKNKNNNONGvtsVVXUX[LLGJKJJINLORRSSSSTUUVUXWWXXXZ[ZZ\Z[[ZZ[\[X[\[[[Y\YWVWWWUSTTRNSPMPPPPMLMLNOQQTTWXZW[ZYZXVUVUVTTTSSMRNPOQOQOONMQPQSRQURSVTTVTVSXTXUVTVTUTUTUTRRRSSSSRPQRTSSUTWYXWUVURSSSUSSVUWWVWWWY"3<-4@$(KPQQPPOQTSSTUTTRTWVURTTUSTSUQRQSSROSRQQMNOMLMMNMOLPPQRQOVTTXWVXYWXWUUTTTUWW[XWXWYVWSXXWWVUWWUSVSSQOTSUUVWYWVSQOONLMOSVUVUSUSOLMMIOOrtvWXXR\cSQPRPQSSSVSVXXVYVYYWVVXTUVTTTSTQTPRMNQLQPOQPUWZZ\[]\[[\[[YWWXTWWUUVTSSPQRRRVUTWUUVTSSVTXXXVUSTSSRNONLOIHHKIJJMMINLONKJOPKQPRSSSSRSOTTRSSTTUUVUUUUUUSSSTTTTSUUSTTUXWXWWYYWYWWVXVWVU*2$*7!*%0&RUTRTUSUTSSSTVUTUSVVTUUTWWTWVXXUWWUTVTUUUUUUUVUVVTUWUVTVTXXVYXY\Y[XZWXXWVWYWZYYYWXWVRVXXZWYXXYWWVUTPPNMLOOPPTUWWWZ[ZYUWWXZVWYXVWVUOptsWXXV[^TSSVVYWYXWZWVYXX\WWWXURRSRSSSSRQQRSSQRSTTVVVXW[VXXXXXWVUUVTTUYSUSTSPQPOOQPUTTVUVWTYZZXZXZZ[Z\YYYZXXUUOSUPSQLJHNNRRMSSQRRQSRSTSUSTVVVWUXXWWVUTSRSRPRQQSPTRTUUXVUVWTTTSUUSSUTRTPPQPQ.;D!-$0'3#*WRSWWWXWWWXU[Y[\Y\WYWWUUURQQRSRQSSRRSRSSQQSQSSSTTTVUXXYYYYZZZ[[YYWWXWYWYXVWWTVUVUSSUSSSRQRQRTTXUTUXVWXWWZZ[WXYUUUUTUUTUVWWWXYZYYYXSqssVWZVY_WWVVVTSTUTTUWWWZZY[[YZZYYWXUZVYWWTXUTUVVTWVWTTUQTRRPLQMOOIOLKONLKIIHLKNOQSVXYX\^^[[ZY[X[]Z[]Z[[[[ZXVTPIA:ns1bf3ei:rwFPUSTWSUUVUSTUWVWWXY[YZZXZWZUUUVUUUWWXWWXWWWUYVVYXXVUUUTVVUSWVVWYYZZ[R&.:&2 **']WTURSUTXXVWUXYXXVXUUTQROPMOOPPSUQUSRTSUUVWY[ZYYX[ZYXXXZYWVSURTSVUTWVVYYXYV[ZY[YZYXXTTSSUSSRSRQQSTRVSSTUTUUWUUYVTURRRPPNPQTSUUSQPOJussVXXR]`UUWZZ[][Y\YZXWZXY\ZY[[XYXXY[ZYZ[Z[ZY\ZZXZWWWXWVVVTUSTRROOKJLIIIPNRTUTVWUTVUSSF9ov7lq=x|FMRUWUVSUVSSQPIH=t|6jp1ae,XZ*SU-W[8inBNW\\YYY[YZXVXWUXXXWYXZZ[W[XXXXYXYW[XTOIGMOSQQQOQPRTTWVXVUWTTRSMMNOPPQPSQU(3")4#.!-08`ZYYYZXXYZZXWYVVXUVWQRUSRSSURRTPTRTTTWYXZZVVTSSQPPOTSUUUVUSTRPQSNRUTWWRSSQSTRVWVWTTRPNPPLPSRPRSRRQUSVWYXWURRSSQUUXVWYYVSSQRSTUVXWXTqswVXXW[_VVVWYXXY\Z[XWTVUTSXXWZWUWTVWXXYWXWYXYZWVURUPSOPMMNNKMLIICA}A~?{y~@}>wDFNSUSVYXVYXYXYXXTXVVXYZTWXSNE2be1bf4gj>zHUSSSVUVWXYXZXg[RRWSUWWVWUSRNQPOWUO$'-:'3#/$7@^YYYXYYXYUWYYYXVXV[ZYWVVVTUSVTSUWXVWVVWXVXWVTVTRPQPQOSUVXWXUPTTTYUXWUYZ[WVVWYYYZZXZWWWSRUVWWWVVSUUTTWZY\[Z^ZVUTQOMLMLLQOVVVWSSUTVVVrtvWXXU[`TPSRUVVWXYZ[[ZZZXUUSUUVVXVSVPQRRRRPRNOQORPRQPNPMONKKKLOKPOQOMKEA>w~:qv*ZV,]\.a^*TV%NN#GF%NO'RS!FF'NP$?D.25:&JN;=!DF=@48476958383759554879659: CC<<EC#GH$JK&ML#EA'OP(SS(PO'OO"FD%JM(TU/[]3ei:v{DFGJFMLKNJOMRQRSOOH8lr4bi0]a,\^+ZZ-[];sxCO[WYVSTXVVVTOB5knDQT;?BFFOOV[biuux~aVNNONSSSUSTK"'1 *$"7A\TWUTPRRTOOONQNPQRROTOPNNNGJNKJJKLKMMKOMNKNNOOOLKNLKMMIFGJHLLLKMNLPPQMOOORLLFECCDDEEKNPNLMPSXWWUWTUSRSUVTXUUYYWVRRRQPRRTUTVVVSUTQTOrtvWXXW[`ZVXTUUUWXWUUTWXWVVUWVWUYWSVSSSRPOMORQSSTWVTTRSPSPOOMJHHDCBDFHEA~A}C9qw7ns;qx6lp1`c+\]+VY%NO%MN"GG<< &2!$*1303246; ??892717475879 @#AE;=?A9;?=CB'PO(PP)TV)RS,XZ0]a/]a,Y[2de&PP%LL$KL"FFA>DA$MM*SV.\^8ns?xDA~B;ry0`e+X[%GL"EF!?D ?A:>::&JN3fjEJPQONKKHDB}Ay<(RR(NL)TU*WYG=gS[JVOKKAE>D>D:>:@>ECFPU^hoy|vops[(0<",";V]^Zhtt~v}v|qypzovnvkuirirlplpmlllmtglbpbpfobncmsrxqӁsЊtʂoЙӚՇvtl``X]U^Y[W^dh`eY[_gi^wfwcn[kY_WSRNMb_xrqkidiXfXh]lZjXo`lbi\e[jZiYtirjvgqedX_JfTlRkQaL`On\o]o^sfujvowswsto~{f_VOTTTTWVTUTSRSSSRQRSRUVTXUWWUW[WYrtqWXXR]cVRPRTTSXUXXWVTSSUWX[XWYZXYWUUSTRTSURRPQOOQMQPQNPPRPTSRQSKLJCD@{?{?z=v~;tx7mr:op5im:pw1_a1af2fj*TU0]`*MW#1 7aiDJID(D=B?DAE?EDGDFEGJBQG^HlRiptTcNjReQpTdPoVtXwXwVqQkSaPZOUOYOQMHH]MdYlUuZvZkxbx\lvejIuSnRpKjHdJfIzRahYo]q]o`p[_xzqbo_lVm^xnwdu[m^m^q_s^m^n[o`q`p`r]s]_N]Jp[lWdNwegpyoԀwwm}}mQNPVQNKMOQSUXYVXYZXXXUSSUUSsqwWXXUY^TQSSSSSSRQVUW[YZYXXWWUWUWWYYXWXZYYX[\XY[[YZWTWSTTRTOROORSRRORQRTRSSRQQRRPTU4\i# MTSPQ6P\&3>!=oyHGE9pv2dg&JM2eiFLNJIIG>{:ot4ch.[^.Y[*SS(NR"FG!D@?<$HJ"HGL8qWrXgQ[UVNSNHI?EGIZQWLZG\HcNnTlQhLZGJI&"'%7:CDAECBD?Q?SDTEJICG@EFHJJTN^QdIkPuRiN`N`NSKMDRHWNSJ[T[Sia\RRHLABE;A:=EGAEQQDFKKYNh\kdrigVTKJAKDIHLBVFaPfNkLlTpSlViXkRlXocqaq`sbt_tep_iawf~dnvhg\i\fWgWdVq^w\q\lVlYlYm\s\v^s[}doVdKlQnVsYlNoTuX~cpUeL]NlXr]r_l[kWjWo_~ydUPSTWWXZZX[YXUSSOPRRRttpWXXU[`VVYWVXVUYWXWVUUUVXWZXYYWXUXUYV[VWXWVVVVUVVUSXUVVVUVWVTVUTTTVSRSPQOOQNQOQSRX)CJ*$.PUTWW5R_?n|MJDHE>~=u|;tzu}:lu@zB|HGIQQRTUVUTQQOPLGG?~@< @@!AE%KO*TT.\_4em9iqEDCEMLWLdOmYnSfNZJXP[STLQLML_Tf[dVXF`QZPWPIICEIFVSUPSKcN]LSFOJWPEEGHK>XFZKaMkUr]p^r^n]l[kZcOfImYhPcJgRaM]B\HgQjWjYjYl\iXeMqUpViYn]l\n^o]n^p`q]eWhVnXxaudw^gScHdReTcOfSgRjTdOcMcPdSmWtXprSmU`KcKm\o\o[oRoTaGm^l[oeqh|p|ePvtsWXXU[`USSVSTRTUWTURSOTTUSWVTUUVUQQQOTSTRUYXRRRRRTSUSTUUORSTQQUSVWURSWTTSSSQSUTXYS%&:gu[VXVW?s}&#'04XdTKONOPQRSRUTVXVVXVVVURSRNPNORQRNSRQQRPPOAy&2T`UTUTUSRTTTTVWVWXPKPLQg~yv$*9;@E?E?EBGNJLCMKEHJJNITMPIPBIHBEAEO9=& ,!-#JGCGGGOJVLUNPLHIPKZRbJ`RWINHF@BCFFHIIJHGFGAE?D>D=DJIMHYP_Y`TTMJJTOPINKRNQPWPLIMKQMKJFHMNPRLGVL]JaDeLaUSNJIIJJJRI`QcWfWgWdOkXn\p`o]j]iVpYoWkUqMlVn\j]fNzltWgUcMfLgTnYnVnWpSzX]zYjKcLkhn_o`eVfUcOiOm]l\kYjYkReMvXsQtRkUp^eyqmSiRjWeSfOmWkXpZqVv^t]rXoYn\l^gVnXm]dNhIͣrtqWXX^XZzzunc[TLLMMKOOOOPQPSTTSTSRPRSTVTSQSUVUVVVVVVTTRPPLOQPRRQOTSSTTVWSTSQPRQTBw#H}USSSQJ%/$.-MYXXWYVVRRQRORQPSPRRQSRUSWXXYZXXYWYYZ[YZY\K)!,,MWWUVTUVUTQKNT`ovok_]OYEU>P4V>X@T6`K'/;$o,.GIOMh[nkc\NLIJDGEFEEEFEELJHGEGNKP29(3!+(<*-SMLKMKFGGEHFJFHGJC?E=D=C=CBFHHJEJH?EGIKLEGLGQFTK^WbV[Nh_h^g]WOYPSI^RMK^^e_gi``TOMLMJFHLKRMGIKEPLUH[J]KXMVLTI^R\OaSjTnUj\gQgTcKgUfOdSeSbKiSnRkItQkKm[vkm^eVeO\EdL`CgUiXgWgWeTiRkPnZx^wdw[t_mWp[kToWhPb{dtNmPkTaGfV^Er]ver`iRjQnXnUl\o_p_n_q_r^vfiTqZqQrVkSjPmXhXm`eMrXʥst{WXXgVXxfucjZkXp\rbzc{hmafSsbׂ҈ŋzo}vuujhgdceb[`TTSRQPFHIJLKMLMPONSUVVVSUTTU6_h )TTSUUUM&(&AKXUUTVVXUQPPJILJILKIJKJMMLNPQSVWXURSQTTSTL"+&)@JUNJMN]w~tndeTcLiIjNoNiMnKpRoYcHiS_K^EXBXCC>DFNFJBOFNFMMSOTPbMfQlRoT/',".*#/>',]LaQ]QSO`W\EbMp[yayd{ksbjU]LVKIDQF\R^RWMTHRHVHQE]Ok`^K\N]ScWaW_V`]aVgTyeu`hivakb^RSPQNPMIILG[PWNKGTOOKQJUI\Xg\s[SLTNdSqTkW_G_IpYtXmOhJiUiWkVu_myW|^_mPgJbKaMfQcFhLlVlUt]pWiXqXhRqYq_udvcyg}byjp\s^jYiXn]p[rYmVmWoUpZm\l[kZr`rdp`sbq`oX{`u`lWq_vZv_pZu_qcwdwdr\kSwXnPɢqssVWZfYS|rpzjvbjx`t`ubzdrYmUjTn^vguarZ}[h{[fcverZxfseu\r_nck[jWfRdUcOiYbQdR]KW?W:]C`BfKcCaD[BT9YA^CdFdG`GX>]CeUdUodoemcmah`qh|s{~uwxy5HM!- #,wuzp}w*5A$,8'-4|vw~ts}u|zywxtyusllfrkuqwqtmtixnwrogtkrkvoxtzu|w|v~~~}&/";:D~sdpdcOZB\OaKeFdH_D^D]I`LeQjXlUgKcFiPXBaN\D\B[HU;Oh l3;IHJJIIMMPMKLCF>D=DHICGUNh\hX]N1(*&!-;")JGOQSNSINKGHRNUQWQ`NaPVGZMUORISLYQUP_QhUaRlS_QX?UILDIISN^U_Q_N]Q[RfWtjqmeXVOMKGILKHHTG]T`Rp_WyX_NRHICXQfYc\^STG`NaPXDcMaKdSaLfOiUnU{\x[oZ{jjZaCpOvWkPjOkRuXvYrSfPq\tdgQcNjXsZhTnXndoXoXpZjXjWrapVv]{gzaraiVoUsRw`pSeNxaw\t\wcrZdLfIxY}VtUgOjTv[x_yeoubybt]pgxhrblYp[tUˣuvrWXXeWWmYtaxZmUw`{f{p}jhsx_tavenYqa`MgV}sznqcshygvhvdm]telVjTcOePlJtX~WdObHaJ]@`KcHhTsRpMlPeRhRZHeKYB`G^BbHpRlNtSnQfDjKkFdDY=\C`DY=R7S\E]B]GbG`J`@hLlMjKWcFeD]FZ@U>S]BeRfGjGlL^@& *r5+`PY@V=Y?^CaH`KI@C"-5%aE[BcKgTkOcM`H\JcMdRaI_KkUybjWhVeTgSgSeQbObMeMgKeOgOlRgOdOdMfKgMhLcNeTfUfU_IbJbLdT-(-#=#$sSmSrQbLfQhQkQv]oUjKlLmPhLjLeNcL`GcPkSiUjQcMePhTaKdFPZUl !Z/1GEFG@EAE@EFHLJQJTMPOQRIIUOSP'\+.JI@FAFGFNLJILDNDLJNJGHBFADJIUJ_QRFSK?EGHOMJJULXKZGbThTiTbX_Ypmofpiocrdvku_lVgWbHzTmRl_\UQOFIIJFFGFHDTOHIMJXNPGVNqPipXdR]GZ?Z?bEX:\GYBvpxvfZnflhpji]eYkbg[]NZBaEbFdIbIgPiTpTiSeMiWgRfTs\lRoRw\}crZt[oZjVn\q\mVsubwlzp}lwkubs_xjzdvbr^s\v`w`p_p`u`wjzdsb|po]q`whw]uXs\r[ˣqsoWXXeYT{xdug{jw^n\r_q~yvdw`qfu]xd{grcwdwbnVzbr[p_r[eUcNlViT_E_AaJ^G_F_I[CZE]@_?aG^IcGjReOuUhOiGX?R8Y@dCpQbDU9S;ZEZ?]GW:ZBW<]IY>U>^A]<^?X9]JbIaCoOiPfFY>P9$"H;X>U7S:ZC^IYB\D^?B&3dPgNgOhGrTlPgSdRgSgVhUkOxZ|[oQjVfUjZePeR_GbJgVcL`H`GjNkRdHbLgUcIhTiLuUzYm\rWhKiObN6++ +!nYfSkXiYcOeP_F^D]GbIcKkSmSu\gTeReUhPdKcMbK^DZC`CY:Z@KFt#0W;[>W@WU<]J]EbJdDX9K7O;U3YE\?]C^D\8F9'T>T3Z:[?V:XC^AdJA1$+$L>^C`KeQdKlXnYpUs\uYmSmSbKhVt[o]oXs^gVo]k[fRpS{_rckVoYiWlYoTr[q]fUmXjWgLmTkNhQcNhRH,. ,*#+lVjXfUdTcLiPbJcQaHlSmQhTmTyZjZlVr_wfqSgViWhMlTuPeF[C8>8>pN`", &2$".;@AF=@CCDCKINKMKIILLCI@EAC#+ ,(37A?EGHKJ?E>E>ELIWMLIEE@EIFHISOIHGHHGGGLKCGEGYLMGOCEIHIFIRN_[kiii|ljj_`QmigcpcfYMKNL]T]SdP^QfUcQTPTPVO^R[OYOCFPGgSmThMfH]H^MXG^D_GbN_CfPsl{rogXgZh\mhmjj_fU`OaIaNkViUcLiSmPlVn[hRsVpYgSq_o\o\wXv]sXwYs^whzlm\nXy\v[}ozbyXv^~fqWiRnXrbxnveu_tdiXq[oYxhucq^yer]tfl]xZlUnTqdˢrtoWXXbWTm[u`xjfw\vW{bu^xcy[kXhOpYp\v]m[rUcMcIgNcCcHZ>fMY@[A_GZ?]G\?dGfG_?V9[A]HkNcAZ9[=dK]>X:Z9X:]@kT`@\<`HaEdCXV@X=V?W;WBKF"#HD@EEGDC($0%%HHEGHIKLEFCFGJKJKKMJEECF?EFHQKF?JHSHULLFEB=DAEIDOIQKYITIUMZUXUgennsvspggjke[\QdVYOUOSKMLOMRURQPPQM\R^Q`RMKKJSJQG?CVHZHYMWF`JYJ[GpQgNbKpTrvwppi^jam`kagaebf[ZMcPm[iTfUdOoZbRdJlVo[hOdPjSpXtZpYl[s_kXubudwhze|mr_tarbs[r_rXtZuiktb{^xc{jq`qcvct`t`p]q]v^oVt]rcsbt^vZgOoRlRˢvtxWXXcXXtdr`xhxhuZvZw[jYlRmY{XjYgThOiQiXeSiSlSkS^BaFX>XDW8bH_MaEdGbL`FY>YAiNfOaLeL\?gR`FhA`HZA^E]@kT[DQ5W8`HYBUbNX?_C]@[CSS8YAS:T6N8O3[=[=Y@aAJ,-%h1*\AXcIiOiVo\rYqYhXjWiTaMiMeP]B[EYE*)-()-((,35E)4@"&1$0(#$)')/&(.()-'',,*0+).*).,*0+).$'.$, +"- +(A/7M0:h1828=?=?9<:?9@;B>C?C?CDEBECDGIJJIGLLXPPJCGBFDFFEFGIIVPZPhdwwmprxv}psmolkc]dX]SWPE;NBULMFONKK[OUMVQSOgT^RWJ]IZNTF[IYD]H_JbOjUcLcQhSmasrmbodpmolohrjgWdUdVdUdOfUgReRbNcLmVfScMkVpYqXrTmSnRq]oWl\o[rcuhrgiYhXq]sbrdqbqZt]o[zZy`y]wgsd{myevawXu_waq_p\n\kSq\gOpWvmvdpcn^tbˣqtqWXXdXPu_{huhwbs\zbyhr`wlv`qgvbo[o]tWiTgSfLmUaImPjRcL[>`FaG_DZ>_CW?\A[HaI]CT9]?aB[AY:Z;W=[E!+2!-#+)),((-('-'(-)(.')/()-('-()-(). *!)!'3!'3().)'-((-$(+$'-$'-#)-#(.#'.!).%*/((2((.+)/*(--+.2*.4,7;*3?+0C,2L17[7:^38p5YF^AhL^DbF^D]?V9\AcMSV;T7]E^A`@sP`EXVAWZ<&LC]=O;V9bK`KmM_BY@_G8+( *A((jMcLeGcKiQgObKk[eSdLhNiNlPhTkSkSiWbLfKlThXcMfShVhX`I[BeNeIkPbH`H]EbG]EdLfPmUjN_Cl8/ )#gKgQeMdHgPgLhTnZsUqZhObQfM_I_H]HaG_E[>[>^N\B\?]=^E`J*).('-')/((-*.92AQ &! )('.()/((-().''.''.)(,((/)'-"&/&1"&3**).&'-))/')/()-()-').').')/('-')/)'.'(-').').').)'+)(,*'+*)/((-((.'(/'(-')/&(0((/%)05:A__txyorkjzdgxbduMO[CFQ#%+.)/#&1@ES97D)'.0)34)3P;FYITO>BS@F[15U06e56~73u54u86v7;@3ZBeLcIǕ›y}{nobMeQhTcQiJkUeOiReMnWgTlSiXfRkUgQnWlZm\lToZq[ulpXn\ver[r\s]nWq_oWv^vYuYmZqYo]p_s^l[t_o]uVrcrbp\tYlZxXkZk[rTˤutyWXXeWSw]p\s[kZkXj]mViRqYmWhXhWhWgTkUmVgKoQfQ_GkO_OfP[C\E\B`F[?[<]?^AYC[@W>[AX]1]ZA]EY?Y=\EY=Y;[@ZBY@[?Z?\<`E_@hN\fAfMX>]?_E[BV9Y=bG().''-().*(,((-.7A'0 $1&%()/().)(.))-').')0''-(',&)/2:F%1&2". $1)(.'(-()/''/)'.)'.'(0)(-('.')/('.()/)(,').''.().)(.')0'',().()-()-''.''.()0'(.)(/))-46Ab`mWXhxugh~[[kNOYMM]35@)'+30#,(.BET44>,-8(*2)'-JIXKL[II]@AL('/*)-"&/`MTH0(-IAdViSm\jTmdyz{~tuefxabuSYiWJfQbMeNeJaKhRoWfLaJdFgPcJjRmSdSkShQcGnUiRhSs]lXkVmZmZu[r]mWoWl\sZm\r\m]jUmYkWeTkZfUoUlWkZr_n\q[nZpYs^kWtYr[ˣssqVWZfWWq^r^m\r[r^kYoWhRhUjUiTjMfLgRgLhRdQiTeQfScJaJdLaJhK`FZB[AYBZ<^AaG]@_EZ?YB^@_DZCY/W4YAeF_C]G`C_AZ@]@T=V`C]C`F[=X<_@\C[AfF]AbDaC^@\=W@G4 &%Y?]B_AlP]<[?W>Z?`D^@{HF +2 !&U?bGiH_D_D`HfNaI`FdQdLlSgPnRdOgSeMhTfUmSeJiUeMeMeLeMcI_D[?W=U5[DU9\AV5UA[E`DaGZB<5 `H]?ZB]BZ?_IcMbMbB\FcNgPgP`JV;_B_A]@`EZ@[BS:X=W7ZBZ7*)-)'/')/))-()-*,4"-6() ()(/('-()-)',('/((-()0*)-)4?!-"'5!-"-().')/)',')/((-((-'(.().''-+'.')0)(-').(',*).'(.((-*',()0((-().()-('/((-''-().('-').'%+ZZgWXlww{}qukkz\\iNO^DAM/-6>?H/-53 498BFFQ?>G:;B,-5%&,NMYBAK:;F*'-'(-%',`75gVcIiRdMeOcQgVcLgP~}zlo|}}spjl^_l]\mNFL_OkOdL`M`F^DaK_C^I]H`BcMbLcIcPdLgPeRhSdPjRjViWgSkWlRiRoXjNiWiNlTmXoWn]lWiXiTnQfQmTmTjYmZhQlRlWiPoZpXm[o]lXlYˢrtvWXXdYSoYm\r]s[jVnUkVfMjSgRkTdKfJiQgObFdFcH`G_J_G]>[AW4]?]E]@Y:^AXEZCY>X=ZB]AX7Z=T6Z>\9x_V>Y?Y;V:YBZ9T@[:V9\ET5\BXW8S9W;[>r6/&2F+-[<[AQ7T=T:W?U8Y:ZCY>M>'+T=\AZB`I]DaF\AdO_BcLcDcKcEaHbMcKfJaJaLbF^KcJaJbI]B\GY@`@S6V?X@P0U;XW;X=`BC;(W@Z?\AS4YA]@[<^EaIW;X<]?_BaJ^F_DWD[>X=\>Z>dDW;V?Z?Q=((-((/*'+'',()/()-'+1&."+$0#%**)-'',*).&)/)'-')/(*2"+"(".$&/*).('.'(.*)/((-((.((/')/((-)&.().((1)&.((.('.((.().((/)'/)&.((.((-'(0''/()-').(',').)(-CDR^^gjgwuwedxUUgJHQ0/6-,3NO\JIT%).@3:?RT_JJW76?67A)&-PP]&%-*(/'(-').$'/E8W8]CW@[:\E^A]B]@ZBzxxii{movsotcdr\\mXXhUAG]@Z?[CZZX=Y<[=]B\F\B]B`C]@\C]G`?`KbD\>`KbHbJbGbMcIdQdKcMbJaFbL_IbKbGdJdNcI`F`E^DbMaM`MdKaM^GşrtvWXX^WRgNhRgSeKaJfObI`B^IbG]A_F_G]C[=ZB\HYEYAYY8V7N3V6S3V6W[V4R7L-P5O3M9S/X!19",@0S:XY@YCX=Z>Y?\?[C\D\@ZDZ>W;W=Q:V4X7Q7T9P5N1J1Q4M1L2K/P8R4T5R3Tb?eCgCjIpOqMoKjJeCc@^7U*P$Q&MJGEEBDDEEIJLM@&&#K&KJJLK$O"NN!Q%Q'T,T+S*R$P$N!N!O"O#N P#KKIF*)-*)-&)/((-)(-))-').)*0&.!+"/"%''-()0().+',')/'&/ &1 #!&&*()/('-((.()0('-*)-().()-('-)'.')/('-('/()-'',()0''+')/('-((.')/''.((-)(.''.)).&'.().().('/\[j][luxyxjkyXWgABL0-4++2;?K00:,+1((-((/$'+_DiDiElCj@hClHqNoLoMpb|mm|nn|oo|xvjk{WViY]kP>kGjFi?d>b;_9]7]6[4[4^7`9bGLN!P#LO"Q#T+S+S+R*ZPX*V(U-X/^9b:bd?jDhAfAi@gAd?iCnJjElHkGjEhBe>_8Y2V,R*P#M IFDDADHKP$R)X/B-%M+M M N!N!P&Y1Z3X2Q(R*U/Z5b;]7T-N!MN!N!LM KIHG*)-)'-((0((/('-''-()-()-),3 )) #.'%&,)'-').((.)(/&(/!(3  $!$&,').()/''.''-()/&'/((-*&-')2().)',')/((/((.()/().((/((-()-()-()-((.*).''-()/''.*).''.*)-''-A@M[]jIFSecvVXd;=G--70/844C-,1'//5N7 >0/6('-)'/+)0&)-JGU%&-')/((.*',,(-kIpMnKlHnJmIoKnKnKkGnPonrpnn|pp|pp|[]hTYmZHiEhChCf@b`:_7X3T.S)Q&P$P#P#P#P#P#Q%R&N!P#M M IKJ)k [HLMN!N!N!LLMLM MMN M KKKIHHFHGIJJH?%!!F"GJMN!N!P#Q%T)Y0V0Z5lVU&0;/$&\6[3_8b:dd=e?iEjGjEmInJmImHmHoKnJoMoKmIhBd>[3[5U.O"M MILLMO"M U/U,G/ O)Q%N!P#U-\7b:f@_9[3]7_5^9[4Q%U/U+Q&O"LN N!N!M JG(',()-')/')/().('-()/()-')-#'-)6@&1"'&)2('.((/)(,&(2!*#.'!,+)-'(0((-()/''-)(/((.((/((-*'.)'/'(-)(.'(0'(/().()/((/((/*)-''.()-((-').*)-')/')1('-(',()/#'*HFTDES78DNP]lnoo]]p76A(*0/-636?328!,&#H->!+*().*)-()-*)/CAJ'(.().''/((/6+-kLlHkBiElHoLrNnKnKnKkGxxjj{nn|ii{kk{ddtJR`hHlHkGkAgBgBf;j@kDnHmIlHjCnKoLsOuQuQtQuRxVxY|\{_}b}bcfhhgijkiheecb}cf^gfiheedc~b~agghh~뛄kjjiidz[yZyXxWwTuSpNnJiDjEhBiDiDiDjBjEh=b8a7^3X4S)Q%P#P#O"M N!N!N!O"KHGFED4o \_HGKMMP#P$P$Q&M(O"L'N M LMIGGGEFEHFGHE:;''GGIILN!Q%S*T,[3Y0^7QA/?K!Y7\5\7[8^8^8_7`9e=gAkFkDoKoKqQqMoKoOoOoOrPmIjEhBdBb;b:]4X0U*R*T)R&P#P%Q%W.T([2Q: ^D_7Z5^7b:b:iCgBmHnKrQuSkA]8U*R'Q%Q&N!N!O"N!M MKH))/')0)'.)'.('-()/((/((.''.*).)1?".# '2)(2(&-()-' #/#",()-)'-)'.('-*).()/()-().('/')/')/((-)',()0''-*).)',('.((-''/*).''/''/*(,''-)(/((-''-()0)',77CJIR<;G>>HAFM==CA@Nut]]jAAI''-/090/5/07'(*)K3>/ ))0('-''/.,4;>L,+2((-'(/*).C2/hDgf?iDlGkGoKpPrRvTtVrQpNrNsPqQoMlKmIkGiCd^6V.L-"Y5\5hAd=`8f@qLsSyZ~bzXy[pLe>_7Z3T,Q'R*O%Q&N!LKIF"(,)(,('-('-((.((.''/''.)(.((.,2=3@N!+#)2((/()- #*# $!'()-*).*)-')/('.')0)'-)'0)'-')/')/((-').().'',((-((/('-((.((-)(.().().((0()0)',''.')/)'-'(0DCPQQX:;E<>H:AI:9A*+3PN_HJZ?AJ23;21:,+0-,4(&,/B< ? 1+'/('.*).,-5@BO*).''-((/()-M2/a?_9^8a;bWB#)!-jFmImInIjDmImIpNuSxVuSuSoJnInEiCd:^6[6_8\8S)M LJI%*-')0+'.()-')/().()-((,()/().)(,=EV#.;!+*((.+'/& *",$!"(')0('-)(-&'/''-*).''-()---5)(.((-').('-((/&(.')/').((-*)-''/&'-()/('-)'.().'(/().)'.')/**2<;GJJV8:D::C4<;DGP)+1:;G::FGDN33:-.5,-7,+2(&,=AFD6+(0&(.().)*.BBL('-()-)'.((.Z70]7\4Y2Z2Z2Z3Y2Z2[5a:d8c=iJ}rtb`sadraeuVCgChAnKqNpLnKnKnKoKpMpLuTwUxXwVvTyXz[}^ehiklloqrsspmpmprpsojkhhgfffbedcfhkilkkjlloqqtuttuwpkf}_y\sOlKiDa:Z4X/T*S(R&P#P#O"O"M M LLKIHFFFFFIJEX{emTHIHIIKLKJJLJLHFGEEDDDPFLFE6%&(?$JIIIJJMP#Q%Q'U*W.W/FFI%09^5'oLpNsRtTuVwWvSwXsOvSy[vTsTqKuTwVy\y]wXvTxYxXxYnDvXsOmJb=d@diClGgBkGnJtPwYy[sPrNqNnIjGaFJKT36>+;3/4464@75?DBMA@H=?J++5//7..5--4$(+ABL>& 7 ()-((/('.''.IIU%'-()-''-((0j:0^9[3[4V.W0W/V.T*T+V-Z2[3^8pa~zwiivttdImIlFnKpLoKoKpMrNrOqMqNrNtPvRxW{\bdgegjklorrqrrppppmoololliihjkliiihkjilllljjloruxyuuxtmke{`xYuSqMlHd=a9T*R&Q%P#P#P#P#P#N!LLM JJIIIJJJJJIWD[GGGGGHGIGIHJH!I"HEEDDDFVyLPJC AGGGHHKM MQ&Q'Q$N!LqQO&1=P1-nJsOy\z[{bz^z_wVyTyExVxYsKuPwXwUxY{_yZwWy]|`y\hCwRvUrOlIeAjElGmHhBnce8nHqMrOiSsVyYrRqOlLe>b:gAe>_8b:fDhCiEpLvW|ayYsTpMqMlF`8Y/X4S+%'+$)*&)/(',()0'(.((-()-%'.*).'(.)(,+),!'0)2=!*'"+"+'"!''-*).((/((/+).().((-(',()3*)0('.'(.().'(/().&(.'),#'+')1*)-'(.')1((-()/('-().''-('-()/()-66@@=GIM[695&:-.,453967ABCH>?KDCN:8C+.4,+3()-(',9CF;':&(-()-((/*,2HEN$&-()-()-#)0?)Z5V.T*T*U+S)S(U-Q&Q%U-]7`:`:b_^1h?kHgA]@$.eFsPrOlGf@`:_8a:b;hBhBnInKsP{_fjl{_yZwWrSiD\3R%Q$+!))*)-')0''-((-((/()-+)0)','(/)(/()-&-75@L '1#!!(($%%'.').*'/)'-&)0()/((.('.(',').')/().&)/'(/)',')/')/%)*&)0&)/'(.')/((/('-)'.''.()/').+(-((.13<7=E=BD-6/5(*,487?46@8:CBBR99?/06..4--6((.((->BF 6&>%))((-('0-,3IHW"$*()-()-#)0@,V.X1X1T*R'Q%S(W0U-W.Z2^7`9c=c=f@d>c@bh@f;e?e?e?i?iEnJsOwTyZ{[{Y{^zYxTzY{^chkklqrvrspmmllllljlmoommmprroololnmppsw{zwzztxz{{zwrokihz[vVsRnJlBjFlHjCd=_7U1W2X2S*R'P%P#MN!M LKIGGEFEa7| XDDFEEEEEEGIKIHFGEELT`3d UO<#F FEDDDDFFGJO"O"Q%X/X@0AL$[?hBoKrNsOqNrOsOvSuBk5t8r={ax\uRuRwVtQyXwUxZtOf;oBoNmInLoMsPuToKh8R zY&bl=kFjElGpNsSrQi/_^T],d:f@a?(#-vUuVtPqMoKj?jEhC]8a:iCrNtQrNsOwYwWsPkHgCd>a:Z3]3X%L!*%#*+,"%'-)(.)'+((-((-')/().('-().()-()-*'+7CS'2&$/&'!'3()-''-*).*)-('-().&'.()-''-)+4//6()-().().((-()/''.*)-+#.%++%,0*(-1%)&.)(0((-'(-*+2ABL:BG9BA+D49%52<)*/').,,267@33;8:C22<.-3,+2').%'-EDQ 7# 40()-').23d=`9Z1U(Y#^&g&Z4_8bBI8@<1E8"@'27725>+)-)(.13<65?=;D9:D64=02;,+3)'-&),IHR-74 73().+),:7C43<**2((/''-#,X6^8Y1Z4\6\5\4\4[4]6]6]6]7[4\5\6T%W.Y1Z2_8a;a:_8a;a:c:b)>GC('Z4Y3_8bws%Pd:l6lKmHgAf@e>dAb;`7[,QU"S!g*\&`7a9b:`3`/3JEU 3CU]0g@^;!hCnLmHjKmJjJlJjFiDhFlHlJmIhCd?c<`8]4T+W0_9^7\-aHpG/. +%&**#'.()0''/)(.((.('-((/((/)'-&'.()-('-)(-'0 &2 -"1"&0*).''.('-'(.()/()/''/().87D56A13;()-('-()/((-''-().')*+!,$17 9('(-2("*'((..-4:CL7A<5C>!C$898B25>()/(&./0874>::B;:E66A//7,*0').+)0?JJ.43 < 5,'1%'-BDP12:(+3()-*)-#-`=d?d?bd>b@ |@BDEM[l2 1y rgX"*#BEGJFEDDEDECCDEH@"9>!P(Q&X.[4b:b?p*o%+h c@x2\tgBlHnJqMjDb:^9\0Y1{TrLV,lV_%[)Y4V-W%\!K >L *A pLU&Z,]< (%hIfBa:b9[6Z6_7b;c?c=]7]:a;e?nLkHmHb;_7[4W0X/^XpD@D -#0.&+#*).*(,((-''/().((-').').('-()-))/))-')/!'),",&&.().()/()-('-)%.').((-/099:B33;')/*).'(.('-*)/('-()-%(+ *&- 5 ?#3(93$'+;>I3C;;DC*B3?5>>23>..6.-5()/96@37A31867@/06..6+).!)+*+2>JH1.4/ 7 ,&2'$*OP`0.7*)1''-*).&/jMnKkFkAfAg=hBg;hDj@j@f>b;`:\5]5Z,b:bc[FOT'U)^@a:b:d>c;X/P%P%R'T)V,T)Q&V,X.`8a;d=d<`8^7bdbHVB {H4/$*'*#$),((.)(.().().((/'(/'(/()-('-((/((-*)- (%" %1$/+)1('-+'-').('0('.)&...5KKY55>)'+()/.-3('.''.()0()-%*.,(0/ ; ??$/!R>DE(B0*?+@!!D.@13:36?,*1,+2.%&-.>GN"3.0 6 *(1--5ST_63;')/))/'', $,xVsPtPqMoKoKnJoJoKmJlElHe?c=c=]+]4f@jClFmCmClEmFiDjDlIpLpJrNsQuVxVyY}]~a}_b|^yXyYxVxTxVvRrPpMpMpMsRuQuQrMpIpMrNvRvStQqNqMrOtRxVyZ{X{\{[{]{^yUwTtPtQwUxWxX{]fhikknpuw}斁痃噅昃痃~~~~~~~z}{{{}wtstonkjihcz]wYsOqMlHg>c:_7\4Z3Y2V3zq |xQlVIFEBB<k?ACDa]cy' ( ujqQ/G/$FDEH@BBDDBACBDCC`C3?- 6EEGR!e^*o ^L#M R'\ %q MS(W/Y/W,T)S%xHSFJNS]{GLN!N!L#Z AD ]E <VE uGR#S) ]9]6]3Z2W0Q$P"P#P#Q%S(T(S'R(P#Q$W2Y4_7`2\+rU_iB=F H0 2.&**0&(),('.'(/''.)'.().''-('/)(-')/)(-((0%0#,)/#!+ ,*)1')/'(-((../9(*1006TRb23?)'-').+*1')0*)/((-('-').')*-+ 6 C1$1H)?%$C0 @&? NS_CDO75?2.6+).+),/0799C017207..5,+2'2/ )&3!6D<3*#;)(.87@LJW23>*)-)'.*)/)(-xUwSz\yXyZwTvSwSsOrOpMpMpInHkEV#i?kGmDmJnKnKlHhCgBhCkApLsOxVxXy[{^}]~b~_`|\zYyUxWvSsOpLqMpLnJmInKnHmFmFgBe?g`9^7]7[4Y2V1" ZZogN!LGE?+<G? AAEQf|ex" 8}fqvTDy<CACBD@BACBDEDDCkCH/9%48Y4JIS[;lncJ Q$Q%Q$P+}mhQFIIHIDo@@? FGTSW}EKG~JYCL> ;@@ HIM%"b;`9Z4X/Y0X0X/Y4T)T*T*W.V,Q%P%P#O!M MX"`+ iR2A IF/ 00/))+",%')/&(.()-&',((/+(,&',((-)'/'(/)(/&".#0 ( -$0'#'1*)/'(-*+2KKX(*1++2WVc106().*).*)0((-((/)(.*).'',%)+ ,(0 1;:1L*G EB&D-SW^5::/0866A,*/()055>:9D36>02;/06+-4%2+%+'&9.%A+0$0;*&,CDOLM[57A()-('-().:.0}V{[yY{]{\{Z{[yZwVwRtQtQrNpMmJvX rQoLnKnKnJkGlChCi>hCgCmIpMrPtPwTz]`f~a}_~_zYzYvRqMnKmIlHlHmImCmIjCjFjFkGiEd>gBkGoLnKnKoLoLnKqMnKnKmIjFi?gCiDkGnElHoFqMqMsOrNsPxVdhlnrrvsz~~䕆}z{wuwwzx{xwtrpololllkhcxYuToKlHkGkAd=`:X0W0oA]iznO#LHHrC3= (= f?DLLK^. po,X 1>DEBBFADBEADAADCG@ GBK4$*&PN`giMp O$O#Q$P#Q$N!P!Dj DEEEAByA=DDE.O pBAEED "QN> e? ;4< eBqBJ"S)U+\4\4U+V+T)[2U*W/Y1[3Y3Y1U-S+Q#R'Ti[p=x H> RCJF72,$/$.%'*0+'+',%(/*).().'(/)(.')/)(-)(-''. ** / (2(6?,6E*"%*).1/6IK[(),*)0RUb/.7').+)2(*1()-((-''.((-').#)(!,$-!, 6 ; 6 ID P A 8FBCJV834>./:*6,%.&'(.5-D3 5/9%)/?@NIHV@>G').'(0((-N67}W{]|_~_~`|]{_y\z\z]y[y[vRqHi@g=yYtQvStPsOpLoKnKnKoLoLnKpMtPvRwUzX|^zY|^}[{]|\zXxXxVuQqNlHiBiDiDlIoLpMrOoKlHlHjFmDkFiEgBf>f>hChCjFlHjCgBgAe>e=e=e?e?fAlInJnKrNvRvTwUafikqtpxxwz}~~~~~uw}vuxzxx~xrrnpllkkijhfe~az\qMiDc<^7Y2U3MzZysKIKGA/= $< RC BqXZa% 9 Hz$I4LFBDEEM? BEEEDCFDV@ FV?d B&YetiAy P#Q'R&Q$P#P$R&M!W1Y GFFCBuC.= DFE(CcAFvP*B gA @(<<.= B> wCE"!O%Q%T)S'S(S(P#P"T)T(S(T)T*U-V-S'P R([jh6b1E 4@ JM M 3250 ."/!%/),%*).''/''-'(0((-().((0((.%&.)(+ '2('0IXo*.;%/!"(118ABM((-**2MJW+-4*)-()2''/*)-'(/')/((.').()-")'- -1::A BI D%A%GLP69559>+*1()0-/8==H98C78B?A!&0.'(.-67=3 49,15JER=?I46>('-((.&'.^?<[|_a]c`|]{]xX}_~b|]zXpN}b+rGy[{]{]|_{^{]xYvUvTxYvSvSvTuQwTzY~_}^bchha}`|`yXwSwUvRrLpMnKkFpMpLpMlFjChCjEnJlGj=iBiCmImIlHlBjFg?f;gAe?hCkFgBi>jFmImJpLpLpLtTuTyY`eimmlmurux~~~~{}~urmrsxtpoopmooproookjezZwVoKh>f:^8Y2Z)vBZGGAL= ;O@ >J |KbpZ- Xj&>;TNEEFCgCb@DDEDEFcARFMXNsN2OeKw/z LJKO"Q$R'S*R'Q%P%yiRG O KFEUB =EFE.@ /E kLL#J kCDB@ ;=?= wA|@O#N!N!N!N!LLJN!N!N!Q$R'R'S)R"QW&kYNl/l >JM!M O"5 6/8 0#/# ,).&"'*()/()-&'-*).('-((-)(0''. *)) ')'-?DP)9E#)5&44=<=J))0*,2B@I.-5.-6,-7*).((.()0)',)',('-().$*)+#-/ 7 =EDJqS{YyYyY|]{]{]{]zY{Z{]|Z{\{UiA\W(|Zdcbda~`|]{]z[zXyZyZz[yZzZ{]~aceafgffca~`|\|_|_xWtPsPqOqNpLpMrNqMpLnJpLnKnKoKqNtStQtSqOoEnLqNuRwYtTsRuSpOsQwWy[{]xVxV}]dilljlopsysoqt{sxurruusurqomppmomsqoopmlhg}auQlHfAa:[4W0T*w Y@ugRDBA;@> -R jLZoZt<ed<? WKFFFGE1> dAEEGDE)? Gz`qm~igWp% SLLJKN"O"P#N!P#Q%T,OXQuM?A >@ NA >IML\\$K ,O  :@> v@zE!$L"IKKJIKM M M P#P#Q!R&OJOMT.[ ' 3A SER'N!KM >6.483$2"*)+*('-()-)','(.((/().-.9 !) * !(1'',76B9@OZp.3E+$12<)'-+-2=>F--4??I)+3').((0().)&/)(,((/((-#)-",".46<<F L J F +E34E>4''-1((./0778C0/7;7B$; J%7,'*+/ @@F)C/3::@FGRP^CDM,+1*(-((.#&-Q 7HVGIGHGEf?8@ CEEnFCG LAMgnYvukf|[NQKKKLN!P#P#N!P#Q&O8J 4M BA 6B &=NB EKMZWZ7L *I UPZ^U=N (> :?> V> i> &#DFIIIIN!P#R*S(R&Q!N!OIIOUca& g$@LIKLJA 7 7 ;3&7 0!-/*/%)+''/()/)(.)'-(',/1: & *"($$/'&0?AL11:BOdYl",($''/,*264>**3IGU+,3*)-''-(',&)0')0''-').(**+$,1 5 7 :SQh c T.H3 @""*$')-298<;H55;3>=F%H/,4!/$ +'+=5,?10::>CNHFRA@P((-')/((-"&.ZHvRxU{[~_dd}`a|abf~[wPPW!wQ܄hjljllle~`}a{[{ZyYxWxW{\{\`diikgeihjlptqjfe}`yWvSvRwSxXxWwUyYz\wTwSwTuQwRxWzZdeedejijmspssrsvuxxyolpmmroljfiejlpppplljlkklkhebcf~cffdihiy[uRpLmIf=_6Y2V.S*R)" pftPyMJF0@ >GH 3_ r )| ' 4x .b < nERMHIIIHrD=[B vC`A 7D !U GYCyDu^dbM NSRKKM N"N!O"M P#Q%P#P$}O$O E%? =5@ HT#KpRFP H]en_]?GD <>C> j=!"!&DBGGGKP#P#Q%T+T)V#PMLNQ9q "} + 'B GIIIIK I 76 = 7!/& :')-)'')/*)-)'.&(/)).)(,,+6'+6$ +(+(0'(0AAI87A**3_m*6? +")*/87@*,3BBN**1')0)'-''/')+&),'),$).&(++%/ 7 37 ?> D _ T N ?B-()//8748?3<6%>-P2<7./6'+-1& ?)?19:@KPON\CDN((.')/((/ %,fNxUxUyX{\~_\dede]xWsNAU؂ciorspppokkid|]}]рN{[~_dihjjjkklouvruof~_}_f~adedeecd{^y[xWzZvRuTqOpMqMsPzYcjjkkijmlmljjkompmmpopllid|`y\dktussoopmllhghgkif}^|\}^dhjhexWqPmIb;_8[4V.U,S,g( ds]J!qJeA ?8? AY)z {$W-qQW[KJIIGE%? =IA ; E%\ Me$ 7 Aw mFuN[PWN M LKKKM O"O#LLwWI<<9A ZC^ L W7E S/` f`G\ cNj\N D;I? @ **&&BCDEEHKQ%T&T)T#S\VuYN$C^ !H CIIM LO"N! O ; @ ==8"+(2"$-)#)*%)/%(.)(.(*3().,,4'#0!+$*).&&,@AK<&I WB:y % v!A5oby_PKHIHG^C :@ @?D QDb VXw@ ^9JfYWNLKKGLHM!O#K}HpHZH><eHDET\oZRC C,a 6` YI;nWT;K =!>_EpD#/2& EGKLLR&W0V(`;Y.P [+gp^jO;ylMHHJLO"N!P# WA B@A= 4."!."*$%(- )*+)-02:(',+)1# +'",*)-&(*;9D24<56=>>H-+2^pEPb")"#')/<pOpMqMpLqNsPyZ{Z{\{YzYsGj:\Z&l_(fgegikmpmlljc|]H{Z{Zy[{X|^{\|\cjoolkrsrmmojfhfeiiga~^c^{_xVzZzZxVxWxUyY{Z~aadcbilprvyxquz}{{}}}}}~xypkjlihfgdihiijilpv|畀疁~|~zz|vvnjg{]uSlBfAc8\5Y2V3n8+TPngTL&?LBUFV 3m =P v^QGHGDEDXC 3A GA N c gpd% kvepQXwjTPLKIIJIIKKGsFdH?:0G 8G oGG`yIgDF]"U Vbfi*V @>AHF 0?%4!O#N#S'[0_7a9mCmD]&pV[&Duw (i^lLK GGGHLLI[ K HB& B 3A /-0(*.-*(.(+21<&&,()-&3)'$.*(-$$+>:G32925<>AK++1IFP.8D!)3$!!':9A&'1*)-%',!))!)).,'00%)* 38< 7 : GA J;TU F F 5 I"FLa1I7>;FGJS24;,+20/8:PJ>::;YWhXWg==E/2?SUdrzlJnJnJnKoLpMsOuTzXyY{\n?ya20KqL{]~a{^~`ddgjeihij~:}V}]}^|_{WzYyXdjoxuwupqookccffb~b`~`}]|^zZxWwUxXxXwUwSy[xXvRwS{]_gggjlru{~~~{}斁~{}~z痃斁ypolikjlmlouzuuu~䘃盋䜉坌韑韑垌韑螏看皈皇暇昃昄yungz\rRnJh>`9W0_w:s TRLND H g/c@m  R0?JPPJFGBzDD FW 9)U &~ 6} )t ,\c^f{D{VsaTeMKNJJM N!LKIHISDFD %?JB4J #E .H HDNKkJPE 5 0Q SD'A BA?,F /+&d3b.a.nJf>kGpNd5d/Z)Z8f  ~ jRM#LHEDEGHH_XJ N 9J #1*= 0$2).2.)(*01:(&-#'/!- #0%*).%'+A>H24<>DPD@KPLX2;>$7+/+7ST^; ; : >`\o]^p7:CwY_p]~unTjFjElHmIoLnKoLrNtQy[qMj@FTEOxXzZz\|\}^|\}aegefa`zSvx.|d{\z[yX{]}^cdlorx~}{zssljjhih~a]|acc|^|]|]|]vSzZxVtQuRxVzZcdb~`bcjlnqrpruzzzx~{xttpqpljdeosyxutwwv|{||斁虇ݙ~㝎堎梒栐袔袔塏硑蠑蟐㝉県噅痂~zsmgcwYrNgBZ8N4 rbM'N CH.z ;s l 8>@S[LEGlL}D@PF $S^>Do  NGDAc ar?b%T QPKKQIHKM LJJGhGYE/B GiZ@H] B @@(B QHIp0I D.H RWECC CM ,-i`)b9fEh@c=Z1]7]2uVne`bh}  \`P(IJHGDEEEESqL EAK<$-*C%.-8)(-*).//:'',&&-$3$%2&'/().'&+89D(&+DENA?JGFR88D((-''-6BO(5?#/!,"&-#+(#)&"*(2/+#3%)*:7: 9 <A'F > A @M P m_U \ c ;>HASONQ]RXc0<76:<>\fhDPO@;"I+gj{NAFaBe@e@f>g?gAf@gAgBmHkAmJmHqNb2\-<]0oKsOuQvSxVxWzYbfegg`ou3˂Wd`~bhkqqppqtw~斁~zsolg`}_db|_{^abhggiikmmjhjjghllonkhllmnqruuxzxxuuqrpmlpuy}~zz}}斁瘅虈㚅蛊蛊蠒љ術蠒䡔梑⢒梑栐梑桑蠒蠒垌蚉嗂~~z{rpi~_uTkGFvs]\SFV/Z  0_ 9 "]3Y NEzRfG tC&@?G N`3g Ehw 6a q>{HFB;W >~ " \l^PFLFGEIM JIvHyI}HVB > NQaVIUB >WL]2GVJ D,L AM-N E>+U )D2*"f8\-Z.\3PdGP PGz^Vusi=s p[L+JKJIGGGEEEMFLD HL?$)-=..0')-)*,+6*)-$-#/+""+().((-89A(&-GIUCDNPQ_0/6((/()/*)1/9H)'3)'$&'./3+"2 8.+ : : : 6;3H=(I2C E E[ I fsY k +O0APO@]Mferpm}LUZ2S8^iiEWaj7[=;II#ω_F[5[3_8^8b;ciF[(dAc>uU"jRB hAkFkDkEmIpLtQwUzXxW{[~`huD}?mopqst{{wutuuwwtrmlhlfaczYxWxW}\~bikikloporuvvrllppomoolqrotppnpsy|vzzuouvxy}痂痃痂}虇虇曉坋韑砑蠒ƚw袔栐ࠌʢ|袔堎術䣑裕䡎術䡎䡎術韑瞏鞐坋囇瘆喀~um}_tQoO?xk *Q 6G DY)o f ?d4d UPPLLF 4D aC G&HM&l Bx  L ?<U@ wJUUsEF1Y ~gR^INJIHEHHHNJhH_GCD >NH]^yTV'NL@fQ`+KPFX=E0OD D@F@ ;2\.V(X%{QNiF'? WJ8^4X2Z h }{r#Q,O#LKHHGGEEDD >~OKWH FC#@*12,34, +),+-4)'-'*&2)(1((/31:25>)(/DDQABNNM](',().-,6'+1.A>#$.(4!&3&1#$-6."-%>+A62 = :8#=)-=66L>*H4*I-$K-O I \glmJ:UDIlSFmX|}s|}ob@p\@"AP)> tJV0U-U.T*U-U,W.W0W2Z4[4`9b@X+W(f@V"pSE eNe@d>b X? U? iML_WYlF!U}GMA rGQHSIL|OMHJJmECF (K ">TOV~PYIA=W#F LNPYU+S Z JU#XLM4K*88#LOeOlU!U%aIPIAIJ F DL RKI#LJIGGEEEHFDE W SJVUH;>&/-910:#),)*/#*,#(&2&+)/--698B..4(&+7:FJJWIGV'(-''.76@HFQBGM-#("2#/#/- -#";#G-G37:= =',C/5K?@UG9PDG&#L'\' TYhihGJO$NS1 D $K NAC@ QYl ~Cy P?@+= ;(?GH MB RCq>j@L K;-: GJ]KHSVQJKtDuGyFBO!S AB |b|Y$K WE BcET"\ PBE DR7O } h ] T [ m F "E>WQnR]U]PO%[G2H \?K kNM#KIIGGEEEEFHEDmsVgSDV P",(D12; B%'*.-$!+'%1*)()/01;;:D('//08DANSR\@AH('//.589DKTWKKU6"'&3- , , '3($D"F''I' = G@XP@QTOX^Mc[Mg^.S4/G)RJ N O\`Q P6@ TDg?kFqNxV{[{Reg*uErnnmprpnqnollkkjhjgefc~`|^|_bgjlllqzw}{zvxx~z}yz{{{}rmic~`~_ddhfhku~斁䗀県県韑鞐坌䞋睎睎袔梑韑鞐蛌國瞎䢔硒硒树硓蠒術矒韑ߜtEX׌uqc4QݐqsqrorspoproommlligvRpMbC`jDGB>[R; 4 ?(B2D 'BDRjx. u>BB@A<</? IM WLmP1v# ,Y A I )S dWSTVOFG|?\@ U> 4R J.B B8g !rMBHIJRaF'U %P a kkw ^ A3KJZ6P /R 2G :F eUQd l_tTIKN!KLJJHIIHHEEEmUd \ XC; L $+/ N+))/5$+')-3 & #/ $-*)165=8;C((./.59:ELM[-2:?BK56??AIY]k#5)3 '-/7""5=M)* 8 )V5#L"=S;cH^NJ'B}U-SV'3L-L ZY\jQH HC0B *D M{JMS+KsF<:<M#P$O"P#Q$P#Q$R'S(T&V,QU,OrKgG\D-D ?W0Y3Z2]4^8b A> $? Z j? yC^B G B@IIIGPJ^$E   } ih L LZQT FQ`k . ZJJKKHIKKM IIHGFEEck\I Y >B%NPTV;.83B$'>9%?7!& 57 ;@@BAEIIHNnX'[ _lUML I F<@B OHSEpHO&{I =<4> IKKN!N!O"O"P#Q%S*MQ#NJJMB =BGQ%Q%U+X1Y2]4]-c8kFqMtRe>4jz\ekjegfie{\{[bf~bcy[zVzZ{\z[~``}`{]|\|_bdeghgjkorut}wz}{wwuxzzuosrmompllkiljihiow~嗂昃喀旂痃痂䘃瘆䛇䛇䛈杌嚇囈嚈瘆䘁嚆瘃xrrvyДlY2}y8z[h1E7?m>wSvL~:xOvFvRdfjaΊ\iWʆZ}YvRk:`&UQLL @@B@A9?< DE K =[ w<{Ond>]IRv>B> :&<>D 'd ?p nn2^=X pcCs kVGfVeU{HPF N@ &DH6gAOXJ BBlJGEEEEEI/O S L  a C=VNT#X EJa \BHFJHIIIIKJIGEECDDj _ X= ` F%F/A!*C1D843?$/ %1*--7/.4BCP79B)'.*+5;>DP[YAAQ+E/ebuILVATO'12-B7$H2^imHi\B@ 6 6 67@BFCEFGGuRA] al LDCDI JC]GMuGIRB J@ IME;iAL!IKKLLJP#P#O#LL]EO"IA *@@=C L#M P$R'S)Z3Y/X-gCjCtQ_,Mb!{Mz]{^f~d{\{\{]{Z|]{VvRsPtQvSxWxXyY{]z[y[yYyZz]zZ{\{]~]}`b~ddeeeffhhlmoqwxqqtrrroomqojejjihgdfhgjox痂~xrtx䗀虆瘄嗁斁昄晅嚈嚆瘆~vsrvskk:Bu%uCJ,HOZ\'}L|z,f$;i8x7E8@Iv7xYs=uq3e|Bl6T G>HCD<><= A BHF JT5 4wjV ofW ^@B+= <<>> Id, ;  |a?w Gf pN_Wi\WPfE OC ?; AMO*O 1L D ?F^ Rk3? }F+? wIG}C4? FL JJIKJJLJM KeDK6B (A CD ?LL M MP V.V+PU)fEjEe>AZr9{\{^}`|]fjc~]z]yYyYy[xXxX{\zZ|Y{]{Y{\xYwVxYxWxYxYyZxY}^|_~`}`}^{\{]{by[wUy[z^}bffhjklijllmsplkkigfec~chllmloossvrporx|薂{薂}|旂晆晅癆暈嘆{ttkkԋ^j3*y]'$  c~9+,;Jh1h=wh=hl/M&$zV\!'ywDr{,_a!ZF;=<<: =@GF~KMNT*K"d  =@zppnf|A@E:? =OCN IIzv5Y N_7i [\cHcH-M ?>?c)EA$B@BCBCEDDEED]E D A WO> =B ?DEk/X BABBDCEEGGEEFEBFGFIHK TmdP V h 5\V"W(325H% !)%2 *77B)(0GIV307>?J69A6>BBEP,L2:Y@uwYllZ>hK P >E6> @? $<?"=:"$o.JAEMoAGX CW oWW bFBA (B OQL mpJ+@ >IfCyD#<:=HHEEFHIJHJ!JGFI? (B [DAwEL"KP$R$P[5U&Nb>a0iJ*K mc)uYyZy\{_|X~cegf|]}b}b{Z{Zy]{\}`{ac~c|_~a{[xWuUvUtPsOvSuSxXxXyZxXxXwUwXvSvSuRvTvRyZ}aegjjiiehkikiijjikjjihilmlkklhjghffhllkhmrxv~晇蝍李瞑蜌tqonjibb'@    ! /Ax +m(rj  d X  ^ 3Ly haC:?<>;>-L LOPT#U,M#d# (Fwd4URI~VbB: B\&R 8b"i Eh o:e HO DV ?MB] C e aXMCPpREH@OBDSA@QC LA D<DJH;: AI I@R MCtF}C@CACEFHDFFxBKA DHIINII}i_ m `:c X #X'=`IH9=?0/< $$))'.3(+0LITFFQLLWKKY:J@ifZ7~wMZ?_C:B W0:@ VA^B ;0< ;=^FR^f-%!%N A{MEB =F Uu[9A >B IJ 6L a I hbhQ/@ =DCC> uA=%=EEDDDEB@AFAE9? ^BADGA KIJMLV)Z-SReAc7aT@]rNpLpLtPtPxW~]}`~`}_c|]{Xz^{_|X|a~c`|`{W|[xYuSpLoKoKnJnJqMsPwRwVvSwUxYwVuTwYrQoKnJnJpLsQsQwUz]z^{`{a|`z^}cdeediijihghheggife}beegc~bghlkikilps{~疁疁噄}zslf{_|\z^xYcg~^{5)  drapZCI>>==B I,P >N IOibf P!S(Q'M&s oSd; UTTF?;H' + c vv3Z Y,a -g .L OL!JKA`8a 6d 8zEiBe PWSa^m+T 7k NVcLLD 4= ;::>===>>?RDCDEDGBFEECCZ@ 7A EIKPTKJJsjrgPa_%b-SQ*20':.*")+7843@-18BFMUVfZmyqv^CT$bG@D^DUA8ge*%@f)^Z .? eeg!ku@RK-M (OB9> 4= b= CDBBDD@>At=D[> r@GA%ECCHK!kCIV/Q^JZ0[,b?>aqMrNoKpLpLvTwWz]~_^{]{\{\{_|^|`y\xXwUxXwWvTuTuSvUpLlHmIjEgFgAjIlHmHpKsRsOrOkEhB`9]7`:`9c:<:=I@ITO$V qQb"_$V*X1Q1i#Ek'`F>x2U ML H#[ roZ f^X C LA$V M AFB`Qb w  #i0I e3 T g 3S )X >;<<:==D<>@*@ 7> eF+P XFNQRMRFvCbK?L L xRPQTjW$P#O"LI ^ usd ]h aSM3T6<)V. &07*(1;AKdYaeyVlemgb?NaAR'Z3R(cHU,3A P"!= kC#>=;cA B?E@od@cx| i% ` z@xNgAIfF ?C L !J>B D Lc ovW += 6> E= !;i?";?EA@B?ABBe? tBP< F@ A HHCCCFFL@ DL!iPOF O!U%CXqIoMnJnJpMrPtQuT|Z{^{`]{\|a~]|_yZyZwVtQqMoKjEhBjEiChBe>dAd<:::GT==&= T_ A1K F Y? %Q "Q 'j `,b zOzSZef+Y3W0T*N!M Lyir g ] $S*SF&a #J+=1!)%wT-^Fwl]>lK[6X0O!];BDV0(? O")>D2@ bA : F9< ?? A@HGNV Q\g-&c"S3p A==~@tCT: HF .? ?B I T \V,;;'<K= <#=ADBA@?A?v@v?A= 6> >GGGDDEGoB~@BN3_ aEq]PN_8b=e?hGjEkJjGmMpLsPuPuSwVxY}`|^xYrOpGlGmHoKjEiDjEmImHiDkFjDf?f>gAd=b;^8Z2Y0S,S&P#M LM Q%R%T,X1`8jEpLrOwTz[~`_fdcef^{`|\yZyXxVxXwXyZwVvUsQxW{\fjkouzzwrpmlmmkc|^}`cdgjjhz\tPpLoKnKnKoLpMrOtPvRvSvRuRwTwS~_}`}az^xVxVxW|^z\}`zR`].IW3[ I>B=A>HcrVpl K1^9j~nEWe*nIEfߐwZL  !+:2b JCBzt g 8N > FB@ @?PB @j=WN^J\U9;2Ff LF"R aoe # )[ NF?::<>CE F _   P &p B W i dywLX pS?bj$j>f=f?_=V+S*R)P# q} pj GiRV H_9h'MK64() L.T*JV-Q#lIX1mGL N EAK<H<tB<<?rACDAjTD;O gR Si \ o9lB tDBKDvUgK ;H YRaTI=<<;(<+<BECCB\@ @l> j@;> a> AKADBoBDHFF? vA@Kp]JIXZ0_:b:diDjDlGgBb<\4Y3V1Y1V,V,U,T)T)U+Q$MLKJM O"Q%V,W-W3_8hCoKsOxWz\{`^z\}[{]{ZzYy[yVyY~`~[{\|`}[{^yZ{YzZzZ}_|^fjlmihgdb|[wVuSsOtPvRtQvRxWzZ{[yYyWxVvSsPsOsOrNrNrNrNsOwUvRwRxXxXwSwTvSwTxW~bdf~an>CROZ#0K?>>@:GH wq a  U u  [6zG#BCU8~ (n LW _ 7 {uQEB]B IJ:<;;=G @C !B*W&  c @O oPGBNbqT&a>`7eE[3S"X.X)[5S* l }j`,bN-_>Pwh6L41$L23{LT(MqKMuFGH)@ N%C@ }C8= ]@ 0< 5> ;=?hAA\_>d]JV.l qf"w)$65#:%JF_]>E ,\MNbJ[KKH@xB1= ?:{ACCCEABM@ ]@ 6> +<ACM@BCs?tDI rDC? b> sC nV_P)T*Y3]5]8b;e?lHpPqMrNrRqOoKoKkEiDd>e?iCmIiDiDcjDoLuQyZxXwUyXyYxX}]}]}_aikh]z\xXvRvRwRwRwTvSwU}]hcegdf{ZxVtSqMnKqMrNsPtPrOqNrOrNsOsPtSwTvSwRuQxVxVuTsPtSwSwSxWwTwTxVwStQvRwS~agb{`~YnD``1(F>??<>@CCS n$  '  (  ^3qA`NjnÀfQQ, rHI F B b]e LUL=>: DCB C,u x  I qyaL??DRMsD+C=:A<@: Ei@p>^< @v`,{  WDg Y Fwm#b?wM![1UJia!W0\,c>c?\6I lY1eYS1l PVYj2Y @ &(;4VwJTgIID L"9? GgB5= C;? ;< : =?l@3J >K AFZ P +p `~/iJ*PDMc}"*I1BAo@F> ==@BCDFA :C< ; :BCAEDH>|C>> :>J:F ?EDAuBLEH\ N? %Rg bzO-X0\4[3[2`8c;>BAABC bSMfb] KB;>??DDBLrL'G PQ=><<<CA=AM? CG xV;O4g w ' gz[&*gp]' p`*ug'q{)nOd9_+S%y d xchW=o Mf p+W Ka -(9!(=dIOXNULRBM@H<B <]? <%<= :$<V> {ATC F=oA,v UoyXi A@?M:&7<#J2~Bj> << -< <= ?>S<S7 : ;"<^?BZ@ i@DK@ 0? a@ ; @J:F @=Cj? @> r?|HAc g(PycL$Q%T*U+Q%Q&S'Z6eFkFqNtSsPqOmMkKoKlHiDe>b;jEkFnJmMlHjDd>^8[5U,Q&Q&Q%P#P#M N MKJLLM M N!M M N M P$Q%U*\4byG6mO@>:@<>@;I!G JV%T-M 4^ u@>DcI\x<B>cdEURA \X\<<=DAB @F)L HL H\J4_]dSWQl&{ 5q  W8i=b7dRYM$U@{( gg]s EE? <,< ;::= >*= TC@uK?A TWXoq Eg >@A?CWE*-+)!% $^CB?@=O?vYY<::=@<3< E4> +<)= =< >Ch@iA.? Cd@ ;JA EV d wXoJK%LN!T(Q%P#N LLO#T([2_7`8_9a:b;d?iCgAf@hBlHlGiDcDGD<D MlK"] -S piPAEKJ!J LiZ0e#v ik$/jo    1>j0 jipll`)g C ^M K G9L L ZH gB\D;F;h? : :5> <<:B=< r?YF NPgT `htpX??@???p?H!9>< 7 Z F^ P T NW }PF: =Q@ $;:= K? S> $=<==&=U? BY@ %>\> cL-O<%>uV`/o PIN!LMLMN MJHEGKN!N"Q%Q&V/Z1c>hCgAcc;a:`9b:eGS@l"xp=^|瘅虆嗁斁~~~畀ꒂꑃ֓y`S+q9zCj:Z. yfT NG @ ;:9@;@E I BHhY r 7 M;+h : ! vEAQ&y U$_ hX;Jgs O WQQ^SFBDGIKO"T)T*Z5gAr@6k'?   !ZN2 t~ p mc iB .a S6# h6Q nH ;-C 1C 0? 4= zC;;<3< ;>>= =D=;V U?h L \pSm= ?i??@?BF> A'? m96"(("*A:N 6= aJn`E:>,= :<> <3= ::>%>E> 4< ?J> <RU#=S Q e9V XJ&JIILKIHLJIJHIKHHLQ$U+a9\4]6b;e?gBmIpLoKmIkFh>gAkFnJpLqNqMoKlGe>`9W-Q'LIIJJLN!N!O"N U-Y2\5`9g?lClHnJqMpLlHmFoKqNrNsPsOtPuSsOtPsOuSuTwUwTvTvRuStPrOsPuQwSxVxVuQtPtQuUuQvUuSwTvTwUxVxVxVxXxWxWxVxVxWwTxVvTuQsOrQsOqQtPsRuQuQsSrOtQvSyX{\_hljʃ_{]4aNGFSK?$G JBG Z'deh%sI҃bu斂}㗃㘆疁▄㓂{zgΑkە{Ygl@Qs']n,uv:qG"T P MI;>::::><F @@Im?Bw)? [th#D:P W T d f^FT nSEBELLMN M O"Q%T*[4fDiDlKpUqYbɌjYK 7L }|r]eI r O l _A$? ==R A DUI<c@#><:<<= < >Pi= \_ :Nmnv4q -@ @== @L> ?>k?B8< A <?1 >9,4> = <:#<?;M= C= V= += <X=D N]HFEEEEEFFIHHLLM O#R)Q&X/W1[3c7bkGkAkGoLoLpLqMrNtPtPtPvRuQuQuQtPuQwUxWxVwTxWyYxVxVxWxWyYxXxWxWwUwUy\y[xXwVxXwTxVyYyYwTxWwUrRsOoNnOoLsOuSxWxZ{[xXxWxZxX|_yZ~^~`azSF_D C F ELDA3LW@I QPbi;zSׂervᒃ痂痂ݔ}‹o䛆ך{ڍyisw?`1Ox+/mp=}>FZ+ HRLUSCJD;=;><?B&F a]'p]%CpLSRC?.I JHk]l d M FV?fC FBILS)P$Q'W,U,X/V-Y/^7f@oKuTpQsS{\fjo $ b sqi T1{ wa QQ/)$"PM> V?:pC:;<>:=&<< H \^,H a| ^b}=,= B)<B4= Bp?^= @/? @=]H3= Rd:F H Y mjgipJRB::;:<;<B? \> <.? 6@ P S P8] `GADEDDBAADDEDEJP$R*T(U+W-]3c=gAlHjEkGmKsOsOnJkGjEiDjFlHd9b;c=hCmHmIgAa:b;b;_8W-U+S(S*R'R&Q$R'Q%Q&Q%S(V0W/X1Y1X1\4^8b;d;hCgBf;gAgBgAhClHoLpMsOtQvTwRwVwVwTwTwUvRwRvSxUyYwUxXzYyYz]y[xXxXxWyYyYyZyYwVwRwTwSyX{]z\yYxX}a|`|`|^~be{\wWsOtPz[z\b}_zYuSqNsOsOsOuQuRxUyWl;oq+)_HHGB ADN.Xa= >.Q=^r5}XrLJfȊo╀ј~b`yYiOe9e4Xs5>$iZzj LZVq,,bV C?CG OTL S0f$?S v]fJ([ D^J@Q= CYVcY% [ QKUGD T NKOkz j^K FKP#U)R*X/Y0[3X2V,_7c=kFpMpNoMnNuT{^~cjJI7 ugkp-q [m Y&-0"b G ^ B =:?"<V? <:B ==F;D > bS {$w M=Q= E< XG:kH';AQ> I> {? =C BR)>c [U$ G J Wl W M@T><:;>:=:"=::<@NMYOECEBCBBAADCCBDDHM Q$X0f?qNtPvUz[ce{ZwVuWvUsQhC`9_8\5^7[6`;g,IJ %L CBX!m 0oWp's@zTV~@xGjq =::<:=;KIOmo ^ mvB:o?:J> %=<AC <U> 9< :T c@ @]G A6B !E \R <d^HRBS:B>D::;<;<=D&D JIFGDABAAACCDEIIFFEKO#S(X/Z2c7kArOsPtPvVwSz\z\sOjFe?b;]7]7^8^8`:a=d>b.kDnKnImHkGa:\5[4Y2W/U-V.U-V,T+X0[4\5\5Z3Z4[4^7`9bc=hCgBhAiAf>iDjFmIkGmImIpLpLqMsOuTy[wWuTvTvTvRwSyYxWwWzVzXzX{XyXxWwTxWxWxWxVvUsPpLnKmInJoKsOuQwRxVxXvTwWzUxYuTvVyYz\vUvVrKqMnKmImIkGoFnKlEe,OXRVRM^eG BJUmL` ^@ H"Q,UYw$i.l4r1J9")C!ziE_t  ( VbdX2d{ _7}dl~ZKb(4\I<HFEFBF4SET@l_   CbD@jHMH KO STQUvPM?EW\!X WQsOQV,[3U)Q%N!N!R&S(]7gCiHjDf?iCkFuUemQ Oru3J` XP v W @R;`A (> A?> ; =<=K; d> ldH )9 1E <9? :1= ::> ;.D :R2> K=L I 4K NUpUd *? C fI::<<;:; ? <G= SDb? EIKO!S)P$LJHIEGIIIO"N!M O"M S*V-[3^7a6f@hCnJuSvTwWz[xXzYtRoKmIhCc=b;a;^1`/vX![$`hDg=i?kGlFlHmIlHmJnDnKnKqMqMpLqNtPxVxWxXvSrNtPsOsOsOqNrOsOtPtSvSvTuQuQuRvStSrOoMoKnJnJkGlInDnKqMnIkBj@h@iBhCk@mIoKnKmIkGiDi?hAhBh=iEnKqNsQoLoFh>iU$ RY&` WBA ?6Ne=iC w p=D I X,kr  4   {{{6/zM hs yAˎnX|Fvs=WS@@P G EISN7N[(S.Oesg f`G<AhD FTAGRHXul`CQv~>}er~g\ Z([5^6`8Z1P%Q#S'X/Z4`9b;c@b:e?nJxXdN o _`unW #z a w V $R WTN[COP::/? 3< @R NX V \k D@ BCCC@?:?>J>;J J O _ ? ]j H R dQ > N@::>C:::<:#=>1? qAmDM!PD 9E U*V-U,T+R'R&P#Q%S)S(R&T+T+W0U+U,W.Y1Y1X0\5be:e;a?Y TOiQ_>`?c=e:gBd?d>a;^7`9a;`:`9`:^8cgBg?f@f@f>hBgBj@f@jEiBhAh>iDgAhDjEiEnKnKoKoKqMoKnKqMqNsOrOsOuQuQrQqOpMnLoKoKnKnKoLoLrNrNpMpMmJoKkGmIlHlIlHlHmIkFkFhCf;f@e?d>b;b;a:bf;hCfAg8c7pase'U` cs w:z#r j T A$J ?P9Ro])[k)t"V'Z$a 5dsp6vPTvOQ*#y (V,L*4x&]-_k|,y? Ëm̉mGLn%4R !L@@:<>\ ZFV%W INWM^JJDB@BCBIUSRW7a nhR<=` { i+I B <:A i +^ gUMYQ Q%Z2Z4U,V,U/T)Y0W/Z4\4\6[3a9lGvXwX=S DE] fuots} 6: : YJDiM @><<<.= ="FGM ^LDA N= @nBKI EWP8C _E AoB>;E M >H HQ M m J S gV\gXcW?7 ::::>>RT;(@ "<:HC*? (> lGONU,W-T,S)S*X/Z2Z4[4Y2Y1[4\4]7a:^8\4^7_8e:d>f@h>lHrOqMkBiBclFlGlFnGnDlHnEjEhBhDiDjFjFkBmJnJoKnKnKqMoLoLoKmIoLoLqNtPrNqNqQtPsPqOpMnKnKoKnKoKnKlHlHmIkFiDi?jFkAhChDhBgBe?d>f@gAf@hCe?d?d8b;]7[6[6\4[6[4\5^7^8`9`:f;fAiAl.Vl)n tyn8sxb#JinOC[Y!ITUg$|66 _ D BA;== ><?=UD n#K,= ,> C3A C&>E)J pIQ/D ]F >]R9Z R`GT H UTuj lm L miCI@:::=<@eD B H b $E <#@< :JRCUCLOX+[3\1\1]5]6\5\5a;c;`8_8b;b;b;ba:]6]7[4[4tT;P RQV*W/U,X1X1W/X2[5^8`:b;a:bjFkAiEkAkGlInKpLnKnKoKnKnKoKnKnKnJnKpLpLpLnKoOsPsOsSsPqNnKnKlFnHnKlHlEkDhCgBf;d?e?fAf=e>d>e:f@f@d=d>i>e?d8a:\6Z3Z4Z3Y3Y3]5\4V.Y2X1Y4\4W)\+[']Z[Qg6|4p F]EZ:SUL@>IJeApOw6a$ _&M +Z1rI;Zh=Nu(1` 1'% u R$L}gfgbij~JZk/$T$II?C:SIKFJ"PwQLE?BADCCGJ9N U`\"G;?K$  :x0S @5A ID >_ }TW]gkrlVN!N!P#P#P"P#Q%W/R'R'S(Z2b:fCmI-O A IW`}^ Y [ L k S _;A>;;;= <BLG(B ;%> ;#?TLrEkPcD9U ?+M 5I RT zS)T GQ N`Q Z\VstkgbRB=J;;=?<:<>l?AL;<:<HDHJZD-> VIdIW%\/S%cAgAfAgA_0c=c6c;c>b;b;b;a:]9a:cd?d8^8]9[3Y1Y1X2X0W*HN.L KS'U+Q&Q&Q&S)U,V.V.Y2[3]6^8^6^8`9_8_9be:e?e?fAf@g@hChCiCkGkGmJmImIoKnKnKnKnKnKnKnKnKpLnKpLnKnKpLpLpLpLoLqMpLnKnEmJkGkBj@jFgBd>c=d>b=d8c u??D- "CaRHKN!R)S(R*P"Q&P#P#R&T*[2^8c>lGO?CBdhfsx @ /=9 BB:<:<;<@I::8= C6C $G=E;JNWC G <P F JT=^ ^i Uk6b :[ LO-E A ===<CH?A<::B G ? <:<AQ>A*> `F+A <hLdKeLTiEe5kB]0a=\1`>d<^-]4^0\3Z3Z4Z4X0Z3[3Y2[4X1Y2X0W1V.N'B yHM#M$N"O"M O"P#P#P#Q%T*U,V/Y1X1Z3\6\5^8^8]6]6_9`:b;d>d=b;be?c:d>d8bH?=@BDaL NS Oh  # I?C> =TC_; ? oux^0A ><4@ nA>aYvd{_MM O"P#N!MO"P#P#P#R%R'Z4c<?F<;K d srvS T: D ;:::<<;= :@A;?> E?= A>P> V<J MR\XLHCQh]}NLJzH}G`JgI[N K [< C+e AI;3J ?==AE ] @:<CM:< : A>3A T+HF>\9T(_5U"R"^:yPPT$W1NT)P T+S(V/R#S(U)U-V.T+V.T*Q$YFHN%M LMJJKM O"O"P#Q%Q$R&S)S*T*U,W0Y2Z4\5]7^8]6^7_9a:^8bh>iAj@jElEjElHkGlHnJmImJlHjFkGmDmJlFlFmImInKlHlHjFmIlHhCf@fAf;fAd=c ><0D OV*V-X2Y1Z2Y5>)%Q:Nnt'{+z?tNuRsPqNqNrOvVy\vVxYz^xUtRoKlLsTkLfAb?eE[9}Q'D:@F;!GFCB Y *XdFpAxQmSo\FoEx&  PR[:0= (=;)> tRx: {VMB+>=;%<K< r=DJTPKLMLN!LO"P&Q%Q&S(\3`9b:> @ FA ;KOvs ] A D 6.AG<;><:: =<:FB[Q_^oHwIYByI[KzKtN|DQFOHL%N%J"HIHHKJPX^QW#V HPg1F ?>D :,@ <A<> A:RZ f =A> @ECf:7B E=wKdJcJSIfIR%$D oLR$T({GO!IP&P!R&JNN S(R$T,S(MwKtEIFEJLLLLLM O"P#P#P#R&R&T)T)S(T*W.X0Z2^7]6`9_8^8`9`9be?d?hClHjFmJlHmImIj@lHkGmDiDlHlFkGjFkBjFkGlHjFlCmIiEkFiBgBiBhAhBhCgBb=b;b;^7^8]6]7^8\5\5\6[4Z4Z3Y2X1U,R'Q%P$R%Q#N%N$M#P$JLGL#L K K KJILIIJKHHHZD B?,B IN S*P#Q%R'T)P.y lH>B ?,D ?AEX 8F 1? AS uWPKI?=>?c?)=$<@IC>HqgOM nE<8= r?z?x>~?@CKOIIKJL!O$O#P#P"U*X0`8[5R? =HG D><?[cI E 0<9<=:?::<<3A <EC;ZclSMBEK"GK!JIII IIJKJGHEIJJVZol;^]H: EBSB =A;<;; L =@I R I @<=C>B><:BE <wH?8C AE $G CfAf@gCgBiDiDjEiDhDiDjEkGjFkAkGiDjEiBhDhCkFmJmInKnJmJnJmIkFmGlCiDj@iDi>gCi?ba;^7X2R'INV[!T D?:FP@L6G (CbMs[ov*f JYzLE@CBCABO> ;p?CCEFIJKMHE>";Y? ~?B@CEIKJLQzO[MQNT&R%V/X.[2]5;<;;?@>BIDDRK<::::;=@CD<2eD[I\?H PBI DGM!HL!IGDGIJIHEGDGHM~QNM"TBjFM?@@<>=SP<?@=JUE=??A"DP?K<)B @?JM+F 6? R"U]VO/N LJHJ6@ nIJIKNGH -@WDHHK JJKKJKKKM M N!LN!M O"P#P#Q%R&S(V-U+W.Y1X1Z2\6]7]7]7_8a;d8d>cg@g@fAg=gAfAiDiChAi?iCfAhChBkGnJnJnKnJmDkDlFlHiEkGiCf@hCe?d?d8e:c=b;b<`9_9^7^8`9\5Z3Z4X1Y2X1W.T+R'O#M LKJIGHGGGGEGDDEDEFBEF6@ ; ?kDFHGIIHFFHJW>NK Yr!rT#N Q%[5d=iCjEkFiCd=gA`= g? BBFIJLIILKPtT[Z\"g4g<>;E;<:=;=;<: RGfU-? O'nDGDCN"B@ M$IKO%oEM"rEN"P$JQ'FFEFDJK!M%|G`EqK.B ? @ =LQ&@B =::=;A>b<?C?? A;:DLZAV J>?F (~-dZwZKm>N=W'\ 4X JvHyfEZ2D YC EHGIIIGHHIJKLM!O#M LP#M N!O"P#P#R&R&V-U,T+W/Y2V.W0Y3[3]7]6]6_8a:be@e@fAe?gBfAe?gBe=d?gBfAfAh=hCj@lIiElHkGlHlIlHiEgBgBgAe@gBe@b eBDFFHFFGIGHEFg^x:[ rGJQ FFKW0\5Z4]5X0V-S(Q%LIFEDHNJ@ ==<#M PC< =?? EMR LPH HU(W mWNEo?9< 8> g> ACBADEEFFEEFBA<>? 0> bA ~BCFJHGFGIN#R(W*\4_5b0c:89>:::<;:@;XD hF=@ dFO"<N >Q&bCIR'M@ S*rEO#PBO#R&nFS*JLkBP%cCL$HzGjEIFB %? :DC D|d 2P9_6 j 1F jAFIK!GGHHGFGGFIIIKLN!O"M O"P#P#Q$Q%R'Q%R'R&S)U,U+V-V/V-V.Y2Z3[4_9]7^8_8b;a:be@g=fAf@gBgBfAh>j@hChCgCh>iDgBiChAiDhChBi>h=f>f>d>d>d=a;^8[6]6[4Z4Z2X0X1U-U-U-U-Q%Q%Q$P#Q&O#LJIIGEEEDDBBB@BA@BB?i@<E? w@BDFHEEGGFHEEEJMX +NECEEIKKM N MKIEGEDBP]IzJ:EK M='ASB k=?c{ ^IH_M\MYKpAd<B; Y> := ?AA@DDDEEDCDDDCB;= D> BBCAFEHHFGIKN!N!O#R(Y5]6_7M EF==?::::89::<>=>=E <SD ;K;{H4<5YTX;? O$=U+xE~HM"VBLcEU.GLO#IMA R'tFMR'O#JJ0> =wH&? I E@>+B )I %D ?<:*> ~H=@[C ?"<!L ;KOR@:8 E k q |Z   | ReIEGK JJHIIIIGFEEGFHIJJJLM M P#P#Q$P#Q%Q%Q$P#Q$R'T+U,V-V.W/X1Z4^7`9`9a;`:b;a:b;b;c:c:e@e:fAf;gBgBfAgCh>hChBf@gChCgBgBiEg*@ <aGnw":M!7A \BPAO%$= N=T)N#UDP%K@ aEHK_DS)O#TDjFCB SDuJ=D ?eFcH-A @D =!V I:< UABZ H W9GL W E@8B 0A HA IH+? OB sGH!eP`rtndeQE#FJJHIGGGGGFHIGGGFIHGHIJKLM N!M LN!O"O"O"O"P#P#P#P#S)T+V.V.Y2]6[5Z3]7]6a;a:a;bd>e>e?d>c=c=e:gc:c:d=fAge?ba;`9_9]6\6]6X1W/V.X0Y2X0W0V.U-T+V.T+S(Q$O"M JJJIHGDDDDBBCABDBC@DCeB@ABACDCBD@DDDDDDDDDADBE]aR6h]I CABDCDCDCBAAEEB[D A>.]z%G$j :DtFC@E@JNP ?G%Q $O SE@@A@B@BB@BBBCADDDDEEoA1> $?DBDIFGFFGJM N!S)S(X0W.]6 ACG :<;;::::::::<:;CC@DB?V Ih ]`E<qYJ]h#^ yJRKRFJAcHS($> R'5A YE/? O#$? R)K,DtV?AM$F > (B C@HT0H XRGM= <:BGG@B?FEX N jF=<#><@>B(B FTH|GK#J DHHLJJGHIGGFEEEGGHGFHFHGIIIJJKIKM N!M LM!M N!N!M P#M O"P#R&S)U,X0Y2\5\5[5\5^8^7\6_7`:bd?e?e?c=d>d>bABEDFGIFIKP#R'X/U+U-W. ;:=<::<:::;:::=A < > C6D 0A O%`\*W J< = B Y Q%PDVC LDqG@W*(A eK=[RL<P'+> EzUc rdY ^;I'= @D T "c 1OXE WB 9D @OH:<CLZQRaF9@ aDGB@@ @< ?AXC-D LHcAvGE~HGKIGGGFGFFEEEFGGFIIIGIHHHIIIKKHJJLLLLM M M LN!P#P#Q%T+V.Y2Z4[5[4]6\6^7]7]6]7_8_9bd;e=f;c=bd=d=d?e@e?e@d>c>e=b=><:::::;:;;:: ; ;<rK!]8Y5N2E+EU)lb~m b CEK G C.[ 9i  P #>+> ;J MPb <j =~ k H = : !E evo] ?G D [ l'g H<=;TR+M 0Z iL Q t K [ < 9? J5? JA yC=> rAdCRA DN GGLIFGGFEEEEEEHFFGFGGFFFFGFFGIIJHKIJJKKKLN!N!P#P#Q&R'S)W/W0Y2Y3[4Y2Y3Z4[4\5\6]7`9c=e?e@d>c=d8be=b;b;_8]8\7[4Y3U+S)U*X/Y2X1Y3X0Z3X1W.T+R&Q$P#P#M LJHIGFGEDECAD@BBDBECB@DAAADBBCBACBBBAAADDBCBBCCBACBDDFBA~PRLCBAAB@AAeA-C PM8bMr'j t U =9@ @BADAABB\v!o 6Z gABA@ABBEDDEBACBDCBDEHEDEC[A xAJ> BCDEGIM LO"P#V+Z3`9;<::;;:::;;:><:<:A @L(K ?;3DR&R-lNW4]?d1vsFw& v LQ b z j+ S  @9 0F`  f E gs FQ >$B C<= T NM#Yi M iKEJfBH#==,@ = <@5> <+H fHFyDKDIIGEFHEGFHGEEEEEFFHJIIEEEGGHIIGGIIJJKKKM M M O"P#Q$R&T)U-U-V.Y2[3Y3Y3Z3[4\5]7`9bd>d>fAe?hBh@i?iDd?e@d8b<^8^8^7\5Y2V.S)R&P#Q%P#T)R'S*R&R&R&R&Q%P#O"M LKIHGIHGEDC@DACAAABABCCDACAAAABABBCC@ADBAACCEDEDDEDDDDDEEDGzJKEEEBAB|J&V a`Hq$ 3> :<f>?AACABDB`V?'9 C> h>|??A?@AADDBBACEEEEEEEHFBF-@ IIJLP#Q$P#P#P$T+X/[7OP!M!4HID=:::?<=::<<A5C ?:+EW'hM!`J[9bFZ1R!V,QQ*X2`5n1#g:   ] p   BhoVDB YETC=<?>bDS(HFANF"G9F V@PPAqCU? {F)> >@ <=>4B `IQG HHCBEHEEEDDEEEEEEEGGIEGEEDDCDEDFGEHHIIIIIKIILLKO"P#P#P#R&S(T+V.V.Z3X0Z4]7]7_8bd>d>fAfAe?e@j@h@hCgBgBf@gBc=a;_8^7\6[4X1U,T*R&R&Q$R'R'Q%Q$R&P#P#P#P#O"M LKKIIGGFDDEBD@CABCBB@AAD@ABACCBEACACBB@CDAECDDDDDDDDEEEEEEEFJNEGIJOQZX ZUZi\?l^=;#?[@ @CDCADDCDO_~n L@ |>A=A@AEDDDGIGEEGEEHIIJLM!N KLJ_NX'U-X/W1T+T+U+W1\4`:@I ]NbH5F IC@:::::;<>;;;<CVLUJ=EY0`;R'kM|OQ#R&]<`=a;_:]2]3[5X;V>^?d6k4n!LsIpp%g* {+y })m ^a \#R0N,HGmDoE7? >@ :B &C $A <zZAMJQ `KUWU MR JKdDSA GE ?]B jCVA lCEEvCFDDCEDCDCDDCFGGFFEEDEEEEEEEFHHGEGGIIHJJIJLLN!P#P#Q%R'S(U+T,U+X1Y2\6^7^8`:d8d?f@fAhCf;fAhCf@f@hChAiBhAgBgBg;BD nS!T.SLmMjM;FqN]aBEML"OqR,[ +OQJ ]V KH|EHTA J _B">-@ > >@/@ 6? ,A AF HBDCDDDAAACACBDEDDDFJKLLLKHJIHFFEEEEGHGHJIJLM P#P#R&S)S(V,V-V-V-X1Z4\5]7_8`:b;bf@iAe=e@gBe=f>iBgCgH@ DDEDDBEEEFGED?P@ ;B qD?ABAAFHKP#O"MJIGFIJN!P#T)T)T)V,[6Z1Z0q\kX,Y2Y2V-X0W-[3BBEB@@:<<::?@==;=> >?'D CJE rEnBGEEBBAADAABBCABDDFIIKN!N N!LJJIGFECDEFFGGHIKLKM P#P#R&R'T*V-W.Y2X1Z3[4[4_7a;b;bbb <;<B(F B<UNdCfAY/hPmO3C hPqQZ+dEhEfAiAhBjEj@gBe@eAdD^7O$KD%@ 4L"Y ZH\Q%O MJKLN$Q+JBA =<><NIH NTOCgV6[ ySW JLO@ JFGFGUC HpCT@ = < >.@ PA uBACCBCCDADACAAAB@DFGGGIKLJJJJJGEDDEFHIIKLKKKM!M O"P#R&T*T*U-Y2Z3[5]6]6^7^7^7_8`:`9^8bb;b;^8_7\6]7]6]6]6^7_8_9^7Z4Z4Y2X1U-T*S(P#N!KIIGGGGEDDDABABAABACBDAAAACDDACAABAACCABDABBBDEEEEIHKLKKM N!P#O"LLMN!T [|Y^QLJIGKJLMeC1@ <UC zGJKFEEECEEEEEEEDABB?=@DHqINS%O%L#K!N"N!JJM MP#P#S(U-U+W.V.Z3]6_9_0h%\4X0[3Y0Z2+F =@ =<:=?>;???>:<<=<=@FLVK,D THwNmNiPU$U)fDfBhFkDg8gBkGmIlHkBe:`4Z6W.T*W:[M_Q8_S&M"OQ%U-S)Q$O$NA 4@ FA HC NB >JC NRM#IFTRQS9Y )I IJ)@JIJ GFDEGmD1? :A ? ;=#<7B JA `B {AA?>BBBBBBBDBCBDDDDEFDDEEFGDDDDEEGJLN!N!LM JLM O"P#R&S)S(V-Y2[4[4\5]6^7]6]6]6]6]6^7_9a:a;_9`9`9b;b;bc=a=c2b6^8`9`9PS;[H<;;C@<?<<=>EGH;AI;+E @K?H7CP%dO"tR#dCjJlHnI^3](^.c6qLrRrNlHk@d;Y"hW\O S%A=-QW+S)Q%R'S(R'R'oF7@ `FSCwFHMB ;HP"L"KLShIyTcS)G %I GHEJJIIFFEGEYB aB S? .? >=!=:= P= m@@AB@ACABADAABCADDDDDDDDDDCBBCDFIKLN!P#Q%Q%O#O$LM O"Q%R'S*S*U,X1W/W.Z3[5[5]6]6[4Z3]6]6[4]6^8^8`9^7^8`9b;b<;<D?:<A@> ?D =?MI_N`OfDpQlJd;X(SO`U qU!jFoKh9b1xY'MQ #= < <G> v?@AAABAC@CABCAABBAAA@A@AABCCBDFGIJKN!P#P$Q&R&R&S(T+T+V.V.X0X0W/X1W.V.X0W/Y1X1Y2Z3Z3Y3[4Y3[3[5\5[4[4\5\5]6^7_9_8^7_8]7^7]6]6\6\5]7`9_8^8`9]6\5Y2W/T+S*Q%N!O"LKJIEEEDDAABBBBDBBCAAABADA@ABADACCACBBDDDEFGGIKLLKJKM P#R)U-U.S(V#kP]GQ!V+X1]6]6\5[4X1Y2U.S)T(N^E$@EJNR&N!N!N P#N!N!M MKLJDEECDEEGGHIILM R(V/W/P#V)Z1\#iin%c)[%Z-Y0`9d8b;d=d=e?iDnJsOsOrNsPh\2RS'C@:GHA;=<?@AQN;<;? ?-H 4I.F ^1i;\1_R)B +D>LhX)h?sUtPn>j>h?rLoJh@b4dP$8D =Z)k^1\6X2X.T(S$LzJMS(P&GMB *@ >!@IL!M!MJ|T^LKII4[ ?XPFpFHIIEFEED>AC?N? R> A> %> ?#=U> |@@?@BBCDB@ACAA@AACDACADABBEDFJJKLN!Q%S(S(T+U-T+U,V.W/X/V,X/U,V-T+U-V.U+V.W/Y1W/Y2[4Y2X2Y2\6[4[4Z3\5\5]6^7\5]6Z4\5Z4]6\6\5^7^7]6]6]6^7_8]7[4Z3Y3U-T,R'Q%O"O"KJIGEEDDDCAB@@CCCAAAEAD@ABACAAC@CAAEDDDDEHJKKMLLLM N!M!L#Q$QbLxY;RP T(V/U-X._8a:a:]7[3Z4X/Z1V0PTFLE KKQ$N!N!N!LN!N!P#P#O"M LIFECDDEFHHJKJKM P#T+V.Y3Z5W/U-U-U%X\W,Y/]5a9[2c;d>iDkCe@ >DA@CPeU[ "N>?&DLMVT#>KC &E TOlU(pZ-pJzXrJk?`,j[+v\1a4Y.SO4H;E&F6V_.gfBb=^8\4X1V-QT(S-O#HiGPD7A fGrF2A mEO"LMPsJM F9I pYVRQN<S@ L!JGHHFEBABAA@?b? `> JA ,? BBF u=@AAAAAABCBABCECBBADBCBDEFFKM M M O"Q%Q$R*Q$R'U+V,V-X/]5\4_8\5Z3Y1X1W0W.W/W.V-U,V.X/U,W0X0Z3Z3[4\5\5[4Z4Y3Y2Z4X1Z3Z3]6]6\5^8]6]6^8]7^8^8^8\5]6Z4Z3X0T+T+R&Q%P#O"M M M JGEEADAEBE@BADDBA@ACACCBBBCAAEEDDDEEEGHIHJJJN"M O^N,S d>d=gBmIuRz\E =?=APA?C<@@?@N JI 7 E F @ E @@ACJPPdV$oNrOb0`/]V!BPQUgArIpLnBf>m:ho [b @hlEgDc>`:]6\5Z3Y3V.NqHXEUELMHOBLM%O"N!KLK IBPk\KW L?>;@ L KIJHHGEBCCA?A?@q?lCa@ .^ ;^ F?ABBAACADB@BB@DBCABAEEGKMN N M N O"P#Q$Q%Q%Q$T*W-Y1X0[3Z3Y0Y1Y1Y2W/W.U,Z3V-V.U,U-U.W.U,V-W/X0Z2W.W.Y2Z3Z4Y2Y2X1[4Y2Z3\5[4]6a:^8`9_8a:_8^8\6Y2Y2W/W0U-T+R'Q$O"N!P#N!M LJHEEDDEBBCBBAAACE@CCACACBABDDDEDDEEIJKJKJIP PGNXV?S IM#MLL#M N!P$Q%R&R&S)T)X/[3]4]5^7\4U.P!{HQ"S&O"O"M M N P#P#P#P#P#LM KHHGFEGFGJKP#R&S(R'T+T+S)W0U+U-W.T*S)T*S(R&R&S)R'T)R'T*T*[2]6a;iDnJrNwR3J >::<=@?=AA@??CSe {l\>==A&H YT!d2a6STQRoW&PQy\.mHxSnIf=}b4b;`:\6]7^7W(PPF qHP"T*JIMDjIQ'N!N!N!O!L LPSKBV P >XB <6? K"IJIGEEEDDBDABAB@@B?rMrevaG@BCAACACDABDABACDEGKJLN!P#N!N!O"N!P#P#P#P#R&S'T*W,X/[3\4Y0Z1\4X/W.Y0[2Y1W.W/X1V-U+W/X0V.T+S(T*T*T+S(S)T*U-U+W/W0W/X/V.X1Y0Y2[4[5]6\5]6]6]6Z4Y2Y1V-W/S)Q%Q%R&R&Q%N!LN!LGGEECAAB@BDADAAACAABBABBCDEEEEEEEEEGGIMMP|KKQHL"O#M M M M M MP#P#Q%Q#P#Q%Q#R&R%S'W-V-R'ONP#P#P#N!LMM O"P#Q'P$P#P#O"LIGEEEFFHFJJM P#P$P#Q%R&R%U,T*S)S(T*T*S)R'S(V-Y2Z4\4[4[3[4]7b;jEnKrNtPFE@ ?cJ@?B>A>BNZ QN G BFOQV^Y(N MOROSQ1SM E Lc|o%jQj>fAh@f>d>a:`9_3T"U)S(W0O"LO!YFbKT+P$P#O#N!O#O!NVK?W+U @~E9? YB 6? IIKIEECEADACDBBBAABAAMPVCBBB@CACCAAADDABACFGGJLN JLKLLM N!P"Q&R&Q$Q#S(T)^6_7^6^6]7\5[4^6`8b6^6^6a5]5]5X/Z3Y2Z4Z4\4V,V.T*V-V.W.V.W/V.W/X1X0V.Y1Z2X1[5Y3Z4]6[5[4]7Z2X2V.T*R'Q%P#P#O"LKKHGGEDDDBCBBABC@CACACBDD@CBDDEEGEEEGIIIIINIHJLM!LLN!P#P#P#P#Q(Q&Q&P%P#P#P#O"O"P#P#R&R&R'Q#T&Q'Q&Q&O"M O"N!O"P#P$Q'P#P#P#O"O"JIIJJLLLKJJM N!N!N!N!Q%R'R'O#O#Q$Q%P#Q#T*Z2]7b6\5]6`9a:c=hCmIrNxXxY@;:<>>@=? Cdc.I ?B H L dw n|0s?gMUZ!$K 0X O? ?5Lp_4oKe?gY"^2e9}f5dX#fW)WN #O Xl p3g>j/mDj@jFj@hAhAd9`5_<\6V,OS'K>`MQ*R&Q$P#N!N!M JQM {S3S ;@ DK> F ;=> L"JIFEDD@C@B@CBDCBABADDHCMDBCDACBAACBBAAAEDEEGEIIIKKJIIIKN!P#P#P"R&T*V,Z2_7`8c;e9e9`8c;c;c;c8^7]6_7\3X0\3`7e<_7X/X/W0X/[3[4Z2W-V/Y1X0V.X1X1Z2[3[3Z3X0X0Y2U-V-U,U-T)R&P#O"M LIJEEDDDCDBAAACBAB@DBCBBDBAADABBCEEEEEEEEGIIFFGGGIKLLM N!O"Q&P#R(S(Q$O"P#O N N O"P"P#P#P#R'R'P#P#Q&R&Q&P$P%P#Q$R&U-U-V-V/U.U/R)Q&O"Q$O"P#P#P#N!LLLM M LLIO"P#P#O"LO"M N!P#R'Z2\5\5[4a:c?YPP?>U['I@ @ER V LH BJ`Gqe{*ez.dc()l I(L \[#e9a1e4f]+=K#NC`CK#D @fjZef)b4mIlImIkAnJkFkFiEe@b<_3X.V+_G<eIT)R&Q%R&Q%P#P#N!N!N!PgTHC fII> IF? "=M? L"HHEDD@BACBBAABAADCBBBDDDCEDBCABADC@BBBDADDDEEFHIJJIIHHHKMP#R(U+X/Y/[3_7d=d=b:^7^7_:]6^8`9`8b:f=`6]5Z2\4]6W-U*R'R'T*U,X.X/Y1V-U+V-V.V.T*S)T*T*R&R'V-V,X0X0T+T*Q$P#KIIIFHEECECAABABA@BABDACCBAAABAAB@BADDEEEEEEEEEFHGIIIIIIKM M N!N!P#P#N!O"P#T*S+P#N!N!P#Q$R&S*R)T-S+T,R)R)S+S-T-T-S*S)T*X0W.U,S'U-U-T*T*T,T*T,R&P#N!N!N!M O"O"N!P#P#Q%P#O"O"P#P#P#Q%R'X0]6]6a:h>lHnHmItP|\gee<:;BBA=>E$R6UG @ D DJ0\a ] PI I H_xs2zXkA-G0M^U /ND l  x y$ P z2uFqLpMoKnJkGmIkGkGkGj@hCg7cD[,HG.A LV/T*S*R'R'R'Q%P#P#P#N#O]E8L XBO$VA WB =eBJGEEDADACAB@CCCCCBBBCCAAABBABABACBABAA@ACACDDCEEGIIIIIFHKM Q'S+U+Y1[4^7`:a:c=c;a9]6\5\4`8b:b:b;^7^6[6_8]5]5[3Z1\4]5b:b7_8\4Z2Y0[3Y0Y0U*U+R&Q%P#Q%P#S)S(R&R&Q%O#N!IIHGEEEDDAABDCBBCBEAAAABC@DACDBABCBBDBDDEEEDDFFEFFJHIGGFGKILJN!O"O"N!P"R*\4W/S)P#P#N!Q$Q#R&W.W.W0X2Z2Z5Y2[5Y3W/V-V-V,V0X1U+T*R'T)R'T)T*W0W.W/T+T*Q%P#O"O"N!O"P#M N!LN!LP#Q%R'T*U-X1[4c:hCoLxVc}_ekjhk?=NLEB>N J I @ +LBEB9 ; :ta6VW#yX~Wтe߄lyQrQkn?€_zMk:ud?g&s7W}EcNamGx[rRsOvRrOqMpLoKnJnKlHnJmIkGhE^/[MsCHGEEEDDCABACABAA@CAABAAABCAAABCCABADADCDAB@DABADDCEEIIM N P#R*Z1Y/[3[3[3X/X0Y0U.U-V-\3a:d7gBgAf?`9]7Y0W/Y1^7b:c;g?A '=HEGEEEDDCDCCABACCBBACBDBADBBBDBBACACBBBAAABCAABDBBDDEIM N!P#P#S(W,\3_7`8[4]5Y2V,U*R&T+X1^9f?eChBgAe?g;e:_7_6c)a1{?G]ņY݇f}^o=f:n^0BWQ[vSz]xWxVxWwUuQrOqMpMrNsOqNpLoKoLi@a4:I{R%c?b=ba5Y1P#LLLKLKJIHGFFGEEDDE@ACBAADABCABAECAACABCBDACDABEACDA@BCCBDEFGGGIHIIJM M P#P#P%P%P#P#S+T-T+X1V,T+V-Z1Z1V-Y1_6b:d8b;]9^6Z4]6[3\8`:a;d9_9\5Z4U-W.V-R'Q$P#P#LKIHJLP#P#Q$Q%Q$R&Q%S)S)W.[4bd7)N)ZtE}\{_z^xXvRuQuQrSsOrNoLnKnKpMoKmKh;['MI\7dEb<`:b;a:`9^7Z3W/V.W/S,aBuTTM!N!M"LpF[EeCG3= HKIKGFFGIFFDDDADAAACBCCBCCAAABBBEACABBCCCAECACBDCDBADBDDDEILP%O"P#P#P#P#T)^7`8b:d8c;d=iFmNmJlHdDaQ%_CR.L|`{'MBBC1N(Udi^ -G D I]!Lk(Nb og,kb/RZ#6^s;[}c{^yZwTuTuUqQlKpLlIiCiEnJmJlIkK]0T!Z,iFgEh=fAd>b;_8`9^8]7]6]6Z4{,FP`P&OP$Q"LhFtEJG?KHIKKJJIJIHGEDBAADAB@BBCBBDBCCDEECCDBCADADAEDBBACAAABABCADEFIJO"P"N!P#T+\4^6b:f@c;b:cgAf@hCd=iDrOrOrOrNqNsQnNnNoKkGiCgAf?d=`9Z4Z1X/X1X1Z2[4U+T,S'P#N!KJIJIHFEEEDDDABCBBBADAADACE@ACAAABBCCBBBAABADDDFFHGIIJJJJM O"P#P#P#R%U+U*W.W.V,Z2V+T*V.W.\4\5Y0X0X/W0W0X/V.Y1Y2V.T+T+R'R'T*T*R'S)Q%R'Q%R'P#N!KHFEGGKLKKN!P#R'W.\4a:_8d>kFpLxW}bjkgnpyyvxpopT ]WMCD#H)Gf·gub<|i?]a.GED @B 8LP^$%y D< > } -s=sRۄ[~Y|TvLpCg1gY%FMSMtT`5b9f@kGmIjDf;b5f=kAnKiEkFf@e=c=bil6I7VZ^+rbUM _`&c7n4o5`8nY+RU"8N"O ,Tg^'a+c;^7^8Z0b9f?f>fAjFlGkGlIiEk@kEhAh>boHrNyZfklsv~~}~痃瘆痃~ ~(bKAF Sj(IV+:N$Ŋm{Tsl?b}We>A*I;S i^5j=oNe:%P`qr1uJvOvGo<~_0_Y#je#i9qLp[pZmRhCbA]4_5^8a;bnMqLpLsKuMsRtUnLiGc9d;h9hCe@b<`:^8Z4Z4]6b;gjCnEoMvS{]egklonx{斁瘄虈虇瘆瘆瘄P>ARV(vR.FvRɈj׊r|҈h~1PEC)JI*Uu`6e3lDsJyVz[{^~[xWtQoLnKkGlHkGjFgCc>`:[6\5Y3Z3[4a;d>e@iEgBjCjEnKkDf@d=a:bgBfAf;b<_9_8\6]7^7]7_9bg=e?d>f@e:d9bkFhCbiElClHkGmDhDj@iDhAiDe@h>hCf@e:gBd?f@d?d8b;c:c:b;a:`:`5S=E -E X1OU.V/U.U-W0V.X1V-Y2[4_7c>hCkAlGgAhEe>f@h?h@e9_8]4[1W-R(P#LGDCBBACABBAAAACAAABAABB@B@CCBBACAA@C@CBDFHGHHIJKKM MMM LM LKJKIKKIKJKJIIIGGHEEEEGGEEEEEGGEEEEDDEDCCBAACAAAABCBACABDEEEEEEEEEEEEDEEGGIHJIIHHGHILKKKJJJJLO"P#P#O"N!KJIFIIILLM N!N!M KN!P#S)R'Q%LLKM N!O"Q$Q%Q&Q%P#R&R&Q%S)T)S)S'U,W0Y0_7lHsQyW|_lmouyy~痂痂瘄睌瞎睍鞐枌睌(N B,M.KtU>Mqf8vTvKrq2{M׊]m~ZtK|R B0MWV(r\2de?f@e@e@f>jEkEg@iDlHhChCjEiEhCiEmCh=d=d:b;d:U#EJUK]7X)Z4[3^7^7_8^7]6]6]6c`:X1T*S(R%S(Q&P#O"IDB@CBAABCADBBACBBBDAADADDACABBBDCDAABBDEEGGIIIJIIJIJIJIKIIJIIJIKKIIIIHIGFIFGHFFIHGGEEEEEEEEDDDBAABBBCBDAAACCCEDDEEDEEEDDEFGFHIHIGFHIIIGHGIGIHKKJIFJJLO"LLKIIGHILJJLKJJJLM O"P#O"N!N!M N!N!O"S(T*S)S*U,T*T*T+U,U+T)T*V.Y2\4_8f;oLwW|_hlouuvy}斁痃瘄蚊益益蛊蛊DAHS#_Y1zd=qRbZ-yUs:|N]cge{\rAj+SC )I6H9JGO ]U(xZ+]0a7f8e>d7e=f=b C'J ?To].`5f&F"F 6M QMuRQT#Y(b@bhCkFlIoLoLrNuQvRwTvUvUuSwUz\{\~bcge~dbdfed~^}_~^}`xVwVxVyZrK$H rQx]$nOxWvRwVtPtPuQvTxXwTxXyZ|b~a|^xWuQrNrNmImMlGhCe?a9]6Y0Y0V,S(U*R'P#O"JJFFEFFFEGFFEECCA@DBB@D@ABCABCBACCDDADDDGEGGGGEEEGGEFFGEGEEEEEGGEGGGFHFFEDDDDDCCDDCCDACAEEBBDBDBADC?ECCBADDDEEGHLKLLLKLKKLM MMN N!LJIKMMKN!MN!M N!N!P#R)U+X3Y/[4\6Y.V/Y1e?hCh?d;Z1V.R%P#N M LLKN!O"P#P#P#Q&W-\4a:b8`9`8\3Y4]6_7_7b:b;a:`6[2]8b:f@f@gAhBf@gAhBkFoLsOz[y[~[chiklorsssuwCMtZ*HIqN^1qNqOoNpPrOvRtPuQxX|\|^}bd|^ib;b:^8_7_7^6V-T)T*S(P'P#N!LJGFHHGGFHEEECBBBBABBBACABBBAABBBBAAEDDDCCEEFFGEEGEEEEEEEEEEEDCCDEEEEEDCDAC@ADABACBEDADDBCEDAEDADDD@DDDDEEFILKLKLKKLKJMLM P#M N!O"LJIIIKKKN!N!O"O$Q%S,S+]4]5diAhCkGpLuQsQwV~be~`bihjklkkiklighehf~b{]}Yz[xYvSwSqN*H b/tQkDxXvSuQsOsOtQsStTsOpLqMqMqNpMmIkFmHmHhChCf@f@d>b;b:b:`9_7_7^6[3V-U,T*Q'P#M LIGHFFFEEEECDACABDAADBBCBACBCCABBBB@ACDCCDEDDDEFEEEDDCCCCDDBDCCEDCCDCDBADBDAAAADABACDACCCCCEEBEEDBDEDEEFGHIKKIIKKJJLMMMN!N N!P#M M JHHFGHM N!P%P#U/X1Y0]7a9b:jDmHrPpNsQsQqMoKqMqNpLoJlHiCb:\6S,S*Q'O"N!P$W-X/Y3bd=c;d>a:]6^9_:a9a:_8\3Z2V.X/W/R'P#Q#MJHHGFFFFEEEECEDBBBADAADABCDBB@DADBBBBAAABCAADDDDDDDDDDAABCAADABBAA@AAD@ADBBBCBABBCABBCABAAABADABDDDDEGEGGHFHIIJILLIJM MO"M N!O"P#P"MN!LLLLIJP$S-U1R*Z1diEoMuTwWsQpNlHnJnLoKtR{_|`tTlHmIrNrPrPz[f|_vVwUvSxXwXxWz[|]biijkkhhjlloUNjOmR nRi>uUvTvTtStSvRtPvSzXyX|ZzZzZab{ZZbX/CQ> ?;M`/rLuYuWoGhAlEj?mJqNuQzZz\ahklmorrrrurolljhgb{[{]zUxXyZwUwWwVvUrJ6KMWwUqNvVvRuPuUuRrPnNmIf@d=`9^8`8]7Z4X0U,X2Y4Y4]6]6\5]6^7_8`9]5\3Y2Y1T*U+S(P#KIFEEEEEEEEDDDCCBCCCB@CA@AC@AACBAD?BCCCDCBCADBAAAAABABABBABAB@ABAADBCBCAADABAAAADCAADACABBBDCBCABDDDEEEGIGHHIHIHJIJJLMLP#P$P$M MLLLJLLO"LLN!T-Y3[5^7a9ckGlHh@gBkFqMx[|a~chklpmgd}ctSlJmKoKjEiDe>h?lHmInJlLnJwYuTqQpLrNuQwVx[vWvVuTrSwW}`~`{_gggghhikjg~`{\bgghikjikjhjfgfafD\0jGkAqMqNqMrOwTxVxVtQtQvRvRwTwTyY}]|\yZ|_efW*nC!I k^. =E iZ%g:e9i;rM{V}`~ddffijlmqprpokjh~bzVvTsOpMnJhCkDhCe?f<_;`:^7[3X0BExQR,[6[3\7]6_9a:_gAiHqMxY|ahfflnkllkf}]{^{^y\xYqMpPmImMlGkFqMrQy^}abgkkkmjhikkklpqstttxtllkklomolspklnrtpljh~`{^zTxUvTd9rRvRvRwTwTsSsSuQuQtRwVvSwVxVxVxVxVwTwTyYyWyZ^[Y.|_IW|WLW"'P B 3Pb`*oInAvNV߅eiiilmomli`yYwUsOqMi?gBf;d>c=bKKN!LKKN!LLM N!P$O"P$O"LJLIIIKM O"P#Q%Q$R&P#P#P#O"LIHEDCECCCDDCCDDDBBBAAABDBDBCCDAABBACADBCCAADCAADABCCACABBBBB@CABBCACBACAAAADCDBABDBBBBCAADBADBBDBCBCBDACABBBAABBCCAB@CCCABADDDDEEGEDFHLO"Q'R(T,W,`9kAkFpLvUz]grywywzvvxzxxqljg}by[y^~afeffiortprqrsqtwx{~y||{ᒁ~唀zxxwxxtxvxuxwxsvuolhb|^wWwUz\{]lBxTxXy[y[yYxXxYxXxXyZy[z^y[y[y[y[xVyZyZyW|^bfnD܂imAvg6ׄiVZ'3T*O}g=2Pf`/vQ|Wڂdjjllmomj~`{ZuRqMmIjFgBe@b;]6\5Y2W.W0U+U,V-S(Q$Q$xD=@ ILJHGEEEGFGHIIFHGGHGIIJJJLLLLJHGHEFDDDAACBABCAADDBBDACDBACABBAABCBABCAAAA@BDADAAAB@DBBBBDABADDB@CCABABDABBABCBBBABBBCBBBBDACCACAA@BBBAABBBACBBAABBABBBBBBDAACCDDDEDDDHKP#W3\5c8a>c*,D,mmdd__bakkPM H !hife``bb__SQ@7$C6&`^jia`cca`HE*$7/Ev͢J}Jddb`^^\\DD&"&(Muh5*4b4RQPQTVKJ9}9#"& IR`fZ\$<82X2ORAB;;55+*'%/,IHa_hHri*7*ihVV=>''%$?9WL`_ZΊzhhii^^DE%%"!=9Z_dx_^|yɼaaiibbaaNN3363OL]~]\~UUll``___`WXDBA;MRVXl-/1Ae?hh__^_]]`_]\OGE9DnJdCSP!J!SSZZ^^^^]__^`YW`G>K{Vyr%,&=%==@@RS\\\]]]_[_Z\NGxc8f6B@7&2&aaCC46HIUW[[^\_Y^X_Tsq  ON<>UU^]`\dbr?`W]]^^HH9746LMddcbdd``_]_^bbYW:855QPdcggjj.F8&6&jj^_RQAA34;=RRb`b__^^^^Za```FF33HJ_`ba2X2FoFlj_]YXKL;;54BCWZ_e_k^z^e_YcaVU22**;=Uc`{__`dmSG# p 8Y8kl`a_]^^\\XVJH7448Gj^aa\N9v'$MKef`_^^]\^^\]UWC\4[=fU^\I13-cTT`a`a^^]]^`\fZcMv9q2@I@-0LSkg < IJ[Z`b_^^_]\][\`Ut@$*,0Mom'`&>@ST_^___\_k^_ZA-L|ߚ  98ABLLWW_^_[_q``W52WҀޥ-85-J-TSGIDEJJYV````ZD%CtܤUUeeONBAB?QJbld]D),FЂݠn&$ghdeUVHFA>MW^^G''IՌ޶#"6W6mm`a\\SPGECmG:%#Oѐ,63TTji`^a_afK]+z$hښƸ5B>ggcc`\d[^c7 kذʽAPM4V4mlc_b\ZQAbl׮DWPUUkhg_Zb9h Mۣ߰Pe`kjhaS]6r#)Uۨb|u%C%khLD:8vߟ`~PE:'2X t%#j +b(Z.64@V¥5B=:6(ʜDJH"VɽCTU@z(d] 2"blobby-1.0rc3/data/gf2x/ball12.bmp0000644000175000017500000000341612042452401020075 0ustar danielknobedanielknobeBM6(  \[][Z\|{}wuxfcfqoqihi^]^XWXywx\ZZ|zzxvv~~igf`_^yxw ZZW$$#ppoddc\\[hif`a^ [^[z}zcecvxvopohih  []]TUUwxxoppklp--.zz|<<=gghXXY}}}|||yyyuuurrrmmmjjjfffeeedddcccbbb```___YYYWWWOOOJJJ<[< &*&kd*Fw*?)t!w!>tZw!KKlϞ Kў6wwM6V̌XtwKʌ6tٟbrVVY6ZGLM_.UsW@MTTVƋ՞w7 lXZ8lSŋ)t ڵSST^ןQ3-.ftQ,Ə PnoQTTP+TmHNQSRToSS0ѭRblobby-1.0rc3/data/gf2x/font25.bmp0000644000175000017500000000337012042452401020134 0ustar danielknobedanielknobeBM6( :@_6A^'.=$banmJJg-0<  JE_ނqRGc% DZ|\E5NhTsȖۭma;kɔuރ>< Ag|@^'c  aEeK߅*%&!"}[$JKck.v| 1!2u@^c; =*)$ %  aX s vt xl n\^C E/ 0\9^pFҫ{~J J23  ad@./8Yvd<ܥ`c78 IqO6PP $$jg'eg88<$ň N:K΃ͧVk:kA&>\+[0"km44fjuQsB3@ ** 3)g i88 UvrodjDE@4;ó?H?}3L$L #|ksvphy`NUC Zlx|ʽvbyJyO4L0"-# <27`bXpglcbTOG>ђѝڮֲ̬˼|mphfr_h`qiyowhqW I? ˻$! 4LRF%֧ @=ik5I"J^3\T7ƅ 4 06T R};|ZQDv q% 0#&@[]o;hO6 k]8 'Jgf:"4_>۵X B J*ߖ"  `xd#d# h2c7ؘ   k߀a |BdR j8SQҞ,6-_( K&B)=tǥC^$S|@TI Me' }nu(3 h  333?blobby-1.0rc3/data/gf2x/ball14.bmp0000644000175000017500000000341612042452401020077 0ustar danielknobedanielknobeBM6( pmpfdf|z|NMNihi`^_ywxjffgeeonnxvu|{zrqpaa_  $%$z|zoqo~~yzyhihefeadb  vwwfggeffcefbcdUVX{|~z{} ffi__aXXZvvxeegxxynnodde}}}|||xxxuuurrrpppkkkeeedddcccbbb```___]]]ZZZWWWTTTQQQKKK===///]\\vw\\2$"L-2 ~g\L,J4*2wf((+0(II(*fhv^Ԃ*Зfgn^GF*(f,gv ~]$'¾'G4n\P'*+<i/Q''(~,ddz;~ccd&Fogabacdd6 CE>cĿCEcd{ BAacBad`CaEBblobby-1.0rc3/data/gf2x/font27.bmp0000644000175000017500000000337012042452401020136 0ustar danielknobedanielknobeBM6( O RH  > zWz TR 6]  vyE2 j 02g/`Xe- m  %'<>GNl88  y &)GI-HdE~=m !Z  "&AA%>#*`!O#0 K  35&XfGg&n:  )-3+ hy 62   36LJQGVn o  f`i% : /  xJNIM   3ae/6   ( _`[^+1  ~ N * +psRW2: *#/8C8G#7 #W. DEX_8B(4~&+39(3 h  333blobby-1.0rc3/data/gf2x/ball15.bmp0000644000175000017500000000341612042452401020100 0ustar danielknobedanielknobeBM6( YX[fdhedfcbd{z|wvx`^a|z|pnpgeg~}~zwyiffdaaYWWxvv~~`__fedjiggge{{zppoddc  _a_npnegeghgWXWz|{ {}}nooghhfggeffabbWYZ_`c//0==>HHIxxy__`VVW|||yyyxxxuuurrrooommmjjjhhheeedddccc]]]ZZZTTTPPP### 1[a~ J~=+H\Iaxt~#$"Skij?V~ ihz|1>f#ԏԂ՜kV9/ ԂhjV.̛jjXRfӜjSjVɚқj؄=e΅j#~8iV666de8e h#V0Q68SjNNdngSNN4P88e 3N4P@L2P6XGN4LKNLL624242M4blobby-1.0rc3/data/gf2x/font28.bmp0000644000175000017500000000337012042452401020137 0ustar danielknobedanielknobeBM6( - C FJK(F0<9.<&5(  i!  0Gawya D1"kK+ "0@"s4b:We[Jz5W5&b8 3(./% $c`DVbQq3I 5$U( e_^RA.'L <>*tPdC_-6|`V;&  ^AAtp~ueS:<//u-c,V-  D-ZA F)"mMJjcn`^GM+?0 \$ ) T+UFH#   %  2Z%?IO%N+' 3Z} 0*+ 0#/ (x%)4?'H5n3+$+"-&.6:POWVTREA:3?9.)q41&R'7/4$=:Qdrzgn}y_j[%-1E,]'W,Qj(T&1_9RQ"- z(3 h  333?blobby-1.0rc3/data/gf2x/ball16.bmp0000644000175000017500000000341612042452401020101 0ustar danielknobedanielknobeBM6(  `_aeceigigeg|{|qpqlkldbcXVWgeeIHHwvvkjjhgg{yxihf ddbqqp``___^cda "lmkegd oqojljTUTCDCpqpghgXYX $%%bddvxxeggjkk^`aklmnnqeegPPQ~~vvwiijggh__`~~~yyyxxxsssooonnnhhhgggeeecccbbb]]]ZZZXXXWWWNNN<<</// $]c[PA MO&Z'#qspNcLKKOpqNO'LIII(܍&]ڤIo4 OVѣKo@ 5΋5УԊKqaa/OrM46`/ll4sO:HGIvO'[G..uIox6a_iȇmKI'øi.ug%i-Gl`pDCigFG LjICD-.Hϣ+)D)iH D)CDdd))DddT)eB))C/blobby-1.0rc3/data/gf2x/font29.bmp0000644000175000017500000000337012042452401020140 0ustar danielknobedanielknobeBM6( 3}Y 7 n \ ;61 EXWAAz  P22kk&' JssJKoQ00mm!#6  llLMa}GGlo(+! 2##uvKLG _`lm.0  2##BDIuweh'*q>&&rv5;5 ^bQXeo=Ack+) *NY.=U #-%; p! "6 > e+Y!DOZ7&b 0[ M E BN.Y'P 0iX O K:5 :H.[SNN[ggW|7_> +$ H"Ebelry}{uxqg`xXm@V';"t&+     0 $;)-K38N47\:?a9?c07GN>GBblobby-1.0rc3/data/gf2x/sch14.bmp0000644000175000017500000000024612042452401017740 0ustar danielknobedanielknobeBM>(3 h  333blobby-1.0rc3/data/gf2x/sch15.bmp0000644000175000017500000000024612042452401017741 0ustar danielknobedanielknobeBM>(3 h  333?blobby-1.0rc3/data/gf2x/pfeil_oben.bmp0000644000175000017500000000316612042452401021124 0ustar danielknobedanielknobeBMv6(  GEH)*L *Zdvu0$&td\Z.t5y.V,u/-./ ...&,$$%% $% *%% ,* %&, % #  /'+(- '( / '()- !  '- !  +- " ''()--  #" ' '" " " " blobby-1.0rc3/data/gf2x/schball.bmp0000644000175000017500000000013212042452401020420 0ustar danielknobedanielknobeBMZ>( 333??blobby-1.0rc3/data/gf2x/pfeil_unten.bmp0000644000175000017500000000316612042452401021332 0ustar danielknobedanielknobeBMv6(  & F  N N6(N. .N(mYZ%&\D( ?DDBB77'+ggSS##bF  >>CCEEBNOkkHH)) y:XHHHHe11 ;$$vvXX>>55))55k!!+GGJJv;;8$$ff^^;;..#k66eeLL--''""::CC~>>T22 W22gg77\N22wwppNNBBPPQQKK^88-  ]]AA>((ooeedd\\rFFH33 ~??PP  )ό}}nnTTY==#:""aa((M+ ֛勋pplKK:..WWJJ >⼼鼼YOOT//``$$! ppĥrdd411 aaGG%%BֿTNN LLkk77v,, $!!ʹʷ~vv$""7$$ttcc>>4QLLٱDAAjjjjMM}xxA//蝝𔔿jj:%%533qnn䕕jHH~~~~A77}}mUU mkkUUU blobby-1.0rc3/data/gf2x/font31.bmp0000644000175000017500000000337012042452401020131 0ustar danielknobedanielknobeBM6( :<:KMKcfbz~z OVO+.+^i^}}KRKaraZiZmmg}g190kh̜nm@Q@xw֖|zPmP#."}|癈҇[[6Q7   Ҍ勆eeFwF"7#  }{yymmTU3h4  ,eb~kkOP\^@B$O&3][kkUU  yzNQ15*QLJLI87=`>kp4;j% )p(=:+)%%`cW](J1/)$M*=,vz?H) &** 3 EoIpy(7v& ,)  akUa*X# ,) lxAQ% AO($ y.E5q1F $4.{-84E:WEq'@$ <$cbRQ20(Ot_l>0 C)rpLNTdm,MJ <_= (`. 1>.oBS <7 n7 ""3)]Ar  KP rL B- - H8>!Pd rlM=  -<- Z@b iNT,. /2 " s #G^Jv1? +#0 B&BWE3_/?)0 i #7-VG7(T!R27"  X -7S=ZD0 $O Y;>48# /&x1FHY*7mC -C&&b?@@C=? #o+?N7H?4 % j:;BBS:<?H$0*/PQe.L'c t/177J1[]-/ #G#+&X]fx1G'K~ #')DJK_`58$&#'8 lqbr2C4"%2mmde;<: +|]j1=($+99Zhh22!"O@AZO[(1{ $/ppSS))""nVX}=E %n  CO(7zz::&&hhgk+1z  btHX"u!!/ab-- km_c%" n]r(.XHHp~~==E"stWY(+ 1-2M  abeg$$x -LN"#NVV88blobby-1.0rc3/data/gf2x/font33.bmp0000644000175000017500000000337012042452401020133 0ustar danielknobedanielknobeBM6( "+'"7#)-. !:&+j,(e(9]</!,3"))&$20?bE7  ?  )*(%CF$h)58 K  '~)::$>' [bkl,* ^  *Z/,'=gD_}g>;  h   56A[C݄QO s & n  -,#)%ԋWT   / &_*!'$~΂VS 8f"  #&  +7.}VU ,!"($w~KJ  Q% *&Za33 ( IV%'$Z6[Xy.8;IsYar#-  .\Rh.k@o rgz. o!"w||#w-+Oi$Y 2L"wny'12q9Z % C/M@}+uBc3 (NsS | xKJv!$ΈJ1R@Ze5Ablobby-1.0rc3/data/gf2x/font38.bmp0000644000175000017500000000337012042452401020140 0ustar danielknobedanielknobeBM6( H E 5 x   >  /   '# \1,#40 2,p3/H ?:"c  PM%!!t  VS2/+(  ZXPM:9[ZqoJI0AXA׌kj;R; % doczB2Cʵ̸~H11fWgڞ\|-/0 xBՋCFÇ8z#%q|:A CdDgqG blobby-1.0rc3/data/gf2x/font39.bmp0000644000175000017500000000337012042452401020141 0ustar danielknobedanielknobeBM6( Zm $+& } 6   _\IDA'wurp=7$";  feNL2/, on]Y:j8  ƘԎYX*6*?E?ɨXaX y|yuiv5*52-2vcCe  ~vς? Qkޞ7,-[1]^7<=N)ON.KM895!M OMN'KMor K NGJ '߳' (]a!ۢ$ 5.&r+wv+|]W@qh2nC0F{ blobby-1.0rc3/data/gf2x/bvende.bmp0000644000175000017500000035437012042452401020273 0ustar danielknobedanielknobeBM6(,+*6ȮŮدYP`ׯƮI=Jǜ 7+7دyexՠjXi&%xmƜٯӱŜ$}gg귶v`XTE?kYRI<6`PH)'&QON4+&m_pxi|=4,ջ '$ͳĤhhexyh897  $AFA*5+еdyiϬs|ڽVa\&%Unl+76wAQQd7$x-$ $ϼܳ n渄Ć}vք kI_>$P"$&>&bwDyk:vb3-L : vzz-}-ီ; &-$ðu%ٙ|ûx%"||ðx|ÞxuÇ %%Ӱ%%%ٰӰJ%JఒJuuٰxu臞ٱxJهjI$ ߮$-󽐄̷I&ŋŧmِ wz-π& &ķ-&Ţ;-- Ï!Śvٝ-܄Őv}}z&}}$ʽe$-̮& &ڮvŗ> yyv 턐y&&$zz -kA&}m;- yA$yIR$IrL!|O%uxPuO6xha6|hu?%|h"|?%|h6"6%uh?uuJ%"xO%|u%xO%u|u6xO%||O6xa|h?|_!!k:&zb k:-}k-yA$r> yR3vHR rO}zR9:RO k:-yA$7-}k3 >&_:-I&}_:-e-kA&n- vA$n-zR3IvI-y_3-yI _3-y_kA-}k-yA$}kH@$}k3 @$k:b& vA&p3 }x} --{;Dh%ahӱxJ% Jhx͇%=Ox͇ 9ЇxӇ "=?|ӱ% 6x|͞P 6hxuP6huaxӇ"|u &h՗ $_eϢp$_ʖv$RP}vI AB}")2պ DI& 67ԗv̆-3>Ζke-$3̏k}&΂xԀ- x" -x; -_I$&Ξ_̋}&$̇Az-Rz Pz=- :}D>B;Çͻsu2OBO PBӞ?BهQ#BùO#)ٞqO2DDO9JBB#هپq8#"Ш#ʻ|IŸv8P-ųrOP%|ABÇA#ʯ2# ٰ#auսo_=uBh:ޞRs:Ňmu!2ڸu!#ůk8;ÐrOŽ|A-͆ BBηķԯse--$P$Pv)+8g#2xuJ/dg 4O1N'h=d##1N?uu+1g/gs2g8du21#Otgs1#E|NN11L|1#68Ng#28ggNg)s{|so)1E|Lvu##9w>s1EsB8#t L+8t1Csp111duԃ9|1d|-+\u2)\21#o+Ow" *8#su##o/g|xo2 'dsx*O\*B9x17w~##|;3ttb&hyH$$y&_y$$y}-bz&$MN2^01)t((g0(<2u|Jt,0#/)x|/fND(t%u"uN((Nu+Nftf\sB/f#}#ff/q#Dxs/(_AeGGT#CGSTUoJ2GSTNCGST([W#oGTG2EGT#>[W,C9[W#[WUNBCSf##CS^P[W<_[UG]D^SQQSTCOH[UHeMA&v&3_v$}[WGoGT1|[W<1STU11SW<-$[WU/tSW#STO&CGNyGTYN6[WY^ST-yBSG#);$-v_}}5$-&Ԅk9 s&#SfNGWq[W#S^gGW#[W´uNS^gGW[WB|6qSTUymm$-mzz-$7_z$&P}&GS`u}&%pGSN4[U$>mvMH&& nyI$IH&IsSGo-xy~k--- z3Ry-$Aw>7|u%O|=%au?x|%au?hu%6u"6h|%?|SQOx%6xa|6h"a|6P|GU%SWf2|u%Ox"|u%?xu"u|Ohu9GW<||?h| ևuGUGU} &Ե-$$gS& nŮy-ۊŚvږy}$e $Ȯ3-$ܧر-ږ7vŝvGSb}- b- -$Ų1Sx rvjh$e_:ݬ>Šuٙh=ÇÞxuaӇÇ uӇ%|ְ%%ȇӻh%? Ӈȹx)Oه||P͇u |s u͇| u9%ÓӤBu͒O |u|h|փ}r Մ =-D$ϗR&|$̐m|3;ʑrk:}xv? ԂvD-Ԭ:γzx3-̝}Ըw:IՂvP byM~ }M &356$HR$̖b_*5z{_ mj--3-kr} 33hOu|""%%xOxu%aO|||%LOP||%OOxx"%OO|xu%%J?ux|%"=?x|%?=xh"?6xh"6%xhuu%6|h|uu6uOhuu6uhPuu%%hR!y$$_Az $$HAIy-& jA-y}&A:vv $- AR}v &:A yky->A}kn-IA-kk -MA7kk 33_Re} 33rR;}>3}_b}-$-_b}z-$-AA!uw &-@Ay--kAy-& bA>vz$IAHvv - :&βx:-eļ_?h=9ȇ=2ۇ ӇuuӦO"Ò|o?xO% |aO%"Jus'ӇӇ)uӪuͪhJÙh?6uh?u?τ oIϧD5$ʸyR7 $ŝv_u$Iwk&%Ձ  ͢ ٖn%pxm3brx3>vĆ:&y:-#zPėy̱A&ċzʳ_&b_$IhI9rÖI:_a"6BhÇ%D͇Çت͇#xuhB|Ç aO͇%OÇ%ػ%OÇرÇ۞|໱us|پuuB|ÇO aÇ ?=͇?- oz{Rvk@k33O̡$2&}_ڢMRHMA> $sH&2 <鍆}xz_Pv _A&=:vz I}$--vvMIm%%xx|''x||u%*)'Jxx|%"%=xx)'=%%||x*6||ux|%'s|x|%%)'=|x)'=|sJ)'%uuxJu'+|x|u'"LsJD%Dsa|*)xxD=*)|x{L'-&pvx$"bwurz$ %Jxyv&*"$-yz|xJ*-$$pxwv%$$xx=hx---$=|!vy7u%*|uvv;>u}>-vx=u'%zI-rwu$$ 5&|x{bu**-ev}~u*M&-vvu~-*&CWW$-&|vvj$-&$yvz&&-$-yvz-$>v vvM& $$}bv}J2|2\1NDJ`8\1"h+g#ѥxE#xuN1<1t1"N1o9#N1إE;q‰\|J#N|d## x1#оq2%#)\'%)#NѦBw)ƉEt1ʓdN2sxq# tƦ=4f/#ϢJ+g+"g1L#gx9sg<#N2xD Pqo5qQ#)3-))#2s:&tJ2t?1^WYNkhya}r }I)H)$&&zu#(åd:khJBDOGVV,#WVYNsuOGV(NWVTBOGVWVTUTOCTVWЇ|E#UTTY$ &"|GST`GUT,#GST`GUTxQGST/|rzzvm& $&vpwx%"%%x|GS/SNU0CUUsPGS1S\U^UU%GS\SNU^CU(/Px|GWY(DGGGUsGW,fg/ST[W <1STSu[W f[WUN[W $-@&vy I$&@@ϼR>B:ÇOBOSSfGWSTDSSgfGWGSW<#SSf0GWQSYN#?־GTY#OP"fSf^SU\[WYCCSTه[Wfg[W [WYfSTCGoUWNJӇfSYGCSVѾCG<\CUWNqS\2[WYSTO2CSG{ x͇|Ӈ %%D|)%u֤" J|uuxxuGSCqhu||uxӇ%P)[W,/J2%P |͇|hxӇ |aasGT# u=|Ӈ%BS,/q[U<% 4%%6{>vI̝I vuzSGBx[WJ$-I-$>": vyv my&}bՄ$$H&u$7[W ̏$y&Sf| Hv}Hz @- $zŖu LͰ͇%ȰuӤ6ٻ%ٱ|ä %"ðx)䇈Ssuهu⇰%GU2SWͰuȪO%|Òh" ||DGW<|%|GUGU<ưs% șaĂŚrڔ$}Đj;GUЋ 〧GU--Įὢ$&ħv̽:-ųyŽv)ܑųv 킧Ր  $ν-GU0ƽjηbڽ-ܳ}ĽvGSJvIԋ܂M$ @6rAuևP|BuӇ?|BP"6BO|6%OO|J=hO uaӇJ)aE2aև "|O9xևuBOx"h6GS\GS#BOO"POOPӇJOOOӇӇDGSOBuӇD6"hB"NSJGSQsBJ֞J?O?֞"3R3~b>AAԀb>"ROgS">_O>~GS~O:P O:|H A:~_RApA"HA}RI6}OJe6-hP@7hAGS8xA-ԂM>yA7nMgS9}A=nA?u!hAJhA-a;vv$xJ9 )uxL96 J֒xu?6=Ӟx|J?%%xhO%ӇxaO*Ӈxuh% aP xuP|uhh%au|x%%?|x%"6u| "6u| 6|9 6Ӓx? 6J|w>&3Lzv@7-$zma;xph!|wh> -uvk> $ krv}$>Hvr$-ayw$-Hy  -6bz >}L7 7vyԃ7$7yvԂ@$3vH&bkJ-bx{a&wz_ --ヌðu#ۇñ#xu2h?͇9s?=s 9=2ÇങﴇÇxouهپxB"xO JOȇB%Ӱ| ȇȻ|ٻ|B|JuOHR3H̽a3  ٯzŝåku_k6R-̀7B$̷$%ķ Ŭݴxų_ ń _3:5s3 㽵 ̻e}r$-% u|ӱu uxӓuuxxȞ|%xux %%h%%=u %9u֙| Ӈ%|x xxxJ||x%=Px"%6 % J͇%J xu͒ JJ sx| =JDx|u >ဖ$ $w}& &$ &5җ& Dvbݐy vyv $}v $~&p&$&~-$-y& yԳ&vvε}& mv}&b$$7-&3p$y- ~vy v_$-7|xh%u%|x6%|J%"ux?%|J%"|x?%|a%xh%J|%%xx%Jx6%""|x=ax6%"|xJx?%"uxJxP%x|xh%%|||x?%u||x6%u"u|ux?%uu%u|xO%u%|u"xh%u%xh%=|6%~w7-z7$}vb-}H$-Mvm&pH$- vk&yk$$ vk&bkA$ }y;Mv@$- yev_$-- yn5v_$--}yk$--&}zv3$5& v@$5& }}v@$;&vk$e}$&vr$5}$$}v>z>$M}vH-z7$ }vH-v_$-vyzk$&HԷ6Pͻ|6 xOٱ|=xs|ȇ |QuӇ"4 %æ?ͻu% DOxhOhÇ"uuh͇Ӈ"qJӻ?sͻ6hDÙu6hÞ|"OQ|͇s# oB>ʧ~$O:p-R?Ő_ŝ_ykՁxς 7H ̼=3ʙ_ij>ܐ_ď- ܁R}5R}s &Ԥ?$̘A--AI ŝAy݂P$~ͰhJ%ðxD?%ñx=%ÇJ ͇9Ͱ|åP ûxØO% xxs%هx|o͇|寇ͯۘuêhͻ%Dêx6?Òx6%|?" uDߢ& ̮Խ5$ Űv̽hH$Ţv_D$Ňxk $ʄՊ ٖnbϷ}3bʽv3&Hv3- y@ Ϸ ս}ʻ@ &ŐvŬ_$yŝ_-}łwI-Ah=a"JOhau?OuPu?O|P|%Ox?huOh?hLOh?Ou?h6O|6OuOh"Pu%OhuPuOhJP|?OuJJ|"6O|6JJ%Oh?|J%Ox6aJOh=Puuu?Ou%Ou6O3A}Մ@_}-Am 7Az-A_Մ-Am:R Rx3Aw -b3A}I>b&Am;-H-A_-> b}:R-I5:_H;:A} >&Az 7b-Ak7H @k7_ Ak$A7_-Am5_}-:__zAk vJuxP|%xux %uuӤ %В|͙x xxȒx axx% P|x%%Ju%%)uu %Ӥx uxD=xPuJx|%=x% %|% Ӓu;v)vvy :vv -$uv3 $}& -$ ӗz ݊vb yv̺}$m}$-b$>-z& |vԢy kv̢v @ʝv$-v&$}̇ٓ|"u J%"%=ٰ͇ PàٻxDۇٱxh uhه%%Ç9 ͻتP|huuxs|Ӟx"|% û%ȇ?%ÆŚ_ۊ7Ŗ|vz-̀$ 5Է-$-ʷ3żn7Mų}ڻvMڳvI >7ν$&Ϸ3bʽvJmykyy>I$-&$xh"uxJ%|hux|%%uxJ||"%%hP||%%uhP"xxL%%"xO|x|ևuOux|ևuO6ux|%uO6xx%P6xx%=6|xu|%"6%ux||u"6|hauu%L6Lxhu|%xhLuxu"%%|OJuxu"%%%xO=xu"%uh>vy$_Ayv& _Ayv$H:-zv$uO$z $A$vy&->$yv}-73vk}5->$-}ke}e-;3&rn$$k_zz$$r_}z&$y_ y-$z_5y$-R3vv&&RAvy&-H3 yv-&H3-}vx)a?Ӈu "uB? ֙xJBO"hJOO͞xhO% ͇xxO%͇|B2%􇱙|Q9)ֱs?%|͒O4%|xO6"|OO͇|Ї? u2%ӤhB%͒hB6xx֞O%$̀vκ_?$π_9 _B B: ϳhH$-̆}ճkH3 }k_;3̄v|΂: y= e=% wj3|A-&&vyΎA&z̀R -O9- h4  &h $"9###a;PhBO62su%QӇuӻهaӦquû=sÙu6OÞ|%OJ2J"###|٥LQ|ٴhOh"AHx6@o غ؂榘)jŴkż%9_Ţ36R}-7Bb>Q#ӮhڤR A_-9_&:)ीԽH&%և=B s| xusx%|uhx%|Ph%%u|%%%au% әx9" uu͒x? J|x ?%|xO%%Ӈ?% Ds""xsu xO| %xOx%L|_$&-m$&~& &Ӗz  ̊y> vy3b$v&H$}-P-$} a-$  Ґ}pyyukzv}v$-;΂v$->w$-$ -}բv  Ԗvxh%=x""ah%PxL"ux%6xuJx%6x|"Jx=%hx"|=%hx=u"|a%Oxu"||%?xP%Ju"u|%6xP%Ju"u%%xh%a|"u%xx%=|u%hx%6|uuuax6%au=x?%xx"xO%P|%xa%OxJ"xx%6x=u|%6xh%ev73vn-z>$kv&-zI$_v&&zb$@v@& &>vH$n&7vH$H}$rm$7} -mr$-}M -nv33y-bv7$y} ey>$bz- zHyz|$HJ-$@H$&3b$"}5wk$}I&mr&Ӱ%|Ȱx%%Òxäx)uٓxuu u"%ͥ)L%پ6઒L%6٪xٰ6)x|=螒| u|% %%D %xÙa%xÒxهhx""u } -z $|&y$HvŎ>yv}}}} -&& $ʚyս5-ږrϽ; ڎrʸvwv $̷ $ܽ@$ڼH㷗}- 3vųI:͇Ͱho?ÇûhhOȇhxBӇÇ|hQӇ|#"#9ͻO)|oO6 uhBOÇ|Qa͇|ÇӥB͇OӰB%êOٰO9"ÓOۻOusOus# هQ&νշB3ʽ-ż}ηO3$ų}ŷA3յ_2ڀ@ŵk#7q7ξ3Խx>ʙAraŖA zŎAzʀA sIs?-ʖϓA& -ʏxA&΀ʏAI&Ղ΀hOaxxu%"OOuxu%6?ux|=%?u"hxL=%?uhx=?=hhu66Ph||a6?=O||a%6JO||a%6Ohu|%OOuxu%"?Oux|%6Oux|%4OJx|%%OPxxu%OOah|%6Pah|J=6?Js|>&:kve-:> _kj-3I_kk"-37H_} $7>A~}& -Amy&&Ak}y& ARy&3Ay}-3Rky-&A}kv-:>mrI-:Hnk};3: >_zI $@_rI&3 _m}-3R_}wBhxOh%|Ps %"PsӇȰÒx"ۓåx|sx6L|O% |?%u9%͇Ӫxo ӇäxB|ÓxO|uO uO% ""B%هӰ !ȏ}Լxy>}r7$ӽk3?$y-9& }uĊyΘkvk$}>k$Ղa$  դ ▂Τx j}ʬv3;-ŝv3-ʂ$&x͇)hÇ6 hه%%s͇ %u"侓 aٻu|uh|O=B=%2 %aÇ||͇hxa?ȇ P6Ӈ%ӻ="Pw;Ÿ⃃vڳڄ6vnM3b$-& ؐ>⮀y{zym& _-@-$%٢-}z yŝz_vڂ@}ν:-Ž$%%|||xP%%u||xa%L%%|uuxa%%%uxx%|%%uLxx%%|6%"uu|xP|6%uJ|xJ%|J %ux%xx%"xx"|x6%"%"|x|x6%""%xxux=%uxuxh%uxuxx%%%%|u|x6%%||xx%%%|||xP%%%u|uxa%"%uxx6u~$$;vy-;}&$;yv5}-$- z5$$-z yk$&--y zm$&- vz}y&$ -yyv>$ $vvm& $-}vb&&&}vy&-&$}yv-&-$-}vn--$-I vb}I$-Ivp-j}$$vy }&$e-yv}&$ - }vh xBӇ|B)և9%|q9"Ӈӱ?%s?ӇaOOu hO|hOux͇xBhӇ|Q9""o?s"sOhӇ|? h6և|OuO9͇uOh2͇qQӇ6B%և͓6B6ӱu=O?ٞJ͇H_? k4bwBԸ B& O:ϗ_ ճ_:R_3yRm6Rn9h6D&h6u-A-έ-ޒA-΂b7{A΂ՀH}R}h}h6A-uuJ)򻇪և=Ͱuï%Jðx6äxa?%suo%|䠥؇ӯua%þsÙh%OÒh%P|%"|)؇ ê%uêx6ÒxP 6|wnj} n ůmսI_ŗI$kv$IvJν @Խ}&3vųH-kŵu-b-ͯԷ$%ևu?B4=Ba=|Bh 6|uOh6ևOh6O"PO%ODJB%OBJsx99 Ӈsx"BOu hOu"uhOP"uxO%ևOs%Ӈ|OB ֞u9B| JӞ|O| =Ӡ|O|%6և|Os33_x&: x&?-|x&9@x}ORպky?A-zy3R& 7A&}IO& Ok$ՑA_3Ԃak 3ԀH_3-ՂIR37 MR|3J-|36 |r&: Ձr%au%hxu%Pu%Oxuu"%6u%?x||u%u6xx"u|%%u%hx"u|%uJ%Ox"u|%%OxJx|%L%?x|x|"%%6h|x|%6h|xx"%P|axu"%P|=x|%6|x|u%%|J6h|u6aJ%hxu%|u%hxu"u%Ju%?x||u"%%?x|"||&7-3k}--$kvjy-$_v5b& $7veIv &:r5-yz3yrv $m-b $>k$&7-}-7yյ&&}5kvԡ&e!_vԂ}$I$Az -5$@-ߔ&3}--kv~z_yyͪs% û٪x%Þ٪xuÙx||h ͇%h%Ӟ%s%"Ȫ%ت6"ðxPٱxx|ٞ|x هx%􇰰%P%uӰP؞uÒh|ux|u6%y&$$Śz$ŗyvJzvv̀v-Ⴗ &-̷$ -ŷԮ$ ̄η7Mڵŷvvb ł7$ $I$νjڮz vڗb?ӻBOȻu6sO|=xB|ÇxuӇ%%9? sOhxBhÇuQ|x͇|uͯ6 sͻØ?O9ñÞ?hٞÇ)h|͇Ls#ÇQ%B$-R3ݗbź-_'mĄ_}km9yx9$} =$H @$|R̵H3̊R̂nyR ΂Մ }k }&s &?--ϒA&;-AwIAyb΂R%"6Jxxu?6"6=|x|P6=uxx|6%%"ux||O%"%%"xxuO6%|xuO?"%|xuuh?"%%"|xuhO%%%"ux|xh%%"au|hJ%J||h=%Ja|xP%%J|uxx"%%=Lxx?%"=LxxuP%6JxxP6"6=|x|P6%uxx|O7&$vyzR&-$vz_& $-mvzkR $-Ikr_$&bv_ &$ bv_-$byv-$>b}v}$-5Ivy3&>v}:$3vvH$ 35yvH& $-}v}k:$&v}m:-&$ v}zk&$ ky_5-$mrkaBO=OOPLOBOÇ?QB͇ه9#͇QhoQOB|ÇDOO͇PBO"=O͇BӇ͇ӾQQӇuBuL|OaLÇ| Os66͇O6DӇB=ӇD #ЇB}2_OkBR_OA;_:OH>B-2ʱ2aā2RʀA7!ς@A7bO@>B2h 2OIʱ_Ob_AaM_AxØuxaa% hJ%%u u 龜ê|êx"hْxPx%%9|9'઒u۪x=sxȾ=Oa 4=a*6Lxuwz-%vw&*v-$m!۳! ۖ!٪{zxze xb$"kb$b$ٸ&= }Էy̷ kyŽy @ųv- :I-&3|"hx%6hu"Pxa%O|JxP%Ox"xx%?x=%"|x%?xJ%xx?hau|hhև|J%hx%%"|J%Ox6'toEt9'="BsB'Du=xh9)|xx9))uh+txx*6xJ%"|B+stB+B|ohBx)xx'9x6*ցp&@vH*=!:ha'|J:xy$$|$_r*%"ߔexx9+)ubwP'au!Hwa'Dv}$7--vv$7y5-yv$3yM- zv-3k-}v>$_n$-vH$_v$$vm&Hv7$z&Rv>$-}}-@) xu|%xou" %)%?% ВPx xOxȠ xBx͇|BuӇ%uouN11#2s4 tqs͞Jﴦ1s|o#sq###Nso2o'tBѨodѥо<\1tttxq1 {)'s-8aQ+Вho2g1tsuDzx|ox} $J  $a $R&$Ry }_yh y -- ?- u|2Çx#"֪#""Ȫ"4 ðxsO ùxhOӒxQ=٠xƨ#©G(0<ɍ811qljCBɌEg´2<<ۇ#N¨tN10ǥh\0ƍש[(gǴgχ#fɨٷƌ\ŸaɌ1+N^¨1)эۆ$-&&~wv bՖv%"x%uӇٯxsuÇxxJOJO%B%ÇȻ%P͇û| JoˣZZccZXXXY,^^VFX^VlXW1ٴUZFKXY(]VlXY(0YcccZXlXc (ےh`TlXYgqScFK`WF.fcZXl.TF^VlX/qWFXY SZXf^Vl˩Ђvv_ń A& 6&& u$̽>z }yuJͪ| ےJٙ|xٓx|||u%% %6઒Ȼ% ٘?xٹqSWU[^]]]][WZXX`^lXY(`^lX1]UZK (g^lX0SUS]]]]UcXX˅,魍ْ|q]ZXTg]SZFXYԳ$@&}γ H v}̝ b6|O%|h6|h%Jh?ua=xO%L|=xO%"|P|O6Lua|O6"uu"|O?u=u"hO%uP%uL`^^]]CgqqqQ\]WlXf\`clT0\`cX#thE`]SZXfEB`c.C^^^^`\8g]TXXYU(fC8)"s`VXcW(#Q`]UZKXTS/Q\WXS(C][^fCgg`\]Vlc(<`cXf#P\WX,Ns"d^lX z:& v> zA$yk_3 b_3-}_:--zkA&-zevA$;- }R3Mb$ yk%B?Ӱxu%OOäx%OBÓxÇ ?Qu͇ ֠=ͻ?ӥsû9?ȯOñ%O]][f/Ouo\[lf\Wl<\WX?y;9|J:ղ̸; :ϼ_nʳ:A|Ԃ-:RʄxqP ûxOO%|OB6Çhۙh۞ٴh"uhOJhs?`[Ug ?\Wl,(\SF<\SlY<\]`TXN\Sl^`[Vgsd`cXfO7\[lXN\`^0`WlXN\]l. \]co2\^lX\SlG Q]Xl(1`VXˈ\SF(LJd]XX<`VXC`U綅N\^W\SlXE\[XUO$}~_$pj$ ΄òԯڢΰwI}Ůk%-5Ţk:-ł3-&xu%?|6h|=xJ%Ox%hu%Ox"%au%Ox"u"%au%Oxu"uJ|%?h|JuJ|%OxJu""6Oxa|"Q`SU/8sEqg]lKYf\^l.Sf\^lXY<2B`[WG`]XXY(G]qg^l`WZVS0Ctu"g[lX,(s>E]X.V<\]W`]Z.Ts`cl (<'q`cX0`]V,\SX/<`VF\]ZX gs\TXW<]ZXSD`TX (t\[X(N]ZlY+9`TXN|\[Z(C`TFFF.XVVX`]ZXY,sq]ZXY1΄v&-$e- $-$; $jޢv nvM vje$-xsÇxְ%هȻ%? =O谞Jx?|Ӟxq|Ç x#ȇJ)ƾ/0,]VFg`VXf<`VFG`WXˌ\`ZXN`VX`TfgJ\WlX¨+%`TX0`^ZN`cXC<\WF<`VXV `\UXYf<`V\UX fQ]XX<`V,\U.,fQ]WC\[VVTWWWWWWWVlX``VXVo`Vs ž&9-Ţ&î: łڗRŀ̂ڐP}}s-&-uxև JBuxu) "J9x)9=֒x%??Ӓxuu666|h|= %4|h|a uhussu %Bsg W^^VcG2\WXG<\Wl(/g`TXfg\[XX ^<\Wl<\U.WUD\[l.Tfgt|6\UX`SZf#\[XN\[FXC\Wlc`2g^lc<\WlY ,#s\^ll 8q`clYN\WXYS\^lX (Ndd`cTf\`[^``````\\`WXC\WXY/\WlW:kԂ;-:kmԄI-3rwՄ}3&kh%3hnI 3"%||POP%u|POJ%%uJOa??uOO=?"uOO%?J"uu?Ou?aOuO?"|OaOO"hh9EgXXZcTS^^W,NО\[XZSf\[lXN]VXo`VXGf\[FY<\]lXTU00<^lT<#Ӟ\^lX,`UX#E`VX,N\]X\[FXO> Ֆrر%oӻu%uͻӇ|#û|hñu%OÓ%Ou %Bu"%2Ͱه u|ɅXZcVWS^]]^fEg]X(\]lG`]c0\g^lXY\]Xf<\TX(<`^Z#Du#|q]Z `WFVg^X<`cK`]lX0s`VX,\]Z<<`VK,#`WFXg]ZlN<`VV<#`WXU\SN/\^XNg]X.X U\]l0|Ľns}Ąn_΂- A- 6- -}ڄyz%ͪx% ➇äx "uÓxaux a |%P%u%9 %ÇӪ ٪|ؙ=fWTW[^]]``\gstt8`cXN`cl(f`^ZX̖vb΂v&7$-$; I5$  vz "u|6%|x6uuP%Jx?uu"a%xOu%xO%u|L%%xO%J|u%|h6|u%|x?%||"%hO%xxuxO9^[^]``\gtB%ax?#QQ`TX gg`TX(`^ZcGNd]ZZ,g`TX#2\^XZY^]UV,gt9uuH\WX(^]VY01s|E]Z#`U.U`TXY<#-\SXYQ\[ZXXT0`][clXU`]UZ#D\[Zl.W]<][clXW0]UZo\^ZcYUf]UVGs\^VZXXYC/]VX@ʳvġ7%v̀b ς-3ղ̬>$ϼx}ʢ_$kŖkm}y=QOȾuûOOپPuOB?ÇOQs͇Ç#2#ȇ#O֦"ӻ?BهBOO͇PBþOÇg`SG2˕V\]\[FX\^TDʹBH`T\]cTNƦDg`]SS[]]]C1LO=g]c]^Zͼo]T\]Z\]Vf)#Դ`TYB\``E`]^]]]]o\]^^]][g_\`g2`]^]]]]g\]^^]][8#Ӧ`][SS]]]AA)ńhR2 xuJ%%6x|aJ%%LPh|||%%Oxu|%Ox|%%%?xuux|%%?h|x|%%6Ouxx%%O||x"%?t`W((Bg\\\xy Q\\3asg\\dJ "8g\\ds5 &7uBg\\\ED%3Id`\Bg\\\\`^[SU()*H-ky$--Rv}$&:rz&$:r}z&$-:_}-$ 3Rzzy- $Ayv--:mv-&3PuOxauaP a%B͇J%BӇә|J ͙x hu Ȟxh%uxO%Ӈ%Ps\UX(f0^VFX)ot21Q`cX<&&-&ΖyΏav>΃zs\[f<#Iv@-I&:--3$՗-j$Ϣv" | ̵y_} ΂y@R$ Hxⓒ⇾xu%uÇ%?%?⪓x?x|oxÇ |P͇aӼg]XX (]WZ筍Ng\TlYuxu= |"û %%\[^TlX,«\]`^Tl#xڐyϡ&y$-$Ş$y̽ĀmłԀy|΄\U t b-$)$ٮ@_zhvjve--߮ &|h)|? "u|D|9  |s6%֒xus6Ӟxh6|xx? uxxO" x|h |||s"u%u\[XZXW^^WV('##\SXV0NoDxh uP Jxg U01o|L|9%" aB%\]^[VXT06|D\]^WF. $$eb՝vH$IԀvH7\[cW2P*-y&5Iu$--v:$$v}ԋR &y}}k&zՂm7&-}w7$H$uxOO%uha%aO6ha"B?h"OOOuӇOOxOJӇP?|OևӇJ9uOӇ9=OOև9=DPD\]TZXZZVTS]^U#)6]Vls\^Xc0BBDuuևӇOB|]VlEB"us BOusu"D?d`]ScOPhag\]UlX dB@%k_ ?? bOb>:nO@:O:7-RA}>&RA};!HALbMbPE]UfO:puOR_PA_;_A_IIA_mIA>RP:7_OeJ@kAL@ "|"hØa%hÓ%hx%B|ﰇ﯇ê|!g`]^[S[]]]]Eٙ|]V8`VTO%a7#]VYЪְ ۥx͹s\`^T$rM$|D Ի ۙʻ }Ŭm -kŢm Rŀ$@ʂ- ⤂ή)Ůy9%Bȇx%#"x s͇Ox O||x6OL%O͇%OӇu%B"ևӓusu ͠)stg\\`gdq9h6u\^%B\^Cq"O6s""B\^0N͇Jqu"Ӈhu B|D\\`[Eh|uE\`^fu :h-3s-$B-̎Dv- !΂=v" bՄ@r@@k$3ճx-3&6|v-aϖv! R̃v Az-̄vA-Մ&A--:$ %h}&}hy u|u%%axJx|%%6x|uxx%6xJxx"%%xa|xu"%%|x|x|"%%uxux|%|%=x|u%|%%xxu%%|6%x|37@%|||u%%>3axu~u%6x|||"%%x|u"%%xx"ux|%%|x=uxu%={>>||%=xJ||">%%xa|xu$&zk&mv$$bb$bv}-$eb$Ivz-$ b$&vv$}3$yv-&$mv}-$bv} --$>v}}-;$-vy$$vm-zv &$vr-v &$y-Mvz-$Iy7-v$-zH&vv$&yb&mv&$>&nv&$b͇ͪB6Lðh?"ñxx9"ux2"Ӈx 6ӻ? ͻ|?uͻxOa u|s||%ȇ =%͇? 9%Ù=h%xhh|||u 䇞L2%!β!$ּIJbh$ܸ-Ŗʚ_ڝ&ĄĖr}>Ĕ &e-Լ&ֹ-ԧ-ʚ3&̬vHܐ@y܋Hĵyza}6&Ģ̚A$-ďʗ_&}ĐR e&τykQ?ê%BO٪h?xÇ6us͇#u% %ͪsð6ٯOÙ%ðO|OuB#|Ç ##͇ӥQu%ͯB |%ٻOÒxÇ?x͇9xÇ&hʷ 3RŽ}3kh}&nBՏqϴP ʴAη&ŘAݽy-aAv >BkI :sI:hb3ܷ_ŝ%RońxuO?6|xuOO%%%Ph|hO%h|hOu%a|aOa"6""=||hu6%"=auxa=6"axh6JxhJ?%%u|x?66u|xuP6%"ux|OO%x|hO"%%|xaOJ%%||uhJ%%Jxuhh%%Jxuhh%|ux|=?%Pu|h 3$-H}}kn337pkp73 &IՂvzH:&>vyM@-7zv@--7}v@>&$v}R@-$-}v}m_$&m}n_M$- bye_e$$Iykj&$Hmrb-$- -brk3--bzk 3->}v3&&e}vH7$huӇOhBOOs9O?%B"͇%QL Ӈ#suهu2Ou=uOau͇uO=uӇ%O=ևB6Ӈ%2"su͇ huB|ӇO|Bx OOh͇? ?sև)?s"qx͇suQx"IOrݖjAkHe:hH :h>3B-:Bv Dr_6k A@_R7x&A--:?s}h}̺} RyAnAx-bγAxÒxÒ)%x%%ذ%JJ٪xٞ|xu ||%a%  %񪒇xͻuxûx)x%=x %% uuͻxپ%{淐Ȅي$vI $-&$&շ-&̷ٚyŷvrŢv$}ŀbĄ-$$⮂ήIݮvڳ 蝆veIځv; -5΂$ -ux6xO Ӈ%xB J96xB'6uP%hs6%"ӓaxO J͞| DhO aӇu xOu|u xO =|ux|)|6PLx?a%Ӡ֞x?h%|P hx6 |h ax9ӇuJxB%u6=x?%J|h%"|u|O% |uJ |_:zIՄjmrpIv?nu ՝P$- _$I>k3>7&Ցy$ yHԀv> y_y=&yx%z|&mk&-n:$}-":$յ}-vR}Iv_yk-}"}3$}-e:&-|xӇ a2| Q6u 26s" B6hȓ%O9O|6OB|hu uss| ussu"%ss %ӥO| %ODx|uOx|և Ox͇ BB"|%B|?|?x|%OxxӁ":mh& vs҄ sb-h@$&_R}&mAv- kAv& IOv B3D:--P: -->:vΔM;:kk΂ 6kτ kՂ $h$ȇs#ۇև#)"ٰӾ#BþB|sqBohB|2ۻ֙""Ça#2ֻaôB 6äsO |ØO |uBӞÇȰ֥# ð|ͰBJ%ñ|ðO%ðOP?>ŵx>> Ԯo3 ʽxDAyJA$zH9&nq)įPŢř_ -nŗ_3&b}3 ߽}HԄ)-Ůk4q6Bև"BuBևJ QsuȪ|9a2Ou69Oh6oOOh%6OsuӇ%B=suu 2Ousx9OBx?Pxև%?O͇9Ps͇Q|Ӈ6 Bx| =Ox|%OhȞx6?O} 3s | B| Bp?_k:_k 3h_$=_&?bx}p RzIA}΂&>&A|΄}>-A5 -:sh}} OϞz|x|%?L%=||x|%6J%=||xx%4J?xuuhx9+OJauxxD4O Pxx?O 9s4hxOa=|Jx|P|JxxJ?hJ**xxuhhu%%%hxuhhh'"uxhx*%=x|xx*|uxxL%4%a|xxu%6%u||x|%6%|uxx%%u%xx"=|"%%LLxx7k$&yv -b&$ }v-H-$v} kz$->v}kv$&y}bre$&-}yv$$&Iyvy-$ &-yvy$- &}vv$I&&yve$7&$}v}I-$5}v}>$-vz Hn$-vv7pM$& v;p&$ -nvIr-$%D ?ۻ??|٤D#sgtǴ#s#Bq9Ы<$uk$eyjyyj܃v|Mm$J>-u$H̸bАH } kʂԂvw}b̸&|&-:$-٧_ _y}ko=ê%B?٪xO2sq\‰q<t #2<­<<Œg+'ƫgѨ8Bذsg1Bx)t" uNNo2Jt'9 ƨ9?ÒxٻJ?xuxÇ "շ-&}η&$ŗyIJv&yųv&ʂő̈́-$@Ϯ$&7ʷvų& ņ:ڬvŔ-܂)ڝvʄM$--$w̖I $vʐy$kzvP"OOD6"ӠPOOaOӇPuOOBB#ffGS(f^0NgR3k5R:m>HA kHbA> }knA@-b R:I-b AAM> bM_A>}_A>7&yA&@7nR>H7R@>:_R pA-r_ A3p_A7_;R:xӻD=4=sx|J6BOho Y˶,GY0/<#YYUY YSg(CE/UYS9%2s˅0/QꅶYf<#(f׉tй0(fEs|6P| Px?quQx&?mճ&ArΖv&:_̡v&3=v$|bb>_}̝$7"_ʔn$%_΂$ -AԄ& ? Rԗz  Az̗}&&Aʐv&A|}v-36usO uhB%fX.XYYXX Y(&$"- Ŭ̪-ڢŰvڀœv3}bw3Mʂ :$=&Ϯ &ʮvŬvR}ŝy_ - _$ ; _LJ 6xx||%|?h ||?BN/UZcZZZllXZYSc [VF.fɥ?UVccZXlXG VlFXSVFY1^VlXY0^VXW(\TlF,C9Os8UZFKlV f#)s0cccZXXX,(^VlXY0qD\WFXfN*?xP|u%6xa||%%xxJx%|h%9xuև%hk$Iy&Iv$&vz &-v7$yv--}>$_y-&I$_vI -b$Hvb -}$7vy- &$vv-$kr-M}&$kv&} >$HvH-$7v@$m}$3vI$bz&vb$Iy-&pr$Hv}&bv3$vy &-z73vy|x|9L|x|6<(WWU[]]]]^WcX(^T^l,(¥xg[S^]]][TVSSTTY]^l쟶gg^l.\^lXY/][TXB%Dq]UZK \[US]]]]UcXXf`^lfts]c.NtL|% J"%"u֙D% %x|֓h% xx͓x-zz΀v  }Ԅv-- &-$&Ώ՗v-$΀ϢvI΂-Hv- Jބv -󀐗&-$$$Ϣ7$ &vΊI &vvb  yzw -Հ-b-$u͇s#u#ֻ<[[^]]`Ngo\`SV^TX`cYUB`][ffC``]]]^W`cYɍ`cXY`cXYŒ]^TXfǴȾ#BۦB#`]SZXC\^^^fэ\]TXX``cZY鉦\WX Νvb7$3΂vԂ>$ kՄJ& |L& }>-՞rΐn-_vv$&_}v$ >z- :)}-  a ?ϗv"Phu%?uPh|Oh"PO|8]^S o)P|"OO2\`SFW\SlXY<2D\]cc0f<#g\]ZKT\SlXY8\SlZf#\UlZVYU]]W^9BOh9P a\][^`TFZg`[VU0=q`cX,0\SFXY<\sLE]XXf#dhu6Ouax|Oh%uh|Oa"%Lhu?h%=ևx|?R-&}y3A}-&zHAn3M}-Rz &I}Ak$7}Hk&3 Hk}$:Hx-:uIh-3J xv %PkvI%RbyA- Ԃy&A&yAx&&MAh& 6h&M՝}=R}%;%uL|N]SVgƒx)\]Z\^ls`VZ,/<`cK`^l.2g^l/\^lS][VXYtDB`[T]XX S`W Bg[l0`^lf«2`c.Yux仒uBx۾x͇6 xÇ6%x!&&& &z-z ؐv}Ÿ rź}&m&$J $e߮ n㷐zڷymڗv _u$>$--> bve% ͇ͪx%Ç"êxؒ6"\]TY\s\`V<]Z/\]cXXX /<`V`]Z(#]ZfN]c.^]WZ qٰ͙Q`U\UXXY,<`TX˶2o2`cX]ZY(`TlS/໒hhxh|u "a!$-ŷ̮h$& Ţħv-łŗv݂ŏ eʂ--e$՚-Ӣ&$ϗv7̖vv_ ݡyyk y &7$& :$$ʏ_͆$zʐk&}vv%uO6"x|%%h?x|uA\]cXfPBg`<<<\WFXG<`Vlf2B\]STVcccUN]TFS``V.(1#`VXCN`SlXV^[TY<8hOBxD`WZW\`ZXfC`Tlc,N6\WX,`VlY,08?\UlYG/DO?hPևL%|O?xhӇ%uO?xh"J6xBJu=?-z_>I:&kH:$k_ 7$rRI$rR}I$yR>}M7R:}&&_A}y-_A}m -kA-rM;-}A&rb-A3r_  H3r_ @3 v_ H3-}_> I:&}_@ 3$_R}xӻs=ÙxhO% uxP\]ZFxׅ f]UFXN\W.Y˅-ʸvĞH$&vw_$ Ĕvzx&aπu-| -@ʚy3$@Őv7:z73h͇BOÇ?9OJg]ZF,IAsb:-@ʽ3)Ÿw ڴkDkA>kA5AIOλ}s}AMŇbAeA3J-A%?xuxx"%%%|u|x%%%3g`clY,02)%\]SUUS]]^VCtg]XlXY(0fU $& ;vyn$$>vy$$-rv}&$ -mvy$$@v}yz$&$7vzy} $&3v}}y&&$ryv-&$Hyv}$-$>z}v}- $7zM5vy- $$zvv-$Inv}-$--mvn-$--Hv}n$&>vyeu)x)|6xu):q`TXX˅o6q\\``\\\`^GE`Tl.XW]UFXffŒ`TN]cXY<`cXY ΂y m &Ԅ-n&-u:$ճ-7͵޳} ӐHzvm }y| -} $ $$|ȇ|և \Sl % 6Ȫs#\^VZZg`UXZYWf]TX#]VKX<`TX`T H̷$ |Aڽy-mOv}耐--%&3Ş:x ń rŝ݄vłㄷ  $-% ևxhB% xB% "g]ZXcW0#s?o10$Ղv|:&x6$keJ$Րv@3&ՏvkA&&ԂvmA -&y}O3&O&-xH-pxR 3$ԵprR!7$nvk3$>yx>-}wH6-k6&5Րv6?JOau6?Ohuu6O|PQ\WXXY,,fN\EsZG\\]\s\`[c9Oq\]^SUS^]]^d`]]STcZXT\\]VY#9g[lZfgog]Vc 0E=O|D\^T,\OuB`T0Bg`]SS[]]]`BOg`go`][[]]]o4P6O|uL6P=Oauu=?OPu"Oh=h7:RMp -:Ob"@ObA_JmA_Jk A_L_:A-A7AAw-P?x=jP_՝=b4Aφ:@Ake>&A{b->-AaI >APM6OJ 6A}ap%|"۞ְ|og]ZXCNUZFX1g`Co\[6OB#\\`g28g\\`^[[Wg`[Nɫd]XXoQ`[su"o\]¨s \]Ggxg\`qs*Q\`Jqg\`N)uuuȰ| uðxJx6%֊& I- ӯ㽗ľ}vŘy vŗv$ my$&u}- &ٷ-蚀ʮ臂Ův)Hښ: >Ŗ@$̂J$I& 㮗ٰܮvٓӇͰ=%ðJ 6u\^lX˅f([Tlo6| @"7%%7\[(#ɉ/q`ZX׍|Ӈ|P||@ L ?"%)3ֹ%x%ۻÓJsx螇|Dxu͇h uÇPob Գ>|$> γ v$ v Mv yjjvmIH3h&|$)7̺ ΢y&)Hy rk ݝy__%ㄖk 7}-{$̝-L>$̀@͊Hy-Ԅvv%aux|"L%%J%Lxx"u|%%LP4|s\[lXXU,,U^^WV 1su||%%%|x|||%%x|~\UcY,(#gC2`VXTfs%%|hJux|"%%L|xx%%a="||%%"|?||%|P|xu"%Jux|u|%%L6%xxu|%%=%xxu|%L%xxL|J3!$&}vyn&$$}v}zz$$vbzz-&$;vb}y--$-yk }vn-$&zk;vz $&_7vvMM$$M_:Ivy $$IkA&vr}Ղ&$&b:$vv}zH$&HApvryb$&e:3pvm&-@$5vm}-&-$-vrye->$-zkIvj-5$&kby- 3$}rH yzaӰB?ӻñ|ȰO?6 u|٤O#oP2\^cXXXZVU^^UUG2ӇQ?ְ6Ӵ9\[XXV^(,t\UXV(o|s|O͇||s ͇uo2äӥB%ÓuͪO%|uêO =%uÓOJӇuhu2Ӈ2"Æԧx-3̬kh:ŝkk?ʂvyBmQ q?ԷqO &ŽO'ŲwO:ڳ|hB6{sa&HսRݽ?-ŗ_A-ŋwìR&hñ)h|J6sshD\`ScZXXcVU^]^f#suӻuE`TXX^VlZsg^X8h͇؇ۇش|ӻ"?sxͻ6پOxû| 6OÞ|O#h&Bbη&?_ʽy-:Dk7jk6̄"M@Υŷ?ôOŻ ôOŚ)O >B;2#B>Oa{AŘk"AoJx"&?6uhx%6uxx66%%hd\`]^SS[]]]`E+6ux|?P%%x|?h%%xxahg`[VZd]VVN)6B`VWg)O|%%|"Oh%%|uhx%%"uhx%6uuahu6Luux|6%|xx66%xx6O%Lxx%?%"xxuOH-$-v}H$&v_m$$vz_k-$ zzb_&$}ybk-$--byjkz$$>v_r3&zkru%A>_kr3A7$Rkx7A=$7kx-AO$%upvIAO7$bvj6R$Hj_v=AI 3_v>Am$$_v;A_$$-HM_u?x|u6x u|?o2O"8g\\`\Q*B)uև )| |͇ |Ja|ӆ2g][Pt\^<%s\^`tJ%6"Ӈu|"Ӈus|"uu hxu hx | ?|ӹ6%Ӈ͞6% aoݡ|v y>yk7_>$_ -u$ae $9zxbuz k|L}>uRH7OD@-$O=3"$Bs@ȇRs΀hh2wҏhOx݀ h:; h6s|||ӓ= ͤ% Ȱu% ْ|ûx6هu|x⇞|Ç|%ӻ͇%Ȼuֻ %Lͻxu⻇ ӇûxuuxهxÇ L%|Ӫ%uȰa3Ð}ŲyvŢv z}΀$ && Ŭ̮--ږIŷv&ڄŚvڐ{u " $Ӯخ6ڮP褊Jͧo  {aӇhBu|xB%u%B?a"O?J"ևh%|Pև|6uhևӇ|?uP=ևuOh?"և=%x6Lx?ևև|O%ևևuO%ևևuO"uuևuh4uO%uDOaևӓO6h6"և"xO6r@Ԃ_%vRՄm6zA&~:yO$Hh3՝I-_:-ԡb&_An"kA%IkOb_B6>xo-s?&D̡hA&?- hAR3ʺkO|R9ݡkBPAB ՁBHhB s?bB3A&6?"huu=%%hu|h6%Dqsu|h=%PauxJ"%JhuxJ%%Lhux|%%?L|x6%?J|x%""6=uxuL%=ux%xuuJ%x|ևJ%"||ևu%|x|%uxxa"%Jh|h"%OaӞhJ%"Ph|k$RRԀv$ 7Pv&-=M&@Iv"& :7ՐvI$3Hԏv}e-$3ԂkjH&3k}J 79%k{  axwH &J_w_ $$Ն_r_3$j_vk֡3>Av@;Ahyx|>%Pxru6$ ?>rq9ȇuۇӯh###uOa|O%|O%u=B%ÇQ ͇û#Ӈh"àh"͇Jh%ӇLh6ÇӇ6"ÇևÇïJ͇ðhQPxOByRB @)#̽ܦm}sk}k3ွ_3nڷ՞ٷŪr9ŗvA-ŁxA&jBʇ"Bʮŷq{D" "u x|hْu"|x|"h aȇ"%J%ЙDÇ u͇u|ȇ Laxu% PuL6"u%u"u |"" |ә|?|xu"? | :-v&$$x-&aճ )ϳ} v}Ν>}:k̡&$_j-$RM$BH k) x}y_uy5RIv5$JPL2:s&M̳uIvʖ b3u"xuP|%%x|ax6% xPOx=%""uu"JxJ%"|uxa%|xx%%"uu|x6%"u|x%uuxu%J%xu%x|%x|=|"%"|x|J%"u|"|u%""|u"u|%%"|uh|%%"||hx=%|k!bvH$-}vm$---zv}$&-}vv&$&$ yv$$}5$}vb&nM$&}vI$b$$v}&_y$$vy-kr3$- zv-Hv:$5v;>yI$- v>vz$$ vvr$&-}yv:'J6~p>_w:$@-}}vb$- $&}vH$-&&vvq6Jh6ș|O#hà||xӇȇ|us%uЇ"Ø%P͇a%O Ӈu4OȇӇ |huȇ uu͇%uև"Ӥ?"͇͙P |uӇua|ևӇa|)&>Ը6$@Hx7ݸĵHrPܺʄbvDՔyo -o6-s:Ž7äAmŸ>ْAy}Pk}w1Bŷ: R&ʳĠ7_ĝʂ>k ݂6ä|Ȼh6% |ðh#⠙xهӇxȇӾӾJְu %ӻx|û6 =xxù6"xà=͇|Ӈäuְ)%ÒӰ=%|uù|%uux" u͇x"ӻӇ ʾMϮ$>eʷv$5Ŗvb%-}vʀ΂ -Է3 &ŰxH$řRŚ_-ڵky&|yԖ&ξB~Ů$Hŗy&&yI&Aj""P|??Pa?O6OO6OuJaOh"JJOPJuOO"JLOO"J?Ou"6Ou=JLOJJ=PP=Ph6LJhL?Luh"O=aL"?aau6Ou|%O"kIAI ;MAIM Ak>AR7_A 7bAe @e>Aj :@ _m3:_kAAH__RAP- I_-AHMmA_bAAmnEC<Ab>5 Rm5-R6hx| %hx|?qh=%)"u")Ӈ%)u |Ӓ|L x|Ӟu%L hxa%=aӇu%=Ӈ͇ =%  u| auӞ|" =|u|% =|Ӈx6"|%" "  v -!vM &;ve$mՂ$&|-J )ΐ}&kΎy:> _z3Aj -$He-& z-ߐz fta >bvI-I&&-O u6x֞OͰ%%͇Ç%͇x6蹇֠xJهx6͇9) %6ȇͰu Ӈ|s||a|uP%=%ևù"| šĢyŎy߄ĂI;Ԅ$$"3 Ů{ Ţvk|_-ʃR$-$ҷI ʽٗbڄgScXXXWg۳Ŋ&y$&- xuJ |9|xӇu9%|DJ%B%Juua%xa"J%xxuL%|xu%ux=|"6%ux)o222s*"xas+)Bs)a%L|)s%stt%|+)sև"u=u|")o2ot R*xuߡJxa!+)xa{|+s=uu%%h?)s*w= sDsBx )tEs҄&-?*at-a4Ju -u')ss"߁*x!u8\`^VX/hrP!$MvL !$ yb $b: "{xhO%%|xh4%ssL|h6""%|x6"u%uh6u6%"J֠xa6%"|h="և|xuh+9o#NNNN`/N##2֓9o#N##gqoE2qssqqsdo)o1##1#ossBo29u|hq#\1##1whD+Mtooo9ts%$gDqt)t8222osx4####g##g1) soogC2)}O4DQuus8+%աt821oxh'MstoB`TZ^1h zyIv$-2#AQȇL#?##Jû#O͇=uqOàPO|LOuÇӇ2͇ yz 3û%h6٪xà Oxٻ|uxx|u| %u%u%|"^TZZZccccZZXXXXSVccZXlXVlF ^VlXYf2EWFX ^VlX ,1ccccZXXX,^VlXYf#xYcZZccZXXXXY,(^Vl,f)TlF/fYcccZXXX,//^Vl<(VccZXlXfGVlFVVcccZXXXY^WZXZcccccccl1ccccZXXX/ѫxqWlFKYGUZlN2VcccZXXX[Vl0N+$ ŖjʸH ʀb Ra$9O?OOu|L??OODhs??hOh|u?"POPuu6"JOPuu%OP|"POxuJAB\][^fCNQQdd\`SXXXY]][^f\]ZX<\]ff0##\]Z<\Sl˩<ǾQ]XX\SlXf<`UY˶f\^W(<\SlX0%q`[0)ػD\Wl.Y\UX ]]Wcɦ`[Yh`cXV\Sl\]GE]VZN\[VVTWWWWWWWVllXN`SVf<]Z]Vf\WX˕cVWS]]]^q`SlXT^[Tˈ`WZg]ZY <<} ? ĪsABQ#sO##ӦQO2#Q)ȯqh#B)êOsQO9)äOBB6uJO%ȇOQ#)ֹ\SFF 1оuoPs`TXY\``]]^]]]UlG\WXYR&:s:OQsPBsuD?BhPBBxDJBOOBOx?=OOsBOx?OOOOJP ?Ox%Ox"6Ox6OB|D"%BO6OP\^ll ,\B9O|uL\[lX GN#NNN\gg\]X#\[lXTWNqg]Z/\[l̔A67:MA2h2QB򦪻2#q##Ц#q#ôhsoBQs \]l+E`Ul##\]^SUS^]]\`q`][[]]][«x\]VEsd`][SS]]]#s\[lXYצ"d`S<#u"uB`cXYf#6%||\UXG(f^<<<^VFfq`ZXfNoM&-vjey|$&e&&&I θ>$7;v@yR{vy by-}H&|$5 -&-z z79`ZXfNo-~mς _m)ゆ;vJ$HJ&$$␥ا####ۥQ媥#22媙2Js##ﰪ \UlXY , G]^T1ͰȤO%|ȓaه%|u|ڝ͋ -ϼI&jŊb`VX˭ǻwI- ܵ~vچHz$ }B2)RBҖܠQ#9B##Ӧ#Oq#Q ʹQ6#BDoþO9QOsDþOBB#hD#OB#OOQ#o#D\^lXW[][W?OO"aOhJӇuO haӇ#V,|B@a OAҘP ճA:uRRAR_ObO2eDODD>BB>-|A I>ՃA5\UXY<\2ӁM >nA=je)R6_A L |O-aIj_-6 &3BJBoa6QBQhJqsxD29BOosx2?BOsOhBO ?OBOshO 6O=OPO%9JBsxaB2BPBB|u\]cXXXXXXTS]][0#sx|x%h|||%%?|u|%=|]Vl<2||u"Jg^l<6u|D\]TZZZcVTS]^Sf2d^ZYND&y}eI $7kzb$3_vbM$$Hrkb$;@k}y-$-7_yy$-7A{mw3$Am}v}&-6kz Iy-3@mz5 $>Q^Z#9$7b>&% _;Ixe$-m}b >9#പ##寙#B##奪#qBۙQQBB#2## 尹#B##2\^UUUUUUS^]]]`tB|xO"ÇOӇ?q]VN" Ӈu~2`VY<P| g`]^[S[]]]`2HB`Ve&-"&9ș} ŗzk݄| Rn:R&n3O$Ϭ$25ķeڮyxš}_k`Vr$:š ϽЂŽv}o:રq9#索#s#دBد2BإqDQ)ooت)2\\`````\\`\gdxax="Ӈ%)\^2=͇"xӇD\^<xȇ {soQ\\`gQ2wՄ)\^N~ $Բ -Ð>IJ-vbڗz v␐ ynԄ$ub$4 ٚ& y z3v̄ }vςpv)\^H-3$&y$z΀~y}RJOA63&OOOs?PؙhBBO|OBhO |ohhQBOB|DhQ?Oq%Js?BoB%6hOoO?hOA)u>"|a|P%"x||h%%||uh6u|"x6%"x=%"u|a%uuxux">>z5M-zI-& zp& zm-m3& m-Iy3$}y;ev:5M&vv_$--{y_$---a-hA3>& z}v3$5$ }vR$I>$yb-5$zk&&-yk$&&yr@-z>$IH}vb>$ yb}b$ v} b$&5s79BذBQBذsBBQhB##s#QD#B=Ⱦ2#Bsaþ9QB#ȹ?O|"Ӈ9ux͇և"4uև)6"hJӇ%hӇӇ% huևӇ% hDӇև ak b -R7Ԗ}-R:̏k_9yka2}xԄ"&93BIķBARʺ >3{_z7ݑAτ>ADԄhB -?&oA@&σA~jAzIA2ho22so#Q#BB#9###ع###ȥ#Q9إQ9۴QBB2BB%au%ÇЪu徇ӪͰx Juñx=|x͇ÇJ͇Ӈ ճ}u -̝}b;$ŝzm$M{b$ ۞b_3bv3Pr7- w= ʮz 9yž?&{zij_ $pk-IwAuOAaROOOO OOBBO%)OQ6BOQq?Q#ۘuQO?2q|ӾaQODBhuBOoQOs)aOOOBևPu%Oh=u?hu""=|?Ou""Ju%Ou"O|"?xL?x6hPu-kIe 5I-j >7_Hr3A} -b$A >-a}&Akhz&ORhyBAk=M3Rz-> :A:b-Ay3aAr $I-Ok$RBRk On_:nMk}3H kz-:_--D_axBhsPqh o9#x9 #hJإxoDBsx9OO? 6ux%=u"% u֒xu% uu|xLuxL%"Lxuև%xuu%"||u>zz -z-  &-}}-&$}-&> z-v mvkpyvj$jb $&>$$7 $& լM & |vӷ~ Rvv-@vy-$>z-$3ږ嘘#sﴯ#Q### ﯦQ#B實#Qتs#q<g#⻪B#P%ӇD%ͻhٞ|xۇӇ||uÇ|u%uȇӰa%ӇͰa-ʝ̀΄M} ս-$- ķL:żn6"Ÿx9uʬ͊I >̷&-ʽᷪ:&Ͻ3a łŽH_踞Êw}>e$7)!? kB2hQQa Q+<\2=B%QO)P?QOoD?OOhB9ECꉉɦNOQ2au%DQQ=oa?"xP6xuu6x|Ӈ"6%x|և%%xa%"|hu%"uOu%"OJu- bI}I M-7 }-@-- A3ՐyΆ$&P3rԀ5AA{R}@9pOsI?}xJ%$bBOI3$hAIe73ՁR;}7:|A=z-kOBvM;R?zH:z-aA-hHRBJROHDusBhhB%2J|sQBgf^18POPBO%9P͘OOBOOOOsOO ?4|hq\0G(((ꌌqB8B92)DsuaBBJ?9%uhO"L6uuxO6"|u|h"6%Luh=6"hJJ6"xuu6x|uO|xO="zH7- }j-eI H _5-$k>"$HmxR3Im_O": 5HkBIA"zxu?% xIB9-)HxmD?-vԠA-&7rx|A-3k{O7'kx_=&kR &Ր}xR $ͥ####ׅ0<8##Q#BQQ#Q9#2g… Y¦Ѿ#)##ȥssuևh|Ȱu JOàu%=O͇uӇ"?"Ç͇qu"B͇Ou|ӇAu"r7uz- p yk&BRÙ3?O|:B=&2 а۝#s⥏Ro{ AB BOͽ6QA)yOu7# #<#sBoQs ,,#D#勒٪u ȱxJÞxJux=%ӇJ% Ç͹%"x%Ӈxxu|{- zՂ&- Ջ &ϋ} ;y%bbv&HDk7O$=&y͖- v|M vrmr@w3 6~$-& zԢ &Jŷ3?oO:Ob7 sO?2P?[ZlYSsBhB2Oh)|OhQOq6?q#9B26/YVccZXFXX S0fBJh?QBO|xh%J|=*xx%|%Jx6|%=x%uu"x=%||xP*J|L"|h'Px|x6x>- e{37vn-M -z &b~5-Iv5&>v>&jn3r_3H} 7:_h3:{;I3_h:3I&kr:$mpM&HrA%hr7IvR$_m- >rR$Hv7->kh3AxP&e&kh:Ah3";-y73vb$5}-$rk&; y@kk:}s OsB2`UlX g<t2q#Nt##<1ƦQ[S[]]]][VFF˶ר1NEsB8sDs sև)+)'sӤ+s*sqt+|sЇ u-Ә;~|͆;|xӆzΝ   $k77ڗkͽJ7Rv-xv}Ӑչ2B:$ݳR"}ڝAŲJ@2žJůQBB2٪##2g]XYYff<Ʀ##N<#ȥNŽxPřAŽxqO}ʄ?!լ~#ԮQʽDB6>ŖűA&ŀŗR= uŖ_:OAOk 3OOh9?2`cWG0(GgED6誐ڽxD诗ڸruź ?9A-$%Žؤ?蓮 ÷9襒ػ⚴Bxނ@}A Ž$H?vȐx2 >xh#)g^XXcVccXl.˶S^VFXT,EfTcccZXlXSUCTccZXc(`^lcY[VlUUVccZXXX(VlFXU^VlX f1)gWFXY(#SZl^VFXG0sdWFX,f1SZX^VlXY(g#TccccZXXXY<^VlXYw^VlFXY<֋}&-DUZFX,0"YVccZXXXf<^VFX(hAa7̼aAhA_@k?_O2bDՀ_?n>O?uD>hO&J-o=#)##t`[U[C#g`UlX<`cN<^^^CN\]TXXf]^^1g\`]Vlf`c`][^0Cg\]`]^WV0\`cl\WXWqg^l<`cU\WXg^l`c]^^^ffgg`[ZX/g`cl(N9`][SN)&ʄzo`]SZU<#g^^^fBg]TX,<<`cXf#\WXNŞkŐ$9Ox=Bx-ԗ# s Rŗ3OP Bss hԢ-հ$ ذ9#=ﴬ##oD\]SU˶Cq\UXXf\WFZYg]]SY^<Q`Tl,G`]UYgog`Wlf\WFYq\]WשNg\\^cX\WF\[lXT2q]ZXC\Wl <\[lXG/q]ZX(f\WFf<`^SW(0N##\]c[\Wla\[T #IIg]`]SF.YfN]]S(/ `TX<<\Wlf#u\[lOQB> Țz2hbIݖ}Jk%;bDA46HPO: Jaq:239-}ҥz2Hւ΢k$:oh:Or7$B_?Qx)|%DP\]TY?B`cXf\SFZW 0E`[T,0`#)Pq`cX(0`ScVfgs=\^l.U\SFXY,/o\]cV(f<<##\]Z\SX ,0)d]XXYf1Dt`Vl<\SlXY<2d]XXY,0#B`VX<\SFXU0\`UYYY f/<\^W<\SFXYGo\[X/tu -\][`TXf0gd`[TY} $3rH$_vM-$j>$AvIv $Rk$A_kIv$@P3Axh3x$>}$3r}-$3_v>kv-&$Rv&Iys: ۮ|۪sBoدg]cX綈Ǥ\[l. \^llY\N`W NBog[lX,f`WXYǯ)`c.Y`^lYqE`VZ˅we턖ų>H垗% ߧ9D D>H >$Iη~_ >漊ܝbB ٰ2إBٷ2#QQ`c.˅<`c`]ZYg`TXY<2`cYf`TX,/#B2\TX˩]ZcYo\]cXXXZ`N`VXf<]ZlN`T.<\[X(<]Z.Nإ`TXo\[l`]ZYf<`TXXXXC]Z&?Pk~P-&96 askh?-A"OkA33kvzR&$A_r}kI-$_krhBADPBBBBOB\WlX E#\[FX<\WXZYGf\UXXYUC###\[lf\UFYUGog^FY\Wl˶,C2E\``]]^]]]UF<\WXXUfo\^lXY<1g`cX\WlXWN\^lXY^oQ`cXf\Wl<\`[^``````\\`WX\WFY N\WXY<2b`TXY<\[XZ<<\UXfBs>92 湝JQBL2J2OaBPJĘRAa>RAP ;_:D 2h咮2s領#Q\[l<<\]l\[l \]l<\^XX\^lX ,鉨]lK\[FF˶1Cxs@uMIs:A)$7$>$H RHb㢊RDR uvsRk Inv%Rx->$-5HIA:rh)}v)Ih96OBB|s9%aQ`TlY,0f]^c#`TFXVG\]ZX ,SU,][c1\[lXfC]c`TlXXG^WllYf00<`Tq`TlFY<`WF(/<`UF&-B:JO-$ Am>3hA4vw>kO2rbkOH>h?$y$J:-h#٢s##ػ#D\[ZFF˅][c\^VXF\^XXX˟Y]U<\WX˅]]TFN\^VXX<`UXXY]TX1ش\[ZlF(C][clW]UXY#\[ZXX`][cll0]UZ#o`TXY2\^Z ]UVǴ\^VZ.XY<]VXc<łz`^ZfЇq]ZX2\^ZY^]UT<\[ZXX(Œ]T#&ԄyI$ĽrŘ9)2=صٵ)ή :㮮ŪB?÷ïO ڽB ʒq󧮼Ϯs$sB###Q\]UWVXcW[]SxvRB|$ b32|BQh%BJD\`g\`][[^]]]Bh\]`\`[VCsg`]SS[]]]CB27B\][US^]\2`TTs\]\+\`[To\]^SUS^]]^Rv__33&_vb_v-3:$AxbxI$67$:hyexvb3:-$b}vv-&$H}zvs3÷O#og\<g\`\Nqq噹Q``d\[NB\\`No?ooQg\`\B\^Qg`\ٹt\^aQ\\`\Ngqq\\hqg\\N2\\\gsq\\)Bg\\gqg\\gؾQ`[0 ? ͮB2&ݷ3ڸ䯒Daⳝxs|₳ВO"w:$" 9۷2s â>v zyخî62Q ؓ|oob## 9ؤ=۪?{u)|ٙs|u uヱ"7%Ӳ"%|#1|u͇ |Ӂ~D\[̗OBOO==uhBPO L= |hB)hBP us=xO?J B?sQO?%әDaBO?%hJhO%=%|u|OL?6axO?P6|O?h6xu6|a"u=%|auO%N<\2h%"u||%}>J\UV#tz_&k -b-z5 rA-I3$ykA>3$nv_>A3-J>BAHBO$D_hAhA&eՏrA_AI7ΝkOkA653vhJrA&Is@mOB$;}vRA& $x_sA3$wHwA3I$}zR3>$zzR6BAͪhu?OOïs9OBؘOu?Q|B9#JqӾ"#B#|QOQsDBOئBñJ%4ٱO|62Ou"Bq)s OLӒu Ou͓|#Wg?D|x͇Ӈ@Ԅ\[Zs7u7ԃp-Mjn-Ik $ HBuHQsQB%QOm&5?A_{7?OR@7Boh=Q2JBq2Bo~ OJqh?OBhjAAaʵj&:As̄zqOؒBB索BQ9h##u#s#os2P|? Q#u2 ۇͪ؇ð|"Òxhuu|]Vl0ƾP% - E]U<̼ėv-ʀ-z-& #Ϸ#2_ْRkDB hus 񮮙媢̯媐B2:Ŭw3:Şx3k{zA_9R>}OB9O 6s%O='oB?Qh6oh?Qsa)ohQBs2|Bh2OsD|%aO =|D%OB?s%B=%DӠ%9||և"x|%J|և%||=|և%a|%6|q]VYss4xuJxJ%x|" $bDo&Hz -7y &y-} &r->$s&%IBx@ }̝IB_O2Br큁?__ Ay7kA-3s6As-&ss:?x7Ь; xA9sk$v7Ok{)kOR{kAw)&I 3Ax$_ &:>7 vko >moOO %|u% %Ps?6%PxB% ?xBsBhBxOB ao|xu % a=%%J6%6 xJ%%u||u|u||"||x|%\^x%uuuu3&v -- yz -$ yz $ }y$ y& && }}y$$-$vv:-rr?DRxxv& mv|$& $_|n6$ $|@DR_%>_)aI aPvhhHBaI-&ub $&7- sQݬڢ# ##ͪ#6#ð#2详ðBإOٰsQsٻ#B#q⻯s###)#ðQ #BQÇÒOh ӇuPxهuxÇ># Ç"#%͇û>"h$Mk$r4 yy&ս$ʷ7̷s3ڧ@ ڽhBڢٷs#sʗz ή2 ŮۯBٲ>ŗ٪BŗإQ⸗Q#ϲ䗥#Ž㺮2$&xL=2񎙘 #֪sJ "2OsuQBshØ69BoOsB42ۘBhQ#qxQ ssЦQhB6BaO%h|||%ӇO|x Oux͇" )9u%?Ӓk7;zy&ԂvԀ&v-|$upM$ukԸM$ HH̳vuL?PBxٳHBokD|&Os{Θ9 A)BhŹ?AhxP6hB%2uhs 5sqs{լ $-3&I_xROR&3;|OBO%6uxuJOhJ9?OxsOOO="=x|OOD4O?%hOO6OB%OuOBsOBQ%su|h|OOhuBOaBO?uxhBOP%|xuOa%|u"OJ|OP%x|OO%||?O|%u|aOu%7ke&$kb&&zy-$v 3 v3- r 3I y}3@ -zyHR $I_AAB3= kx}@AA&$3M}v} A_$3 ryIOAb6&RvOA_ :3 _yRAk A@-HyHRr>:A&A_O_A9-3pOhIAO@-~hRj3A>&;}v 6qӾ|#ر|#s|Ⱎ2#QشsOs4BD4##u##sJsxBx?=xև6 6%6ÇuٹuxÞ@6m7v$w &ճ&Аҳ y ݖ%vn$zj3 OлqB н7vܻhq{sObJAw$лB) 2)ۙ%| ؚٝsyy îaت夞x)蒰簾蘯sx2 䘪9#ت9 ۪2誒s蘒""|% "u)%J |a%uÙx|ukw z-}-$-- ̚}& Ţ}θ}z}ijzy-j ЮBٮ3zʷ>І蓐٪u۰B&٢? ņ  o 殧aaIܷzMMA;u-A3hAn-O92hO ||h9BOBhuOOB2axs26hQ9PB6QO?2%B6qOB6a%OO O?ٱ?hOhBu|xB2OhB u|h%xP""h6xh|6|h%u|?%|h%u|P%|h6uL"uP%uh?;&y@-}_&}m$ _$Mr3 j m:-}j z>-b7-m--MBARoB3-R:@wA& b37vR$p%%_AmJ_B_O2%m%bOB@yu J&px?${-3DoA:@_:xA3wHvA:Jr@MyR% 2Ra3??ðss962B2B#咘##u?BQ?sO4BB6Bx#=|uu %P|%%PJ͒x%6xxȱP6xa 7Հ "- Րz-$ Ԑ}պ} $ϏpzM$&ςm}-{v| #DPӴ2ܦO߷xHբ$$HԢvʝ37Aͳvu:6B嬒{B2 &|B'BhR3kŐ_%kzksQOŽBBQûB#BͥQ#Q#ȥq###ͻ#Q##د#B##o#B#QûBQ#Q#os#oQ#2#Çͯ##ӇûuBJ"ñuBDJà|Oo6ÇO=͇ÇP5ĒIk}>k=AkIA9 #QŰ#BA7ePO:JBQ2#2#ٷ#⻝ŦBQۘQQŘOOBŢsO#_zH:@9k_h?BBO?s 62?BOo 2?Q|a)Q|sPQBqx?2OBh?)9OOs?'O)Os4%J=%auxauh|J? hxJ=Pxau%66x|%"6s|&-k}- -bz}- -7yz-$pz $b }  &p }y $Hv -- HwߘaRJ2svHHy}I$Imτn$=$AHhD:Obs2P39s5 I&Ovw @Oxsx%7 Akuv7>AI- $:RI}Oh ROO%O?QB%ذ)9OQ4ۙuh26 ۘh2#BBasx2OoPDBO?Bu?B6Bu%BP%)|Da uxx4u"uJx| 6u"xx6|%u"x%%aӇ|a%h&H--z&>&z-} & z-v&y-r$-v}$& z-$ }v- Ȑwoq_?9H펎΁v$; v$7-I3: $@AOqD䷃JxA9oa̚HskbDvAkH܁v%Ak$ v&A$&-R)x噚تۥ###Q着۾B=贯Qﯘ谴s#򥙴尯s#2ؙ2##2ػ ۴2%ٰ6û"ٓ?xñu?xu͇Jx|= uͻ%͇%x-ʽ-m&Ŗv}z怖 &$̮ ő$ܢڽABﻗss݄ u- 2殴BBﯚŞs̖ؒDÄ-&߮ &Mxoku͎2=k2 )q?# o?#B êOBqO9ْxsB?咘hQ9x#Q#)#Q% uB%͹|P%%Jxh ?%|hJJ|xJ %|%" |B%"Ӓk_$- }zb$-Ղzաp$-zy>-yJ&}k-$Քm&$-ՂvM$-eb΢_B?HBxqؐ?Mz&I $7PqB:sOO 3wxA6rh7x{sx6 $ϳ@-$ _~HJ AOAIOP)9>OBOOBBLBPBBODQOJӘP"BOODBOPӞPOODOOOӇJOOBOOBa?OBBOBQJD"OQBPBBP=BOJOOP|u??POPJJ?POPuu=JOP"OOLa"OOuPOOLPu"POJHLMAIH__He_>HI_>HMRM;@AHbM__; R_IMRA @IeRPAAO"A?b_MA> A@>@HIHOAeaOIԸ_BAPoBRH"OADAA>_ABaAO"RB>AB̈́JIO?RAIb_IAIMH> #D妥#sD#osBBqo?qs2s####)򦞯s=Du٤x6Óx?%|xP%JD ؇ه xӻ&Jzm&&Iz}$&~}-$- -͝ʋ y}-jv%hJBL& ӚԷ 寐ssB_9O%9 ή ̸y?%2د媾s#o###QлsBhQ9##ﯥB#寒##s#û|sxuÞ|huÇh%ևh%"ӇJرÇ"aÇ||ʼr6mz3m &{-լ&΢-ʖȳv;Ԅv &Oo9 D&$M ڢܪ)q{⢖wBbåOsIxA">??2լ }& Գykz Rb& 33xRr&:2hOxDOhssxshhOQ2hxP4hq9h=%Bq?O69%qO?4 BO2xaJ |? xxuuL%%u=%ax||u%%J%Jx|uu"L%x|"uu""J6xh|""%%|xuxu%%|xJ||"%w>y $5zI}} $}I}v&-b-y-&j&y}&j& y-&yz & 7&zy3$kD@$D3RaI-$@$7vk}-H$6v_yII kk=uvpIHRR9_v>b%$_k7IvI$Hx:3>5=$6hA:_I$bR&yv-&>&jv--j6 IʮO?s:uhBBDs#sQB⒘#B# ➯o#6D#Q9B2o便B#B|xOP uxBJ||uӇu69%O9%͇͒JP%xhh"|xw6 Ղքv&=$՝{$ ϝ՝r$ Δԝk3&Δr%- ΄vJ τbͰ?= @Ӫ:-ʮwPْAڲrBwA ųRBԗvs&-$ 㮥=O%oݽ{A3$}ʊR $Մzv QOsBOsQ#QØ#Q#򯦥û2########Bش#o#Q##sBQOuO|q#uÇ#ӻӇBJïu;BPêuٻOÒx" O|:΂  j3-Řb3|_Ą7IRτ-ʀsաHQ#Q #BeqOٷBBx@Qqó##s2Mν#2QsŽBQBŽj:R΄ :_kzBA?HkOA: DsOBO?BOQ 9=quBQo=2#) s|sD22%BqOB4OBO#BBOOOO=|x"?9uPxuPP"x|Ou %||JOJ%%uxuhJ%%J|ӇhP%%ӇLwm{$ }w $Ir$&5r&- Ir-&-Mvy5:&>zvM:&z;6 nMz2?5>՞xyBA"*7ճryaR$3__A3 pkO:-PaBoI%xx'& azsRJ97RzsR?: @zϏv3&&eՂv@3 &O݀OxA ?sBB?9BQ6 9s?#Ps#Qh2h#BB|2s#BBhs2B?DO6a͇BJ%͇uDӇ9xu9uӇO|Ox͇h 6xӇP6hևP3h$Rzz&?y&3vՄ:r 53k-7h&- $h-7ʢIQx7oٗ2xb Oz{9_{A~:R4A&b&A6:Oq>4B~-r7srvOx_Jw AxI-Aw&> 3ٚ) ۙo򦪪تsﴤ#o򦪯oB%  %%uxӻux|%|%"&&  Őń-ʀyz̄z-y &-$&&ս& Şۮî J流۰ٲv {7 I$Ю)஧nH⮐蘘ؗ>--΂$ -h2RB"z6xq #?#2 Q6qBðOQOB ٰhoBhٓsOQ2s蓥#h##BsӰ?2?s2Ȟ|s6D͇Ӓ6%aӇx? h%xOshxhux Ӈu JxB%և6B%Ӈߝ>-m$Ԁk$ r3Mv6&vP$P$&Ժ_$>՗_3Rĵeݲ_BAʖv)_Oʡwvsb6&u$O-޷?oOٞ&θ>A n&mxOrՏkB24k z$-:&-p_DI: RBPvhj 7OQOhBa29%QODsa?BOsOhs6 OOOhh? O9hOqxBO2PBQaxs9Q9?QQhxs))B?"sOa|%%xOxu%"|O=||%JODxhJ=%OO|x"%O?uxPOuxuL?>m} :5vnM:&ym 73z_M -$ _e 3 _me :-h_3&RA!;3_A_aݝ: bAuv%kO vՄ>-?3v-H@xϸ6 BA=s_A%4A_bΝI@ARB͖kHA RsM-hsu 3{h $ ҪD# ί###ۦ#Q# ؘ#QBqQ#OB#Q)#o#o##å####Ͱs##=êxBÒxþO |O uB#Ӡ"Ӱuq" ȹuӰB>&ĝR&$ ĝk5$ r;3  ź Ϸ#:;ķA"ŽkPO'ŽxO$ԧkq )ŸʰHĻBO >B9{o?Ĭ١̮-ŮvQ#4#ػ#B##إ#s#QBo#BQB#B#q#######s蓮uBxxOsxÇOOsB66Ç"u#|ȇ)qxÞI?rʄ5:kԄ 5$h3 2v|I9_{ ?R _!ABO $D&B&?)x)yB_hOk%P As>7O&Q%syЏ OyHR$47bk:B3 xhhBB6|sxOaahxO%=axO %9%OOB%6B?Q? s6xB#oOP=x|x| P= ax|h|%"Jx|xx%%=Lh%%%|u|xu%6%u|ux|%6%u|uxu=%L~Mvz& $yy5&& zvI&- v-n&$vb-$Mv by$& &5vvMHv>$---zybre$&-&r_r|$3I$Iyvy&3L5$$yyy &;&zz &M&-} vv>AH RrhvxAk$:~"bv-@m3$>>>@AkI$&I;vv>Ah7$%kb-wv5p$$ ;&bv}m}-$9 ٮBoB BoQ﯒BsQo|ٞ2h#Pه6ӻ%#B%ٰ2Q# ۰ﴴBs谘贘Q#Bȱx#Dx6 6%"6 s%Ù6h%ȇu=h➇͇uw ԄeD$h$ x3;v3Iܖkyb܆w2m̄3x $b&3O&θy&ݐ_ ΀R}ڸhB֚Rq|2-֪O$&Ŗ O ؗ&Ąڐ_y΄zkۦнoB9sô22s2=噘uӒإ#2Q復Q22qBq=qْ" #Ӿo"|ͻ%;9|=%?Òxu P|ź!z ս-%-ŚpŁ-$ŗkIJy$vxųv&Āõo $7ʮIŻ ű@ʽvź-ņ@ܸvš>H2﹘Ȏ e̷-3sa3kŗyk}yRHBA)?ROAoA)JAؘOBO2ȇOBOևӞaBBuO9uaBOBBJӯQOD#B?٥h;BO2#OBPûOBQOQ?9OBOOQ aB)|OBJDusauBB=|aOOJ%u|hO=u|O?J="uuOO:kA ;3 kR7pR A7RI@:kI@AkmHA k_bA>mhnA:sbBO-"& zAA3ճkn_A-kAI7&_O2A2u_BBbOpBA =?&sOIA7rRߞAA7e~RbA: pRR: h2P9P2Oha6BOho4?B|6Qu"Qsu#Bqx#BBh92BBs9#BO9%QO2qJ9"%?sx?usxu ?Ohu%??hu7x-hz O}Am}Arv-:_y -$Pr 3B a2_>2bwzϳ%RO{ʳ$@R̄&=&Re3BbJ&BqwHBm֙ ; OmΖz$AxJr-&Axpv -:虷ش2o2B2͘# ֻu⯒#Q奙#Q#s崪#Q⴪##o ۰#P|ؙ||þhuh?Ph6Ç$-&۝Ő؀ ŋ}z@m&z>{$>&- ٴ%٬Űv}rIbw:MĂ:谳ݪHÆ٪uŲvDzŖO7H$  HaP_saH6Oqa"26sBO9%ss?6"?%BO|xOsxuDOB#Bxu9hQB|6B#O %29Os=%oOJͱ"6 xu|6 hx)uua Pxu" 6xB%uև%6xP%&&r&$y&} Ir3 Mv:&y}Iv=$y-H$HM&k$@nk$=H k kz7rr}z)_x6-J$kw- =$hO:-$Rek3hAvj΀7y_>zM yxR_w%$r &57${vkDk? P9vs7_B &hB7yB?hQ%96uO?s6%66xJh? 66 xxux? )O9xO2)hO=?asqhxB6BhBPxqB6B%6ssJxO%D?%h|hO%|?%uhuxh%u=%aJxxu%%J"xxL|6%J=|xJ"|6%="ua"|63!> I$--vzp$&-yyՂm&-&yՄy7-y&-|- $-|Iw-$&ρbՖr>$n_Ґvb7Hv~>7v}a&)>u&>xb6D:Hӗ:$7$w_͗A "3rwzk $ yzk- >}-$|b-$Bq#򙦴#2ž#Bئ#=û#BB)ٰBBOٱþB#qQ#ٞB#2##ؙٴ##s##ȯ#2##DӾ#O٦B?ð6ٯO9ÙuLٱOa͇uBuӾۇQ#"هӇQЇ̻O-ʰ-ĽA |IAb:RbOo#D:ĽPBAHsO۽{ekObճbBϓսϖ#2 O#ུOQ)BAٰBŽBA}s_#ԝIo#̰) #-Ż@ )=#񦴒s4#Bs9Oss?2Bs2?QB### ###)Bxþ9Osx? ?h=%aP؇)ŕ $Hvϝy&$@rΡy&7az&&D D  y՗-'Hϱk">3HkL@3?k΄"$%;hԂus Lx}άLAsk6Owk%6O{x$%?j-6D - Bz OΓy-As:O>Т59o_A2BIe)Ok3BOa D|Ox)BOP6JOhDOO %PxOOs 6DPOOq4?QDx?BQOQ"sx?QBO2asxJ9Ox?Ju?O|%?J"u=Ox%6a"uOx%PuuPxu%?Ohu%Ou|x~-Ab - v}&:p- zz:byz :_-&zy3A}-$}&_y-$ z-Rv;$-z}5AkM$$ A_$AIH_p$:j-ky&:I I_y&:Rkv;3A&-M_k@6AkIRkkAk;& H_w:Ah$&vy&A_$$vy :_e$& mv5:Ry&$ z@R}-$D Dخ)sB #oxsDBPۘ #2Qػ#2#)9勒ت)o2#ۙhBxu uPu% 6"%%"%xox ٓuvv >z&&y-&&~&~&-y}}rvij kź&k-$a $ b I_Mw_谐h_@63$%&$@ Ž_ v} îoBuðs2٤ش#)虪軪2 ٰ2 尥寥QB谥د#D辘ؾxh⇱xJ"%ӻ"P%ah%Òxh"zڝy}ń $ ήծ$ -ŷή|$& ŢŮv-}Ţv"܄ŝnսĂj-̽- $Ľή-ķJۤ:ŲvÐ?Ⲛwqߗ}߮) ޷M$$㽖ս_ñ{Śkڂ }yv:BA)hř3OA_u:AOOsu ?Bxh|OQaso| sQ9PQQJ6QODqBO6BOO9OO?O2xOBOOQ|͇?%OO"u|և%OO|x%hO%uxu%xO6xuuO?hh"uOAvm_3zk I3-rI:_II:&k_:3kR 7$;rA}>3-bA>z53zO@z -zR: m&&RAy5-_Akj-&_A>k-_O|k΄_B9Irk 9:-BH?ARI >:-_>ՄHAAA3$RAxoA9)oOOOODh_O 6shB4Lsuxq" #sBsqOBBsh9BssOQưsBhDu͙x֤D%xӤO= %hxͱO%Jx_-I{y  I |x բ$7bϳv>$&H̝ve3&̄rI6& D 7 >՞wϞ>-Ԟk}|_$&k~R$ ʆh{s3-B =a &szΙx-@Ηv3$:̐k@%:nv> 7sڻQQQТQQ?QJ %###u奻崒#Q#Q#s#QBs#N\N寰#4###ào|#aȇO2|OOOهO?s;3q ܷó~پо2k}?_M:OHպ:&:Ľ% 洐籠ksDkOHxA>O 32۬2ؗ#|żzBm;ߎAMոA=A7@kx?%RR: 3hk%BLu| %"xo% sO2sxahsxOOhh?OBg1<8as% %4x% usx֞|u  hxx|  ?xx% 6x"x"${z-$$} -$zv 2q2t"&}vyԄ&}&v$"$b&$$_-&=}Ֆ} M$vyԝz &rvދv-k}v-$@j-3$6uI-$b-v$pv _n avԂ>nbve$- 7j$&-7r_9?_O 7{ yB:RB3ue?:_h33~"u|%hh6%uLuJ%ax?%|J|hBhODu| PxOOh |?hBOhu6hoB?2NG(CqO'h%ӇxP sh ևxP ax |h =x LӇ| ?xD%~5>@$5-m$ Հe-r$& u^00N2ր}nv3Mev=$;a$>-ԝ x$>-&Րv$%w7&Ԗr7 vHϑv> y_yum&Ԅy~-mx$;x3aA$և-|3=A$"z-_yuv_ e&zyk-"}3$}-@$&Ŭo#ݳ#2 #9 %êxùBOػPB|B#s##|s#񤪾0YYY#QDٯODuOxȇuqx致| #-λ9--ŽAŐz7d`f/|΂y-$A sA̷$xAڽz-kOv}BԐ脗ʽ AŽœAxJņ?hŢIxšτ Խ$9ړqs# ֥#2 ʰ#Oû|sP%aȻhBOhqB#ر# ocXZY¨NB#B?6ÒxȘO? %|O|us|s2usB&Ζ՗_ $΃ϳk55$eΔkJ(V)*69M՚xI3 ϬPA-̳v_:& ʵybA- Ԁh & s) P &uA3bw_7$bzh:Jzx7 }uH{&"ϗy-ABAOb&9QR:Bs}B_7BRzJ%6J?O||"%J?Ou"au?O?Oh||6P?OsOs"L%OO|Os|J?OOu\WZlX.XTWGC\t?aOOhOh""%Ox"Ohu""OOOO?P"?O|6P6O|7HAr-@A_7Ak\WZlX.XYf#=3A}II&Ammz -A_@kM-A_>_ :_-_z :AIRp7Aj-Ak-@A_:n :Rzp3H3Az j:An7Ak5>&AkIp-7&AR}>k7-AA;bM:AH:A} H} خ ت!hÒx4Jxx= OO%6Q o﯒2`]STFlYosPx% 6%% |u ~ҷ} M θvM-ʝv-M&o`]STFXXY 2t -}ṝȗ}̱ vzēvvŖv$bĀ $$L΀-$ $ήbʚvm☊7-ŏ7$7j%$ 7M ʬ v~ ٮ22ﯙq#Quxo|ٞxJٞO' ۰Bﴘٻ覦8t۹u\\\`[lXY˶\\\`[lX /# ż $Mv%jڳvvĄڇ kI_$J-ν$) ʽ}&ۮHٵżzxmłϖrs薽ճbĽ-k$ż&)ŝۧR ٢ڐkzyz$$B:JxH: oEE#dEt82BE2sh|}7 B8+x|u*=9tstq+Jsss+88EED?++tBDtE8sOPu||\SF.TTg)=POB=xxJ%Jh?"xx"%*JsEtts%|O6uxxD8|߂$9'xwP*yvu=)D+)|x;>9\SlXW(N*&v} $$r_z; &$v_ vI-$-z_Ivj$&}_5v} 5$&_7vv $$k: yv&$mA-yv-$H:-zv5$ 3&v}b&eAMvy-H$yr---$-re--$-_Iyn$&kb$$ybPQO9ŤBCCC=$Ϣk"a: ̺kmA;ynO mB% s: ճusA-zʒA3&zxA7 }yR>zs)H 6 &̝ԒR;4-̆xRA$ʐ_R3΀rb6B2#gˆU YYYY/QgNP# /ׅfEƍg0ׅU)B\]ZX ¥Bʹ#Х־2uCƦh8G1P؍EN"<<<{g]ZXXVצ&6_ʽy-APżk@QMxHؘ @h:OŢ}$"Ał IBԷo}ʮBmŮ}AŞmAI{3?DwOx10,cVcYS1<<(,z$_-$@H$~&3@$u&vb|yrm7ybv3y-I3y-j&$v ؙ^TZllllllllllllFY(^TFl,0cZZlFFYU^TFXcZXFFl f^TFlXXcZXFFXY,񪤇\]TFKKf9hప|Jh۾ScFFiK˶socZZlFlX ]TFX(]TFlY<(˕ZZlFlXX(t|Ÿg]TFK˩ }ń -& $Ůj&Śjʮ!&ږ>ŷvڳ&ڀJڬvڔٖڀeԄ$ ̷$󮚂ŷ7 &ܽŢIvŖvz̀- $sųsog][SS[^^^^^^[SWcFl.X,C]VlX,\\WUS[[SWcllW,<]VXcWS[[[UVFXX f`]VXWS[[[UVFXX˅1qB`]Sc.YG1ƥØ9O9٘|DO][WZFWsBWUS[[SWclXX,1g]VXXY(g]VlXh`]^Ul(Cwz&--:Mvv&&:Iyv5$ :@zvz5$$7zvzn> $3 jkzH&3 krH--3-kv} -$_ky$-H_yn$&;_zy&$ _by-$Akyv-$ ARv$7@vz&-&:MzzB2 \][U N򘞯qg\]TlX\^FT]]^^S$ $-j My悄$$7|_:BaE`Ul.^1tx: -\]cFZU``VFXYN`]UVY0#xu"h\^lXY\`^TV,0q%"\]ll(\`^TfBD?g]llYUG1DQ`[UG\^FlYU(/oxx%u%"xx=|g]SSf\[FFYY(1\]UVc fQ'&\^lX0\`VlY#g`V.(`]UVVU0/#2g`WW012-q`[U\^FX Nxr$&& }yv7$&$}yv$$-$-zvb&;$&z vm&I$$ vv-bp$$ vy-Iz&$ - yv ;z>$--}ve}I$--v};vz$$vzyy$&& z}zv&$$zyv>$ &-yvm$ $&yvn$--y}6QOD ųB`SlKXSƗ@k:t\SFFYf`WFTS`]TXW,1so\WlX,\^cX,/#4t\Wl\^cc רPB\Wlg]WVWC\Wl˶ gd2 |͇ Js2%Ӈ`]WVU\`VlXY,<`]TXcצ6R&o\WXX<`WFXYGq`WlG`]TXXYf(\CS<#+&g]WV<\Wl.0#ϳHxA΂̺H%rAI vOys &s: -sA 5-x:m ̊Ay}P k?{ 3 3-h-΢3 R7Ν̆@_@΄ImsĤt\^Z ǥtx\]c\SF,\]cXW\]lf\^XYf« g]lFf\^X EذD\]lW\]TZ\]ZXYO~:ԑ>Op6="Ok%:_kAIH_7AI_-AH ek:J{}AO ~m@OԆp=A>;Ԃ}>Ab&>Rb->:B_x&\]TFKV////CN\]WlXf\]clcU\]VX f#|D\SF\]Z01|s\Sl,\]ZZTf\%?\Sl<#`^c.cg]cXXY,0s"% sx|2`^cVf#\]XXX0\]VXc0:J\Slf\]clW\]cc(\`WXFlllXXXXXXXXlF/Bs`^clfog]cXU}_v$&Iv-&vso܏`UFKX , ]]UZg`VlX N`TX ˩/ƞȞ\^lX,`VYtou\^lX``V.&Խn$=-ʽ$ ŸM̮&>ڝŷvIŚvkŖm&ڄb$-$Է I-ʷ×H-zڐ|}y y$- wݏ6h2zt\[lKXXXYU]]Ucg`WlX \SllYfNB9\]ZX f`WFVYgo\]clC`WFYGf$-}zr> $$yv_$&yvk&$Hevk >$HIyrII$->b}vbI$-5v}& yB#?Q\]WZlllllllZcTS^^[WY1\]XFD ?osLBIObAue"JA;MO;B;úȄHB \`^S[^^^^^^^]]]^SY綶\]cF0Qg]cXU,][cXGg`WFKU(`^VlN\`WFKX˶`^VX<`]WXXY\]XXZ,/Px%B`]Tl02\[Fl0\]cXYf][cXg]cX(g]cX\]cXX˶S,G]^cXY`]WZ.\]XX˅0ỚĮ} wŧyrŗ{$ km$-a} $ ԝ-聄Ův Hŗv& &ŖH$$IH$I& zς $3-b$Rv5-d\]^]`\\\\\\\\\]SXVT,fg`TlXYGgq\]ZXXYYYYW^]SVNq`^VFKKXYYG][VZoQ`^VFKK.VY G][VSg`]TXNQ|d\WlX`d+9x=%L"|x%B`]VlXY&}y:rH$-}-7rI$>} >rz$$z-kv&$}-_v7$e-HvH$b Ivj$Iz-5 3̺-&\]UYWצH92\]VXX<`Ull\]cXXXXVU^^U(ot\]STcXW[]ST 8\]STcKXW[]STˉ`]VXTN\]ZXX f Joo`^VFXY/\^lXZ,\]cXXXXZU^^UYfB`Ull˩N#`UlX,g\]VlXXXW[^SV1)9`]VZY)&7\]ZX/ݐkΗz-vkʖy- yh摊y􂐬 $7e $ӓ7&$ʖ3v}:v =v bՀ&;-$-x բ-̳$&reϝ&vԀ} Ղ9 Ąչ\]VXg۲kBg]Vl\[F<\`SVZZcW[]^S#\][][TccTS^^SGB\][G][TccTS^^S `^VCٳt\SlXou#%o`^cY/g`TlF1\`SVZZcW[]^qŢh\[F<\[FK#\`[TcZcVS]^Sq`^Vɾʮ-\Sl#ƪh Ş3-żvk?Ŗv2̋ -ϽM:-ʷ:&&}ŐA>}ys>yԖ&=$Ů_=$Ŗz> ʀ}MR7:>{m\]VFFY^\oku6\]XFFY\]ZX \sud\`]]]]]]`qs\]USf\\`]]]]]C\|u\]UGf\\`]]]]]`16Oo\]WY<JO)|g`WZ #89as"9?g`[cNAs\^ZXXY,0#g\`]]]]]]C2OOҔ}\]ZF^g\]ZX02d\``]]]]]]gQEHs\]WYNBPՖyg`WZW\x -:Rk-7Ak@O"_|"OHhx:>J_Ճ53@|_eԔ::ԁAbԂ5@eRaՄ3 Ax%;AO%>PA>JAIՁ-;AeՀ} @I>O"kwy&&g`Wl,C'Bxd`WFK(\`VlY(/&Dq\\\gB֐d`WVCg\\\dD?%"Q`WVC\\\#hsuaq\`^Pu7\]Sos")u֘h\]SZЙr&E`WlXYBBq\\\Qtx33&d`VlY(1g`VXYDDd\\`EDIQ\`^PI$s\]SCB $̆vmk $-knx-$-un -}ΐy k̋v$bvv3&@} 3 $7} 3&y- !ԗvJ -ΐvHM-zvI&-z--}hӡB`SF`8\[XK(<`Wl˅&ېIt`WX鉦uÇ)|q`Wlfs?Ӽ> ?ЧÇۙ=|۱`]UZa\]ZlY:~̽@q`Wlɦ`WlXų z|DvmτH _ >$P&$㮗$;ڷ}}Äz_ӂߖ_>A&&$έ-خIŝ}}Ăx|r6yn& M-k&zvD\^ZXU/f0/<`^cKfg\SFXY,f|&z՝z;D\Sll˅Q uxu|s\SlUs%%|% u*aa`]UXfo Mzd\UF.Y#sM$M\Sl\SlX '}Ԣ}$ v}Ζz - v̎y -vz$y}-$5-$$̐Iᚊ-&zIҗv! }Iv΄ }v}$Me}$Գ-e-$$&-$&v΋ $vyn-vz-y &}-yR-MvA$ju\]cFlZVT,U]^VFy7-> 6{ԏ&\]TlKXXY[][VX\]ZXG'kz$ʆR}\]ZXfC|͇D\]Zֻ 4u?ӻ|= ?ho`]TX tĂ Q\[FXYԮ7ʮxų&6:Ĭkŝ-7ڳkł-ڂ}sʷ3λŲ$ŻAŗ} AzzP} Մ>Գ@B~d`UllXlXXcU[^[Tsg`VXfxw A& }ņR-Q`VXN\>ʇևg`VX¨toÒuӯhàȘhȁœO: o`^VZT+\`VlXo "ήg`VXfǫd`VXVųvAP$łvABHQ #὚ݥk Ős_zh_3}mx 3-}3}  ~)4Ηv?;ʐv:-}v"A&&}AD&ՄPB& Քj& -_&$RyD\^TccccccccTU[]][S0duD`WXV`s)|z $bIz &B`WXV<&P6xD`WX01du%|"%h|%u%O|a3Aw!>E`[Vcq>-:k}j\^lX^t)a~  3Ry-HyD`WXc`Ps`WXN8x"&r&3ke$k-3_&Ry 3_&_v $H}&:r&7}-3kz>z;:_}e7p-$_yM-e $Rv ;}-I$:v;zM-Ak-b ;-:_-b-3_y-Hy$Rv-7v$:y53k-@p&m-7y&m} -}->z  }-$\`]]]]]]]]]]]]]]]# \^c ΄v&\^c)ֳ%͇\^cY/uӓxuxJ "ux7̄E`]TY/M"$ ̆\`SZs*;}pM\^cYg\^cWxx--n$--Ձ$-~$-͖̗v zʐv" ʎb"zb $Ԃ-L $-$ޗvM;̐vϳyIvκy jz$ z$- $&e$-y z) 3 7\\\\\\\\\\\\`\Nm\`[N z M\`[ɴ3Hu%\`[|6aÇu)|x͇uusu-\\`^$ų-P\`^ǠR w@ڢ_\`[܁\`[ㄚ ծ$%η9$3ķH:⽄żbv?̂ŵr2赝y - - -:ņ:nłŐAnvPmyIԔ --ΰ3-Ų@a&ŖIȳe} kh3$&}}} &-nmΖmP"3p_v_5-$5r}yz$IM}y $ MMv$-MIw&JOJ~w~&!3w{&!!3wwb!37}vn7&7vz7&-$v}yb-$kmzk--$kkՀr> $&kk}zM*$mR vp&$IRMvy &$RIyvM-$-HHv;$&7:zvՄ&&73zr7&-3vH&3-vvb$3&vv}- &$mky>$$}k}zH-$r}z}-$-kI}y $&__}v $&bkv&&MHyz-$ @;y} & >5}y AkIHHmnRA-_@O)-"| A= ~AI}RH ՄHR&}PAP7-ՄnR-eO&%ՄOP>_?>̡ϞR?>uRAA }kAA-|AO-|_B|JBHԢvOR&zAA3zRA3RO-3_B 3sb6Ρhk:x_ :5__:ՀkMA@ xAeO9 Ֆ=A ԏ}JAIԀzաRI-_Ik kŎ-3p}-3oh $ $ήݗr$I_H$-Hń&@-) }νvĽy56Ųy5:w-:;*ݯwMnkI3 kń$hʀ&oԆ-̷ŷ _ŗ}_py-Re--6M&M蓂ݪ}bڙz>ځvʄ >--->wv)r z>y-:s 3-x$$ ՝ ~v$Ղv$ev&- Մv: y&&-y-$-3|& IP$Νvjkz ̝vRvvR$&H$>~$ y$ zxy& 5ހwvHmH -p$@-$6-$&|-ϗv ke Ύv"k &΀v k $Ԁ&_$ b$ u>$ ᳀ ᡀv vz &z$v&-yy&}>&z-b&yM&&|O:HH -I$;ye&z--y>$zm3$Iv- 5$Mv@-e$v_&&-v_&}&-v_$jy-&_$-yz-$jr3&yy &;y:$vr-Iv:$yr5-}@$ubHvHb7vH~-v_$yxM7$~x3-յI$"x:ԡI$v@ vA}y -v_Iy-k$Iy -p& z p$-y y:$r >$r5 >&y5 I&MvI &zb--&݂y- ̮?$ʽ}շ;$&̺y&v ȳ" á -ս&ʷ@$-Ͻ}Ŭ:$vŖ_ vzxԐ}-& Ů-$œeŮk$uuŚrIŖp Mz}〮  ʻĮŘ3$Ųvw3& ڢvy=ڂz--ņI $nŐbjymՂ} }&̧R݂PՄBu#BʽBHA ĵ;uA ̂AIO IQo9ϯA );A=5ARP;O_΄BP#o#LλQ_@߷B:Že@AAʵjbAO>uRBaBB@=BR@ԄAAA"ROAkOOjBODeJPՁeuA ~IϖA΄MAL AJHP~:A vv $-I}y- >y 374_kv7$Iy}-z}I&-pyIM&7IvH>$3I;ve$>kzb-$-kynm$-_yy$$-:rv&$-3kv-&3bwv-3&Hv3-:nv}>-3bbvI-&$IIrzHH-$>;kyIH$7 mvbM$3eHvbM$$@rkp&$ Hk}yy-$-&_z}y$--Ry}y&&&Rz}v&-7pv-&Iz} 5&> yz- $>}y5 ->v; -$y&m}&p&--բ--Ζz ʝ2k9jږr;v&-5w&$-&淝;Є̱vJʢv@ ĝvA:$-6$ռ-z$νsy zhv>}Rv >ՑR$7 b$-$ źݙvM܄ŗvkŋ R zR$ς&A$Ԯ$Oά)Νȳ}kz&5k&y$}&&y&y&&ij&ҧ-đ-ݗ ΂ڐŽ_藐̄ $ʼ$L L$x㽢rݽy ryby >; $)&$J Ľ&b żbvų"uvń 〲$- $I̷7 bŷ}-ۊaڬyv~ yb$ 5$ͮ3Ú7z܂zy΄턊Մ &--̝-zʂyzzk$  r3 v7&7̎kO zRyb- }m-$&r&;y3$yv7xA$vHzR$vk-}_$- yvyk$&-} }k:&- :$!& zMR$M&zՋ_3$&yk$5z3&zk3-}3vP-@$Ց_&H$- k-_3-;k-k3$ }r>r: ՀvyA& v{Mvh$-& yh$---} }x:--}v:&&vI- -yb- zr-&-v- -&yy&z&}n$-blobby-1.0rc3/data/gf2x/font00.bmp0000644000175000017500000000337012042452401020125 0ustar danielknobedanielknobeBM6( #$,.,"0";ITU G =. C4DTUTF]F?r>=;;985/, x I # /$/vlwgygbbbbwwdb~}tqNJ^ , \Q]~~zzww  XhWّ~95 r 6  ml``PK | 3  ;P;?O>VS s . }}+:+AU@D@ g  on)MfM}z,' Q  ȵee-balh ,  ji1@1 {yGC g ơon:O9LgKlj  5̯~~IeH rq:5 djqjʝ__ HiIUQ .2/ٶvu8R8 ^[6ٽWxW) XU,'D7:7εٴ{z>V>  ;9.) F xy̪өhg,<+.++' Eؿҧde*;*  =;.*$!G BFB³ˮ׭_}_):)  8B?41/,RNTNӳڲqq7M7" DB@=>:?<AILI˸ܷ_^5P5"6"('2U3IGURa][XNK 695ɸ綇on[ZTQljywꈍqo8bgbν弰⯩ߨ鯴ꬋ‰-B-7;7jqjȶǰDTDblobby-1.0rc3/data/gf2x/pokal.bmp0000644000175000017500000013672612042452401020141 0ustar danielknobedanielknobeBMֽ6(h   FDTJL bdDBD,vt VTTnl .,$fd$NLTZ\lq̬d,., OObd4VTZ\tjlLce44v|ԲĞ <<T\v|,T<$&$d^\LPT<:<~|侼$TRT $rt^\|rt~DJLĥZ\RTܶlbd$JL\^\Ԯ\VT,fl$Z\[OOOOOOO_OOOOOOOOOOOOOOOOESOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO_JEOOOOO- [OO_ _OOO_>J>-OOOOOOOO_SOOOOOOOOOOOOOOOOOOOOOOOOOOS4S-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO-E_OOOO-S--OO- OJIPE_----N>JOOOOOOOOOOOOO_O S OOOOOOOOJX-OOOOOOOOOOOOOOOOOOOOOOOOOOOOO[OOOOOOOOOOOOOOOOOOOO[4P  -_OOO EK>>.OOOOOOOOOOOOOOO-E_OOOOOO-@PM OOOOOOOOOOOOOOOOOOOOOOOON-OOOOOOOOOOOOOOOOOOO- _OOOOOOOOOO-[OOOOOOOOOOOOOOOOOOO-OOOOOOOOO /@PXOOOOOOOOOOOOOOOOOOOO_OOOOOOOOOOOOOOOO_-O_ SJ>NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO[KN_[4X[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOKMPPP. -OOOOOOOOOOO[-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO >_OOOE[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOON4I@..E[ _OOOOOOOOOMOOOOOOOO_ OOOOOOOOOOOOOOU O-JJ> N-OOOOOOO __-[EEE -[OOOOOOOOOOOOOOOO @F>X--E+EOOOOOOE@JOOOOOOOO_OOOOOOOOOOOOOOONS[N[N SK-OOOOOOOO_OO-__->I-_E-O-O_OOOOOOOOOOOJ].S-OOOO-.J^E-[UP@ OOOOOOOOOO__OOOOOOOOOOO_O__O__S-OOOOOOOOOOOOOOOOOOO->EJUSEN-_O[--_OEEN_OOOOOOO_ SE[ K>SS -OOOOOOOOO_[NK-OOOOOOOOOOOOOOOOO[S_OOOOOOOOOOOOOOOOOOO .Q..>J>[NS.OOOOOOOOOOOOOOOOOO_OOOOOO[-_OOOOOOOOOOOOOOOK_OOOOOOOOOOOOOOOOOO-EOOOOOOOOOOOOOOOOOOOEE___->.[_OO_-OOOOOOOOOOOOOOOOOO [OOO_OOOOOOOOOOOOOOOOOOOO-[OOOOOOOOOOOOOOOOOOE[OOOOOOOOOOOOOOO E.-[N+]_OOOOOOOOOOOOOOOOOOOOOOJ OOOOOOOOOOOO[OOOOOOOOOE__OOOOOOOOOOOOOOO K_OOOOOOOOOOOOOOO SEOOOOOO__-OOOOOOOOOOOOOOOOOOOOOOOO_OOOOOOOOOOOOOO[SOOOOO>P>OOOOOOOOOOOOOOO S.OOOOOOOOOOOOOO_[-OOOOOOOOOOOOOOOOOOOOOO_-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOK-OOOOOOOOOOOOOO-.EOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO S. _OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO_OOOOOOOOOOOO_ J[OOOOOOOOOOO-_OOOOOOOOOOOOOOOOOOOOOOOO_-[ OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO-OOOOOOOOOOOOO_E@-OOOOOOOOOOU4SOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOEOOOOOOOO-[]OOOOOOOOOOOOO_.OOOOOOOOOO-@Y.-OOOOOOOOOOOOOOOOOOOOOOOOOOO-S OOOOOOOOOOOOOOOOOOOOOOOOO-OOOOOOOOOKQ_OOOOOOOOOOOOO OOOOOOOOQ.M-OOOOOOOOOOOOOOOOOOOOOOOOOOOONP-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOON>SOOOOOOOOOOOOOOOOOOOOOOOOOOO_OOOOOO >-OOOOOOO- OOOOOOOOOO-N[OOOOOOOOOOOOOOOO  OOOOOOOOOOOOO_]E>MP.OOOOOOOOOOOOOO-E_OOOOOOOOOOOE [[> -OOOOOOOO_.PMM OOOOOOOOO-O_[-OOOOOOOOOEOOOOOOOOOOOO_UE_OOO OOOOOOOOOOOOX@E-OOOOOOOO_[XJ>-OOOOOOOOOOOOO OOOOOOOOOOOO_>E-[OO_OOO-M)OOOOOOOOOOOOOOS_OOOOOO-OOOOOOOOOOOOO- OOOOOOOO-US[].OOOOOOOOOOOOOOOOOOOO -OOOOOOOOOO[U. EEEKO__OOOOOOOOOOOOOOO>M[OOOOOOOO_U OOOOOOOOOOOO->POOOOOO[ O .OOOOOOOOOOOOOOOOOOO-E-OOOOOOOOOOOOO_- JOOOOOOOOOOOOOOOK-OOOOOOOOOOE-OOOOOOOOOOO_-JP>[OOO E>>E_OOOOOOOOOOOOOO N[-OOOOOOOOOOOOOOOOOOOO_ES.OOOOOOOOO-OOO[KOOOOOOOOOOOOOOOOOOOOOOOO_ J>OOEEJ_OOOOOOOOOOOOOO-UU OOOOOOOOOOOOOOOOUS_OOOOOOSPME-OOOOOOOOOOOOOOOOOOOOOOOOOO--_OOOOOOO [[]K[___OOOOOOOOOOOOOOO_EOOOOOOOO OOOO_O-_-OOOOOOOOO >E_OOOOOOOOOOOOOOOOOOOOOOOOJ>-OOOOOOOOOOO_[OOOOOOOOOOOOOOOOOOOO SOOOOOOOOOO S-[-OO[[_OOOOOOOOOO_OOOOOOOOOOONOOOOOOOOOOOOOOOO[[OOOOOOOOOOOOO_OOOOOOOOOOOOOOOOOOO--OOOOOOOO[OOO--+ EN EOOOOOO[S _--OOOOOOO-[--OOOOOOO _OOOOOO-OOOOOOOOOOOO-OOOOOOOOOOOOOOOOOO S[OOOOOOOO >-OOOOO>>U>[[EEE][OOOO@___OOOOOO_[ E[ . _O-.. OOOOOOOOOOOOOOOO >_OOOOOOOOOOO @OOOOO EEOOOOO_. OOOOO-QJS --QMSU-O--OOO-OOOO_ OOOOOOOO_-USMS[--OOOOOOOOS@>S -OOOOOO_E. SS _O[[EMJ KOOOOOO-S_OOOOO_][ ES[_O- _O[[-[EJOOOOOOOOOOOOOOOO--OMP>+OOOOOOOO->4@M@[OOOOOOOOOOOOOOO[-O_OOOOOOO ]_OOOOOOOO_OOOOOOOOOOOOOOO[U OOOOOOOOOOOOOOOOOOOOOJMKOOOOOOO_SK-OOOOOOOOOOOOOOOOOOOOOOOOOOO[JP[OOO_ -_OOOOOOOOOOOOOOOOO_--OOOOOOOOOOOOOOOOOOOOOO_ S OOOOOO[_[[ [OOOOOOOOOOOOOOOO>-OOOOOOOOOO-UOO_ >OOOOOOOOOOOOOOOOO_O-- OOOO_OOOOOOOOOOOO-NOOOOOOOOOO- SQEP4X_OOOOOOOOOOOOO_4P-OOOOOOOOOOO-_[ E OOOOOOOO-OOOOOOOO__OOO-O-OOOOOOOOOOOOOOO-[OOOOOOOO-[- S>[OO_OOOOOOOOOOE OOOOOOOOOOOO[N-OOOOOOOOOOOOO-_OOOOOOOOOOOOOSP[-OOOOOOOOOOOOOO_OOOOOOOOOOOO>MNOOOOOOOOOOOU)PEOOOOOOOOOOOOO[NOOOOOOOOOOOOOO_OOOOOOUS-OOOOOUSE-J>-OOOOOOOOON]SN-OOOOOOOOOOOO U-OOOOOOO]S@-OOOOOOOOOOOOOOOOON-OOOOOOOOOOO[-OOOOOO@EOOOOOOOOOO--_O-_OOOOE]>SOOOOOOOOOOOOOO-[_OOOOOOOO-P_OOOOOOOOOOOOOOOOOOO[-[NQ[N OOOOOOO[ --- .-OOOOOOOOOOOOOO_.XUEEJXO-E _OOOOOOOOOOOOOOOO_>OOOOOOOOOOOOOOO_O>E[E-_ .M-OOOO[..[[>PJ_OOOOOOOOOOOOOO /P@IS[-_OOO- JJ-OOOOOOOOOOOOOOOOPOOOOOOOOOOOOOO__[[Q>]OOOOOOOO_ >]-OO[[OOO_EOOOOOOOOOOOOOOOOOOSIX [-OOOOOOOOO OOOOOOOOOOOOOOO[.>_OOOOOOOOOOOOOO_-QJS[OOOOOOOOOOOES[_[_OOOO_KOOOOOOOOOOOOOOOOOOX>-OOOOOOOOOOOO-J>NOOOOOOOOOOOOJPKOOOOOOOOOOOOOO_--4EOOOOOOOOOOOOO-SOOOOOOOOO[-OOOOOOOOOOOOOOOOOO -OOOOOOOOOOOOOQP^-OOOOOOOOOOOO[S OOOOOOOOOOOOOOOOOOEI OOOOOOOOOOOOOO.EOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO__OOOOOOOOOOOO-+-OOOOOOOOOOOOOOOOOOOO-E-OOOOOOOOOOOOO.-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO- -OOOOOOOOOOOOOOO OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOK_OOOOOOOOOOOOOOOOO-OOOOOOOOOOOOOOOOOOOOO-[ _OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO___OOOOOO--_OOOOOOOOO-_OOOOOOOOOOO_-__OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO__OOOOOOOOOOOO>UOOOOOOOOO_OOOOOOOOOOO MN-ON]]]++Q+]]]+]]]]]]]QX>N++]+]]]+++]++]]]]XSQ]]+Q]]]]]]]+]NQ+]]++QN+]]]]SS>SXSQ]>S>QQ]]]Q]Q]]Q>QQQ>]K]Q>]Q>>>>>]]]]]]QU>>X.X.QK]]JMXSU+ ]]] ]QQ>]KK+EEQ+KSXQQSJ>++]Q+]]]]++]]]]QQQ]]QQ]]]]]]Q]++]]QQ]]]Q]Q]KQK]]]KQQQ]].PG7FFF'$FFFGFFGGI)J>EKN++]Q>SQQ]]+]]]]]]]]]]]]]+SS>Q]Q>SXSQ]Q]]Q]QKQ.^GFGF'GG7FFGGFI/PX>>>]]]]]QQ]K]]+QQQQQ>/JX/U/F&77777777111177'''','&'''',,Z\VV:J.>Q]Q]QKSQQ]]]KN]QQQQQ>>>XJ/JXXSF''77111111121111??= LL""LLL%LZZ'77777'''7''7',09QKQU>X>J/QQUQQQ]QQ+''771166666666???== (LL"R""""%ZZZ#C,,'77777(V9999! :JSK]Q]QQYSJX>QQQQQQQQ>F',?6666 6 66666??== (LLR""%Z,',D(==7'R9999!!99S>XSU>QQQ.(=??66 6666??== (DL"R""RRLZ'7'CLD(,999!!!!!!99<&XQ]QQX/.U>>QQQ,=?66 ** * 66??== (DLL"R"R\,'7'CDD( %!!!!!!!!!!!999Y++>QQ>UQQQ]>Z=?6 ********* 66??= (DL"R""R%Z'',DDD(  !999999!!!!!!9L?66 ****8888*8*88 22 6? (LLR"""%#ZCC(D  ?6 ***8888888888 52 66?== (RR"RL%LL(6 *88888888888888 88 8** 666?C((RR"LD(   *8888888888888*****22 166??7?'=C(RRR""""RL   **** *****8*8*8*28 2 6?? ===CCCZ%%%"""L66 ****888** 611? #(DD%RRRLLL""00" ?66 66 =C=',C''',DLL"""""RR\\\$\\%R""LLL("00B"  (#CC=,'??7?7? ==C(LLVRL\ZZZ\%"\Z\ZL(L =?6 ******* 555226??==(DL"R"RC,C(L(((D0=6 *8888888888 O5266??== (LL""R"RR(L ?6 *888888888888 5OO5* 6??= DLR""LLD 6 *8888888888888 55 * 66?=((LRRRLL(?6 *88888888888888 55 * 66?== LLR"""RLLD?6 *88888888888888 55 8* 66?== (((LRRLLD(( 6**8888888888888 5 * 6??= (LRR"RLLDDDD"?6 *88888888888888 55 8* 66== (DL""RRLLD *8888888888888 55 * 6?==((LRR"LLLDDL6 *8888888888888 555 * 66?? =(LLR"""LLD(D *888888888888 55 2 66??= (LL"R"RL(DLD6 *888888888888 58 6??== DR"RLLLLD *88888888888 5O 66?= (LL"R"LL((L6**888888888885O 6? (LR"RRLLD((6 *88888 *888 OOO526??= (LR"R"LLD0?6 *88888888885_OO5 ? =(L""RLL((? ***888888885OO 6??= (LR"R"LL((( ?6 **88888888 O26? = (D"RRRL((B *888888888552?= (DRR"LLLL((6 **88888888 526 D"R%LLL(=? *** *88**2 2 6? ((L"R"%D\LD( 6 ********* 6??= ((L""RRZC#L(?66 ******* 66?=L"R"R,,DL  ?6 ** ** 6 = (DL""RZ,,L(( =66 ** * 666?= (L""R"RZ',#D ?66 ** 66??= D""RRZ''DD=6 66?= (LL"R"%,7,(D 0=?66 * 666?? = (("R%,',#(B=?66 66?= (LR""%,7,D(=66 666?? = L"RR\,7,#D  ?66 66?= (R""%,7CDD 66 666??= (L"R"\'',D  ?6 666? (LRR"%'',D( ?666 6 66?= ("R"\'7CL(  66 6 666?= (LR"""#''CD "=666 666? L""R\'7D( " ?66666666??== (R""#''CL B ?666666? (DL"""R''#D(BB ?666666?= L"R""#''( ??66666??= (D"R"''((  ?666666?? (DL"R"0R"#''#L =666666?= (L""""R''#  ?66666?? (DLR""#''D =??666666??=(L"R"0RZ''(  ?66666?= (LL"R""Z''D =6666666? ((L""R""Z''#  ?666666?==(LL""RZ7,(D =??6666666??= ("RRRZ7'(D  ?666 666?= (LR"RZ''(=666 6 666?? (L""RR,7'(D(  ?66 6666==(DR""RZ7,( =?66 66?== DL""RR,',#L ?6 66??= L"""R,',L B=?66 66? (LR"RRZ,CD( ?66 ***** 6??=(L""ZL=?6 ****8*** 2 ?= (L"""%LLL  *88888852?= LLR"L= *88888885 ?= (L"R"RL6*888888888888* 6??= DLR"""L6*888888888888** 66= CDLRR""6 **8*8***** 66= (LR""0??????? ((DL"R00B=666 6 26?= (""066661 L\LL" ,7CL""""RL ='C(L"R"0""""LLDLR""""( ( (=C(#LLRRR""""""BBB=?66666 = DL"R33B ** ?= (R""0 *8** 6=D""B?88888 6""R"666="RR000"(30"R""D" (""""""""B"L0"0"R""B"" L"""030L( 7C""""0"0300"D= ===(LR""0003000""RD D"""0000000000""""L(((DLR"""R""0000000000000000LL"R"R""""R"""0"000000000000000000ZZ ====',DLLRRRR"R000000000000000000000VWZ==== ("""RRR00000000000000000000000$#C,CC(L"%\V\\#%V$000000000003000000000000000$Z#C,=CZ\\\\%LR0$V00000000000000000000000000000ZC='=CC%%%\\\R%V000000000000300000000000000\$Z ?7=C(%%L%\%#$0000000000000000000000000000000000$,7== LR\%$Z00000000000000000000000000000000V,==R\%$0000000000000000000000000000000000000V;ZZ\000000000000000000000000000000000000V0000000000000000000000000000000000000,ZZZ;,00000000000000000000000000000000000R$V"0000000000"RV$\00000000000000000000000000000000000000000000000000000000000000000000000003B00000000000000000000000000000000000000000000000000000"""""""B"  = = = = = ,,C#LL"""""00"""LLLLL(?6 **8888888 8 88 88 55 22 6666??=C LLLLD%LRR"LLD(D(7777777771777777777777777'''''''''',,,ZZZZZ$$\$%$$\$$$$V$$$$VV0AY:T::&&&&&F&FT&TF&FT&TFT:&&W&W:W:WAAYA;AAAA; <<<<<<< HHH;;;;;;Y:T&&&FF44F444444444FF44FF&&&&:&&:T::YYAAYYAAH <<<<<< H;;;;;;;;HH9Y:FF&FFFFF44G444444G4444GF&F&&T&&&&TT:TYYYYAA <<SX.XXJXJXJXJJXX.XXXJ/J.J.MJ)J^PPP@@@@@@44GGGGGG44FF&&TWTAA;;;;;;;;;A;;;!@MXXJXS>X.X.XXXXJXXXX>SXXX.S..)>.S).)PPPPP@@@@@I4GGG44F&&TWWYA;;;;A;;;;;;@M//XXS>>.XXSXXX.XXXXJXXX.XSSS>>.SS.S..S>.).PPMPP@P@@@I4GIGGGGG44FT&T:W;W;;;A;;AAAWH;!@/M/M/X.S>>..XXX.SXXX.XXXS.S>>SS>.SSS>.>.SX...)/PMPM@PPPPPPIIIIIG4FF&T&TYWWA;;;;;;AW;HH;P///M//.SS>>S.XXXS.>.>>>SS..S>>S>S>>>>>SS.S...M^PPPPP@@@@I@IIIIIGG&&&WTFWWYW;;;;A;A;A;;H;&M/J/JJSS>Q>>S.SXX.>U>>U>>>UUUS>>.>.>SS>...S)JMJMMMPPP@P@@@@IIIIIIG4FF&&F:WWW;;;;A;AH;9PP//JX/>>QQ>S.>SS.S>US>>>>>>S>>>>..>U>>>S..JMMMMPP@PPPP@@IIIGGGF&&&&&TWY;;;A;AA;A;;;;@//M/JJXXS>]Q>>X>>SSS>>>>>>U>>>>>>U>S)JJJ/MP/@P@@@@@GIGG&FT&TTWY;;;;A;;;;;;////JXXXX.>]QQQQ>>>SS.>>>>QQEU>U>.)JJJJJJMMM//PPPP@P@@@@IIGFF4F&T:TWYAH;;A;AA;;;PM/JXXXXX.>]+QQ>S.SS>S.SSQQE]QEEU>E>..JJJJJ//MMM/PPPPPP@IGGGFF4FWTTWTWY;;;;;;;A;;;;;@///JX.XSS>]]QUSS>S.>S>>>UUQK]Q]]QQ]Q]EE>S.XX)JJMMMMM//PPPPPPPPGGGI4FFFF&TTTW;W;;;;A;A;;;;;;;HP/MJ.>SSSQ]]Q>>S>>X>>.SQQ]]EQ]]E]EEQEES..)J/J/M/MMMPMPPPPPPPPGIGGGFF4F&TTTWWA;;;;;W;;;;H;;A//JXX>..>Q]+Q>>>.>>>>>KK+KQ>>UQ]KEEEE>S.JJJM/MJM/P/MP^PG4G4FFFFTTTTTWA;;;;;;A;;;;H;P/JXXX>>>>]]QQS>>>>>.>>>>QQKQ]EKK+KQQKK>.S).S.JJMMM/MM/PP^^IGG4GGFFF&TTTWW;;;;;;;;;;;@//XXS>>SQ]QQ>>>>SQ>>UQQQKKKQQQQEUQEQE>.S..X//MMJJM/MPP^P^)^GGGGFFFF&TTT&TT;;;;;;;H;H<PJXX.>>>]]QQ>>>>>>>QU>>>>>>>QUSUUUESUSUSS..XJJJMJJJ/JM@PPPPP^^GGGG4FFFF&&F&TW;H;AW;;;;;;;@/JXX.>>>+QQ>>Q+Q>>>>>>SS>Q>UQQS>.S...XX//MJJJJMPPPIGGGGFF4FFF&T&T:;;A;AA;A;;;;/X.>>>>>QQ>Q>>QQ>QQQ>>>>>>>>Q>>>>>>UK>UUSS.S.X/JJ/JMMP/PPP^P^^GGG4FGFFFFTTTTWW;H;;A;A;;;;>S>>>QQ>QQ>QQ>>>>>>Q>>>>>>>EU>>.S.>X/XXMM/MMM/PPMPPP^)PPIGG4GFFFFF&TTWW; AA;;;H;H//XS>>SQ>>>+QS>>>>Q+Q>>>>Q>>>U>>+QSU>S..>XJJ/JJ/JMM/JJM/MPPM^)IGGGIGF44FF&&:WH;A;A;;;;;H;//JX>>>Q+>>S>>>>QQ>QQ+QQ>>Q>S>>>>>>S>QKUU>>.S.S.../XXJ/JMJJMJJ/MMMMPPPP^)^^GGG4G44FFF&TTTW;;;;;;AH9M/JX>>Q++Q>>>QQQQQQ>QQK>>>>>>>>Q>UQKE>U>.>S.>.X.XXX.XJJJXJJJJMJMMPPPPP)^@@IGGG444GGF:F&&TT;H;A;;;;;;HH//JS>>]]]]>QQQNQQ>QKQQ>>>>>>>QQNQ>>USS..S.X.X.XJXXJJJJMMMMMPPM)^GGG4FF4F&TTTH;A;;;;;;;A;M/>SQ+]]]>>>Q>QQQNKQQQQQQQQQ>>>+NKQSS>>SS.SX.XXJXJJJJJJMMMPPPPP)^^@IIGGG444FF&&&WTTW;H;AA;; ;;;/JJS>Q]QQKQ>>QUQQQQQQKQ>QQQQ>QQK[+KS>UU>>>SX.XXXXJXJJ/JJMMPPMPPP^^^IGG44FF&T&&&TT;A;;;;H;;;;/JX>>QQ]Q+Q>>>Q>QQ>QQQQQQQQ>>Q>QQQQ[NKSUU>>>.XSX.X.XXXJJJJ/MP^^^^@IGGGG4FF&&&&TWA;H;; ;;;P//X>]Q]+QKQQQQQKQQQQQUQQQ>>Q>>QKQKNQUU>>U.>.XX.SXXXXJJMMMMPJ))^^^IGGGFFFFFF,&TWWAA;;;;H;HHMJX>Q]Q]K+]QQQQQQNQQQQQQQQQ>Q>>>Q>QQKQK>UU>>>U>S.......XJXXXJMMJMMM/^))^@I4G44FFFFT&&TW;A;A;;H;;/XXQ]]QK+]K>>>QQQQQQQQQQKQ>>>Q>>QUQQKQQ>US.>SSSS..SS..XXXXJJJMMMMJJ))))IGGGGFG4FF&&TWW;A;A;A;;;;HM/JSQQQQQ++Q>Q+K]Q]QQQQQQQQQQ>Q>Q>QQQQKKQU>U.S.>XSX>SS...XXJJJM/JJ^^^@IGIGGGFFF&TTWWA;;;;;;;///SQQQQ]K+KQQQQKK+N]Q+N]QQQQ>Q>UQKQQQQQ>.>>...SXS.S..XXJJMMMM^M)PIGGGF4F&&TTWW;A;A;;H;M/XQQ]+]++KQ]K]]]+NKQ+KKQQQQQQQ>QQQ>QQQKNKQ>US>>>SS.S....XJ/J/JMMJM^^)^IIGGGFFFFFTTA;A;;;;;JJJXQ]Q+]KK]+QK]K+N]NQQQQQQQQQ>>Q>QQQQQ+QQKKQE>>>SS>>>.S.XXXJ/JJMMMM^GGGFFF&TTW;;;;;;;;;TJ/X>QQ]]QQ+Q+]QQ]]+N+K]KQ]Q>QQ>>Q>QQKQQQQUU>>>>.>>S.....XJJJJMJM^)^IGGGFFF&:W;;AW;;;;;A;;JJ>>Q]Q]]K+++]Q]]K]NNQQN]QKQQQQQ>QQQUKQQEQU.U>>S..S.X.JJMMMJJMJ))^^IIGGGFGFFF&TTW;;;A;A;;;;;/XX>SQQQK]+KK]N]]+Q+++N]]]]QQQ>QQ>Q>>Q>QQQQKKQS>E>S>SS..>SS../MJ/M^)^^)^IGG4FF&FT;;A;;;;A;/JX>>QQQ+]QQK+++]QK]K+K+N++]QQQKQQQQQQ>QQQKKQEQN Q>QU>>>>S..J/JJMMMMM^^)^)^IGGG4FFF&TTW;A;;;;;;;;9JXS>>>Q]Q+]Q]++KK]]]K]K+NNKQ]QQKKQQQ>>>QKQQKKQQ]K]]E>>S>S.JJJMJMJMMJ/J..))PP^GG4GFF&WT;;;;A;;;H/J>Q>QQK+]K+++K+++++++++[KQQK+QQQQQQ>Q>>QQKQKQ+QEQNN]EU>.>..JJJMJJ.J))))^@I^^^GGFFF&TWA;;;;;;;JJ>>>>Q]++N++++K+++N++++NNNQNKQQ>QQQQQQQN[K +K]KK]EUSS>....X/JJJJX...)^PPP^^^IGGFFFFFTW;A;A;;JX>>>QQQ++K+K+++++NNN+K++K+NQ++QKQ>QQQQQQQQQKQQUQ+KEUSS>U.SS...JJJJ)).))P@^^^^GGGGFFF&TWWWW;;;;JX>QQQQ++QQQ+KQQK+K++QQQ+QKQK++NQQQQ++NNKKQQQ++KQQKU>USUE>>S..S.JJJ..)^^P^^^IGGGFFT&TWWA;AA;;JX>QQQQ+Q>QQQQQQQQQQKQKQQNQQ]K+NN>Q+KKK+NQQKNNQN+QQEUS>UUUS>>S.S.JJJJ..))^^P^^^^GGGFFFFTTWWA;AA;A/S>Q>]+]QQQKQKQQKKKQK]++K+KQ+NNNQ>QQ++N+N[+QNKQQ>EUSUSU>>...XJJ/.J.J))^^^^))^GGGFFF&TTW;;WJXQ>+]QQQK]]QQQ]]]]QK+KKQ]KN+]KQQQN++N+KKK+QUQKK>QQQ>>>UU>>.XJJ..^J)PP@)))@IG4FFF&T&W;A;WW;A /S>Q>Q]Q]]QQQQ+++K+KQQK]K]]QKQQ+++++QQKQKQQQKKK>KKEUU>>SS>XXJJX.S..)J^^^^)^GGG4F&&TTWW;;AWWWA;/JXQSQQ]]QNK]+K]Q++]Q]QQKQQK++QQKQKNQK+N[[[N+[Q>QQUEU>S>>..XJXX/...)JJ)^^^^P^^))^I44FF&&TWW;;;;WWWAAM/X>QQQQK]QN[N]+KQNNNN+N++Q]NNNNNQQQQQQQ+KQKN[[N[KU>EKEQSU>>SSXXXJ/..JJ^J^^)))^GGGF4FF&&:TT;;;;WW;;WM/>QQQQK]]QQNN+K]Q]NNNNNNNNNNNNN[NNNKQQQQ++++QN[[[QU>>QSSSXXXX.XJXJ..SJ))J)@^))GGGFF&FTTTWWA;;WW;/X>Q]Q+QQQN]]]]]+NN+NNNNNN+[[[[[NN+KQQQ+QKQ+KN[NK>QEUQ>S.S...X.XXJ..S.)J))^^@^)^GGG4FFF&&TTW;;A;AW;W!/XSK+QK]QN[N+N]K+NNNNNNN[N++++++[N+NQQ+QKN]K]N+QQEUQUUS>SSS...X....XSS))^^^)))IGGGFFF&TFTW;;;;;;W<JX.>Q]]+QQ]++NN+++KN+[NN+[NN++KQ++]NN+NQQ+KQN[+KQQKKQQ>>USS>.SXXXXJ.XX....>SJ..)))^P^)^)^GGGGFFF&TW;;;A;W;JXSSQ+N++N[NNNNNNNN[N[NNNN+K++][N]QN++QK]QN[NK+NQKNNQ>U>U>.S.S.SS.XXX...........)^^))))GGGFFF&FTTWW;;;A;;A;;XX>>Q]++NN[NN+NNN[+NNNNNN++Q+++NN]KNNNNQK]QQNNQK]N[NNEU>>>>>.S>>.>.S.......JJ^.MP^^P^)))I4GGGFFT&&&;;;A;;AW;XXXS>++NN[NNNNN[NNNNNNN[[N]KQK+NNN]Q]N]QK++]+NNN++[NQUUUS>>>S>..S.S.X.....)JJ)))^)^^)^)^G4G4FFF&TW;;;;AW;X.>>>K++N[NNN[NNN[NN[N[NN[N+N]+[NN++]Q]QQ++KK]N[[NNKQ>UUUS>>>>>.S.SS.X...J.J)^P^^))PIGG44FF&F&TW;;;;WW;W.S.S>Q+++[-[NNNNNNNNNNNN[NN++++KNN++Q+]Q]QQK+++]K+[[QQQQ>>>>>.>>SS.....X...)JM)).)))P^))))GGGGG4&FTTWY;;;WWAWXX>>>Q+N+[[[NNNNNN[NN[[[[[+]N++KNNN]+++K++KQQKN+QQ[[KEKKQS>SS>>>.>>>.S.....JJJJ)^^^))IIGFF4F4FTT;;WW;AX.>>UQ+NN--[N[[NN[[[N[NN[N+NN++QN[NKK+++QKKQK++QK+[[NQQQQ>S>>..>S.>>.S.>..JJJJ)^^^^^^^^))^)^G4F4F&&&TTTAWWWAW .>>QNN[[-[[[[[[[-NN[N[[[+N[N+]NN[]]KK+KQQ]+NKKQ+NN KKKQUS>S...S>>S.>...S.S..JJJ^^^^^^^^)^)^IG44FFF&&&;AAW;WAW>QKQ+[---[[-[[[[NN[[[[-[[N+N++++++++]]K]K+++N+QK+KN]QQQ>S.SS>>>U>.S>SX...JJJ)^M)^^^^^^G44444&TW;;WA;W!QN]N---[-[-[--[-[----[[[NN++[[N+NNNNNNN+N+NN[N+N+[+]QQQ>>>>>.>>>.X.....))J)^^P^^PP^^GGF&T;WA;W;W$VSN[--_-----[-------[[[--[[--[+[-+NNN[[+N----NN---[QQQQQQQ>>>>>>>S>.......)^)^^)^P))^^IF&;;AAAAAW;;$00300$4-OO_O_O-_-O_------[-[-[N[+NNNN[NN-----[[--[N]]Q]]QQ>>>X.....X.....X..)))))^^^4F:;;;A;$$$V000""00VFG _____OOOOO_--O[[[N[--_O_OOOOO_O_-[[+K+]]QQQQQQ>>>U>>>>>S>UU.S>>)MPIF&WWAA;;;;;;;;;;;V$VVV0"LB?? $$$$$G^  O-[OO-OOOOOOOOOOOOOO_[[N[[NN+]++]++++Q]>>...P@@@IGF&::YHHH;H $$;$$H$H$$"R"R(D6 * * 6= (%RVVVVV;:F4FF4F4IIIIPIIG4GIG&F&TWWTW;HVVVVVVVVVVVVVVVVVR"RRRLL(LLR""6 **88888* 6? (%RLVVVVVVVV$$$$$$$$$$$$$$$$$$$$$$$$$$$$$V$$$VV$VV$VVVVVV00R"""""""R""000?66 ***888888888888*** * 6111?777'',=C((LRR""R"""""""""""""""R""R"%"""0000000000???66666 ********8*888888888888888***** 6666???==== C((D(DLLLR"""""00000?????6666666666666666666666666????==== C((DDD""""""0000000???????????????????????======((((DDDRR"""""000?????????????????????????????????= == === (DDLDDR"""""0000000?????????????????????????????? ========= (DDDR"R"""R00003B??????????????????????666???======= (DDLDLR"R""0000B?????????????????????6 666??======((DDD"R"R00000???????????6 666??=====(D(LRR"""""000000?6666666666????66666 6??= ==== ((LDR"R"R0000066666 6 6 666666666666666???===== D(DR"R000003666 66666 666666 6???====(LLR""""00006 * **** ** * 6 66666?????== (DL""000006 ***8*8888*8***** * 666666???? = ((LR"R""00006 *8888888888888888**** 6666???== ((LL""""0000 8888888888888888888**** 666??== ((DR"R03 *88888888888888888888*** 666?? = DR"003? *888888888888888888888*** 666??== ((L"R""006 *8888888888888888888*** 666?= (LL"""006*88888888888888888888*** 6?? = (DR""0"0" *88888888888888888888** 666?== ((D"""""""3 *8888888888888888888*** 66?= (LR00""06*88888888888888888888* 66?= (((R""""""" *888888888888888888*** 6?=(R""00RRB *888888888888888888** 66??= (D""""R"0""6*888888888888888888** 66?? = (D""R""0"0"0""R *8888888 888888888** 6??= ((L""R0"""R 88888888888888888** 66?? = DL"""0R06*8888888888888888** 66?= (L""""0R"R" *8* 88* 888888888* 6?? = (L"""0""*8888888 8888888*** 66?== (LRR"R"0"R3*888888888* 8888* 66? (D""0"0"R" 888888888888888** 6?= DL"RR"0""*88888888888888* 66? (L""R"B88888888888888** 66??= (""0R"0" *8* 888888888** 66 (R""0" 888888888888** 6?==(D""0 *888888888** ?=R""R%=?66****8 5 6 """LR =?1=="""R\LL?666 66?? D""%LL ?6 * *221= R""""(???77=(R""R,CLD " =?= D"""\',D(666666?? = (L"0"R#L3*8888**2521="R"""LLB6*88888 * 6?==(R"L0"(=????== ((D(("DDLDD((((D((((((((((D((((((((((B3((((((((D((((D(D(((((((((D(D(D(D((D((((D((((((((((((((D(((((D((((D(((D(D(DD(((D(D((((((((D((D((D(D(D"(D(((D((L""DD3((((D""00"D((D(D(L"""00"D((((((D(D(""RR"0(((DL"RRRR"0"D((DDDR"D"0(((((RR""((D((((DD(L"0R""((DLR"""D((((D(((L"00R(="D(((((L0R(=(D(D(((D(== (RZ D((D=? (RZD=D(((BDD(? 1(R=D(((D? 1?"=(((((((D((D((68855(" (D(6885""L D((D((D(D=8882="(((((D=888*="(((((((((D((D( 8 5?(R=(D(DD68 ?R"(((DD(R(((*2=D=D((D(* =D=DD((((D3((DD?* L"(D((DD?* L"=D(((((B("(D((D8 "D D((((* ?R (((((D(((R(D 885(D((D((=885"(((D((((D"((D(1886 D(D(((((D188 =LD(D(((D((D0(L"((D((? 6 L(D(((D(D? 6 DDD((((3(DD(((D((D((((((((DDD((((((D((((D((((((((((D(((DD(DD(D(D((((((D("D(D(D(D(D((D""(((D(D((D((((((DD(((((D((D(R"D0"(D(((((DD((((0(((((D(((D"DR(D0DD(((D((D(((DD(((D((((DDR"0D((((((((((("(((((((((D0% LD(D(((D((((LDD0((D((D( ?=("%=((((D((D((((("D(((((D**51("D((D(((D(D(((((0((((DD 8 L"D(D((((((D(((D(((DD?88*("DD((((DD(((B(DL((D((D=6 (D(((((((DD(#"0D(((((D(D(((D((D0((D(0((D((D(((((((DD((DB((D""D(((((((L"L(((D((((D""""""LD((B3((((DDL"LLDD(((B(((((DDD((D(D((((((DBL3Bblobby-1.0rc3/data/gf2x/schild.bmp0000644000175000017500000143741612042452401020302 0ustar danielknobedanielknobeBM?6(N  dnXK],6Mf|LVt,duTa4>T'%% ,! !!# !!!!  ! , "! ,  $!  !! ! ,#!! !#!.%( '# ! .% ,!!! ! ," , .# " !   ,#!! $ !'.'##%! ! %  ! ! (   ! "  , " ! !#!,'%(  '# ! !% !!! ,    " ! % ! . ! !% ,, !!'% #., ! ! # !!!!,,, " ! !,%, .  ! !,,, !!!'#( .!  ,, # !!!,!,, !, '".! ,,,$, . ,#! !  !!'%  ,!#.!!!,   !!, "!  , !$   !,%!!! !!'%  .!!  ,!#.!!!!  !! ". , '"  , %! !!, ! '%(#  .,!  !!,! !!, !$ ,$!,  !#  ! !! '%# ,  ! ,%! !!!!   ". ,,, ! $   ! #!,! , !! ''%(  #   #!,!,(   ! " ! !#  $!, , !!!''% , %, , !! !!! !!!    '"!! # !" , ! % ! ! '%( #  !, ! !  , " ! # . "!  !#,! !! ''#,  ,  ,!!!! % !  , !" !,  #.. $!  !% ,  ! !! %# ,!, !, !,!!   , !" .!!.."  ,% !, !!!'%(.#  ! # ! !!!  , !'"  # "!   , !%  ! ,!'%#  %  ,,   ,! (!   "# ,!,     !.% ,! !!!' #! ! ! !!! , ,, "!,!,. "    .# !  !!''#( .#!    !!,  !"! ,     # ,!  !! !'/'% %# !, !    !!(#!  '",!    *! ,! %   ! ,,''%##! , ! ( !! !! #!  ", ,  "   , %  !(! '.'#. !,,  ,# ,!!, ! " !! "!  % !, ''%   , !%,!,!  , !" !   %  !! ! !''%( .(,    !,!!! ( ,  !'", . !  "!  !   !#! !!''#  # , ! # ! !!! . ,!" ! . "   ! %!  !!!''% % ,,,  ! !!! !,!, ,$  !'"!   ! !, !'%% %#   ,!# , !! !    "., !", !, !,% ! ,! ,!''%( ## ,,!!. !,!,, !$.  ! '"!  ,!! %  !.!#! !''%  , !!! !,!,, !,! ! ".,  . ", !! ,,!% ! # !!'%# # ,!!!  !!!,! ! , $  ! !," . ! ! % !#   '%(#!   !! , ,!!  %! !  "  " ! %  !!!,!'%#  !,!, # !,!,."%, ! ,, $    '"  ,! %  %, ''$(!  , , !!#  ,,!  ! "  ,  "!  , !% ,,! !! '% . .( ,,,!  !,! , ! ! $ . . "! ,, ! %   !#! .'#(! # !  .!! ,! , , !, $,#  " .  !# !  # !'%,!% ,, !! # !! , !  . , "  !  % ! # !!''%(##  ! %. !!# ,  %. ! . "!, !!!%   , !'%( . # ,,!! !,,,!$# ! ,, . !# .$.  !,.!!#  #, !''#! % !,! # !,!!!$. , , $..#  %. ,!! %.## ! '%(  %# ,,! !#. , !!. , ! ,! $,  , "!, #.!, ,#, # ,! ''% ##!  !!   !    ! ! ".  ",  !,# % !!'%(.% , !!% ,!,! !! ,, $. .!." .! !!#  # '%!. ,, !! #   ,.#, , ,    . $ !!, ,#  ! ''(#. ##!,! !# , !,.##  , .# .* ! !!!%!# ! ''#% '%# ,!!# !!!!.#! , , ,   # ." #!, !,#  !'' (# ## ,! .!# !,! (  , , .  $, .!!! ! !% ,! .! ''#.! # ! ! !,  , , . ! ,  , .!!! #,  !!  %(##! ! ! !!. ! ! !!  ! ",!!  !# ! #!'%! '%,, ! !  !.%#! . , , # .,   ,, !#!! , .''( %% ! !, % !! ! % ! !! % , ., %,!  !, ,#!!!! ! !'#  !, !,!! #,!,  ,  ! %,! %!! ! ,#,!  !!'''%  % ,  !!! !!!! !.!    ., #,!  ! !!!  ! '''#!! !.! !,! ! . !. ! . .!!   !!.!!!!''''+'! !.'' .  !!!..  ! . !! .!!/ . ! . !'  +''/'.'/'''''''''''''''''''/'''!''/''/ '' !.!'  '...'!'.' ''''. '!''.','./'///'/'//////////////'/////'//////////.+//'//++/////'/''''/+''//. / ' '''''!''''''''''.''.'''''''''''''''/+.///+//////////////////++/////////////./'/'//'///////////////'////////'/////'////////''//.'''''.''/''/'/''''/'/'''''/'/'/'''////////'+/////'///'+/////////////''/'//////''/'////////'//////'/////'''//''/'//'////////'////////'////////'/////'///'//////+//'/////////////////+/++////////'///////////'////'///////////'/'/'+//////////'/////+/''////////////////////////+'///////////////////'///////////////////////////////////////+//////////////////////////'/////////////////////+//+////////////////////'//////////'///////////////////////////'//////////////////'/////////////++///+/////+///+/////////+++/////////////////+///////////////////+/////+//+'/////////////'/////////////'///////////////////////////'////////+///+////////////////////+///////////////+'/////////////////////////////'////////////'+//////////''//'''////''//'/'//'///'''/''''////'/'/''//'''///////////////////////////////////////////////////!                   ! """"""""""""""'''''//'/////////////////'////'/'/'//''////'////++++++++++++++++++''''///////'''''/////'''/'!!     , ! ! ! ! , ,,,                              '   '..........''......       '  ''''''''''''''''''''''''''''''''''''//////////////+''/'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''.'.''''''''''''''''''''''''''...'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''/'///'''''''''''/''/''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''..'''/...''''.  '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''...'''''''''''.'''''''.''''''''''''''''''''''''''''''''''''''''''''''''''''...''''.'''''''''''''''''''''''''''''''''''''''''' .....      !!  '/ '.      !     !  !! !!!! ! ''''.  . .'.       #      !      !                           !     ! ##          !        ")"""$#$$"""$""$"""""$"""$""$""""""""""""""""""#"#$""""""""""""""""""""" ### ##$$"""""""""""""""""""""""""  $""""""##"""""""    #  #$#      ##$$       ####                      ## ### ####   !     !            /$      #   #  ##!! !        !       #!   $    ###$ # ##!  ##       #       !,        .            !        !  #                ###  '%!  !!!! !   , !  !   !!!  !  !!,!  ! !   !   ! ! ! ! ! !      !!!! !    !!!!!, !! ,      ,! !, !! !!,!!! !, !!!! ,!!,!, ,!   !!!!!!!! , ! ! !!! !,,! !!  !   .. ..         , . ... ...  ! ... .   , ,! .    '.   !!, !  ..... .   !!!!!! !!,!!, ! !! !  ,' % .  !  , ,!,,,,,, ,,,   , ,!, ,   ,     ,,,!,, ..     ,,, .,..,,,,,!!!!,, ,,,,.....  ............................... ........... ..... . .''......................    .. ... ''%$#,,!!!!,!!!!  !!!      !!!!!!!,!!!!! , !!!!    !!!       ! ,!!!,!!!!! !!!!!!!!!!!!,,, ,.,,,!!!,!!!!!!!!!!!!!,,,!!!,!,!!!!,,,!!,!!! !!!!!!!!  , ,,,,,, ,!!,,!,,!,,!!!,,,!. !,!!!! !!!   ! ,! ,!!! ,, !!!!!!!!!! ! !!!    !  ,!! ,  ,!!  ! !!  !..  !,,! !! ! ,  !!!!!!! ,,! !!!   ! , !!  , ,!   , , ! .%"# ##  # ##$$$$$### ### ######### # ## #######  #### ##   ########$####  ## ###########   ######  #   # #  ##### !       ######   !             #   !! ######### ##       ! !    #### #$""$     #%$$# #  # # #######  ##    # ##$#  ##$## # #####   #####   ## ! #  ##   #       ###        #   !            !   ####         !!! ,!   ### +(,!!!!,, !,,!!,!!  !,!!!, , ! ,!!!!!!!!!!!!!!!!!!,,!!!!!!!,,! ! !!,!!,, ,!!!!!!!!!!!!!,,! !, ! ! !!,,,, !!!!!!!!!!!!,,,,!,!!,!! ,!!!!!,!!!,!! ,,!,,!!!! !!!,! !!!,!!!  !,!!!,!!!, ! ! !!! !!!,, !! .  !!, ,!!! ! ! , ,!,,!, ,, !!!!    !!   , !!, ! ! !!!!!!!,,   !  ! .. !! !! .! . !  ! ! .  !  ,! ,,!! ! , !, !!!!!!!! , !!!!!!!!,!!! ,! !  !    !!!!!!!! !!!!! ...!, , ,,...    , ,,, , ,,    ,  .  , .......  ,,,, ., . , ,,,,,,, ,.... ,,,   .......... ., ..    .......... . ............................................ ............. ..........'..................... ...............  .. .  ../ !!!!!!!!!!!,,,,,!!!,,,!!,!!!!!!!!,!!!!!!!!!!!!!!!!!! !!,!!!!!!!!!,!,,!!,!!,,!! ,!!,,,!!!!!!!!!!,!!!!,, ,!!,,!!,!!!!!!!!!,!   ,,!,!,!,!!,!!,,,!!!,!,,!,,!,,,!, ,!,!!,.,,,!,!!!,!!!!!,, ,!, !!,!,,, ,!,!!!!!!!!, , !! . !,,!,!!!!!!!!,!!,!!!!,!!,   ,   !  ! !,, , ! , ,   ,,!!  .! !!!   ,!  !! !! . ... !   , , !,! !,, , , , , ,   .. !  !,....    ,   '!"**$################# $#"#########%%$#### !############%$# ###!! #####$$### !!### #### #!  ##########    #### !   #########  #      ######          !      !# #    #!   ! !  ####   !! !    ! #  !!!     +"""$########  #####$$$##$$$## # !### #######$  ### $##    ### #   #########   ! ####       ###      ##    #   # ####       #        #   ########             !    !"!,,!!,,,!, !!!, ,!!!!!!!!!!!,!,, ,,, ,!!! !,, , ,, ,,, , !!!!, ,,,!, ,,, ! ,,,,,,!!!!,! !,,! !!,  ! ,,,!!! ,, ,!,!!!!,,,, ,!,!,!!,, , !!,,,,! ! ,, ,!!!!!,!, , ,,!!,!!!,!!,, ,!!!!!!,!,!!!!!, ,!,,!!,,,,!    !, !,,!!!,, ,,,,!!!!,!!! !!,,,,!!!!, , ,,,,   !, ! ,!! ,,,,, , ! ! ,  , ! !!!,!!  ,  . !!!  ,   , !   ,  !!! ! !! ...   !! ,   !! ! !, ,,!(,,!   , ,! ,!  !    ,  !   .   ''+' ,    ,  , ,   .  ,, . , , , ,  .    ,,, , .. ,  ,     ,. ,, , ,,,,   , ,.  ,, , ,     . ...   ,      ,, ...... .....  ................. ................ ......   ...............'......'....... . ''' (!.. ...................... ''!!     !!!! !!!!!!!, !,      !!  !!!,!! !  !!!!!,!,,      ! ,,,!     !,,!!!!!!     !! ,, (!,!,!!!! !!!,! ,! !,   !  !!!!,, !!!!  ! , ! !!  .  ! ! ,   ! !!! !   ,  , !! /'( !!!! ,,! , !  !! !  , , , ,.,$%####%%$$$#%## ##########%###$%$$$$$### $##$$  ####$$## # ########## ## ###$### ##$$$##  ##### ''"$# !  !# ####  # ! #   #####    #  ########### # !#    ###       ''(#  ###########  #       '#%# #    ,!!!  ############    ##  ,!       !    ##     ########           ! !  '(%%  ! ! !       ! !! !! ! !!!!      .!  ,, !           !!   # ,!      !    !!!  !!!   '' !       ! !!!! ,!  . (%, ,,                 .  !   ,,, ,,    ,,!, , ,      ,, , ,,      ,, ,   . , , ,   .., ,    ,,,.,. . .    ,,  ,    .    ..  .........'''!,   .   ........ ....................  ......  ...................................'( ' .  .. ..............'.......... /%,,,,,,,,,,,,,,,,,,,,!,,!!,,!,,,,!,,!!!,,,!,,,,,,,,,,!,,,,,,,,,, ,!, ,!, ,,,,  ,,  ,,,,,, , , ,!!,,,,, ,,,,,,,,!   ,,,,!, !,,,!,!!!!!!!!!,,!!!!,,,,,!!,,!,,, ,,,,!,,,,,,,!,..,.. ,!,,,,,,,,!!,, !!!,!, ,,,,!!,,,, , ,,, , ,,, ,,,  ,' ''!,,, ,!   . , ,!,,  ...  ,      ,  .  , ,,, ,       ,, .......... ,.... ''( '!'.........'.''  .. . '''/-$## ###%%######################!!! !     #######      !!!!!     ####!,    !, !# , !!       !             !. '      ! !! !!   !     !!!!   !!,            ! ! ''.  '''''''! '''' ' '      %"$"# $$$##!  ###########  !!###   ## ##########  !!!,  !..!. !!!!  ..' !!!,  !!!!!!! !. !! !!!!!...! !!!!  ! !! !!!! ! .''''''/!! !!!! !!!!!! ,! ..'  ! ! ! ! . !!''''' .    ''.''''.'.'. ! ! ! ! , !!   !   . ! !'''///////''/////'////''////////////''''''''//'''    ! "' % . ,, ,,,...    ,,  ...'.''''''''''''''''''''''''''''''''''''''.''''''''/''''''''''''''''''///////////////////////////////////////////////////////++'!*****************/' .   .. ''(, ,,  ,,  ,, .'''''////////////////////////////////////////////////////////////////////////////////////   * **********************************************************************************************'....'' (% ! ##,,#!'' (                     ** **************************************************************    '$%%!! # !#                *******************************/        %%, #,!!,  ,!,  ****************************************************************************************************************  /!!. , . ,, ,,, ,************************************************************************************'!%%%#%%#$%%%#%#%%#%#%/   !!,!!!,!,!           *********************************************************************************/. ! , #$"#### !!, !*          *****************************************************************************************'       '%" ####  !,!!!!!! *                      *****************************************************************************/.!     . /%,%%%%%%%%%%%%%%%%%%%%%%%%              ********************************************************************************************'......... '''%$.    *                 ***************************************************************************************'  !! !!! ''$$       *                  **********************************************************************************************/   !    %$    ! !               ***************************************************************************************************'  ,!!,!, !!,'/%, ,,,, ,   ,, ,,                 *******************************************************************************************..  .. .. '' !$*"#######,                *******************************************************************************************'   !  **$#                          **********************************************************************************'      +!,!,!,!!!!,,!,, ,,,,,,!,,,*                   ******************************************************************************'     ''.,!!!!!!!!,!!!,,,,,,!,,!,,!,                       *************************************************************************/', .  .' '$$###############*                      *************************************************************************** ! !!    '$" #####                               *******************************************************************       /(% , , ,  ,, !,,!,,!! ,!                         ******************************************************************************'. !! ,   '%..,,.                                     **************************************************************************************'..... ,''% !,!!!! !!,  !                        ********************************************************************* /'!!!! !   ! ,%% ##### #### *                                 *******************************************************************************/   !     #%$% ! ,  , #, ! !                               *****************************************************************************/' ! ! .! .  ! !  ,,, ... , ..   *                          ****************************************************************'...............''''##!,,!!## ,#%!!!                                    *********************************************************************** !!!!! .!%$#  #####                                 ************************************************************ ###   /%- ,,  !  #!*                                  ********************************************************************    %..,,  ......                                      **********************************************************....... ''(,,,,,, ,,,,  ,! !!!..                                *************************************************************** !#$$$$%$$$*) '%$%######                                           *************************************************************$""$$$*#   $$ #'""$######                                             *****************************************************'%$$"""$#%%%# .!!  $$$#   ! !                                        *****************************************************************'    !/%- ......  . *                                 **********************************************************'. ......'!$$$$$$$"$$$$$$$$$$$$$$""%                                           ************************************************************************** .   ! .'/#$. ####### *                                        ******************************************  !  #!!,!!,,!!                                           ***************************************************************'     '## !     *                                         ****************************************************************  !! !!! ! '' '(. ,   .                                                *************************************************************'.. .....',%! !!!!!!!.!! ,!,  *                                            ******************************************************************'...   .'+%$!!! # ! !!,                                                 **********************************************************************        $""#  ##!##                                                     *************************************************************' ## '$*"#     #                                                         ****************************************************************    !!,!!  !                                                       ************************************************************/' .!!!!! / , , , , , ,  ,                                                   ************************************************************'.  ...... .., ,,,,,,,,,,,,,!!,,                                                    ***************************************************...... .. ''+%-%,!!,                                                      ***************************************************** !! !  !""%###    #                                                    ************************************************************/     #"""#####    ##                                                        ****************************************************       #-$    ! ,,!                                                *********************************************************' # , !! !  '%$,,,, ,!!!!!!!!,,!!,,,!,,                                                   ****************************************************************/' ,!,, , , ''.(%-!      *                                              ***************************************************'  ....' +$,!!,,, ,,,,,,,!,,,,!,                                                  **************************-******************************************/  ,,   '% !      , *                                                 **********************-**************************************/     # ##%                                               ****************************--***********************************   ### /'###    # *                                                **********************-*****************************    ' ##%$%%#%%###$!#                                                   ********************-****************************************** /!,!!, %# """""$"$$$-$$!                                                ********************-******************************************* /'  ,!, !  ,  ,.#$ '  """%                                        * ****************-*****************************************/ ,  ,  .' %%,! ,,!!,!!!!!!!!!''. *                                              ****************** ****************************************' ...''!. ,,,, ,!!!,,,,,,, ,, ..'                                                ******************** ***************************************************'...'!,,,!, ,!!!!!!!,,,,,!!,!                                              *******************************************************************.  ! ! .'# !!     #                                             ******************* *********************************************/'! ! !   $*#  # #                                                 ********************* ************************************************' !!   "$""#  # ##                                               ********************** ************************************     ! .%% ##  !                                                  ************************* *************************************************' $$$$" "    '/+%% !!                                                     ************************ *****************************************************/! ##*  ####% ' ,,!!!,,!!!!,!!!!!!!                                                    *********************** **************************************************/!!$"$$$-$"*&'.' !,,,!, ,, ,  , , , , ,                                                     ********************** ************************************/!!  //   %.  ,,,,, ,,,,,,,,,,                                                 ********************* ***********************************************'..........%$,!!!!,!!!,!!!,!!!!!!!!!*                                                       *************** ********************************************/'   ! '.$   !!!!!!                                                     *********************** ****-***********************************/'  !!! ! ! !  %"  !! *                                                  *************** *************************************************** /    $$ ########%####! *                                     )              *********************** ***--****************************************************/'!       %$######                                      ) )              *********************** ***--*************************************************** #     %!  !  #######*                                  ) )            ************************* ****--***************************************************      !,!!!  "(%. !,!!!                                          ) )                *************************** ****-******************************************************* !!!! ,,, !  %$,!,,,!!!,!!! ! ,,!!!!!!!*                                   ) )            *-************************ ***-*****************************************************/!!, !!!!,,! ,, '%,!,,,, ,, ,,, ,, ,,,,!!                                     )            -*************************** ***-************************************************************/' , ,,   '%. , , , , , ,  *                                       )               --****************************** ****************************************************************' ...'!!% ,,,,!,!,,,,,,,,,,,                                     ) )               -**************************************-***************************************************************    ,  ''"%%!!!!!!!!,,!,,!,, *                                     ) )                  -**************************************** **************************************************************' , ,  !! ,, '%$"#     ,                                        ) )                   --***************************************************************************************************/' !! !!! !!! %$% ######## *                                       )  )                *-***************************************************************************************** /  !  .' /#%$#####%#                                        )  ) )                  -****************************************************************************************** '    #     !# # # ##                                                       -**********************************-*********************************************************     #%!       ***-----------------------------------------------------------------------------------------------------------------------------------------------------            ------ -    % % % % % % %  %    % % % %  % %  % % % % %%  ---------------------       ---*****!    !, !%$,!!!!!!!!    *****------------------------------------------------------------------------------------------------------------------------------------------------------             ------            ---------------                         ----**/'! !!! !! (%, ,,, ,  ,,  ,, *                                                      -*******-**************************************************.....  ..' $%((!*                                                   -*************************-**** *********************************************** /! !(!$.%$%(  )  )                           )  ) ) )  ) )                     -**************************** *****************************************************%*    )" ''+++*                                                          -***************************-**** ************************************************************************+                                                        -****************************-********* **************************************************************** """"""""""""""""""""%                                      )                        -*************************************-*********** *********************************************************************** ' """""""""                                                              -**************************************-******* *************************************************************** ''    $*"#                                                       **********************************-*** ************************************************************/'      +*#                                                             --********************************-*******************************************************************/ !#   . $"                                )                            -***************************************** *******************************************************/        ++$$$###                                                    -******************************************-**********************************************  ! # '$$$###  #                                                         --****************************************************************************************       # $"$########                                                          -********************************************************************************/  !  !# +/$"###########                                                    -*********************************--**********************************************************   . ,  '+#%$$##### #                                                              -****************************** **--****************************************************  !    %"$  ##                                                                     -****************************** *******--****************************************************     ''#         !*                                                   --******************************* ***********--*************************************************************!    !! ' (      ! *                                                           **************************************--********************************************************/   !+%%!    !*                                                       **********************************************--********************************************    !   %$%,!!    ! *                                                         *********************************** ********-*************************************************  %#,!!!!!!!!!!!!!*                                                  ****************************************--***************************************/' ! ''(%!!!!!!!!!!!,!!,!!,!!!*                                                   *********************************-******************************************/' ! !!!!!!!!!!!!% ,,!!!!,!!!!,,,,,,,,,,,,                                             *****************************-***************************************.. . .%( ,,,,,,, ,,, ,, ,                                              ***********************************-**************************************/'!!!!!!!,!!!!!,,!!!,, '. ,,, ,  ,,,,                                         ******************************--********************************'!(%%%%%%%%%%%%%%%%%%& ', ,,,,,!!,!,,,,,!,,, !                                                      ************************--************************************'.',!,!!,!!!!!!!!,,,!!!!, .   ***---------------------------------------------------------------------------------------     -    %  % %  %%  %  ------------------------------           ----' .  , ''%( !!!    ------------------------------------------------------------------------------                       ------           --------------------------------               -*/     ,,, .%%  !                             *************-*************--*****************************/ !! !! !! '%$#     ###                                                **********************************-***************************************   ! !   $$"$#############                                       -***********************--************************************* ##### ""*$*                                           -*********************-****************************************** #########  $*$ ###################                                        ***************-*--*******************************************/       ##           *                                     *********************-********************************************/! !!! ,!+!!!!!!!!,*                                              ************-***-********************************************** !!,!,!!!,!,, . ! '%-,!!!!!!!!!!!,,!!,!,!!,!,                                          ****************-**-******************************************' , , ,  . '%, , ,, ,, , , , ,,, *                                    *************************-****************************************************''      ...'%"., , ,,,,,, ,  ,,                                           -************************-*****************************************.  .'(% ,!!!,,,!,,!!,,,!!!!,                                   ********************--**********************************'. .   '%!!! ,!!!,                                       -**********************--****************************************/' !  ! ,   ' + !!,,!# !##  !!  *                                  ************************-*******************************/ !!,!!! !!!! #!##                                   ************* ****--**********************************       # ###*                                  ********************--************************************ ### ### '%$    # #* *                                          *********************-************************************************ #  #%$ !!!                                        ****************************--*******************************************      #$!,!!!!,!,,,!!!!,*                                    ******************************-****************************************************/' !!!,,!!!! !, !!' ,,,,!,,,,,,,,, !! , ,,,,   *                                        *******************-*********************-************************************************' .   . ...  **                                        ********************-***********************-**************************************'..... '!,,!,!!!!!!!!! ! **                                   ******************-*********************--*****************************************'  , , , '+%,$ %%% $$$ $%%%%%%%%%%% *                                               *******************--******************-**********************************',! ,!,,, , !! .%$" '. ... ...**                                       *******************--*********************-************************************/ ,! . ,! ,! ' $"$####   #                                            ******************--******************--*********************************  # ,!,, $"##  **                                         ***************-************************-************************************/ #    ! '/$"$#########                                             ***************-**********************-************************************'  ! !!  !!''%#########*******-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------   ------------------------------------                      ---/'!., ! !,!  +$!,!    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------                  ---*/' , ! !,, !!,, %,,,,! ,!,!!!!!!!!! ,!,!!!**                             **********--*************************************--**************************************'.  !!' %! , ,   , , ,  **                                        ************-*****************************************--***************************************************..'' %,, , ,  , **                                         **************--*************************************--**********************************************'!!!! ,!!, ! , %(, ,,,!,,,!!!,,, ,,!,!!,!!,**                                           **************-*********************************--**************************************************'!%%%%%%%%%%%%%%%%#%%%$*"%!!!!!!!!!!!!!!**                                           -***********--********************************--******************************************/. ' %# ###  **                                                 -************--*****************************--*****************************************'    %$#%#$###                                                 -**************-***********************************--********************************************/.    '$$% ##$ *                                          ************--*****************************--********************************************      /%%# !!   ###### ****                                               -**********-************************--*****************************************    !   .% ,!,!,!,!!! *                                                  -********--***************************-*****************************************/   ,!,!, ''%!, ....... ..****                                        -***************-************************--****************************************'.  . .  '//+(% %##                                             -*************--*******************************--***********************************************.................%$%%%%%%%%%% %% %( %%%%*                                             -- *************-***************************-****************************************************'    .%*'........'.*                                           -*************-*****************************--**********************************************/!!!  %%# ""$""$"%#**                                             - *******************-*******************************-*********************************************/#    /#! *                                          --*************-****************************-************************************************** ##  $$% ####### **                                        -******************-***************************-************************************************'       %"%!,!!! ,,,*****                                             -***************-*****************************-*****************************************************'  , !!! ! , !!!.'% ,,,!,,!!!, ,, , , ,,****                                            --  ********************-*************************-*****************************************************************'..   ,   ,   .,,***                                                -*****************-***********************************-************************************************************'... ..... !!,!!,,!,,!,!,,!,,!!,!..**                                         -******************--**********************-***********************************************************/' !!!  ! ,! "$% ## ! !**                                            ****************-*********************************************************************************   !! !!+$**$ ####### ****                                         -*****************-***********************-*********************************************** ##    $%#### # # *** *                                          -  ****************--***************************-******************************************************** # #  +#$%   !***** *                                   *****************-********************-****************************************************'    !,!! ,,,,,, ,,,,,,,,, ,,, ,,.***                                             *****************--********************-***********************************************'.   ,!,  , ,, ,,,,,  ****------------------------------------------------------------------------------------------------------------------  -------------------------------------------------------   ---------------------------------------------              --------*****........./+(% !!!! ,******----------------------------------------------------------------------------------------------------------       --------------------------------------------------         ------------------------------------          ---------****/' !! !  , , //+$"-! ####*                            ********--*******-***************************************** #   $""%##### #****                                         **********--*********--**********************************************************/ #    #%%     ! ,!!! *                                  -***********--*********--*******************************************************************'    ,! ',. , ,,,,,,, !,, ,  ....                                **************--*********--*****************************************************************/' .. '+(!,  ,   ,,,,,,,..**                             -   *****************--*********--***********************************************************************'.....%%# ,!!!!!  ,                                **************--************-***********************************************************/.! ,      $%#### **                        - **************-*********--***************************************************************/#  ## %#                                 -   **********--*****************************************************************************/      %,. !,!!,,,,!, !!  , *                       -******************************************************************************.   !/+((,. , , ,,,,,,, , ,,***                   -      **********-***************************************************************** .....''%%!#  **                        --   *********************************************************************/,!!!!!'%%%# ##  ##                       -    ************************************************************  ##### . ,!!!,! ! ,!!!!!*                     -   **************************************************************************    ,,,, ,   .   ,  ,.*****                     -- ******** *****************************************************' ,    .   ''! !!!!,,, ,,,,,,,,**              -     *********************************************************************************.... $"%##$##  !!*           -  *****************************************************************************'.     #%$"#"#####**                 -   ***************************************************************************$"$"*"$"%$$$$$$$"$"" % .. , ,!!,!!,!!!,!!**              --     *************************************************************************************/'''''''''''''''%%%!  !!!!****         -   ************-**************************************************************'''',$ %%%%%% %(((%%%%(%(% %             ******** -****************************************************************/      *,,!!!!! '' ,!,,, ****               -   ******** --********************************************************/    .%$"%##########  *              ************ --*****************************************************'    ' #$    , ,! ,! ,,****                  ****--*************************************************'  .. '%%!,!!!!,!,!!!!!!,***            ********  *--*******************************************************/.  !   .'"$%%$$#$$****             ********--****************************************************       +*$"$## ****              *********** *--**************************************************/'!     + !,, , ,, , ,  , !,,*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------           --****.........'!,!!!,!!,!!!!!!!,!!!**----     --------       ------------------------------------------------------------------------------------------------------------------------------------------------------                 ---******/'!!  !!+      **    **-*-******************************** ## # (% !!,! ,  ! !!! !****    **-*--********************************      %..,  ,, .,  ****        *-*-**************************************************'..........''/%" ! ! ! ! ****           --*--********************************************'.    .''$*$######## *******                - -*******************************************************     ',$$  !,,!,.**********          *-** -*****************************************************/    %, , ,,,,,,,,!,,,,, !,.*******   **        ******--***********************************************..   ..'%#   .********* *          -****** ***********************************************/ .,!,,, ,/%$$$###**************  *       -* *****************************************/'   ##### %    !***********     -**-***********************************(/!!! !  !  /'( , , , , ,!!,,,,,************            -*   -**************************************//'. ........../'++$!!,!,,,,,,,,!!!**********         -**  *********************************** ' !! !..! '''"*##! #********* *        -*   ******************************'       $"% !,  ,!!*******************            -*  -**************************************.  ! !    '!%. ... . .  ************          --***  *-*******************************'.......%!,, !!!!, ,! !,!! *********************      -* ******************************************/' ! ! , !  . '-""$"%$""%"$$%$%%%"$%$$$"$$$**********************-           -*-****************************************       $%$%%%$"$$$$$$$%$$%$$-$$"-%**************-         -** -****************************************#     "&"%""****"$"$"$"$"$"$""$$*************************            --************************************/'.. '**********************  -          -**  --************************************/.  !!/++++*************************-            -*    --******************************%""""$$"$""""""$"-$""$ ''., !,, , ,! ,,,*************************         **    --********************************'$""""""""""""""""$"""" * '''''''..'.....**************************             -     --**********************.  !!! !! !!! ") "#  ######************************-         *   -****************************%"$ ### ### ************************            *         --*****************************++ . ,,,!!!!,!!! !****************************       -       -************************'...'.'''''''%,  , , ,  ***----                   ------------------------------------------------------------------------------------------------------------------------------------------------------ -------------'............. /$*" !!,!!!!!!!!!!!,,*----                    ------------------------------------------------------------------------------------------------------------------------------------------------------------------    ---------------------*'''...'  $""####### %#### **********************  - -*****************.!  #      / $######****************************** --     -********************/',!,  !!! !   ' %,     ************************************   -   *-************************....../, ,,,, ,, , ,,!!!!,,,, ,, *******************************   -    ************************** ... '.%%  .  , , , ******************************************     --    -************************       ! '($%.!,.',! ,! ,, !!!************************************     --              *****************    !    $""## #   *****************************************       --          -*************************/   !!!,   '$"" ### ##*****************************************        --           ***********' ... .. '/#%,     #  *********************************          --               -********************'......  ! ! , ,!!,,, ,!,.********************************          --       *****************/!   !  ,  '!.. .. ,,,,,,.'********************************         --         *-*********************** ##   ' %-" ##!,, , !!! !************************************         -             -*********************/. ,    $"*#####   ************************************       --         *-*********************/'  !!!  !  ! '#$$#  # #   !**********************************      -       -*********************'......! .. . %.. . !  !!'***********************************        -              ********************'............../'%#%$    .....'*******************************************     --             -****************'!! !! ,,  .   .. , %%%%%%%#%#(%%********************************       -           *-*****************     ,, !  %%,!!,! ##%##(((!****************************************       --                   *****************/ ###    *%**$""$!!!!!!,'..*****************************************   *-            -*****************/         $$"$$$#**********************************************     --                  -*********************'   ,! ,  //'%%. ,!,! !****************************************** *--            *********************....................'/.   ,,  .************************************************      --                     -***************'   ,!     .   '$ !!!!,!,!!,,!!,,!,!,,,,.*******************************************    -              ****************          '"*"# ##, ### **************************************************      *-                   -****************      # ""*#######  # ## ******************************************    *-             **************** !  !!!!!!!!  !'/%.,,,,,,,,, ,!! !, !, !!!*****************************************************    *                      ****************'''............''!.( ,,!,,, ,    . .*******---   ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------******/'    .....'.(%%$ !      !!!!!********---             -----------  ---------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------  ---*******  !    .%"" ## !****************************                  - ***********/    !    '$$, #   ##### ,****************************                  -****************  ..   .. .  . ,,   ,.'*******************************************  *                    *********************!%%#$',,,,, ,,., ,,,,,,,,,********************************     * -              -***************** .....'.'.'$* %###   ####*********************************** ***                   -*************/   ..  ..  $" "##   # *********************************   **                      -   *****************       %!!!!!!!!!!!,! !!!!!*******************************     *                       -- **********************'..  ! ..'., ,( ,,    ,**********************************************     ****                     - ***************................./$$!  ## !, *************************    ****-                     -******************/.      "'/$######### *****************************    **** -                        --   ************/ ########### !! !,,      ****************************  -     *--                    -**********/     . , .  , ,  ,'*******************************  **-                        -   ***********'.................'%" !  !!######**************************   --    ** --                     --  * ***************'  ! ! !,! !!!! , '$""$  !  ##### ****************************   -   ****--                    ****************      "+%%%,,, ,!! , !!!!!!!!************************       -*****--                   -   ***************/       !,!,!,,,,!,,,,  ,'*********************** -- ********--                    -  ***************'.    .. ... ' %$-$####### #!  #************************   --  *****--                     ****************** !, , !... ,..,'$""     !  # ,*********************************   *-  ********-                 -    *****************    !  #%%- ,,, ,,  , ,****************************** --*******-                       ***************  ! ! ,  '%$,!!,!!!!!,!!,!!,,!,,,,.********************************   -*************                              ***************'..  ,  .   !.' /%%  ##############**************************   --***********-                     -    *****************' . ! ,! !!!!!'.,!!! ,  ***************************      -******                       -      *************         #!!,    , , !, ************************** -**********-                  -  ****************/ , !!,!,   !!! ' #%%### !!,*************************  --************-                        -        ****************/ !!,!!!!!!,,! !'',#%% ####  !!***************************** -******************-                   -   ***************/!  !(. !,,!!!!!!!!!!!!!!*******************************  ***************-                     -   *******************'!!,,! ,, ,,! .%! .,!, , ,,,,,,!!,!!!,,,!*---------------      -------------------------------------------------------------------    ----------------------------------------------------------------------------------------------------------------------------------     --***.............''%" ## ## #### -----------            ---------------------------------------------------------------        --------------------------------------------------------------------------------------------------------------------------------------------          ----******/       .#$% ,!! ! ! !  !***************************  *-*******-                -- **********       %!,!,,!!,, ,!!,!, ,!!, ,,!****************************** **************--*                       -   ****************/ !  , ,!  %%!   !!! ************************************** ******************--*                - *************'.   .........(%%  ##  !!******************************  *-*******************-                --  ********************       % ,,,,  ,,,!,,,,,,,,,! ,,******************************* *******************--              ********************'#$$$$"""""""$$"$$"$""""""&$%$"%,,!     ****************************      -************************-               --     ***********************'''''....'.....$ !'$$. !    ************************************-**************************-               -  *****************''''''''($. ,,,,,! ,,,,,!!!!!!,,**********************************   ***************************-              -            ****************           !,!,,,,,,,,,, ,!!!!,!!,.*********************************-************************                     *******************' , ! !!!,  #  ! ###***************************************  -*************************                    -     ********************/ !!!!!!!!!  ! , ,! , ! ,,, ! ! ,   , ************************************* *-*********************** *                          *****************            % ! ! ! ********************************   **-**************************** *             -         ***************/!!!!  $-$$-$-$-$$$$-$-$-$-$-$-$%%%%%**********************************   -****************** *                  ************.................... ''%"$! ,  , .*****************************-******************************                             *******************/ !, !, ! !  ! . !  ! ,!!!!!!!!!!!,*****************************   -************************* **           -  ***********************          .!!, , ,!!!!!,,,,,,!,!,!!!!!!************************************* -*****************************                  *****************/ !!!!,!!, ,!  #-$         !*****************************   ***-***********************************              -  **********************/.!!!,!!! !!! !!,,,  .#$% #   ###***************************************** *-****************************                  - ********************          ,!%  , ,, ,, ,,,,,.************************************-************************************               -     ********************'      # %%$#,!!!!,!!,,!,,! !!,, ,, ,!********************************************************************************              --          ******************' ... ..... '' $*###    !********************************************************************                 --  ****************/' ! !!, ,!  .$$########    !*************************************************************************              -     *******************/      , !  '+,     , , ,,, ,,**************************************** **********************************-***                  -    ***********************'  ! !!!!,   !  ,!,,, ,,,, ,,  ,,,,,.***************************************************************************                  -      ***************........... ''#-$#%%%# ******************************************************************--**                        ******************'        "%$########## ********************************************************************-**                        *******************/       %% ,,,, ,,, ,,!!!!!!!,!,,,,,,****------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------        ---*******/    , ..  !'%-, ,. ,, ,! ,  , .***-----     ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------           -****'........ ........'" !!! ####   #####*******************************--**                 *************        '*$  !!    ##******************************************************--**               ***********************' # ### .,% !!!! !!*********************************************************************--******                     *****************************' ,, !!,, , !,  ! !!,, ! .'+,%((((((( ***********************************************-*********************************--***                   ******************'.... ..  ....,'/#$%%$%-%%%%%%%%%%%%%%%%%%%%%**********************************************-****************************--***                   *******************/! !!!!!! . "$""*" "**$""""**""*$$"$%******************************************--************************--*                             *****************'    ## ******************************************--********************-**                            ******************/ !!!  ,,!, !,,!, !!.#.'''''''//'/***********************************************--******************-**                                *****************************  !!'/'' !  !!*************************************--******************-***                                  ****************** '!#$%%%%%%%%%-%-%%%%%%%%%%%%% % ! '****************************************-**************-**                             ************************' %$$%$$$%%%%%#%%%$$$$%%$%$-% , , ,,'*********************************************-*************--*                                     **************************+//////////''/''''.'''(% ...,!!,*************************************************-********************-****                                       ***************************%$(  !,!,! ********************************************--*****************--                                      **********************$%   #!#,**************************************************-******************-***                                        **********************/    ! !%%$"!# # ###   !*****************************************************--*********************--*                           *                   *********************** ...'%$$!!!,!  !!! .***********************************************************--**********************--***                                        ************************* /''''''''''''''''. '***********************************************************--**************--*                                               **************************'!  /+(%% !,!!,,!! !!. ****************************************************-*****************-                                                **************************/      '$$""###  !************************************************-*******************                                                    *****************************/     ''$* ## !   ## !*******************************************************************--                                                  ***********************         '$%!,!!, ! !! ,! !**************************************************************-***************-**                                                 ***************************'        ,.  ...'***************************************************************************--*                                             ********************.. .. ! ,,,,,,!, ,,,.******************************************************-******-**                                                    ****************************.. ...../ #  ######## !*******************************************************-*************-*                                                    *********************** !!   $""$#####******************************************************************-                                                **************************** '      %$$     ,*********************************************************************                                                 ****************************/    !' ' ',    '*********************************************-*****-                           -                    ********************************'........../.!!,!!!!!!!,*******************************************************--*                                        **************************************/'!   .  '#%###$$# ********************************************-**********-                         -                         ***********************************     $##$$#************************************************************-*                                          ***********************************     " !%. , . ,, ,, .***************************************************-********-**                                                 *******************************************,....... //!(!%%%%%%%%%. /*------------------------------------------------------------------------------------------------------------------------------------------          ----------------------------------------------------------------------------------------------------------------------------------------------          --************.... .... /'/" !! !%$%$$"""$""$#**-----------------------------------------------------------------------------------------------------------   -------------------------------------------------------------------------------------------------------------------------------------    --****************/!!    %"#,,,  $"""$$$""%***************-************--                                        *****************    ! -% !,!! ''''******************************************-*********-***                                             ************************/,      !!.. , *************************************--*********--                                         **********-****************......'/%# !! !,,!! !********************************************-************-                                           **********-  ************/.!   ,$ ### !!  ********************************************-**********-                                          **********-((-************** #     %$$ ,, ,!!,*************************************************--*****                                         ***********-(( ( -**************/! !!! ! '%% ,  ,'********************************************--*******                                          *****--( ( *****************''.....   . /%$    !  *******************************************--**********-                                      *************-((-' (-*************    '%    ******************************************--********                                           *********-- --(' **********/ !   !    #% ,, ,,,, ,,!!,,!, ,,,,************************************************--***********-                                      ***********-(--' ( *********************'   ,! .. .  !' %%%!!,,,,, ,,,,,,,!,,,,,.'*****************************************--*********-                                    ********* ( -('(-*************** ..  . .. ''$ ########*********************************************************-********-                                    ******-((----'( -****************/ !    #  # ###*************************************************-********-                                    ********-(-----' -********** '$""$""" &. ''!,. , . . ,  ..'************************************************--**************-                                     ******((----' (-***********+/// #$&%% , ,!!!,!!!!!!!****************************************--*********-                                 *****-( --$-'  ************  .,, !$''#$$    ####***************************--*********-                                      ******-((---' (-*********/       %! # ,!   ********************-********-                            **-((--- '' (***********  .,! '/... ,  . '****************************--********                  *             ***-(---(**********'.......%"" ! # !!!! ************************-******                                ***-(---'' ( *******'        !$""     ***************--*********                  *       ****-(--- ' *******/    !   %$.!!!!,, ,!!!,!!!***********--*********                        *             **-((----$ (-********/'.  , ! , .% , ,,,!!!!!,  .**********--*********                *        **- -----''  -******/' ! '##########**********-********                                ****-( ------'(********. ###  ,!,! ! *******--********                          **-(------$-$*' (-**********'         ,!!,!!,!!!!!!!*****-************                    ****-(---- '' *******/ !!!!!!!!,''$""%,!,,!!! !***--**********                         **( ----$-**  ******** !  !    $"  !!!!! *****-********                          -(---**'  ******' !!!!!   .% !!!,!, ,,,!!, ,!,****************                           *- (---$-*******'( ******** ..   '/%"$#####   !! !**********         )               ***-(------*** ' (*****.       %!!!!!!.***************              )              **-(---$$****' ( *******.     !!!',!,! ,, ,!!!!!!!,!!!,!!,'***********                            *-(---------*****' ( ******'!! , ,, ,!! !       # ****************                 ) )                -( -------$******' ( ****/' ! ,!! !  . '%- *" #   *********           ) ) ) )   )  ) )              *-(-$----*******'' ( ********/ !! ,      %.'/. %%%%%%%#%%#%%#%**************                )   )  ) )             ((-$****** ' ( -****/'.. .. .. /$ !!!! !!! ********             )   )  )  ) )                 - ----***** ' ( ** ! ... ! ! ' %$" .,,,, !!, .********                    )    ) )            -( ---**** '  -*        /%"$    !!! **************              )  )     )             ((--$-***** ' (( -' !!!  ! ,  ......'**********               )    ) ) ) ) ) )           *-(( ---***''  ('..............'%$% ##### ***************                ) )    ) )             ((--******  '' (      %%!  # !*************                )  )    ) )  )            -((---******' ( -'       !  .. ,, ,, ,!!,,,********            )   )  )  )           ((--********( ((-'' ,, ! !!!!!!,  ''!% ...  ..... '***************             )  ) ) )  )    )           **-(-**********-- ''......... .....  '%*"$# #  ### ***************              )    ) )  )           (----*************-(     # ##" %$"% !,   !!****************               )  ) ) )             -( ---***********---%!, !! !.%%%,,!!,,,,,,,,,, '***********************              ) ) )  )  )             ((----******----!.     .'' %$ ! !!,!!*****************              )))  )              *-(-*********--- ,, ! ! !!!!!!  !!!! .'%$$ ###  ********************            ) ) ) ) ) ) )              -(-*********$-     ###### # . , ,,!!,!! !!!! ************           )  ) ) ) ) )           -( ----*************-----   ..  !,!!,, ,! ,,!, !,,   ! !!,,,*******************         ) ) )  ) ) )           -((-$-***********--((%##%%%#%%#%%#%%%..'#$$ #   !  !******************            ) ) ) ) )  )  )        --(-****************--$!!(((! (% %#%%%#%"%%%%#%$"$$$$"") $""   !*******************        ) ) )  ) ) ) ) ) )         -(-******************--   !  ..  !! $$$$%%%%$""$%$$$$$%$"$$$$$*& %$,!!!!!,,, ,, ,******************         ) ) )) ) )        *-(---****** **  ! ! !!!  !! ''/  . ,,,,  ..'********************          ) ) ) )  )  )           -(--**********--!'.......  ...... ...  .  '.%$$! ###### !*****************           ) ) )  ) ) ) )         -(---***********-             %$ ##****************            )  ) ))           -(---*********--#              (! ,! ,   ****"************           ) ) ) )    )       -(--*********** . !!!!!!,!! !    .!  ,,!!!!! !!!!!,!!!!, .%.  ....... '**************          )   ) ))          -(----****** ................................................''%$., ,! !!!!  ,,,.***********************          ) ) ) ) )          -(--$*****   ******-!   ! ! !!!!!,!!!!!  !. ! , ,!"########******"**************           )  ) ) )          -(---*** ***********!##   #####       #   !!***************             ) ) ) ) )        -((---***   ****%             ,      , ,, ..*******************               )             -(--****  ********% .. .. ........      ..... ... ...! , ,!,!!,!,, ,,,, ,,..'****************               )        (-****   **-!'.............'..................... .  .''/$"$!###########*****************            )          -(--*    *****%!  # !    !    !       !  !!    . $%""- ## ## **************           ) )         ( ---**  ***-#    !!!!   ####  !!   ####     %"$.!  !!******************               )            - (----**   *! !    !  !! ,,   ! ! ! ! ! , ,!!!,,,,!!!!!!!!************                       -(--**** * *(.''... ....... ...... ............. .. . ''',%%%%%%%%%%%#%%%%%%%*****************              )             --**    *!'..'........... ........ . .  ..      .. . .. .#) '''.'....''' .*****************                      ---***   ***-!!,.! ! !..     !!!!!!! !!!,! !! ,      ")### # !! !***************                         -(---****    *** ##     ! !   !    #####      ## # *********************                     -(--****    **!     !!!  !   ! ! !     !  ! !  !  !! . , ,!!, ., !,,''*********************                  -(--****      ''.............. !.'    ... ..................   . '....''...... , ..*****************                -(-******    *-.................. .................. ........ '       **************                     -((-**** **(    !!! , ! ,, ,..    . ! .. ,, ,! ! ,,, , , ,,  ,! , .%%######****************                  (-****   !!    !           !!   !               #%#    ****************               -(-**    *      #####                #####  !%.     , , , ,, ***************               -(-****     **( !! !!! !!,  !,  ,!        !!!!  ! .  !  , ,! , !! !!!!    !(!... ..    '*********************                 (-****   !'.........................( ( !...  ....................  .....!' ,,,,,! !!!!,,,*********                 -(--**    *-!   ! ! ,!!  . .. ... .. /''/',!!!,!!!!!!!!!!! ,!!  !  .  ! ,!   ,,    ...'.+%    # !,*******************                 ***    **!  !!     !     ! !    !''-#    !   !!     '!##   ##  !************************            -(-**    !!        !!        !'$####    !      !   ! **************************                  -(-* -%(. !   !  !! !! ! , !!! ,  '#!!!!!!!!, ! ! !  !!!!,,!,!!  %% ,!,. ,!!!!! ,. .************************               -(-****  -!  ..!    !!  ,!     .  , '   ', ..................... ..............,'' .......'***********************                   (** -, ...........................( ................    ......'''..... '(% !, ,!!,,, !!,**********************                      --**  !......' .     .  ! . '( !         !     ,  ' $$$%#     !*************************                    (*  % !       ! !! ##  ! ! ! '( #       $$#  !  ,************************************                    - ** *!.  !   ##  ##  !!   '( !,!!,!, , , ,! !! ! ,, !, !!!!!!!!    !    !%%. ,,,,     ******************************************                 -   ('' .   ,! .. !  !!!!! ,, !, ,!!    .'''''...........................................  ........ '($     ...'********************************************                   *--**  ( ' ........................................'.'...'.  ... ! !!! !, , ,       .......... ''%$#,!!!!!!!!!******************************************                    -*    ! !!! !!!!!!!,!!!! ,, !! !,!!,!   !            !!!  !! !+#%%##########*****************************************                   *-   -% #   !!  ####    #   !!     ########## #%#   #*******************************************************                            ( #  !            !     ! !,#       # %,   .....  ,*********************** ) ! ! , !!!!!!,!! ,!     .......'................' ,.   ,!  .              '... ........... ..*"*****       )   )          '''''........... ........................................................... .....................................'$$     *******************************************************************  *          )            $-   !  !! !                          ! !   ! !         !"*$#######***************************************************** **..   !   !  !,!!!!!        !! ! !! (      %%(      #####################  !       ###########"$%     ,, , !!!, !!!!!! !!!!!,!,!!!!! !!!,,,, ,, ,!  ! ,!!, !!,!!!!!!!!!!!, !,!!,..,, !!!,   ,!,!!!!!   !, ! ! ,,!!!! !! ! !! , !!! ,  !! ! !,! !!! !!!!!! ,!  ,!!, !!!!!!!    #   !!!  !!! !    ##!  ! !!                     !! !  !   ! !        ... .. .. .   .   .., , ..... .. .    ,,,  .. ,!!................ ... . ..  .........   .....  .... ......   , !,........... ,.............., .................... ........................................................................................ ..... ... .....................''.,,! #$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"&%,,,  ,,,,,............. . . .. ...........   ,!,,!,,,,!!!!!!,,  ,!!,,,!!!!,!! !,   ! !,,,! , !!,!,,,!!!!!!!!,!!! ,,,   ! ! ,!!,!!! !, ,,!, ,,,,,. ! . , ! .  , , , ,, , , , ,, , ,, ,,,, ! , ! ,  , !! , ! !,!    ! , , ,! !    .,   .! , .. ... .  '  #!    $$##  #"""""""""""*"*"""""""""""""*"""""""""$$$%$$$$"""*"""""""""""""""""""""*"""""""$$"""""""$$$$$"""""""""""""""""""""""""$$$$"""""*"""$$"""""""""""""""""""""$*"$$$$$""""""""""""""""""" &       .                      !    !    ########     !      #%! !!   # , #%#%##$%%%#%#%%####%####%###%#%####$%%#(%%#%#%#%#$##$%#%%$#%#%##%%#%##%###%####%#%$%%%#%%### ##%%#%#%#########%##%#%%%#%##%##%#!##### ####%#$%#%#####$   ! ! $$$$$$$$"$$$$$$%%$$%#$%%##%%%#%$$$$$$$$$$$%%%%%%%#%%%$####%#%%#$#%#####%#%$$$$$$$$%%%%%#%%#%####%$#%##%%$%$$$$$$$$$$$$$$%$$$$$#%#%$ $###%$$$%%##%#%%$$$$$$$"""$$$$$"$$$$$$$$$$$$$"$$$$"                    !      ! !! !!! ,!,,,!, ,, .'''.'.'''''''''''''''    %#%#%%%%%%% (%(%#%%%%%%%(( ((((((# ((((##(%%%%#((( %(#((#%%%%%%%%%(%%% (%%#%#%###%%%#%%##%#%#    ,, ! !!! ,! ! ,! !!    ! !!! !!  !,! !!!! ,!, !! ! , !!  !!! '!(... .       .,,,, , , !, ,,, ,  , !, !,!,, , ,!,!,!,!, ,!! !!,,,  ,!! ,  !!!!!,,, ,!,,,!!,, ,  !,,  ,!!!..  , . !,,,, ,!,! ,!!!, ,!,.. ,,!, !! , ! ,, ,, ,!!!,, ,,   . ,, !,,! .  ...//'''''''''''''///'''''''''''''/''/'/'''''/'''''''//'/''......!%%%%##%#%%%%#%%%#%%%#%%%%%%%%%%%#%%%%%%%%%%%%%%%%%%%%%#%%%#%%%%%%%*($$%  !!    , ###### ####### ####   # !  #####     #### ! !!!!!!  # ############   #######  # $$$#! # "#"$$$## ###  # $"$$##   $$$$$$# !  $ #  ##   ".! ! !!!!,  ! ! ! ! ! ,  !  !,!    %$"$  !!!    , ! ,  #   ##   #  # !!!  !!!      ##             , !!!!        ,!          ! !  #              !         !              !  ##                          ! $ !!. . ,,! ,    , !,,!,!!!!,,,,,, ,,,,, ,,,, , ,, , ,,, ,  ,, ,!!!,,, ,  ,,,, ,!!,,,,!,!,  , !!!!,!!,,,,,,,,,, , ! ,! ,   . , . ,,,!,,,,,, ,,! , ,, . . ,!,, , ,, ,,,,,,,,,,,  .   , ,, ,,, ,  . .. !,.. ,!!!! ., !!!!, ,   .  ,!, ,,     .. ,                ..  !, !,  ...    , ,, , ! !, , ,, ,  ,   , , ,  ,  !,!!, ! , , ! ,,  ,  ,.! ,..,   !!!,  ,,,,  , ,,,,,,,,,,         ,,,.. . ,,,,, ,,,, , ,. ,,,.  , ,,! .. ,,!,,,,,,, , , ,,,,!,! ,,,!! . ,, , , ,,, , ,,,,,, ,,,,, .  ,    ,,, ,, ,,,, ......  , , ,..  ..... .  .....      . .       ..  ...... .. . ......    ....  ................................  ... ... ..''#$% !!# ! ##  ! #######!   ##%#%#%########  ####### #!!! ! ##  ######## ##   ############# # !!!! , ! ! ###### # #  # !  ###  !  ,! ###  #   ! ,  ########   !   #####  ! !  #   !  #       !! !                             '%$#. !! ! !!     #,,  ,,!! #######################  !!!    #        , !!!!!!!        ,    !!  , #######   !    #      ##  !  ###    !!,! !       !. !     !!    !!!  . ! #############%#%$$$$$$$$$$$$$$$$$$$$$$$$$"""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%$$$$$$$$$$$$$$$$$$$$$$$$$"$ '#$'........................ ...................... . . ........................................  ............. ................ .. ...... ......... ............'. .....!,!, !,,,,!!!!!!!! !! (###!!!####(#( (   %'-  !# !!  !  !! ,,!!,!!!  !!!!!!!!!!! !!       #  !! #      !!!!  !!!!!# ! #,,!!,!!!#(((((#% %#%%%%%%%%%#%%%#(%(((#%%%%%(%%%#%%#%%%%%%%##%%# #% #%%%#((  (( ((((((((( ((((((((! !.    !, ! !'/'''/-""$$%$$$$$"-%"$%% -""$% %""$$$"$"$""$""""$$"%%$"*"%%%"$%-$$$$$$$$-$%%%$"$-$-$"%$$"""""""""""""""""$-""""""""""""""$""$"""""-$$-"-$"-$-$%$"$-"$-$ $%$%-$-$$"-%$%$%$$"""""*"""""""""""""""$"$$$$"$-%%%$%$$-"$$$$"$$""""""""""$""""$"$"$$$$$$$ %$-$-"$$$% $%%$$$$%$$%%$$$$"$""$""""""""""$""$"$"$-$"$$$$%$"$$"$"""""""""$"$$$$% $$"$"$$$$$$$$$$-"$-$"$"$$"$$"$-"$-$$$$-%$%%$$-$$$$$$$$$$%$$$$$$$$$$%$$%$%$%%%%%%%##%%%%%%%$%%%%$%%%$%$%%######%%#%(#%$%# %#%###%%%%%%%%#%%#%%%#%%#%%%%%%########%%#!# ##%## ! #!!!!! . +$%! ##(###%#(###%((%## #%#%####%#%%#%#%#%%$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%##%##%#%###%#%#%##%##%%##%%#%################%%%! %%#%###%%###%##%#! ( ((######%#%##%##%#%((,(###((( (########(#(###%#%%##%%###%#####!!!,!!!!!!!! ! !! ! ,,!!///////////$##,#,!!,!!!!! !'%!,# #,  .,%,# , ,!# ! ,!!!!,.'$%## !%,#,,$ !,! !,". . !,!!! ,.!!! !'%%$, !,,%". ,   ,, %! ,,% #!$! ,,!,!!! !!,% ''$(!,!#,,!! !, !!! !$,  %,   # # !!! !  !!!!!,'$#. ,,%,#!!!!$ !,!  ! #$. ,#,,,!!# !!!!'"! !#.!, $* !,!#!,!, .%, , # " # !! ,, !!  $%% ! !! #!!, !, ,!,!,$,!!%,# ##,$ !,!!! !! ! .'%(%#! ,$ , !!  % !! ! ! ,#.". ! . ,!! !!#,! !!..%$!  !,, !,#,, .%! , #!$   , !!,!,! !'%%%$,%,, #% !#!!%,.",%%" %%%%% % $%%%,#,% ##%%%% ..$!,,#$ ! ,, $ !!!! $. .  #,,,,!!!! '#("! ! !! %$. ! #!, '$!!,,# $!! ! ,,!!!! , ,'$ %$""#%%$%%-$%%%%%$$#$%%,"%#%!$$"%$$"$%$% $%$%"%#(%$ #%%%$%%%%%!.% %%#! ! !# , !.$ !!#!!,! %."!. ! # !! # !! ,! !  %$!,! ,# %. !(# , .$,!  % #!"  ,! !!,  "$$$$($$%%$(%$%$$$$(#%$ $$%%#%$  "%%%%%$$$(#%%% %$%%"%%#%%%%$$%%%%% .$%%"$ %!%,$!###,%$%%#%%%"! #.#!$!#%#!!  %%#!,!'%!,  !#$ !#!  $,!,#!$# !! ! !!!! , .$%%"$ %#+$$$%%%%%$-%% %%%%!%$-%-$%%/%%(%-%% $$$"*$%%$%"$$%$%%%#-%%%$$% %%%%%"$%%%%$$%$(#(%%%$$#%%#%%%$%%!$$! ! !!#,% !!,#!, $ ,  #$.# #,!  ! ! ! !, ''/''!,/'/+'/'/+'/$$'!//.""%-%%$%%%%$%$%%$%%  !-$-!%%$%%$'"%%*$$%%%-$% "%/$%%$ %%%%%%%%%(!! #,'%$,!  !#,%#,!(,!!! ,.$! !.,#."#  !! ,!!   '"%%$%%$%$% $%%%%%%%$%%"$%%%$$%$% "%%%%%%%%/%%#$ !  ! # % # , ,!!!  .$!, #$ ,! ! ,!!!! %'$($ !#, !!#% !.!!! $  ,#$,   ,! ,, ,! !! '%$,!, !,. # !!#!! $! ,$. # , ! ,!, !! '%$,,#, ,.,,!,! $ ! #$,  ! ,!,$! ! , , %,.%$ !#, ,! !,!!! .$ ! % #$  ,  ! ,,". ! , !#  '%%$#,,!#% #! ,#,#,,$ ! ,!#.",   ,.$ ! ,! ,   '%$ !#!!,!# #!!,%!!!  .$!!, .$ , ! ,, $ ,,!  '% !#!,, #!,#!!!",! ,#.$. ,   , % !! ! !  .%$ #!!,%. #! !!!! !$!! , !  #.$. , ,#!  !!   .!'%$ !,!!,! ,#!,!!.$!! !% #". , # ,$ ,! ! , # %",,!!%,#.%! !,$,,   ,#.$. ,   $ ,, , !''%($ , !#%!   ,!, $, ,!,#.$. # #!  .$  ! ! ,#%$,,!,% !! #!!, $,,!.$  #!!!,$ ,,! !# !'%$ !!# %, %,,! , $,  !#.$,  , , $,, , ! !%$,!,%,  #!!,  $  ! %!$.#, !,."  , !!!,# !'''%($!,,# %   %! ,! ! ! !  !#.#,# , $..! ! !#!!'%$,!!%.% !! # !  ,!$! ! !-#, # ! ,," ! , !#!!'%%% !,,.% ! ,#! ,, , %  !# . !! , $.  !!,.% !%%  ,.%,! (#! ,!,$! ! #!# %,#!!,$ !, !! % !''$($!!% ! #,, !!,$,,!%!# %#,,, ,,$!!! !! !, .% !'%% , !!# % !! .%,!!!!!,!!!!  !%  !! ,, "!!! % !'%%,# !% !  #!! ! $!!,! !# # ,,   , $!!, % , ''+'%% .#.$ #! .%, ,!,$,,,# #.,# #! #! ,$. !!! % !!'$%,!.% #!!% !!% ,,!!# ! ! # ,!, "., ! !!, % !''%(#,# $ ! ! %!,!,$ ! ,#! ,#,!, !  ! ,! $! ,!! $ '%%!,  !,#$   %%   ,!%!!#! $,#! !! # !  !".  , !!! % ''%%,,!.$.! !, !% ,! #! !, ,,  $ !!!!  % !!''%%! !$! !#% ,!  % !#! ,%,! ! "!,!!! %''%# #.!#", %!!!!!%, ,#! ,%# !  , ! $.! ,!  % !'%  #$  !#% , !!!#, !!$ # .#! , !$.,!! # ''%%,#,." ! !#$ , !,!!,# %$%!!#!,!%!*,# #!! % ''$%$$#!-%%",%"  !$$####, !#!"%%"%$"-% #$%% %"#"%%$%%$%$# '+/$-$$%%'$$*$*%%- -" $%%-%%! %%%$,% $$%$$%$$$% $ "(%%%%%%$%($-#*%%# '""$%%%'!$"($$ $$%+%$%$%$$%($$ %"%% !''/$ %$$%+''%(+'/''%(!!%$%# -%% #-$%%(%$%%$% %%%%$%$!%"/%$%%%%%blobby-1.0rc3/data/gf2x/font01.bmp0000644000175000017500000000337012042452401020126 0ustar danielknobedanielknobeBM6( v  L )& ) #4#/, n NJ&"35 PL z  QO,'3 5X5NJ lUS 4Y4$ W# t  J2G C I*(Z  )s'PLGC>9 : DwC{y~_\%  h  poje>WsW]Y EXEA= R@T@yx" TW`_ ] `fGF dj)*_)OY#20R:H_)oG6YL:u\blobby-1.0rc3/data/gf2x/font02.bmp0000644000175000017500000000337012042452401020127 0ustar danielknobedanielknobeBM6( 6R61g08$7$GFMJ85,v*!B!  />F7H@/@H#@H@Iٍfg4l  qH\+ebܒ_1CL^GáOև&DM %'hC߉%@L2!7\7~<J &+3%.5XͲ5s4DeMo}VtC4>-4K͟,_s% 2KDSjqJ!Q(e<|%K_  vv~sU%1 m45c#q4G }j=*N Dy8\#p ­piZu=kvqĴʳqO"%&v}~zvblobby-1.0rc3/data/gf2x/font03.bmp0000644000175000017500000000337012042452401020130 0ustar danielknobedanielknobeBM6( '*-27?8=E38BHP\?ES:?N+/;! *,6kpszt{Ж~ltPV;?g13N)  ϟҞӥˢݏ|dh?AC&&'/صᶸ``y ;910     blobby-1.0rc3/data/gf2x/font04.bmp0000644000175000017500000000337012042452401020131 0ustar danielknobedanielknobeBM6(  HwF p(@ x)]> r [6jK & P3l.L 0 w  G#@ * / #b2" M40  K , % 0 7M&x 5 ) / 9 A@:3$  y~#     $1hdMF"  . 5(" #!$*)50))>vrMF!'2-=:QK;5!,(G=+$#4։^Y83$!E1. "ԄRO'$ B({xCA**w N(novuHH--** _utfgBCq1ØxyKL"OwwUV*,u( uuRT,/K DG,."bbrsBEU\\ylm/2 ffUW##\LKn}~PP::^UUblobby-1.0rc3/data/gf2x/font40.bmp0000644000175000017500000000337012042452401020131 0ustar danielknobedanielknobeBM6( A[ <~ yW=*r^%%%% ]4J- YY{{wwkjQQ('O# KK!!b..?WWwrrge c &NNCC $$3hg}>:h &..OZZCJHi@7^aa)) g IGtrg0#%2KIFE  cYI47'@7WU 8F;aCQ9$L SQ1- ZD2fA[9"FEDOK  !<]:vHk=-L65ahc,$53M/qBdžP|F e_MD,L 7,h1%\, N7$aC'ze:|DBN4h]Xi\@.1& qR~Q}MyCEP͂H=$T<:]u_I@-8 rѴ︇p[J {aX;3#] oyШЕcCR!bobxtIK1z ,%0}k=w* 0'=Zh>!,"e8ӊ+6: tpJ@"R\80@D{`Ώc{7)0 t%~?0,LN=1C]4P Y-0.2MP*./+$!#h\6Ұ20/.ALB'uZzݟ{g`^jvh6 ##gKj|Êȿ~ÁǓ]W1Y%%blobby-1.0rc3/data/gf2x/font05.bmp0000644000175000017500000000337012042452401020132 0ustar danielknobedanielknobeBM6(  $ >O [[M 4  ! s c 9  / %-=EVeelkqY`:= b. K[QPX]J^/#4;@T|^yƇtHY-!@  rv:) J/"&7-1' IdXըMŇ XC; %,37F og6D$,-f+Rm +B6IPQ d =f Nm D)#! 6[~c D/dZuL ?, D{ O ?5)''x QP6 F A 9;+LI=nRH)+  -d>98 ,Wu~b Pa-:($# %$C,r 7 <;6a|o,} O3a%M"O&h1='O8Y4P#f pyF|P 6/ 2?A`[tVj-8y "*ځUx'N:~"*8-6e 15B{Nh ;\7]"" \B={zh CBCA% G"jQCTbqK @=-&~E RF-g}=C`.  HFJEJFZTOB, ] +    !OM%U~/9b$  gckexpxsp^T9.'S"H* R@]aa#Q&9'o$O [X|yҍؒߙڗ׌y߆c_#M rQ fae_M6)-u    {jդuB!VX':f-F6"e"JvúzPt,Fk3#.+"~i{;W Cv%  IF8zXX+6 MW;ufO/  t_uPC #)tj_# *@N3rc(2 P<(= #L%I  ,A<'  e aX?L6NGPJ?9/  Ih8w{~w{en\OD-6 i\ڎv?N5ra5O"blobby-1.0rc3/data/gf2x/font06.bmp0000644000175000017500000000337012042452401020133 0ustar danielknobedanielknobeBM6( )!M#@AJRMB 7 "  $ :&#! sJ )  N* =#&G)9aԀk}`lBK{@& h @+7' !m֎{RVb2  m#/J)0 OtX~ʋzNᖔQR]#KBzkߍ{KKPᛙXV_ vfߚ+):"##㡜d`fqzf ग़NHK fk СPIJ}ҡ} #)'(÷ζ|ikaJk͒ệG6LumnŶYGH."0ā}Z$ дhi ۉd7'9q]^ܣln=&&K9MrpAp 2()֐UWb76  }ZI,GuwgjKLH3HwM6!3`c^aFGjFhqٔM2.inLN'*rf͉:uB$7s8=FJ7:-. geKZ)B" @ @E6;03<xZoyK5MP +B[!')0,2+/')B0;ɁoEq1L#2$0#-$((*"h=L\;]+?+8.6&*BLcnTr?Uu.6.6ql|ucvfpQY& )  yrxy~]b26+ E-3ېꖠnvTXhj>?blobby-1.0rc3/data/gf2x/font07.bmp0000644000175000017500000000337012042452401020134 0ustar danielknobedanielknobeBM6( w*s H  8@  #O52C },PeGV g )93pևAX ' GEeXr*S }-0iaяXZ7 oTj@@0=B|C[s)> DWdl!m5j6ATq;q:Y/ 1F= S / 5@mKbB]0l <!  R[po]n4E" wXP R UbmLT|[c&.    ()+<-/A35SbeW\+/&#"0%7(  9:DE&'+&H  $& +7586*%*$& FY!%! <=UUa_XSB<2)1)!F-/x/2,/-/02=>VWpp|c\TJA4-"Z!-.`xyzzss}|Ԥ洲vlZfQ*E 76AcaywtߦzXGq #!)>;KF=X;/Fblobby-1.0rc3/data/gf2x/font43.bmp0000644000175000017500000000337012042452401020134 0ustar danielknobedanielknobeBM6( :-s+-  !5!#  E Ep   FD I ,  $F# M8H8IE W9  (& |  nlD?| _C  LCCaCmjD?/+  , *([  tr^ZROHfI.-,)*K)&\%'%!c &WVfeNJ-h-NL]ZHEEBDB98+h*";"0=0jhFB74\[ywjhTR@v>1K1  omID,)/poܥҕ}}b`FWF((&EZF`])&2n2jsi̵idiE=E tr:80-{x{Ⱥw_E_1%0OfN_]65-=- ״ĊÓ[T5RvsQO@\@ ޟn~Dy.,h{fywWmW$#) &ДށٲLW,Q978{y{E1CoQliΔE+& ~E~ v۽Li4W]MZɓɲRa&['$zWB/" Əlػ8r:_]Bj0JkKcr2  + ?{4\( ' ti XW E& gFk+mU2z(d+!o%&'t)Wr4[iZQO_R;z&S p鉹v7$,Sdv_c-blobby-1.0rc3/data/gf2x/font08.bmp0000644000175000017500000000337012042452401020135 0ustar danielknobedanielknobeBM6(   ",)$3-$4<.D=,DA/G=,C4&6-#/  '&::6RI;_\BstNVekjgbZvJ{Q3T8$8 5/VMDȝIհQރF").'/P@RnoFqA1D DΟFݯ@1 B5CըޘhzN|C1DFۯ@.JbŽt~V~  O=!8K:*;؉z` [D\m  xa֛S ]^ 29RXӢQ 5%BzZ~5:BD9OϦH|T^QV-0  GJ#3EؽT hiptDH=?/ 246`a$1GD^Ddvu2ؙ|x}|   7ߪ<)* ^;dU1   '\ ]  TG&,Sjv|oU.#SU ]C&#NQ//+,9#;Y;Ze݋V?@_Cbn$gj`\* _ c  q4s*{ "c=g]NR22ؚ+-ebnqDF*++,km 57 iy? Ȧ *#%,,D0En{V:07FUVHk&n1$0sOt̊~O.-blobby-1.0rc3/data/gf2x/font44.bmp0000644000175000017500000000337012042452401020135 0ustar danielknobedanielknobeBM6(  ):)  _^ecAl@- ߝ~DB9vv|z=<  ͩ~|  VcVGfFblobby-1.0rc3/data/gf2x/font09.bmp0000644000175000017500000000337012042452401020136 0ustar danielknobedanielknobeBM6( @D@NRNHIHý\_[&*$OPOگq}oEME#(#SURÿΧWhW$1$  #%#իѪfe5K5"AEAƲ¤ܣYX%B$.  vuD~A'M&9 >V?igZX<:$V#> !$,$@N?RlQca~kjbbVTPK52X:*U_U\qZdcts}{҄މ`[A=!`B =I=|{ឮﭲ쏁~[X85!y I Ğע<];;O  n  £ߠol;|:5KxJok  B 3<3Ңc`0y/0  li83  u OcONJ!{=  #O$?;  VoV@;Q $   -) J`IڑEAx C   '4'Ԓda#  y Q 3 *,p kiPL    +;,if䈄c_FB=8E?KGB>4/">[OR" ΋  K#++Ufߧ2$ $    blobby-1.0rc3/data/gf2x/font47.bmp0000644000175000017500000000337012042452401020140 0ustar danielknobedanielknobeBM6( UT7a6%E$ RQNL5q4<RQpmCA-n,a tsb^B    *~(G 8y661 O qnUP S"+H,xvki,%  a 0 ,rp@<q2``KF |6L|J}{LG  { 8 9`8zxEB   9  (D'soKG  9 IiYJ:a8yuJF s *(% @=l;tq>;i % 8KH (DxAa^)"Y I}Gmj/+v  =:GC'$0fdzw95{,$GE63(&'c&  tsNJ)%,)){'65IE7475 ŅwtRNOKd`sqmk_]\ZWyW|y:k:&c}bΡ걥ᤛ֚MpMblobby-1.0rc3/data/gf2x/stange.bmp0000644000175000017500000001412612042452401020301 0ustar danielknobedanielknobeBMV6(B ʦPwlAЫ( |@wlA(%  DwlAЫ( |@wlA(%  8wlAЫ( |@wlA(%  ,wlAЫ( |@wlA(% @ @     @ @ @ @@@     @ @    @ @@   @ @ @    @   )@      @ @ @          @   @ @ @ @ @    @ @ @ @W,3F;jAxਠkl01t:ehJ݇~r?/0R+/eGAF5Zh,hsp+0,gݘY4Fhwu,_,`[frntv<TVJghjjwxyJgMK-x.DŽEȈRQi,wgXHT?:9޸:8f>Jap:4t6xJ]M-X.wpޕFP=:4:5ZݤP;f<fjZ\:g23Z^RevM01xwz|?|qlmň}R:7Ph:]--P8ؐgwG:P5:_/0/C~xzĢ,K+Iw{wë,mljXi`ê~NHSPƽhwcn[]ǘآean2SUSUJg>/0|LyK\suDGMXDExrIėƙ~aT:yB[LGBvbvdNv<A[>YfS[gzUY֟vgKvXFvxNyyrf;;Ghv:df7fj”>DɴAOhau[yt_g`gc:kfwjaUXWqNj\IJ@|?}Ru+Vr;S[g,?Dw-}?Aگ&&&,,,,&&&,3(,u&&&ZZZ,Z'[,33,O&&[ZZ,3,,&&w -_Z,,,ZZ&&L [32ZZ& _w[Z,Z,Z3u{*L-_L2/32, )Lw L3ZAJJcJ*JXL_ nX0MJJJ$XL Lw3y&nc0`JM*`$L-ZZ n0XvccX$y30)$a\0J$Vo{)cJ2,Z,)vMccv-JQQM  wL33&av)n0gm6$0M-[,OZ,,&Z)66$M*  C}}0MJ*,&,2R[[,u&a)\0Lx mm\X*([u&-O,,,Ouu{vXva0JXdxUQt$JL_Zy&&(/(,yZO,&aM))MvcJBd ]]wXJ(,,JL[O3ZZZ3,,)Ma66drt]* ,3/w233Z,';pav0ac[JLdmknX[MZ3u,,&u&u& **2L_- L-L23Z3uvMQ\) LC H}pvJa$,w[3[w[Zww'  _*$0J`_LZ3Z&cW)a*$: %o]JZZ,3/[2f,AZwNN[~Ou&J*DMJ _J([Z3,6Xv0v0n dUvJ-R,&,ހN[RZ~ZA(ZNZ[((wu JLJ(-JM5L3(Z[u vvv06)Jc~CUmn$ O(,u3[w[[yww((RwRww(~[wRO00_LM*$ *3Z3,a$vJMvcLX[CCt6Xn*`2y,,([(w;T(N(/ N(yATbyw[~[ww/A;\JMM0*)JJ*Jv[~b&n)00JJnL:%CU}p0J [[2ZyuAZ[2`((; T;__N;;_wf/wfR(ZZZNZ) 0vJJJv$M0\ )X,~,)5nM0\ [[CUt6]`L JL(R3Z(f[;0(($L_05 ;$_fX5;(;f;(NRZ~RZN݀[yO$v_*XM Mp6$ao*X[LJJZ&(vnp$o6) wZ̥%xm]J-J0L_[[(w[(;L[a0J0X5;v`);T__/NbwZZyOwQV0$aJv{{o6tto{*$*JRZ[u;X6JJ6Jqii%UUm])cMJ JJ_(wLL(0; ;{$;v_v`va6Q;p5vQM; NV5;R(Nb~Z(u))X0JJ XvX67m6vnnJL-[3[,&Xa9)J:% i.avML`2$JX TL`ff`XJoM 6$)6+66app)Ma6aM5a_;5f5NNRyZZRZ&&ut0)XJMXnJJ !H U7a ZZ3~0]JJn*didx V}QJ[MP00XX  )c0X0 va$)]p)paQaa$a]6p\a)5M5N (fyZZRZ(3Z&&,a$\D0)$nJM%C UU >tJ$X 3w60M0J$L   ]QaJL*MX*0L JaL5av6)));a]na]{aa66{]6)V)Mav])aX;5[wRZZy'[,(~,&M0cv)gddiUUU mpaJ2,&Ma c00cM3R:%im)JM0LJXJ)`$${$))p)+666o{6+{Qk6{Q#]{aQ6Va6{0pLVNA([RR3,Zy3,&& `XJ*ddCC }]oMJ(ZO,MM)*:BxBixUm}aJJaXJJ`c6v5 ])MpV)a]a{Q]tkpp#6]oQش6m66a6onQbn)_ ;5`NZ[ZRZZ~(ZN,Z,&J]Lp`J,知x mo$ ~wX0n0M:̠%:d  #mvXJXJ;XMJXv\$v])6op+Q+o}]Q]]#tQto^Q6pp6p{+6a{)v 6;; [N([w~3O,&&nJJnJXM$[֤ d xm6$JJ[2,5;X)JJMJ\`:F dddiCVtoJ0J5L){00v_p]a#p#]66Q]otr]mm}m}}ott]]po]++Q#]oa6{6 ;);/O`[wZ33Z3O,&JLJJanL[&4qd xxCmQpX-X [,,, Mcav;;gB   mQnJJXJL vMa))6aa66a]t]tt}m}7m}mmmm}}7oto]]m^o)+tm]n0_NMNJL-__(_23Z&,Z,33,G&&0cJvL,z8i%C }t$J O,,,c);X$JQodx%%t];cv0;0aak{p+QQtttm}}mmHA}mm]]t6]aM]$_nva0J)0[L;L[3LZ,ZZG,([Z,&&&/L/X*0JM,4 C 7m{c*J0)0JXJvcZ:xCUmoQXvLJJcaa${aapokot}}.鰁 Vmm}mt]V6p]6a60]f0{JvL/[(wN3yyO~3[NZ[ZO3ZZw3,&&&J2`$J$L ` ,"j% %U}6)-&M00J;$cL[ҧ%dd U#}])0$J0JL]p6]Qo6t}oo#b =eeeeٙ=t}}ttt]]]a)p)XX0cJ[LL2[[2w[3Z,yRZ3(Nu, LJ$n*J$X;~uCC%%Chm]aaJJ 2,c{6JXJ$(g:Cxi d%pv*XXvJM6v6Q#6oQm]ݰلeeʫeee= o}oom.666a\)v)_\ JMJ0JLLcNL[22[[w,Z3O'Z3[Z',&[L/JMJ*$&z8˥%x7maJJJ-2ZZvv\0vMJ;dddiCQa$J 0pMv))]66QQ]otoeʫeʫeeee===m]]6]$66npa0MJ)cn-[- Z32,3(3GO3~[ZZZ(Zn$[ &jixxx^6JL('X5L5XcJFiid di%UU}t.Qnvpap6o7t}t1ʂʫ e==ee=}mt]]k]6a)pa)av){pvX)0v$*JJJ*  [[wZ,,Z~,2[(,~MJJJJ_&'j@:dU}]MJ[33/cXXoLJ*J!%ixi%U }.v)av))apa]mt1ʫe==eee=ݺ}o]k{66avm]6]QQQ{a0*;[JJ cMLTw[ L[(2[23ZL LJ$(,&@iCU"mt5J 5-[,&uXn0J̟  iCxi r66a0aMv)ao6Qptomt1 ʫeeeeee=mmm6p))Qkmm}tpJcMMn JJ_-P[DLO([-(/ JcX6nZ,jdUmacc_Z,J*pJLX)-  %%x% iiCg}t]c{)aa6{Q]o}tʨʣeʫeʫeeeeee==}]t]aa)arr}}o{$cXXJccJJ$L ` LN `cJ_L[JML(,j:iixx oJ RZ0nXXX0JB:d % i d%C}pvM))Mo6]oomoʣee ʫeeeee=ѯ}6Va6vgxxg}Q{$*J`M$JMXJXJJnXJJX*J- -J_*Xc0J,ujE%%ixxUpLJwOZ&&aa)0X(XX__% d x%%xU.o]XM))aomt]t#ȣȣȣeeʂʂʫeeeee==}]o#V; %CU>6JJ*JL)) oL*JJJ- LJcMJX$vcu@E%x"}v* `2ZZ&XXJ00XXcB%id dd d %]v0X66{6]Qm}㇣ȣȣeeeee==m]p#;@FCo)J *Jv)6kQpp6XJJ-JJ0XJ$JJ_vJJ0LMJn[&zjdixCUm}]XM,[cMcXJ5y̟d i:dd C}}6Qa)a.pp]]Kȇȇȣȣʣʫeee==V}]t]E@lF7tLJJc*J\{Q]mm}}QpJJ$JJc*X0-(&hYddx }{XJLf3ZJ;JJJ[-̟dCFޝiiU}k6M)]Qo+ȇȣȣʂʫeeeeee=m}oQbj@C}p)M0 ק{XJ)XJLv(JL*MJ00JJJvn-,Odd C }]vJ_ G&5J] J)J F̠ddx L8i  }6)\oappto7ȣȣʂeeeee==#Qojq%taXJ% UU K) XX$X0vQ)$]6])J0M))66\nnJ[,,z@%di%%Um]aJ*[,3O(&,c XJcvJXJLFBxi d zEB%}Qap6Q|potȇȣȣʂeeee=֯}o#b@矟Cht6XJ(%xU C |M$MXJaa)ptkok66M[*JJDV]}ta$JJ `&8l iCxUtm6* 33[Z)$$*JJ ̟%x% ddzz8C=>o\aaQ{]o}ȇȇȇȂʂʂʫeeeee== 7m]ф4CUm6XJixC Um7)X* X6p]tm}]{oXMJ -^m60Lc*&j8id%CxU.]cJ` 3[vJvJJJc(ddi jqiC m{t\QosȇSȇȇʂeeee=ѯmejlF#tpvMJOi% UU#o);*0*X_"CU ًm}Qa$* iU }paJJX[O4zidUUm#0M LN&uMJ5+JJX B䖥9J,j@q e]nmo#]ssȇȇSȣʫʣʂeeeee=o}ьj%Crm+XJnG,,:d x Vt{M0J *%UU= >$0M([d%U 7mQLJa,&@qdC}r)nX*(2,L$$JJM_CUXM, C}Эo]]ȣȇSSȣʂʫeeee=mm]j:C]vJJM,zqx dtkJJJJ Cx% "K>oJM6C U76XJ0L,&jixC%CUm]6JJ$vv Xv-JXddxUc &8zdCKm]p.oQ}ȇssSSȣʂʫeeeee=mm7Vjz:C1}oJJJ,qx%U!tp]XJBdC%dxU >maJO >]*J,z:xCC.mp0X*L[,O0c_vM[dBdCxC_cJj@ʯtkpoo]ssssssSSȣȇȂʫeeeٰr}]ƄjFq"}Q)J)c,qd%%%U"]mMOdxxxdi%dCx ">}aJ2,gixm{)&EixU QnL[[R,GXXJJcJLFxddx%x$p0,zjdd=76]pm]ssssssSsȇȇȂʂeeeee>^rѨz엖%m]]cZ,zBdd]]6J[/I dddxxidd dU"o)JL,jddCU}m6QJJL,@q%%CU"]p*3NG&00vcvJJLi)aJ_&z̟̖}o]Q>tsssssȇȇȣʂʫeeeee=m]ݫj8%moa)J*[EB  U]QJa dd%iFId ddU ٢}m,,z:%xxC }}$ [,j CCUm]oX&X0JcJXXJ_̟d ix$J$J,"j@FӘm#]7sssȂʂeeeٰr´ѫj˟%%#tacc2,zFdi% UtpLL[Zl dd8FUU }6\L,zEx }{Z,פ diUU#m]c_-[Z!0JJJ0*[di CJ00,@jzEmm]tssssssSȣʂeeee=mt݄4xUotoJJ@liU7mk_~qqxxdl@El xCmX,,E˥U}>#X,,jzddx }Q)JJ$`,&c$JcJJ*d di%o*J(&jjiU›mQp}sssssssssʂʫeee===}]#ݜlxCK]QaJHҥ%CU}ac`22d %CxCy4 dx%U 7p,qBd%U}t]&qi U7)J/(,J$XML)X8xdd%UJc`X,uj1Әo]7sssssssȣʂʫe===ٰ}t#ф4:‘XJ$,ddiCC#]]J iC`,&jd%Cx >n,@xdd%xo&j̥i% }t]XJMn(Z,,X*$X$cJ qxxxvJMX(,Ozj˟ommsssȣʫe==7m#bjziUm]v_,OEq ddxCCQrM0,q: i%X&&jqdUnդ:%dCxU tv&~j:Bdi%UVtpM/XL-[cJvJ)*J*L_ 䥥 %XJJ`L(,jz}mtotssSȣeee='mttVRjFUQMX-,q%dU"tp;[,x%6&&4 iU}t[qd%d CxCUӯ,,x%xU>{XJJP&*_))$0*-Zd%i xCJL(,Ojҗ:F1ӾmtpSssSssSȣʫ eee=ݯm|bjqdtQoX,qCiC tQ]X,lixp6[&'jz%xC rma,@d iidi 6Z&@iCUhQoMJL`[,,JJ)_nc:d d x!JcvL&Ej@}t]ssssssssSsȣʫ ekrVejjqd}ooJL&zihtpvM [y8%%CUt},,&@ii%U­L,?zd % %dUo$O@ddC}]M*[ZZ3$`*_d idaXnMJL,jq̠Fϫmo7sssʫem}pQQRejCtop$JJJO@F%xxCtmJX,xxCtmk&&E%CU>Q1E8:CUU%x U]@%d%CU^t6;*L*Z L0JX**J*0J(dixCxx%!6$0nJ(&jj@: FϾ7mtSssssȂ1mmpVfe4:di%rt{J O~ dC]pa*,?q %xxmoQ&48zFCU7pzl%Cd%ddx OEECiix6vL)oL'&MLJ$0JXXddix) n*nMZ,@Fӯo#}sssȣ}Q6{VA4Ezd˥#]v$X@id xVmaJJZ,8qiC%kMX(&ͨii%oXפlddid%dddix m*l@xmtp0[,,0XJX [dx%i%x")X XMJL6,Ojqz@𲘾}otssȂȇȣʰom+tVQV֜jxCC}p *,E%% U}]J,8UC"=mv&44l: xCC]*~% d% Ci UvqiUm]v*[ [,cJ0JJc cJ%%i*nv0JJM(~&䖗Ә}]]mssssȣȇȣ1Ưo]]Q6fRjU׾]a*,&zqCU]J[,8B%%xU  ,jiUh]n&zl%d%%%i%Umqi UCUop$ - &&M$0X$LcJ`JMn--i ddi%xIcXJ_XL[O͜qF1}tttssssȇȣemm7]6]6Z4z:%%ChmQJ,qd%xU֛paiixUC >J4 {c,zzi%idd di % Ucq%%x})J2~,w[J JcMcM`X h ddddd%JJJ*JXJ O,E:F}QкsȇȂtoV+{6\6fRezi =.m)**XJ,知idUQ62,d ddU 7mJ4j C),h@l:dixddd dd6Fx dCov*_J*XJXMTߠd ddddixCXcJ0nL&͜lҥӯQ]}smoQV]a6)yjzi]a]Jפqxx%t5Fi dd d%%ixC m0,HE4dJ["ld ddi xdxP %iC #6JJJ*JR&0cXJ]cJ!d˖x ddixJcNJXnMR,,jjlKӯ}t]]Sȣtm]pQ666naRb4@8CUUt]avM,@:Cx%Up]\,,q%%di d CU)&M_,'8CC d:d UUg %>6vJM-[L00MvLJ*JJF%%%cJ $JXJw[&@qFҧʘ]ptSst]66k\6pRAz8CCU m6JX2,,q x ]tpJi%ddCxdidd%  oJ,CiҠdd%UixxUïp)*[[,&&X0c00`_yidd%CCxhX{*vX_,'jӾt}]}mSȑtttop]a]a)_,i C }o)*&@Ut]0,O@:diddxdddddCC m>oLJ3&ͤd %?iddCdi U}tXXc*L2Z,$cncJLJJFddd %_JL*$LM`uzq>mpp#t}r]mtm6V6Qa)\03E dd "t])*J,EjdUUm{0,,d xddd CxK6JJX((,zdi%*8dd%x ddiU tkpJJJJ*_2Z&&&,&p00XJM$Jid %%%IcJJJXLJ[,jqCϾmto]oQ}m]Q]]aa5)a5MwǿddiCU}pa,jdq@B "mpv,?疥%CdddiCC%xC U}>aL(&"jd J[z: i dxzdd%C^mm{ N,,(2,(3,O0)0J(cJ*Iq dixC;J_JJJ`,&jq:̖FC>|}]]Qm]tto6]7]aQa)Xaa5`,zFi%Um}a0LJ&e:ddUt]X,jixCxi CCC UtJ&Hz@F %xj4l:ddi%idxC mQX;cZyw&&, [O,ZZR2(,,&,aavvM -dx di%xL*XaJXJ*JJ0,z8BϧӢmt{o#7t]Qm}Qtmo]mt{oapaavZ@z%xm60,~zdd%C }t)[Z,'zj x%̟qxCx H\&z@iCi d%xiiidd k)-*3Z,,[,'Z33,32~&O)0X;0X_ZdiddxDMJcMJ*M5_$M**X3,&Ǩ lC]]pVQm##tmӼӯА}mkppV6a)v\\vXMD,q x}tvJ*JHj祥  ]p$M &jdxxiDqqx im]X,zzid J&j@d %x% iddx U]6L[w(![Z3Z2w[uZX ;J[ ddi di %caJJJJ_0JZ,j?Uӯmop6r#p]mtQQ>ӯ]|#o]oooppQ6va$\MJ,E:ddd"mt),~@jx CUm],dixx`qqB% d k)L,8dx{??8d xxdixi%xrm]  bL /(/3,Zu&;)v)5M{MM )  d%xx * $JJ0\pJ`nL,ujjӘ]]6o]Qt]]]]]]]p]tom#|mo6ktt}kQ}Q]+66Q+6\$pVMc _'?lBiiiCUpa0v [O&zq%x iCU6;Z,'zddiC_UUU}o,I t&Y8Biddxxx%U} L[Z3,_-wL` `2Z[3O[u&&;a)X)6JXJdddixCJ)M**)$*JJ0v$MJ&@ӯ}o6)o{op{ootQkpmQ}]otmppo6oa)6v)n*)J0,gE%CxUto]c;J,@BditL_&jdd% UP*2j@qix}oa,פxd%C^6 iidxx%iUUm]ac*([,JJ -L2Z(,y(,,u&uv]6XvcMMdd dxxC_Xca0MX*XLJ,&4zdUӯ6app6ap.pQkt7]+7tt]t]QQQaQQ]6)aa6aX)60o X; ixx r}] L,褓qdx 7])0JZ&zq%d  )~jq%i maX,q% o*L&ddii xC d8U mtav**XLZ,J$V-JL [Z(3GZ~,Z,&&))6_vX[yBdx%J0$ MJaX{{ar)cVJ;,=4dCmt)6t)#tV]7Q]]oرm6QQ6Q66]6{n)$) 00J0(',i%U}k5,פ?Fq: iUm)n[,&jjq:x%iCU$,4jdd ݘocO,@ %iCo)jqdixdd%C%d }pJpJJ,*nJ*2/,333ZZ[,&5{M))vJ*,Bd %Cxac$_n _JJ\M66p0$cL 2,&ͪiU>mpv)Q56#6p]{Qopa6kt66p6Q6a)#aa66]00 0J#XJOqdd%C ],椿?i%xraa_,&ͤii%cjj8dd >M,,8qixx }p_,&ͷddddx%bm]v Z&&0)MJ0 J*-LZ333O,&6o)aM)JJ,@dFXJ*JJJa])pta6]))cc-L,j U K]))vQpapa{6apaaa6pa^Q6aa)a$p{p6a6a{)vMX$6 @ixC to)0,:4xxm6aMJ[,&4l i%CUm3j d i t]n,&qz U moX,&u4ddddi%xUm]oa- J23JLXJJMJ2//ZZO~R,&O]aa)$)~ji /*$*XLJMM]OpoQ.]]Xa0JL2,ujj@% >m6pa5a)pn6ap)6)a6pp5){$aQovQ+$a{6$6\`J0,椿:x#}vJ[,zqxt}vX&ͷ ddC ^),z8%i }6/,@q8x }) ,&͜xddd m])`XL* 333(6M;JnJ JJ JJJJ(3ZRN(3ZO]mo6pa06X44d`0*J_L*__Aкooa)aavv L&j%C m]pXX6vv))p))na6n{aQ$5pntp6Q{#a{{J JJ,zE xxma2,椤4d CmmaJJX,&4l:d Um{դiiUU}p[O:%x +m6&&j xxx%x%Cmp*$_D3Z60v$JJJcJ0Xa_J* L`_L[((/[,R,,p]opaaLMJ,HjJML WX$Oom7Q+6{X*J[&jiddUӘm00avXvanv6MvXa\a)a{6a{oVkQQ]o6]+]a)vvJnO@zi%C }mJ,٤%d%UttaJǮdxdCU ]8q CC#tn '@@ ddi%}t0&'jdd%CCC%x m]JX  [Z&u )v0M-cn[0v)){6]o$cn* #_ (2[yZy,2,tok6Q}X*JWO,&ժqL LJJJ*~mtt])0* ,&ͤ U rӯ]6)cXM0vvv)_)0))])\pa+p66]mp}]]pM)$XJ[Z,@xi%Ut{X,@F %Um6;,&xd%CCCgq%CVk$,&E dd%Cm6/O&ͤ:d %i dU }tJZ,,,Ovv)6)v5LX*cva{Qo}}]{Xa)JJ JLL (/3,&'toQ]6aJL,O-X$J*`c}oKo]]0n,&z%x ma_Jn0JX60cMQ65\kp66}mQrtoQ0; _J-&ixCUmo{*L ,,4qdC%Up\&HǤi CxaL̟̥CCC{6,@d xCh}])X,ujj知xddUU }a) *LZZ/anva)MJJJJա"mmQ60JJnJJ JLLw[3R2&,,&ummtooQ6n n_[J;X[ A}mpo]M- *(,ujBiCU=>k) 0JJ00J_vJMMvpa.]pm]r}t]Qp{n* (&j%%CUro6J_&&Eqd %x mtJ[,&ͤCC%C ;ddUa&F:CCm)&@ Cd CUm^M0JLLO3[;)$vX0L`JLxx }}mm6kMa{a$;Mc$\-L233[2&&Z23,32,,,&uV}m}m]Q{vMJ MJJJ[Mc(L&ro}^pvnM*,&zͤid ta5JLX$cJ_))M)0;))66'tQ\JL[A@ziC% }6 ,qdd maJ&&j CxC/ddxCppcHFxixUUUmV,& x%% xC"}mn* XL`[Z,&aaa5\c[_iiCC }##}}}moo)p)MXM-JM(XwyOuZ,Z(,&Z3[Z&&}m›p]QavJ0nJJJJJJ** >}mo]]\LM`$X,&jҥddx]a6LJXc\JJJcXJJnJp)'u}}oo6`a_`,ujd ixUmt*Z,1@id % ^.*J&4 iii%% xx)0J,@dxU +a&zqdi%C U}mp_2~O~3O)){6vv**:qU%# "V}>}m}]6]6)*M*[2~Z2,u,2[3O3,[Z,&&}mm}]]QapVXvJvL**Jc6JL2J&Vm]k#6aJ&ͤqidxC o{cJJJcJJ 0c0MJ-yO}}mmp6{ XX(,&dd Umman,@qdd%#}aJJL[&&4jq dd Cid%%0k0,z ii  }^ ,&ǪFxiddx Q]X0*J3)]aX2XZ8C% diCI%U U #}}}oQ+6pc-JJJJL~[,3ZZ2[[y2(Z,3ZZZ3,&&&&&&m}mmookQo5V6)$v0M0JJJc_ mm}..]o{XZ,&j@dd %CUmmt;JMMJvTJ2'r]pV6pJXL&F%%x Uota0LO,jF% dd mQ0XJJ&&jͤ xCd%CxdCC5f*&@iii%U"}t\J0 &zdiUr}Q0L_*_3,Z;6tp)Xo0X,liddix%C UC >>}}]a(/JL`Zw(3ZZ(2((L! [,3Z(,,3ZZO,&&ݛ}}tmm]Qpp6apoV{a))J*}}^t6a)MMXX&@qdiCC]$X0aX* -L_u 'mQV6QJn[[,jF%xx m]M(&x%i% toJJ&H@xxi xx %U6J,,E4@d%U Q$*JJJ,&zd xixC"kM^3(&yp{{6$cL,zl:d %iixC%% CUUHm}mt#o0nXXJJ  ([ LL- (,3Z3O~Z33,'&b>}}mmt]6]p{6a6aMa0{M*,>}mt]Q6))L[,&ͤq䥥i%I6cX6_0$*XL([&u}mtpQnc*(,֤% xC m{)0J,@ %U mocL,&'jxdiiddCxycy&פ:d% ttkJ$_,&H@qxixCmtan_33[Obm66rpa)JJ$ ,'Ed%x%%xii%CxU U rm>o]aM*J0 *J_[2_ XJX((((,/3ZOOZ,33Z&&&}m}tm]otp]QQa)\)MJJ ,m}opaJJ[&uYj:i dicnMXn* 2Armm]oV\0*JL,OiiiU"m]0$*J&zi CUo}{_*L,&':d %JJ(,~ii˖xUm6J-ML,&Ϳx%di%Utm{cJ3Zyfp6{6)JJ*J'qxxz id%% d CUCCCU mt`M M X `LMJ  ; ;2O33yZ3ZZZ,&&}}mmtopkp]Q6W{JJ}}mmppQJ,&4zFFiL*J** cLVLZ&pmtQ]t{ `,Eq%U m}6J,@li%Um+v ,&"4j@z埠Cii"*$$ ,z@:ddiׯpv_*&'͜x%̖xxUrm6XLXnL[Z&t666)0Xv,zd% xz ddd dxxUUUU7}t])L-*J5J$-JJJMJ*$X-([[Z2(Z3ZZ,,H+7m>}}to}QQQm)XJ&}t]Q)-,,&@icJ0MML_/ Z&ZZm}m66p[[,@q%%CU}aJPOidCop)JJ*JZ,&&jE:!JX ,,@EdUUo6o J,&:%diC mQMn*LLwZ3&utpp6)o$;_,14q %%@@l:i dxdCCUU moo)JMJJJJ[*LLLL(L( (3Z33&'R#m]mtmtQa66$n[&mmoQ6Mn$X[&H4qX_nXM;(Z(,u}omp6a\J* J,ziCUr6L,OzEdxi mt$J_[,&&4BLJMJ_,,xCCU>p)$&Hjq:d i%}]{_XX-A'}toop)$J*L,zi%[ljjj@8B d%x dx Ur6c_JMJ_LJ*JJ`_[L_JL[[2Z((Z,Z,u&u~V]p{p]a(mop6$XJL,,, XX*/b mtp6a5J* ,'4@d %r}6aX,q%x%UUm}6JM*2,,,&&H! *5XJJ,%U6)$XX,&j:dxx 7m]MJ_Z]]]k]\0Jw,HddJO'=jjj@EqdddC%C m}mm{JJ$ L$MJJJn__0J*MLMLL*P-`_;[L(Z,Z,,fR,'}mo7p{cJ_Z,LJ$XXJ M*2y,+o]VV0*,z x Q)J_3,@:ix#o]cJJJJ(_P*JL[,j@B diCo)$JJ&Hz%iiCmQaJ--ZZQo]]Qv^n,'@zdd%%Cc&O,&&j@qidd %UU}o6J*J$XnX *;0X))apJMJM JJ 2Z[332(,,&.}}mmo6aoaLJ(-]XLV5cnM ,3Z&t]]Q{$)LJ&8i CC}t$*_,Hx%Bx }mvJX MJ_`LX)-,'4j@8qdd5))vJ-,&̥xxUm])J2 [ZZ,V]]oQ6on*QL,Ezdd%hQ(O,,&Oujjjj窟dddxC "Ә}6ML**X)kQopa)LJXcX*- XX*M2_[,3[Z2,,OO~w32,&}tt7V)vX$* Xa]{mmm$X JL[,H}mmQ66{XX[&j%CCtQ0(LO@@qd%UH)X*$)JLLL&j@dd$XZ&ͤd%x%UU})XJ[[/ZAmop+p]0X*L&z CCCt]J(,,,&gjjjqd xU K}6)_ Jcvpm^m7p5*)*LJXJccw5yZZyZZ,bmm]}oa0XXJJL*0o6]}m6)M23Ou]t]])X(uHj?x%Urm{X[,E@qCtQ$MJcL$XJJJX-~Ezf*M&&jji%%CU.})LJ'mo6]\0*J,ld%UCCU|mpJ,&&ͤ:idd%% UK}Q6J$cJL("hUUUho)[M_J JvJLw*XL/([(NZOOZ,327mm]6X0-JLLJUUU>{XL-[Z[Qopoa)J &jjdd%Uo6M,8:dxCmtaXJL v)XJ*X*aJ[,,,&,, v,jϖd %PaX bOo.Qo]$Mc*J(,d% m6)MJ_*L[,&&@8Fidd% K}]X*HxxC e{-JJ$cJ)XLJXM J((5,ZZO,,&&tk6\0JXJ[!%xCU QL([~3V}oQ6a0Jn,8jddCCmtJ,j8B U6;0`J$JXnv6X\vX;XMnJJ L*M,&埠 %]JL2&6}t]6{ JXJL,zjqxidx!{)M_XJL(/,&&jlqdxxC }Q;nM*qd %xm6*cnXJXXJc_o$o)aMv$J**[w(_[/R3,}m#o)vJ!dd iC hm]Vnw3b]Q]6 JJ*&줪d CotJ,Ojx%ixUtv JJJJ06aa)$))0_;JLJ XJ_JJXJ ,&4qd XJ_LL5Nt]t6aV &4Ϳ%% d H}tvnJX3&&&zj@Fi%UQJ*6$[NqdddxxCU}kW`LJXJJJnv{{otmokpa$J 2-L_LL[w3O&]mp]]a0JJnXJE˥%CUmo6_,&tm]QaX0[,դz: dxUVpaJL&C%iC"mQJLn$X)vp6n]{]Qa{$5n0XXcc)/L`JvJJ*`(&hjJX-LZ'pkQ6o0c&j?i % m}6J$*Jp_L(,&&gj d i o nJw,gq̥xxC6vJJX2n06$rgË}}}mt6cM*JJJ$J$2[[Z32&RoQ66pLJLפB%U7}aML-2omkp6ccO,jqBddC{-&4B % m#a;vX$n*M_\)76ptpa]6p\)vM)$JXXJJcXL&,=Z,,J/3mopk))XJ,դ8:%C omaa6J0*JJJXL&&jqi dCU 6 JXJM[ ,% tJJ-6J"CCU "m}kQMJJJM*[J-[,,ZZZ3,N{apa*JJ,Hqd U >pa-yOV}mooQpMJLL&Yjq:dia0JJL,,՜@xUUotav**JcX]ap]6]]]]pkao}6]6]$v0X;JJJ0pJc L,, [ XX__,]mpQ)vXH: CUr{XX$[X$(*JXX,,&ujFxxxU ӯ60_*;,"知dU ]t7 *CCeU U}}]XJXcJJXX-J; -3LZZy,,&O&p)vJJ&,dx "m])X*J2,,ftQ)MaJZ&jdiJ0JJ2,ujjUx.m{X_J-Jv)6]QkptttQoo6p]6ta666Mv5T ))ncJJ{$$L`J+}m]pv6vJ&O@qdixm] [,&&:i xU >ma$2,'C%xU]])MX!xiixYC }>6TJJJJMJ* L(Z[3&2([(&&,, vcvX0ML,,Ei mtpM0$L2wottQp)$*/"@ͤ8JJ_&jqC%xC`JJJO]]ot]oott]kmpp]]kpma)paXa{avM;6MX_LJXX_NRo7Qp]vML,@qx%xU}ta)**J[*Vc&&Yqdd xUUoX*O:xC ]{$JJiddiid %i %C U7a$Jn6X M6_XJZZ3~&O,[2R,&&J $;$XXJ(,hjBx .}6JMc(,[m}}m6{0,&,HJJ&jiUCCk6MMM&]}}]mtptmtQ6}#Vop6aaaa6oa6MQM0nM$JMc  [(}kp]p;JX,q ovL*0nX*J[O&4Ǥqd%x }pJJJJHx%dC Vdd xd %%ixCUU7}]$J$J*aJnJToaMJJ-(~ZO2[3&Z[y[OZ~wZu,& nnLJX,'jxid% }])J*[/Z&bomo]6vJJy*0*[,jjddxxh\{)JJJ__&'7mmot}]tm]QQto66p]{pa666p+ppM6QcX;M*,Ho]6p)V,jBx C t)XX$JMJJX*JX,ͤC ix }m),@ixCgt)ndi%Cxxid xmm6JJJc )aopot.Qv*n* NNZ3(&&N,Z/,ZZOR3Z([&,LJ$*-)JMc-,,@iixo6pX$Z,2m}mm]) J*M_2X `* L[,Hj4窟d !0M $_`u#m}ԯt]tt#]Qo]]]6Q+ttQQo6Q6{$5a5_)J0MJ_*( Otto#a{MccXMX&Oj:iiC r}oXJM*J`)JJJ,&Hj d U UmaW*J*,z8idi%%o]a;2Xddx%C %q8ix%U )LJM*Jc6WQrmmQ6)JJJ5-RZGZ[3L[([ZZ33[&&;(JJL,jq iUo}60J[3Z}]m]p{6XXc$M*J_cJM&jj@ - aM2Vt}}mtt]#]{]]p}tp]]]]7pk]pp6))6X_L*ptQV{)0J$L,&jqdx%"m]VJXLJ_M/$*L,&Ǥ%ddUra0*)f,訝 %%"]t\*J_hd%iiFdd d@z@d xxU])XJCUUU}}mQ6MJJJL`,,2f,[[L/w;[ZZZ~(&-PJJJJLnO"j: d %U#tQ;;3}m}}]QQ{0MJJL-JMJ,hz3JJ* L&Hm]]]m}o]o]tom6mQ]o{]p6po6M)$$X,fm^oQ$aL*P*_,@@q ittacNn*XJJ*&&jjdd x þQ$nXJ, CCUQJ*5B̠%xdddyzjd C ØmQ`JJLxxxU H77}m]McJL5-2( X;a( -[(ZZZZyZZZ,`w2 MLJ$-J(&,jii }66_[&ѯ}}mt]ok6a+\Mc$JO,O,JJL ub#>Vr6}'mmmttmoQp]^]6QQ].]M\c0)JymoQQnaX$,qCm]5nXJX_*XXL ,Oj U7m)cJ ,,q:iCtQ6MLixdd,&jq d  ]6c0i iCU t]X$v(-[25 JL$-LwZZ3ZZ&3L_XccJ,,E }t0L[&}}mo]666)5)6X X J Ly[_J_X*,otmmmm}o}]oQ+a ]`)6J$J ,uo]]]{6a0J[,jldixUp}aXJ*JJ MJ [,&ͪBdlU #t{MJ,z:d%xCC}]~̟ i iZ&&jii%UU}}QJdd%C ӯm6QJXLLLMJ JJLJ3P,33(&O5_L[_MM_ 6$M,4qd%Uo5Jc*JL}}mкmQppQ]]6p6op{nJ(JJ_LLM*XJ/f,u]}mo]mtppppa)p]vJZ}otVt))X("z i%UU}6vXJ*JLL&H4zi UtoJ(J~O@ xxCU#]̥ di*,&&j dixhmoտqd8xi%#M`XJ--JLJMJLMX*;Ln[[ZZ,~O,_--JJJcJMJ-2,פqd  r]XPLJZZ&u}mغtQoo6]]Q66))$aX60 cXJJ$$JJ(M_5&~#}}t^toa\k(&Qt]p)(JMJ$,@@ddCgQ$cJMJJ-n**v(Z&E@ii%%oa])Z,%% ])MJqd%xP*c&&jdd i%UKL &@%xdddiC m+vcMMJLJcXJ/cJ$MJ n* 3Z,(2L_*JJJ-_,Oj@  }Qn -[&m>mm}o]o]pt.QQ666aap]a))XJM$M*`J2w_'f+]]orp)NH}]Q]aa*00X,E@q x%CImQp0*Xo_MJL(,4qҥiCgt)M_Z~ d }6a(dx%x`\pJZjqd % "ta,dd }6)*- XN*JJLJJ**[L,3- MLJ)L*LX Z,@qxC}tvJL;3'umm]QQo]p#Qotkp66a]aa\]$)0 $6XLJJJuRmQp66a0*J*L&dxt]6JJJ **LJ[&Bd {v-Z,E@lddCgt60 qC xCVQ$[,&ͤdxU |{,d d%xiiU U}moo*WX JJ*J[*JJ0XXL_(LL (JXXL ,&jq%%i% UmmoM*(-ZZ,#m}mtot]o]K]pt.Q]6666{);a)_vc_L,pt.o6a5vJ,j:%xCot{wM$JJaJ 'qdi%]JX Z,8E:dixUt]L[~8q: d{[&&j:xCC hQ$,@8ddi xd %% }6)JMJJJX*cMJ$\#\at{an*_`LLL--JJL`$JJJ-,H d xU}#0J ;L,H}mmmt]otkQ.]QkVaa]]aV66_J\)nJJ]}tt]ptt]QQ]pkpo]0v$*/_J&mQrQa)$*_X,,zqd %%ׯ]aJ JcXMcJX!:FxD6aX,դq%%%UmQ,qi %Ct6XX&&jz:ddxx }oOdix iiE%U }taX$ J c_*5DU>mQQaMMJJJ*J;*PQoa$XJJXL&B%i%UU]a0JJ[OV}ttm]t}tpo]]QoopQ) *$,bmQ]^{0**,ix%}t6p) LJ[X_:xxxQa-JX[פ矟UmQf, d ixkM`J,&Oͤq ii% }OEx%i%xi %:C eӯm)v2JJJJJF%xCm}}t6QJXJ*J*X)mtt6oJvL,,j?%xUU7ta-HZ2u}ӯt}mt}}pQpp6{6Q$RttQp6aXJ_,%x}Ӿm]+\MM2JXwPddx%%IpvMJP,@d%xUV{'ddtpcZ,&jd % mX,8ii iiidddqq% Ӿoa (JnJ* L~idiUU "7}]\XLn_`MJx^t]5Ja,פEii impXLJJub}>}ot]]Q6a$c6;Opopao) 6 * &j:iCCUHr}}mm]{\M_JMXM ddixhaJJMJ`Z&@q CCoQz d %ttJ[[&H@ %xg}$,lqidd ddddiddd %x٘}L J[!i% d%CU Ӻv$[XLnXJPxUUo]pJ JJ*&j@iddxUmQ)XLu~]m]QpQ6))~uttoa)JncX[ud% CUUh!r}mtoaJJJJ\J*xidix xn)5cc ,@x%%xCV],q %CUtQ)c/Z&@jF%% m6Jפddddddddddi i þa$*J_J*nngqBd %%CUU }|a0JX(MXw( CUm]6  &z@ ddoaJ$X (,ZufoN~;&Qap{;JJJ,z xi%xCUU ӛ>m]aa6vX$0\d x*$J,@ddi %Uբa0O,x%htt &j@iddU #t'jx %iiddq% d "m{*JO:d x%CU }o6XMcJJ ]0_*&jdddU ›aJX [~Zp6{6VJJ,,jzIx %C% r7}}m]QQQtav06% dd%xii!v**J ,qdii"mQ,BiiUUoQc *J,&jjddU>J'jzixx%d!:ii%x% vJ*-(,idd %%CU}m]-JXJJ$c&%C}mVXM&"4@: C }Q *JL[Rp){o5JXJ,li %%xC U ċmmttQ]xd%C% % cM6LM,d iC]],jqiUmtJL$*$J&j i%U,?zdd%i i dU "}>]a6cJJ/,zqddddd i 澾o60XLJ,٤qdC 7V)0M,i%% 6XJ3Z&H)\a$ޱJJ,"4? d% d %x%%UUU "hiCCdd i!J0*cMJ Z,~砥iixov,'瓥i%C}aaJML,jiUUt)Lqdd  !z8: i % }to0J,qi d d%dd%Uho{v**c,H@di%U m60J$nZ&@@dUo\**L,Z$)0vJ-_$*2OEzqdxiiddd idixxU%Cxixxxi%ddd !J#2O%iiU]t6,,j@dixa,&j%ddUmo褿dd d iljj@:%i%C TLn5*M, %ii di%ix >o{_JJLJXM,z:%%C 7o]a_,jdiCC mtJ/Z&vX)aaX(*JZ,z%did i %%C%x%iCdxd d`MJXO@d U}QJ,@8i% "}aM _J*,l4 dix]J' i ddi%%jxx "Q6*XM&" %dd di% di }6XJMJ$,,zqxC ]\J ,Fi UtJ2J$ZZvv0J&ddd q?:x%%ixx%dd dddd JX)J_,Ed xoJJ,j8E %U pLn*5,4ddCU])~iiiiddixU$,1j@ ddi%xC }o$LJ[&~j:ddd% d dd xxC }kocXJJ ,&Eidx o6;,,@@i dieQ6Xv 2ZO&$v JpJ[,褪B d  :qE ix%d Cxdiid i%LLJ5XM2,qixC!m0Z,"j iUp\$J**,@ iixoc,qdid CCmM&jqdiix]6,Oddd% dd xC% ^>p)cJX$-L,l:dd  }}t6~&@jdiitQaX**/Z,,Ov)`MJ0X(JJ,椨ddCi?jEqid%iii ix IX*$ac0JXJ-,q:imt,%x "oaM,,j:%dxCI]$,zd dd%CQ&ji%%U m]$XL&@q% ddxdd dd%CU m{JJJM*(,@lBi x% ӘtQ,j %x mm6$LXL,ZX5XXX*[,,@dFFxiH44z:%idx C"*JJJJL&ļqxiU}tpZ&E48%x }}{0;J,qi:xn0,zE i d C>a,@ i oML[,8qx%did i%ix Q{JJ-MXL(,H@zid%xU UtJw,Hqd% ma0JJX3/&Jv0cc`$JXXJ,,@E%:xv,&&u1jj4Fiiddxi%[XJJ -,qx iCU.ma_,,hjz:id iCr>Q$c [,qd xt{0,'jx %F }m&&j@diiiC K]a,jq%xd idiˠ%xC}mvJM-y&iddxӯo ,,@@dd%H}]aLXZ&X$XMvX$ JJJJJ[Z&j pQ~,&&'g4:dix!{JJ*JJ* *JLn[Z,xdi mM,ujqdd%m[*L&z8i %C^t-Oqi8qiU>o,gjz ixCUU}QaZ,'diddi ddF x >Q)XX/,Ez̥di =6X,l4dCmo]JLN,3&\6a;{JnL*JL3&jqB ddUU]tX,&&Oj4iUmpJJaJ;M&j%U}}0L&j  oMJ*c2(8qxiCp&zqd iiU }&&j8 ddixC mm,~@dddd %ii 窝i  }p,qid%x%UmMxxCUmt60 RAyZZ))) X6c(_JXM ,dddUUmpM,&&הz % }t cMM X,EqBxxxCU}]vL&j@di }]Jvcq%i%C p6&d d C Ø7\&&4dxCU >m+c, di%% `qqd %U rӘQ{M,zddCi%iC "ddxxx tQ$JX[,ZZ3&_)va)0XJJLp])X&@qddCmQJ* 2,&&j砥%%CU6*PJJ[,%UUmmM,&4jqi%%C.}t0XJ(JJ&qq  UVQX-,Hj:%iddddiU >>p&&jqdi }]JL,&E@d d xidi jii xxx }ov,Hzd  d xCCU$,E@id ֯.*&p);)vcotkp6J&d tkMJ,&jxUoX*JLJMX*JJXդi%C mk$,&ji%%Uhc*[(q:iiCrQ ,jz ddi% >]&&4qd dd%C טm{J&jd %%ixJZzjF%dC 6aJ__,֤didd%xtF%%im]XX3,Z3&n)aMc Dgm}mi ixCmm\ J&&j@F l%mt)*JJ _TJ , xxmQ3,'ͤq dU ӯaL,q: d%x{6&E@diiddi%C 7})&Ojdi U}(,פ Cx ,j@B i x 7m])J~@qd d%dx>m8F %Utm6J_3OWv0\LXL(xU"m}{%iC{JJ&&ji %CUo)$JJM[)`X,,qd"])L,&EͤddxC ]]p*,:d xCC}ac,,i ddddddC U}}]M,&HjBi %C >an' %dC]Ǫ:i% ix }ok_*(JJ,dxdi i%/z d r}] [&Na$\0a HiiU UU6 ̟ xxUmm0cLJX(~&1jlBxxi%#]aX*$J*JJ[Z,lqd%%CQJ[&'ͤ ix "onL d%xC)JZ,8:iiddd i%maJL&& d  }oaJ,&8d  dCop,&Sq̥%x U6JJ,,E Cd Cx ii% vJi%i%Upa$ L (Z,&p)$\)JX` iCxCIdidd%x}{v*JMJXJy,4EippcM-M_JM*&ͿBddxUo}pMO&jz%dCCU"t]a!d %i%6X(,Ojdi dd %C Kt&&j4qd dxU >m0,,8 d%CUemJ&&EǤiixC }mpJJJ,h %d %xx U ]*%xCUmt]c;M O)]a\n0Zd%ixxCidC d%]J-JXX[,OzҥiiiQ6JXX[,jd xxxr}\ (&ujF d% "]J d i%)u@qdddii %%U"}o0O&H4d  6,j%  x }]X&&Ǥi C"Q),"jd%d% CC*d:iidx#{MMLO3Ot5)${X)*L L!qiddiCidix xtp`L-JXJ{qq %acJJJJJ$XMJ**,'@:ix!m$J,&4idU 7mo!diii d%%\v*L&Ei %idd C ٢}kJM[,&4zi %"}L,jix%U})&&4% ӯpvXLOEqdx%Cx̟d%xULdF%% Um6MJ*ZyO,p){6\$0XJ`3zid %xx dddi%iU7m]0XnJ*XX:xxx)56_JJJXJMn,jdqU"m{&&zd%ix hġ diidi x%)&ej:i idd x m{J &&jqdddCUm}X,@xxiddixU7&Oz%xxii 6J Z,@d % d%x%%D U %Uo{XXM`JLZ,5]]pv0XJ@ ixdddd%ixC>mQ6JydCxo5vnJ*JMJ$J,, i}Q\v-_~'% d xCxCx ixI))$J)&Oqii %i %%xUU}o),&i%CUmo,8@qFdddi  ro[&,jEddix }t&Eid d88xxd xPiidx%dU pJ*XN,]Q6a6aMJjzd %id%dxľ}mpMvX[xddi%Cx`MMn X&jq%xC]t6XJJX,&j@dd ddi%xi: %6*M,Ozq i%%iid %i U}m6&Ojj%%%U"m6&i d%dixU"$&&HEdC% ">ma0(,q:Udͤxd x iU oJLJL/Z[to6{)R`JJ,ujqqixxidiid  }>m]k)ddd ixx/vcXJ M[(,դxxUrm6JJ&&jqi i%xid %ǟdJ0 X,,jq % dddiiC hmJ&j%ii%Cokc&j dddi%xx .m0&4 x r]0M(,H@ixxx _jdd%xi%xxUVmQM[,,2}tpp{r -,&HYjd dd % d %UU#}}tii id vvMJ`XJL_&Ojj%% mQ\[[,ej@ id iddd%d ;M*JJ,jdi%% ddd i % em]&'j%Cot'@di dd%%% pX(,&jiU}pJ('@dd %iP֤4xxd%UmQnX ,Z,r}]tt66p0J,&&14jjEqddiiddxx gCx% diiM)JJ*J*M0O%Umk]J&&ͤ:dd %%ddd ixM*Jn[~ujd x% iiC eӛk&xpkaJ&@qidd Uٯ}J,&Ǥq Cxx׾mJ,zddCxgjqiid %iddx }t)/Z3,b}mt]6MJJ*JLN,&'j4dxdd%iC UCxx d%id%i%JM J*Jn,88xx% m]{JJc&&Hjjj@qFddJMZ,d !F:x >0 i%CVQ,ljq ddxd C7t&&%x% }}p,z@1ddCCI6,ͤqd%d % dd  m6cMLL(,Zt}mo]kpXnJ6J,&j%id%C%%%C ddi IJJJXJJ*cJ &z%C 6L[&רjjzqd!*cLJL, di% 8i C 1}gqq i%xV]n,Hj dxd CU}}]XL,&'jB% d վoL&FddiCC6*jx%%d  d C m6a/ cL[,3mo]tp6cMc-,&'"j@:xiix i%CddiCC didLJXXLJJXMvJL5*La[,"d}60JJ_J,&,H׸8zz:[JXLaJRZ,hjd ii Lq@qddd%C }6Bddd%#av[,Hzz% iddCCmpJ&&ͤqi x mm,8FddxCxCm&Hj% d%xddU ׯQXL*PGmm}oQV6\v_J)/,&&gjj4jldd dix %d%dd!L JJJc000J$J3u d%C6pJ[*L ,,,O','vJJ$,Odddig@qdd %CUJdi dCa6,zzi ddx% 7oa,&j4:d%U}p ֤d x%%ͤq didi %UUU|aJJ-3֘}mm}otQ6$V{JMJ$Z,&'׷j窟iddi i xiJ2JJ0vv#{)XJJMJZ,,dFxm]JWJJJ(3X$J*&jqdddxxF 8ͤdd%x%x xi xaa Z,lBxx d CtX,&"@qx xC},qd%C%%Utc,&dd%% %UUmt60JJ$L33b}mQ]o{)0 XJJXL,,&&'4ǔj@q̟Bdd%I-LJnc;v)]XvcJ,dxttoML`*J*L[w_JJJLcOjddiM@48 %%% x%xIvcL&j didi%%U"t ,&jxiiCU}o[,q%C%%U"a&j% i%%xC gm6XJLyZ,}}m]tQQ666vMXJ&&uE4j4zzdJN*0 '6aa\0J0&ldd%Uta$J*c a $Jn $*JMJ*J,jddiZ,jj:i iii dd %%x))$,'qddi dxdi %}QJ&j%xiC mv,%xU mpJO&ͤiidddx%%ix mo6XJX23&Әmo}mQtQp6)M)M(,,,uHײ4@@q_J* L*Macn),mp6pMM0JJJJ,Hjqdd%CU}ovJJMXX JMcXL&Hjqd dixC{,ujqdddddddd x%_vL&qd% dx ddC>p,&@j C I}0,椪%%U tm$&&d iddxmop**LLJ2,RZ,A7}}to]tQoo6{)XvXJ_,,&&'=,Jnp&otQaQ0vXJM[&jddiCC!mQ-JJXMX$JM6M$J*JLnJJ J&@q iCmv,&Ej矠i dii ii%JXJ,jiidd d  }ӛa,,hjiCoa,@:x% iC #tp,&"Ǥqddx%dddx t5M L~R,&>m}tt}Q]Qp]66)vJ avL[,u,LJ$X05ptQ6{6JJ &qddixUUt6$M_v*p^Mv6$nLL`MJX3,jx%xxU]o$,j坠dd vJ /,":idd iddd mv&jqdCta,'@x ixUQJ[,&ǿddd CmQaM-_&b}mmm]]]op6a6anJv*([L_ JcLvVo]V#6vJ*J;,1jq dd%UmmXXJJ00XJ$ )5aa] ))6)LJ$0,Hj %C 5,&&4jjq:d J ,Ojq di dPd id ]8qd CC]n,qi%gmQ_&u4dx d% C}vJ-,,o>}mmo]mt]Q666]6]v)Xvt$*LJJ_$*_0_Xa0֢tm+o0JJ*,H%ixCU^t#J`an*$0akpWa\]])a`JJ*JL,&zj %=][&,&'j缼B9XX*(~,di%dd4ddU pqq %C\L,El i%CU]vJL,&ͤqiC%ix m{M0X Z3,'V}}mm]o]tpp66ato\MM`J0JJM-JJ,tm]{pX J$,&jq%x DQ)LMXnMMa{p$6kp+a\66_JJJ* &EjB%%%o}+J,,,&'Ϧ_J[,4ddi%Fqddi% >X: ix%oJ&llidd%U]aJ,ujj%xx CU }}6$ ֯>mmtmot]{6mompMt)66XvaXc_M*JJ$J J_X&]t]]a\;0JMJ,jdii % mpM XX5)]p]Q]Qpoo#]ppap)nJ&פ: x }52,,,,"dddi iU ga/  %%Q$,"4 xC Vt]&ͤ dd dUm6VcJJX2-Z3b#om}toQQ]666a66))6a0)J0v0Jc0$ cVm6VacL&ji }o6M$n-[ J**vaoQ6pm]]ttQoo)$6$JnL,&jqddd%mo6 L_[$J*X cJ(,,jqddddjzid CUh% d x\v,'j xC +m]XL&&4Ϳi dddippc*JcZ2,'}om}o^pQpo]6pp{a]t$vVv0)0J*7]#aJZ,դ iiCUC]6 )LJ,w5 Qt}ttmot}o]ton00,&zqddBxC o6$LX*Q -XX*JL,uq d j@qiC%%C%x% dxo0&,zq d%okX*&jjdd%i Uo}tJJc _O,2o}mttmtt]to}koQ6a.6a)6${cJXZu}mV])`JJX,,jz8d %x7Q)JnXXLu&'upt}}mmtpQ6a;$&j:qUoXMM0Z$XX JLL&ji xoh@qdx %% xx%/0MJ,8q% ixChm>XJOjd x%%xCU}6MLZ,,'7m}}}tttpoQo]V]Q{appa6{vNLJJJL,Vm]Q])M,&jjEq)` J J/,uru־m]6a) $MML,H:ddi }]6MJJ]J*$cJ,j:idi%C),jq:dxdi ixxv$v*,q %tQv_XZ&uǜ d ixx}{* LZ&'V}}mttkm]p]Q6n{M0JMV}tt660Jc-J &4qd c*3J-2X&'}|]6))J0&jdd ix]QcJvJ*JJ*J,,4q  պJ&Hjidddxi %,@:%dd%tot5J[&jd iC%]pX$&ub]}}m]tt]]p]]pvaa0Luu}omp+ac[,Hͤq:Ҟ!**cL o>>76{J-,Ejd diU})nJJW J5JJ LZ&zi CUUma&&j4qddd%dxy*v,48 d Ut{ &'jqdCdxUtm$*_*-ZZZ~fQo>}}motpp6)m]a)J}mmttvQJy,'lX L[>o]]o$MXXL&jdi U<L$LMMJ(J_c_XLL,,j%id%C }[ &&4jjqqddM2O@%xt$$,&j xd% m]JMJ[,Z'br]mmQp6 +mmttov\JW[,,2J ((uH}m]VaMwJX2,'4qi%x}t)J [J*-JJM,'j: dix H>6,&O44jqd$,,EC%Chk$J&uͤxdC }p)nMJJL(/&u'~H,}t]QpM5JJ-*Ln* (mmQV{v{J*;&EjqBBxxCUmt6JLLJJJJ*J~,@8 %Um,,,&'8ElJXL,qi%hoQvL *,&jz:dx%H}]{nX-2(Z2&>}oo}paJcXJ$_XcX-L- į]m])`M2L,jdd%U>Q;MJXJaM*JJZ&E4Bi}}QnJ(3&&&,L0XJ,@qdCt))*X 3,Hj xi m]acMXJ,3ZyummmtQ6{0a$$_JX nLNb}mt]]])cJ_$(,j@dd x}])0XM_cXLJX-JXn&j% CUtpMJ[JLc[,פdddCCt]pnJcJ,&j xii }p_*M&#}mm}+6\6vcDJMJL$Z[,m]6o5$0X ,&zqi  mta__JJJJXMJc5M [,' xC")XJX0**/,"jzdd%xPά2&'ͤqd% %}]$J ,}ot#{6pp6aM JJJ2&}]tooM00&EjB  #pMXJJ pnM JJ*JJXL,zjdd UtmJJJJJ0_ cL'4@dx`aJcL,&j知UU]aXJw,&m7m}oQoop6a){)v)$*JMJ>.o#]pJJ(,Hj% diUmQaJXJ])0JMcJJL,dddxUrmp;M* X_&j0**J&@ dd%xm6J_L&A}}mm7]]6+]66Q\p6)X0$ mop{{f c[,&iiixU mpJL*JJ__v$v6n);XJ)J,jqdddCCm)`*av-XX-(X ,׮@@l"/*;J*ncL(,&jqd d xVQ6M (Z#ӯ}}om]t]pQ]a#pp\)]XXJML&ot}]v_*`_ ZEj:ii%CU})-J0)&6{)aaJX,,jqdii x]$M0MJXJJ0 L&,u,,,-&qddd {)p-[,u鯘momto]]pp6{aaa\ JJvmmo|a_J*fL[&j d xC{aM* LXM0V]Q66vV)&jq%i%U#m{Jn*J)-X JJX LJXJ*M`J3O&sǤq:ix00JX_Z,,m}m7}tQtpav00L}mtQQap0L;&jj:dd%!\`M$c \y]pp65)nL J[,jq% i >Qv$ LJLJJL( JJ*n&jdxX$J`X0[w(ub.o}m6|6]Ma0,}m]o{a JM&zͤqd d (LL0Zm]oavMJ5,&j%x % t{JJJ*JMJvJ;J2)*nXX &jҞ*MyLL[,([u]}mºok)_o}ppa)MXLO,jqJ[(J &]t6pQQa)0O&z%diU a0J*MM-Mv;Xa)6MX6 _*LLJJ-,'JL_L [OuNu&دmmoa0JOE8-JMcLR*Jpmtpt]$;nJJZ&idi% pm-XX{)va6\6oavvXaJJM` 5MJnO[ MJn[L2~,mt]6aJJ,JMJPLb]pp{vJ X_J,&@q di ]p(J*J*J$aWon{o])]6a){0$nX)JMMcLXp*[( [cMP[&'>mom]Qa_J_,LLJ**_J;JZm]t]),J&4 xx}maXM-*MLav)6)]k+Q66p6o])a566$avva6X$ J`J[c(`m>}mt]p6))[L_ M`,#K]p6)5M,jzd i%x į]_*n*J J6M6+6]Qpp]tok#]6ap6a]{)6ap6{X)McXLJ_L[Ø}mQm]a0JJJnn-J$(LLL}t]p6JJ-,'jEddip)c; J)JLQ]{]t#]t]o]]]6o#QaQ{Q6{Q6to5$0avJ0M$  Z~}}}}o]66acJXJ0*_}om]])0n)J,&@zEd%U}t)0JJ0*Z{6}m]}}oQ]o{6oo6]poQp]Q+Q6{pa))0o$)XJv5LX  ,b}mm]tp]ap){o{X;0X*Lw3mmm]a;$c;,48% %CrovJJ-&m}mmtmQo]Qk]p]]t}Vt]]Qo{]{6pQa{a0pJ! &r}}>7m]}Vpa){)\vvMLX*Jc,'tpm{QJJL,'jqidixra -*L LbHt›}m]}t]]]ott]]ttkm]]]]oQ6o]{)00MM,Ęmmmt.Qp]p6a$a])p50 JX*Momtta;*JX&&jj:xiiJc vLJ`,f+t+]+Hbmmmto]QpVQQpav66X}m}]7oQ}p]p{QpMp$J!m}mtm\6XJ*,jqILJ*-L]m}mQot]#pQ5t aM)Jy~}}mo]t]]]]]Vvn6Jb}m]mao\) L*,"j@,J_ 2'btmmmЯ}m]p6oo)a0)$#mo›mmtQk+]{{vavJ$M&}}}]pa) ,,~AJ*LZ[uA]momot{{\$\ wuu"b#tommmt]pt)p)JJ&ѯt]QMc[[JLL [(&r]mQapoN[ub#+}]p)p[˜tooa] LX$[uRuuu~ffR~R7}7}mpp60MM$5JJMX)L['}oQ#{a0XJX*JJcMLwZ.}}tt66a)0)pXJ)0LL}>}}moKQt{aa))X)nM;_XJLLL&7}mmo]m+6ppa)6av_6cJ*J}t}mtmQ7o6pooaQaXvJL*&7}}ommt]6o]6{]{50X)'}}m}}}].]]]Q6]aMapvc*&'7}mmok6Q)Ј$J,uV#o}to>o]aQ0Lu'VfNblobby-1.0rc3/data/gf2x/font48.bmp0000644000175000017500000000337012042452401020141 0ustar danielknobedanielknobeBM6( ###klk()({~{LOLUVUĿ^a^*++Ֆ   blobby-1.0rc3/data/gf2x/font49.bmp0000644000175000017500000000337012042452401020142 0ustar danielknobedanielknobeBM6( sssWWW333}}}HHHppp===rrq000(((尰WWWDDDޚLMMqqr|322۪\^]EFFԌ7:8ltq-0/͝V^ZGKKƵɿ/50 ǼEUI8=;do2D7 |ʊMnV""&%θڮbo1N7foۯt·B|K)#*'ͤ{ٍM[-_4`kXi070Օcq.8^  Qv[|:D% 4 ~ԇip'-" ^eW[2;1B6ޑrz7w<blobby-1.0rc3/data/gf2x/blobZeichenWeiss.bmp0000644000175000017500000000146012042452401022254 0ustar danielknobedanielknobeBM0(  ..---------! ----------------#"! ------------'%#"! ----------'%$#"! ---------)&%##""! --------&#"!!! !  !-------&$"!  !"""""#------$"  !"###$------!  !"$%&------ "#&'------ !#&)------- !!#####(-------- "#$%&'()+,,-------- !"#$%'(*+,+*--------- !"#$%&'(())---------- !"#$$%&'((---------- !""##$&'((---------- "##$%&'))---------- !"#$&()----------- !#&------------ !#'------------- !-------------------------------------------blobby-1.0rc3/data/sounds/bums.wav0000644000175000017500000002341012042452401020446 0ustar danielknobedanielknobeRIFF'WAVEfmt Ddata&aa\aai{^d_aaa^]c_aicdksphal(H _ ' 5e/$25jI*EV. ,SR 7"M=,K0z *(aQK\ NT   =߇BP%\πP#ʔZpB!`$$ZvUx]&ꖧI;)bd=<[\ nntNv/ikN%R*-O ˒Z˿?b  3O2?Q=Qz`aLY[b4k82oeUJ)|!ĆAZn𐊏1/ PQP;X{YSUNOFH:<+-/ z8cل/ޞ +V+596.>>wBC8;+@8vl}eڸΥȢ٢v~:Se;Z;EOOgOKQ6>@;%'Dr@|Sm<-?7t+(tؼ:D**2 4/0(/*] $/z^^Ho4ivu sk?sWm{֖*[ʹڸxڳ>϶M1ԙgWgFAv22mȀ1a"𝤠~uK߰ ر.*z7ݜECCE4ؠ ȑdz\XD8̶M<Єao۔ۇ(!^s@qJ,<5;RaɺnG޻dZP * a|j2n߯Әњt>˾qα؉=Nm:=  xt'sGt7@Tl~u fy PX S  EAKR<<-}- T=#i$n!")%RXQ I TL *v.D: Ms cX9tk)( ^ eD b# P y2Y!"$%)),-,-'X)h!"@~w M2Wtf CeW %!X$C%&^'/)%**+0,N-()h !#R%t9L %,&,b-Y0F123J4h55765768845#12U./(W*#K$"l#&'-,,012}34=52L40/2021245?6s7789 ;6 813K2r34!55667E8L9_7812.)0-.i,-a+,(_)&'&')).,-O/[001_212M2313/(1./././01263353 5,Y.E%&W!e"!"7$/%'()*=,2-,D-'(m%&#%A!j"!"@!c"(  ?\hs|{V# Xs{l L +qi| YQ!J cz qnNp+x:ibMp97TKT&\V/Q#g{9#rJ!U*'M,59S-[&X'O{SLy#?CFDZb&x,_d)M ^hB5 n"5aXRCl=U  ? on'#*%8dO|*>I   ^7 I  ptIi  xz' 6     P mJ! a q b  j}<<cL-QzQ>%|d 6 ^ 4 r K   `   9  Y=PM^  rO=e*  Y R n O g $ m'/c*eVZhsSf/tV-"x|B{d\A Z e> 3`'i$m'<y^ DNPP&C>,U;s]['$zj76qwPK !~0U=aOx#LqPwmEKWYoUVsw5.?*8Vq%@BxVcQ jb6aPP@M6sQJ0[&@U q D63PMTlU _   y Q @t;  , :I w- ZOr`b5%KKy;V~O"&#)&'&(a%&#$ !)`sun\/B jZr *  \i  e6r<f~*EN; ' r [ 7 3~](4 + : F  C %   i3TDWN" 0 u } +/Y<,X#I2yXyK-u[b aefz ;u%FD2N7v"&9de9:&Tz)\J9^T)i%F. )bsvIF-s#7}XFim{Ec2UB Q0s>+}\ E\2,y02G S54 |:,b{OC"B_M;C%E#Y `qpT##s7}[zP=}wG!pj&vnVm~` zNj]W ii< ybE^n:mBwByDm6[[! zJ\oYbKn$vAM dX!~utmn#ibM{AdK , 3+ SAhR * D  q ]v f D #!  4 )&erMA`,l< yo=:hSe`lC%/~KG0JO}  J)F64 vmoF`~H#?[ew4w_kN cX<oxn@^"c\$4,<(e \\.{MWvZzH7'ym_:pN= j`Jq/G]5 #8x,psj zJW'lv2Rf]G2IWtG@{Gk s2=/{/ 7*o,\(#f.nT7,8R3Y!=K2}VD^jt'Sp/JYaE?1r[z|bXA>fbeI, Wz;W5O6I%{EPR/i3TXRRn>%( (#FWGhqz=0E"q>Uivf"/lOdUxyiVAZFpJ>h:o |l:# )~i  * C )   p O J @ & | m  u f t Q   V G  C , X 2  i W /  >!UR >GRM'!Y.xw2 I [ 5(wh v s  "   @ P  < 7)  ~ _  & +  T . t Z ( - | g  + ; ) jjbU & ,  6 * C 9  61}L0*$?A/a@KQ! ~TO5v2 e7YN  -;sU-^*X"d|<7C5-N!Gk@hI1zK1^!nWXr4 YI[#9w%G6}*C4~7e5h71~2zc$fCN(&58fFQ<-~[_EtK> : w`3wB6 [Q'h$R+= x$ C{J5 Gf/>xh4<O+fwl[-9o>x>*   .P mTz=2   gb l h  Z 4 O ) *  V  Y T p B = + z w - , [ 9 I N e ] t Z N >   .  @ 0  T K  N = ^ [ m Q 0 w j _ : ? f S  0  1 1 x p 9  z d J 1 0   C E i ^ M , ! [3tfUbGXNL`fI`QdR-9q^Sp=J;-'e"-y3m?,G=[[,eU4j/4(z-db@&X<I%^I{HH>mT#':xl(:8-w7S-~KlP\~c^,3%n84."=<;i\: ' `).C6$,ic?wZ F9&W/7L,K92R9!Sn?H>}1.wL @il6g^4MP[f+tBud|WUw:F6ZP@wI|t$j@.|DT3g>|O`tOjH2 (Z,IA62WA8"R*IP4S1H*PK87M? -U(!M'-<G >]WL%4U U%I4D<*>01U DH*[/K4UZ/S*PF <>F(7*K%blobby-1.0rc3/data/sounds/pfiff.wav0000644000175000017500000004713212042452401020601 0ustar danielknobedanielknobeRIFFRNWAVEfmt DDfact Ndata NМc//////HКc//////Lʘd2/////PÔe8/////WйhA////:]˯lL5//4IeȹpYH@@HXmvg]XY`iv|wtrtwz~~~wpiefjr}}l\PKMWfzz`I936D[xͷxV8///4RwÞuL/////Jv̢sD/////DuФp=/////@uЦo8/////>vЦm5/////>wХl4/////@xУl6/////DzРl9/////H{ɜm@/////P|nG////5W}ϴpP8//0D`ɼt\J@?GVjxi^XX^gt}wtrtvz}~~xpjfeip{n^RLLTdwdM;34AWtм}[DPcx}maZX\dp|xtrsux|~~{slgegmwtcUMKP]plT@44BM`upcZX[cmzztsstx|~|tmgegluweWMKP\mpWC63:LeƭkJ0///>_жf>////2Yпa4/////Uđ]//////SȑZ//////QɑX//////RȐW//////TŏW//////WX//////Zй\0////5`в`;////@eƨdG1//7NkƵkUF?@L\qsd\XZ`lxzusstw{~~}umhefktyhYOKNYju[E728H`~ɲpO4///:YмmD/////SŘi;/////M̙f4/////JКc//////IК`//////IЙ`//////K͘`//////Nȕ`0/////Sc7////0Xиf@////<_ʭjK4//4JgȸoXH@@IZnug]XY`jv|vsrtwz~~~woiefjr}|k[PKMWg{y_H937E\z̶wT7///5TxœtJ/////Lx̠pA/////GxТn;/////BxУl6/////@xУj3/////@zУi2/////B{Рi4/////F|Нi7/////K}Țj=/////QоmF////7YγpP7//1Faɼt\J@@GWjxh^XY^ht|wtrtwz}~~xpjfeip{m]QLLTdx}dL;35BXtϺ|Z;///1NrǣzP/////EpЧxI/////?pЪwC/////:oЬu=/////8pЬt;/////8pЬs://///:qЩr///>Xw|cOB>DPdz}m`ZX\dp}xtrsux|~~zslgegmxsbTMLQ^plS@44=Pk§dD////Dfа]7////:bжX/////1`кS//////]нO//////]оL//////]нL//////_лL//////aжN/////4dаR/////=hϩW4////Hm^B///;TqðgQD>CN`vpcZX[cn{ytsstx|~|tlgeglvvdWMKP\mpWB53;MgƬjI////@`еd=////4[м_2/////X[//////UŏX//////TƏU//////TƍT//////VÌT//////YоW//////]иZ/////7aа]9////CgŧdF0//8PlŴjTE?AL]srd[XZalxzusstx{~~}umhegltxgXOKOZjtZD738IbɰpM2///;ZлkC/////TÖg9/////Pɘd1/////M͘`//////LϘ^//////LΖ]//////M˕]//////PƓ_//////Tп`5////0Zжd?////=`ɬhJ4//5KhǸnWG?@JZotf\XZ`jw{vsrtwz~~~~wohefjs}|j[PKMWh|x^H827F]{̵tS6///7UzqH/////Mzɞo@/////HzРl8/////DzСj4/////C{Рh0/////C|Рg0/////D}Оg1/////H͜g5/////MƘh////Jm˧U0////@kЬ~N/////:jа}H/////4iг|D/////2iгzA/////2jвy@/////4kаxA/////8mЬxD/////=pЧxI/////FrȡxP/////OtлyX=///@ZxzaMB>DQdz|l`YX\ep}~xtrsuy}~~zrkgehnxraTLLR`rjQ?44=QlcC////EhЭ\5////;dдU/////4aиP//////`лL//////`мJ//////`лI//////aиJ/////0dдL/////7gЯP/////?jͨV4////Io]@///COawobZX\do|ytsstx|~|tlgegmwudVMKP]onUA53/////?tЧp9/////=tШn7/////wФm7/////BxСm://///Gz˝n@/////N{pH////4W}дrR8//0D`~ɼu]K@?GUixj^XX]gt}wtrtvz}~~xpjfehpzo_RLLTcveM<34@Wsм\=///0Lpɦ}T/////CmЪ|L//////////6mЭv?/////:pЩvB/////@qХvG/////GtƠwN////0PwйwW44>SmaA////GjάZ4////=fвT/////5dжN/////0cиJ//////bйG//////cиG//////dжG/////2gв~J/////8iЬ}N/////@m̦}T1////Kpо}\@///=Vt¯}dPC>DPcx~naZX\dp|ytssux|~~{tlgegmwtdVMKP]pmT@43BM_tpd[XZbmzztsstx|~|tmgegluwfXNKP[lqXD63:KdǮlK0///=]иg@////1Xc5/////Tœ_//////Qɓ\//////PʓY//////PɑX//////RƐX//////UŽZ//////Yл]2////4^г`/////P}пmG////6XϳpP8//1E`ɼt\J@?GVjxi^XX^ht}wtrtvz}~~xpjfeip{n^RLLTdw~dL;35BXtл}ZhаQ/////7fдL/////1eжH//////dзE//////eж}D/////0gг|E/////4iа|H/////:lЪ{L/////Coʤ{R0////Lrм|Z?///>Wv|dOB>DPdy}m`ZX\dp|xtrsux|~~{slgegmwtcUMKQ^plT@44BN`upcZX[cmzztsstx|~|tmgeglvweWMKP\mpWB53:LfƬjJ0///>`жe=////3Zн`4/////WÐ\//////TƐY//////SȐW//////TǏV//////TčV//////XX//////\йZ0////6`б_:////AfƨdG1//7OlƵkTE?@L]rrd\XZalxzusstw{~~}umhefktyhYOKOYjt[E728HaɱpN3///:ZмlD/////TĘh://///O˙d2/////Lϙb//////JЙ`//////JИ_//////L̖_//////Oǔ`0/////Tb6////0Yзe@////<`ɬjK4//4JgǸoXG?@JZntg\XY`jw|vsrtwz~~~wohefjr}|j[PKMWg|y_H837E]z̶vT7///6TysJ/////Mx˟p@/////GxСm://///DyУk4/////AzУi1/////A{Сh0/////D|Рh3/////G}Ϝh7/////L~Ǚj=/////RнlE////7ZͲoP7//2Gbɻs[J@@GWkwh]XY^ht|wtrtwz}~~xpjfeip|m]QLLUdx}cL:35CXuϹ|Y;///2OsƢzP/////GqЦxH/////@pЩvA/////tХp=/////Dv͠qD/////KxÜsJ////3TzзtT://0C]|ʾw_L@?FTh}zj_YX]gr}wtrtvy}~~yqjfehpzp`SLLTatgO<44@Tpо^?////Jm̨V0////@jЭP/////8hб~J/////4gд}E/////0gд|C/////0hг{A/////2iбzC/////7lЭyE/////DQdz}m`ZX\dp}xtrsuy|~~zslgehnxsbTLLQ_qkR?44=PldD////DgЯ]7////:dеW/////2`йQ//////_мM//////^нL//////_мJ//////`йK//////cеM/////5fаQ/////>jΩW4////Hm]A///CO`wobZX[dn{ytsstx|~|tlgegmvudWMKP\noVA53;MgŬiH////@`дd////=aȬhJ4//5LhǷmWG?@JZptf\XZ`jw{vsrtw{~~~~vnhefjs~{jZPKMXh|x]G827G^|̴tR5///7V{pH/////Ozɜm?/////I{Пk8/////E|Рh3/////D|Рg0/////D}Пf//////E~Мe0/////I̚f4/////Mŗh;/////TмjD////9[̰mN6//3HdȺqZI@@HXlwh]XY_ht|wtrtwz}~~wpifejq|}l]PKMVey|aJ:36DZwιzW:///3PtŠxN/////HtϤuF/////BsЧt@/////=sЩq://///;tЩp8/////;tШo7/////=uЦn8/////@wУn44>RmbB////FhϬ[4////CPbxnbZX\do|ytsstx|~~|tlgegmwtdVMKP]onT@43/////;pЩtA/////@sФtG/////HtƟuM////0QwйwVDPcx}maZX\dp|xtrsux|~~{slgegmwtdUMKP]pmT@44BM`upcZX[cmzztsstx|~|tmgegluwfWMKP[mpXC63:LeƭlJ0///=^зg?////2Yпa4/////UĒ]//////SȒZ//////QɑX//////QȐW//////TƏX//////VY//////Zк\0////5_в`;////@dƩeG2//7NkƵlUF?@K\qsd\XZ`lxzusstw{~~}umhefktzhYOKNYj~u\F728H`~ɲqP4///9X~мmD/////Rři;/////M̚f4/////JЛd//////HЛa//////HЙ`//////JΘ`//////MȖa0/////Rd7/////Xиg@////<_ʮjL4//4JfȸpXH@@IYnug]XY`jv|vsrtwz~~~woiefjr}|k\PKMWg{z`H937E\y̶wT7///5TxœtJ/////Lw̠qB/////FwУo;/////BxФl6/////@xФk4/////@yУj2/////AzРi4/////E|Оj8/////J}Țk=/////P~оmF////7XγpP7//1Faɼt\J@@GWjxh^XX^ht}wtrtwz}~~xpjfeip{m]QLLTdx}dL;35BXtк|Z/////8oЬt///>Xv|cOB>DPdz}m`ZX\dp}xtrsux|~~zslgegmxtcTMLQ^plS@44=Pk§dD////Dfа]7////9bжX/////0_лS//////]нP//////]пM//////]нL//////_лL//////aзO/////4dаS/////=hϩX4////Hl^B///;TqñgQD>BN`vpcZX[cn{ytsstx|~|tlgeglvvdWMKP\mpWB53:LgƬjI////?`еd=////4[м`3/////WÐ[//////TƐX//////TǏV//////TƎT//////VÌU//////XпW//////\иZ0////7`а^:////BgŧdF0//8OlŴjTE?AL]srd\XZalxzusstx{~~}umhegltxgXOKOZjtZD728IbɰpN3///:ZлkC/////TÖg9/////Pɘd1/////L͘`//////KИ_//////Kϗ^//////M̕^//////PƓ_//////Ta5////0Zжd?////=`ɬiJ4//4KgǸnWG?@JZotf\XZ`jw{vsrtwz~~~~wohefjs}|j[PKMWg|x^H827F]z̵uS6///7TzrI/////Mzɞo@/////HzРl9/////DzСj4/////B{Сh0/////B|Рg0/////D}Пg1/////H~Μg6/////LƘi////Jm˨U0////@kЬN/////:iа}H/////4hг|D/////2hг{A/////1iгz@/////4jаyA/////7lЬxD/////=oШxI/////EqȡxP/////OtлyX=///@YxzaNB>DQdz|l`ZX\ep}xtrsuy|~~zrkgehnxraTLLR_rjR?44=QlcC////EhЮ\6////;dдV/////3aиP//////`мM//////`мJ//////`мJ//////aйJ/////0dдL/////6gЯP/////?jͨV4////In]@///COawobZX[do{ytsstx|~|tlgegmwudWMKP\noUA53;MhīhH////@aдc;////5]л]0/////YY//////WČU//////WČT//////WċR//////XS//////[мT/////0_жX/////8dЯ\8////DhĦbD0//8PmĴiTD?AL^tqd[XZbmyzusstx|~~}tmhegltxgXNKOZlsYD739JdȰnL1///<\йiA////0V”e7/////QȖa0/////O̖^//////M̖\//////M̔\//////Pɓ\//////RĐ]//////Wн_4////3\еc=////>bȫgI3//6LhƷmWG?@J[ptf\XZ`kw{vsrtw{~~~~vnhefjs~{iZPKMXh}w]G827G_|˴tQ5///7W|pG/////P|Ȝm>/////J|Ϟj7/////G|Пg1/////D}Пf//////D~Оd//////GМd0/////J̙e4/////OĖg://///TлiC////9\̰mM6//3HdȹqZH@@HXlwh]XY_iu|wtrtwz}~~wpifejq|}l\PKMVez|aJ:blobby-1.0rc3/data/sounds/chat.wav0000644000175000017500000003351412042452401020425 0ustar danielknobedanielknobeRIFFD7WAVEfmt DXdata 7)--,-,*  )--,-,& %+-,--+'-,--+$  *,---'*,--*%*)%     "# %*-+($*,-+)  "*-,--)'-,--,) +,-,-," &,---,-(-,-,-$#--,-,-&-,-,-',,-,-* ",,--+%",,-,( '*($     ##  %+,,& %-,-,)"*--,-',--,-,##,-,-,**,--,-& ,-,,-+# (----,)+--,-,& "+-,--*$,,-,+##*-,,&  !'+(#        $! (--,%  )---+&&-,-,-% +,-,-* *,-,--(*---,,$%-,--,+  '-,--,)+,-,--" $+-,-,+%+--,,!%--,-# !(*)#      #" (-,+$  (,-,-'#+-,-,%(-,-,,! $+-,--(*-,--,' #,,-,-* +,-,-,'*--,-*"%+----(%+-,-+"%,,-+# !()(!        #" (,,*" *,,,,$%,+,,*" ++,+,* &,+,++' ),,+,*$&++,+,) '++,++% (,,+,)""),,+,%&+,+,*  &*,+(! !'('          # ()+( (*+*)!(*+*+)   !(++*+'%++*+** (++++*# %*+*++)&*))*)%))*)*(#*)*))!%**)*% &*)*("''$          '()&&)()& %)()((  )()))&%)())(! ()()))$'('('#'('('(  (('('%%('((& &('(&#%'('#  $#!       %'%"  '&'&$  %&%&%#%%&&%& &%&%&$ "%&&&%#$%&&%%! %%&%&$!%$%$%#!%$$%$  #%$%$!"%$%  ""          ### "$##  !"#"# #"#"#  ""#"#"###"##"#"#"  ""#!"  !"!"" ""!"  !!"" "!""                                                                                                                                                                                               blobby-1.0rc3/doc/ScriptAPI_de.txt0000644000175000017500000000524412042452365020327 0ustar danielknobedanielknobeDieses Dokument beschreibt die neue Lua-Schnittstelle, mit der Bots für Blobby Volley 2 gescriptet werden. Sie basiert auf der alten Schnittstelle von Blobby Volley 1, ersetzt aber hässliche Funktionen und stellt bessere Möglichkeit zur Programmierung von korrektem Aufschlagsverhalten bereit. Die wichtigste Neuerung ist aber, dass der Bot immer für die linke Seite programmiert wird. Wenn der Bot auf der rechten Seite spielt, dreht Blobby Volley 2 automatisch alle Werte um. Folgende Funktionen können aufgerufen werden: left() : Ein Tastendruck nach links right() : Ein Tastendruck nach rechts jump() : Ein Tastendruck der Sprungtaste moveto(zahl) : Läuft zur angegebenen X-Koordinate. Die Bewegung ist leicht gepuffert, sonst würde der Blobby immer um die Zielstelle "vibrieren", da er sie niemals exakt erreichen kann. touches() : Gibt die Anzahl der Ballberührungen zurück. launched() : Gibt zurück ob der Blobby gerade springt und in der Luft ist. estimate() : Gibt die X-Koordinate der Stelle zurück, an der der Ball vorraussichtlich auftreffen wird. Kollisionen am Netz oder am Rand werden aber nicht miteinkalkuliert! estimx(zahl) : Gibt die X-Koordinate der Stelle zurück, in der sich der Ball nach der angegebenen Zahl von Physikschritten befinden wird. Auch hier ohne Kollisionsberechnung. estimy(zahl) : Gibt die Y-Koordinate der Stelle zurück, in der sich der Ball nach der angegebenen Zahl von Physikschritten befinden wird. Auch hier ohne Kollisionsberechnung. ballx() : Gibt die X-Koordinate der Ballposition zurück. bally() : Gibt die Y-Koordinate der Ballposition zurück. bspeedx() : Gibt die X-Koordinate der Ballgeschwindigkeit zurück. bspeedy() : Gibt die Y-Koordinate der Ballgeschwindigkeit zurück. posx() : Gibt die X-Koordinate der Spielerposition zurück. posy() : Gibt die Y-Koordinate der Spielerposition zurück. oppx() : Gibt die X-Koordinate der Gegnerposition zurück. oppy() : Gibt die Y-Koordinate der Gegnerposition zurück. debug(zahl) : Gibt die angegebene Zahl auf der Konsole aus. Nützlich zur Fehlersuche Zusätzlich kann der Funktionsumfang der Lua-Mathematikbibliothek genutzt werden. Deren Funktionen sind hier dokumentiert: http://www.lua.org/manual/5.1/manual.html#5.6 Ein Skript muss selber die folgenden Funktionen enthalten: function OnServe(ballready) : Wird aufgerufen nachdem der Ball abgepfiffen wurde und der gesteuerte Spieler nun aufschlägt. ballready gibt an, ob der Ball schon zurückgesetzt wurde. function OnOpponentServe() : Wird aufgerufen nachdem der Ball abgepfiffen und nun der Gegner aufschlägt. function OnGame() : Wird während des regulären Spiels aufgerufen blobby-1.0rc3/doc/Tutorial_de.txt0000644000175000017500000000574512042452365020342 0ustar danielknobedanielknobeIn diesem Tutorial werden die Grundlagen von Lua und der Botprogrammierung von Blobby Volley 2 erläutert. Für weiterführende Informationen gibt es die Dokumentation der Script-API. Für Leute, die noch nie programmiert haben, empfehle ich folgendes Tutorial, um die wichtigsten Grundlagen zu erlernen: http://robertico.ro.funpic.de/index.php Vor der Programmierung ist zu beachten, dass Blobby-Skripte in 'data/scripts' abgelegt werden müssen und die Dateiendung '.lua' tragen müssen, um vom Spiel erkannt zu werden Für ein gültiges Blobby-Script müsst ihr 3 Funktionen festlegen: function OnServe(parameter) -- Wird aufgerufen wenn der Ball abgepfiffen wurde und man selber angeben -- soll. Der Parameter gibt an, ob der Ball schon in der Schwebe plaziert ist end function OnOpponentServe() -- Wird aufgerufen wenn der Gegner angeben soll end function OnGame() -- Wird ansonsten während des gesamten Spieles aufgerufen -- Mit -- werden übrigens Kommentare markiert, solltet ihr es noch nicht bemerkt haben ;D end Bevor ihr jetzt loslegt, noch etwas zum Koordinatensystem: Das Spielfeld ist 800 Einheiten breit und 600 Einheiten hoch, ganz an der alten Blobby-Auflösung orientiert. X wächst dabei nach rechts und Y nach oben. Damit wäre also 0,0 unten links und 800,600 oben rechts. Falls ihr euch wundert dass es keine Möglichkeit gibt die eigene Seite zu bestimmen, das ist Absicht. Programmiert einfach als ob der Bot immer links stehen würde, das Programm dreht gegebenenfalls alle Koordinaten um. Ich werde jetzt einfach mal ein Beispiel zeigen, wie ein simpler Bot aufgebaut sein kann: function OnOpponentServe() moveto(130) -- Wenn der Gegner spielt, in Ausgangsposition gehen end function onServer(ballready) moveto(ballx() - 40) -- Etwas links vom Ball hinstellen if posx() < ballx() - 37 and posx() > ballx() - 43 then -- Dieser zugegeben etwas komplizierte Ausdruck bewirkt, dass -- man sich erstmal unterhalb des Balles befinden muss. Leider muss -- das so aufwendig gemacht werden, weil moveto() niemals eine Stelle -- ganz exakt erreicht. if ballready then jump() -- Natürlich nur springen wenn der Ball schon bereitsteht end end end function OnGame() if ballx() < 400 then -- Wenn sich der Ball links von der Mitte, -- also auf unserer Seite befindet moveto(ballx() - 20) -- Etwas links vom Ball ausrichten if ballx() < posx() + 50 then jump() -- Wenn der Ball kurz vor oder hinter dem Blobby ist, springen end end end Ich hoffe, dieses Tutorial hat einen Eindruck entwickelt, wie man einen Bot programmiert. Für weitere Informationen gibt es wie gesagt die Script-API-Doku. Um fortgeschrittene Bots zu programmieren, solltet ihr auch nicht immer blind dem Ball hinterherrennen, sondern mit den estim*-Funktionen Vorhersagen machen. Ansonsten kann ich euch nur als Tip mitgeben, euren Bot immer wieder zu beobachten und gegen jede gefundene Schwäche einen Schutzmechanismus zu entwickeln. blobby-1.0rc3/doc/ScriptAPI.txt0000644000175000017500000000373212042452365017657 0ustar danielknobedanielknobeThis document tries to design a new API for the Lua scripting interface for bots. This API is based on the old API, but replaces ugly functions and provides a more sophisticated way to implement serving behaviour. The main difference of the new API is, that the bot is always programmed for the left side. If the controlled blob is actually on the other side, the API converts all values automatically. These are the available function: left() : moves one step left right() : moves one step right jump() : presses jump key moveto(number) : moves to the given position with a small buffer touches() : Number of own ball touches since last ball exchange. launched() : Tells whether the player is jumping at the moment. estimate(): Estimated impact point of the ball, without collision estimx(number) : Estimates ball x component for n timesteps estimy(number) : Estimates ball y component for n timesteps ballx() : x component of the ball position bally() : y component of the ball position bspeedx() : x component of the ball velocity bspeedy() : y component of the ball velocity posx() : x component of own position posy() : y component of own position oppx() : x component of opponent position oppy() : y component of opponent position debug(number) : Prints a number to stderr for debugging purposes getScore() : own score getOppScore() : opponent's score getScoreToWin() : score needed to win the match getGameTime(): time the game is running (for bots simulating exhaustion) All functions of the lua math library are also available. You can find their documentation at: http://www.lua.org/manual/5.1/manual.html#5.6 Scripts MUST provide these entry point: function OnServe(ballready) : Called when the ball went down and the controlled blob has to serve it next. The boolean parameter is true when the ball is already placed. function OnOpponentServe() : Called after balldown when the opponent has to serve the next ball function OnGame() : Called during the normal game blobby-1.0rc3/src/Blood.cpp0000644000175000017500000000655112042452374017107 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "Blood.h" /* includes */ #include #include "RenderManager.h" #include "IUserConfigReader.h" /* implementation */ BloodManager* BloodManager::mSingleton = NULL; // Blood class // ------------ Blood::Blood(const Vector2& position, const Vector2& direction, int player): mPos(position), mDir(direction), mPlayer(player), mLastFrame(SDL_GetTicks()) { } void Blood::step() { const float GRAVITY = 3; /// \todo this is the only place where we do step-rate independent calculations. /// is this intended behaviour??? int diff = SDL_GetTicks() - mLastFrame; RenderManager::getSingleton().drawParticle(mPos, mPlayer); const int SPEED = 45; //this calculation is NOT based on physical rules mDir.y += GRAVITY / SPEED * diff; mPos.x += mDir.x / SPEED * diff; mPos.y += mDir.y / SPEED * diff; mLastFrame = SDL_GetTicks(); } // BloodManager class // ------------------- BloodManager::BloodManager() { mEnabled = IUserConfigReader::createUserConfigReader("config.xml")->getBool("blood"); } void BloodManager::step() { // don't do any processing if there are no particles if ( !mEnabled || mParticles.empty() ) return; // start drawing RenderManager::getSingleton().startDrawParticles(); // iterate over all particles std::list::iterator it = mParticles.begin(); while (it != mParticles.end()) { std::list::iterator it2 = it; ++it; it2->step(); // delete particles below lower screen border if (it2->getPosition().y > 600) mParticles.erase(it2); } // finish drawing RenderManager::getSingleton().endDrawParticles(); } void BloodManager::spillBlood(Vector2 pos, float intensity, int player) { const double EL_X_AXIS = 30; const double EL_Y_AXIS = 50; for (int c = 0; c <= int(intensity*50); c++) { /// \todo maybe we can find a better algorithm, but for now, /// we just discard particles outside the ellipses /// so it doesn't look that much like a square. int x = random(int(-EL_X_AXIS * intensity), int(EL_X_AXIS * intensity)); int y = random(int(-EL_Y_AXIS * intensity), 3); if( ( y * y / (EL_Y_AXIS * EL_Y_AXIS) + x * x / (EL_X_AXIS * EL_X_AXIS) ) > intensity * intensity) continue; mParticles.push_front( Blood(pos, Vector2(x, y), player) ); } } int BloodManager::random(int min, int max) { /// \todo is this really a good way of creating these numbers? return (int)((double(rand()) / RAND_MAX) * (max - min)) + min; } blobby-1.0rc3/src/GenericIO.cpp0000644000175000017500000002440212042452374017647 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #include "GenericIO.h" #include #include #include #include "raknet/BitStream.h" #include "FileWrite.h" #include "FileRead.h" #include "InputSource.h" // ------------------------------------------------------------------------------------------------- // File Output Class // ------------------------------------------------------------------------------------------------- class FileOut : public GenericOut { public: FileOut(boost::shared_ptr file) : mFile(file) { } private: virtual void byte(const unsigned char& data) { mFile->writeByte(data); } virtual void boolean(const bool& data) { mFile->writeByte(data); } virtual void uint32(const unsigned int& data) { mFile->writeUInt32(data); } virtual void number(const float& data) { mFile->writeFloat(data); } virtual void string(const std::string& string) { uint32(string.size()); mFile->write(string.data(), string.size()); } virtual unsigned int tell() const { return mFile->tell(); } virtual void seek(unsigned int pos) const { mFile->seek(pos); } virtual void array(const char* data, unsigned int length) { mFile->write(data, length); } boost::shared_ptr mFile; }; // ------------------------------------------------------------------------------------------------- // File Input Class // ------------------------------------------------------------------------------------------------- class FileIn : public GenericIn { public: FileIn(boost::shared_ptr file) : mFile(file) { } private: virtual void byte(unsigned char& data) { data = mFile->readByte(); } virtual void boolean(bool& data) { data = mFile->readByte(); } virtual void uint32(unsigned int& data) { data = mFile->readUInt32(); } virtual void number(float& data) { data = mFile->readFloat(); } virtual void string(std::string& string) { /// \todo this might be bad performance wise unsigned int ts; uint32(ts); string.resize(ts); for(int i = 0; i < ts; ++i) { unsigned char nc; byte(nc); string[i] = nc; } } virtual unsigned int tell() const { return mFile->tell(); } virtual void seek(unsigned int pos) const { mFile->seek(pos); } virtual void array(char* data, unsigned int length) { mFile->readRawBytes(data, length); } boost::shared_ptr mFile; }; // ------------------------------------------------------------------------------------------------- // Bitstream Output Class // ------------------------------------------------------------------------------------------------- class NetworkOut : public GenericOut { public: NetworkOut(boost::shared_ptr stream) : mStream(stream) { } private: virtual void byte(const unsigned char& data) { mStream->Write(data); } virtual void boolean(const bool& data) { mStream->Write(data); } virtual void uint32(const unsigned int& data) { mStream->Write(data); } virtual void number(const float& data) { mStream->Write(data); } virtual void string(const std::string& string) { uint32(string.size()); mStream->Write(string.c_str(), string.size()); } virtual unsigned int tell() const { return mStream->GetNumberOfBitsUsed(); } virtual void seek(unsigned int pos) const { mStream->SetWriteOffset(pos); } virtual void array(const char* data, unsigned int length) { mStream->Write(data, length); } boost::shared_ptr mStream; }; // ------------------------------------------------------------------------------------------------- // Bistream Input Class // ------------------------------------------------------------------------------------------------- class NetworkIn : public GenericIn { public: NetworkIn(boost::shared_ptr stream) : mStream(stream) { } private: virtual void byte(unsigned char& data) { mStream->Read(data); } virtual void boolean(bool& data) { mStream->Read(data); } virtual void uint32( unsigned int& data) { mStream->Read(data); } virtual void number( float& data) { mStream->Read(data); } virtual void string( std::string& string) { /// \todo this might be bad performance wise unsigned int ts; uint32(ts); string.resize(ts); for(unsigned int i = 0; i < ts; ++i) { unsigned char nc; byte(nc); string[i] = nc; } } virtual unsigned int tell() const { return mStream->GetReadOffset(); } virtual void seek(unsigned int pos) const { mStream->ResetReadPointer(); mStream->IgnoreBits(pos); } virtual void array( char* data, unsigned int length) { mStream->Read(data, length); } boost::shared_ptr mStream; }; // ------------------------------------------------------------------------------------------------- // File Output Class // ------------------------------------------------------------------------------------------------- class StreamOut : public GenericOut { public: StreamOut(std::ostream& stream) : mStream(stream) { } private: virtual void byte(const unsigned char& data) { mStream << data << "\n"; } virtual void boolean(const bool& data) { mStream << data << "\n"; } virtual void uint32(const unsigned int& data) { mStream << data << "\n"; } virtual void number(const float& data) { mStream << data << "\n"; } virtual void string(const std::string& string) { mStream << string << "\n"; } /// currently not supported by StreamOut virtual unsigned int tell() const { return -1; } virtual void seek(unsigned int pos) const { } virtual void array(const char* data, unsigned int length) { std::string stringed(data, length); mStream << stringed << "\n"; } std::ostream& mStream; }; // ------------------------------------------------------------------------------------------------- // Factory Functions // ------------------------------------------------------------------------------------------------- boost::shared_ptr< GenericOut > createGenericWriter(boost::shared_ptr file) { return boost::make_shared< FileOut > (file); } boost::shared_ptr< GenericOut > createGenericWriter(boost::shared_ptr stream) { return boost::make_shared< NetworkOut > (stream); } boost::shared_ptr< GenericOut > createGenericWriter(std::ostream& stream) { return boost::shared_ptr< StreamOut > ( new StreamOut(stream) ); } boost::shared_ptr< GenericIn > createGenericReader(boost::shared_ptr file) { return boost::make_shared< FileIn > (file); } boost::shared_ptr< GenericIn > createGenericReader(boost::shared_ptr stream) { return boost::make_shared< NetworkIn > (stream); } // ------------------------------------------------------------------------------------------------- // Default generic implementations // ------------------------------------------------------------------------------------------------- /* Several instantiations of serializer functions are made so these can be used without further user actions. These are the instantiations for the standard types * unsigned char * bool * unsigned int * float * string Furthermore, some types used in BlobbyVolley get their serialisation algorithms here * Color * PlayerInput * PlayerSide */ // these templates help to avoid boilderplate code #define GENERATE_STD_SERIALIZER_OUT(type) \ template<> \ void predifined_serializer::serialize(GenericOut& io, const type& value) #define GENERATE_STD_SERIALIZER_IN(type) \ template<> \ void predifined_serializer::serialize(GenericIn& io, type& value) #define GENERATE_STD_SERIALIZER(type, func) \ GENERATE_STD_SERIALIZER_OUT(type) { io.func(value); }; \ GENERATE_STD_SERIALIZER_IN(type) { io.func(value); }; namespace detail { // std implementations GENERATE_STD_SERIALIZER(unsigned char, byte); GENERATE_STD_SERIALIZER(unsigned int, uint32); GENERATE_STD_SERIALIZER(bool, boolean); GENERATE_STD_SERIALIZER(float, number); GENERATE_STD_SERIALIZER(std::string, string); // Blobby types GENERATE_STD_SERIALIZER_OUT(Color) { io.uint32(value.toInt()); } GENERATE_STD_SERIALIZER_IN(Color) { unsigned int target; io.uint32(target); value = Color(target); } GENERATE_STD_SERIALIZER_OUT(PlayerInput) { io.uint32(value.getAll()); } GENERATE_STD_SERIALIZER_IN(PlayerInput) { unsigned int target; io.uint32(target); value.set(target); } GENERATE_STD_SERIALIZER_OUT(PlayerSide) { io.uint32(value); } GENERATE_STD_SERIALIZER_IN(PlayerSide) { unsigned int target; io.uint32(target); value = (PlayerSide)target; } } blobby-1.0rc3/src/Global.h0000644000175000017500000000605312042452374016712 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include // I hope the GP2X is the only combination of these systems #if defined(__linux__) && defined(__arm__) #define GP2X GP2X #endif /*! \def DEBUG \brief Enable debugging support \details when this marko is present, Blobby generates some additional debugging code usefull for tracking down bugs. */ const int BLOBBY_PORT = 1234; const int BLOBBY_VERSION_MAJOR = 0; const int BLOBBY_VERSION_MINOR = 101; const char AppTitle[] = "Blobby Volley 2 Version 1.0 RC3"; const float ROUND_START_SOUND_VOLUME = 0.2; const float BALL_HIT_PLAYER_SOUND_VOLUME = 0.4; // max. 1 ms additional latency, but much improved performance const int RAKNET_THREAD_SLEEP_TIME = 1; enum PlayerSide { NO_PLAYER = -1, LEFT_PLAYER = 0, RIGHT_PLAYER = 1, //LEFT_PLAYER_2 = 2, //RIGHT_PLAYER_2 = 3, MAX_PLAYERS // This is always one more than the highest player enum // and can be used to declare arrays }; enum InputDeviceName { KEYBOARD = 1, MOUSE = 2, JOYSTICK = 3 }; /*! \class Color \brief represents RGB Colours \details This class represents colors as RGB with one byte for each channel. */ struct Color { Color(int red, int green, int blue) : r(red) , g(green) , b(blue) {} /// \sa toInt() Color(unsigned int col) : r(col&0xff) , g((col>>8)&0xff) , b((col>>16)&0xff) { } Color() {} union { struct { Uint8 r; Uint8 g; Uint8 b; }; Uint8 val[3]; }; bool operator == (Color rval) const { return !memcmp(val, rval.val, 3); } bool operator != (Color rval) const { return !(*this == rval); } unsigned int toInt() const { int i = 0; i |= r; i |= g << 8; i |= b << 16; return i; } }; struct ExtensionUnsupportedException : public std::exception { std::string extension; ExtensionUnsupportedException(std::string name) : extension(name) {} ~ExtensionUnsupportedException() throw() {} }; struct ScriptException : public std::exception { std::string luaerror; ~ScriptException() throw() {} }; /// we need to define this constant to make it compile with strict c++98 mode #undef M_PI const double M_PI = 3.141592653589793238462643383279; blobby-1.0rc3/src/PhysicState.h0000644000175000017500000000344612042452374017755 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Global.h" #include "Vector.h" #include "InputSource.h" #include "GenericIOFwd.h" namespace RakNet { class BitStream; } struct PhysicState { Vector2 blobPosition[MAX_PLAYERS]; Vector2 blobVelocity[MAX_PLAYERS]; Vector2 ballPosition; Vector2 ballVelocity; float ballAngularVelocity; bool isGameRunning; bool isBallValid; PlayerInput playerInput[MAX_PLAYERS]; /// \todo remove code duplication! /// this function is needed for compressing stream bool blobbyHitGround(PlayerSide player) const; void writeToStream(RakNet::BitStream* stream) const; void readFromStream(RakNet::BitStream* stream); // generic reading/writing void serialize(GenericOut* io) const; void deserialize(GenericIn* io); // equality comparision bool operator==(const PhysicState& other) const; void swapSides(); }; blobby-1.0rc3/src/lua_api_doc.dox0000644000175000017500000004127712042452374020323 0ustar danielknobedanielknobe/* This file contains the documentation of the blobby volley lua bot api in a form readable by doxygen for generating a clearer documentation. As doxygen is not capable of parsing lua source code, the function declarations are in C++ syntax, providing parameter and return types, too (in contrast to lua functions which would be unable to define type inforamtion) */ // ---------------------------------------------------------- // | documentation of the api function set | // ---------------------------------------------------------- /*! \defgroup lua_api API Documentation Here you find the documentation of the functions offered by Blobby Volley's lua interface for scripting a bot. For each function you get to know what it does, which parameters are expected and which values are returned. This documentation is subdivided into several function groups: \li Functions for input simulation: \ref command_func "Input" \li Functions for getting data: \ref ball_func "Ball Information", \ref blobby_func "Blobby Information", \ref game_status_func "Game Status" \li Estimation/Calculation functions: \ref estim_func "Estimation/Calculation" All functions of the lua math library are also available. You can find their documentation at: http://www.lua.org/manual/5.1/manual.html#5.6 \n Besides these functions that are provided by Blobby Volley, each script can define the functions listed under \ref script_func "Script Functions". Note that some of them are required to get your bot working. In addition to these functions, the Blobby Volley 2 Lua API provides a lot of preset constants to simplify bot development and make adaption to new circumstances (e.g. different fieldsize, gravity) easier if that gets implemented. \attention Remember that Lua is \b not able to declare a variables as const. Thus, it IS possible to change the value of these constants. @{ */ // ---------------------------------------------------------- // other // ---------------------------------------------------------- /*! \brief debugging \details prints \p num to the debugging output (stderr) \ingroup lua_api */ void debug(float num); // ---------------------------------------------------------- // input performing functions // ---------------------------------------------------------- /*! @{ \anchor command_func \name Input Functions */ //! \brief step to the left void left(); //! \brief step to the right void right(); /*! \brief simulate jump key \details Simulates pressing the jump key, so a first call causes the blobby to jump whereas successive calls (in the next time steps) make the jump higher */ void jump(); /*! \brief Move to position \details moves to a given position, using a small buffer to avoid whobbling around the target \param target destination */ void moveto(int target); //! @} // ---------------------------------------------------------- // ball information querying // ---------------------------------------------------------- /*! @{ \anchor ball_func \name Ball Information This set of functions allows the script to query information about \n ball position, velocity and the number of touches done by the bot. */ /*! \brief get number of touches \details returns the number of touches the player had made since the opponend had touched the ball last. */ int touches(); //! \brief x component of ball position float ballx(); //! \brief y component of ball position float bally(); //! \brief x component of ball velocity float bspeedx(); //! \brief y component of ball velocity float bspeedy(); //! @} // ---------------------------------------------------------- // blob information querying // ---------------------------------------------------------- /*! @{ \anchor blobby_func \name Blobby Information */ //! \brief is blobby launched //! \return returns true if blobby doesn't stand on the ground bool launched(); /*! \brief x component of blobby position \details returns the x component of the middle of the blobby \sa oppx(), posy() */ float posx(); /*! \brief y component of blobby position \details returns the y component of the middle of the blobby, i.e. the blobby's top minus half of \ref CONST_BLOBBY_HEIGHT. \sa oppy(), posx() */ float posy(); /*! \brief x component of enemy position \details returns the x component of the middle of the enemy \sa posx(), oppy() */ float oppx(); /*! \brief y component of enemy position \details returns the y component of the middle of the enemy, i.e. the enemy's top minus half of \ref CONST_BLOBBY_HEIGHT. \sa posy(), oppy() */ float oppy(); //! @} // ---------------------------------------------------------- // game status // ---------------------------------------------------------- /*! @{ \anchor game_status_func \name Game Status Information */ /*! \brief get own score \sa getOppScore() \sa getScoreToWin() */ int getScore(); /*! \brief get opponent's score \sa getScore() \sa getScoreToWin() */ int getOppScore(); /*! \brief score required for winning \sa getScore() \sa getOppScore() */ int getScoreToWin(); //! \brief game time passed so far //! \return game time in seconds int getGameTime(); //! @} // ---------------------------------------------------------- // estimations // ---------------------------------------------------------- /*! @{ \anchor estim_func \name Calculation/Estimation Functions */ /*! \brief estimates ball impact on ground \details Calculates the position where the ball is going to hit the ground. Ignores bounces with walls and the net. \deprecated This funcion is relatively useless as both its purpose (calculating ground impace, where it is already to late to catch the ball) and its calculation itself (ignoring bounces) are not good. As a consequence, it is hardly used in the current bots so it might be removed in one of the next releases. \return x component of the estimated position \sa predictImpact */ float estimate(); /*! \brief estimates x position after certain time \details calculates the estimated x position of the ball after \p time steps, ignoring bounces with walls and net \param time Number of steps to anticipate \deprecated use predictx() for more accurate estimations \sa estimx() */ float estimx(int time); /*! \brief estimates y position after certain time \details calculates the estimated y position of the ball after \p time steps, ignoring bounces with walls and net \warning May be a little inaccurate for long timespans as it uses explicit formulars instead of step-by-step simulation \param time Number of steps to anticipate \deprecated use predicty() for more accurate estimations \sa estimy() */ float estimy(int time); /*! \brief estimates ball impact on blobby head \details Calculates the position where the ball will be on height with the blobby head. Takes bounces with the walls into account. \return x component of the calculated position \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall before it reaches its destination. */ float predictImpact(); /*! \brief estimates time ball need to hit net top \details Reckons the time the ball needs to hit the net's center the next time. Collisions with blobbys, walls or the ned rod are not calculated. \attention extensive use may need a lot of cpu power */ float nettime(); // ---------------------------------------------------------- // spec in development // ---------------------------------------------------------- /*! \brief predicts ball position \details Calculates the position the ball will have after \p time steps, taking into account bounces with walls and a simplified model for the net (i.e., the more complicated collision with the net top is diregarded). \warning As this uses explicit formulas instead of simulating step by step, the results may slightly vary from the real values. \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall during the considered timespan. \sa predicty(int time); */ float predictx(int time); /*! \brief predicts ball position \details Calculates the position the ball will have after \p time steps, taking into account bounces with walls and a simplified model for the net (i.e., the more complicated collision with the net top is diregarded). \warning As this uses explicit formulas instead of simulating step by step, the results may slightly vary from the real values. \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall during the considered timespan. \sa predictx(int time); */ float predicty(int time); /*! \brief time ball needs to position \details Calculates the duration until the ball reaches a certain position the next time. Bounces with the wall and net rod are calculated correctly, net top is disregarded. \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall before reaching its destination. \sa timetoy(float target) */ float timetox(float target); /*! \brief time ball needs to position \details Calculates the duration until the ball reaches a certain position the next time. Bounces with the wall and net rod are calculated correctly, net top is disregarded. \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall before reaching its destination. \sa timetoy(float target) */ float timetoy(float target); /*! \brief x when ball reaches certain y \details caluclates the x coordinate the ball has when it reaches \p target. Equivalent to predictx(timetoy(target)) \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall before reaching its destination. \sa yatx */ float xaty(float target); /*! \brief y when ball reaches certain x \details caluclates the y coordinate the ball has when it reaches \p target. Equivalent to predicty(timetox(target)) \sideeffect Sets \ref FLAG_BOUNCE to true if ball hits a wall before reaching its destination. \sa xaty */ float yatx(float target); /*! \brief time the blobby needs to reach \p target */ float blobtimetox(float target); /*! \brief time the blobby needs to reach \p target. \details calculates the time the blobby needs to reach a certain height. It is assumed that jump() is called incessantly. */ float blobtimetoy(float target); /*! \brief time till next event \details calculates when the next event will occur. in this context, event means bouncing against the wall, the net or hitting the ground. \sideeffect Sets \ref FLAG_BOUNCE to true if next event is hitting wall or net. */ float nextevent(); //! @} // ---------------------------------------------------------- // functions to be implemented by script // ---------------------------------------------------------- /*! @{ \anchor script_func \name Script Functions These functions are to be implemented by the scripter. \n They create the bot behaviour and allow response to certain events. \n Some of them are required for a working bot and marked with \b required. */ /*! \brief function for serving \details Called when the ball went down and the controlled blob has to serve it next. \param ballready true when ball is placed \attention \b required */ void OnServe(bool ballready); /*! \brief Oppenent is serving \details Called after balldown when the opponent has to serve the next ball */ void OnOpponentServe(); /*! \brief Mainloop function \details Called each step during the normal game \attention \b required */ void OnGame(); /*! \brief ball bounce event \details Called when ball trajectory changes (i.e. ball hits border/net/blobby) */ void OnBounce(); //! @} // ---------------------------------------------------------- // | constants | // ---------------------------------------------------------- //! width of the playing field const float CONST_FIELD_WIDTH = 800; //! y coordinate of the gound plane const float CONST_GROUND_HEIGHT = 100; //! strenght of the gravity that affects the ball const float CONST_BALL_GRAVITY = -0.28; //! radius of ball const float CONST_BALL_RADIUS = 31.5; //! start velocity of jumping blobby const float CONST_BLOBBY_JUMP = 15.1; //! radius of the lower sphere of the blobby const float CONST_BLOBBY_BODY_RADIUS = 33; //! radius of the upper sphere of the blobby const float CONST_BLOBBY_HEAD_RADIUS = 25; //! height of the blobby const float CONST_BLOBBY_HEIGHT = 89; //! gravity that affects the blobby if jump() is not called. //! otherwise, half of this value is used const float CONST_BLOBBY_GRAVITY = -0.88; //! y coordinate of the top of the net const float CONST_NET_HEIGHT = 316; //! radius of the net / half the breadth const float CONST_NET_RADIUS = 7; // ---------------------------------------------------------- // | variables | // ---------------------------------------------------------- /*! \brief wall bounce calculated? \details Is set by some functions to indicate whether a collision with the wall had to be taken into account, i.e. the ball hits a wall during the processed timespan. */ extern bool FLAG_BOUNCE; //! @} // ---------------------------------------------------------- // | tutorial | // ---------------------------------------------------------- /*! \mainpage \page tutorial In diesem Tutorial werden die Grundlagen von Lua und der Botprogrammierung von Blobby Volley 2 erlutert. Fr weiterfhrende Informationen gibt es die Dokumentation der Script-API. \n Fr Leute, die noch nie programmiert haben, empfehle ich folgendes Tutorial, um die wichtigsten Grundlagen zu erlernen: http://robertico.ro.funpic.de/index.php \n Vor der Programmierung ist zu beachten, dass Blobby-Skripte in 'data/scripts' abgelegt werden mssen und die Dateiendung '.lua' tragen mssen, um vom Spiel erkannt zu werden \n Fr ein gltiges Blobby-Script msst ihr 3 Funktionen festlegen: \n \code function OnServe(parameter) -- Wird aufgerufen wenn der Ball abgepfiffen wurde und man selber angeben -- soll. Der Parameter gibt an, ob der Ball schon in der Schwebe plaziert ist end function OnOpponentServe() -- Wird aufgerufen wenn der Gegner angeben soll end function OnGame() -- Wird ansonsten whrend des gesamten Spieles aufgerufen -- Mit -- werden brigens Kommentare markiert, solltet ihr es noch nicht bemerkt haben ;D end \endcode Bevor ihr jetzt loslegt, noch etwas zum Koordinatensystem: Das Spielfeld ist 800 Einheiten breit und 600 Einheiten hoch, ganz an der alten Blobby-Auflsung orientiert. X wchst dabei nach rechts und Y nach oben. Damit wre also 0,0 unten links und 800,600 oben rechts. Falls ihr euch wundert dass es keine Mglichkeit gibt die eigene Seite zu bestimmen, das ist Absicht. Programmiert einfach als ob der Bot immer links stehen wrde, das Programm dreht gegebenenfalls alle Koordinaten um. Ich werde jetzt einfach mal ein Beispiel zeigen, wie ein simpler Bot aufgebaut sein kann: \code function OnOpponentServe() moveto(130) -- Wenn der Gegner spielt, in Ausgangsposition gehen end function OnServe(ballready) moveto(ballx() - 40) -- Etwas links vom Ball hinstellen if posx() < ballx() - 37 and posx() > ballx() - 43 then -- Dieser zugegeben etwas komplizierte Ausdruck bewirkt, dass -- man sich erstmal unterhalb des Balles befinden muss. Leider muss -- das so aufwendig gemacht werden, weil moveto() niemals eine Stelle -- ganz exakt erreicht. if ballready then jump() -- Natrlich nur springen wenn der Ball schon bereitsteht end end end function OnGame() if ballx() < 400 then -- Wenn sich der Ball links von der Mitte, -- also auf unserer Seite befindet moveto(ballx() - 20) -- Etwas links vom Ball ausrichten if ballx() < posx() + 50 then jump() -- Wenn der Ball kurz vor oder hinter dem Blobby ist, springen end end end \endcode Ich hoffe, dieses Tutorial hat einen Eindruck entwickelt, wie man einen Bot programmiert. Fr weitere Informationen gibt es wie gesagt die Script-API-Doku. Um fortgeschrittene Bots zu programmieren, solltet ihr auch nicht immer blind dem Ball hinterherrennen, sondern mit den estim*-Funktionen Vorhersagen machen. Ansonsten kann ich euch nur als Tip mitgeben, euren Bot immer wieder zu beobachten und gegen jede gefundene Schwche einen Schutzmechanismus zu entwickeln. */ blobby-1.0rc3/src/FileRead.cpp0000644000175000017500000001324612042452374017522 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "FileRead.h" /* includes */ #include #include #include #include #include #include "tinyxml/tinyxml.h" extern "C" { #include "lua/lua.h" #include "lua/lauxlib.h" #include "lua/lualib.h" } #include "Global.h" /* implementation */ FileRead::FileRead() { } FileRead::FileRead(const std::string& filename) : File(filename, File::OPEN_READ) { } FileRead::~FileRead() { // no more actions than what ~File already does } void FileRead::open(const std::string& filename) { File::open(filename, File::OPEN_READ); } uint32_t FileRead::readRawBytes( char* target, std::size_t num_of_bytes ) { check_file_open(); PHYSFS_sint64 num_read = PHYSFS_read(reinterpret_cast (mHandle), target, 1, num_of_bytes); // -1 indicates that reading was not possible if( num_read == -1) { throw( PhysfsFileException(mFileName) ); } if( num_read != num_of_bytes ) { throw ( EOFException(mFileName) ); } return num_read; } boost::shared_array FileRead::readRawBytes( std::size_t num_of_bytes ) { // creates the buffer boost::shared_array buffer ( new char[num_of_bytes] ); readRawBytes( buffer.get(), num_of_bytes ); return buffer; } char FileRead::readByte() { check_file_open(); char ret; readRawBytes(reinterpret_cast(&ret), sizeof(ret)); return ret; } uint32_t FileRead::readUInt32() { check_file_open(); if ( length() - tell() < 4) { throw( EOFException(mFileName) ); } PHYSFS_uint32 ret; if(!PHYSFS_readULE32( reinterpret_cast(mHandle), &ret)) { throw( PhysfsFileException(mFileName) ); } return ret; } float FileRead::readFloat() { check_file_open(); float ret; readRawBytes(reinterpret_cast(&ret), sizeof(ret)); return ret; } std::string FileRead::readString() { char buffer[32]; // thats our read buffer std::string read = ""; // thats what we read so far size_t len = length(); while(true) // check that we can read as much as want { int maxread = std::min(sizeof(buffer), len - tell()); readRawBytes( buffer, maxread ); // read into buffer for(int i = 0; i < maxread; ++i) { if(buffer[i] == 0) { seek( tell() - maxread + i + 1); return read; } else { read += buffer[i]; // this might not be the most efficient way... } } // when we reached the end of file if(maxread < 32) break; } throw(EOFException(mFileName)); } uint32_t FileRead::calcChecksum(uint32_t start) { uint32_t oldpos = tell(); seek(start); // buffered reading char buffer[128]; size_t len = length(); boost::crc_32_type crc; while(true) { int maxread = std::min(sizeof(buffer), len - tell()); readRawBytes( buffer, maxread ); // read into buffer for(int i = 0; i < maxread; ++i) { crc.process_bytes(buffer, maxread); } if(maxread < 32) break; } // return read pointer back to old position seek(oldpos); return crc(); } // reading lua script struct ReaderInfo { FileRead file; char buffer[2048]; }; static const char* chunkReader(lua_State* state, void* data, size_t *size) { ReaderInfo* info = (ReaderInfo*) data; int bytesRead = 2048; if(info->file.length() - info->file.tell() < 2048) { bytesRead = info->file.length() - info->file.tell(); } info->file.readRawBytes(info->buffer, bytesRead); // if this doesn't throw, bytesRead is the actual number of bytes read /// \todo we must do sth about this code, its just plains awful. /// File interface has to be improved to support such buffered reading. *size = bytesRead; if (bytesRead == 0) { return 0; } else { return info->buffer; } } int FileRead::readLuaScript(std::string filename, lua_State* mState) { if( !boost::ends_with(filename, ".lua") ) { filename += ".lua"; } ReaderInfo info; info.file.open(filename); return lua_load(mState, chunkReader, &info, filename.c_str()); } boost::shared_ptr FileRead::readXMLDocument(const std::string& filename) { // create and load file FileRead file(filename); // thats quite ugly int fileLength = file.length(); boost::scoped_array fileBuffer(new char[fileLength + 1]); file.readRawBytes( fileBuffer.get(), fileLength ); // null-terminate fileBuffer[fileLength] = 0; // parse file boost::shared_ptr xml = boost::shared_ptr (new TiXmlDocument()); xml->Parse(fileBuffer.get()); /// \todo do error handling here? return xml; } blobby-1.0rc3/src/RenderManagerSDL.cpp0000644000175000017500000004704312042452374021126 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "RenderManagerSDL.h" /* includes */ #include "FileExceptions.h" /* implementation */ RenderManagerSDL::DynamicColoredSurface RenderManagerSDL::colorSurface(SDL_Surface *surface, Color color) { SDL_Surface *newSurface = SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA | SDL_SRCCOLORKEY, surface->w, surface->h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000); SDL_BlitSurface(surface, 0, newSurface, 0); SDL_SetAlpha(newSurface, SDL_SRCALPHA, surface->format->alpha); SDL_SetColorKey(newSurface, SDL_SRCCOLORKEY, SDL_MapRGB(newSurface->format, 0, 0, 0)); SDL_LockSurface(newSurface); for (int p = 0; p < newSurface->w * newSurface->h; ++p) { SDL_Color* pixel = &(((SDL_Color*)newSurface->pixels)[p]); int rr = (int(pixel->r) * int(color.r)) >> 8; int rg = (int(pixel->g) * int(color.g)) >> 8; int rb = (int(pixel->b) * int(color.b)) >> 8; int fak = int(pixel->r) * 5 - 4 * 256 - 138; bool colorkey = !(pixel->r | pixel->g | pixel->b); if (colorkey) { pixel->r = 0; pixel->g = 0; pixel->b = 0; continue; } if (fak > 0) { rr += fak; rg += fak; rb += fak; } rr = rr < 255 ? rr : 255; rg = rg < 255 ? rg : 255; rb = rb < 255 ? rb : 255; // This is clamped to 1 because dark colors would be // colorkeyed otherwise pixel->r = rr > 0 ? rr : 1; pixel->g = rg > 0 ? rg : 1; pixel->b = rb > 0 ? rb : 1; } SDL_UnlockSurface(newSurface); SDL_SetColorKey(newSurface, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(newSurface->format, 0, 0, 0)); SDL_Surface *convSurface = SDL_DisplayFormatAlpha(newSurface); SDL_FreeSurface(newSurface); return DynamicColoredSurface(convSurface, color); } RenderManagerSDL::RenderManagerSDL() : RenderManager() { mBallRotation = 0.0; mLeftBlobAnimationState = 0.0; mRightBlobAnimationState = 0.0; } RenderManager* RenderManager::createRenderManagerSDL() { return new RenderManagerSDL(); } void RenderManagerSDL::init(int xResolution, int yResolution, bool fullscreen) { mLeftPlayerNameTexture = 0; mRightPlayerNameTexture = 0; Uint32 screenFlags = SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF; if (fullscreen) screenFlags |= SDL_FULLSCREEN; SDL_WM_SetCaption(AppTitle, ""); SDL_WM_SetIcon(SDL_LoadBMP("data/Icon.bmp"), NULL); mScreen = SDL_SetVideoMode(xResolution, yResolution, 0, screenFlags); SDL_ShowCursor(0); mOverlaySurface = SDL_CreateRGBSurface(SDL_HWSURFACE | SDL_SRCALPHA, mScreen->w, mScreen->h, mScreen->format->BitsPerPixel, mScreen->format->Rmask, mScreen->format->Gmask, mScreen->format->Bmask, mScreen->format->Amask); SDL_Rect screenRect = {0, 0, (short)xResolution, (short)yResolution}; SDL_FillRect(mOverlaySurface, &screenRect, SDL_MapRGB(mScreen->format, 0, 0, 0)); SDL_Surface* tempBackground = loadSurface("backgrounds/strand2.bmp"); mBackground = SDL_DisplayFormat(tempBackground); BufferedImage* bgImage = new BufferedImage; bgImage->w = mBackground->w; bgImage->h = mBackground->h; bgImage->sdlImage = mBackground; mImageMap["background"] = bgImage; SDL_FreeSurface(tempBackground); for (int i = 1; i <= 16; ++i) { char filename[64]; sprintf(filename, "gfx/ball%02d.bmp", i); SDL_Surface* ballImage = loadSurface(filename); SDL_SetColorKey(ballImage, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(ballImage->format, 0, 0, 0)); SDL_Surface *convertedBallImage = SDL_DisplayFormatAlpha(ballImage); SDL_FreeSurface(ballImage); mBall.push_back(convertedBallImage); } SDL_Surface *tempBallShadow = loadSurface("gfx/schball.bmp"); SDL_SetColorKey(tempBallShadow, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(tempBallShadow->format, 0, 0, 0)); SDL_SetAlpha(tempBallShadow, SDL_SRCALPHA, 127); mBallShadow = SDL_DisplayFormatAlpha(tempBallShadow); SDL_FreeSurface(tempBallShadow); for (int i = 1; i <= 5; ++i) { char filename[64]; sprintf(filename, "gfx/blobbym%d.bmp", i); SDL_Surface* blobImage = loadSurface(filename); mStandardBlob.push_back(blobImage); mLeftBlob.push_back(colorSurface(blobImage, Color(255, 0, 0))); mRightBlob.push_back(colorSurface(blobImage, Color(0, 255, 0))); sprintf(filename, "gfx/sch1%d.bmp", i); SDL_Surface* blobShadow = loadSurface(filename); SDL_SetColorKey(blobShadow, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(blobShadow->format, 0, 0, 0)); SDL_SetAlpha(blobShadow, SDL_SRCALPHA, 127); mStandardBlobShadow.push_back(blobShadow); mLeftBlobShadow.push_back( colorSurface(blobShadow, Color(255, 0, 0))); mRightBlobShadow.push_back( colorSurface(blobShadow, Color(0, 255, 0))); } for (int i = 0; i <= 53; ++i) { char filename[64], filename2[64]; sprintf(filename, "gfx/font%02d.bmp", i); sprintf(filename2, "gfx/font_small/font%02d.bmp", i); SDL_Surface *tempFont = loadSurface(filename); SDL_Surface *tempFont2 = loadSurface(filename2); SDL_SetColorKey(tempFont, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(tempFont->format, 0, 0, 0)); SDL_SetColorKey(tempFont2, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(tempFont2->format, 0, 0, 0)); SDL_Surface *newFont = SDL_DisplayFormatAlpha(tempFont); SDL_Surface *newFont2 = SDL_DisplayFormatAlpha(tempFont2); SDL_FreeSurface(tempFont); SDL_FreeSurface(tempFont2); mFont.push_back(newFont); mHighlightFont.push_back(highlightSurface(newFont, 60)); mSmallFont.push_back(newFont2); mHighlightSmallFont.push_back(highlightSurface(newFont2, 60)); } mScroll = loadSurface("gfx/scrollbar.bmp"); mStandardBlobBlood = loadSurface("gfx/blood.bmp"); mLeftBlobBlood = loadSurface("gfx/blood.bmp"); mRightBlobBlood = loadSurface("gfx/blood.bmp"); } void RenderManagerSDL::deinit() { SDL_FreeSurface(mOverlaySurface); SDL_FreeSurface(mBackground); SDL_FreeSurface(mBallShadow); SDL_FreeSurface(mScroll); SDL_FreeSurface(mLeftBlobBlood); SDL_FreeSurface(mRightBlobBlood); SDL_FreeSurface(mLeftPlayerNameTexture); SDL_FreeSurface(mRightPlayerNameTexture); for (unsigned int i = 0; i < mBall.size(); ++i) SDL_FreeSurface(mBall[i]); for (unsigned int i = 0; i < mStandardBlob.size(); ++i) { SDL_FreeSurface(mStandardBlob[i]); SDL_FreeSurface(mStandardBlobShadow[i]); SDL_FreeSurface(mLeftBlob[i].mSDLsf); SDL_FreeSurface(mLeftBlobShadow[i].mSDLsf); SDL_FreeSurface(mRightBlob[i].mSDLsf); SDL_FreeSurface(mRightBlobShadow[i].mSDLsf); } for (unsigned int i = 0; i < mFont.size(); ++i) { SDL_FreeSurface(mFont[i]); SDL_FreeSurface(mHighlightFont[i]); SDL_FreeSurface(mSmallFont[i]); SDL_FreeSurface(mHighlightSmallFont[i]); } } void RenderManagerSDL::draw() { if (!mDrawGame) return; if (mNeedRedraw) { SDL_BlitSurface(mBackground, 0, mScreen, 0); mNeedRedraw = false; } int animationState; SDL_Rect position; // Ball marker Uint8 markerColor = SDL_GetTicks() % 1000 >= 500 ? 255 : 0; position.y = 5; position.x = lround(mBallPosition.x - 2.5); position.w = 5; position.h = 5; SDL_FillRect(mScreen, &position, SDL_MapRGB(mScreen->format, markerColor, markerColor, markerColor)); // Mouse marker position.y = 590; position.x = lround(mMouseMarkerPosition - 2.5); position.w = 5; position.h = 5; SDL_FillRect(mScreen, &position, SDL_MapRGB(mScreen->format, markerColor, markerColor, markerColor)); if(mShowShadow) { // Ball Shadow position = ballShadowRect(ballShadowPosition(mBallPosition)); SDL_BlitSurface(mBallShadow, 0, mScreen, &position); // Left blob shadow position = blobShadowRect(blobShadowPosition(mLeftBlobPosition)); animationState = int(mLeftBlobAnimationState) % 5; SDL_BlitSurface(mLeftBlobShadow[animationState].mSDLsf, 0, mScreen, &position); // Right blob shadow position = blobShadowRect(blobShadowPosition(mRightBlobPosition)); animationState = int(mRightBlobAnimationState) % 5; SDL_BlitSurface(mRightBlobShadow[animationState].mSDLsf, 0, mScreen, &position); } // Restore the rod position.x = 400 - 7; position.y = 300; SDL_Rect rodPosition; rodPosition.x = 400 - 7; rodPosition.y = 300; rodPosition.w = 14; rodPosition.h = 300; SDL_BlitSurface(mBackground, &rodPosition, mScreen, &position); // restore the background for clock position.x = 400 - mTime.length()*12; position.y = 24; rodPosition.x = 400 - mTime.length()*12; rodPosition.y = 24; rodPosition.w = mTime.length()*24; rodPosition.h = 24; SDL_BlitSurface(mBackground, &rodPosition, mScreen, &position); // Drawing the Ball position = ballRect(mBallPosition); animationState = int(mBallRotation / M_PI / 2 * 16) % 16; SDL_BlitSurface(mBall[animationState], 0, mScreen, &position); // update blob colors colorizeBlobs(LEFT_PLAYER); colorizeBlobs(RIGHT_PLAYER); // Drawing left blob position = blobRect(mLeftBlobPosition); animationState = int(mLeftBlobAnimationState) % 5; SDL_BlitSurface(mLeftBlob[animationState].mSDLsf, 0, mScreen, &position); // Drawing right blob position = blobRect(mRightBlobPosition); animationState = int(mRightBlobAnimationState) % 5; SDL_BlitSurface(mRightBlob[animationState].mSDLsf, 0, mScreen, &position); // Drawing the score char textBuffer[8]; snprintf(textBuffer, sizeof(textBuffer), mLeftPlayerWarning ? "%02d!" : "%02d", mLeftPlayerScore); drawText(textBuffer, Vector2(24, 24), false); snprintf(textBuffer, sizeof(textBuffer), mRightPlayerWarning ? "%02d!" : "%02d", mRightPlayerScore); drawText(textBuffer, Vector2(800 - 96, 24), false); // Drawing the names //drawText(mLeftPlayerName, Vector2(12, 550), false); SDL_Rect rect; rect.x = 12; rect.y = 550; SDL_BlitSurface(mLeftPlayerNameTexture, 0, mScreen, &rect); drawText(mRightPlayerName, Vector2(788-(24*mRightPlayerName.length()), 550), false); // Drawing the Clock drawText(mTime, Vector2(400 - mTime.length()*12, 24), false); } bool RenderManagerSDL::setBackground(const std::string& filename) { try { SDL_Surface *tempBackground = loadSurface(filename); SDL_FreeSurface(mBackground); delete mImageMap["background"]; BufferedImage* newImage = new BufferedImage; newImage->w = tempBackground->w; newImage->h = tempBackground->h; newImage->sdlImage = SDL_DisplayFormat(tempBackground); SDL_FreeSurface(tempBackground); mBackground = newImage->sdlImage; mImageMap["background"] = newImage; } catch (FileLoadException) { return false; } return true; } void RenderManagerSDL::setBlobColor(int player, Color color) { if (color != mBlobColor[player]) { mBlobColor[player] = color; } else { return; } SDL_Surface** handledBlobBlood = 0; if (player == LEFT_PLAYER) { handledBlobBlood = &mLeftBlobBlood; } if (player == RIGHT_PLAYER) { handledBlobBlood = &mRightBlobBlood; } SDL_FreeSurface(*handledBlobBlood); *handledBlobBlood = colorSurface(mStandardBlobBlood, color).mSDLsf; } void RenderManagerSDL::colorizeBlobs(int player) { std::vector *handledBlob = 0; std::vector *handledBlobShadow = 0; int frame; if (player == LEFT_PLAYER) { handledBlob = &mLeftBlob; handledBlobShadow = &mLeftBlobShadow; frame = mLeftBlobAnimationState; } if (player == RIGHT_PLAYER) { handledBlob = &mRightBlob; handledBlobShadow = &mRightBlobShadow; frame = mRightBlobAnimationState; } if( (*handledBlob)[frame].mColor != mBlobColor[player]) { SDL_FreeSurface((*handledBlob)[frame].mSDLsf); (*handledBlob)[frame] = colorSurface(mStandardBlob[frame], mBlobColor[player]); SDL_FreeSurface((*handledBlobShadow)[frame].mSDLsf); (*handledBlobShadow)[frame] = colorSurface(mStandardBlobShadow[frame], mBlobColor[player]); } } void RenderManagerSDL::showShadow(bool shadow) { mShowShadow = shadow; } void RenderManagerSDL::setBall(const Vector2& position, float rotation) { SDL_Rect restore = ballRect(mBallPosition); SDL_BlitSurface(mBackground, &restore, mScreen, &restore); restore = ballShadowRect(ballShadowPosition(mBallPosition)); SDL_BlitSurface(mBackground, &restore, mScreen, &restore); restore.x = lround(mBallPosition.x - 2.5); restore.y = 5; restore.w = 5; restore.h = 5; SDL_BlitSurface(mBackground, &restore, mScreen, &restore); mBallPosition = position; mBallRotation = rotation; } void RenderManagerSDL::setMouseMarker(float position) { SDL_Rect restore = { short(lround(mMouseMarkerPosition - 2.5)), 590, 5, 5 }; SDL_BlitSurface(mBackground, &restore, mScreen, &restore); mMouseMarkerPosition = position; } void RenderManagerSDL::setBlob(int player, const Vector2& position, float animationState) { SDL_Rect blobRestore; SDL_Rect shadowRestore; if (player == LEFT_PLAYER) { blobRestore = blobRect(mLeftBlobPosition); shadowRestore = blobShadowRect( blobShadowPosition(mLeftBlobPosition)); mLeftBlobPosition = position; mLeftBlobAnimationState = animationState; } if (player == RIGHT_PLAYER) { blobRestore = blobRect(mRightBlobPosition); shadowRestore = blobShadowRect( blobShadowPosition(mRightBlobPosition)); mRightBlobPosition = position; mRightBlobAnimationState = animationState; } SDL_BlitSurface(mBackground, &blobRestore, mScreen, &blobRestore); SDL_BlitSurface(mBackground, &shadowRestore, mScreen, &shadowRestore); } void RenderManagerSDL::setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning) { SDL_Rect restore = { 24, 24, 96, 24 }; SDL_BlitSurface(mBackground, &restore, mScreen, &restore); restore.x = 800 - 96; SDL_BlitSurface(mBackground, &restore, mScreen, &restore); mLeftPlayerScore = leftScore; mRightPlayerScore = rightScore; mLeftPlayerWarning = leftWarning; mRightPlayerWarning = rightWarning; } void RenderManagerSDL::setPlayernames(std::string leftName, std::string rightName) { mLeftPlayerName = leftName; mRightPlayerName = rightName; int tl = mLeftPlayerName.size() * FONT_WIDTH_NORMAL; SDL_FreeSurface(mLeftPlayerNameTexture); SDL_Surface* surface = createEmptySurface(tl, 24); SDL_SetAlpha(surface, SDL_SRCALPHA, 255); SDL_SetColorKey(surface, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(surface->format, 0, 0, 0)); drawTextImpl(mLeftPlayerName, Vector2(0,0), TF_NORMAL, surface); mLeftPlayerNameTexture = SDL_DisplayFormatAlpha(surface); SDL_FreeSurface(surface); tl = mRightPlayerName.size() * FONT_WIDTH_NORMAL; SDL_FreeSurface(mRightPlayerNameTexture); surface = createEmptySurface(tl, 24); SDL_SetAlpha(surface, SDL_SRCALPHA, 255); SDL_SetColorKey(surface, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(surface->format, 0, 0, 0)); drawTextImpl(mRightPlayerName, Vector2(0,0), TF_NORMAL, surface); mRightPlayerNameTexture = SDL_DisplayFormatAlpha(surface); SDL_FreeSurface(surface); } void RenderManagerSDL::setTime(const std::string& t) { mTime = t; } void RenderManagerSDL::drawText(const std::string& text, Vector2 position, unsigned int flags) { drawTextImpl(text, position, flags, mScreen); } void RenderManagerSDL::drawTextImpl(const std::string& text, Vector2 position, unsigned int flags, SDL_Surface* screen) { int FontSize = (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL); int length = 0; std::string string = text; int index = getNextFontIndex(string); while (index != -1) { if (flags & TF_OBFUSCATE) index = FONT_INDEX_ASTERISK; SDL_Rect charPosition; charPosition.x = lround(position.x) + length; charPosition.y = lround(position.y); if (flags & TF_SMALL_FONT) if (flags & TF_HIGHLIGHT) SDL_BlitSurface( mHighlightSmallFont[index], 0, screen, &charPosition ); else SDL_BlitSurface( mSmallFont[index], 0, screen, &charPosition ); else if (flags & TF_HIGHLIGHT) SDL_BlitSurface( mHighlightFont[index], 0, screen, &charPosition ); else SDL_BlitSurface( mFont[index], 0, screen, &charPosition ); index = getNextFontIndex(string); length += FontSize; } } void RenderManagerSDL::drawImage(const std::string& filename, Vector2 position) { mNeedRedraw = true; BufferedImage* imageBuffer = mImageMap[filename]; if (!imageBuffer) { imageBuffer = new BufferedImage; imageBuffer->sdlImage = loadSurface(filename); SDL_SetColorKey(imageBuffer->sdlImage, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(mScreen->format, 0, 0, 0)); mImageMap[filename] = imageBuffer; } SDL_Rect blitRect = { (short)lround(position.x - float(imageBuffer->sdlImage->w) / 2.0), (short)lround(position.y - float(imageBuffer->sdlImage->h) / 2.0), (short)lround(position.x + float(imageBuffer->sdlImage->w) / 2.0), (short)lround(position.y + float(imageBuffer->sdlImage->h) / 2.0), }; SDL_BlitSurface(imageBuffer->sdlImage, 0, mScreen, &blitRect); } void RenderManagerSDL::drawOverlay(float opacity, Vector2 pos1, Vector2 pos2, Color col) { SDL_Rect ovRect; ovRect.x = lround(pos1.x); ovRect.y = lround(pos1.y); ovRect.w = lround(pos2.x - pos1.x); ovRect.h = lround(pos2.y - pos1.y); SDL_SetAlpha(mOverlaySurface, SDL_SRCALPHA, lround(opacity * 255)); SDL_FillRect(mOverlaySurface, NULL, SDL_MapRGB(mScreen->format, col.r, col.g, col.b)); SDL_SetClipRect(mScreen, &ovRect); SDL_BlitSurface(mOverlaySurface, 0, mScreen, 0); SDL_SetClipRect(mScreen, 0); } void RenderManagerSDL::drawBlob(const Vector2& pos, const Color& col) { SDL_Rect position; position.x = lround(pos.x); position.y = lround(pos.y); static int toDraw = 0; mLeftBlobAnimationState = 0; mRightBlobAnimationState = 0; setBlobColor(toDraw, col); /// \todo this recolores the current frame (0) /// + shadows; thats not exactly what we want colorizeBlobs(toDraw); // Second dirty workaround in the function to have the right position of blobs in the GUI position.x = position.x - (int)(75/2); position.y = position.y - (int)(89/2); if(toDraw == 1) { SDL_BlitSurface(mRightBlob[mRightBlobAnimationState].mSDLsf, 0, mScreen, &position); toDraw = 0; } else { SDL_BlitSurface(mLeftBlob[mLeftBlobAnimationState].mSDLsf, 0, mScreen, &position); toDraw = 1; } } void RenderManagerSDL::drawParticle(const Vector2& pos, int player) { mNeedRedraw = true; SDL_Rect blitRect = { (short)lround(pos.x - float(9) / 2.0), (short)lround(pos.y - float(9) / 2.0), (short)lround(pos.x + float(9) / 2.0), (short)lround(pos.y + float(9) / 2.0), }; SDL_Surface* blood = player == LEFT_PLAYER ? mLeftBlobBlood : mRightBlobBlood; SDL_BlitSurface(blood, 0, mScreen, &blitRect); } void RenderManagerSDL::refresh() { SDL_Flip(mScreen); } blobby-1.0rc3/src/ReplayPlayer.h0000644000175000017500000000755612042452374020134 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "Global.h" #include "ReplayDefs.h" #include "InputSource.h" class DuelMatch; class IReplayLoader; /// \class ReplayPlayer /// \brief Manages playing of replays. /// \details This class is responsible for actually replaying the replays. /// It uses the data the IReplayLoader gets from files and uses them to create a full replay. /// \todo maybe we should rename this class. It might be confused with Player (a blob) and NetworkPlayer /// (a network connection on server). class ReplayPlayer { public: ReplayPlayer(); ~ReplayPlayer(); void load(const std::string& filename); // ----------------------------------------------------------------------------------------- // Replay Attributes // ----------------------------------------------------------------------------------------- std::string getPlayerName(const PlayerSide side) const; Color getBlobColor(const PlayerSide side) const; int getGameSpeed() const; // ----------------------------------------------------------------------------------------- // Status information // ----------------------------------------------------------------------------------------- /// \brief Replaying finished /// \details This reports whether the record is played to the end, so the /// blobbys don't have to stand around bored after an incomplete /// input record. bool endOfFile() const; /// \brief Replay Progress in precent /// \details returns (an estimate for) the replay progress in percent. Depending on /// replay file version, this is either exact or a guess of the system (when we /// don't know how long the replay is). float getPlayProgress() const; /// \brief current replay position /// \details returns the current position in replay in steps. int getReplayPosition() const; /// \brief length of replay /// \details returns the replay length in steps. int getReplayLength() const; // ----------------------------------------------------------------------------------------- // replaying interface // ----------------------------------------------------------------------------------------- /// \brief advances the game one step bool play(DuelMatch* virtual_match); /// \brief Jumps to a position in replay. /// \details Goes to a certain position in replay. Simulates at most 100 steps per call /// to prevent visual lags, so it is possible that this function has to be called /// several times to reach the target. /// \param rep_position target position in number of physic steps. /// \return True, if desired position could be reached. bool gotoPlayingPosition(int rep_position, DuelMatch* virtual_match); private: int mPosition; int mLength; boost::scoped_ptr loader; std::string mPlayerNames[MAX_PLAYERS]; }; blobby-1.0rc3/src/TextManager.cpp0000644000175000017500000002067512042452374020272 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "TextManager.h" /* includes */ #include #include #include "tinyxml/tinyxml.h" #include "Global.h" #include "FileRead.h" /* implementation */ TextManager* TextManager::mSingleton = 0; TextManager* TextManager::createTextManager(std::string langname){ delete mSingleton; mSingleton = new TextManager(langname); std::string langfile = "lang_"+langname+".xml"; bool loaded = false; try{ loaded = mSingleton->loadFromXML(langfile); }catch(FileLoadException& fle){ std::cerr << fle.what() << std::endl; }; if(!loaded){ std::cerr << "error loading language " << langfile << "!" << std::endl; std::cerr << "\tfalling back to english" << std::endl; } } const TextManager* TextManager::getSingleton(){ return mSingleton; } TextManager::TextManager(std::string l):lang(l){ mStrings.resize(COUNT); setDefault(); } void TextManager::switchLanguage(std::string langname){ // if old and new language are the same, nothing must be done if(langname == mSingleton->lang) return; // otherwise, the old TextManager is destroyed and a new one is created createTextManager(langname); } const std::string& TextManager::getString(STRING str) const{ return mStrings[str]; } std::string TextManager::getLang() const{ return lang; } /// \todo why no const std::string& ? bool TextManager::loadFromXML(std::string filename){ // read and parse file boost::shared_ptr language_data = FileRead::readXMLDocument(filename); if (language_data->Error()) { std::cerr << "Warning: Parse error in " << filename; std::cerr << "!" << std::endl; } TiXmlElement* language = language_data->FirstChildElement("language"); if (!language) return false; int num_strings = mStrings.size(); int found_count = 0; // this loop assumes that the strings in the xml file are in the correct order // in each step, it reads the next string element and writes it to the next position in mStrings for ( TiXmlElement* stringel = language->FirstChildElement("string"); stringel; stringel = stringel->NextSiblingElement("string")) { /// \todo we don't check for duplicate entries! const char* e = stringel->Attribute("english"); const char* t = stringel->Attribute("translation"); if (t && e){ // search the english string and replace it with the translation std::vector::iterator found = std::find(mStrings.begin(), mStrings.end(), e); if(found != mStrings.end()) { found_count++; *found = t; } else std::cerr << "error in language file: entry " << e << " -> " << t << " invalid\n"; } else if(t) { std::cerr << "error in language file: english not found for " << t << std::endl; } else if(e) { std::cerr << "error in language file: translation not found for " << e << std::endl; } } // do we check if we got all? if(num_strings != found_count) { std::cerr << "missing translations: got " << found_count << " out of " << num_strings << " translation entries" << std::endl; } return true; } void TextManager::setDefault() { // Hardcoded default language mStrings[LBL_OK] = "ok"; mStrings[LBL_CANCEL] = "cancel"; mStrings[LBL_YES] = "yes"; mStrings[LBL_NO] = "no"; mStrings[LBL_CONF_QUIT] = "really quit?"; mStrings[LBL_CONTINUE] = "continue"; mStrings[MNU_LABEL_ONLINE] = "online game"; mStrings[MNU_LABEL_LAN] = "lan game"; mStrings[MNU_LABEL_START] = "start"; mStrings[MNU_LABEL_OPTIONS] = "options"; mStrings[MNU_LABEL_REPLAY] = "watch replay"; mStrings[MNU_LABEL_CREDITS] = "credits"; mStrings[MNU_LABEL_EXIT] = "exit"; mStrings[CRD_PROGRAMMERS] = "programmers:"; mStrings[CRD_GRAPHICS] = "graphics:"; mStrings[CRD_THX] = "special thanks at:"; mStrings[RP_SHOW_AGAIN] = "show again"; mStrings[RP_PLAY] = "play"; mStrings[RP_DELETE] = "delete"; mStrings[RP_INFO] = "info"; mStrings[RP_DURATION] = "duration:"; mStrings[RP_RESULT] = "result:"; mStrings[RP_CHECKSUM] = "checksum error"; mStrings[RP_FILE_CORRUPT] = "file is corrupt"; mStrings[RP_VERSION] = "version error"; mStrings[RP_FILE_OUTDATED] = "file is outdated"; mStrings[RP_SAVE_NAME] = "name of the replay:"; mStrings[RP_WAIT_REPLAY] = "receiving replay..."; mStrings[RP_SAVE] = "save replay"; mStrings[GAME_WIN] = "has won the game!"; mStrings[GAME_TRY_AGAIN] = "try again"; mStrings[GAME_WAITING] = "waiting for opponent..."; mStrings[GAME_OPP_LEFT] = "opponent left the game"; mStrings[GAME_PAUSED] = "game paused"; mStrings[GAME_QUIT] = "quit"; mStrings[NET_SERVER_SCAN] = "scan for servers"; mStrings[NET_DIRECT_CONNECT] = "direct connect"; mStrings[NET_SERVER_INFO] = "server info"; mStrings[NET_ACTIVE_GAMES] = "active games: "; mStrings[NET_WAITING_PLAYER] = "waiting player: "; mStrings[NET_HOST_GAME] = "host game"; mStrings[NET_CONNECTING] = "connecting to server ..."; mStrings[NET_DISCONNECT] = "disconnected from server"; mStrings[NET_CON_FAILED] = "connection failed"; mStrings[NET_SERVER_FULL] = "server full"; mStrings[OP_INPUT_OP] = "input options"; mStrings[OP_GFX_OP] = "graphic options"; mStrings[OP_MISC] = "misc options"; mStrings[OP_VIDEO] = "video settings"; mStrings[OP_FULLSCREEN] = "fullscreen mode"; mStrings[OP_WINDOW] = "window mode"; mStrings[OP_RENDER_DEVICE] = "render device"; mStrings[OP_SHOW_SHADOW] = "show shadow"; mStrings[OP_BLOB_COLORS] = "blob colors"; mStrings[OP_LEFT_PLAYER] = "left player"; mStrings[OP_RIGHT_PLAYER] = "right player"; mStrings[OP_RED] = "red"; mStrings[OP_GREEN] = "green"; mStrings[OP_BLUE] = "blue"; mStrings[OP_MORPHING] = "morphing blob?"; mStrings[OP_KEYBOARD] = "keyboard"; mStrings[OP_MOUSE] = "mouse"; mStrings[OP_JOYSTICK] = "joystick"; mStrings[OP_JUMP_BUTTON] = "jump button"; mStrings[OP_SET_ALL] = "set all"; mStrings[OP_LEFT_KEY] = "left key"; mStrings[OP_RIGHT_KEY] = "right key"; mStrings[OP_JUMP_KEY] = "jump key"; mStrings[OP_LEFT_BUTTON] = "left button"; mStrings[OP_RIGHT_BUTTON] = "right button"; mStrings[OP_PRESS_MOUSE_BUTTON] = "press mouse button for"; mStrings[OP_PRESS_KEY_FOR] = "press key for"; mStrings[OP_MOVING_LEFT] = "moving left"; mStrings[OP_MOVING_RIGHT] = "moving right"; mStrings[OP_JUMPING] = "jumping"; mStrings[OP_PRESS_BUTTON_FOR] = "press button for"; mStrings[OP_BACKGROUND] = "background:"; mStrings[OP_VOLUME] = "volume:"; mStrings[OP_MUTE] = "mute"; mStrings[OP_FPS] = "show fps"; mStrings[OP_BLOOD] = "show blood"; mStrings[OP_NETWORK_SIDE] = "network side:"; mStrings[OP_LEFT] = "left"; mStrings[OP_RIGHT] = "right"; mStrings[OP_SPEED] = "gamespeed:"; mStrings[OP_VSLOW] = "very slow"; mStrings[OP_SLOW] = "slow"; mStrings[OP_DEFAULT] = "default"; mStrings[OP_FAST] = "fast"; mStrings[OP_VFAST] = "very fast"; mStrings[OP_LANGUAGE] = "language"; mStrings[OP_DIFFICULTY] = "bot strength"; mStrings[OP_WEAK] = "weak"; mStrings[OP_MEDIUM] = "medium"; mStrings[OP_STRONG] = "strong"; mStrings[UPDATE_NOTIFICATION] = "please visit http://blobby.sourceforge.net/ for a new version of blobby volley"; } std::map TextManager::language_names; struct lang_init{ lang_init(){ TextManager::language_names["de"] = "deutsch"; TextManager::language_names["en"] = "english"; TextManager::language_names["fr"] = "francais"; TextManager::language_names["it"] = "italiano"; } }; static lang_init init; blobby-1.0rc3/src/InputDevice.cpp0000644000175000017500000001554212042452374020267 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "InputDevice.h" /* includes */ #include #include #include "DuelMatch.h" #include "RenderManager.h" /* implementation */ // ------------------------------------------------------------------------------------------------- // MOUSE INPUT // ------------------------------------------------------------------------------------------------- MouseInputDevice::MouseInputDevice(PlayerSide player, int jumpbutton) : InputDevice() { mJumpButton = jumpbutton; mPlayer = player; if (SDL_GetMouseState(NULL, NULL)) mDelay = true; else mDelay = false; mInputs.set_capacity(10); for(unsigned int i = 0; i < 10; ++i) { mInputs.push_back( PlayerInput() ); } } void MouseInputDevice::transferInput(PlayerInput& input) { // check if we have a running game, // otherwise leave directly DuelMatch* match = DuelMatch::getMainGame(); if (match == 0) return; input = PlayerInput(); int mMouseXPos; bool warp = SDL_GetAppState() & SDL_APPINPUTFOCUS; int mouseState = SDL_GetMouseState(&mMouseXPos, NULL); if (warp) SDL_WarpMouse(mMouseXPos, 310); if (mouseState == 0) mDelay = false; if((mouseState & SDL_BUTTON(mJumpButton)) && !mDelay) input.up = true; const int playerOffset = mPlayer == RIGHT_PLAYER ? 200 : -200; mMouseXPos = mMouseXPos < 201 ? 201 : mMouseXPos; if (mMouseXPos <= 201 && warp) SDL_WarpMouse(201, 310); mMouseXPos = mMouseXPos > 600 ? 600 : mMouseXPos; if (mMouseXPos >= 600 && warp) SDL_WarpMouse(600, 310); // here we load the current position of the player. float blobpos = match->getBlobPosition(mPlayer).x; // ask our lag detector about estimated current lag int lag = mLag.getLag(); // adapt this value lag -= 1; if(lag < 0) lag = 0; mInputs.set_capacity(lag+1); // now, simulate as many steps as we have lag for(boost::circular_buffer::iterator i = mInputs.begin(); i != mInputs.end(); ++i) { if(i->right) blobpos += BLOBBY_SPEED; if(i->left) blobpos -= BLOBBY_SPEED; } mMarkerX = mMouseXPos + playerOffset; if (blobpos + BLOBBY_SPEED * 2 <= mMarkerX) input.right = true; else if (blobpos - BLOBBY_SPEED * 2 >= mMarkerX) input.left = true; // insert new data for evaluation mLag.insertData(input, match->getPlayersInput()[mPlayer]); mInputs.push_back(input); RenderManager::getSingleton().setMouseMarker(mMarkerX); } // ------------------------------------------------------------------------------------------------- // KEYBOARD INPUT // ------------------------------------------------------------------------------------------------- KeyboardInputDevice::KeyboardInputDevice(SDLKey leftKey, SDLKey rightKey, SDLKey jumpKey) : InputDevice() { mLeftKey = leftKey; mRightKey = rightKey; mJumpKey = jumpKey; } void KeyboardInputDevice::transferInput(PlayerInput& input) { Uint8* keyState = SDL_GetKeyState(0); input = PlayerInput(keyState[mLeftKey], keyState[mRightKey], keyState[mJumpKey]); } // ------------------------------------------------------------------------------------------------- // JOYSTICK INPUT // ------------------------------------------------------------------------------------------------- // Joystick Pool JoystickPool* JoystickPool::mSingleton = 0; //static JoystickPool& JoystickPool::getSingleton() { if (mSingleton == 0) mSingleton = new JoystickPool(); return *mSingleton; } SDL_Joystick* JoystickPool::getJoystick(int id) { SDL_Joystick* joy = mJoyMap[id]; if (!joy) std::cerr << "Warning: could not find joystick number " << id << "!" << std::endl; return joy; } int JoystickPool::probeJoysticks() { int id = 0; SDL_Joystick* lastjoy; while ((lastjoy = SDL_JoystickOpen(id))) { mJoyMap[id] = lastjoy; id++; } return id; } void JoystickPool::closeJoysticks() { for (JoyMap::iterator iter = mJoyMap.begin(); iter != mJoyMap.end(); ++iter) { SDL_JoystickClose((*iter).second); } } // Joystick Action JoystickAction::JoystickAction(std::string string) { type = AXIS; number = 0; joy = 0; joyid = 0; try { const char* str = string.c_str(); if (strstr(str, "button")) { type = BUTTON; if (sscanf(str, "joy_%d_button_%d", &joyid, &number) < 2) throw string; } else if (strstr(str, "axis")) { if (sscanf(str, "joy_%d_axis_%d", &joyid, &number) < 2) throw string; } joy = JoystickPool::getSingleton().getJoystick(joyid); } catch (const std::string& e) { std::cerr << "Parse error in joystick config: " << e << std::endl; } } JoystickAction::JoystickAction(const JoystickAction& action) { type = action.type; joy = JoystickPool::getSingleton().getJoystick(action.joyid); joyid = action.joyid; number = action.number; } JoystickAction::~JoystickAction() { } std::string JoystickAction::toString() { const char* typestr = "unknown"; if (type == AXIS) typestr = "axis"; if (type == BUTTON) typestr = "button"; std::stringstream buf; buf << "joy_" << joyid << "_" << typestr << "_" << number; return buf.str(); } // Joystick Input Device JoystickInputDevice::JoystickInputDevice(JoystickAction laction, JoystickAction raction, JoystickAction jaction) : mLeftAction(laction), mRightAction(raction), mJumpAction(jaction) { } void JoystickInputDevice::transferInput(PlayerInput& input) { input.left = getAction(mLeftAction); input.right = getAction(mRightAction); input.up = getAction(mJumpAction); } bool JoystickInputDevice::getAction(const JoystickAction& action) { if (action.joy != 0) { switch (action.type) { case JoystickAction::AXIS: if (action.number < 0) { if (SDL_JoystickGetAxis(action.joy, -action.number - 1) < -15000) return true; } else if (action.number > 0) { if (SDL_JoystickGetAxis(action.joy, action.number - 1) > 15000) return true; } break; case JoystickAction::BUTTON: if (SDL_JoystickGetButton(action.joy, action.number)) return true; break; } } return false; } blobby-1.0rc3/src/BlobbyDebug.cpp0000644000175000017500000000402412042452374020221 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #include "BlobbyDebug.h" #include #include #include #include std::map& GetCounterMap() { static std::map CounterMap; return CounterMap; } int count(const std::type_info& type) { std::string test = type.name(); if(GetCounterMap().find(type.name()) == GetCounterMap().end() ) { GetCounterMap()[type.name()] = CountingReport(); } GetCounterMap()[type.name()].created++; GetCounterMap()[type.name()].alive++; } int uncount(const std::type_info& type) { GetCounterMap()[type.name()].alive--; } std::fstream total_plot("logs/total.txt", std::fstream::out); void report(std::ostream& stream) { stream << "MEMORY REPORT\n"; int sum = 0; for(std::map::iterator i = GetCounterMap().begin(); i != GetCounterMap().end(); ++i) { stream << i->first << "\n- - - - - - - - - -\n"; stream << " alive: " << i->second.alive << "\n"; stream << " created: " << i->second.created << "\n\n"; sum += i->second.alive; } total_plot << sum << std::endl; } blobby-1.0rc3/src/BotAPICalculations.cpp0000644000175000017500000001451512042452374021467 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "BotAPICalculations.h" /* includes */ #include #include #include "GameConstants.h" /* implementation */ bool FLAG_BOUNCE = false; // helpers float time_to_x_direct(float pos, float vel, float destination); float time_to_y_direct(float pos, float vel, float destination); float parabel_time_first(float pos, float vel, float gravity, float destination); float make_unsigned(float f) { return (f > 0 ? f : std::numeric_limits::infinity()); } void reset_flags() { FLAG_BOUNCE = false; } float time_to_x(const Vector2& pos, const Vector2& vel, float destination) { // check whether velocity is valid if( vel.x == 0 ) return std::numeric_limits::max(); // direct? float timedirect = time_to_x_direct(pos.x, vel.x, destination); // needs wall bounce if( timedirect < 0 ) { FLAG_BOUNCE = true; float wall = vel.x > 0 ? (RIGHT_PLANE - BALL_RADIUS) : BALL_RADIUS; float twall = time_to_x_direct(pos.x, vel.x, wall); float net = vel.x > 0 ? (NET_POSITION_X - BALL_RADIUS - NET_RADIUS) : (NET_POSITION_X + BALL_RADIUS + NET_RADIUS); float tnet = make_unsigned(time_to_x_direct(pos.x, vel.x, net)); if ( tnet < twall ) { Vector2 nhitpos = Vector2(net, predict_y(pos, vel, tnet)); if ( nhitpos.y > NET_SPHERE_POSITION - NET_RADIUS - BALL_RADIUS ) return tnet + time_to_x(nhitpos, vel.reflectX(), destination); } Vector2 whitpos = Vector2(wall, predict_y(pos, vel, twall)); return twall + time_to_x(whitpos, vel.reflectX(), destination); } float net = vel.x > 0 ? (NET_POSITION_X - BALL_RADIUS - NET_RADIUS) : (NET_POSITION_X + BALL_RADIUS + NET_RADIUS); float tnet = make_unsigned(time_to_x_direct(pos.x, vel.x, net)); Vector2 nhitpos = Vector2(net, predict_y(pos, vel, tnet)); if ( tnet > timedirect || nhitpos.y < NET_SPHERE_POSITION - NET_RADIUS - BALL_RADIUS) { // if ball is too high or destination is reached before net, no collision can occur return timedirect; } FLAG_BOUNCE = true; // if ball hits net on false side, it is impossible to reach its destination if( nhitpos.y > pos.y ) { return std::numeric_limits::max(); } return tnet + time_to_x(nhitpos, vel.reflectX(), destination); } float time_to_y(const Vector2& pos, const Vector2& vel, float destination) { return time_to_y_direct(pos.y, vel.y, destination); } float predict_x(const Vector2& pos, const Vector2& vel, float time) { // can net collision occur float net = vel.x > 0 ? (NET_POSITION_X - BALL_RADIUS - NET_RADIUS) : (NET_POSITION_X + BALL_RADIUS + NET_RADIUS); float tnet = make_unsigned( time_to_x_direct(pos.x, vel.x, net) ); // calculate estimated hitpos Vector2 nhitpos = Vector2(net, predict_y(pos, vel, tnet)); // can ignore net bounce if ( tnet > time || nhitpos.y < NET_SPHERE_POSITION - NET_RADIUS - BALL_RADIUS) { float spos = pos.x + vel.x*time; if ( spos < BALL_RADIUS) return 2 * BALL_RADIUS - spos; else if ( spos > RIGHT_PLANE - BALL_RADIUS ) return 2*(RIGHT_PLANE - BALL_RADIUS) - spos; return spos; } // collision with net return predict_x(nhitpos, vel.reflectX(), time - tnet); } float predict_y(const Vector2& pos, const Vector2& vel, float time) { return pos.y + (vel.y + BALL_GRAVITATION/2.0 * time) * time; } float y_at_x(const Vector2& pos, const Vector2& vel, float destination) { float time = time_to_x(pos, vel, destination); return predict_y(pos, vel, time); } float x_at_y(const Vector2& pos, const Vector2& vel, float destination) { float time = time_to_y(pos, vel, destination); return predict_x(pos, vel, time); } float next_event(const Vector2& pos, const Vector2& vel) { // walls and net float time_wall; float time_net; if( vel.x > 0 ) { time_wall = time_to_x_direct(pos.x, vel.x, RIGHT_PLANE - BALL_RADIUS); time_net = time_to_x_direct(pos.x, vel.x, NET_POSITION_X - NET_RADIUS - BALL_RADIUS); } else { time_wall = time_to_x_direct(pos.x, vel.x, LEFT_PLANE + BALL_RADIUS); time_net = time_to_x_direct(pos.x, vel.x, NET_POSITION_X + NET_RADIUS + BALL_RADIUS); } // ground float time_ground = time_to_y_direct(pos.y, vel.y, GROUND_PLANE_HEIGHT_MAX - BALL_RADIUS); time_net = make_unsigned(time_net); time_wall = make_unsigned(time_wall); time_ground = make_unsigned(time_ground); if ( time_net < time_wall && time_net < time_ground ) { FLAG_BOUNCE = true; return time_net; } else if ( time_wall < time_net && time_wall < time_ground ) { FLAG_BOUNCE = true; return time_wall; } else { return time_ground; } } float time_to_x_direct(float pos, float vel, float destination) { return (destination - pos) / vel; } float time_to_y_direct(float pos, float vel, float destination) { return parabel_time_first(pos, vel, BALL_GRAVITATION, destination); } float parabel_time_first(float pos, float vel, float grav, float destination) { float sq = vel*vel + 2*grav*(destination - pos); // if unreachable, return -1 if ( sq < 0 ) { return -1; } sq = std::sqrt(sq); float tmin = (-vel - sq) / grav; float tmax = (-vel + sq) / grav; if ( grav < 0 ) { float temp = tmin; tmin = tmax; tmax = temp; } if ( tmin > 0 ) return tmin; else if ( tmax > 0 ) return tmax; return -1; } blobby-1.0rc3/src/NetworkGame.cpp0000644000175000017500000003011112042452374020260 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "NetworkGame.h" /* includes */ #include #include #include "raknet/RakServer.h" #include "raknet/BitStream.h" #include "raknet/GetTime.h" #include "NetworkMessage.h" #include "ReplayRecorder.h" #include "FileRead.h" #include "FileSystem.h" #include "GenericIO.h" /* implementation */ NetworkGame::NetworkGame(RakServer& server, PlayerID leftPlayer, PlayerID rightPlayer, std::string leftPlayerName, std::string rightPlayerName, Color leftColor, Color rightColor, PlayerSide switchedSide) : mServer(server), mLeftInput (new DummyInputSource()), mRightInput(new DummyInputSource()) { mMatch = new DuelMatch(mLeftInput.get(), mRightInput.get(), false, false); mLeftPlayer = leftPlayer; mRightPlayer = rightPlayer; mSwitchedSide = switchedSide; mLeftPlayerName = leftPlayerName; mRightPlayerName = rightPlayerName; mWinningPlayer = NO_PLAYER; mPausing = false; mRecorder.reset(new ReplayRecorder()); mRecorder->setPlayerNames(mLeftPlayerName.c_str(), mRightPlayerName.c_str()); mRecorder->setPlayerColors(leftColor, rightColor); mRecorder->setGameSpeed(SpeedController::getMainInstance()->getGameSpeed()); // buffer for playernames char name[16]; // writing data into leftStream RakNet::BitStream leftStream; leftStream.Write((unsigned char)ID_GAME_READY); leftStream.Write((int)SpeedController::getMainInstance()->getGameSpeed()); strncpy(name, mRightPlayerName.c_str(), sizeof(name)); leftStream.Write(name, sizeof(name)); leftStream.Write(rightColor.toInt()); // writing data into rightStream RakNet::BitStream rightStream; rightStream.Write((unsigned char)ID_GAME_READY); rightStream.Write((int)SpeedController::getMainInstance()->getGameSpeed()); strncpy(name, mLeftPlayerName.c_str(), sizeof(name)); rightStream.Write(name, sizeof(name)); rightStream.Write(leftColor.toInt()); mServer.Send(&leftStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, mLeftPlayer, false); mServer.Send(&rightStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, mRightPlayer, false); } NetworkGame::~NetworkGame() { delete mMatch; } void NetworkGame::injectPacket(const packet_ptr& packet) { mPacketQueue.push_back(packet); } void NetworkGame::broadcastBitstream(RakNet::BitStream* stream, RakNet::BitStream* switchedstream) { // checks that stream and switchedstream don't have the same content. // this is a common mistake that arises from constructs like: // BitStream stream // ... fill common data into stream // BitStream switchedstream // .. fill data depending on side in both streams // broadcastBistream(stream, switchedstream) // // here, the internal data of switchedstream is the same as stream so all // changes made with switchedstream are done with stream alike. this was not // the intention of this construct so it should be caught by this assertion. /// NEVER USE THIS FUNCTION LIKE broadcastBitstream(str, str), use, broadcastBitstream(str) instead /// this function is intended for sending two different streams to the two clients assert( stream != switchedstream ); assert( stream->GetData() != switchedstream->GetData() ); RakNet::BitStream* leftStream = mSwitchedSide == LEFT_PLAYER ? switchedstream : stream; RakNet::BitStream* rightStream = mSwitchedSide == RIGHT_PLAYER ? switchedstream : stream; mServer.Send(leftStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, mLeftPlayer, false); mServer.Send(rightStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, mRightPlayer, false); } void NetworkGame::broadcastBitstream(RakNet::BitStream* stream) { mServer.Send(stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, mLeftPlayer, false); mServer.Send(stream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, mRightPlayer, false); } bool NetworkGame::step() { bool active = true; while (!mPacketQueue.empty()) { packet_ptr packet = mPacketQueue.front(); mPacketQueue.pop_front(); switch(packet->data[0]) { case ID_CONNECTION_LOST: case ID_DISCONNECTION_NOTIFICATION: { RakNet::BitStream stream; stream.Write((unsigned char)ID_OPPONENT_DISCONNECTED); broadcastBitstream(&stream); mPausing = true; mMatch->pause(); active = false; break; } case ID_INPUT_UPDATE: { PlayerInput newInput; int ival; RakNet::BitStream stream((char*)packet->data, packet->length, false); // ignore ID_INPUT_UPDATE and ID_TIMESTAMP stream.IgnoreBytes(1); stream.IgnoreBytes(1); stream.Read(ival); stream.Read(newInput.left); stream.Read(newInput.right); stream.Read(newInput.up); if (packet->playerId == mLeftPlayer) { if (mSwitchedSide == LEFT_PLAYER) newInput.swap(); mLeftInput->setInput(newInput); } if (packet->playerId == mRightPlayer) { if (mSwitchedSide == RIGHT_PLAYER) newInput.swap(); mRightInput->setInput(newInput); } break; } case ID_PAUSE: { RakNet::BitStream stream; stream.Write((unsigned char)ID_PAUSE); broadcastBitstream(&stream); mPausing = true; mMatch->pause(); break; } case ID_UNPAUSE: { RakNet::BitStream stream; stream.Write((unsigned char)ID_UNPAUSE); broadcastBitstream(&stream); mPausing = false; mMatch->unpause(); break; } case ID_CHAT_MESSAGE: { RakNet::BitStream stream((char*)packet->data, packet->length, false); stream.IgnoreBytes(1); // ID_CHAT_MESSAGE char message[31]; /// \todo we need to acertain that this package contains at least 31 bytes! /// otherwise, we send just uninitialized memory to the client /// thats no real security problem but i think we should address /// this nonetheless stream.Read(message, sizeof(message)); RakNet::BitStream stream2; stream2.Write((unsigned char)ID_CHAT_MESSAGE); stream2.Write(message, sizeof(message)); if (mLeftPlayer == packet->playerId) mServer.Send(&stream2, LOW_PRIORITY, RELIABLE_ORDERED, 0, mRightPlayer, false); else mServer.Send(&stream2, LOW_PRIORITY, RELIABLE_ORDERED, 0, mLeftPlayer, false); break; } case ID_REPLAY: { boost::shared_ptr stream = boost::make_shared(); stream->Write((unsigned char)ID_REPLAY); boost::shared_ptr out = createGenericWriter(stream); mRecorder->send( out ); assert( stream->GetData()[0] == ID_REPLAY ); mServer.Send(stream.get(), LOW_PRIORITY, RELIABLE_ORDERED, 0, packet->playerId, false); break; } default: printf("unknown packet %d received\n", int(packet->data[0])); break; } } // don't record the pauses if(!mMatch->isPaused()) mRecorder->record(mMatch->getState()); mMatch->step(); int events = mMatch->getEvents(); if(events & DuelMatch::EVENT_LEFT_BLOBBY_HIT) { RakNet::BitStream stream; stream.Write((unsigned char)ID_BALL_PLAYER_COLLISION); stream.Write(mMatch->getWorld().lastHitIntensity()); stream.Write(LEFT_PLAYER); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_BALL_PLAYER_COLLISION); switchStream.Write(mMatch->getWorld().lastHitIntensity()); switchStream.Write(RIGHT_PLAYER); broadcastBitstream(&stream, &switchStream); } if(events & DuelMatch::EVENT_RIGHT_BLOBBY_HIT) { RakNet::BitStream stream; stream.Write((unsigned char)ID_BALL_PLAYER_COLLISION); stream.Write(mMatch->getWorld().lastHitIntensity()); stream.Write(RIGHT_PLAYER); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_BALL_PLAYER_COLLISION); switchStream.Write(mMatch->getWorld().lastHitIntensity()); switchStream.Write(LEFT_PLAYER); broadcastBitstream(&stream, &switchStream); } if(events & DuelMatch::EVENT_BALL_HIT_LEFT_GROUND) { RakNet::BitStream stream; stream.Write((unsigned char)ID_BALL_GROUND_COLLISION); stream.Write(LEFT_PLAYER); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_BALL_GROUND_COLLISION); switchStream.Write(RIGHT_PLAYER); broadcastBitstream(&stream, &switchStream); } if(events & DuelMatch::EVENT_BALL_HIT_RIGHT_GROUND) { RakNet::BitStream stream; // is it correct to send ID_BALL_GROUND_COLLISION even if the // error was a forth hit of a player? stream.Write((unsigned char)ID_BALL_GROUND_COLLISION); stream.Write(RIGHT_PLAYER); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_BALL_GROUND_COLLISION); switchStream.Write(LEFT_PLAYER); broadcastBitstream(&stream, &switchStream); } if(!mPausing) { switch(mMatch->winningPlayer()){ case LEFT_PLAYER: { RakNet::BitStream stream; stream.Write((unsigned char)ID_WIN_NOTIFICATION); stream.Write(LEFT_PLAYER); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_WIN_NOTIFICATION); switchStream.Write(RIGHT_PLAYER); broadcastBitstream(&stream, &switchStream); // if someone has won, the game is paused mPausing = true; mMatch->pause(); mRecorder->finalize( mMatch->getScore(LEFT_PLAYER), mMatch->getScore(RIGHT_PLAYER) ); return active; } break; case RIGHT_PLAYER: { RakNet::BitStream stream; stream.Write((unsigned char)ID_WIN_NOTIFICATION); stream.Write(RIGHT_PLAYER); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_WIN_NOTIFICATION); switchStream.Write(LEFT_PLAYER); broadcastBitstream(&stream, &switchStream); // if someone has won, the game is paused mPausing = true; mMatch->pause(); mRecorder->finalize( mMatch->getScore(LEFT_PLAYER), mMatch->getScore(RIGHT_PLAYER) ); return active; } break; } } if (events & DuelMatch::EVENT_RESET) { RakNet::BitStream stream; stream.Write((unsigned char)ID_BALL_RESET); stream.Write(mMatch->getServingPlayer()); stream.Write(mMatch->getScore(LEFT_PLAYER)); stream.Write(mMatch->getScore(RIGHT_PLAYER)); stream.Write(mMatch->getClock().getTime()); RakNet::BitStream switchStream; switchStream.Write((unsigned char)ID_BALL_RESET); switchStream.Write( mMatch->getServingPlayer() == LEFT_PLAYER ? RIGHT_PLAYER : LEFT_PLAYER); switchStream.Write(mMatch->getScore(RIGHT_PLAYER)); switchStream.Write(mMatch->getScore(LEFT_PLAYER)); switchStream.Write(mMatch->getClock().getTime()); broadcastBitstream(&stream, &switchStream); } if (!mPausing) { broadcastPhysicState(); } return active; } void NetworkGame::broadcastPhysicState() { const PhysicWorld& world = mMatch->getWorld(); RakNet::BitStream stream; stream.Write((unsigned char)ID_PHYSIC_UPDATE); stream.Write((unsigned char)ID_TIMESTAMP); stream.Write(RakNet::GetTime()); PhysicState ps = world.getState(); if (mSwitchedSide == LEFT_PLAYER) ps.swapSides(); ps.writeToStream(&stream); mServer.Send(&stream, HIGH_PRIORITY, UNRELIABLE_SEQUENCED, 0, mLeftPlayer, false); // reset state and stream ps = world.getState(); stream.Reset(); stream.Write((unsigned char)ID_PHYSIC_UPDATE); stream.Write((unsigned char)ID_TIMESTAMP); stream.Write(RakNet::GetTime()); if (mSwitchedSide == RIGHT_PLAYER) ps.swapSides(); ps.writeToStream(&stream); mServer.Send(&stream, HIGH_PRIORITY, UNRELIABLE_SEQUENCED, 0, mRightPlayer, false); } blobby-1.0rc3/src/FileSystem.cpp0000644000175000017500000001055212042452374020130 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "FileSystem.h" /* includes */ #include #include /// \todo remove this? currently needed for that probeDir error messages #include /* implementation */ FileSystem* mFileSystemSingleton = 0; FileSystem::FileSystem(const std::string& path) { assert(mFileSystemSingleton == 0); PHYSFS_init(path.c_str()); /// \todo do we need to check if this operation suceeded? mFileSystemSingleton = this; } FileSystem& FileSystem::getSingleton() { assert(mFileSystemSingleton); /// \todo instead of assert, throw exception? return *mFileSystemSingleton; } FileSystem::~FileSystem() { PHYSFS_deinit(); mFileSystemSingleton = 0; } std::vector FileSystem::enumerateFiles(const std::string& directory, const std::string& extension, bool keepExtension) { std::vector files; char** filenames = PHYSFS_enumerateFiles(directory.c_str()); // now test which files have type extension /// \todo this does not ensure that extension really is at the end of the string for (int i = 0; filenames[i] != 0; ++i) { std::string tmp = filenames[i]; if (tmp.find(extension) != std::string::npos) { files.push_back(std::string(tmp.begin(), keepExtension ? (tmp.end()) : (tmp.end() - extension.length()) )); } } // free the file list PHYSFS_freeList(filenames); return files; } bool FileSystem::deleteFile(const std::string& filename) { return PHYSFS_delete(filename.c_str()); } bool FileSystem::exists(const std::string& filename) const { return PHYSFS_exists(filename.c_str()); } bool FileSystem::isDirectory(const std::string& dirname) const { return PHYSFS_isDirectory(dirname.c_str()); } bool FileSystem::mkdir(const std::string& dirname) { return PHYSFS_mkdir(dirname.c_str()); } void FileSystem::addToSearchPath(const std::string& dirname, bool append) { /// \todo check if dir exists? /// \todo use PHYSFS_mount? PHYSFS_addToSearchPath is listed as legacy function only there for binary /// compatibility with older version. /// \todo check return value PHYSFS_addToSearchPath(dirname.c_str(), append ? 1 : 0); } void FileSystem::removeFromSearchPath(const std::string& dirname) { PHYSFS_removeFromSearchPath(dirname.c_str()); } void FileSystem::setWriteDir(const std::string& dirname) { if( !PHYSFS_setWriteDir(dirname.c_str()) ) { throw( PhysfsException() ); }; addToSearchPath(dirname, false); } std::string FileSystem::getDirSeparator() { return PHYSFS_getDirSeparator(); } std::string FileSystem::getUserDir() { return PHYSFS_getUserDir(); } void FileSystem::probeDir(const std::string& dirname) { if ( !isDirectory(dirname) ) { if (exists(dirname)) { /// \todo simple delete such files without a warning??? deleteFile(dirname); } if (mkdir(dirname)) { std::cout << PHYSFS_getWriteDir() << dirname << " created" << std::endl; } else { std::cout << "Warning: Creation of" << PHYSFS_getWriteDir() << dirname << " failed!" << std::endl; } } } // exception implementations std::string makeSafePhysfsErrorString() { const char* physfserror = PHYSFS_getLastError(); return physfserror != 0 ? physfserror : "no physfs error message available."; } PhysfsException::PhysfsException() : mPhysfsErrorMsg( makeSafePhysfsErrorString() ) { } blobby-1.0rc3/src/InputSource.h0000644000175000017500000000564712042452374020002 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include /*! \class PlayerInput \brief struct for easy exchange of a single player input frame */ struct PlayerInput { PlayerInput() { left = false; right = false; up = false; } PlayerInput(const std::string& string); PlayerInput(bool l, bool r, bool u) { left = l; right = r; up = u; } void set(bool l, bool r, bool u) { left = l; right = r; up = u; } void set( unsigned char all ) { left = all & 4; right = all & 2; up = all & 1; } void swap() { bool tmp = left; left = right; right = tmp; } bool operator==(const PlayerInput& other) const { return left == other.left && right == other.right && up == other.up; } bool operator!=(const PlayerInput& other) const { return !(*this == other); } unsigned char getAll() const { unsigned char c = 0; c = (left ? 4 : 0) + (right ? 2 : 0) + (up ? 1 : 0); return c; } bool left; bool right; bool up; }; /*! \class InputSource \brief abstracts several possible input sources. \details This class abstracts several possible input sources, like local input from InputManager, input from a scripted player or input over network which in turn can all be recorded over a decorator It should be only called once per frame because some implementations may use this to activate a refresh routine on their actual source */ class InputSource { public: virtual PlayerInput getInput() = 0; virtual ~InputSource() { } }; // This class serves as a dummy input source. // It can optionally be set from outside if low level input access // is required at a higher level class DummyInputSource : public InputSource { public: virtual PlayerInput getInput() { return mInput; } virtual ~DummyInputSource() { } void setInput(PlayerInput input) { mInput = input; } private: PlayerInput mInput; }; // This operator converts a PlayerInput structure in a packed string // suitable for saving std::ostream& operator<< (std::ostream& out, const PlayerInput& input); blobby-1.0rc3/src/GameLogic.cpp0000644000175000017500000002530212042452374017672 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "GameLogic.h" /* includes */ #include #include #include extern "C" { #include "lua/lua.h" #include "lua/lauxlib.h" #include "lua/lualib.h" } #include "FileRead.h" #include "GameLogicState.h" /* implementation */ /// how many steps must pass until the next hit can happen const int SQUISH_TOLERANCE = 11; IGameLogic::IGameLogic(): mLastError(NO_PLAYER), mServingPlayer(NO_PLAYER), mWinningPlayer(NO_PLAYER), mScoreToWin(15) { // init clock clock.reset(); clock.start(); reset(); mSquish[LEFT_PLAYER] = 0; mSquish[RIGHT_PLAYER] = 0; } IGameLogic::~IGameLogic() { // nothing to do } int IGameLogic::getScore(PlayerSide side) const { return mScores[side2index(side)]; } void IGameLogic::setScore(PlayerSide side, int score) { mScores[side2index(side)] = score; } void IGameLogic::setScoreToWin(int stw) { assert(stw > 0); mScoreToWin = stw; // when are the values in the lua script updated? //lua_pushnumber(mState, mScoreToWin); //lua_setglobal(mState, "SCORE_TO_WIN"); } int IGameLogic::getScoreToWin() const { return mScoreToWin; } PlayerSide IGameLogic::getServingPlayer() const { return mServingPlayer; } void IGameLogic::setServingPlayer(PlayerSide side) { mServingPlayer = side; } int IGameLogic::getHits(PlayerSide side) const { return mTouches[side2index(side)]; } PlayerSide IGameLogic::getWinningPlayer() const { return mWinningPlayer; } Clock& IGameLogic::getClock() { return clock; } PlayerSide IGameLogic::getLastErrorSide() { PlayerSide t = mLastError; mLastError = NO_PLAYER; /// reset mLastError to NO_PLAYER /// why? return t; } GameLogicState IGameLogic::getState() const { GameLogicState gls; gls.leftScore = getScore(LEFT_PLAYER); gls.rightScore = getScore(RIGHT_PLAYER); gls.servingPlayer = getServingPlayer(); gls.leftSquish = mSquish[LEFT_PLAYER]; gls.rightSquish = mSquish[RIGHT_PLAYER]; return gls; } void IGameLogic::setState(GameLogicState gls) { setScore(LEFT_PLAYER, gls.leftScore); setScore(RIGHT_PLAYER, gls.rightScore); setServingPlayer(gls.servingPlayer); mSquish[LEFT_PLAYER] = gls.leftSquish; mSquish[RIGHT_PLAYER] = gls.rightSquish; } // ------------------------------------------------------------------------------------------------- // Event Handlers // ------------------------------------------------------------------------------------------------- void IGameLogic::step() { clock.step(); if(clock.isRunning()) { --mSquish[0]; --mSquish[1]; } } void IGameLogic::onPause() { /// pausing for now only means stopping the clock clock.stop(); } void IGameLogic::onUnPause() { clock.start(); } void IGameLogic::onBallHitsGround(PlayerSide side) { onError(side); } bool IGameLogic::isCollisionValid(PlayerSide side) const { // check whether the ball is squished return mSquish[side2index(side)] <= 0; } void IGameLogic::onBallHitsPlayer(PlayerSide side) { if(!isCollisionValid(side)) return; // otherwise, set the squish value mSquish[side2index(side)] = SQUISH_TOLERANCE; // now, the other blobby has to accept the new hit! mSquish[side2index(other_side(side))] = 0; // count the touches mTouches[side2index(other_side(side))] = 0; mTouches[side2index(side)]++; if( ! OnBallHitsPlayerHandler(side, mTouches[side2index(side)]) ) { // if a player hits a forth time, it is an error onError(side); } } void IGameLogic::score(PlayerSide side) { ++mScores[side2index(side)]; mTouches[0] = 0; mTouches[1] = 0; mWinningPlayer = checkWin(); } void IGameLogic::reset() { mScores[0] = 0; mScores[1] = 0; mTouches[0] = 0; mTouches[1] = 0; mSquish[0] = 0; mSquish[1] = 0; } void IGameLogic::onError(PlayerSide side) { mLastError = side; mTouches[0] = 0; mTouches[1] = 0; mSquish[0] = 0; mSquish[1] = 0; OnMistake(side); mServingPlayer = other_side(side); } // ------------------------------------------------------------------------------------------------- class LuaGameLogic : public IGameLogic { public: LuaGameLogic(const std::string& file); virtual ~LuaGameLogic(); private: virtual PlayerSide checkWin() const; virtual void OnMistake(PlayerSide side); virtual bool OnBallHitsPlayerHandler(PlayerSide ply, int numOfHits); // lua functions static int luaScore(lua_State* state); static int luaGetOpponent(lua_State* state); static int luaGetServingPlayer(lua_State* state); static int luaGetGameTime(lua_State* state); // lua state lua_State* mState; }; LuaGameLogic::LuaGameLogic( const std::string& filename ) : mState( lua_open() ) { lua_pushlightuserdata(mState, this); lua_setglobal(mState, "__GAME_LOGIC_POINTER"); /// \todo how to push parameters??? /// \todo how to react when mScoreToWin changes? lua_pushnumber(mState, getScoreToWin()); lua_setglobal(mState, "SCORE_TO_WIN"); // add functions lua_register(mState, "score", luaScore); lua_register(mState, "opponent", luaGetOpponent); lua_register(mState, "servingplayer", luaGetServingPlayer); lua_register(mState, "time", luaGetGameTime); // now load script file int error = FileRead::readLuaScript(filename, mState); if (error == 0) error = lua_pcall(mState, 0, 6, 0); //! \todo thats not good, needs a hardcoded fallback ruleset if (error) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; ScriptException except; except.luaerror = lua_tostring(mState, -1); lua_pop(mState, 1); lua_close(mState); throw except; } // check that all functions are available lua_getglobal(mState, "IsWinning"); if (!lua_isfunction(mState, -1)) { std::cerr << "Script Error: Could not find function IsWinning"; std::cerr << std::endl; ScriptException except; except.luaerror = "Could not find function IsWinning"; lua_pop(mState, 1); lua_close(mState); throw except; } lua_getglobal(mState, "OnMistake"); if (!lua_isfunction(mState, -1)) { std::cerr << "Script Error: Could not find function OnMistake"; std::cerr << std::endl; ScriptException except; except.luaerror = "Could not find function OnMistake"; lua_pop(mState, 1); lua_close(mState); throw except; } } LuaGameLogic::~LuaGameLogic() { lua_close(mState); } PlayerSide LuaGameLogic::checkWin() const { bool won = false; lua_getglobal(mState, "IsWinning"); lua_pushnumber(mState, getScore(LEFT_PLAYER) ); lua_pushnumber(mState, getScore(RIGHT_PLAYER) ); if( lua_pcall(mState, 2, 1, 0) ) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; }; won = lua_toboolean(mState, -1); lua_pop(mState, 1); if(won) { if( getScore(LEFT_PLAYER) > getScore(RIGHT_PLAYER) ) return LEFT_PLAYER; if( getScore(LEFT_PLAYER) < getScore(RIGHT_PLAYER) ) return RIGHT_PLAYER; } return NO_PLAYER; } void LuaGameLogic::OnMistake(PlayerSide side) { // call lua scoring rules lua_getglobal(mState, "OnMistake"); lua_pushnumber(mState, side); if(lua_pcall(mState, 1, 0, 0)) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; }; } bool LuaGameLogic::OnBallHitsPlayerHandler(PlayerSide ply, int numOfHits) { bool valid = false; lua_getglobal(mState, "OnBallHitsPlayer"); lua_pushnumber(mState, ply ); lua_pushnumber(mState, numOfHits ); if( lua_pcall(mState, 2, 1, 0) ) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; }; valid = lua_toboolean(mState, -1); lua_pop(mState, 1); return valid; } int LuaGameLogic::luaScore(lua_State* state) { int pl = int(lua_tonumber(state, -1) + 0.5); lua_pop(state, 1); lua_getglobal(state, "__GAME_LOGIC_POINTER"); LuaGameLogic* gl = (LuaGameLogic*)lua_touserdata(state, -1); lua_pop(state, 1); gl->score((PlayerSide)pl); return 0; } int LuaGameLogic::luaGetOpponent(lua_State* state) { int pl = int(lua_tonumber(state, -1) + 0.5); lua_pop(state, 1); lua_pushnumber(state, other_side((PlayerSide)pl)); return 1; } int LuaGameLogic::luaGetServingPlayer(lua_State* state) { lua_getglobal(state, "__GAME_LOGIC_POINTER"); LuaGameLogic* gl = (LuaGameLogic*)lua_touserdata(state, -1); lua_pop(state, 1); lua_pushnumber(state, gl->getServingPlayer()); return 1; } int LuaGameLogic::luaGetGameTime(lua_State* state) { lua_getglobal(state, "__GAME_LOGIC_POINTER"); LuaGameLogic* gl = (LuaGameLogic*)lua_touserdata(state, -1); lua_pop(state, 1); lua_pushnumber(state, gl->getClock().getTime()); return 1; } class FallbackGameLogic : public IGameLogic { public: FallbackGameLogic() { } virtual ~FallbackGameLogic() { } private: virtual PlayerSide checkWin() const { if( getScore(LEFT_PLAYER) >= getScoreToWin() ) { return LEFT_PLAYER; } if( getScore(RIGHT_PLAYER) >= getScoreToWin() ) { return RIGHT_PLAYER; } return NO_PLAYER; } virtual void OnMistake(PlayerSide side) { score( other_side(side) ); } bool OnBallHitsPlayerHandler(PlayerSide ply, int numOfHits) { return numOfHits <= 3; } }; GameLogic createGameLogic(const std::string& file) { try { return std::auto_ptr( new LuaGameLogic(file) ); } catch(...) { std::cerr << "Script Error: Could not create LuaGameLogic"; std::cerr << std::endl; std::cerr << " Using fallback ruleset"; std::cerr << std::endl; return std::auto_ptr(new FallbackGameLogic()); } } blobby-1.0rc3/src/RenderManagerGL2D.h0000644000175000017500000000765012042452374020641 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #if HAVE_LIBGL #include #if defined(WIN32) #include #endif #if __MACOSX__ #include #include #else #include #include #endif #include #include #include #include "RenderManager.h" /*! \class RenderManagerGL2D \brief RenderManager on top of OpenGL \details This render manager uses OpenGL for drawing, SDL is only used for loading the images. */ class RenderManagerGL2D : public RenderManager { public: RenderManagerGL2D(); virtual void init(int xResolution, int yResolution, bool fullscreen); virtual void deinit(); virtual void draw(); virtual void refresh(); virtual bool setBackground(const std::string& filename); virtual void setBlobColor(int player, Color color); virtual void showShadow(bool shadow); virtual void setBall(const Vector2& position, float rotation); virtual void setBlob(int player, const Vector2& position, float animationState); virtual void setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning); virtual void setPlayernames(std::string leftName, std::string rightName); virtual void setTime(const std::string& t); virtual void drawText(const std::string& text, Vector2 position, unsigned int flags = TF_NORMAL); virtual void drawImage(const std::string& filename, Vector2 position); virtual void drawOverlay(float opacity, Vector2 pos1, Vector2 pos2, Color col); virtual void drawBlob(const Vector2& pos, const Color& col); virtual void startDrawParticles(); virtual void drawParticle(const Vector2& pos, int player); virtual void endDrawParticles(); private: struct Texture { float indices[8]; float w, h ; GLuint texture; Texture( GLuint tex, int x, int y, int w, int h, int tw, int th ); }; GLuint mBackground; GLuint mBallShadow; std::vector mBall; std::vector mBlob; std::vector mBlobSpecular; std::vector mBlobShadow; std::vector mFont; std::vector mHighlightFont; std::vector mSmallFont; std::vector mHighlightSmallFont; GLuint mParticle; GLuint mScroll; std::list mLastBallStates; Vector2 mBallPosition; float mBallRotation; Vector2 mLeftBlobPosition; float mLeftBlobAnimationState; Vector2 mRightBlobPosition; float mRightBlobAnimationState; bool mShowShadow; int mLeftPlayerScore; int mRightPlayerScore; bool mLeftPlayerWarning; bool mRightPlayerWarning; std::string mLeftPlayerName; std::string mRightPlayerName; std::string mTime; Color mLeftBlobColor; Color mRightBlobColor; void drawQuad(float x, float y, float width, float height); void drawQuad(float x, float y, const Texture& tex); GLuint loadTexture(SDL_Surface* surface, bool specular); int getNextPOT(int npot); void glEnable(unsigned int flag); void glDisable(unsigned int flag); void glBindTexture(GLuint texture); GLuint mCurrentTexture; std::set mCurrentFlags; }; #endif blobby-1.0rc3/src/Player.cpp0000644000175000017500000000642112042452374017300 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "Player.h" /* includes */ #include // for cerr #include "LocalInputSource.h" #include "ScriptedInputSource.h" #include "UserConfig.h" /* implementation */ Player::Player(PlayerSide side) : mInitialised(false), mPlayerSide(side) { } Player::~Player() { } void Player::loadFromConfig(const std::string& prefix, bool initInput) { UserConfig gameConfig; gameConfig.loadFile("config.xml"); // init local input if(initInput) { try { // these operations may throw, i.e., when the script is not found (should not happen) // or has errors if (gameConfig.getBool(prefix + "_player_human")) { mInputSource.reset(new LocalInputSource(mPlayerSide)); } else { mInputSource.reset(new ScriptedInputSource("scripts/" + gameConfig.getString(prefix + "_script_name"), mPlayerSide, gameConfig.getInteger(prefix + "_script_strength"))); } } catch (std::exception& e) { /// \todo REWORK ERROR REPORTING std::cerr << e.what() << std::endl; mInputSource.reset(new DummyInputSource()); } mName = gameConfig.getBool(prefix + "_player_human") ? gameConfig.getString(prefix + "_player_name") : gameConfig.getString(prefix + "_script_name"); } else { // input is set externally (network) // so we need not to create any input source mInputSource.reset(); // don't use bot name if extern input is used mName = gameConfig.getString(prefix + "_player_name"); } mStaticColor = Color( gameConfig.getInteger(prefix + "_blobby_color_r"), gameConfig.getInteger(prefix + "_blobby_color_g"), gameConfig.getInteger(prefix + "_blobby_color_b")); mOscillating = gameConfig.getBool(prefix + "_blobby_oscillate"); mInitialised = true; } Color Player::getColor() const { assert(mInitialised); if (!mOscillating) { return mStaticColor; } else { float time = float(SDL_GetTicks()) / 1000.0; return Color( int((sin(time*2) + 1.0) * 128), int((sin(time*4) + 1.0) * 128), int((sin(time*3) + 1.0) * 128)); } } void Player::setColor(Color ncol) { mStaticColor = ncol; } std::string Player::getName() const { assert(mInitialised); return mName; } void Player::setName(const std::string& name) { mName = name; } InputSource* Player::getInputSource() const { assert(mInitialised); assert(mInputSource != 0); return mInputSource.get(); } blobby-1.0rc3/src/FileWrite.h0000644000175000017500000001024112042452374017376 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "File.h" /** \class FileWrite \brief File interface extesion for writing. \details This class provides methods for writing various data to files. Whenever a file is opened for writing, all its previous content is erased, or, in case it did not exist beforehand, the file is created. \sa FileRead */ class FileWrite : public File { public: /// \brief default ctor /// \details File has to be opended with open() /// \throw nothing explicit FileWrite(); /// \brief constructor which opens a file. /// \param filename File to be opened for writing /// \param no_override Set to true if you want to forbid writing over existing file. /// \throw FileLoadException, if the file could not be loaded /// \throw FileAlreadyExistsException in case of trying to write over existing file with no_override = true FileWrite(const std::string& filename, bool no_override = false); /// \brief opens a file. /// \param filename File to be opened for writing /// \param no_override Set to true if you want to forbid writing over existing file. /// \throw FileLoadException, if the file could not be created /// \throw FileAlreadyExistsException in case of trying to write over existing file with no_override = true /// \pre No file is currently opened. void open(const std::string& filename, bool no_override = false); /// destructor, closes the file (if any open) /// \sa close() /// \throw nothing ~FileWrite(); // ------------------------------------ // writing interface // ------------------------------------ /// \brief writes one character /// \details writes exactly the one character supplied. /// \throw PhysfsFileException when Physfs reports an error. /// \throw NoFileOpenedException when called while no file is opened. void writeByte(char c); /// \brief writes 32 bit integer /// \details writes an integer, converted to little endian if necessary /// \throw PhysfsFileException when Physfs reports an error. /// \throw NoFileOpenedException when called while no file is opened. void writeUInt32(uint32_t v); /// \brief writes a single precision float /// \throw PhysfsException when Physfs reports an error. /// \throw NoFileOpenedException when called while no file is opened. void writeFloat(float fl); /// \brief writes a std::string /// \details writes the content of the string to the file. /// does not write a null-termination character /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. void write(const std::string& data); /// \brief writes null-terminated std::string /// \details writes the content of the string to the file. /// ends the string with a null terminator. /// \throw NoFileOpenedException when called while no file is opened. /// \throw PhysfsFileException when Physfs reports an error void writeNullTerminated(const std::string& data); /// \brief writes a sequence of characters /// \details writes \p length characters from \p data to the file /// \throw PhysfsFileException when Physfs reports an error void write(const char* data, std::size_t length); }; blobby-1.0rc3/src/doxyfile0000644000175000017500000020662012042452374017111 0ustar danielknobedanielknobe# Doxyfile 1.7.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Blobby Volley Lua API" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = "beta 0.9" # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = lua_api_doc # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = YES # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES blobby-1.0rc3/src/SpeedController.cpp0000644000175000017500000000610712042452374021151 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "SpeedController.h" /* includes */ #include #include /* implementation */ /// this is required to reduce rounding errors. now we have a resolution of /// 1s. This is much better than SDL_Delay can handle, but we prevent /// accumulation errors. const int PRECISION_FACTOR = 1000; SpeedController* SpeedController::mMainInstance = NULL; SpeedController::SpeedController(float gameFPS) { mGameFPS = gameFPS; mFramedrop = false; mDrawFPS = true; mFPSCounter = 0; mOldTicks = SDL_GetTicks(); mFPS = 0; mBeginSecond = mOldTicks; mCounter = 0; } SpeedController::~SpeedController() { } void SpeedController::setGameSpeed(float fps) { if (fps < 5) fps = 5; mGameFPS = fps; /// \todo maybe we should reset only if speed changed? mBeginSecond = mOldTicks; mCounter = 0; } bool SpeedController::doFramedrop() const { return mFramedrop; } void SpeedController::update() { int rateTicks = std::max( static_cast(PRECISION_FACTOR * 1000 / mGameFPS), 1); static int lastTicks = SDL_GetTicks(); if (mCounter == mGameFPS) { const int delta = SDL_GetTicks() - mBeginSecond; int wait = 1000 - delta; if (wait > 0) SDL_Delay(wait); } if (mBeginSecond + 1000 <= SDL_GetTicks()) { mBeginSecond = SDL_GetTicks(); mCounter = 0; } const int delta = SDL_GetTicks() - mBeginSecond; if ( (PRECISION_FACTOR * delta) / rateTicks <= mCounter) { int wait = ((mCounter+1)*rateTicks/PRECISION_FACTOR) - delta; if (wait > 0) SDL_Delay(wait); } // do we need framedrop? // if passed time > time when we should have drawn next frame // maybe we should limit the number of consecutive framedrops? // for now: we can't do a framedrop if we did a framedrop last frame if ( delta * PRECISION_FACTOR > rateTicks * (mCounter + 1) && !mFramedrop) { mFramedrop = true; } else mFramedrop = false; mCounter++; //calculate the FPS of drawn frames: if (mDrawFPS) { if (lastTicks >= mOldTicks + 1000) { mOldTicks = lastTicks; mFPS = mFPSCounter; mFPSCounter = 0; } if (!mFramedrop) mFPSCounter++; } //update for next call: lastTicks = SDL_GetTicks(); } blobby-1.0rc3/src/UserConfig.cpp0000644000175000017500000001316012042452374020106 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "UserConfig.h" /* includes */ #include #include #include "tinyxml/tinyxml.h" #include "Global.h" #include "FileRead.h" #include "FileWrite.h" /* implementation */ std::map > userConfigCache; boost::shared_ptr IUserConfigReader::createUserConfigReader(const std::string& file) { // if we have this userconfig already cached, just return from cache std::map >:: iterator cfg_cached = userConfigCache.find(file); if( cfg_cached != userConfigCache.end() ) { return cfg_cached->second; } // otherwise, load user config... UserConfig* uc = new UserConfig(); uc->loadFile(file); boost::shared_ptr config(uc); // ... and add to cache userConfigCache[file] = config; return config; } bool UserConfig::loadFile(const std::string& filename) { boost::shared_ptr configDoc = FileRead::readXMLDocument(filename); if (configDoc->Error()) { std::cerr << "Warning: Parse error in " << filename; std::cerr << "!" << std::endl; } TiXmlElement* userConfigElem = configDoc->FirstChildElement("userconfig"); if (userConfigElem == NULL) return false; for (TiXmlElement* varElem = userConfigElem->FirstChildElement("var"); varElem != NULL; varElem = varElem->NextSiblingElement("var")) { std::string name, value; const char* c; c = varElem->Attribute("name"); if (c) name = c; c = varElem->Attribute("value"); if (c) value = c; createVar(name, value); } return true; } bool UserConfig::saveFile(const std::string& filename) const { // this trows an exception if the file could not be opened for writing FileWrite file(filename); const std::string xmlHeader = "\n\n\n"; const std::string xmlFooter = "\n\n"; file.write(xmlHeader); for (unsigned int i = 0; i < mVars.size(); ++i) { char writeBuffer[256]; int charsWritten = snprintf(writeBuffer, 256, "\t\n", mVars[i]->Name.c_str(), mVars[i]->Value.c_str()); file.write(writeBuffer, charsWritten); } file.write(xmlFooter); file.close(); // we have to make sure that we don't cache any outdated user configs std::map >:: iterator cfg_cached = userConfigCache.find(filename); if( cfg_cached != userConfigCache.end() ) { userConfigCache.erase(cfg_cached); } return true; } float UserConfig::getFloat(const std::string& name) const { return atof(getValue(name).c_str()); } std::string UserConfig::getString(const std::string& name) const { return getValue(name); } bool UserConfig::getBool(const std::string& name) const { return (getValue(name) == "true") ? true : false; } int UserConfig::getInteger(const std::string& name) const { return atoi(getValue(name).c_str()); } void UserConfig::setFloat(const std::string& name, float var) { char writeBuffer[256]; snprintf(writeBuffer, 256, "%f", var); setValue(name, writeBuffer); } void UserConfig::setString(const std::string& name, const std::string& var) { setValue(name, var); } void UserConfig::setBool(const std::string& name, bool var) { setValue(name, var ? "true" : "false"); } void UserConfig::setInteger(const std::string& name, int var) { char writeBuffer[256]; snprintf(writeBuffer, 256, "%d", var); setValue(name, writeBuffer); } UserConfigVar* UserConfig::createVar(const std::string& name, const std::string& defaultValue) { if (findVarByName(name)) return NULL; UserConfigVar *var = new UserConfigVar; var->Name = name; var->DefaultValue = var->Value = defaultValue; mVars.push_back(var); return var; } void UserConfig::setValue(const std::string& name, const std::string& value) { UserConfigVar *var = findVarByName(name); if (!var) { std::cerr << "Warning: impossible to set value of " << "unknown configuration variable " << name << "\n Creating new variable" << std::endl; var = createVar(name, value); mChangeFlag = true; return; } if (var->Value != value) mChangeFlag = true; var->Value = value; } std::string UserConfig::getValue(const std::string& name) const { UserConfigVar *var = findVarByName(name); if (!var) { std::cerr << "Warning: impossible to get value of " << "unknown configuration variable " << name << std::endl; return ""; } return var->Value; } UserConfig::~UserConfig() { for (unsigned int i = 0; i < mVars.size(); ++i) delete mVars[i]; } UserConfigVar* UserConfig::findVarByName(const std::string& name) const { for (unsigned int i = 0; i < mVars.size(); ++i) if (mVars[i]->Name == name) return mVars[i]; return NULL; } blobby-1.0rc3/src/GenericIO.h0000644000175000017500000002067512042452374017324 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include #include #include "GenericIOFwd.h" #include "GenericIODetail.h" #include "Global.h" // forward declarations class FileWrite; class FileRead; namespace RakNet { class BitStream; } // Factory functions /// creates a generic writer that writes to a file boost::shared_ptr< GenericOut > createGenericWriter(boost::shared_ptr file); /// creates a generic writer that writes to a BitStream boost::shared_ptr< GenericOut > createGenericWriter(boost::shared_ptr stream); /// creates a generic writer that writes hman readable to a stream /// currently, there is no corresponding reader because this is mostly for debugging purposes boost::shared_ptr< GenericOut > createGenericWriter(std::ostream& stream); /// creates a generic reader that reads from a file boost::shared_ptr< GenericIn > createGenericReader(boost::shared_ptr file); /// creates a generic reader that reads from a BitStream boost::shared_ptr< GenericIn > createGenericReader(boost::shared_ptr stream); // GenericIO class template /*! \class GenericIO \brief Class template that abstracts IO \details This class abstract IO to/from different sources. Current implementations are File and BitStream input/output. The template parameter tag decides wether the actual instance is an input or an output object. This design ensure that input and output have exactly the same interface and enables writing algorithms that read and write data with exactly the same code, reducing the chance of errors. This class derives from boost::noncopyable, which seems a reasonable choice for these IO classes. Having different GenericIO objects which read/write from/to the same source/target just makes things more complicated and error prone. */ template class GenericIO : public boost::noncopyable { public: /// virtual d'tor to ensure correct cleanup virtual ~GenericIO() { }; /// reads/writes one byte virtual void byte ( typename detail::conster::type data) = 0; /// reads/writes one boolean. virtual void boolean( typename detail::conster::type data) = 0; /// reads/writes on 32 bit unsigned integer virtual void uint32( typename detail::conster::type data) = 0; /// reads/writes a floating point number virtual void number( typename detail::conster::type data) = 0; /// reads/writes a character string. virtual void string( typename detail::conster::type string) = 0; /// reads/writes a character array of certain length virtual void array ( typename detail::conster::type data, unsigned int length) = 0; /// returns the current read/write position virtual unsigned int tell() const = 0; /// sets the current read/write position /// \attention Use for pos only values you have received /// from a prior call to tell of the same instance /// of GenericIO as these positions are not /// guaranteed to match for different source/target /// types. virtual void seek(unsigned int pos) const = 0; // generic implementation /// this is a nonvirtual generic function which can be used to write or read arbitrary (supported) /// types. these types are serialized using the methods above for the primitive types. /// supported values for \p T are all the basic types which can be written directly, /// PlayerInput, PlayerSide and Color. If T can be serialized, this function can serialize /// containers of T provided the have the following methods: /// * begin() /// * end() /// * size() /// * resize() /// Additional user types can be serialized if the user defines the appropriate methods /// in UserSerializer. template void generic( typename detail::conster::type data ) { // thats a rather complicated template construct. It uses the serialize_dispatch template // with working type T, boost::true_type or boost::false_type as init depending wether the // supplied type T has a default implementation associated (as determined by the // has_default_io_implementation template). // the second parameter is a bool which is true if the type offeres a container interface // and false otherwise (determined by the is_container_type template) // depending on the second two template parameters, serialize_dispatch is either // derived from detail::predifined_serializer (when init is boost::true_type) // or UserSerializer if init is boost::false_type and container is false. // if it is a container type, the partial template specialisation foudn below is // used to serialize that template. detail::serialize_dispatch::type, detail::is_container_type::value >::serialize(*this, data); } typedef tag tag_type; }; /*! \def USER_SERIALIZER_IMPLEMENTATION_HELPER \brief Helper macro for autogenerated user serializers \details use like this: \code USER_SERIALIZER_IMPLEMENTATION_HELPER( \p type ) { generic serialisation algorithm for both input and output. Variable \p io contains the GenericIO object, variable value the \p type object. } \endcode remember to use generic\< \p type\> like this: \code io.template generic\< \p type\>(value) \endcode otherwise, the compiler won't recognise generic as a template function. \example USER_SERIALIZER_IMPLEMENTATION_HELPER(int) { io.uint32(value); } */ #define USER_SERIALIZER_IMPLEMENTATION_HELPER( UD_TYPE ) \ template \ void doSerialize##UD_TYPE(GenericIO&, typename detail::conster::type value); \ template<> \ void UserSerializer::serialize( GenericOut& out, const UD_TYPE& value) \ { \ doSerialize##UD_TYPE(out, value); \ } \ template<> \ void UserSerializer::serialize( GenericIn& in, UD_TYPE& value) \ { \ doSerialize##UD_TYPE(in, value); \ } \ template \ void doSerialize##UD_TYPE(GenericIO& io, typename detail::conster::type value) // ------------------------------------------------------------------------------------------------- // Implementation detail // ------------------------------------------------------------------------------------------------- namespace detail { // serialisation algorithm for container types: // read/write size of the container. // if reading, resize container to fit // iterate over all elements and read/write template struct serialize_dispatch { static void serialize( GenericOut& out, const T& list) { out.uint32( list.size() ); for(typename T::const_iterator i = list.begin(); i != list.end(); ++i) { out.generic( *i ); } } static void serialize( GenericIn& in, T& list) { unsigned int size; in.uint32( size ); list.resize( size ); for(typename T::iterator i = list.begin(); i != list.end(); ++i) { in.generic( *i ); } } }; } blobby-1.0rc3/src/ReplayPlayer.cpp0000644000175000017500000001022612042452374020453 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "ReplayPlayer.h" /* includes */ #include #include "IReplayLoader.h" #include "DuelMatch.h" /* implementation */ ReplayPlayer::ReplayPlayer() { } ReplayPlayer::~ReplayPlayer() { } bool ReplayPlayer::endOfFile() const { return (mPosition >= mLength); } void ReplayPlayer::load(const std::string& filename) { loader.reset(IReplayLoader::createReplayLoader(filename)); mPlayerNames[LEFT_PLAYER] = loader->getPlayerName(LEFT_PLAYER); mPlayerNames[RIGHT_PLAYER] = loader->getPlayerName(RIGHT_PLAYER); mPosition = 0; mLength = loader->getLength(); } std::string ReplayPlayer::getPlayerName(const PlayerSide side) const { return mPlayerNames[side]; } Color ReplayPlayer::getBlobColor(const PlayerSide side) const { return loader->getBlobColor(side); } int ReplayPlayer::getGameSpeed() const { return loader->getSpeed(); } float ReplayPlayer::getPlayProgress() const { return (float)mPosition / mLength; } int ReplayPlayer::getReplayPosition() const { return mPosition; } int ReplayPlayer::getReplayLength() const { return mLength; } bool ReplayPlayer::play(DuelMatch* virtual_match) { mPosition++; if( mPosition < mLength ) { PlayerInput left; PlayerInput right; loader->getInputAt(mPosition, left, right); virtual_match->setPlayersInput(left, right); virtual_match->step(); /* int point; if(loader->isSavePoint(mPosition, point)) { ReplaySavePoint reference; loader->readSavePoint(point, reference); DuelMatchState current = virtual_match->getState(); assert(reference.state == current); }*/ // everything was as expected return true; } // error or end of file return false; } bool ReplayPlayer::gotoPlayingPosition(int rep_position, DuelMatch* virtual_match) { /// \todo add validity check for rep_position /// \todo replay clock does not work! // find next safepoint int save_position = -1; int savepoint = loader->getSavePoint(rep_position, save_position); // save position contains game step at which the save point is // savepoint is index of save point in array // now compare safepoint and actual position // if we have to forward and save_position is nearer than current position, jump if( (rep_position < mPosition || save_position > mPosition) && savepoint >= 0) { // can't use mPosition // set match to safepoint ReplaySavePoint state; loader->readSavePoint(savepoint, state); // set position and update match mPosition = save_position; virtual_match->setState(state.state); } // otherwise, use current position // this is legacy code which will make fast forwarding possible even // when we have no safepoint and have to go back if(rep_position < mPosition) { // reset the match and simulate from start! virtual_match->reset(); mPosition = 0; } // in the end, simulate the remaining steps // maximum: 100 steps for(int i = 0; i < 100; ++i) { // check if we have to do another step if(endOfFile() || rep_position == mPosition) return true; // do one play step play(virtual_match); } return false; } blobby-1.0rc3/src/IMGUI.cpp0000644000175000017500000005543112042452374016723 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "IMGUI.h" /* includes */ #include #include #include /* implementation */ enum ObjectType { IMAGE, OVERLAY, TEXT, BUTTON, // Unused! SCROLLBAR, ACTIVESCROLLBAR, EDITBOX, ACTIVEEDITBOX, SELECTBOX, ACTIVESELECTBOX, BLOB, CHAT }; struct QueueObject { ObjectType type; int id; Vector2 pos1; Vector2 pos2; Color col; std::string text; std::vector entries; int selected; int length; unsigned int flags; }; typedef std::queue RenderQueue; IMGUI* IMGUI::mSingleton = 0; RenderQueue *mQueue; IMGUI::IMGUI() { mQueue = new RenderQueue; mActiveButton = -1; mHeldWidget = 0; mLastKeyAction = NONE; mLastWidget = 0; mButtonReset = false; mInactive = false; } IMGUI::~IMGUI() { delete mQueue; } IMGUI& IMGUI::getSingleton() { if (!mSingleton) mSingleton = new IMGUI; return *mSingleton; } void IMGUI::begin() { mUsingCursor = false; mButtonReset = false; while (!mQueue->empty()) mQueue->pop(); mLastKeyAction = NONE; if (InputManager::getSingleton()->up()) mLastKeyAction = UP; if (InputManager::getSingleton()->down()) mLastKeyAction = DOWN; if (InputManager::getSingleton()->left()) mLastKeyAction = LEFT; if (InputManager::getSingleton()->right()) mLastKeyAction = RIGHT; if (InputManager::getSingleton()->select()) mLastKeyAction = SELECT; } void IMGUI::end() { int FontSize; RenderManager& rmanager = RenderManager::getSingleton(); while (!mQueue->empty()) { QueueObject& obj = mQueue->front(); switch (obj.type) { case IMAGE: rmanager.drawImage(obj.text, obj.pos1); break; case OVERLAY: rmanager.drawOverlay(0.65, obj.pos1, obj.pos2, obj.col); break; case TEXT: rmanager.drawText(obj.text, obj.pos1, obj.flags); break; case SCROLLBAR: rmanager.drawOverlay(0.5, obj.pos1, obj.pos1 + Vector2(210.0, 26.0)); rmanager.drawImage("gfx/scrollbar.bmp",obj.pos1 + Vector2(obj.pos2.x * 200.0 + 5 , 13)); break; case ACTIVESCROLLBAR: rmanager.drawOverlay(0.4, obj.pos1, obj.pos1 + Vector2(210.0, 26.0)); rmanager.drawImage("gfx/scrollbar.bmp",obj.pos1 + Vector2(obj.pos2.x * 200.0 + 5 , 13)); break; case EDITBOX: FontSize = (obj.flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL); rmanager.drawOverlay(0.5, obj.pos1, obj.pos1 + Vector2(10+obj.length*FontSize, 10+FontSize)); rmanager.drawText(obj.text, obj.pos1+Vector2(5, 5), obj.flags); break; case ACTIVEEDITBOX: FontSize = (obj.flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL); rmanager.drawOverlay(0.3, obj.pos1, obj.pos1 + Vector2(10+obj.length*FontSize, 10+FontSize)); rmanager.drawText(obj.text, obj.pos1+Vector2(5, 5), obj.flags); if (obj.pos2.x >= 0) rmanager.drawOverlay(1.0, Vector2((obj.pos2.x)*FontSize+obj.pos1.x+5, obj.pos1.y+5), Vector2((obj.pos2.x)*FontSize+obj.pos1.x+5+3, obj.pos1.y+5+FontSize), Color(255,255,255)); break; case SELECTBOX: FontSize = (obj.flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL)); rmanager.drawOverlay(0.5, obj.pos1, obj.pos2); for (unsigned int c = 0; c < obj.entries.size(); c++) { if(c == obj.selected) rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags | TF_HIGHLIGHT); else rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags); } break; case ACTIVESELECTBOX: FontSize = (obj.flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL)); rmanager.drawOverlay(0.3, obj.pos1, obj.pos2); for (unsigned int c = 0; c < obj.entries.size(); c++) { if(c == obj.selected) rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags | TF_HIGHLIGHT); else rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags); } break; case CHAT: FontSize = (obj.flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL)); rmanager.drawOverlay(0.5, obj.pos1, obj.pos2); for (unsigned int c = 0; c < obj.entries.size(); c++) { if (obj.text[c] == 'R' ) rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags | TF_HIGHLIGHT); else rmanager.drawText(obj.entries[c], Vector2(obj.pos1.x+5, obj.pos1.y+(c*FontSize)+5), obj.flags); } break; case BLOB: rmanager.drawBlob(obj.pos1, obj.col); break; default: break; } mQueue->pop(); } if (mDrawCursor) { rmanager.drawImage("gfx/cursor.bmp", InputManager::getSingleton()->position() + Vector2(24.0, 24.0)); mDrawCursor = false; } } void IMGUI::doImage(int id, const Vector2& position, const std::string& name) { QueueObject obj; obj.type = IMAGE; obj.id = id; obj.pos1 = position; obj.text = name; mQueue->push(obj); } void IMGUI::doText(int id, const Vector2& position, const std::string& text, unsigned int flags) { QueueObject obj; obj.type = TEXT; obj.id = id; obj.pos1 = position; obj.text = text; obj.flags = flags; mQueue->push(obj); } void IMGUI::doText(int id, const Vector2& position, TextManager::STRING text, unsigned int flags) { doText(id, position, TextManager::getSingleton()->getString(text), flags); } void IMGUI::doOverlay(int id, const Vector2& pos1, const Vector2& pos2, const Color& col) { QueueObject obj; obj.type = OVERLAY; obj.id = id; obj.pos1 = pos1; obj.pos2 = pos2; obj.col = col; mQueue->push(obj); RenderManager::getSingleton().redraw(); } bool IMGUI::doButton(int id, const Vector2& position, TextManager::STRING text, unsigned int flags) { return doButton(id, position, TextManager::getSingleton()->getString(text), flags); } bool IMGUI::doButton(int id, const Vector2& position, const std::string& text, unsigned int flags) { bool clicked = false; QueueObject obj; obj.id = id; obj.pos1 = position; obj.text = text; obj.type = TEXT; obj.flags = flags; if (!mInactive) { // M.W. : Activate cursorless object-highlighting once the up or down key is pressed. if (mActiveButton == -1) { switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; default: break; } } // Highlight first menu object for arrow key navigation. if (mActiveButton == 0 && !mButtonReset) mActiveButton = id; // React to keyboard input. if (id == mActiveButton) { obj.flags = obj.flags | TF_HIGHLIGHT; switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; case SELECT: clicked = true; mLastKeyAction = NONE; break; default: break; } } // React to mouse input. Vector2 mousepos = InputManager::getSingleton()->position(); if (mousepos.x > position.x && mousepos.y > position.y && mousepos.x < position.x + text.length() * (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL) && mousepos.y < position.y + (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL)) { obj.flags = obj.flags | TF_HIGHLIGHT; if (InputManager::getSingleton()->click()) { clicked = true; mActiveButton = id; } } } mLastWidget = id; mQueue->push(obj); return clicked; } bool IMGUI::doScrollbar(int id, const Vector2& position, float& value) { QueueObject obj; obj.id = id; obj.pos1 = position; obj.type = SCROLLBAR; bool deselected = false; if (InputManager::getSingleton()->unclick()) { if (id == mHeldWidget) deselected = true; mHeldWidget = 0; } if (!mInactive) { // M.W. : Activate cursorless object-highlighting once the up or down key is pressed. if (mActiveButton == -1) { switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; default: break; } } // Highlight first menu object for arrow key navigation. if (mActiveButton == 0 && !mButtonReset) mActiveButton = id; // React to keyboard input. if (id == mActiveButton) { obj.type = ACTIVESCROLLBAR; switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; case LEFT: value -= 0.1; mLastKeyAction = NONE; break; case RIGHT: value += 0.1; mLastKeyAction = NONE; break; default: break; } } // React to mouse input. Vector2 mousepos = InputManager::getSingleton()->position(); if (mousepos.x + 5 > position.x && mousepos.y > position.y && mousepos.x < position.x + 205 && mousepos.y < position.y + 24.0) { obj.type = ACTIVESCROLLBAR; if (InputManager::getSingleton()->click()) { mHeldWidget = id; } if (mHeldWidget == id) { value = (mousepos.x - position.x) / 200.0; mActiveButton = id; } if(InputManager::getSingleton()->mouseWheelUp()) value += 0.1; if(InputManager::getSingleton()->mouseWheelDown()) value -= 0.1; } } value = value > 0.0 ? (value < 1.0 ? value : 1.0) : 0.0; obj.pos2.x = value; mLastWidget = id; mQueue->push(obj); return deselected; } void IMGUI::resetSelection() { mInactive = false; mActiveButton = -1; mButtonReset = true; } bool IMGUI::doEditbox(int id, const Vector2& position, int length, std::string& text, unsigned& cpos, unsigned int flags, bool force_active) { int FontSize = (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL); bool changed = false; QueueObject obj; obj.id = id; obj.pos1 = position; obj.type = EDITBOX; obj.length = length; // lenght does not actually work! obj.flags = flags; // React to mouse input. Vector2 mousepos = InputManager::getSingleton()->position(); if (mousepos.x > position.x && mousepos.y > position.y && mousepos.x < position.x + length * FontSize + 10 && mousepos.y < position.y + FontSize + 10) { obj.flags = obj.flags | TF_HIGHLIGHT; if (InputManager::getSingleton()->click()) { // Handle click on the text. if (mousepos.x < position.x + text.length() * FontSize) cpos = (int) ((mousepos.x-position.x-5+(FontSize/2)) / FontSize); // Handle click behind the text. else if (mousepos.x < position.x + length * FontSize + 10) cpos = (int) text.length(); mActiveButton = id; } } if (!mInactive) { // M.W. : Activate cursorless object-highlighting once the up or down key is pressed. if (mActiveButton == -1) { switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; default: break; } // M.W. : Initialize the cursor position at the end of the string. // IMPORTANT: If you make changes to EditBox text that alter the length // of the text, either call resetSelection() to come back // to this area of code or update cpos manually to prevent // crashes due to a misplaced cursor. cpos = text.length(); } // Highlight first menu object for arrow key navigation. if (mActiveButton == 0 && !mButtonReset) mActiveButton = id; // React to keyboard input. if (id == mActiveButton || force_active) { obj.type = ACTIVEEDITBOX; switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; case LEFT: if (cpos > 0) cpos--; mLastKeyAction = NONE; break; case RIGHT: if (cpos < text.length()) cpos++; mLastKeyAction = NONE; break; default: break; } std::string input = InputManager::getSingleton()->getLastTextKey(); if (input == "backspace" && text.length() > 0 && cpos > 0) { text.erase(cpos - 1, 1); cpos--; } else if (input == "del" && text.length() > cpos) { text.erase(cpos, 1); } else if (input == "return") { // Workarround for chatwindow! Delete this after GUI-Rework changed = true; } // This is a temporary solution until the new // UTF-8 class can tell the real length!!! else if (text.length() < length) { if (input == "space") { text.insert(cpos, " "); cpos++; changed = true; } else if (input == "keypad0") { text.insert(cpos, "0"); cpos++; changed = true; } else if (input == "keypad1") { text.insert(cpos, "1"); cpos++; changed = true; } else if (input == "keypad2") { text.insert(cpos, "2"); cpos++; changed = true; } else if (input == "keypad3") { text.insert(cpos, "3"); cpos++; changed = true; } else if (input == "keypad4") { text.insert(cpos, "4"); cpos++; changed = true; } else if (input == "keypad5") { text.insert(cpos, "5"); cpos++; changed = true; } else if (input == "keypad6") { text.insert(cpos, "6"); cpos++; changed = true; } else if (input == "keypad7") { text.insert(cpos, "7"); cpos++; changed = true; } else if (input == "keypad8") { text.insert(cpos, "8"); cpos++; changed = true; } else if (input == "keypad9") { text.insert(cpos, "9"); cpos++; changed = true; } else if (input.length() == 1) { text.insert(cpos, input); cpos++; changed = true; } } } } obj.pos2.x = SDL_GetTicks() % 1000 >= 500 ? cpos : -1.0; obj.text = text; mLastWidget = id; mQueue->push(obj); // when content changed, it is active // part of chat window hack if( changed && force_active ) mActiveButton = id; return changed; } SelectBoxAction IMGUI::doSelectbox(int id, const Vector2& pos1, const Vector2& pos2, const std::vector& entries, int& selected, unsigned int flags) { int FontSize = (flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL)); SelectBoxAction changed = SBA_NONE; QueueObject obj; obj.id = id; obj.pos1 = pos1; obj.pos2 = pos2; obj.type = SELECTBOX; obj.flags = flags; const int itemsPerPage = int(pos2.y - pos1.y - 10) / FontSize; int first = (int)(selected / itemsPerPage)*itemsPerPage; //the first visible element in the list if (!mInactive) { // M.W. : Activate cursorless object-highlighting once the up or down key is pressed. if (mActiveButton == -1) { switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; default: break; } } // Highlight first menu object for arrow key navigation. if (mActiveButton == 0 && !mButtonReset) mActiveButton = id; // React to keyboard input. if (id == mActiveButton) { obj.type = ACTIVESELECTBOX; switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; case LEFT: if (selected > 0) { selected--; changed = SBA_SELECT; } mLastKeyAction = NONE; break; case RIGHT: if (selected < entries.size()-1) { selected++; changed = SBA_SELECT; } mLastKeyAction = NONE; break; default: break; } } // React to mouse input. Vector2 mousepos = InputManager::getSingleton()->position(); if (mousepos.x > pos1.x && mousepos.y > pos1.y && mousepos.x < pos2.x && mousepos.y < pos2.y) { obj.type = ACTIVESELECTBOX; if (InputManager::getSingleton()->click()) mActiveButton = id; } //entries mouseclick: if (mousepos.x > pos1.x && mousepos.y > pos1.y+5 && mousepos.x < pos2.x-35 && mousepos.y < pos1.y+5+FontSize*itemsPerPage) { if (InputManager::getSingleton()->click()) { int tmp = (int)((mousepos.y-pos1.y-5) / FontSize)+first; /// \todo well, it's not really a doulbe click... /// we need to do this in inputmanager if( selected == tmp && InputManager::getSingleton()->doubleClick() ) changed = SBA_DBL_CLICK; if (tmp < entries.size()) selected = tmp; mActiveButton = id; } if ((InputManager::getSingleton()->mouseWheelUp()) && (selected > 0)) { selected--; changed = SBA_SELECT; } if ((InputManager::getSingleton()->mouseWheelDown()) && (selected < entries.size()-1)) { selected++; changed = SBA_SELECT; } } //arrows mouseclick: if (mousepos.x > pos2.x-30 && mousepos.x < pos2.x-30+24 && InputManager::getSingleton()->click()) { if (mousepos.y > pos1.y+3 && mousepos.y < pos1.y+3+24 && selected > 0) { selected--; changed = SBA_SELECT; } if (mousepos.y > pos2.y-27 && mousepos.y < pos2.y-27+24 && selected < entries.size()-1) { selected++; changed = SBA_SELECT; } } } doImage(GEN_ID, Vector2(pos2.x-15, pos1.y+15), "gfx/pfeil_oben.bmp"); doImage(GEN_ID, Vector2(pos2.x-15, pos2.y-15), "gfx/pfeil_unten.bmp"); first = (selected / itemsPerPage)*itemsPerPage; //recalc first if ( !entries.empty() ) { int last = first + itemsPerPage; if (last > entries.size()) last = entries.size(); obj.entries = std::vector(entries.begin()+first, entries.begin()+last); } else obj.entries = std::vector(); obj.selected = selected-first; mLastWidget = id; mQueue->push(obj); return changed; } void IMGUI::doChatbox(int id, const Vector2& pos1, const Vector2& pos2, const std::vector& entries, int& selected, const std::vector& local, unsigned int flags) { assert( entries.size() == local.size() ); int FontSize = (flags & TF_SMALL_FONT ? (FONT_WIDTH_SMALL+LINE_SPACER_SMALL) : (FONT_WIDTH_NORMAL+LINE_SPACER_NORMAL)); QueueObject obj; obj.id = id; obj.pos1 = pos1; obj.pos2 = pos2; obj.type = CHAT; obj.flags = flags; const int itemsPerPage = int(pos2.y - pos1.y - 10) / FontSize; int first = selected; //the first visible element in the list if (!mInactive) { // M.W. : Activate cursorless object-highlighting once the up or down key is pressed. if (mActiveButton == -1) { switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; default: break; } } // Highlight first menu object for arrow key navigation. if (mActiveButton == 0 && !mButtonReset) mActiveButton = id; // React to keyboard input. if (id == mActiveButton) { switch (mLastKeyAction) { case DOWN: mActiveButton = 0; mLastKeyAction = NONE; break; case UP: mActiveButton = mLastWidget; mLastKeyAction = NONE; break; case LEFT: if (selected > 0) { selected--; } mLastKeyAction = NONE; break; case RIGHT: if (selected < entries.size()-1) { selected++; } mLastKeyAction = NONE; break; default: break; } } // React to mouse input. Vector2 mousepos = InputManager::getSingleton()->position(); if (mousepos.x > pos1.x && mousepos.y > pos1.y && mousepos.x < pos2.x && mousepos.y < pos2.y) { if (InputManager::getSingleton()->click()) mActiveButton = id; } //entries mouseclick: if (mousepos.x > pos1.x && mousepos.y > pos1.y+5 && mousepos.x < pos2.x-35 && mousepos.y < pos1.y+5+FontSize*itemsPerPage) { if ((InputManager::getSingleton()->mouseWheelUp()) && (selected > 0)) { selected--; } if ((InputManager::getSingleton()->mouseWheelDown()) && (selected < entries.size()-1)) { selected++; } } //arrows mouseclick: if (mousepos.x > pos2.x-30 && mousepos.x < pos2.x-30+24 && InputManager::getSingleton()->click()) { if (mousepos.y > pos1.y+3 && mousepos.y < pos1.y+3+24 && selected > 0) { selected--; } if (mousepos.y > pos2.y-27 && mousepos.y < pos2.y-27+24 && selected < entries.size()-1) { selected++; } } } doImage(GEN_ID, Vector2(pos2.x-15, pos1.y+15), "gfx/pfeil_oben.bmp"); doImage(GEN_ID, Vector2(pos2.x-15, pos2.y-15), "gfx/pfeil_unten.bmp"); first = (selected / itemsPerPage)*itemsPerPage; //recalc first if ( !entries.empty() ) { int last = selected + 1; first = last - itemsPerPage; /// \todo maybe we should adapt selected so we even can't scroll up further! // we don't want negative chatlog, so we just scroll upward without coming to negative // elements. if (first < 0) { // increase last element so we alway draw itemsPerPage items last -= first; first = 0; } // check that we don't create out of bounds problems if(last > entries.size()) { last = entries.size(); } obj.entries = std::vector(entries.begin()+first, entries.begin()+last); // HACK: we use taxt to store information which text is from local player and which from // remote player. obj.text = ""; for(int i = first; i < last; ++i) { obj.text += local[i] ? 'L' : 'R'; } } else obj.entries = std::vector(); obj.selected = selected-first; mLastWidget = id; mQueue->push(obj); } bool IMGUI::doBlob(int id, const Vector2& position, const Color& col) { QueueObject obj; obj.id = id; obj.pos1 = position; obj.type = BLOB; obj.col = col; mQueue->push(obj); return false; } bool IMGUI::usingCursor() const { return mUsingCursor; } blobby-1.0rc3/src/PhysicWorld.h0000644000175000017500000001026712042452374017763 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Global.h" #include "Vector.h" #include "InputSource.h" #include "PhysicState.h" const float BLOBBY_SPEED = 4.5; // BLOBBY_SPEED is necessary to determine the size of the input buffer namespace RakNet { class BitStream; } /*! \brief blobby world \details This class encapuslates the physical world where blobby happens. It manages the two blobs, the ball and collisions between them and the environment, it calculates object movements etc. \todo remove all game logic related stuff! */ class PhysicWorld { public: PhysicWorld(); ~PhysicWorld(); Vector2 getBallVelocity() const; bool getBlobJump(PlayerSide player) const; bool getBallActive() const; void setLeftInput(const PlayerInput& input); void setRightInput(const PlayerInput& input); Vector2 getBlob(PlayerSide player) const; Vector2 getBall() const; float getBlobState(PlayerSide player) const; float getBallRotation() const; float getBallSpeed() const; // These functions tell about ball collisions for game logic and sound bool ballHitLeftPlayer() const; bool ballHitRightPlayer() const; bool ballHitLeftGround() const; bool ballHitRightGround() const; bool blobbyHitGround(PlayerSide player) const; // Blobby animation methods void blobbyAnimationStep(PlayerSide player); void blobbyStartAnimation(PlayerSide player); // This reports the intensity of the collision // which was detected and also queried last. float lastHitIntensity() const; // Here the game logic can decide whether the ball is valid. // If not, no ball to player collision checking is done, // the input is ignored an the ball experiences a strong damping void setBallValidity(bool validity); // This returns true if the ball is not valid and the ball is steady bool roundFinished() const; // This resets everything to the starting situation and // wants to know, which player begins. void reset(PlayerSide player); // This resets the player to their starting positions void resetPlayer(); // Important: This assumes a fixed framerate of 60 FPS! void step(); // For reducing ball speed after rule violation void dampBall(); // gets the phyisc state PhysicState getState() const; // sets a new physic state void setState(const PhysicState& state); //Input stuff for recording and playing replays const PlayerInput* getPlayersInput() const; #ifdef DEBUG bool checkPhysicStateValidity() const; #endif private: inline bool playerTopBallCollision(int player) const; inline bool playerBottomBallCollision(int player) const; bool resetAreaClear()const; // Do all blobby-related physic stuff which is independent from states void handleBlob(PlayerSide player); // Detect and handle ball to blobby collisions void handleBlobbyBallCollision(PlayerSide player); bool mBallHitByBlob[MAX_PLAYERS]; Vector2 mBlobPosition[MAX_PLAYERS]; Vector2 mBallPosition; Vector2 mBlobVelocity[MAX_PLAYERS]; Vector2 mBallVelocity; float mBallRotation; float mBallAngularVelocity; float mBlobState[MAX_PLAYERS]; float mCurrentBlobbyAnimationSpeed[MAX_PLAYERS]; PlayerInput mPlayerInput[MAX_PLAYERS]; bool mIsGameRunning; bool mIsBallValid; float mLastHitIntensity; ///! \todo thats not relevant for physics! It's game logic!! float mTimeSinceBallout; }; blobby-1.0rc3/src/LagDetectionSystem.h0000644000175000017500000001147112042452374021261 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include class CC_Result; #include "InputSource.h" /*! \class LagDetector \brief Class for detecting network lag \details This class is used to determine how much lag we have in network game. It works by comparing the input generated locally with the input data the server sends together with the world information, and determines the time difference by using cross-correlation. \attention There are situations when it is impossible to determine the lag exaclty, e.g. ehen ping is changing rapidly or the imformation we get from the network is not identifiable as the same sequence (i.e. half the packets got lost etc). In either of these cases incorrect lag values are our smalles problem ;) Still, we should be able to detect such situations. Also, when player input is not changing, we get no inforamtion and cannot calculate the ping correctly. \todo Auto-Detect unusable data (due to packet loss, fast changing ping or no user input) \todo Fix false detection for periodical structures. When ping is generally about 8, and the same input data is sent twice and due to some noise the older one fits better, the lag is assumed to be much bigger than it actually is. \todo should this lag detector get a understanding of real time? so if steps are not equi-timed, it still would work. \todo I would like to implement this in a more generic manner, so we are no limited to using PlayerInput values for determining ping but could use data send especially for this purpose. (can't happen before 1.0 though, as we agreed not to change any network packets till this release). */ class LagDetector { public: /// \brief constructor /// \todo add parameters, as we have internals which affec how good the detection works /// (and how much CPU power is consumed ;) /// \param buffer_length: Determines the size of the buffer used to store data. /// The longer the buffer, the slower the data has to change in order to recognize /// the lag. Note, however, that a long buffer increases CPU consumption and /// does not neccesaryly lead to better results, eps. when lag changes, as the /// data before the lag change becomes useless. LagDetector(int buffer_length = 80); /// \brief submit data for evaluation /// \details This function has to be called every frame and gets /// the current input values. /// \attention Don't!! swap the ordering of these parameters. Lag detection /// produces weird results! /// \param send_value Current PlayerInput, which was sent/will be send within this /// time step to the server /// \param received_value Last Input data we received from server. void insertData(PlayerInput send_value, PlayerInput received_value); /// \brief returns current lag /// \todo cache the lag value so to successive calls without any call to insertData inbetween /// don't result in doing the cross-correlation again! int getLag() const; #ifdef DEBUG /// \brief Debug string /// \details returns the current internal data in human readable format for checking /// if cross-correlation found the correct ping. /// \todo would be good if we could get rid of the include, though std::string getDebugString() const; /// \brief Internal data for debugging /// \details returns the CC_Result that was generated by /// the last CrossCorrelation. CC_Result getDebugData() const; #endif private: /// is set to true, when new data is available for lag calculation mutable bool recalc; /// last lag data cache mutable int mLastLag; /// \todo it would be cool if we could hide this details in a pimpl pointer boost::circular_buffer sended; boost::circular_buffer received; }; blobby-1.0rc3/src/ScriptedInputSource.cpp0000644000175000017500000004003212042452374022016 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "ScriptedInputSource.h" /* includes */ #include #include #include #include extern "C" { #include "lua/lua.h" #include "lua/lauxlib.h" #include "lua/lualib.h" } #include "DuelMatch.h" #include "GameConstants.h" #include "BotAPICalculations.h" #include "FileRead.h" /* implementation */ DuelMatch* ScriptedInputSource::mMatch = 0; ScriptedInputSource* ScriptedInputSource::mCurrentSource = 0; struct pos_x; struct pos_y; struct vel_x; struct vel_y; template float convert(float f); template struct ScriptedInputSource::coordinate { coordinate(float f) : value(convert(f)) { } coordinate(double f) : value(convert(f)) { } operator float() const { return value; } float value; private: // other constructors ar prohibited ! template coordinate(U u); static float convert(float f); }; // functions for coodinate transformation template<> float ScriptedInputSource::coordinate::convert (float val) { return mCurrentSource->mSide == LEFT_PLAYER ? val : RIGHT_PLANE - val; } template<> float ScriptedInputSource::coordinate::convert (float val) { return 600.f - val; } template<> float ScriptedInputSource::coordinate::convert (float val) { return mCurrentSource->mSide == LEFT_PLAYER ? val : -val; } template<> float ScriptedInputSource::coordinate::convert (float val) { return -val; } ScriptedInputSource::ScriptedInputSource(const std::string& filename, PlayerSide playerside, unsigned int difficulty): mLastBallSpeed(0), mMaxDelay(difficulty), mCurDelay(difficulty), mSide(playerside) { mStartTime = SDL_GetTicks(); mState = lua_open(); // set game constants lua_pushnumber(mState, RIGHT_PLANE); lua_setglobal(mState, "CONST_FIELD_WIDTH"); lua_pushnumber(mState, 600 - GROUND_PLANE_HEIGHT_MAX); lua_setglobal(mState, "CONST_GROUND_HEIGHT"); lua_pushnumber(mState, -BALL_GRAVITATION); lua_setglobal(mState, "CONST_BALL_GRAVITY"); lua_pushnumber(mState, BALL_RADIUS); lua_setglobal(mState, "CONST_BALL_RADIUS"); lua_pushnumber(mState, BLOBBY_JUMP_ACCELERATION); lua_setglobal(mState, "CONST_BLOBBY_JUMP"); lua_pushnumber(mState, BLOBBY_LOWER_RADIUS); lua_setglobal(mState, "CONST_BLOBBY_BODY_RADIUS"); lua_pushnumber(mState, BLOBBY_UPPER_RADIUS); lua_setglobal(mState, "CONST_BLOBBY_HEAD_RADIUS"); lua_pushnumber(mState, BLOBBY_HEIGHT); lua_setglobal(mState, "CONST_BLOBBY_HEIGHT"); lua_pushnumber(mState, -GRAVITATION); lua_setglobal(mState, "CONST_BLOBBY_GRAVITY"); lua_pushnumber(mState, 600 - NET_SPHERE_POSITION); lua_setglobal(mState, "CONST_NET_HEIGHT"); lua_pushnumber(mState, NET_RADIUS); lua_setglobal(mState, "CONST_NET_RADIUS"); luaopen_math(mState); lua_register(mState, "touches", touches); lua_register(mState, "launched", launched); lua_register(mState, "debug", debug); lua_register(mState, "jump", jump); lua_register(mState, "moveto", moveto); lua_register(mState, "left", left); lua_register(mState, "right", right); lua_register(mState, "ballx", ballx); lua_register(mState, "bally", bally); lua_register(mState, "bspeedx", bspeedx); lua_register(mState, "bspeedy", bspeedy); lua_register(mState, "posx", posx); lua_register(mState, "posy", posy); lua_register(mState, "oppx", oppx); lua_register(mState, "oppy", oppy); lua_register(mState, "estimate", estimate); lua_register(mState, "estimx", estimx); lua_register(mState, "estimy", estimy); lua_register(mState, "timetox", timetox); lua_register(mState, "timetoy", timetoy); lua_register(mState, "predictx", predictx); lua_register(mState, "predicty", predicty); lua_register(mState, "xaty", xaty); lua_register(mState, "yatx", yatx); lua_register(mState, "nextevent", nextevent); lua_register(mState, "predictImpact", predictImpact); lua_register(mState, "getScore", getScore); lua_register(mState, "getOppScore", getOppScore); lua_register(mState, "getScoreToWin", getScoreToWin); lua_register(mState, "getGameTime", getGameTime); //lua_register(mState, "parabel", parabel); int error = FileRead::readLuaScript(filename, mState); if (error == 0) error = lua_pcall(mState, 0, 6, 0); if (error) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; ScriptException except; except.luaerror = lua_tostring(mState, -1); lua_pop(mState, 1); lua_close(mState); throw except; } // check whether all required lua functions are available bool onserve, ongame, onoppserve; lua_getglobal(mState, "OnServe"); onserve = lua_isfunction(mState, -1); lua_getglobal(mState, "OnGame"); ongame = lua_isfunction(mState, -1); lua_getglobal(mState, "OnOpponentServe"); onoppserve = lua_isfunction(mState, -1); if (!onserve || !ongame ||!onoppserve) { std::string error_message = "Missing bot function "; error_message += onserve ? "" : "OnServe() "; error_message += ongame ? "" : "OnGame() "; error_message += onoppserve ? "" : "OnOpponentServe() "; std::cerr << "Lua Error: " << error_message << std::endl; ScriptException except; except.luaerror = error_message; lua_pop(mState, 1); lua_close(mState); throw except; } // record which of the optional functions are available lua_getglobal(mState, "OnBounce"); mOnBounce = lua_isfunction(mState, -1); if(!mOnBounce) { std::cerr << "Lua Warning: Missing function OnBounce" << std::endl; } lua_pop(mState, lua_gettop(mState)); // init delay mBallPositions.set_capacity(mMaxDelay + 1); mBallVelocities.set_capacity(mMaxDelay + 1); for(unsigned int i = 0; i < mMaxDelay + 1; ++i) { mBallPositions.push_back(Vector2(0,0)); mBallVelocities.push_back(Vector2(0,0)); } } ScriptedInputSource::~ScriptedInputSource() { lua_close(mState); } PlayerInput ScriptedInputSource::getInput() { bool serving = false; // reset input mLeft = false; mRight = false; mJump = false; mCurrentSource = this; mMatch = DuelMatch::getMainGame(); if (mMatch == 0) { return PlayerInput(); } // ball position and velocity update mBallPositions.push_back(mMatch->getBallPosition()); mBallVelocities.push_back(mMatch->getBallVelocity()); // adapt current delay char action = rand() % 8; switch(action) { case 0: case 1: mCurDelay--; break; case 2: case 3: mCurDelay++; } if ( mLastBallSpeed != DuelMatch::getMainGame()->getBallVelocity().x ) { mLastBallSpeed = DuelMatch::getMainGame()->getBallVelocity().x; // reaction time after bounce mCurDelay += rand() % (mMaxDelay+1); } if(mCurDelay == -1) mCurDelay = 0; if(mCurDelay > mMaxDelay) mCurDelay = mMaxDelay; int error = 0; if (!mMatch->getBallActive() && mSide == // if no player is serving player, assume the left one is (mMatch->getServingPlayer() == NO_PLAYER ? LEFT_PLAYER : mMatch->getServingPlayer() )) { serving = true; lua_getglobal(mState, "OnServe"); lua_pushboolean(mState, !mMatch->getBallDown()); error = lua_pcall(mState, 1, 0, 0); } else if (!mMatch->getBallActive() && mCurrentSource->mSide != (mMatch->getServingPlayer() == NO_PLAYER ? LEFT_PLAYER : mMatch->getServingPlayer() )) { lua_getglobal(mState, "OnOpponentServe"); error = lua_pcall(mState, 0, 0, 0); } else { if ( mOnBounce && mLastBallSpeedVirtual != getBallVelocity().x ) { mLastBallSpeedVirtual = getBallVelocity().x; lua_getglobal(mState, "OnBounce"); error = lua_pcall(mState, 0, 0, 0); if (error) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; lua_pop(mState, 1); } } lua_getglobal(mState, "OnGame"); error = lua_pcall(mState, 0, 0, 0); } if (error) { std::cerr << "Lua Error: " << lua_tostring(mState, -1); std::cerr << std::endl; lua_pop(mState, 1); } // swap left/right if side is swapped if ( mSide == RIGHT_PLAYER ) std::swap(mLeft, mRight); PlayerInput currentInput = PlayerInput(mLeft, mRight, mJump); int stacksize = lua_gettop(mState); if (stacksize > 0) { std::cerr << "Warning: Stack messed up!" << std::endl; std::cerr << "Element on stack is a "; std::cerr << lua_typename(mState, -1) << std::endl; lua_pop(mState, stacksize); } if (mStartTime + WAITING_TIME > SDL_GetTicks() && serving) return PlayerInput(); else return currentInput; } void ScriptedInputSource::setflags(lua_State* state) { lua_pushnumber(state, FLAG_BOUNCE); lua_setglobal(state, "FLAG_BOUNCE"); } int ScriptedInputSource::touches(lua_State* state) { lua_pushnumber(state, mMatch->getHitcount(mCurrentSource->mSide)); return 1; } int ScriptedInputSource::launched(lua_State* state) { lua_pushnumber(state, mMatch->getBlobJump(mCurrentSource->mSide)); return 1; } int ScriptedInputSource::debug(lua_State* state) { float number = lua_tonumber(state, -1); lua_pop(state, 1); std::cerr << "Lua Debug: " << number << std::endl; return 0; } int ScriptedInputSource::jump(lua_State* state) { mCurrentSource->mJump = true; return 0; } int ScriptedInputSource::left(lua_State* state) { mCurrentSource->mLeft = true; return 0; } int ScriptedInputSource::right(lua_State* state) { mCurrentSource->mRight = true; return 0; } int ScriptedInputSource::moveto(lua_State* state) { float target = lua_tonumber(state, -1); lua_pop(state, 1); coordinate position = mMatch->getBlobPosition(mCurrentSource->mSide).x; if (position > target + 2) mCurrentSource->mLeft = true; if (position < target - 2) mCurrentSource->mRight = true; return 0; } const Vector2& ScriptedInputSource::getBallPosition() { return mCurrentSource->mBallPositions[mCurrentSource->mMaxDelay - mCurrentSource->mCurDelay]; } const Vector2& ScriptedInputSource::getBallVelocity() { return mCurrentSource->mBallVelocities[mCurrentSource->mMaxDelay - mCurrentSource->mCurDelay]; } int ScriptedInputSource::ballx(lua_State* state) { coordinate pos = getBallPosition().x; lua_pushnumber(state, pos); return 1; } int ScriptedInputSource::bally(lua_State* state) { coordinate pos = getBallPosition().y; lua_pushnumber(state, pos); return 1; } int ScriptedInputSource::bspeedx(lua_State* state) { coordinate vel = getBallVelocity().x; lua_pushnumber(state, vel); return 1; } int ScriptedInputSource::bspeedy(lua_State* state) { coordinate vel = getBallVelocity().y; lua_pushnumber(state, vel); return 1; } int ScriptedInputSource::posx(lua_State* state) { coordinate pos = mMatch->getBlobPosition(mCurrentSource->mSide).x; lua_pushnumber(state, pos); return 1; } int ScriptedInputSource::posy(lua_State* state) { coordinate pos = mMatch->getBlobPosition(mCurrentSource->mSide).y; lua_pushnumber(state, pos); return 1; } int ScriptedInputSource::oppx(lua_State* state) { PlayerSide invPlayer = mCurrentSource->mSide == LEFT_PLAYER ? RIGHT_PLAYER : LEFT_PLAYER; coordinate pos = mMatch->getBlobPosition(invPlayer).x; lua_pushnumber(state, pos); return 1; } int ScriptedInputSource::oppy(lua_State* state) { PlayerSide invPlayer = mCurrentSource->mSide == LEFT_PLAYER ? RIGHT_PLAYER : LEFT_PLAYER; coordinate pos = mMatch->getBlobPosition(invPlayer).y; lua_pushnumber(state, pos); return 1; } int ScriptedInputSource::estimate(lua_State* state) { static bool warning_issued = false; if( !warning_issued ) { warning_issued = true; std::cerr << "Lua Warning: function estimate() is deprecated!" << std::endl; } Vector2 pos = getBallPosition(); const Vector2& vel = getBallVelocity(); float time = (vel.y - std::sqrt((vel.y * vel.y)- (-2 * BALL_GRAVITATION * (-pos.y + GROUND_PLANE_HEIGHT_MAX - BALL_RADIUS)))) / (-BALL_GRAVITATION); coordinate estim = pos.x + vel.x * time; lua_pushnumber(state, estim); return 1; } int ScriptedInputSource::estimx(lua_State* state) { static bool warning_issued = false; if( !warning_issued ) { warning_issued = true; std::cerr << "Lua Warning: function estimx() is deprecated!" << std::endl; } int num = lround(lua_tonumber(state, -1)); lua_pop(state, 1); coordinate estim = getBallPosition().x + num * getBallVelocity().x; lua_pushnumber(state, estim); return 1; } int ScriptedInputSource::estimy(lua_State* state) { static bool warning_issued = false; if( !warning_issued ) { warning_issued = true; std::cerr << "Lua Warning: function estimy() is deprecated!" << std::endl; } int num = lround(lua_tonumber(state, -1)); lua_pop(state, 1); coordinate estim = getBallPosition().y + num * (getBallVelocity().y + 0.5*BALL_GRAVITATION*num); lua_pushnumber(state, estim); return 1; } int ScriptedInputSource::predictx(lua_State* state) { reset_flags(); float time = lua_tonumber(state, -1); lua_pop(state, 1); coordinate estim = predict_x(getBallPosition(), getBallVelocity(), time); lua_pushnumber(state, estim); setflags(state); return 1; } int ScriptedInputSource::predicty(lua_State* state) { reset_flags(); float time = lua_tonumber(state, -1); lua_pop(state, 1); coordinate estim = predict_y(getBallPosition(), getBallVelocity(), time); lua_pushnumber(state, estim); setflags(state); return 1; } int ScriptedInputSource::timetox(lua_State* state) { reset_flags(); coordinate destination = lua_tonumber(state, -1); lua_pop(state, 1); float time = time_to_x(getBallPosition(), getBallVelocity(), destination); lua_pushnumber(state, time); setflags(state); return 1; } int ScriptedInputSource::timetoy(lua_State* state) { reset_flags(); coordinate destination = lua_tonumber(state, -1); lua_pop(state, 1); float time = time_to_y(getBallPosition(), getBallVelocity(), destination); lua_pushnumber(state, time); setflags(state); return 1; } int ScriptedInputSource::xaty(lua_State* state) { reset_flags(); coordinate destination = lua_tonumber(state, -1); lua_pop(state, 1); coordinate x = x_at_y(getBallPosition(), getBallVelocity(), destination); lua_pushnumber(state, x); setflags(state); return 1; } int ScriptedInputSource::yatx(lua_State* state) { reset_flags(); coordinate destination = lua_tonumber(state, -1); lua_pop(state, 1); coordinate y = y_at_x(getBallPosition(), getBallVelocity(), destination); lua_pushnumber(state, y); setflags(state); return 1; } int ScriptedInputSource::predictImpact(lua_State* state) { reset_flags(); coordinate x = x_at_y(getBallPosition(), getBallVelocity(), GROUND_PLANE_HEIGHT_MAX - BLOBBY_HEIGHT - BALL_RADIUS); lua_pushnumber(state, x); setflags(state); return 1; } int ScriptedInputSource::nextevent(lua_State* state) { reset_flags(); float time = next_event(getBallPosition(), getBallVelocity()); lua_pushnumber(state, time); setflags(state); return 1; } int ScriptedInputSource::getScore(lua_State* state) { float score = mMatch->getScore( mCurrentSource->mSide ); lua_pushnumber(state, score); return 1; } int ScriptedInputSource::getOppScore(lua_State* state) { float score = mMatch->getScore( mCurrentSource->mSide == LEFT_PLAYER ? RIGHT_PLAYER: LEFT_PLAYER ); lua_pushnumber(state, score); return 1; } int ScriptedInputSource::getScoreToWin(lua_State* state) { float score = mMatch->getScoreToWin(); lua_pushnumber(state, score); return 1; } int ScriptedInputSource::getGameTime(lua_State* state) { float time = mMatch->getClock().getTime(); lua_pushnumber(state, time); return 1; } blobby-1.0rc3/src/RenderManagerGP2X.h0000644000175000017500000000557412042452374020674 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "RenderManager.h" /*! \class RenderManagerGP2X \brief SDL Render Manager for GP2X \details as SDLRenderManager, but uses scaled down textures */ class RenderManagerGP2X : public RenderManager { public: RenderManagerGP2X(); virtual void init(int xResolution, int yResolution, bool fullscreen); virtual void deinit(); virtual void draw(); virtual void refresh(); virtual bool setBackground(const std::string& filename); virtual void setBlobColor(int player, Color color); virtual void setBall(const Vector2& position, float rotation); virtual void setBlob(int player, const Vector2& position, float animationState); virtual void setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning); virtual void setTime(const std::string& t); virtual void drawText(const std::string& text, Vector2 position, unsigned int flags = TF_NORMAL); virtual void drawImage(const std::string& filename, Vector2 position) {}; private: SDL_Surface* mBackground; SDL_Surface* mBallShadow; std::vector mBall; std::vector mStandardBlob; std::vector mStandardBlobShadow; std::vector mLeftBlob; std::vector mLeftBlobShadow; std::vector mRightBlob; std::vector mRightBlobShadow; std::vector mFont; std::vector mHighlightFont; std::vector mSmallFont; std::vector mHighlightSmallFont; SDL_Surface *mScreen; Vector2 mBallPosition; float mBallRotation; Vector2 mLeftBlobPosition; float mLeftBlobAnimationState; Vector2 mRightBlobPosition; float mRightBlobAnimationState; int mLeftPlayerScore; int mRightPlayerScore; bool mLeftPlayerWarning; bool mRightPlayerWarning; std::string mLeftPlayerName; std::string mRightPlayerName; std::string mTime; SDL_Surface* colorSurface(SDL_Surface *surface, Color color); }; blobby-1.0rc3/src/ReplayRecorder.cpp0000644000175000017500000002177412042452374020776 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "ReplayRecorder.h" /* includes */ #include #include #include #include #include #include "tinyxml/tinyxml.h" #include "raknet/BitStream.h" #include #include "Global.h" #include "IReplayLoader.h" #include "PhysicState.h" #include "GenericIO.h" #include "FileRead.h" #include "FileWrite.h" /* implementation */ ChecksumException::ChecksumException(std::string filename, uint32_t expected, uint32_t real) { std::stringstream errorstr; errorstr << "Error: Corrupted replay file: " << filename << std::endl << "real crc: " << real << " crc in file: " << expected; error = errorstr.str(); } ChecksumException::~ChecksumException() throw() { } const char* ChecksumException::what() const throw() { return error.c_str(); } VersionMismatchException::VersionMismatchException(const std::string& filename, uint8_t major, uint8_t minor) { std::stringstream errorstr; errorstr << "Error: Outdated replay file: " << filename << std::endl << "expected version: " << (int)REPLAY_FILE_VERSION_MAJOR << "." << (int)REPLAY_FILE_VERSION_MINOR << std::endl << "got: " << (int)major << "." << (int)minor << " instead!" << std::endl; error = errorstr.str(); } VersionMismatchException::~VersionMismatchException() throw() { } const char* VersionMismatchException::what() const throw() { return error.c_str(); } ReplayRecorder::ReplayRecorder() { mGameSpeed = -1; } ReplayRecorder::~ReplayRecorder() { } void ReplayRecorder::save( boost::shared_ptr file) const { boost::shared_ptr target = createGenericWriter(file); writeFileHeader(target, 0); uint32_t replayHeaderStart = target->tell(); writeReplayHeader(target); writeAttributesSection(target); writeJumpTable(target); writeInputSection(target); writeStatesSection(target); // the last thing we write is the header again, so // we can fill in all data we gathered during the // rest of the writing process target->seek(replayHeaderStart); writeReplayHeader(target); target->seek(0); FileRead checksum_calculator(file->getFileName()); /// \todo how can we make sure that we open the right file? uint32_t checksum = checksum_calculator.calcChecksum(replayHeaderStart); writeFileHeader(target, checksum); } void ReplayRecorder::send(boost::shared_ptr target) const { target->string(mPlayerNames[LEFT_PLAYER]); target->string(mPlayerNames[RIGHT_PLAYER]); target->generic (mPlayerColors[LEFT_PLAYER]); target->generic (mPlayerColors[RIGHT_PLAYER]); target->uint32( mGameSpeed ); target->uint32( mEndScore[LEFT_PLAYER] ); target->uint32( mEndScore[RIGHT_PLAYER] ); target->generic >(mSaveData); target->generic > (mSavedStates); } void ReplayRecorder::receive(boost::shared_ptr source) { source->string(mPlayerNames[LEFT_PLAYER]); source->string(mPlayerNames[RIGHT_PLAYER]); source->generic (mPlayerColors[LEFT_PLAYER]); source->generic (mPlayerColors[RIGHT_PLAYER]); source->uint32( mGameSpeed ); source->uint32( mEndScore[LEFT_PLAYER] ); source->uint32( mEndScore[RIGHT_PLAYER] ); source->generic >(mSaveData); source->generic > (mSavedStates); } void ReplayRecorder::writeFileHeader(boost::shared_ptr file, uint32_t checksum) const { file->array(validHeader, sizeof(validHeader)); // after the header, we write the replay version // first, write zero. leading zero indicates that the following value // really is a version number (and not a checksum of an older replay!) file->byte(0); file->byte(REPLAY_FILE_VERSION_MAJOR); file->byte(REPLAY_FILE_VERSION_MINOR); file->byte(0); file->uint32(checksum); } void ReplayRecorder::writeReplayHeader(boost::shared_ptr file) const { /// for now, this are fixed numbers /// we have to make sure they are right! uint32_t header_ptr = file->tell(); uint32_t header_size = 9*sizeof(header_ptr); uint32_t attr_size = 128; /// for now, we reserve 128 bytes! uint32_t jptb_size = 128; /// for now, we reserve 128 bytes! uint32_t data_size = mSaveData.size(); /// assumes 1 byte per data record! uint32_t states_size = mSavedStates.size() * sizeof(PhysicState); file->uint32(header_size); file->uint32(attr_ptr); file->uint32(attr_size); file->uint32(jptb_ptr); file->uint32(jptb_size); file->uint32(data_ptr); file->uint32(data_size); file->uint32(states_ptr); file->uint32(states_size); // check that we really needed header_size space assert( file->tell() - header_size ); } void ReplayRecorder::writeAttributesSection(boost::shared_ptr file) const { attr_ptr = file->tell(); // we have to check that we are at attr_ptr! char attr_header[4] = {'a', 't', 'r', '\n'}; uint32_t gamespeed = mGameSpeed; uint32_t gamelength = mSaveData.size(); /// \attention 1 byte = 1 step is assumed here uint32_t gameduration = gamelength / gamespeed; uint32_t gamedat = std::time(0); // check that we can really safe time in gamedat. ideally, we should use a static assertion here //static_assert (sizeof(uint32_t) >= sizeof(time_t), "time_t does not fit into 32bit" ); file->array(attr_header, sizeof(attr_header)); file->uint32(gamespeed); file->uint32(gameduration); file->uint32(gamelength); file->uint32(gamedat); // write blob colors file->generic(mPlayerColors[LEFT_PLAYER]); file->generic(mPlayerColors[RIGHT_PLAYER]); file->uint32( mEndScore[LEFT_PLAYER] ); file->uint32( mEndScore[RIGHT_PLAYER] ); // write names file->string(mPlayerNames[LEFT_PLAYER]); file->string(mPlayerNames[RIGHT_PLAYER]); // we need to check that we don't use more space than we got! // set up writing for next section. not good! file->seek(attr_ptr + 128); } void ReplayRecorder::writeJumpTable(boost::shared_ptr file) const { jptb_ptr = file->tell(); // we have to check that we are at attr_ptr! char jtbl_header[4] = {'j', 'p', 't', '\n'}; file->array(jtbl_header, sizeof(jtbl_header)); file->seek(jptb_ptr + 128); } void ReplayRecorder::writeInputSection(boost::shared_ptr file) const { data_ptr = file->tell(); // we have to check that we are at attr_ptr! char data_header[4] = {'i', 'p', 't', '\n'}; file->array(data_header, sizeof(data_header)); file->generic > (mSaveData); /// \todo why don't we zip it? even though it's quite compact, /// we still save a lot of redundant information. } void ReplayRecorder::writeStatesSection(boost::shared_ptr file) const { states_ptr = file->tell(); // we have to check that we are at attr_ptr! char states_header[4] = {'s', 't', 'a', '\n'}; file->array(states_header, sizeof(states_header)); file->generic > (mSavedStates); } void ReplayRecorder::record(const DuelMatchState& state) { // save the state every 750 frames (10 secs for normal gamespeed) if(mSaveData.size() % 750 == 0) { mSavedStates.push_back(state); } // we save this 1 here just for compatibility // set highest bit to 1 unsigned char packet = 0 << 7; packet |= (state.worldState.playerInput[LEFT_PLAYER].getAll() & 7) << 3; packet |= (state.worldState.playerInput[RIGHT_PLAYER].getAll() & 7) ; mSaveData.push_back(packet); // update the score mEndScore[LEFT_PLAYER] = state.logicState.leftScore; mEndScore[RIGHT_PLAYER] = state.logicState.rightScore; } void ReplayRecorder::setPlayerNames(const std::string& left, const std::string& right) { mPlayerNames[LEFT_PLAYER] = left; mPlayerNames[RIGHT_PLAYER] = right; } void ReplayRecorder::setPlayerColors(Color left, Color right) { mPlayerColors[LEFT_PLAYER] = left; mPlayerColors[RIGHT_PLAYER] = right; } void ReplayRecorder::setGameSpeed(int fps) { mGameSpeed = fps; } void ReplayRecorder::finalize(unsigned int left, unsigned int right) { mEndScore[LEFT_PLAYER] = left; mEndScore[RIGHT_PLAYER] = right; // fill with one second of do nothing for(int i = 0; i < 75; ++i) { unsigned char packet = 0; mSaveData.push_back(packet); } } blobby-1.0rc3/src/RenderManagerGL2D.cpp0000644000175000017500000005071412042452374021173 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "RenderManagerGL2D.h" /* includes */ #include "FileExceptions.h" /* implementation */ #if HAVE_LIBGL RenderManagerGL2D::Texture::Texture( GLuint tex, int x, int y, int width, int height, int tw, int th ) : w(width), h(height), texture(tex) { assert(x + w <= tw); assert(y + h <= th); indices[0] = x / (float)tw; indices[1] = y / (float)th; indices[2] = (x + w) / (float)tw; indices[3] = y / (float)th; indices[4] = (x + w) / (float)tw; indices[5] = (y + h) / (float)th; indices[6] = x / (float)tw; indices[7] = (y + h) / (float)th; } int debugStateChanges = 0; int debugBindTextureCount = 0; // wrapper functions for debugging purposes void RenderManagerGL2D::glEnable(unsigned int flag) { if(mCurrentFlags.find(flag) != mCurrentFlags.end()) return; debugStateChanges++; ::glEnable(flag); mCurrentFlags.insert(flag); } void RenderManagerGL2D::glDisable(unsigned int flag) { if( mCurrentFlags.find(flag) == mCurrentFlags.end() ) return; debugStateChanges++; ::glDisable(flag); mCurrentFlags.erase( mCurrentFlags.find(flag) ); } void RenderManagerGL2D::glBindTexture(GLuint texture) { if(mCurrentTexture == texture) return; debugBindTextureCount++; ::glBindTexture(GL_TEXTURE_2D, texture); mCurrentTexture = texture; } int RenderManagerGL2D::getNextPOT(int npot) { int pot = 1; while (pot < npot) pot *= 2; return pot; } GLuint RenderManagerGL2D::loadTexture(SDL_Surface *surface, bool specular) { SDL_Surface* textureSurface; SDL_Surface* convertedTexture; textureSurface = surface; // Determine size of padding for 2^n format int oldX = textureSurface->w; int oldY = textureSurface->h; int paddedX = getNextPOT(textureSurface->w); int paddedY = getNextPOT(textureSurface->h); SDL_Rect targetRect; targetRect.w = oldX; targetRect.h = oldY; targetRect.x = (paddedX - oldX) / 2; targetRect.y = (paddedY - oldY) / 2; SDL_SetColorKey(textureSurface, SDL_SRCCOLORKEY, SDL_MapRGB(textureSurface->format, 0, 0, 0)); convertedTexture = SDL_CreateRGBSurface(SDL_SWSURFACE, paddedX, paddedY, 32, #if SDL_BYTEORDER == SDL_BIG_ENDIAN 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); #else 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); #endif SDL_BlitSurface(textureSurface, 0, convertedTexture, &targetRect); if (specular) { for (int y = 0; y < convertedTexture->h; ++y) { for (int x = 0; x < convertedTexture->w; ++x) { SDL_Color* pixel = &(((SDL_Color*)convertedTexture->pixels) [y * convertedTexture->w +x]); int luminance = int(pixel->r) * 5 - 4 * 256 - 138; luminance = luminance > 0 ? luminance : 0; luminance = luminance < 255 ? luminance : 255; pixel->r = luminance; pixel->g = luminance; pixel->b = luminance; } } } GLuint texture; glGenTextures(1, &texture); glBindTexture(texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, convertedTexture->w, convertedTexture->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, convertedTexture->pixels); SDL_FreeSurface(textureSurface); SDL_FreeSurface(convertedTexture); return texture; } void RenderManagerGL2D::drawQuad(float x, float y, float w, float h) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); GLfloat vertices[] = {x - w / 2.f, y - h / 2.f, x + w / 2.f, y - h / 2.f, x + w / 2.f, y + h / 2.f, x - w / 2.f, y + h / 2.f}; GLfloat texCoords[] = {0.f, 0.f, 1.f, 0.f, 1.f, 1.f, 0.f, 1.f}; glVertexPointer(2, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glDrawArrays(GL_QUADS, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } void RenderManagerGL2D::drawQuad(float x, float y, const Texture& tex) { glBindTexture(tex.texture); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); float w = tex.w; float h = tex.h; GLfloat vertices[] = {x - w / 2.f, y - h / 2.f, x + w / 2.f, y - h / 2.f, x + w / 2.f, y + h / 2.f, x - w / 2.f, y + h / 2.f}; glVertexPointer(2, GL_FLOAT, 0, vertices); glTexCoordPointer(2, GL_FLOAT, 0, tex.indices); glDrawArrays(GL_QUADS, 0, 4); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } RenderManagerGL2D::RenderManagerGL2D() : RenderManager() { } RenderManager* RenderManager::createRenderManagerGL2D() { return new RenderManagerGL2D(); } void RenderManagerGL2D::init(int xResolution, int yResolution, bool fullscreen) { glDisable(GL_DEPTH_TEST); mCurrentFlags.insert(GL_MULTISAMPLE); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); Uint32 screenFlags = SDL_OPENGL; if (fullscreen) screenFlags |= SDL_FULLSCREEN; SDL_WM_SetCaption(AppTitle, ""); SDL_WM_SetIcon(SDL_LoadBMP("data/Icon.bmp"), NULL); SDL_SetVideoMode(xResolution, yResolution, 0, screenFlags); SDL_ShowCursor(0); glDisable(GL_MULTISAMPLE); mLeftBlobColor = Color(255, 0, 0); mRightBlobColor = Color(0, 255, 0); SDL_Surface* bgSurface = loadSurface("backgrounds/strand2.bmp"); BufferedImage* bgBufImage = new BufferedImage; bgBufImage->w = getNextPOT(bgSurface->w); bgBufImage->h = getNextPOT(bgSurface->h); bgBufImage->glHandle = loadTexture(bgSurface, false); mBackground = bgBufImage->glHandle; mImageMap["background"] = bgBufImage; mBallShadow = loadTexture(loadSurface("gfx/schball.bmp"), false); mScroll = loadTexture(loadSurface("gfx/scrollbar.bmp"), false); for (int i = 1; i <= 16; ++i) { char filename[64]; sprintf(filename, "gfx/ball%02d.bmp", i); GLuint ballImage = loadTexture(loadSurface(filename), false); mBall.push_back(ballImage); } for (int i = 1; i <= 5; ++i) { char filename[64]; sprintf(filename, "gfx/blobbym%d.bmp", i); GLuint blobImage = loadTexture(loadSurface(filename), false); mBlob.push_back(blobImage); sprintf(filename, "gfx/blobbym%d.bmp", i); GLuint blobSpecular = loadTexture(loadSurface(filename), true); mBlobSpecular.push_back(blobSpecular); sprintf(filename, "gfx/sch1%d.bmp", i); GLuint blobShadow = loadTexture(loadSurface(filename), false); mBlobShadow.push_back(blobShadow); } // create text base textures SDL_Surface* textbase = createEmptySurface(2048, 32); SDL_Surface* hltextbase = createEmptySurface(2048, 32); SDL_Surface* smalltextbase = createEmptySurface(1024, 16); SDL_Surface* hlsmalltextbase = createEmptySurface(1024, 16); int x = 0; int sx = 0; for (int i = 0; i <= 53; ++i) { char filename[64], filename2[64]; sprintf(filename, "gfx/font%02d.bmp", i); sprintf(filename2, "gfx/font_small/font%02d.bmp", i); SDL_Surface* fontSurface = loadSurface(filename); SDL_Surface* fontSurface2 = loadSurface(filename2); SDL_Surface* highlight = highlightSurface(fontSurface, 60); SDL_Surface* highlight2 = highlightSurface(fontSurface2, 60); SDL_FreeSurface(fontSurface); SDL_FreeSurface(fontSurface2); fontSurface = loadSurface(filename); fontSurface2 = loadSurface(filename2); SDL_Rect r = {(short)x, 0, (short)fontSurface->w, (short)fontSurface->h}; SDL_BlitSurface(fontSurface, 0, textbase, &r); SDL_BlitSurface(highlight, 0, hltextbase, &r); r.x = sx; r.y = 0; r.w = fontSurface2->w; r.h = fontSurface2->h; SDL_BlitSurface(fontSurface2, 0, smalltextbase, &r); SDL_BlitSurface(highlight2, 0, hlsmalltextbase, &r); //GLuint ballImage = loadTexture(sf, false); //mBall.push_back(ballImage); Texture s = Texture(0, x, 0, fontSurface->w, fontSurface->h, 2048, 32); mFont.push_back(s); mHighlightFont.push_back(s); s = Texture(0, sx, 0, fontSurface2->w, fontSurface2->h, 1024, 16); //mFont.push_back(newFont); //mHighlightFont.push_back(loadTexture(highlight, false)); mSmallFont.push_back( s ); mHighlightSmallFont.push_back( s ); x += fontSurface->w; sx += fontSurface2->w; SDL_FreeSurface(fontSurface); SDL_FreeSurface(fontSurface2); } GLuint texture = loadTexture(textbase, false); GLuint hltexture = loadTexture(hltextbase, false); GLuint smalltexture = loadTexture(smalltextbase, false); GLuint hlsmalltexture = loadTexture(hlsmalltextbase, false); for (int i = 0; i < mFont.size(); ++i) { mFont[i].texture = texture; mHighlightFont[i].texture = hltexture; mSmallFont[i].texture = smalltexture; mHighlightSmallFont[i].texture = hlsmalltexture; } mParticle = loadTexture(loadSurface("gfx/blood.bmp"), false); glViewport(0, 0, xResolution, yResolution); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 800, 600, 0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_TEXTURE_2D); glAlphaFunc(GL_GREATER, 0.5); glEnable(GL_ALPHA_TEST); } void RenderManagerGL2D::deinit() { glDeleteTextures(1, &mBackground); glDeleteTextures(mBall.size(), &mBall[0]); glDeleteTextures(1, &mBallShadow); glDeleteTextures(mBlob.size(), &mBlob[0]); glDeleteTextures(mBlobSpecular.size(), &mBlobSpecular[0]); glDeleteTextures(mBlobShadow.size(), &mBlobShadow[0]); glDeleteTextures(1/*mFont.size()*/, &mFont[0].texture); glDeleteTextures(/*mHighlightFont.size()*/1, &mHighlightFont[0].texture); glDeleteTextures(/*mSmallFont.size()*/1, &mSmallFont[0].texture); glDeleteTextures(/*mHighlightSmallFont.size()*/1, &mHighlightSmallFont[0].texture); glDeleteTextures(1, &mScroll); for (std::map::iterator iter = mImageMap.begin(); iter != mImageMap.end(); ++iter) { glDeleteTextures(1, &(*iter).second->glHandle); delete iter->second; } glDeleteTextures(1, &mParticle); } void RenderManagerGL2D::draw() { if (!mDrawGame) return; // Background glDisable(GL_ALPHA_TEST); glColor4f(1.0, 1.0, 1.0, 1.0); glBindTexture(mBackground); glLoadIdentity(); drawQuad(400.0, 300.0, 1024.0, 1024.0); if(mShowShadow) { // Generic shadow settings glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); // Blob shadows Vector2 pos; pos = blobShadowPosition(mLeftBlobPosition); glColor4ub(mLeftBlobColor.r, mLeftBlobColor.g, mLeftBlobColor.b, 128); glBindTexture(mBlobShadow[int(mLeftBlobAnimationState) % 5]); drawQuad(pos.x, pos.y, 128.0, 32.0); pos = blobShadowPosition(mRightBlobPosition); glColor4ub(mRightBlobColor.r, mRightBlobColor.g, mRightBlobColor.b, 128); glBindTexture(mBlobShadow[int(mRightBlobAnimationState) % 5]); drawQuad(pos.x, pos.y, 128.0, 32.0); // Ball shadow pos = ballShadowPosition(mBallPosition); glColor4f(1.0, 1.0, 1.0, 0.5); glBindTexture(mBallShadow); drawQuad(pos.x, pos.y, 128.0, 32.0); glDisable(GL_BLEND); } glEnable(GL_ALPHA_TEST); // General object settings glBlendFunc(GL_SRC_ALPHA, GL_ONE); // The Ball glColor4f(1.0, 1.0, 1.0, 1.0); glBindTexture(mBall[int(mBallRotation / M_PI / 2 * 16) % 16]); /* float opacity = 0.0; for (std::list::iterator iter = mLastBallStates.begin(); iter != mLastBallStates.end(); ++iter) { // glColor4f(1.0 / MotionBlurIterations, // 1.0 / MotionBlurIterations, 1.0 / MotionBlurIterations, 1.0 - opacity); glColor4f(1.0, 1.0, 1.0, opacity); Vector2& ballPosition = *iter; */ drawQuad(mBallPosition.x, mBallPosition.y, 64.0, 64.0); /* opacity += 0.1; } if (mLastBallStates.size() > MotionBlurIterations) mLastBallStates.pop_back(); glDisable(GL_BLEND); */ // blob normal // left blob glBindTexture(mBlob[int(mLeftBlobAnimationState) % 5]); glColor3ubv(mLeftBlobColor.val); drawQuad(mLeftBlobPosition.x, mLeftBlobPosition.y, 128.0, 128.0); // right blob glBindTexture(mBlob[int(mRightBlobAnimationState) % 5]); glColor3ubv(mRightBlobColor.val); drawQuad(mRightBlobPosition.x, mRightBlobPosition.y, 128.0, 128.0); // blob specular glEnable(GL_BLEND); glColor4f(1.0, 1.0, 1.0, 1.0); // left blob glBindTexture(mBlobSpecular[int(mLeftBlobAnimationState) % 5]); drawQuad(mLeftBlobPosition.x, mLeftBlobPosition.y, 128.0, 128.0); // right blob glBindTexture(mBlobSpecular[int(mRightBlobAnimationState) % 5]); drawQuad(mRightBlobPosition.x, mRightBlobPosition.y, 128.0, 128.0); glDisable(GL_BLEND); // Ball marker glDisable(GL_ALPHA_TEST); glDisable(GL_TEXTURE_2D); GLubyte markerColor = SDL_GetTicks() % 1000 >= 500 ? 255 : 0; glColor3ub(markerColor, markerColor, markerColor); drawQuad(mBallPosition.x, 7.5, 5.0, 5.0); // Mouse marker // Position relativ zu BallMarker drawQuad(mMouseMarkerPosition, 592.5, 5.0, 5.0); glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); // Scores char textBuffer[64]; snprintf(textBuffer, 8, mLeftPlayerWarning ? "%02d!" : "%02d", mLeftPlayerScore); drawText(textBuffer, Vector2(24, 24), false); snprintf(textBuffer, 8, mRightPlayerWarning ? "%02d!" : "%02d", mRightPlayerScore); drawText(textBuffer, Vector2(728, 24), false); // Drawing the names drawText(mLeftPlayerName, Vector2(12, 550), false); drawText(mRightPlayerName, Vector2(788-(24*mRightPlayerName.length()), 550), false); // Drawing the clock drawText(mTime, Vector2(400 - mTime.length()*12, 24), false); } bool RenderManagerGL2D::setBackground(const std::string& filename) { try { SDL_Surface* newSurface = loadSurface(filename); glDeleteTextures(1, &mBackground); delete mImageMap["background"]; BufferedImage *imgBuffer = new BufferedImage; imgBuffer->w = getNextPOT(newSurface->w); imgBuffer->h = getNextPOT(newSurface->h); imgBuffer->glHandle = loadTexture(newSurface, false); mBackground = imgBuffer->glHandle; mImageMap["background"] = imgBuffer; } catch (FileLoadException) { return false; } return true; } void RenderManagerGL2D::setBlobColor(int player, Color color) { if (player == LEFT_PLAYER) mLeftBlobColor = color; if (player == RIGHT_PLAYER) mRightBlobColor = color; } void RenderManagerGL2D::showShadow(bool shadow) { mShowShadow = shadow; } void RenderManagerGL2D::setBall(const Vector2& position, float rotation) { mBallPosition = position; mBallRotation = rotation; static int mbCounter = 0; mbCounter++; if (mbCounter > 1) { mLastBallStates.push_front(position); mbCounter = 0; } } void RenderManagerGL2D::setBlob(int player, const Vector2& position, float animationState) { if (player == LEFT_PLAYER) { mLeftBlobPosition = position; mLeftBlobAnimationState = animationState; } if (player == RIGHT_PLAYER) { mRightBlobPosition = position; mRightBlobAnimationState = animationState; } } void RenderManagerGL2D::setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning) { mLeftPlayerScore = leftScore; mRightPlayerScore = rightScore; mLeftPlayerWarning = leftWarning; mRightPlayerWarning = rightWarning; } void RenderManagerGL2D::setPlayernames(std::string leftName, std::string rightName) { mLeftPlayerName = leftName; mRightPlayerName = rightName; } void RenderManagerGL2D::setTime(const std::string& t) { mTime = t; } void RenderManagerGL2D::drawText(const std::string& text, Vector2 position, unsigned int flags) { glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glDisable(GL_BLEND); glColor4f(1.0, 1.0, 1.0, 1.0); int FontSize = (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL); std::string string = text; int index = getNextFontIndex(string); float x = position.x - (FontSize / 2); float y = position.y + (FontSize / 2); while (index != -1) { if (flags & TF_OBFUSCATE) index = FONT_INDEX_ASTERISK; x += FontSize; if (flags & TF_SMALL_FONT) { if (flags & TF_HIGHLIGHT) drawQuad(x, y, mHighlightSmallFont[index]); else drawQuad(x, y, mSmallFont[index]); } else { if (flags & TF_HIGHLIGHT) drawQuad(x, y, mHighlightFont[index]); else drawQuad(x, y, mFont[index]); } index = getNextFontIndex(string); } } void RenderManagerGL2D::drawImage(const std::string& filename, Vector2 position) { glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glDisable(GL_BLEND); BufferedImage* imageBuffer = mImageMap[filename]; if (!imageBuffer) { imageBuffer = new BufferedImage; SDL_Surface* newSurface = loadSurface(filename); imageBuffer->w = getNextPOT(newSurface->w); imageBuffer->h = getNextPOT(newSurface->h); imageBuffer->glHandle = loadTexture(newSurface, false); mImageMap[filename] = imageBuffer; } glColor4f(1.0, 1.0, 1.0, 1.0); glDisable(GL_BLEND); //glLoadIdentity(); //glTranslatef(position.x , position.y, 0.0); glBindTexture(imageBuffer->glHandle); drawQuad(position.x, position.y, imageBuffer->w, imageBuffer->h); } void RenderManagerGL2D::drawOverlay(float opacity, Vector2 pos1, Vector2 pos2, Color col) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_TEXTURE_2D); glDisable(GL_ALPHA_TEST); glEnable(GL_BLEND); glColor4f(col.r, col.g, col.b, opacity); //glLoadIdentity(); glBegin(GL_QUADS); glVertex2f(pos1.x, pos1.y); glVertex2f(pos1.x, pos2.y); glVertex2f(pos2.x, pos2.y); glVertex2f(pos2.x, pos1.y); glEnd(); } void RenderManagerGL2D::drawBlob(const Vector2& pos, const Color& col) { glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); //glLoadIdentity(); //glTranslatef(pos.x, pos.y, 0.6); glBindTexture(mBlob[0]); glColor3ubv(col.val); drawQuad(pos.x, pos.y, 128.0, 128.0); glEnable(GL_BLEND); glColor4f(1.0, 1.0, 1.0, 1.0); glBindTexture(mBlobSpecular[0]); drawQuad(pos.x, pos.y, 128.0, 128.0); glDisable(GL_BLEND); } void RenderManagerGL2D::startDrawParticles() { glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBindTexture(mParticle); glBegin(GL_QUADS); } void RenderManagerGL2D::drawParticle(const Vector2& pos, int player) { //glLoadIdentity(); //glTranslatef(pos.x, pos.y, 0.6); if (player == LEFT_PLAYER) glColor3ubv(mLeftBlobColor.val); if (player == RIGHT_PLAYER) glColor3ubv(mRightBlobColor.val); if (player > 1) glColor3ubv(Color(255, 0, 0).val); float w = 9.0; float h = 9.0; glTexCoord2f(0.0, 0.0); glVertex2f(pos.x - w / 2.0, pos.y - h / 2.0); glTexCoord2f(1.0, 0.0); glVertex2f(pos.x + w / 2.0, pos.y - h / 2.0); glTexCoord2f(1.0, 1.0); glVertex2f(pos.x + w / 2.0, pos.y + h / 2.0); glTexCoord2f(0.0, 1.0); glVertex2f(pos.x - w / 2.0, pos.y + h / 2.0); } void RenderManagerGL2D::endDrawParticles() { glEnd(); } void RenderManagerGL2D::refresh() { //std::cout << debugStateChanges << "\n"; SDL_GL_SwapBuffers(); debugStateChanges = 0; //std::cerr << debugBindTextureCount << "\n"; debugBindTextureCount = 0; } #else RenderManager* RenderManager::createRenderManagerGL2D() { std::cerr << "OpenGL not available! Falling back to SDL renderer" << std::endl; return RenderManager::createRenderManagerSDL(); } #endif blobby-1.0rc3/src/BlobbyDebug.h0000644000175000017500000000356412042452374017676 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include int count(const std::type_info& type); int uncount(const std::type_info& type); /*! \class ObjectCounter \brief Logging number of creations and living objects \details To use this class for logging creations of a class TYPE, just derive it from ObjectCounter. A full memory report can be written to a stream by the record function. \todo more specific reporting, watches, etc. */ template class ObjectCounter { public: ObjectCounter() { count(typeid(Base)); }; ~ObjectCounter() { uncount(typeid(Base)); }; ObjectCounter(const ObjectCounter& other) { count(typeid(Base)); } ObjectCounter& operator=(const ObjectCounter& other) { return *this; } }; void report(std::ostream& stream); struct CountingReport { CountingReport() : alive(0), created(0) { } int alive; int created; }; blobby-1.0rc3/src/BotAPICalculations.h0000644000175000017500000000301012042452374021120 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Vector.h" // flags extern bool FLAG_BOUNCE; void reset_flags(); float time_to_x(const Vector2& pos, const Vector2& vel, float destination); float time_to_y(const Vector2& pos, const Vector2& vel, float destination); float predict_x(const Vector2& pos, const Vector2& vel, float time); float predict_y(const Vector2& pos, const Vector2& vel, float time); float y_at_x(const Vector2& pos, const Vector2& vel, float destination); float x_at_y(const Vector2& pos, const Vector2& vel, float destination); float next_event(const Vector2& pos, const Vector2& vel); blobby-1.0rc3/src/SoundManager.h0000644000175000017500000000400012042452374020063 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include #include /// \brief struct for holding sound data struct Sound { Sound() { data = 0; } Uint8* data; Uint32 length; int position; float volume; }; /*! \class SoundManager \brief class managing game sound. \details Managing loading, converting to target format, muting, setting volume and, of couse, playing of sounds. */ class SoundManager { public: static SoundManager* createSoundManager(); static SoundManager& getSingleton(); bool init(); void deinit(); bool playSound(const std::string& filename, float volume); void setVolume(float volume); void setMute(bool mute); private: SoundManager(); ~SoundManager(); static SoundManager* mSingleton; /// This maps filenames to sound buffers, which are always in /// target format std::map mSound; std::list mPlayingSound; SDL_AudioSpec mAudioSpec; bool mInitialised; float mVolume; bool mMute; Sound* loadSound(const std::string& filename); static void playCallback(void* singleton, Uint8* stream, int length); }; blobby-1.0rc3/src/FileSystem.h0000644000175000017500000000626412042452374017602 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include #include "FileExceptions.h" // some convenience wrappers around physfs class FileSystem : public boost::noncopyable { public: FileSystem(const std::string& path); ~FileSystem(); /// \brief gets the file system /// \details throws an error when file system has /// not been initialised. static FileSystem& getSingleton(); /// \brief enumerates files /// \details searches for all files in a certain directory with a given extension. The found files /// are returned as a vector containing the filenames. The extension is cuttet from the filenames. /// \param directory where to search /// \param extension file types to search for. /// \param keepExtension If true, the return vector contains the full filenames, if false [default behaviour], /// only the filenames without the extensions are saved. std::vector enumerateFiles(const std::string& directory, const std::string& extension, bool keepExtension = false); /// \brief deletes a file bool deleteFile(const std::string& filename); /// \brief tests whether a file exists bool exists(const std::string& filename) const; /// \brief tests wether given path is a directory bool isDirectory(const std::string& dirname) const; /// \brief creates a directory and reports success/failure /// \return true, if the directory could be created bool mkdir(const std::string& dirname); // general setup methods void addToSearchPath(const std::string& dirname, bool append = true); void removeFromSearchPath(const std::string& dirname); /// \details automatically registers this directory as primary read directory! void setWriteDir(const std::string& dirname); /// \todo this method is currently only copied code. it needs some review and a spec what it really should /// do. also, its uses should be looked at again. void probeDir(const std::string& dir); /// \todo ideally, this method would never be needed by client code!! std::string getDirSeparator(); /// \todo ideally, this method would never be needed by client code!! std::string getUserDir(); }; blobby-1.0rc3/src/InputManager.cpp0000644000175000017500000003727612042452374020452 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "InputManager.h" /* includes */ #include #include #include #include "UserConfig.h" #include "IMGUI.h" //#include "SoundManager.h" // this is temponary commented out. check this. #include "utf8.h" /* implementation */ InputManager* InputManager::mSingleton = 0; const int DOUBLE_CLICK_TIME = 200; InputManager::InputManager() { SDL_InitSubSystem(SDL_INIT_JOYSTICK); SDL_JoystickEventState(SDL_ENABLE); JoystickPool::getSingleton().probeJoysticks(); assert (mSingleton == 0); mSingleton = this; mRunning = true; mInputDevice[LEFT_PLAYER] = 0; mInputDevice[RIGHT_PLAYER] = 0; /// \todo init properly? mLastInputKey.sym = SDLK_UNKNOWN; mLastClickTime = 0; } InputManager::~InputManager() { JoystickPool::getSingleton().closeJoysticks(); } void InputManager::beginGame(PlayerSide side) { // Move Mouse to default position SDL_WarpMouse(400, 300); std::string prefix; if (side == LEFT_PLAYER) prefix = "left_blobby_"; if (side == RIGHT_PLAYER) prefix = "right_blobby_"; UserConfig config; /// \todo we need only read only access here! config.loadFile("inputconfig.xml"); // determine which device is to be used std::string device = config.getString(prefix + "device"); // load config for mouse if (device == "mouse") { int jumpbutton = config.getInteger(prefix + "mouse_jumpbutton"); mInputDevice[side] = new MouseInputDevice(side, jumpbutton); } // load config for keyboard else if (device == "keyboard") { SDLKey lkey = stringToKey(config.getString(prefix + "keyboard_left")); SDLKey rkey = stringToKey(config.getString(prefix + "keyboard_right")); SDLKey jkey = stringToKey(config.getString(prefix + "keyboard_jump")); mInputDevice[side] = new KeyboardInputDevice(lkey, rkey, jkey); } // load config for joystick else if (device == "joystick") { JoystickAction laction(config.getString(prefix + "joystick_left")); JoystickAction raction(config.getString(prefix + "joystick_right")); JoystickAction jaction(config.getString(prefix + "joystick_jump")); mInputDevice[side] = new JoystickInputDevice(laction, raction, jaction); } else std::cerr << "Error: unknown input device: " << device << std::endl; } void InputManager::endGame() { if (mInputDevice[LEFT_PLAYER]) { delete mInputDevice[LEFT_PLAYER]; mInputDevice[LEFT_PLAYER] = NULL; } if (mInputDevice[RIGHT_PLAYER]) { delete mInputDevice[RIGHT_PLAYER]; mInputDevice[RIGHT_PLAYER] = NULL; } } InputManager* InputManager::getSingleton() { assert(mSingleton); return mSingleton; } PlayerInput InputManager::getGameInput(int player) { assert (player >= 0 && player < 2); return mInput[player]; } InputManager* InputManager::createInputManager() { return new InputManager(); } void InputManager::updateInput() { mUp = false; mDown = false; mLeft = false; mRight = false; mSelect = false; mExit = false; mClick = false; mMouseWheelUp = false; mMouseWheelDown = false; mUnclick = false; mLastMouseButton = -1; /// \todo init properly mLastInputKey.sym = SDLK_UNKNOWN; mLastJoyAction = ""; // Init GUI Events for buffered Input SDL_PumpEvents(); SDL_Event event; SDL_JoystickUpdate(); // process all SDL events while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: mRunning = false; break; case SDL_KEYDOWN: mLastInputKey = event.key.keysym; switch (event.key.keysym.sym) { case SDLK_UP: mUp = true; break; case SDLK_DOWN: mDown = true; break; case SDLK_LEFT: mLeft = true; break; case SDLK_RIGHT: mRight = true; break; case SDLK_RETURN: case SDLK_SPACE: mSelect = true; break; case SDLK_ESCAPE: //case SDLK_BACKSPACE: mExit = true; break; default: break; } break; case SDL_MOUSEBUTTONDOWN: mLastMouseButton = event.button.button; switch (event.button.button) { case SDL_BUTTON_LEFT: mClick = true; if(SDL_GetTicks() - mLastClickTime < DOUBLE_CLICK_TIME ) { mDoubleClick = true; } mLastClickTime = SDL_GetTicks(); break; case SDL_BUTTON_WHEELUP: mMouseWheelUp = true; break; case SDL_BUTTON_WHEELDOWN: mMouseWheelDown = true; break; } break; case SDL_MOUSEBUTTONUP: mUnclick = true; break; case SDL_JOYBUTTONDOWN: { JoystickAction joyAction(event.jbutton.which, JoystickAction::BUTTON, event.jbutton.button); mLastJoyAction = joyAction.toString(); break; } case SDL_JOYAXISMOTION: { if (abs(event.jaxis.value) > 10000) { int axis = 0; if (event.jaxis.value > 0) axis = event.jaxis.axis + 1; if (event.jaxis.value < 0) axis = -(event.jaxis.axis + 1); JoystickAction joyAction(event.jaxis.which, JoystickAction::AXIS, axis); mLastJoyAction = joyAction.toString(); } break; } /* This handles the special buttons on the GP2X, this will * have to be renewed with the next GP2X release. /// even if we reintroduce this behaviour, we should move this code /// elsewhere... /// this is input processing not input retrieving/managing! #if defined(__arm__) && defined(linux) case SDL_JOYBUTTONDOWN: switch (event.jbutton.button) { case 17: volume -= 0.15; break; case 16: volume += 0.15; break; } SoundManager::getSingleton().setVolume(volume); break; #else #endif */ } } // Device gives status to the playerinput if (mInputDevice[LEFT_PLAYER] && !IMGUI::getSingleton().usingCursor()) mInputDevice[LEFT_PLAYER]->transferInput(mInput[0]); if (mInputDevice[RIGHT_PLAYER] && !IMGUI::getSingleton().usingCursor()) mInputDevice[RIGHT_PLAYER]->transferInput(mInput[1]); } // GUI-Methods bool InputManager::up() const { return mUp; } bool InputManager::down() const { return mDown; } bool InputManager::left() const { return mLeft; } bool InputManager::right() const { return mRight; } bool InputManager::select() const { return mSelect; } bool InputManager::exit() const { return mExit; } Vector2 InputManager::position() { SDL_GetMouseState(&mMouseX,&mMouseY); return Vector2(mMouseX,mMouseY); } bool InputManager::click() const { return mClick; } bool InputManager::doubleClick() const { return mDoubleClick; } bool InputManager::mouseWheelUp() const { return mMouseWheelUp; } bool InputManager::mouseWheelDown() const { return mMouseWheelDown; } bool InputManager::unclick() const { return mUnclick; } bool InputManager::running() const { return mRunning; } // declaration of the keymap InputKeyMap InputManager::mKeyMap[] = { { "",SDLK_UNKNOWN }, { "",SDLK_FIRST }, { "backspace",SDLK_BACKSPACE }, { "tabulator",SDLK_TAB }, { "",SDLK_CLEAR }, { "return",SDLK_RETURN }, { "pause",SDLK_PAUSE }, { "",SDLK_ESCAPE }, { "space",SDLK_SPACE }, { "exclaim",SDLK_EXCLAIM }, { "quotedouble",SDLK_QUOTEDBL }, { "#",SDLK_HASH }, { "$",SDLK_DOLLAR }, { "&",SDLK_AMPERSAND }, { "'",SDLK_QUOTE }, { "(",SDLK_LEFTPAREN }, { ")",SDLK_RIGHTPAREN }, { "*",SDLK_ASTERISK }, { "+",SDLK_PLUS }, { ",",SDLK_COMMA }, { "-",SDLK_MINUS }, { ".",SDLK_PERIOD }, { "/",SDLK_SLASH }, { "0",SDLK_0 }, { "1",SDLK_1 }, { "2",SDLK_2 }, { "3",SDLK_3 }, { "4",SDLK_4 }, { "5",SDLK_5 }, { "6",SDLK_6 }, { "7",SDLK_7 }, { "8",SDLK_8 }, { "9",SDLK_9 }, { ":",SDLK_COLON }, { ";",SDLK_SEMICOLON }, { "<",SDLK_LESS }, { "=",SDLK_EQUALS }, { ">",SDLK_GREATER }, { "?",SDLK_QUESTION }, { "@",SDLK_AT }, /* Skip uppercase letters */ { "[",SDLK_LEFTBRACKET }, { "backslash",SDLK_BACKSLASH }, { "]",SDLK_RIGHTBRACKET }, { "^",SDLK_CARET }, { "_",SDLK_UNDERSCORE }, { "`",SDLK_BACKQUOTE }, { "a",SDLK_a }, { "b",SDLK_b }, { "c",SDLK_c }, { "d",SDLK_d }, { "e",SDLK_e }, { "f",SDLK_f }, { "g",SDLK_g }, { "h",SDLK_h }, { "i",SDLK_i }, { "j",SDLK_j }, { "k",SDLK_k }, { "l",SDLK_l }, { "m",SDLK_m }, { "n",SDLK_n }, { "o",SDLK_o }, { "p",SDLK_p }, { "q",SDLK_q }, { "r",SDLK_r }, { "s",SDLK_s }, { "t",SDLK_t }, { "u",SDLK_u }, { "v",SDLK_v }, { "w",SDLK_w }, { "x",SDLK_x }, { "y",SDLK_y }, { "z",SDLK_z }, { "del",SDLK_DELETE }, /* End of ASCII mapped keysyms */ /* International keyboard syms */ { "world0",SDLK_WORLD_0 }, { "world1",SDLK_WORLD_1 }, { "world2",SDLK_WORLD_2 }, { "world3",SDLK_WORLD_3 }, { "world4",SDLK_WORLD_4 }, { "world5",SDLK_WORLD_5 }, { "world6",SDLK_WORLD_6 }, { "world7",SDLK_WORLD_7 }, { "world8",SDLK_WORLD_8 }, { "world9",SDLK_WORLD_9 }, { "world10",SDLK_WORLD_10 }, { "world11",SDLK_WORLD_11 }, { "world12",SDLK_WORLD_12 }, { "world13",SDLK_WORLD_13 }, { "world14",SDLK_WORLD_14 }, { "world15",SDLK_WORLD_15 }, { "world16",SDLK_WORLD_16 }, { "world17",SDLK_WORLD_17 }, { "world18",SDLK_WORLD_18 }, { "world19",SDLK_WORLD_19 }, { "world20",SDLK_WORLD_20 }, { "world21",SDLK_WORLD_21 }, { "world22",SDLK_WORLD_22 }, { "world23",SDLK_WORLD_23 }, { "world24",SDLK_WORLD_24 }, { "world25",SDLK_WORLD_25 }, { "world26",SDLK_WORLD_26 }, { "world27",SDLK_WORLD_27 }, { "world28",SDLK_WORLD_28 }, { "world29",SDLK_WORLD_29 }, { "world30",SDLK_WORLD_30 }, { "world31",SDLK_WORLD_31 }, { "world32",SDLK_WORLD_32 }, { "world33",SDLK_WORLD_33 }, { "world34",SDLK_WORLD_34 }, { "world35",SDLK_WORLD_35 }, { "world36",SDLK_WORLD_36 }, { "world37",SDLK_WORLD_37 }, { "world38",SDLK_WORLD_38 }, { "world39",SDLK_WORLD_39 }, { "world40",SDLK_WORLD_40 }, { "world41",SDLK_WORLD_41 }, { "world42",SDLK_WORLD_42 }, { "world43",SDLK_WORLD_43 }, { "world44",SDLK_WORLD_44 }, { "world45",SDLK_WORLD_45 }, { "world46",SDLK_WORLD_46 }, { "world47",SDLK_WORLD_47 }, { "world48",SDLK_WORLD_48 }, { "world49",SDLK_WORLD_49 }, { "world50",SDLK_WORLD_50 }, { "world51",SDLK_WORLD_51 }, { "world52",SDLK_WORLD_52 }, { "world53",SDLK_WORLD_53 }, { "world54",SDLK_WORLD_54 }, { "world55",SDLK_WORLD_55 }, { "world56",SDLK_WORLD_56 }, { "world57",SDLK_WORLD_57 }, { "world58",SDLK_WORLD_58 }, { "world69",SDLK_WORLD_59 }, { "world60",SDLK_WORLD_60 }, { "world61",SDLK_WORLD_61 }, { "world62",SDLK_WORLD_62 }, { "world63",SDLK_WORLD_63 }, { "world64",SDLK_WORLD_64 }, { "world65",SDLK_WORLD_65 }, { "world66",SDLK_WORLD_66 }, { "world67",SDLK_WORLD_67 }, { "world68",SDLK_WORLD_68 }, { "world69",SDLK_WORLD_69 }, { "world70",SDLK_WORLD_70 }, { "world71",SDLK_WORLD_71 }, { "world72",SDLK_WORLD_72 }, { "world73",SDLK_WORLD_73 }, { "world74",SDLK_WORLD_74 }, { "world75",SDLK_WORLD_75 }, { "world76",SDLK_WORLD_76 }, { "world77",SDLK_WORLD_77 }, { "world78",SDLK_WORLD_78 }, { "world79",SDLK_WORLD_79 }, { "world80",SDLK_WORLD_80 }, { "world81",SDLK_WORLD_81 }, { "world82",SDLK_WORLD_82 }, { "world83",SDLK_WORLD_83 }, { "world84",SDLK_WORLD_84 }, { "world85",SDLK_WORLD_85 }, { "world86",SDLK_WORLD_86 }, { "world87",SDLK_WORLD_87 }, { "world88",SDLK_WORLD_88 }, { "world89",SDLK_WORLD_89 }, { "world90",SDLK_WORLD_90 }, { "world91",SDLK_WORLD_91 }, { "world92",SDLK_WORLD_92 }, { "world93",SDLK_WORLD_93 }, { "world94",SDLK_WORLD_94 }, { "world95",SDLK_WORLD_95 }, /* Numeric keypad */ { "keypad0",SDLK_KP0 }, { "keypad1",SDLK_KP1 }, { "keypad2",SDLK_KP2 }, { "keypad3",SDLK_KP3 }, { "keypad4",SDLK_KP4 }, { "keypad5",SDLK_KP5 }, { "keypad6",SDLK_KP6 }, { "keypad7",SDLK_KP7 }, { "keypad8",SDLK_KP8 }, { "keypad9",SDLK_KP9 }, { "keypad.",SDLK_KP_PERIOD }, { "keypad/",SDLK_KP_DIVIDE }, { "keypad*",SDLK_KP_MULTIPLY }, { "keypad-",SDLK_KP_MINUS }, { "keypad+",SDLK_KP_PLUS }, { "keypadenter",SDLK_KP_ENTER }, { "keypad",SDLK_KP_EQUALS }, /* Arrows + Home/End pad */ { "up",SDLK_UP }, { "down",SDLK_DOWN }, { "right",SDLK_RIGHT }, { "left",SDLK_LEFT }, { "insert",SDLK_INSERT }, { "home",SDLK_HOME }, { "end",SDLK_END }, { "pageup",SDLK_PAGEUP }, { "pagedown",SDLK_PAGEDOWN }, /* Function keys */ { "f1",SDLK_F1 }, { "f2",SDLK_F2 }, { "f3",SDLK_F3 }, { "f4",SDLK_F4 }, { "f5",SDLK_F5 }, { "f6",SDLK_F6 }, { "f7",SDLK_F7 }, { "f8",SDLK_F8 }, { "f9",SDLK_F9 }, { "f10",SDLK_F10 }, { "f11",SDLK_F11 }, { "f12",SDLK_F12 }, { "f13",SDLK_F13 }, { "f14",SDLK_F14 }, { "f15",SDLK_F15 }, /* Key state modifier keys */ { "numlock",SDLK_NUMLOCK }, { "capslock",SDLK_CAPSLOCK }, { "scrollock",SDLK_SCROLLOCK }, { "rightshift",SDLK_RSHIFT }, { "leftshift",SDLK_LSHIFT }, { "rightcontrol",SDLK_RCTRL }, { "leftcontrol",SDLK_LCTRL }, { "rightalt",SDLK_RALT }, { "leftalt",SDLK_LALT }, { "rmeta",SDLK_RMETA }, { "lmeta",SDLK_LMETA }, { "leftwin",SDLK_LSUPER }, { "rightwin",SDLK_RSUPER }, { "mode",SDLK_MODE }, /* "Alt Gr" key */ { "compose",SDLK_COMPOSE }, /* Multi-key compose key */ /* Miscellaneous function keys */ { "help",SDLK_HELP }, { "print",SDLK_PRINT }, { "sysreq",SDLK_SYSREQ }, { "break",SDLK_BREAK }, { "menu",SDLK_MENU }, { "power",SDLK_POWER }, /* Power Macintosh power key */ { "euro",SDLK_EURO }, /* Some european keyboards */ { "undo",SDLK_UNDO }, /* Atari keyboard has Undo */ {NULL} // end of the keymap }; std::string InputManager::keyToString (const SDL_keysym& key) { // use direct unicode translation when we did not // get a control key // for now, we can not do this, as // other parts of blobby don't work correctly // when string returns a character consistsing // of 2 bytes if(key.unicode > 0x1F) { wchar_t c = key.unicode; // we must convert from wchar_t to utf8 char cc[4] = {0, 0, 0, 0}; to_utf8(c, cc); // if this is no multibyte character, we can use it directly if(cc[1] == 0) { return std::string(cc); } // special behaviour for some german keys // for now, that's öäüß if(cc[0] == "ß"[0]) { switch((unsigned char)cc[1]) { case 0x84: case 0xa4: return "a"; case 0x96: case 0xb6: return "o"; case 0x9c: case 0xbc: return "u"; case 0x9f: return "s"; } } // otherwise, we have to use the old behaviour and look in our translation table } int i = 0; while (mKeyMap[i].keyname != NULL) { if (mKeyMap[i].key == key.sym) { return mKeyMap[i].keyname; } ++i; } return ""; } SDLKey InputManager::stringToKey (const std::string& keyname) { int i = 0; while (mKeyMap[i].keyname != NULL) { if (keyname == mKeyMap[i].keyname) return mKeyMap[i].key; i++; } return SDLK_UNKNOWN; // stringinformation = "" } std::string InputManager::getLastTextKey() { if (mLastInputKey.sym != SDLK_UNKNOWN) return keyToString(mLastInputKey); else return ""; } std::string InputManager::getLastActionKey() { /// \todo this is a hack we cannot prevent until SDL 1.3 is out int i = 0; while (mKeyMap[i].keyname != NULL) { if (mKeyMap[i].key == mLastInputKey.sym) { return mKeyMap[i].keyname; } ++i; } return ""; } std::string InputManager::getLastJoyAction() const { return mLastJoyAction; } blobby-1.0rc3/src/utf8.h0000644000175000017500000000133212042452374016373 0ustar danielknobedanielknobe #pragma once // utility functions for handling utf8 inline void to_utf8(wchar_t codepoint, char* target) { if(codepoint <= 0x7F) { target[0] = codepoint; } else if (codepoint <= 0x07FF ) { unsigned char high = codepoint >> 8; unsigned char low = codepoint & 0xFF; // codepoint: 00000xxx|xxyyyyyy // => 110xxxxx|10yyyyyy target[0] = 0xC0 | (0x1F & (high << 2 | low >> 6 ) ); target[1] = 0x80 | (0x3F & low); } else { } } // get following char length inline int getCodepointLength(char first) { // 1 byte = if( first >> 7 == 0) { return 1; } if( first >> 5 == 5) { return 2; } // \todo implement more cases // no starting point return 0; } blobby-1.0rc3/src/NetworkMessage.cpp0000644000175000017500000000643512042452374021007 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "NetworkMessage.h" /* includes */ #include #include "UserConfig.h" #include "SpeedController.h" /* implementation */ ServerInfo::ServerInfo(RakNet::BitStream& stream, const char* ip, uint16_t p) { strncpy(hostname, ip, sizeof(hostname)); hostname[sizeof(hostname) - 1] = 0; port = p; stream.Read(activegames); stream.Read(gamespeed); stream.Read(name, sizeof(name)); stream.Read(waitingplayer, sizeof(waitingplayer)); stream.Read(description, sizeof(description)); } ServerInfo::ServerInfo(const UserConfig& config) { /// \todo we only need a config reader here! // default values std::string n = "Blobby Volley 2 Server"; std::string d = "no description available"; memset(this, 0, sizeof(ServerInfo)); std::string tmp; tmp = config.getString("name"); if( tmp == "" ) tmp = n; strncpy(name, tmp.c_str(), sizeof(name) - 1); tmp = config.getString("description"); if( tmp == "" ) tmp = d; strncpy(description, tmp.c_str(), sizeof(description) - 1); gamespeed = config.getInteger("speed"); /// \todo maybe we should check if that's a reasonable value, too. if (gamespeed == 0) gamespeed = 75; } ServerInfo::ServerInfo(const std::string& playername) { memset(this, 0, sizeof(ServerInfo)); strncpy(name, std::string(playername + "'s game").c_str(), sizeof(name) - 1); strncpy(description, "client hosted game", sizeof(description) - 1); gamespeed = (int)SpeedController::getMainInstance()->getGameSpeed(); } void ServerInfo::writeToBitstream(RakNet::BitStream& stream) { stream.Write(activegames); stream.Write(gamespeed); stream.Write(name, sizeof(name)); stream.Write(waitingplayer, sizeof(waitingplayer)); stream.Write(description, sizeof(description)); } void ServerInfo::setWaitingPlayer(const std::string& name) { strncpy(waitingplayer, name.c_str(), sizeof(waitingplayer) - 1); } const size_t ServerInfo::BLOBBY_SERVER_PRESENT_PACKET_SIZE = sizeof((unsigned char)ID_BLOBBY_SERVER_PRESENT) + 2 * sizeof(int) // activegames & gamespeed + 32 // name + 64 // waiting player + 192; // description bool operator == (const ServerInfo& lval, const ServerInfo& rval) { // check if ip and port are identical! /// \todo maybe we should user raknets pladerId directly? return lval.port == rval.port && !strncmp(lval.hostname, rval.hostname, sizeof(lval.hostname)); } blobby-1.0rc3/src/CrossCorrelation.h0000644000175000017500000000532212042452374021003 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once typedef float(*timeWeightFunction)(float pdt); inline float constantWeightFunction(float ) { return 1; } /// \brief Results of Cross Correlation Test struct CC_Result { CC_Result(int o) : offset(o) { } int offset; //!< determined time shift }; #if CC_USE_EXTERN_DEFINITIONS // just extern template definitions template extern float crossCorrelationTest(const T& signal, const T& search_pattern, int offset); template extern CC_Result crossCorrelation(const T& A, const T& B); #else // Functions for cross correlation. we should move them into a seperate file template float crossCorrelationTest(const T& signal, const T& search_pattern, int offset, timeWeightFunction twf) { float rel = 0; typename T::const_iterator signal_read = signal.begin(); std::advance(signal_read, offset); int pos = 0; int len = search_pattern.size(); for(typename T::const_iterator comp = search_pattern.begin(); comp != search_pattern.end(); ++comp, ++signal_read, ++pos) { assert(signal_read != signal.end()); if((*signal_read) == (*comp)) rel += twf( (float)pos / len ); }; return rel / search_pattern.size(); } /// \brief Cross Correlation between to containers /// \todo document algorithm template CC_Result crossCorrelation(const T& A, const T& B, timeWeightFunction f = constantWeightFunction) { assert(A.size() >= B.size()); float best = 0; int boffset = 0; int samevals = 0; for(unsigned int offset = 0; offset <= A.size() - B.size(); ++offset) { float val = crossCorrelationTest(A, B, offset, f); if(val > best) { best = val; boffset = offset; samevals = 1; } else if ( val == best) { samevals++; } } return CC_Result(boffset); } #endif blobby-1.0rc3/src/IReplayLoader.h0000644000175000017500000001206212042452374020203 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include // for time_t #include #include "Global.h" #include "ReplayDefs.h" #include "GenericIOFwd.h" class PlayerInput; /// \class IReplayLoader /// \brief Base class for replay loaders. /// \details \todo add detailed description. class IReplayLoader { public: /// \todo maybe we should use a safeptr here. /// \brief create IReplayLoader by major version /// \details creates a replay loader for replays with a /// certain major version. The minor version of the /// created loader is the highest one possible. /// \return the replay loader or 0 is no sufficient loader has been found. static IReplayLoader* createReplayLoader(int major); /// \brief Creates an IReplayLoader for a certain file. /// \details Determines the version of the file and creates a /// corresponding IReplayLoader. /// \exception \todo we have to add and document the exceptions static IReplayLoader* createReplayLoader(const std::string& file); /// \brief virtual destructor /// \details /// \exception none virtual ~IReplayLoader() {}; // General Interface /// \brief returns the major version of replay files the loader can load. /// \details /// \exception none virtual int getVersionMajor() const = 0; /// \brief returns the maximum minor version this loader can load. /// \details As minor indicates downward compatibility, this indicates /// that all replays with lower minor version can be loaded, too. /// \exception none virtual int getVersionMinor() const = 0; // Attributes Interface /// gets the name of a player virtual std::string getPlayerName(PlayerSide player) const = 0; /// gets blob color of a player virtual Color getBlobColor(PlayerSide player) const = 0; /// get final score of a player virtual int getFinalScore(PlayerSide player) const = 0; /// gets the speed this game was played virtual int getSpeed() const = 0; /// gets the duration of the game in seconds virtual int getDuration() const = 0; /// gets the length of the replay in physic steps virtual int getLength() const = 0; /// gets the date this replay was recorded virtual std::time_t getDate() const = 0; // Replay data interface /// \brief gets the player input at the moment step /// \param step Timestep from when the player input should be received. /// Has to be in range 0 ... getLength(); /// \param left[out] target where left player input is stored /// \param right[out] target where right player input is stored virtual void getInputAt(int step, PlayerInput& left, PlayerInput& right) = 0; /// \brief checks wether the specified position is a savepoint /// \param position[in] position to check for savepoint /// \param save_position[out] if \t position is a savepoint, this int /// contains the index of the savepoint /// \return true, if \t position is a savepoint virtual bool isSavePoint(int position, int& save_position) const = 0 ; /// \brief gets the save point at position targetPosition /// \details returns the index of the last safepoint before targetPosition, /// so the game status at targetPosition can be calculated /// by simulating as few steps as possible. /// \param targetPosition[in] which position should be reached /// \param save_position[out] which position the safepoint has /// \return index of the savepoint, or -1 if none found. virtual int getSavePoint(int targetPosition, int& save_position) const = 0; /// \brief reads the specified savepoint /// \param index[in] index of the savepoint, as returned by getSavePoint /// \param state[out] the read savepoint is written there virtual void readSavePoint(int index, ReplaySavePoint& state) const = 0; protected: /// \brief protected constructor. /// \details Create IReplayLoaders with createReplayLoader functions. /// \exception none IReplayLoader() {}; private: /// \todo add documentation virtual void initLoading(boost::shared_ptr file_handle, int minor_version) = 0; }; blobby-1.0rc3/src/Player.h0000644000175000017500000000275112042452374016747 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2008 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "Global.h" class InputSource; class Player { public: Player(PlayerSide side); ~Player(); void loadFromConfig(const std::string& prefix, bool initInput = true); InputSource* getInputSource() const; std::string getName() const; Color getColor() const; void setColor(Color ncol); void setName(const std::string& name); private: bool mInitialised; const PlayerSide mPlayerSide; boost::scoped_ptr mInputSource; std::string mName; Color mStaticColor; bool mOscillating; }; blobby-1.0rc3/src/DuelMatch.cpp0000644000175000017500000001550612042452374017716 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "DuelMatch.h" /* includes */ #include #include "IUserConfigReader.h" #include "UserConfig.h" #include "DuelMatchState.h" /* implementation */ DuelMatch* DuelMatch::mMainGame = 0; DuelMatch::DuelMatch(InputSource* linput, InputSource* rinput, bool global, bool remote) : mLogic(createGameLogic("rules.lua")), mPaused(false), events(0), external_events(0), mRemote(remote) { mGlobal = global; if (mGlobal) { assert(mMainGame == 0); mMainGame = this; } mLeftInput = linput; mRightInput = rinput; mBallDown = false; mPhysicWorld.resetPlayer(); mPhysicWorld.step(); /// \todo we better pass this as a parameter so DuelMatch has no coupeling with UserConfigs...s mLogic->setScoreToWin(IUserConfigReader::createUserConfigReader("config.xml")->getInteger("scoretowin")); } void DuelMatch::reset() { mPhysicWorld = PhysicWorld(); mLogic = createGameLogic("rules.lua"); mBallDown = false; mPhysicWorld.resetPlayer(); mPhysicWorld.step(); UserConfig gameConfig; gameConfig.loadFile("config.xml"); mLogic->setScoreToWin(gameConfig.getInteger("scoretowin")); } DuelMatch::~DuelMatch() { if (mGlobal) { mMainGame = 0; } } DuelMatch* DuelMatch::getMainGame() { return mMainGame; } void DuelMatch::step() { events = external_events; // do steps in physic an logic if (mLeftInput) mPhysicWorld.setLeftInput(mLeftInput->getInput()); if (mRightInput) mPhysicWorld.setRightInput(mRightInput->getInput()); // in pause mode, step does nothing except input being set if(mPaused) return; mPhysicWorld.step(); mLogic->step(); // check for all hit events if(!mRemote) { // create game events // ball/player hit events: if (mPhysicWorld.ballHitLeftPlayer() && mLogic->isCollisionValid(LEFT_PLAYER)) events |= EVENT_LEFT_BLOBBY_HIT; if (mPhysicWorld.ballHitRightPlayer() && mLogic->isCollisionValid(RIGHT_PLAYER)) events |= EVENT_RIGHT_BLOBBY_HIT; // ball/ground hit events: if(mPhysicWorld.ballHitLeftGround()) events |= EVENT_BALL_HIT_LEFT_GROUND; if(mPhysicWorld.ballHitRightGround()) events |= EVENT_BALL_HIT_RIGHT_GROUND; } // process events if (events & EVENT_LEFT_BLOBBY_HIT) mLogic->onBallHitsPlayer(LEFT_PLAYER); if (events & EVENT_RIGHT_BLOBBY_HIT) mLogic->onBallHitsPlayer(RIGHT_PLAYER); if(events & EVENT_BALL_HIT_LEFT_GROUND) mLogic->onBallHitsGround(LEFT_PLAYER); if(events & EVENT_BALL_HIT_RIGHT_GROUND) mLogic->onBallHitsGround(RIGHT_PLAYER); switch(mLogic->getLastErrorSide()){ case LEFT_PLAYER: events |= EVENT_ERROR_LEFT; case RIGHT_PLAYER: // if the error was caused by the right player // reset EVENT_ERROR_LEFT events &= ~EVENT_ERROR_LEFT; events |= EVENT_ERROR_RIGHT; if (!(events & EVENT_BALL_HIT_GROUND)) mPhysicWorld.dampBall(); // now, the ball is not valid anymore mPhysicWorld.setBallValidity(0); /// \todo why do we set balldown? /// we could get here just /// by for hits mBallDown = true; break; } // if the round is finished, we // reset BallDown, reset the World // to let the player serve // and trigger the EVENT_RESET if (mPhysicWorld.roundFinished()) { mBallDown = false; mPhysicWorld.reset(mLogic->getServingPlayer()); events |= EVENT_RESET; } // reset external events external_events = 0; } void DuelMatch::setScore(int left, int right) { mLogic->setScore(LEFT_PLAYER, left); mLogic->setScore(RIGHT_PLAYER, right); } void DuelMatch::trigger(int event) { external_events |= event; } void DuelMatch::resetTriggeredEvents() { external_events = 0; } void DuelMatch::pause() { mLogic->onPause(); mPaused = true; } void DuelMatch::unpause() { mLogic->onUnPause(); mPaused = false; } PlayerSide DuelMatch::winningPlayer() { return mLogic->getWinningPlayer(); } int DuelMatch::getHitcount(PlayerSide player) const { if (player == LEFT_PLAYER) return mLogic->getHits(LEFT_PLAYER); else if (player == RIGHT_PLAYER) return mLogic->getHits(RIGHT_PLAYER); else return 0; } int DuelMatch::getScore(PlayerSide player) const { return mLogic->getScore(player); } int DuelMatch::getScoreToWin() const { return mLogic->getScoreToWin(); } bool DuelMatch::getBallDown() const { return mBallDown; } bool DuelMatch::getBallActive() const { return mPhysicWorld.getBallActive(); } bool DuelMatch::getBlobJump(PlayerSide player) const { return mPhysicWorld.getBlobJump(player); } Vector2 DuelMatch::getBlobPosition(PlayerSide player) const { if (player == LEFT_PLAYER) return mPhysicWorld.getBlob(LEFT_PLAYER); else if (player == RIGHT_PLAYER) return mPhysicWorld.getBlob(RIGHT_PLAYER); else return Vector2(0.0, 0.0); } Vector2 DuelMatch::getBallPosition() const { return mPhysicWorld.getBall(); } Vector2 DuelMatch::getBallVelocity() const { return mPhysicWorld.getBallVelocity(); } PlayerSide DuelMatch::getServingPlayer() const { // NO_PLAYER hack was moved into ScriptedInpurSource.cpp return mLogic->getServingPlayer(); } void DuelMatch::setState(RakNet::BitStream* stream) { PhysicState ps = mPhysicWorld.getState(); ps.readFromStream(stream); mPhysicWorld.setState(ps); } void DuelMatch::setState(const DuelMatchState& state) { mPhysicWorld.setState(state.worldState); mLogic->setState(state.logicState); } DuelMatchState DuelMatch::getState() const { DuelMatchState state; state.worldState = mPhysicWorld.getState(); state.logicState = mLogic->getState(); return state; } const PlayerInput* DuelMatch::getPlayersInput() const { return mPhysicWorld.getPlayersInput(); } void DuelMatch::setPlayersInput(const PlayerInput& left, const PlayerInput& right) { mPhysicWorld.setLeftInput( left ); mPhysicWorld.setRightInput( right ); } void DuelMatch::setServingPlayer(PlayerSide side) { mLogic->setServingPlayer(side); mPhysicWorld.reset(side); } const Clock& DuelMatch::getClock() const { return mLogic->getClock(); } Clock& DuelMatch::getClock() { return mLogic->getClock(); } blobby-1.0rc3/src/Vector.h0000644000175000017500000001212412042452374016750 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include /// \brief class for repesenting 2d vectors /// \details e.g. positions, velocities. class Vector2 { public: union { struct { float x; float y; }; float val[2]; }; Vector2(); Vector2(float x, float y); Vector2(const Vector2& v1, const Vector2& v2); void clear(); Vector2 reflectX() const; Vector2 reflectY() const; Vector2 scale(float factor) const; Vector2 scaleX(float factor) const; Vector2 scaleY(float factor) const; float length() const; Vector2 normalise(); Vector2 contraVector() const ; inline Vector2 halfVector(const Vector2& vec) const { return Vector2(x + (vec.x - x) / 2, y + (vec.y - y) / 2); } inline Vector2& operator = (const Vector2& newVector) { x = newVector.x; y = newVector.y; return *this; } inline bool operator == (const Vector2& vector) const { return (x == vector.x && y == vector.y); } inline bool operator != (const Vector2& vector) const { return (x != vector.x || y != vector.y); } inline Vector2 operator + (const Vector2& vector) const { return Vector2(x + vector.x, y + vector.y); } inline Vector2 operator - (const Vector2& vector) const { return Vector2(x - vector.x, y - vector.y); } inline Vector2 operator * (float scalar) const { return Vector2(x * scalar, y * scalar); } inline Vector2 operator * (const Vector2& vector) const { return Vector2(x * vector.x, y * vector.y); } inline Vector2 operator / (float scalar) const { assert(scalar != 0.0); float invert = 1.0 / scalar; return Vector2(x * invert, y * invert); } inline Vector2 operator - () const { return Vector2(-x, -y); } inline Vector2& operator += (const Vector2& vector) { x += vector.x; y += vector.y; return *this; } inline Vector2& operator -= (const Vector2& vector) { x -= vector.x; y -= vector.y; return *this; } inline Vector2& operator *= (const Vector2& vector) { x *= vector.x; y *= vector.y; return *this; } inline float dotProduct(const Vector2& vector) const { return x * vector.x + y * vector.y; } inline float crossProduct(const Vector2& vector) const { return x * vector.y - y * vector.x; } inline Vector2 reflect(const Vector2& normal) const { return Vector2(*this - (normal * 2 * dotProduct(normal))); } }; // ------------------------------------------------------------------------------------------------- // INLINE IMPLEMENTATION // ------------------------------------------------------------------------------------------------- inline Vector2::Vector2() : x(0), y(0) { } inline Vector2::Vector2(float a, float b) : x(a), y(b) { } inline Vector2::Vector2(const Vector2& v1, const Vector2& v2) : x(v2.x - v1.x), y(v2.y - v1.y) { } inline Vector2 Vector2::reflectX() const { return Vector2(-x, y); } inline Vector2 Vector2::reflectY() const { return Vector2(x, -y); } inline Vector2 Vector2::scale(float factor) const { return Vector2(x * factor, y * factor); } inline Vector2 Vector2::scaleX(float factor) const { return Vector2(x * factor, y); } inline Vector2 Vector2::scaleY(float factor) const { return Vector2(x, y * factor); } inline float Vector2::length() const { #ifdef USE_SSE float ret; asm ( "movss %1, %%xmm0 \n" "movss %2, %%xmm1 \n" "mulss %%xmm0, %%xmm0 \n" "mulss %%xmm1, %%xmm1 \n" "addss %%xmm1, %%xmm0 \n" "sqrtss %%xmm0, %%xmm0 \n" "movss %%xmm0, %0 \n" : "=m"(ret) : "m"(x), "m"(y) : "%xmm0", "%xmm1" ); return ret; #else return sqrt(this->x * this->x + this->y * this->y); #endif } inline Vector2 Vector2::normalise() { float fLength = length(); if (fLength > 1e-08) return Vector2(x / fLength, y / fLength); return *this; } inline Vector2 Vector2::contraVector() const { return Vector2(-x, -y); } inline void Vector2::clear() { x = 0.0; y = 0.0; } inline bool operator < (const Vector2& v1, const Vector2& v2) { if (v1.x < v2.x) { if (v1.y < v2.y) return true; } return false; } inline bool operator > (const Vector2& v1, const Vector2& v2) { if (v1.x > v2.x) { if (v1.y > v2.y) return true; } return false; } blobby-1.0rc3/src/LagDetectionSystem.cpp0000644000175000017500000000624612042452374021620 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "LagDetectionSystem.h" /* includes */ #include "CrossCorrelation.h" /* implementation */ /// time function /// we have to determine which function is best float weight(float tp) { return 1 - tp; } /// this function seems to perform better, /// but we have not enough tests to check this /// assumption for real network data right now /// thats why we conservatively stick to /// the linear weight function. /*float weight(float tp) { return (1 - tp) * 0.5/(tp + 0.5); } */ LagDetector::LagDetector(int buffer_length) : recalc(true), mLastLag(0) { /// \todo document what this values do /// \todo adapt this values depending on gamespeed /// \todo add a gamespeed changed callback to speedmanager sended.resize(buffer_length + 20); received.resize(buffer_length); /// difference between sended.size() and received.size() is maximum detected lag } void LagDetector::insertData(PlayerInput send_value, PlayerInput received_value) { // just insert the data into the two circular_buffers sended.push_front(send_value); received.push_front(received_value); recalc = true; } int LagDetector::getLag() const { // only do CC if data has changed if( recalc ) { CC_Result lagres = crossCorrelation(sended, received, weight); recalc = false; mLastLag = lagres.offset; } return mLastLag; } #ifdef DEBUG std::string LagDetector::getDebugString() const { // construct debug string std::string s; // just make a textual representation for every input for( boost::circular_buffer::const_iterator it = sended.begin(); it != sended.end(); ++it) { s += it->left ? (it->up ? (it->right ? '-' : 'L') : (it->right ? '_' : 'l')) : (it->up ? (it->right ? 'R' : '^') : (it->right ? 'r' : 'o')); } s += "\n"; for( boost::circular_buffer::const_iterator it = received.begin(); it != received.end(); ++it) { s += it->left ? (it->up ? (it->right ? '-' : 'L') : (it->right ? '_' : 'l')) : (it->up ? (it->right ? 'R' : '^') : (it->right ? 'r' : 'o')); } return s; } CC_Result LagDetector::getDebugData() const { return crossCorrelation(sended, received); } #endif blobby-1.0rc3/src/PhysicWorld.cpp0000644000175000017500000003411412042452374020313 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "PhysicWorld.h" /* includes */ #include #include "raknet/BitStream.h" #include "GameConstants.h" /* implementation */ const int TIMESTEP = 5; // calculations per frame const float TIMEOUT_MAX = 2.5; // Gamefeeling relevant constants: const float BLOBBY_ANIMATION_SPEED = 0.5; const float STANDARD_BALL_ANGULAR_VELOCITY = 0.1; // helper function for setting FPU precision inline void set_fpu_single_precision(); PhysicWorld::PhysicWorld() { reset(LEFT_PLAYER); mCurrentBlobbyAnimationSpeed[LEFT_PLAYER] = 0.0; mCurrentBlobbyAnimationSpeed[RIGHT_PLAYER] = 0.0; mTimeSinceBallout = 0.0; } PhysicWorld::~PhysicWorld() { } bool PhysicWorld::resetAreaClear() const { return (blobbyHitGround(LEFT_PLAYER) && blobbyHitGround(RIGHT_PLAYER)); } void PhysicWorld::reset(PlayerSide player) { if (player == LEFT_PLAYER) mBallPosition = Vector2(200, STANDARD_BALL_HEIGHT); else if (player == RIGHT_PLAYER) mBallPosition = Vector2(600, STANDARD_BALL_HEIGHT); else mBallPosition = Vector2(400, 450); mBallVelocity.clear(); mBallRotation = 0.0; mBallAngularVelocity = STANDARD_BALL_ANGULAR_VELOCITY; mBlobState[LEFT_PLAYER] = 0.0; mBlobState[RIGHT_PLAYER] = 0.0; mIsGameRunning = false; mIsBallValid = true; mLastHitIntensity = 0.0; } void PhysicWorld::resetPlayer() { mBlobPosition[LEFT_PLAYER] = Vector2( 200, GROUND_PLANE_HEIGHT); mBlobPosition[RIGHT_PLAYER] = Vector2(600, GROUND_PLANE_HEIGHT); } bool PhysicWorld::ballHitRightGround() const { if (mIsBallValid) { if (mBallPosition.y > GROUND_PLANE_HEIGHT && mBallPosition.x > NET_POSITION_X) return true; } return false; } bool PhysicWorld::ballHitLeftGround() const { if (mIsBallValid) { if (mBallPosition.y > GROUND_PLANE_HEIGHT && mBallPosition.x < NET_POSITION_X) return true; } return false; } bool PhysicWorld::blobbyHitGround(PlayerSide player) const { if (player == LEFT_PLAYER) { return (getBlob(LEFT_PLAYER).y >= GROUND_PLANE_HEIGHT); } else if (player == RIGHT_PLAYER) { return (getBlob(RIGHT_PLAYER).y >= GROUND_PLANE_HEIGHT); } else return false; } void PhysicWorld::setBallValidity(bool validity) { mIsBallValid = validity; } bool PhysicWorld::roundFinished() const { if (resetAreaClear()) { if (!mIsBallValid) { if (mBallVelocity.y < 1.5 && mBallVelocity.y > -1.5 && mBallPosition.y > 430) return true; } } if (mTimeSinceBallout > TIMEOUT_MAX) return true; return false; } float PhysicWorld::lastHitIntensity() const { float intensity = mLastHitIntensity / 25.0; return intensity < 1.0 ? intensity : 1.0; } bool PhysicWorld::playerTopBallCollision(int player) const { if (Vector2(mBallPosition, Vector2(mBlobPosition[player].x, mBlobPosition[player].y - BLOBBY_UPPER_SPHERE)).length() <= BALL_RADIUS + BLOBBY_UPPER_RADIUS) return true; return false; } inline bool PhysicWorld::playerBottomBallCollision(int player) const { if (Vector2(mBallPosition, Vector2(mBlobPosition[player].x, mBlobPosition[player].y + BLOBBY_LOWER_SPHERE)).length() <= BALL_RADIUS + BLOBBY_LOWER_RADIUS) return true; return false; } bool PhysicWorld::ballHitLeftPlayer() const { return mBallHitByBlob[LEFT_PLAYER]; } bool PhysicWorld::ballHitRightPlayer() const { return mBallHitByBlob[RIGHT_PLAYER]; } Vector2 PhysicWorld::getBall() const { return mBallPosition; } float PhysicWorld::getBallRotation() const { return mBallRotation; } float PhysicWorld::getBallSpeed() const { return mBallVelocity.length(); } Vector2 PhysicWorld::getBlob(PlayerSide player) const { return mBlobPosition[player]; } float PhysicWorld::getBlobState(PlayerSide player) const { return mBlobState[player]; } void PhysicWorld::setLeftInput(const PlayerInput& input) { mPlayerInput[LEFT_PLAYER] = input; } void PhysicWorld::setRightInput(const PlayerInput& input) { mPlayerInput[RIGHT_PLAYER] = input; } // Blobby animation methods void PhysicWorld::blobbyAnimationStep(PlayerSide player) { if (mBlobState[player] < 0.0) { mCurrentBlobbyAnimationSpeed[player] = 0; mBlobState[player] = 0; } if (mBlobState[player] >= 4.5) { mCurrentBlobbyAnimationSpeed[player] =- BLOBBY_ANIMATION_SPEED; } mBlobState[player] += mCurrentBlobbyAnimationSpeed[player]; if (mBlobState[player] >= 5) { mBlobState[player] = 4.99; } } void PhysicWorld::blobbyStartAnimation(PlayerSide player) { if (mCurrentBlobbyAnimationSpeed[player] == 0) mCurrentBlobbyAnimationSpeed[player] = BLOBBY_ANIMATION_SPEED; } void PhysicWorld::handleBlob(PlayerSide player) { // Reset ball to blobby collision mBallHitByBlob[player] = false; float currentBlobbyGravity = GRAVITATION; if (mPlayerInput[player].up) { if (blobbyHitGround(player)) { mBlobVelocity[player].y = -BLOBBY_JUMP_ACCELERATION; blobbyStartAnimation(PlayerSide(player)); } currentBlobbyGravity -= BLOBBY_JUMP_BUFFER; } if ((mPlayerInput[player].left || mPlayerInput[player].right) && blobbyHitGround(player)) { blobbyStartAnimation(player); } mBlobVelocity[player].x = (mPlayerInput[player].right ? BLOBBY_SPEED : 0) - (mPlayerInput[player].left ? BLOBBY_SPEED : 0); // compute blobby fall movement (dt = 1) // ds = a/2 * dt^2 + v * dt mBlobPosition[player] += Vector2(0, 0.5f * currentBlobbyGravity ) + mBlobVelocity[player]; // dv = a * dt mBlobVelocity[player].y += currentBlobbyGravity; // Hitting the ground if (mBlobPosition[player].y > GROUND_PLANE_HEIGHT) { if(mBlobVelocity[player].y > 3.5) { blobbyStartAnimation(player); } mBlobPosition[player].y = GROUND_PLANE_HEIGHT; mBlobVelocity[player].y = 0.0; } blobbyAnimationStep(player); } void PhysicWorld::handleBlobbyBallCollision(PlayerSide player) { // Check for bottom circles if(playerBottomBallCollision(player)) { mLastHitIntensity = Vector2(mBallVelocity, mBlobVelocity[player]).length(); const Vector2& blobpos = mBlobPosition[player]; const Vector2 circlepos = Vector2(blobpos.x, blobpos.y + BLOBBY_LOWER_SPHERE); mBallVelocity = -Vector2(mBallPosition, circlepos); mBallVelocity = mBallVelocity.normalise(); mBallVelocity = mBallVelocity.scale(BALL_COLLISION_VELOCITY); mBallPosition += mBallVelocity; mBallHitByBlob[player] = true; } else if(playerTopBallCollision(player)) { mLastHitIntensity = Vector2(mBallVelocity, mBlobVelocity[player]).length(); const Vector2& blobpos = mBlobPosition[player]; const Vector2 circlepos = Vector2(blobpos.x, blobpos.y - BLOBBY_UPPER_SPHERE); mBallVelocity = -Vector2(mBallPosition, circlepos); mBallVelocity = mBallVelocity.normalise(); mBallVelocity = mBallVelocity.scale(BALL_COLLISION_VELOCITY); mBallPosition += mBallVelocity; mBallHitByBlob[player] = true; } } void PhysicWorld::step() { // Determistic IEEE 754 floating point computations set_fpu_single_precision(); // Compute independent actions handleBlob(LEFT_PLAYER); handleBlob(RIGHT_PLAYER); // Move ball when game is running if (mIsGameRunning) { // dt = 1 !! // move ball ds = a/2 * dt^2 + v * dt mBallPosition += Vector2(0, 0.5f * BALL_GRAVITATION) + mBallVelocity; // dv = a*dt mBallVelocity.y += BALL_GRAVITATION; } // Collision detection if(mIsBallValid) { handleBlobbyBallCollision(LEFT_PLAYER); handleBlobbyBallCollision(RIGHT_PLAYER); } // Ball to ground Collision else if (mBallPosition.y + BALL_RADIUS > 500.0) { mBallVelocity = mBallVelocity.reflectY().scaleY(0.5); mBallVelocity = mBallVelocity.scaleX(0.55); mBallPosition.y = 500 - BALL_RADIUS; } if (ballHitLeftPlayer() || ballHitRightPlayer()) mIsGameRunning = true; // Border Collision if (mBallPosition.x - BALL_RADIUS <= LEFT_PLANE && mBallVelocity.x < 0.0) { mBallVelocity = mBallVelocity.reflectX(); // set the ball's position mBallPosition.x = LEFT_PLANE + BALL_RADIUS; } else if (mBallPosition.x + BALL_RADIUS >= RIGHT_PLANE && mBallVelocity.x > 0.0) { mBallVelocity = mBallVelocity.reflectX(); // set the ball's position mBallPosition.x = RIGHT_PLANE - BALL_RADIUS; } else if (mBallPosition.y > NET_SPHERE_POSITION && fabs(mBallPosition.x - NET_POSITION_X) < BALL_RADIUS + NET_RADIUS) { mBallVelocity = mBallVelocity.reflectX(); // set the ball's position so that it touches the net mBallPosition.x = NET_POSITION_X + ((mBallPosition.x - NET_POSITION_X > 0) ?( BALL_RADIUS + NET_RADIUS) : (- BALL_RADIUS - NET_RADIUS)); } else { // Net Collisions float ballNetDistance = Vector2(mBallPosition, Vector2(NET_POSITION_X, NET_SPHERE_POSITION)).length(); if (ballNetDistance < NET_RADIUS + BALL_RADIUS) { // calculate Vector2 normal = Vector2(mBallPosition, Vector2(NET_POSITION_X, NET_SPHERE_POSITION)).normalise(); // normal component of kinetic energy float perp_ekin = normal.dotProduct(mBallVelocity); perp_ekin *= perp_ekin; // parallel component of kinetic energy float para_ekin = mBallVelocity.length() * mBallVelocity.length() - perp_ekin; // the normal component is damped stronger than the parallel component // the values are ~ 0.85 and ca. 0.95, because speed is sqrt(ekin) perp_ekin *= 0.7; para_ekin *= 0.9; float nspeed = sqrt(perp_ekin + para_ekin); mBallVelocity = Vector2(mBallVelocity.reflect(normal).normalise().scale(nspeed)); // pushes the ball out of the net mBallPosition = (Vector2(NET_POSITION_X, NET_SPHERE_POSITION) - normal * (NET_RADIUS + BALL_RADIUS)); } // mBallVelocity = mBallVelocity.reflect( Vector2( mBallPosition, Vector2 (NET_POSITION_X, temp) ).normalise()).scale(0.75); } // Collision between blobby and the net if (mBlobPosition[LEFT_PLAYER].x+BLOBBY_LOWER_RADIUS>NET_POSITION_X-NET_RADIUS) // Collision with the net mBlobPosition[LEFT_PLAYER].x=NET_POSITION_X-NET_RADIUS-BLOBBY_LOWER_RADIUS; if (mBlobPosition[RIGHT_PLAYER].x-BLOBBY_LOWER_RADIUS RIGHT_PLANE) mBlobPosition[RIGHT_PLAYER].x=RIGHT_PLANE; // Velocity Integration if( !getBallActive() ) mBallRotation -= mBallAngularVelocity; else if (mBallVelocity.x > 0.0) mBallRotation += mBallAngularVelocity * (getBallSpeed() / 6); else mBallRotation -= mBallAngularVelocity * (getBallSpeed() / 6); // Overflow-Protection if (mBallRotation <= 0) mBallRotation = 6.25 + mBallRotation; else if (mBallRotation >= 6.25) mBallRotation = mBallRotation - 6.25; mTimeSinceBallout = mIsBallValid ? 0.0 : mTimeSinceBallout + 1.0 / 60; } void PhysicWorld::dampBall() { mBallVelocity = mBallVelocity.scale(0.6); } Vector2 PhysicWorld::getBallVelocity() const { return mBallVelocity; } bool PhysicWorld::getBlobJump(PlayerSide player) const { return !blobbyHitGround(player); } bool PhysicWorld::getBallActive() const { return mIsGameRunning; } PhysicState PhysicWorld::getState() const { PhysicState st; st.blobPosition[LEFT_PLAYER] = mBlobPosition[LEFT_PLAYER]; st.blobPosition[RIGHT_PLAYER] = mBlobPosition[RIGHT_PLAYER]; st.blobVelocity[LEFT_PLAYER] = mBlobVelocity[LEFT_PLAYER]; st.blobVelocity[RIGHT_PLAYER] = mBlobVelocity[RIGHT_PLAYER]; st.ballPosition = mBallPosition; st.ballVelocity = mBallVelocity; st.ballAngularVelocity = mBallAngularVelocity; st.isGameRunning = mIsGameRunning; st.isBallValid = mIsBallValid; st.playerInput[LEFT_PLAYER] = mPlayerInput[LEFT_PLAYER]; st.playerInput[RIGHT_PLAYER] = mPlayerInput[RIGHT_PLAYER]; return st; } void PhysicWorld::setState(const PhysicState& ps) { mBlobPosition[LEFT_PLAYER] = ps.blobPosition[LEFT_PLAYER]; mBlobPosition[RIGHT_PLAYER] = ps.blobPosition[RIGHT_PLAYER]; mBlobVelocity[LEFT_PLAYER] = ps.blobVelocity[LEFT_PLAYER]; mBlobVelocity[RIGHT_PLAYER] = ps.blobVelocity[RIGHT_PLAYER]; mBallPosition = ps.ballPosition; mBallVelocity = ps.ballVelocity; mBallAngularVelocity = ps.ballAngularVelocity; mIsGameRunning = ps.isGameRunning; mIsBallValid = ps.isBallValid; mPlayerInput[LEFT_PLAYER] = ps.playerInput[LEFT_PLAYER]; mPlayerInput[RIGHT_PLAYER] = ps.playerInput[RIGHT_PLAYER]; } const PlayerInput* PhysicWorld::getPlayersInput() const { return mPlayerInput; } // debugging: #ifdef DEBUG #include bool PhysicWorld::checkPhysicStateValidity() const { // check for blobby ball collisions if(playerTopBallCollision(LEFT_PLAYER) || playerBottomBallCollision(LEFT_PLAYER)) { std::cout << mBallPosition.x << " " << mBallPosition.y << "\n"; std::cout << mBlobPosition[LEFT_PLAYER].x << " " << mBlobPosition[LEFT_PLAYER].y << "\n"; return false; } if(playerTopBallCollision(RIGHT_PLAYER) || playerBottomBallCollision(RIGHT_PLAYER)) { std::cout << mBallPosition.x << " " << mBallPosition.y << "\n"; std::cout << mBlobPosition[RIGHT_PLAYER].x << " " << mBlobPosition[RIGHT_PLAYER].y << "\n"; return false; } return true; } #endif inline void set_fpu_single_precision() { #if defined(i386) || defined(__x86_64) // We need to set a precision for diverse x86 hardware #if defined(__GNUC__) volatile short cw; asm volatile ("fstcw %0" : "=m"(cw)); cw = cw & 0xfcff; asm volatile ("fldcw %0" :: "m"(cw)); #elif defined(_MSC_VER) short cw; asm fstcw cw; cw = cw & 0xfcff; asm fldcw cw; #endif #else #warning FPU precision may not conform to IEEE 754 #endif } blobby-1.0rc3/src/UserConfig.h0000644000175000017500000000440112042452374017551 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "IUserConfigReader.h" #include #include struct UserConfigVar { std::string Name; std::string Value; std::string DefaultValue; }; /*! \class UserConfig \brief user configuration from xml data \details This class manages user configurations read from/written to xml data. It allows saving/loading from disk and getting/setting floats, booleans, strings and integers by name */ class UserConfig: public IUserConfigReader { public: ~UserConfig(); bool loadFile(const std::string& filename); bool saveFile(const std::string& filename) const; void setValue(const std::string& name, const std::string& value); std::string getValue(const std::string& name) const; float getFloat(const std::string& name) const; std::string getString(const std::string& name) const; bool getBool(const std::string& name) const; int getInteger(const std::string& name) const; void setFloat(const std::string& name, float value); void setString(const std::string& name, const std::string& value); void setBool(const std::string& name, bool value); void setInteger(const std::string& name, int value); private: std::vector mVars; bool mChangeFlag; UserConfigVar *findVarByName(const std::string& Name) const; UserConfigVar* createVar(const std::string& name, const std::string& defaultValue); }; blobby-1.0rc3/src/SpeedController.h0000644000175000017500000000454012042452374020615 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once /// \brief class controlling game speed /// \details This class can control the game speed and the displayed FPS. /// It is updated once a frame and waits the necessary time. /// A distinction is made between game FPS and real FPS. /// Game FPS is the number of game loop iterations per second, /// real FPS is the number of screen updates per second. The real /// FPS is reached with framedropping /// The class can report how much time is actually waited. If this value /// is close to zero, the real speed can be altered. class SpeedController { public: SpeedController(float gameFPS); ~SpeedController(); void setGameSpeed(float fps); float getGameSpeed() const{return mGameFPS;} /// This reports whether a framedrop is necessary to hold the real FPS bool doFramedrop() const; /// gives the caller the fps of the drawn frames: int getFPS() const { return mFPS; } void setDrawFPS(bool draw) { mDrawFPS = draw; } //help methods bool getDrawFPS() const { return mDrawFPS; } /// This updates everything and waits the necessary time void update(); static void setMainInstance(SpeedController* inst) { mMainInstance = inst; } static SpeedController* getMainInstance() { return mMainInstance; } private: float mGameFPS; float mRealFPS; int mFPS; int mFPSCounter; bool mFramedrop; bool mDrawFPS; static SpeedController* mMainInstance; int mOldTicks; // internal data unsigned int mBeginSecond; int mCounter; }; blobby-1.0rc3/src/RenderManagerNull.h0000644000175000017500000000242412042452374021055 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "RenderManager.h" /*! \class RenderManagerNull \brief Render Manager for debugging purposes \details This Render Manager can be used to build a client that does no rendering, e.g. for bot tournaments, bot benchmarks or debugging. */ class RenderManagerNull : public RenderManager { public: virtual void draw() {}; }; blobby-1.0rc3/src/GenericIOFwd.h0000644000175000017500000000422312042452374017754 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once /** \file GenericIOFwd.h Including this header will declare the types \p GenericIn an \p GenericOut so they can be passed as parameters etc. If you wan't to use these classes for actual io, you have to include GenericIO.h. Including this header spares the compiler from having to parse all the template stuff, though. */ /// base class template for all GenericIO operations. do not use this class directly. /// use the provided typedefs GenericIn and GenericOut instead. template class GenericIO; // implementation detail, if you just use GenericIO, ignore this namespace ;) namespace detail { struct READER_TAG; struct WRITER_TAG; } // the two typedefs /// Base class for generic input operations. typedef GenericIO GenericIn; /// Base class for generic output operations. typedef GenericIO GenericOut; /// to make GenericIO support a user defined type, you have to implement /// the two functions in this template for that type. template struct UserSerializer { static void serialize( GenericOut& out, const T& value); static void serialize( GenericIn& in, T& value); }; blobby-1.0rc3/src/RenderManager.cpp0000644000175000017500000001415612042452374020562 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "RenderManager.h" /* includes */ #include "FileRead.h" /* implementation */ RenderManager* RenderManager::mSingleton = 0; RenderManager::RenderManager() : mDrawGame(false) { //assert(!mSingleton); if (mSingleton) { mSingleton->deinit(); delete mSingleton; } mSingleton = this; mMouseMarkerPosition = -100.0; mNeedRedraw = true; } SDL_Surface* RenderManager::highlightSurface(SDL_Surface* surface, int luminance) { SDL_Surface *newSurface = SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA | SDL_SRCCOLORKEY, surface->w, surface->h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000); SDL_BlitSurface(surface, 0, newSurface, 0); SDL_SetAlpha(newSurface, SDL_SRCALPHA, surface->format->alpha); SDL_SetColorKey(newSurface, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(newSurface->format, 0, 0, 0)); SDL_LockSurface(newSurface); for (int y = 0; y < surface->h; ++y) { for (int x = 0; x < surface->w; ++x) { SDL_Color* pixel = &(((SDL_Color*)newSurface->pixels) [y * newSurface->w +x]); if (surface->format->colorkey != ((Uint32*)newSurface->pixels)[y * newSurface->w +x]) { pixel->r = pixel->r + luminance > 255 ? 255 : pixel->r + luminance; pixel->g = pixel->g + luminance > 255 ? 255 : pixel->g + luminance; pixel->b = pixel->b + luminance > 255 ? 255 : pixel->b + luminance; } } } SDL_UnlockSurface(newSurface); // no DisplayFormatAlpha, because of problems with // OpenGL RenderManager SDL_Surface *convSurface = SDL_DisplayFormat(newSurface); SDL_FreeSurface(newSurface); return convSurface; } SDL_Surface* RenderManager::loadSurface(std::string filename) { FileRead file(filename); int fileLength = file.length(); // just read the whole file boost::shared_array fileContent = file.readRawBytes(fileLength); SDL_RWops* rwops = SDL_RWFromMem(fileContent.get(), fileLength); SDL_Surface* newSurface = SDL_LoadBMP_RW(rwops , 1); if (!newSurface) throw FileLoadException(filename); return newSurface; } SDL_Surface* RenderManager::createEmptySurface(unsigned int width, unsigned int height) { SDL_Surface* newSurface = SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA | SDL_SRCCOLORKEY, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000); return newSurface; } int RenderManager::getNextFontIndex(std::string& string) { if (string.empty()) return -1; int index = 47; wchar_t testChar = string.at(0); string.erase(string.begin()); if (testChar >= '0' && testChar <= '9') index = testChar - '0'; else if (testChar >= 'a' && testChar <= 'z') index = testChar - 'a' + 10; else if (testChar >= 'A' && testChar <= 'Z') index = testChar - 'A' + 10; else if (testChar == '.') index = 36; else if (testChar == '!') index = 37; else if (testChar == '(') index = 38; else if (testChar == ')') index = 39; else if (testChar == '\'') index = 44; else if (testChar == ':') index = 45; else if (testChar == ';') index = 46; else if (testChar == '?') index = 47; else if (testChar == ',') index = 48; else if (testChar == '/') index = 49; else if (testChar == '_') index = 50; else if (testChar == ' ') index = 51; else if (testChar == '-') index = 52; else if (testChar == '%') index = 53; else if (testChar == std::string("ß")[0]) // UTF-8 escape { testChar = string.at(0); string.erase(string.begin()); if (testChar == std::string("ß")[1]) index = 40; else if (testChar == std::string("ä")[1]) index = 41; else if (testChar == std::string("ö")[1]) index = 42; else if (testChar == std::string("ü")[1]) index = 43; else if (testChar == std::string("Ä")[1]) index = 41; else if (testChar == std::string("Ö")[1]) index = 42; else if (testChar == std::string("Ü")[1]) index = 43; } return index; } void RenderManager::setMouseMarker(float position) { mMouseMarkerPosition = position; } SDL_Rect RenderManager::blobRect(const Vector2& position) { SDL_Rect rect = { (short)(lround(position.x) - 37), (short)(lround(position.y) - 44), 75, 89 }; return rect; } SDL_Rect RenderManager::ballRect(const Vector2& position) { SDL_Rect rect = { (short)(lround(position.x) - 32), (short)(lround(position.y) - 32), 64, 64 }; return rect; } Vector2 RenderManager::ballShadowPosition(const Vector2& position) { return Vector2( position.x + (500.0 - position.y) / 4 + 16.0, 500.0 - (500.0 - position.y) / 16.0 - 10.0 ); } SDL_Rect RenderManager::ballShadowRect(const Vector2& position) { SDL_Rect rect = { short(lround(position.x) - 64), short(lround(position.y) - 16), 128, 32 }; return rect; } Vector2 RenderManager::blobShadowPosition(const Vector2& position) { return Vector2( position.x + (500.0 - position.y) / 4 + 16.0, 500.0 - (500.0 - position.y) / 16.0 - 10.0 ); } SDL_Rect RenderManager::blobShadowRect(const Vector2& position) { SDL_Rect rect = { short(lround(position.x) - 64), short(lround(position.y) - 16), 128, 32 }; return rect; } void RenderManager::redraw() { mNeedRedraw = true; } void RenderManager::drawGame(bool draw) { mDrawGame = draw; } void RenderManager::setTitle(const std::string& title) { SDL_WM_SetCaption(title.c_str(), ""); } blobby-1.0rc3/src/RenderManagerGP2X.cpp0000644000175000017500000002641012042452374021217 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "RenderManagerGP2X.h" /* includes */ #include "FileExceptions.h" /* implementation */ SDL_Surface* RenderManagerGP2X::colorSurface(SDL_Surface *surface, Color color) { SDL_Surface *newSurface = SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA | SDL_SRCCOLORKEY, surface->w, surface->h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000); SDL_BlitSurface(surface, 0, newSurface, 0); SDL_SetAlpha(newSurface, SDL_SRCALPHA, surface->format->alpha); SDL_SetColorKey(newSurface, SDL_SRCCOLORKEY, SDL_MapRGB(newSurface->format, 0, 0, 0)); SDL_LockSurface(newSurface); for (int y = 0; y < surface->h; ++y) for (int x = 0; x < surface->w; ++x) { SDL_Color* pixel = &(((SDL_Color*)newSurface->pixels) [y * newSurface->w +x]); int rr = (int(pixel->r) * int(color.r)) >> 8; int rg = (int(pixel->g) * int(color.g)) >> 8; int rb = (int(pixel->b) * int(color.b)) >> 8; int fak = int(pixel->r) * 5 - 4 * 256 - 138; if (fak > 0) { rr += fak; rg += fak; rb += fak; } pixel->r = rr < 255 ? rr : 255; pixel->g = rg < 255 ? rg : 255; pixel->b = rb < 255 ? rb : 255; } SDL_UnlockSurface(newSurface); SDL_Surface *convSurface = SDL_DisplayFormat(newSurface); SDL_FreeSurface(newSurface); return convSurface; } RenderManagerGP2X::RenderManagerGP2X() : RenderManager() { } RenderManager* RenderManager::createRenderManagerGP2X() { return new RenderManagerGP2X(); } void RenderManagerGP2X::init(int xResolution, int yResolution, bool fullscreen) { Uint32 screenFlags = SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF; // screenFlags |= SDL_FULLSCREEN; mScreen = SDL_SetVideoMode(320, 240, 0, screenFlags); SDL_ShowCursor(0); SDL_Surface* tempBackground = loadSurface("backgrounds/strand2.bmp"); mBackground = SDL_DisplayFormat(tempBackground); SDL_FreeSurface(tempBackground); for (int i = 1; i <= 16; ++i) { char filename[64]; sprintf(filename, "gf2x/ball%02d.bmp", i); SDL_Surface* ballImage = loadSurface(filename); SDL_SetColorKey(ballImage, SDL_SRCCOLORKEY, SDL_MapRGB(ballImage->format, 0, 0, 0)); SDL_Surface *convertedBallImage = SDL_DisplayFormat(ballImage); SDL_FreeSurface(ballImage); mBall.push_back(convertedBallImage); } SDL_Surface *tempBallShadow = loadSurface("gf2x/schball.bmp"); SDL_SetColorKey(tempBallShadow, SDL_SRCCOLORKEY, SDL_MapRGB(tempBallShadow->format, 0, 0, 0)); SDL_SetAlpha(tempBallShadow, SDL_SRCALPHA, 127); mBallShadow = SDL_DisplayFormat(tempBallShadow); SDL_FreeSurface(tempBallShadow); for (int i = 1; i <= 5; ++i) { char filename[64]; sprintf(filename, "gf2x/blobbym%d.bmp", i); SDL_Surface* blobImage = loadSurface(filename); mStandardBlob.push_back(blobImage); mLeftBlob.push_back(colorSurface(blobImage, Color(255, 0, 0))); mRightBlob.push_back(colorSurface(blobImage, Color(0, 255, 0))); sprintf(filename, "gf2x/sch1%d.bmp", i); SDL_Surface* blobShadow = loadSurface(filename); SDL_SetColorKey(blobShadow, SDL_SRCCOLORKEY, SDL_MapRGB(blobShadow->format, 0, 0, 0)); SDL_SetAlpha(blobShadow, SDL_SRCALPHA, 127); mStandardBlobShadow.push_back(blobShadow); mLeftBlobShadow.push_back( colorSurface(blobShadow, Color(255, 0, 0))); mRightBlobShadow.push_back( colorSurface(blobShadow, Color(0, 255, 0))); } for (int i = 0; i <= 53; ++i) { char filename[64], filename2[64]; sprintf(filename, "gf2x/font%02d.bmp", i); sprintf(filename2, "gf2x/font_small/font%02d.bmp", i); SDL_Surface *tempFont = loadSurface(filename); SDL_Surface *tempFont2 = loadSurface(filename2); SDL_SetColorKey(tempFont, SDL_SRCCOLORKEY, SDL_MapRGB(tempFont->format, 0, 0, 0)); SDL_SetColorKey(tempFont2, SDL_SRCCOLORKEY, SDL_MapRGB(tempFont2->format, 0, 0, 0)); SDL_Surface *newFont = SDL_DisplayFormat(tempFont); SDL_Surface *newFont2 = SDL_DisplayFormat(tempFont2); SDL_FreeSurface(tempFont); SDL_FreeSurface(tempFont2); mFont.push_back(newFont); mHighlightFont.push_back(highlightSurface(newFont, 60)); mSmallFont.push_back(newFont2); mHighlightSmallFont.push_back(highlightSurface(newFont2, 60)); } } void RenderManagerGP2X::deinit() { SDL_FreeSurface(mBackground); SDL_FreeSurface(mBallShadow); for (unsigned int i = 0; i < mBall.size(); ++i) SDL_FreeSurface(mBall[i]); for (unsigned int i = 0; i < mStandardBlob.size(); ++i) { SDL_FreeSurface(mStandardBlob[i]); SDL_FreeSurface(mStandardBlobShadow[i]); SDL_FreeSurface(mLeftBlob[i]); SDL_FreeSurface(mLeftBlobShadow[i]); SDL_FreeSurface(mRightBlob[i]); SDL_FreeSurface(mRightBlobShadow[i]); } for (unsigned int i = 0; i < mFont.size(); ++i) { SDL_FreeSurface(mFont[i]); SDL_FreeSurface(mHighlightFont[i]); SDL_FreeSurface(mSmallFont[i]); SDL_FreeSurface(mHighlightSmallFont[i]); } } void RenderManagerGP2X::draw() { if (!mDrawGame) return; SDL_BlitSurface(mBackground, 0, mScreen, 0); int animationState; SDL_Rect position; // Ball marker Uint8 markerColor = SDL_GetTicks() % 1000 >= 500 ? 255 : 0; position.y = 5; position.x = lround(mBallPosition.x - 2.5); position.w = 5; position.h = 5; SDL_FillRect(mScreen, &position, SDL_MapRGB(mScreen->format, markerColor, markerColor, markerColor)); // Ball Shadow position.x = lround(mBallPosition.x) + (200 - lround(mBallPosition.y)) / 4 - 19; position.y = 200 - (200 - lround(mBallPosition.y)) / 16 - 5; SDL_BlitSurface(mBallShadow, 0, mScreen, &position); // Left blob shadow position.x = lround(mLeftBlobPosition.x) + (200 - lround(mLeftBlobPosition.y)) / 4 - 19; position.y = 200 - (200 - lround(mLeftBlobPosition.y)) / 16 - 10; animationState = int(mLeftBlobAnimationState) % 5; SDL_BlitSurface(mLeftBlobShadow[animationState], 0, mScreen, &position); // Right blob shadow position.x = lround(mRightBlobPosition.x) + (200 - lround(mRightBlobPosition.y)) / 4 - 19; position.y = 200 - (200 - lround(mRightBlobPosition.y)) / 16 - 10; animationState = int(mRightBlobAnimationState) % 5; SDL_BlitSurface(mRightBlobShadow[animationState], 0, mScreen, &position); // Restore the rod position.x = 160 - 3; position.y = 120; SDL_Rect rodPosition; rodPosition.x = 160 - 3; rodPosition.y = 120; rodPosition.w = 7; rodPosition.h = 120; SDL_BlitSurface(mBackground, &rodPosition, mScreen, &position); // Drawing the Ball position.x = lround(mBallPosition.x) - 13; position.y = lround(mBallPosition.y) - 13; animationState = int(mBallRotation / M_PI / 2 * 16) % 16; SDL_BlitSurface(mBall[animationState], 0, mScreen, &position); // Drawing left blob position.x = lround(mLeftBlobPosition.x) - 15; position.y = lround(mLeftBlobPosition.y) - 18; animationState = int(mLeftBlobAnimationState) % 5; SDL_BlitSurface(mLeftBlob[animationState], 0, mScreen, &position); // Drawing right blob position.x = lround(mRightBlobPosition.x) - 15; position.y = lround(mRightBlobPosition.y) - 18; animationState = int(mRightBlobAnimationState) % 5; SDL_BlitSurface(mRightBlob[animationState], 0, mScreen, &position); // Drawing the score char textBuffer[8]; snprintf(textBuffer, 8, mLeftPlayerWarning ? "%02d!" : "%02d", mLeftPlayerScore); drawText(textBuffer, Vector2(20, 10), false); snprintf(textBuffer, 8, mRightPlayerWarning ? "%02d!" : "%02d", mRightPlayerScore); drawText(textBuffer, Vector2(320 - 65, 10), false); // Drawing the names drawText(mLeftPlayerName, Vector2(12, 550), false); drawText(mRightPlayerName, Vector2(788-(24*mRightPlayerName.length()), 550), false); // Drawing the clock drawText(mTime, Vector2(400 - mTime.length()*12, 24), false); } bool RenderManagerGP2X::setBackground(const std::string& filename) { try { SDL_Surface *tempBackground = loadSurface(filename); mBackground = SDL_DisplayFormat(tempBackground); SDL_FreeSurface(tempBackground); } catch (FileLoadException) { return false; } return true; } void RenderManagerGP2X::setBlobColor(int player, Color color) { std::vector *handledBlob = 0; std::vector *handledBlobShadow = 0; if (player == LEFT_PLAYER) { handledBlob = &mLeftBlob; handledBlobShadow = &mLeftBlobShadow; } if (player == RIGHT_PLAYER) { handledBlob = &mRightBlob; handledBlobShadow = &mRightBlobShadow; } for (int i = 0; i < 5; ++i) { SDL_FreeSurface((*handledBlob)[i]); SDL_FreeSurface((*handledBlobShadow)[i]); } handledBlob->clear(); handledBlobShadow->clear(); for (int i = 0; i < 5; ++i) { SDL_Surface *tempBlob = colorSurface(mStandardBlob[i], color); handledBlob->push_back( SDL_DisplayFormat(tempBlob)); SDL_FreeSurface(tempBlob); SDL_Surface *tempShadow = colorSurface( mStandardBlobShadow[i], color); handledBlobShadow->push_back( SDL_DisplayFormat(tempShadow)); SDL_FreeSurface(tempShadow); } } void RenderManagerGP2X::setBall(const Vector2& position, float rotation) { mBallPosition = position * 0.4; mBallRotation = rotation; } void RenderManagerGP2X::setBlob(int player, const Vector2& position, float animationState) { if (player == LEFT_PLAYER) { mLeftBlobPosition = position * 0.4; mLeftBlobAnimationState = animationState; } if (player == RIGHT_PLAYER) { mRightBlobPosition = position * 0.4; mRightBlobAnimationState = animationState; } } void RenderManagerGP2X::setScore(int leftScore, int rightScore, bool leftWarning, bool rightWarning) { mLeftPlayerScore = leftScore; mRightPlayerScore = rightScore; mLeftPlayerWarning = leftWarning; mRightPlayerWarning = rightWarning; } void RenderManagerGP2X::setTime(const std::string& t) { mTime = t; } void RenderManagerGP2X::drawText(const std::string& text, Vector2 position, unsigned int flags) { int FontSize = (flags & TF_SMALL_FONT ? FONT_WIDTH_SMALL : FONT_WIDTH_NORMAL); int length = 0; std::string string = text; int index = getNextFontIndex(string); while (index != -1) { if (flags & TF_OBFUSCATE) index = FONT_INDEX_ASTERISK; length += FontSize; SDL_Rect charPosition; charPosition.x = lround(position.x) + length - FontSize; charPosition.y = lround(position.y); if (flags & TF_SMALL_FONT) if (flags & TF_HIGHLIGHT) SDL_BlitSurface( mHighlightSmallFont[index], 0, mScreen, &charPosition ); else SDL_BlitSurface( mSmallFont[index], 0, mScreen, &charPosition ); else if (flags & TF_HIGHLIGHT) SDL_BlitSurface( mHighlightFont[index], 0, mScreen, &charPosition ); else SDL_BlitSurface( mFont[index], 0, mScreen, &charPosition ); index = getNextFontIndex(string); } } void RenderManagerGP2X::refresh() { SDL_Flip(mScreen); } blobby-1.0rc3/src/DuelMatchState.h0000644000175000017500000000231412042452374020355 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "PhysicState.h" #include "GameLogicState.h" struct DuelMatchState { void swapSides(); bool operator==(const DuelMatchState& other) const; PhysicState worldState; GameLogicState logicState; }; blobby-1.0rc3/src/IMGUI.h0000644000175000017500000000644612042452374016372 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "Vector.h" #include "Global.h" #include "InputManager.h" #include "RenderManager.h" #include "TextManager.h" /// needed because we can't forward declare that enum // Warning: This may explode if we use the GUI from several files #define GEN_ID (__LINE__) enum KeyAction { UP, DOWN, LEFT, RIGHT, SELECT, NONE }; enum SelectBoxAction { SBA_NONE = 0, SBA_SELECT, SBA_DBL_CLICK }; /*! \class IMGUI \brief GUI Manager \details This class manages drawing and input handling of the blobby GUI. It is poorly designed, does not use OOP and makes extension difficult, so it needs a complete rewrite. */ class IMGUI { public: static IMGUI& getSingleton(); void begin(); void end(); void resetSelection(); void doImage(int id, const Vector2& position, const std::string& name); void doText(int id, const Vector2& position, const std::string& text, unsigned int flags = TF_NORMAL); void doText(int id, const Vector2& position, TextManager::STRING text, unsigned int flags = TF_NORMAL); void doOverlay(int id, const Vector2& pos1, const Vector2& pos2, const Color& col = Color(0, 0, 0)); void doCursor(bool draw = true) { mDrawCursor = draw; mUsingCursor = true; } bool doButton(int id, const Vector2& position, const std::string& text, unsigned int flags = TF_NORMAL); bool doButton(int id, const Vector2& position, TextManager::STRING text, unsigned int flags = TF_NORMAL); bool doScrollbar(int id, const Vector2& position, float& value); bool doEditbox(int id, const Vector2& position, int length, std::string& text, unsigned& cpos, unsigned int flags = TF_NORMAL, bool force_active = false); SelectBoxAction doSelectbox(int id, const Vector2& pos1, const Vector2& pos2, const std::vector& entries, int& selected, unsigned int flags = TF_NORMAL); void doChatbox(int id, const Vector2& pos1, const Vector2& pos2, const std::vector& entries, int& selected, const std::vector& local, unsigned int flags = TF_NORMAL); bool doBlob(int id, const Vector2& position, const Color& col); bool usingCursor() const; void doInactiveMode(bool inactive) { mInactive = inactive; } private: IMGUI(); ~IMGUI(); static IMGUI* mSingleton; int mActiveButton; int mHeldWidget; KeyAction mLastKeyAction; int mLastWidget; bool mDrawCursor; bool mButtonReset; bool mInactive; bool mUsingCursor; }; blobby-1.0rc3/src/SoundManager.cpp0000644000175000017500000001424112042452374020426 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "SoundManager.h" /* includes */ #include #include #include "Global.h" #include "FileRead.h" /* implementation */ SoundManager* SoundManager::mSingleton; Sound* SoundManager::loadSound(const std::string& filename) { FileRead file(filename); int fileLength = file.length(); // safe file data into a shared_array to ensure it is deleten properly // in case of exceptions boost::shared_array fileBuffer = file.readRawBytes( fileLength ); SDL_RWops* rwops = SDL_RWFromMem(fileBuffer.get(), fileLength); // we don't need the file handle anymore file.close(); SDL_AudioSpec newSoundSpec; Uint8* newSoundBuffer; Uint32 newSoundLength; if (!SDL_LoadWAV_RW(rwops , 1, &newSoundSpec, &newSoundBuffer, &newSoundLength)) throw FileLoadException(filename); // check if current audio format is target format if (newSoundSpec.freq == mAudioSpec.freq && newSoundSpec.format == mAudioSpec.format && newSoundSpec.channels == mAudioSpec.channels) { Sound *newSound = new Sound; newSound->data = new Uint8[newSoundLength]; memcpy(newSound->data, newSoundBuffer, newSoundLength); newSound->length = newSoundLength; newSound->position = 0; SDL_FreeWAV(newSoundBuffer); return newSound; } else // otherwise, convert audio { SDL_AudioCVT conversionStructure; if (!SDL_BuildAudioCVT(&conversionStructure, newSoundSpec.format, newSoundSpec.channels, newSoundSpec.freq, mAudioSpec.format, mAudioSpec.channels, mAudioSpec.freq)) throw FileLoadException(filename); conversionStructure.buf = new Uint8[newSoundLength * conversionStructure.len_mult]; memcpy(conversionStructure.buf, newSoundBuffer, newSoundLength); conversionStructure.len = newSoundLength; if (SDL_ConvertAudio(&conversionStructure)) throw FileLoadException(filename); SDL_FreeWAV(newSoundBuffer); Sound *newSound = new Sound; newSound->data = conversionStructure.buf; newSound->length = Uint32(conversionStructure.len_cvt); newSound->position = 0; return newSound; } } bool SoundManager::playSound(const std::string& filename, float volume) { if (!mInitialised) return false; // everything is fine, so we return true // but we don't need to play the sound if( mMute ) return true; try { Sound* soundBuffer = mSound[filename]; if (!soundBuffer) { soundBuffer = loadSound(filename); mSound[filename] = soundBuffer; } Sound soundInstance = Sound(*soundBuffer); soundInstance.volume = volume > 0.0 ? (volume < 1.0 ? volume : 1.0) : 0.0; SDL_LockAudio(); mPlayingSound.push_back(soundInstance); SDL_UnlockAudio(); } catch (const FileLoadException& exception) { std::cerr << "Warning: " << exception.what() << std::endl; return false; } return true; } bool SoundManager::init() { SDL_AudioSpec desiredSpec; desiredSpec.freq = 44100; desiredSpec.format = AUDIO_S16LSB; desiredSpec.channels = 2; desiredSpec.samples = 1024; desiredSpec.callback = playCallback; desiredSpec.userdata = mSingleton; if (SDL_OpenAudio(&desiredSpec, &mAudioSpec)) { std::cerr << "Warning: Couldn't open audio Device!" << std::endl; std::cerr << "Reason: " << SDL_GetError() << std::endl; return false; } SDL_PauseAudio(0); mInitialised = true; mVolume = 1.0; return true; } void SoundManager::playCallback(void* singleton, Uint8* stream, int length) { std::list& playingSound = ((SoundManager*)singleton)->mPlayingSound; int volume = int(SDL_MIX_MAXVOLUME * ((SoundManager*)singleton)->mVolume); for (std::list::iterator iter = playingSound.begin(); iter != playingSound.end(); ++iter) { int bytesLeft = iter->length - iter->position; if (bytesLeft < length) { SDL_MixAudio(stream, iter->data + iter->position, bytesLeft, int(volume * iter->volume)); std::list::iterator eraseIter = iter; std::list::iterator nextIter = ++iter; playingSound.erase(eraseIter); iter = nextIter; // prevents increment of past-end-interator if(iter == playingSound.end()) break; } else { SDL_MixAudio(stream, iter->data + iter->position, length, int(volume * iter->volume)); iter->position += length; } } } void SoundManager::deinit() { for (std::map::iterator iter = mSound.begin(); iter != mSound.end(); ++iter) { if (iter->second) { if (iter->second->data != 0) { delete[] iter->second->data; } delete iter->second; } } SDL_CloseAudio(); mInitialised = false; } SoundManager* SoundManager::createSoundManager() { return new SoundManager(); } SoundManager::SoundManager() { mMute = false; mSingleton = this; mInitialised = false; } SoundManager::~SoundManager() { if (mInitialised) { deinit(); } } SoundManager& SoundManager::getSingleton() { assert(mSingleton); return *mSingleton; } void SoundManager::setVolume(float volume) { volume = volume > 0.0 ? (volume < 1.0 ? volume : 1.0) : 0.0; mVolume = volume; } void SoundManager::setMute(bool mute) { static bool locked = false; if (mute) { if (!locked) //locking audio twice leads to a bug(??) { locked = true; SDL_LockAudio(); } } else { mPlayingSound.clear(); SDL_UnlockAudio(); locked = false; } mMute = mute; SDL_PauseAudio((int)mute); } blobby-1.0rc3/src/ScriptedInputSource.h0000644000175000017500000001040012042452374021457 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "Global.h" #include "InputSource.h" #include "Vector.h" #include #include /// \class ScriptedInputSource /// \brief Bot controller /// \details ScriptedInputSource provides an implementation of InputSource, which uses /// Lua scripts to get its input. The given script is automatically initialised /// and provided with an interface to the game. /// The API documentation can now be found in doc/ScriptAPI.txt // The time the bot waits after game start const int WAITING_TIME = 1500; class lua_State; class DuelMatch; class ScriptedInputSource : public InputSource { public: /// The constructor automatically loads and initializes the script /// with the given filename. The side parameter tells the script /// which side is it on. ScriptedInputSource(const std::string& filename, PlayerSide side, unsigned int difficulty); ~ScriptedInputSource(); virtual PlayerInput getInput(); private: /// this variable saves the current match /// it is set each step in getInput /// as in current design, only in single match mode bots are allowed /// it would even be enough to set it once, but we may change this /// for making bot tournaments^^, so the idea of setting it for each /// bot seems better to me static DuelMatch* mMatch; static ScriptedInputSource* mCurrentSource; // helpers static int debug(lua_State* state); static void setflags(lua_State* state); // coordinate system conversion template struct coordinate; // commands static int jump(lua_State* state); static int left(lua_State* state); static int right(lua_State* state); static int moveto(lua_State* state); // ball information // internals static const Vector2& getBallPosition(); static const Vector2& getBallVelocity(); static int touches(lua_State* state); static int ballx(lua_State* state); static int bally(lua_State* state); static int bspeedx(lua_State* state); static int bspeedy(lua_State* state); // blob information static int launched(lua_State* state); static int posx(lua_State* state); static int posy(lua_State* state); static int oppx(lua_State* state); static int oppy(lua_State* state); // game status static int getScore(lua_State* state); static int getOppScore(lua_State* state); static int getScoreToWin(lua_State* state); static int getGameTime(lua_State* state); // predictions static int estimate(lua_State* state); // deprecated static int estimx(lua_State* state); // deprecated static int estimy(lua_State* state); // deprecated static int predictx(lua_State* state); static int predicty(lua_State* state); static int timetox(lua_State* state); static int timetoy(lua_State* state); static int xaty(lua_State* state); static int yatx(lua_State* state); static int nextevent(lua_State* state); static int predictImpact(lua_State* state); lua_State* mState; unsigned int mStartTime; // ki strength values unsigned int mMaxDelay; unsigned int mCurDelay; // which functions are available bool mOnBounce; // ball position and velocity in adapted coordinate system boost::circular_buffer mBallPositions; boost::circular_buffer mBallVelocities; float mLastBallSpeed; float mLastBallSpeedVirtual; PlayerSide mSide; // input data bool mLeft, mRight, mJump; }; blobby-1.0rc3/src/ReplayLoader.cpp0000644000175000017500000002263312042452374020432 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "IReplayLoader.h" /* includes */ #include #include #include #include #include // debugging #include #include #include "InputSource.h" #include "FileRead.h" #include "GenericIO.h" /* implementation */ IReplayLoader* IReplayLoader::createReplayLoader(const std::string& filename) { // do some generic loading stuff: // first, try to open the file boost::shared_ptr file = boost::make_shared(filename); // then, check the file length. We need at least 12 bytes. int fileLength = file->length(); if (fileLength < 12) { /// \todo add some error handling here! return 0; } // check if file contains a valid BV2 header char header[4]; file->readRawBytes(header, sizeof(header)); if (memcmp(&header, &validHeader, 4) != 0) { /// \todo add some error handling here! return 0; } // now, find out which version we need! char version[4]; file->readRawBytes(version, sizeof(version)); // now we got our version number. int major = version[1]; int minor = version[2]; // read checksum uint32_t checksum; file->readRawBytes((char*)&checksum, 4); // calculate reference checksum uint32_t refchecksum = file->calcChecksum(file->tell()); if(refchecksum != checksum && major > 0 && minor > 0) { throw(ChecksumException(file->getFileName(), checksum, refchecksum)); } IReplayLoader* loader = createReplayLoader(major); boost::shared_ptr in = createGenericReader(file); loader->initLoading(in, minor); return loader; } /*************************************************************************************************** R E P L A Y L O A D E R V 0.1 ***************************************************************************************************/ // That version used different physics than we use now, and it does not include any save-points that would // allow to extrapolate the match, so we could not play these matches, even if we had a loader for them // // ------------------------------------------------------------------------------------------------- // /*************************************************************************************************** R E P L A Y L O A D E R V 1.x ***************************************************************************************************/ /*! \class ReplayLoader_V1X \brief Replay Loader V 1.x \details Replay Loader for 1.0 and 1.1 replays */ class ReplayLoader_V1X: public IReplayLoader { public: ReplayLoader_V1X() {}; virtual ~ReplayLoader_V1X() { }; virtual int getVersionMajor() const { return 1; }; virtual int getVersionMinor() const { return 1; }; virtual std::string getPlayerName(PlayerSide player) const { if(player == LEFT_PLAYER) return mLeftPlayerName; if(player == RIGHT_PLAYER) return mRightPlayerName; assert(0); } virtual Color getBlobColor(PlayerSide player) const { if(player == LEFT_PLAYER) return mLeftColor; if(player == RIGHT_PLAYER) return mRightColor; assert(0); } virtual int getFinalScore(PlayerSide player) const { if(player == LEFT_PLAYER) return mLeftFinalScore; if(player == RIGHT_PLAYER) return mRightFinalScore; assert(0); } virtual int getSpeed() const { return mGameSpeed; }; virtual int getDuration() const { return mGameDuration; }; virtual int getLength() const { return mGameLength; }; virtual std::time_t getDate() const { return mGameDate; }; virtual void getInputAt(int step, PlayerInput& left, PlayerInput& right) { assert( step < mGameLength ); // for now, we have only a linear sequence of InputPackets, so finding the right one is just // a matter of address arithmetics. // each packet has size 1 byte for now // so we find step at mReplayOffset + step char packet = mBuffer[mReplayOffset + step]; // now read the packet data left.set((bool)(packet & 32), (bool)(packet & 16), (bool)(packet & 8)); right.set((bool)(packet & 4), (bool)(packet & 2), (bool)(packet & 1)); } virtual bool isSavePoint(int position, int& save_position) const { if(position % 750 == 0 && mReplayFormatVersion != 0) { save_position = position / 750; return true; } return false; } virtual int getSavePoint(int targetPosition, int& savepoint) const { /// \todo detect if replay contains safepoints int index = targetPosition / 750; savepoint = index * 750; if(index < mSavePoints.size()) return index; return -1; } virtual void readSavePoint(int index, ReplaySavePoint& state) const { state = mSavePoints.at(index); } private: virtual void initLoading(boost::shared_ptr file, int minor_version) { mReplayFormatVersion = minor_version; mSavePoints.resize(0); /// \todo check if minor_version < getVersionMinor, otherwise issue a warning // we start with the replay header. uint32_t header_size, attr_ptr , attr_size , jptb_ptr, jptb_size , data_ptr , data_size, states_ptr, states_size; file->uint32(header_size); file->uint32(attr_ptr); file->uint32(attr_size); file->uint32(jptb_ptr); file->uint32(jptb_size); file->uint32(data_ptr); file->uint32(data_size); // legacy support for 1.0 RC 1 replays if(minor_version != 0) { file->uint32(states_ptr); file->uint32(states_size); } // now, we read the attributes section // jump over the attr - marker file->seek(attr_ptr + 4); // copy attributes into buffer // read the attributes file->uint32(mGameSpeed); file->uint32(mGameDuration); file->uint32(mGameLength); file->uint32(mGameDate); file->generic (mLeftColor); file->generic (mRightColor); if(minor_version != 0) { file->uint32(mLeftFinalScore); file->uint32(mRightFinalScore); file->string(mLeftPlayerName); file->string(mRightPlayerName); } else { mLeftPlayerName = ""; unsigned char c; do { file->byte(c); mLeftPlayerName += c; } while(c); mRightPlayerName = ""; do { file->byte(c); mRightPlayerName += c; } while(c); } // now, read the raw data file->seek(data_ptr + 8); // jump over the dat marker and over the length value /// \todo why do we set mBufferSize again? should we check if these two are identical // read into buffer std::cout << "read into buffer\n"; std::cout << "length: " << mGameLength << " " << data_size << "\n"; mBuffer = boost::shared_array(new char[data_size]); file->array(mBuffer.get(), data_size); mReplayOffset = 0; // now read safepoints if(minor_version != 0) { file->seek(states_ptr + 4); // jump over the sta marker file->uint32(mSavePointsCount); std::cout << "read " << mSavePointsCount << " safe points\n"; mSavePoints.reserve(mSavePointsCount); for(int i = 0; i < mSavePointsCount; ++i) { DuelMatchState ms; file->generic(ms); ReplaySavePoint sp; sp.state = ms; sp.step = 750 * i; mSavePoints.push_back(sp); } } else { mSavePointsCount = 0; } /// \todo check that mSafePointsCount and states_size match } boost::shared_array mBuffer; uint32_t mReplayOffset; std::vector mSavePoints; uint32_t mSavePointsCount; // specific data std::string mLeftPlayerName; std::string mRightPlayerName; unsigned int mLeftFinalScore; unsigned int mRightFinalScore; unsigned int mGameSpeed; unsigned int mGameDate; unsigned int mGameLength; unsigned int mGameDuration; Color mLeftColor; Color mRightColor; unsigned char mReplayFormatVersion; }; IReplayLoader* IReplayLoader::createReplayLoader(int major) { // we find a loader depending on major version /// \todo throw, when version is too old switch(major) { case 0: 0; break; case 1: return new ReplayLoader_V1X(); break; } // fallback return 0; } blobby-1.0rc3/src/main.cpp0000644000175000017500000002015112042452374016764 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* includes */ #include #include #include #include #include #include "config.h" #include "RenderManager.h" #include "SoundManager.h" #include "InputManager.h" #include "TextManager.h" #include "UserConfig.h" #include "IMGUI.h" #include "SpeedController.h" #include "Blood.h" #include "FileSystem.h" #include "state/State.h" #if defined(WIN32) #ifndef GAMEDATADIR #define GAMEDATADIR "data" #endif #endif #ifdef WIN32 #include #undef main #endif /* implementation */ void deinit() { RenderManager::getSingleton().deinit(); SoundManager::getSingleton().deinit(); /// \todo this is more a hack than a real solution /// how do we make sure our current state /// is certainly destructed properly? delete State::getCurrentState(); SDL_Quit(); } void setupPHYSFS() { FileSystem& fs = FileSystem::getSingleton(); std::string separator = fs.getDirSeparator(); // Game should be playable out of the source package on all // platforms fs.addToSearchPath("data"); fs.addToSearchPath("data/gfx.zip"); fs.addToSearchPath("data/sounds.zip"); fs.addToSearchPath("data/scripts.zip"); fs.addToSearchPath("data/backgrounds.zip"); #if defined(WIN32) // Just write in installation directory fs.setWriteDir("data"); #else // handle the case when it is installed fs.addToSearchPath(BLOBBY_INSTALL_PREFIX "/share/blobby"); fs.addToSearchPath(BLOBBY_INSTALL_PREFIX "/share/blobby/gfx.zip"); fs.addToSearchPath(BLOBBY_INSTALL_PREFIX "/share/blobby/sounds.zip"); fs.addToSearchPath(BLOBBY_INSTALL_PREFIX "/share/blobby/scripts.zip"); fs.addToSearchPath(BLOBBY_INSTALL_PREFIX "/share/blobby/backgrounds.zip"); // Create a search path in the home directory and ensure that // all paths exist and are actually directories std::string userdir = fs.getUserDir(); std::string userAppend = ".blobby"; std::string homedir = userdir + userAppend; /// \todo please review this code and determine if we really need to add userdir to serach path /// only to remove it later fs.setWriteDir(userdir); fs.probeDir(userAppend); /// \todo why do we need separator here? fs.probeDir(userAppend + separator + "replays"); fs.probeDir(userAppend + separator + "gfx"); fs.probeDir(userAppend + separator + "sounds"); fs.probeDir(userAppend + separator + "scripts"); fs.probeDir(userAppend + separator + "backgrounds"); fs.removeFromSearchPath(userdir); // here we set the write dir anew! fs.setWriteDir(homedir); #if defined(GAMEDATADIR) { // A global installation path makes only sense on non-Windows // platforms std::string basedir = GAMEDATADIR; fs.addToSearchPath(basedir); fs.addToSearchPath(basedir + separator + "gfx.zip"); fs.addToSearchPath(basedir + separator + "sounds.zip"); fs.addToSearchPath(basedir + separator + "scripts.zip"); fs.addToSearchPath(basedir + separator + "backgrounds.zip"); } #endif #endif } #undef main extern "C" int main(int argc, char* argv[]) { FileSystem filesys(argv[0]); setupPHYSFS(); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_EnableUNICODE(1); atexit(SDL_Quit); srand(SDL_GetTicks()); // Default is OpenGL and false // choose renderer RenderManager *rmanager = 0; SoundManager *smanager = 0; // Test Version Startup Warning #ifdef TEST_VERSION struct tm* ptm; time_t time = std::time(0); ptm = gmtime ( &time ); if( ptm->tm_year > (2012-1900) || ptm->tm_mon >= 4 ) { #ifdef WIN32 MessageBox(0, (std::string("This is a test version of ") + AppTitle + " which expired on " "1.5.2012. Please visit blobby.sourceforge.net for a newer version").c_str(), "TEST VERISON OUTDATED", MB_OK); #endif return -1; } #ifdef WIN32 MessageBox(0, (std::string("This is a test version of ") + AppTitle + " for testing only.\n" "It might be unstable and/or incompatible to the current release. " "Use of this version is limited to 1.5.2012.\nUntil then, " "the final version will most likely be released and you should update to that one.\n" "Visit blobby.sourceforge.net for more information or bug reporting.").c_str(), "TEST VERISON WARNING", MB_OK); #endif #endif try { UserConfig gameConfig; gameConfig.loadFile("config.xml"); TextManager::createTextManager(gameConfig.getString("language")); if(gameConfig.getString("device") == "SDL") rmanager = RenderManager::createRenderManagerSDL(); else if (gameConfig.getString("device") == "GP2X") rmanager = RenderManager::createRenderManagerGP2X(); else if (gameConfig.getString("device") == "OpenGL") rmanager = RenderManager::createRenderManagerGL2D(); else { std::cerr << "Warning: Unknown renderer selected!"; std::cerr << "Falling back to OpenGL" << std::endl; rmanager = RenderManager::createRenderManagerGL2D(); } // fullscreen? if(gameConfig.getString("fullscreen") == "true") rmanager->init(800, 600, true); else rmanager->init(800, 600, false); if(gameConfig.getString("show_shadow") == "true") rmanager->showShadow(true); else rmanager->showShadow(false); SpeedController scontroller(gameConfig.getFloat("gamefps")); SpeedController::setMainInstance(&scontroller); scontroller.setDrawFPS(gameConfig.getBool("showfps")); smanager = SoundManager::createSoundManager(); smanager->init(); smanager->setVolume(gameConfig.getFloat("global_volume")); smanager->setMute(gameConfig.getBool("mute")); /// \todo play sound is misleading. what we actually want to do is load the sound smanager->playSound("sounds/bums.wav", 0.0); smanager->playSound("sounds/pfiff.wav", 0.0); std::string bg = std::string("backgrounds/") + gameConfig.getString("background"); if ( FileSystem::getSingleton().exists(bg) ) rmanager->setBackground(bg); InputManager* inputmgr = InputManager::createInputManager(); int running = 1; while (running) { inputmgr->updateInput(); running = inputmgr->running(); IMGUI::getSingleton().begin(); State::getCurrentState()->step(); rmanager = &RenderManager::getSingleton(); //RenderManager may change //draw FPS: static int lastfps = 0; if (scontroller.getDrawFPS()) { // We need to ensure that the title bar is only set // when the framerate changed, because setting the // title can ne quite resource intensive on some // windows manager, like for example metacity. int newfps = scontroller.getFPS(); if (newfps != lastfps) { std::stringstream tmp; tmp << AppTitle << " FPS: " << newfps; rmanager->setTitle(tmp.str()); } lastfps = newfps; } // Dirty workarround for hiding fps in title if (!scontroller.getDrawFPS() && (lastfps != -1)) { std::stringstream tmp; tmp << AppTitle; rmanager->setTitle(tmp.str()); lastfps = -1; } if (!scontroller.doFramedrop()) { rmanager->draw(); IMGUI::getSingleton().end(); BloodManager::getSingleton().step(); rmanager->refresh(); } scontroller.update(); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; if (rmanager) rmanager->deinit(); if (smanager) smanager->deinit(); SDL_Quit(); exit (EXIT_FAILURE); } deinit(); exit(EXIT_SUCCESS); } blobby-1.0rc3/src/File.h0000644000175000017500000001461712042452374016376 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include #include #include "FileExceptions.h" /** \class File \brief convenience wrapper around PHYSFS_file \details This class provides object oriented access to physfs files. Furthermore, it automatically closes opened files on destruction to prevent leaks. For now, it is defined as noncopyable, because we don't have implemented any copying behaviour yet. This class is not intended for direct use, it is a base class for FileRead and FileWrite which provide read/write functionality respectively, so it is impossible to accidentially read from a file opened for writing. \exception PhysfsException When any physfs function call reported an error, this exception is thrown. Its what() string contains the error message physfs created. \todo write tests for this class. */ class File : boost::noncopyable { public: /// \brief closes the file /// \details This function does nothing when no file is opened. /// This behaviour is necessary as this function is called in the destructor. /// \throw nothing void close(); /// destructor, closes the file (if any open) /// \sa close() /// \throw nothing ~File(); /// \brief gets the PHYSFS_file as void* /// \details We don't return a PHYSFS_file* here, as this type is a typedef so it cannot /// be forward declared, which would require us to include here. /// \throw nothing /// \depreciated don't use this method if it is not absolutely neccessary. If there is /// physfs functionality that cannot be accessed without the use of this method, /// consider to add these functions together with proper tests and checks to this /// class instead of using this function. /// You bypass all the security that this File class offers by using the direct /// Physfs_file. void* getPHYSFS_file(); // ------------------------------------ // information querying interface // ------------------------------------ /// \brief checks if a file is opened /// \throw nothing bool is_open() const; /// \todo check if these return types fit! /// gets the length of the currently opened file. /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. uint32_t length() const; /// gets the name of the currently opened file /// \todo function needs tests /// \return name of the opened file, or "" when no file is opened /// \throw nothing std::string getFileName() const; // ------------------------------------ // cursor interface // ------------------------------------ /// gets the current reading position. /// \throw PhysfsFileException when Physfs reports an error. /// \throw NoFileOpenedException when called while no file is opened. uint32_t tell() const; /// moves the read/write cursor to the desired position /// \throw PhysfsFileException when Physfs reports an error /// \throw NoFileOpenedException when called while no file is opened. void seek(uint32_t target); protected: enum OpenMode { OPEN_READ, ///!< open file for reading OPEN_WRITE ///!< open file for writing }; /// \brief default ctor /// \details File has to be opended with open() /// \throw nothing explicit File(); /// \brief constructor which opens a file. /// \param filename File to be opened /// \param mode Should this file be opened in reading or in writing mode. /// \param no_override Set to true if you want to forbid writing over existing file. /// \throw FileLoadException, if the file could not be loaded /// \throw FileAlreadyExistsException in case of trying to write over existing file with no_override = true File(const std::string& filename, OpenMode mode, bool no_override = false); /// \brief opens a file. /// \param filename File to be opened /// \param mode Should this file be opened in reading or in writing mode. /// \param no_override Set to true if you want to forbid writing over existing file. /// \throw FileLoadException, if the file could not be loaded /// \throw FileAlreadyExistsException in case of trying to write over existing file with no_override = true /// \pre No file is currently opened. void open(const std::string& filename, OpenMode mode, bool no_override = false); /// we use void* instead of PHYSFS_file here, because we can't forward declare it /// as it is a typedef. void* mHandle; /// we safe the name of the opened file, too, mainly for debugging/logging purposes std::string mFileName; /// helper function which checks if a file is opened and throws /// an exception otherwise. /// this is called in every function that directly invokes physfs calls /// because physfs does not check that istelf (just crashes) /// doing an assert would be acceptable from a coders point of view /// as trying to read/write/etc. from a closed file is clearly a /// programming error. But we don't do anything performance critical with /// files, and even then woudl hard-disk speed be the limiting factor, /// not the few additional cycles needed for this exception handling. /// so, i think. doing an exception is the best choice here, as it allows /// better error recorvery and error reporting. void check_file_open() const; }; blobby-1.0rc3/src/FileExceptions.h0000644000175000017500000001371412042452374020435 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include /*! \class FileSystemException \brief common base class of all file system related errors */ class FileSystemException : public std::exception { public: FileSystemException() { }; ~FileSystemException() throw() { }; virtual const char* what() const throw() { return "a file system related exception occured!"; } }; /*! \class PhysfsException \brief signales errors reported by physfs \details base class for all exceptions that report physfs errors; */ class PhysfsException : public virtual FileSystemException { public: // implementation in FileSystem.cpp PhysfsException(); ~PhysfsException() throw() { }; virtual const char* what() const throw() { return ("physfs reported an error: " + getPhysfsMessage()).c_str(); } std::string getPhysfsMessage() const { return mPhysfsErrorMsg; } private: /// this string saves the error message std::string mPhysfsErrorMsg; }; /*! \class PhysfsInitException \brief signals an error during the physfs initialisation phase */ class PhysfsInitException : public PhysfsException { public: PhysfsInitException(const std::string& path) : mPath(path) { } ~PhysfsInitException() throw() { } virtual const char* what() const throw() { return ("could not initialise physfs to path " + getPath() + ": " + getPhysfsMessage()).c_str(); } std::string getPath() const { return mPath; } private: std::string mPath; }; /*! \class FileException \brief common base type for file exceptions. \details does not owerride what(), so there is no default error message for FileException s */ class FileException: public virtual FileSystemException { public: FileException(const std::string& f) : filename(f) { } virtual ~FileException() throw() { } /// get the name of the file of the exception const std::string& getFileName() const { return filename; } private: std::string filename; ///!< name of the file which caused the exception }; /*! \class FileLoadException \brief error thrown when a file could not be opened or created. \todo use a better name as FileLoadException does only fit for the open for reading case. */ class FileLoadException : public FileException, public PhysfsException { public: FileLoadException(std::string name) : FileException(name) { /// \todo do we really need to do this? std::exception already /// provides the functionality for setting exception messages, i think. error = "Couldn't load " + name + ": " + getPhysfsMessage(); } virtual ~FileLoadException() throw() {} virtual const char* what() const throw() { return error.c_str(); } private: std::string error; ///< saves the error message }; /*! \class FileAlreadyExistsException \brief error thrown when trying to create a new file even though this file exists already. */ class FileAlreadyExistsException : public FileException { public: FileAlreadyExistsException(std::string name) : FileException(name) { } virtual ~FileAlreadyExistsException() throw() { } virtual const char* what() const throw() { return ("File " + getFileName() + " already exists.").c_str(); } }; /*! \class PhysfsFileException \brief combines FileException with PhysfsException */ class PhysfsFileException : public FileException, public PhysfsException { public: PhysfsFileException(const std::string& filename) : FileException(filename) { }; ~PhysfsFileException() throw() { }; virtual const char* what() const throw() { return (getFileName() + ": " + getPhysfsMessage()).c_str(); } }; /*! \class NoFileOpenedException \brief signals operations on closed files \details Exceptions of this type are thrown when any file modifying or information querying functions are called without a file beeing opened. These are serious errors and generally, exceptions of this type should not occur, as it indicates logical errors in the code. Still, this allows us to handle this situation without having to crash or exit. \sa File::check_file_open() */ class NoFileOpenedException : public FileException { public: NoFileOpenedException() : FileException("") { }; ~NoFileOpenedException() throw() { }; virtual const char* what() const throw() { // default error message for now return "trying to perform a file operation when no file was opened."; } }; /*! \class EOFException \brief thrown when trying to read over eof \details Exceptions of this type are issued when a reading operation tries to read behind a files eof. */ class EOFException : public FileException { public: EOFException(const std::string& file) : FileException( file ) { }; ~EOFException() throw() { }; virtual const char* what() const throw() { // default error message for now return (getFileName() + " trying to read after eof.").c_str(); } }; blobby-1.0rc3/src/ReplayRecorder.h0000644000175000017500000000635612042452374020442 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /// \file ReplayRecorder.h /// \brief contains replay recorder and associated enums #pragma once #include #include #include #include "Global.h" #include "ReplayDefs.h" #include "InputSource.h" #include "DuelMatchState.h" #include "GenericIOFwd.h" namespace RakNet { class BitStream; } class FileWrite; /*! \class VersionMismatchException \brief thrown when replays of incompatible version are loaded. */ struct VersionMismatchException : public std::exception { VersionMismatchException(const std::string& filename, uint8_t major, uint8_t minor); ~VersionMismatchException() throw(); virtual const char* what() const throw(); std::string error; uint8_t major; uint8_t minor; }; /// \brief recording game /// \todo we safe replays in continuous storeage (array or std::vector) /// which might be ineffective for huge replays (1h ~ 270kb) class ReplayRecorder { public: ReplayRecorder(); ~ReplayRecorder(); void save(boost::shared_ptr target) const; void send(boost::shared_ptr stream) const; void receive(boost::shared_ptrstream); // recording functions void record(const DuelMatchState& input); // saves the final score void finalize(unsigned int left, unsigned int right); // game setup setting void setPlayerNames(const std::string& left, const std::string& right); void setPlayerColors(Color left, Color right); void setGameSpeed(int fps); private: void writeFileHeader(boost::shared_ptr, uint32_t checksum) const; void writeReplayHeader(boost::shared_ptr) const; void writeAttributesSection(boost::shared_ptr) const; void writeJumpTable(boost::shared_ptr) const; void writeInputSection(boost::shared_ptr) const; void writeStatesSection(boost::shared_ptr) const; std::vector mSaveData; std::vector mSavedStates; // general replay attributes std::string mPlayerNames[MAX_PLAYERS]; Color mPlayerColors[MAX_PLAYERS]; unsigned int mEndScore[MAX_PLAYERS]; unsigned int mGameSpeed; // here we save the information needed to create the header // pointers to replay sections /// \todo this is ugly mutable uint32_t attr_ptr; mutable uint32_t jptb_ptr; mutable uint32_t data_ptr; mutable uint32_t states_ptr; }; blobby-1.0rc3/src/GameLogicState.cpp0000644000175000017500000000413312042452374020672 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #include "GameLogicState.h" #include #include "GenericIO.h" USER_SERIALIZER_IMPLEMENTATION_HELPER(GameLogicState) { io.uint32(value.leftScore); io.uint32(value.rightScore); io.template generic( value.servingPlayer ); io.uint32(value.leftSquish); io.uint32(value.rightSquish); } void GameLogicState::swapSides() { std::swap(leftScore, rightScore); std::swap(leftSquish, rightSquish); if(servingPlayer == LEFT_PLAYER) { servingPlayer = RIGHT_PLAYER; } else if(servingPlayer == RIGHT_PLAYER) { servingPlayer = LEFT_PLAYER; } } bool GameLogicState::operator==(const GameLogicState& other) const { return leftScore == other.leftScore && rightScore == other.rightScore && servingPlayer == other.servingPlayer && leftSquish == other.leftSquish && rightSquish == other.rightSquish; } std::ostream& operator<<(std::ostream& stream, const GameLogicState& state) { stream << "GAME LOGIC STATE [ " << state.leftScore << " : " << state.rightScore << " " << state.servingPlayer << " " << state.leftSquish << " " << state.rightSquish << "]"; return stream; } blobby-1.0rc3/src/CMakeLists.txt0000644000175000017500000000566512042452374020111 0ustar danielknobedanielknobeadd_subdirectory(lua) add_subdirectory(tinyxml) add_subdirectory(raknet) add_subdirectory(blobnet) add_definitions(-DTIXML_USE_STL) include_directories(.) set(common_SRC Global.h Clock.cpp Clock.h DuelMatch.cpp DuelMatch.h ReplayRecorder.cpp ReplayRecorder.h NetworkGame.cpp NetworkGame.h NetworkMessage.cpp NetworkMessage.h GameLogic.cpp GameLogic.h PhysicWorld.cpp PhysicWorld.h SpeedController.cpp SpeedController.h UserConfig.cpp UserConfig.h File.cpp File.h FileWrite.cpp FileWrite.h FileRead.cpp FileRead.h FileSystem.cpp FileSystem.h PhysicState.cpp PhysicState.h DuelMatchState.cpp DuelMatchState.h GameLogicState.cpp GameLogicState.h GenericIO.cpp GenericIO.h ) set (blobby_SRC ${common_SRC} Blood.cpp Blood.h TextManager.cpp TextManager.h main.cpp IMGUI.cpp IMGUI.h LagDetectionSystem.cpp LagDetectionSystem.h InputDevice.cpp InputDevice.h InputManager.cpp InputManager.h InputSource.cpp InputSource.h LocalInputSource.h Player.cpp Player.h RenderManager.cpp RenderManager.h RenderManagerGL2D.cpp RenderManagerGL2D.h RenderManagerGP2X.cpp RenderManagerGP2X.h RenderManagerSDL.cpp RenderManagerSDL.h ScriptedInputSource.cpp ScriptedInputSource.h BotAPICalculations.cpp BotAPICalculations.h SoundManager.cpp SoundManager.h Vector.h ReplayRecorder.cpp ReplayRecorder.h ReplayPlayer.cpp ReplayPlayer.h ReplayLoader.cpp state/State.cpp state/State.h state/LocalGameState.cpp state/LocalGameState.h state/NetworkState.cpp state/NetworkState.h state/OptionsState.cpp state/OptionsState.h state/NetworkSearchState.cpp state/NetworkSearchState.h state/ReplayState.cpp state/ReplayState.h state/ReplaySelectionState.cpp state/ReplaySelectionState.h ) set (blobby-server_SRC ${common_SRC} server/DedicatedServer.cpp server/DedicatedServer.h server/NetworkPlayer.cpp server/NetworkPlayer.h ) find_package(Boost REQUIRED) find_package(PhysFS REQUIRED) find_package(SDL REQUIRED) find_package(OpenGL) include_directories( ${Boost_INCLUDE_DIR} ${PHYSFS_INCLUDE_DIR} ${SDL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR} ) if (OPENGL_FOUND) add_definitions(-DHAVE_LIBGL) endif (OPENGL_FOUND) if (CMAKE_SYSTEM_NAME STREQUAL Windows) set(RAKNET_LIBRARIES raknet ws2_32) else (CMAKE_SYSTEM_NAME STREQUAL Windows) set(RAKNET_LIBRARIES raknet) endif (CMAKE_SYSTEM_NAME STREQUAL Windows) add_executable(blobby ${blobby_SRC}) target_link_libraries(blobby lua raknet blobnet tinyxml ${RAKNET_LIBRARIES} ${PHYSFS_LIBRARY} ${SDL_LIBRARY} ${OPENGL_LIBRARIES}) if (UNIX) add_executable(blobby-server ${blobby-server_SRC}) target_link_libraries(blobby-server lua raknet tinyxml ${RAKNET_LIBRARIES} ${PHYSFS_LIBRARY} ${SDL_LIBRARY}) endif (UNIX) if (CMAKE_SYSTEM_NAME STREQUAL Windows) set_target_properties(blobby PROPERTIES LINK_FLAGS "-mwindows") # disable the console window endif (CMAKE_SYSTEM_NAME STREQUAL Windows) if (WIN32) install(TARGETS blobby DESTINATION .) elseif (UNIX) install(TARGETS blobby blobby-server DESTINATION bin) endif (WIN32) blobby-1.0rc3/src/GenericIODetail.h0000644000175000017500000001236312042452374020442 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "GenericIOFwd.h" #include #include #include "Global.h" class PlayerInput; class Color; class DuelMatchState; namespace detail { // this class indicates the GenericIO is an input handler struct READER_TAG{}; // this class indicates the GenericIO is an output handler struct WRITER_TAG{}; // helper class to make parameter types /* \class conster helper class for parameter types. Zhis class is used to adapt the parameters of the read/write methods, making them const references for writing and reference for reading. The partial specialisations ensure that behaviour. */ template struct conster { }; // partial specialisations which generate the conster logic template struct conster { typedef T& type; }; template struct conster { typedef T* type; }; template struct conster { typedef const T& type; }; template struct conster { typedef const T* type; }; // helper classes to determine which io algorithm to use for which type // if any template specialisation of this class inherits from boost::true_type, // this means that the io algorithm uses a predifined_serializer. template struct has_default_io_implementation : public boost::false_type { }; // specialisations for default types template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; template<> struct has_default_io_implementation : public boost::true_type { }; // this class uses SFINAE to determine whether a type can be used as a container type, i.e. // provided the methods size and begin template struct is_container_type { typedef char yes[1]; typedef char no[2]; template static yes& test_size( int arg = (C()).size()); template static no& test_size(...); template static yes& test_begin( typename C::iterator* it = &(C()).begin()); template static no& test_begin(...); // this is only true if size and begin functions exist. static const bool value = sizeof(test_size(0)) == sizeof(yes) && sizeof(test_begin(0)) == sizeof(yes); }; /* \class serialize_dispatch \brief manages which generic implementation is used to serialize certain types \details uses partial specialisation with the parameters init and container. init has to be boost::true_type or boost::false_type. */ template struct serialize_dispatch; template struct predifined_serializer { static void serialize( GenericOut& out, const T& c); static void serialize( GenericIn& in, T& c); }; // inserts the methods from predefined_serializer, which are forward declared and // implemented in GenericIO.cpp. This happens when init is true_type template struct serialize_dispatch : public predifined_serializer { }; // uses a UserSerializer // the user has to implement the UserSerializer::serialize(GenericOut, const T&) and UserSerializer::serialize(GenericIn, T&) // somewhere, otherwise a link error happens. // User serializers are used when there is no default implementation and the type does not provide a container // interface. template struct serialize_dispatch : public UserSerializer { }; } blobby-1.0rc3/src/InputManager.h0000644000175000017500000000543412042452374020106 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include "InputSource.h" #include "InputDevice.h" #include "Vector.h" /*! \class InputKeyMap \brief mapping of keynames to SDLKey s */ struct InputKeyMap { const char *keyname; SDLKey key; }; /// \brief class for managing input class InputManager { public: static InputManager* createInputManager(); static InputManager* getSingleton(); ~InputManager(); void beginGame(PlayerSide side); void endGame(); bool running() const; PlayerInput getGameInput(int player); void updateInput(); // For GUI navigation (Gamepad, Joystick or Keyboard) bool up() const; bool down() const; bool left() const; bool right() const; bool select() const; bool exit() const; // extention for mouse included, so that right click = exit std::string getLastTextKey(); std::string getLastActionKey(); int getLastMouseButton() const { return mLastMouseButton; } std::string getLastJoyAction() const; // For GUI navigation (Mouse) Vector2 position(); bool click() const; bool doubleClick() const; bool mouseWheelUp() const; bool mouseWheelDown() const; bool unclick() const; // config conversion methods std::string keyToString(const SDL_keysym& key); SDLKey stringToKey(const std::string& keyname); private: static InputManager* mSingleton; // Keyboard static InputKeyMap mKeyMap[]; // Type for String <-convert-> SDLKey // GUI storage (because we need event based input for the GUI) bool mUp; bool mDown; bool mLeft; bool mRight; bool mSelect; bool mExit; bool mClick; bool mDoubleClick; bool mMouseWheelUp; bool mMouseWheelDown; bool mUnclick; int mMouseX; int mMouseY; int mLastClickTime; SDL_keysym mLastInputKey; int mLastMouseButton; std::string mLastJoyAction; PlayerInput mInput[MAX_PLAYERS]; InputDevice *mInputDevice[MAX_PLAYERS]; bool mRunning; InputManager(); }; blobby-1.0rc3/src/PhysicState.cpp0000644000175000017500000001762012042452374020307 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) Copyright (C) 2006 Daniel Knobe (daniel-knobe@web.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ /* header include */ #include "PhysicState.h" /* includes */ #include #include "raknet/BitStream.h" #include "GameConstants.h" #include "GenericIO.h" bool PhysicState::blobbyHitGround(PlayerSide player) const { return blobPosition[player].y >= GROUND_PLANE_HEIGHT; } USER_SERIALIZER_IMPLEMENTATION_HELPER(PhysicState) { io.number( value.blobPosition[LEFT_PLAYER].x ); io.number( value.blobPosition[LEFT_PLAYER].y ); io.number( value.blobVelocity[LEFT_PLAYER].x ); io.number( value.blobVelocity[LEFT_PLAYER].y ); io.number( value.blobPosition[RIGHT_PLAYER].x ); io.number( value.blobPosition[RIGHT_PLAYER].y ); io.number( value.blobVelocity[RIGHT_PLAYER].x ); io.number( value.blobVelocity[RIGHT_PLAYER].y ); io.number( value.ballPosition.x ); io.number( value.ballPosition.y ); io.number( value.ballVelocity.x ); io.number( value.ballVelocity.y ); io.number( value.ballAngularVelocity ); io.boolean( value.isGameRunning ); io.boolean( value.isBallValid ); // the template keyword is needed here so the compiler knows generic is // a template function and does not complain about <>. io.template generic ( value.playerInput[LEFT_PLAYER] ); io.template generic ( value.playerInput[RIGHT_PLAYER] ); } void writeCompressedToBitStream(RakNet::BitStream* stream, float value, float min, float max) { assert(min <= value && value <= max); assert(stream); unsigned short only2bytes = static_cast((value - min) / (max - min) * std::numeric_limits::max()); stream->Write(only2bytes); } void readCompressedFromBitStream(RakNet::BitStream* stream, float& value, float min, float max) { unsigned short only2bytes; stream->Read(only2bytes); value = static_cast(only2bytes) / static_cast(std::numeric_limits::max()) * (max - min) + min; } /* implementation */ void PhysicState::writeToStream(RakNet::BitStream* stream) const { // if the blobbys are standing on the ground, we need not send // y position and velocity stream->Write(blobbyHitGround(LEFT_PLAYER)); stream->Write(blobbyHitGround(RIGHT_PLAYER)); if(!blobbyHitGround(LEFT_PLAYER)) { writeCompressedToBitStream(stream, blobPosition[LEFT_PLAYER].y, 0, GROUND_PLANE_HEIGHT); writeCompressedToBitStream(stream, blobVelocity[LEFT_PLAYER].y, -30, 30); } if(!blobbyHitGround(RIGHT_PLAYER)) { writeCompressedToBitStream(stream, blobPosition[RIGHT_PLAYER].y, 0, GROUND_PLANE_HEIGHT); writeCompressedToBitStream(stream, blobVelocity[RIGHT_PLAYER].y, -30, 30); } writeCompressedToBitStream(stream, blobPosition[LEFT_PLAYER].x, LEFT_PLANE, NET_POSITION_X); writeCompressedToBitStream(stream, blobPosition[RIGHT_PLAYER].x, NET_POSITION_X, RIGHT_PLANE); writeCompressedToBitStream(stream, ballPosition.x, LEFT_PLANE, RIGHT_PLANE); writeCompressedToBitStream(stream, ballPosition.y, -500, GROUND_PLANE_HEIGHT_MAX); writeCompressedToBitStream(stream, ballVelocity.x, -30, 30); writeCompressedToBitStream(stream, ballVelocity.y, -30, 30); stream->Write(playerInput[LEFT_PLAYER].left); stream->Write(playerInput[LEFT_PLAYER].right); stream->Write(playerInput[LEFT_PLAYER].up); stream->Write(playerInput[RIGHT_PLAYER].left); stream->Write(playerInput[RIGHT_PLAYER].right); stream->Write(playerInput[RIGHT_PLAYER].up); } void PhysicState::readFromStream(RakNet::BitStream* stream) { bool leftGround; bool rightGround; stream->Read(leftGround); stream->Read(rightGround); if(leftGround) { blobPosition[LEFT_PLAYER].y = GROUND_PLANE_HEIGHT; blobVelocity[LEFT_PLAYER].y = 0; } else { readCompressedFromBitStream(stream, blobPosition[LEFT_PLAYER].y, 0, GROUND_PLANE_HEIGHT); readCompressedFromBitStream(stream, blobVelocity[LEFT_PLAYER].y, -30, 30); } if(rightGround) { blobPosition[RIGHT_PLAYER].y = GROUND_PLANE_HEIGHT; blobVelocity[RIGHT_PLAYER].y = 0; } else { readCompressedFromBitStream(stream, blobPosition[RIGHT_PLAYER].y, 0, GROUND_PLANE_HEIGHT); readCompressedFromBitStream(stream, blobVelocity[RIGHT_PLAYER].y, -30, 30); } readCompressedFromBitStream(stream, blobPosition[LEFT_PLAYER].x, LEFT_PLANE, NET_POSITION_X); readCompressedFromBitStream(stream, blobPosition[RIGHT_PLAYER].x, NET_POSITION_X, RIGHT_PLANE); readCompressedFromBitStream(stream, ballPosition.x, LEFT_PLANE, RIGHT_PLANE); // maybe these values is a bit too pessimistic... // but we have 65535 values, hence it should be precise enough readCompressedFromBitStream(stream, ballPosition.y, -500, GROUND_PLANE_HEIGHT_MAX); readCompressedFromBitStream(stream, ballVelocity.x, -30, 30); readCompressedFromBitStream(stream, ballVelocity.y, -30, 30); // if ball velocity not zero, we must assume that the game is active // i'm not sure if this would be set correctly otherwise... // we must use this check with 0.1f because of precision loss when velocities are transmitted // wo prevent setting a false value when the ball is at the parabels top, we check also if the // y - position is the starting y position /// \todo maybe we should simply send a bit which contains this information? if( std::abs(ballVelocity.x) > 0.1f || std::abs(ballVelocity.y) > 0.1f || std::abs(ballPosition.y - STANDARD_BALL_HEIGHT) > 0.1f) { isGameRunning = true; } else { isGameRunning = false; } stream->Read(playerInput[LEFT_PLAYER].left); stream->Read(playerInput[LEFT_PLAYER].right); stream->Read(playerInput[LEFT_PLAYER].up); stream->Read(playerInput[RIGHT_PLAYER].left); stream->Read(playerInput[RIGHT_PLAYER].right); stream->Read(playerInput[RIGHT_PLAYER].up); } void PhysicState::swapSides() { blobPosition[LEFT_PLAYER].x = RIGHT_PLANE - blobPosition[LEFT_PLAYER].x; blobPosition[RIGHT_PLAYER].x = RIGHT_PLANE - blobPosition[RIGHT_PLAYER].x; std::swap(blobPosition[LEFT_PLAYER], blobPosition[RIGHT_PLAYER]); ballPosition.x = RIGHT_PLANE - ballPosition.x; ballVelocity.x = -ballVelocity.x; ballAngularVelocity = -ballAngularVelocity; std::swap(playerInput[LEFT_PLAYER].left, playerInput[LEFT_PLAYER].right); std::swap(playerInput[RIGHT_PLAYER].left, playerInput[RIGHT_PLAYER].right); std::swap(playerInput[LEFT_PLAYER], playerInput[RIGHT_PLAYER]); } bool PhysicState::operator==(const PhysicState& other) const { return blobPosition[LEFT_PLAYER] == other.blobPosition[LEFT_PLAYER] && blobPosition[RIGHT_PLAYER] == other.blobPosition[RIGHT_PLAYER] && blobVelocity[LEFT_PLAYER] == other.blobVelocity[LEFT_PLAYER] && blobVelocity[RIGHT_PLAYER] == other.blobVelocity[RIGHT_PLAYER] && ballPosition == other.ballPosition && ballVelocity == other.ballVelocity && ballAngularVelocity == other.ballAngularVelocity && isGameRunning == other.isGameRunning && isBallValid == other.isBallValid && playerInput[LEFT_PLAYER] == other.playerInput[LEFT_PLAYER] && playerInput[RIGHT_PLAYER] == other.playerInput[RIGHT_PLAYER]; } blobby-1.0rc3/src/LocalInputSource.h0000644000175000017500000000262212042452374020743 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "InputManager.h" #include "RenderManager.h" class LocalInputSource : public InputSource { public: LocalInputSource(PlayerSide player) : mPlayer(player) { InputManager::getSingleton()->beginGame(player); } virtual PlayerInput getInput() { return InputManager::getSingleton()->getGameInput(mPlayer); } ~LocalInputSource() { RenderManager::getSingleton().setMouseMarker(-6); InputManager::getSingleton()->endGame(); } private: int mPlayer; }; blobby-1.0rc3/src/Clock.h0000644000175000017500000000411712042452374016544 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include /*! \class Clock \brief Game Timing Management \details This class represents a clock. It can be started, paused, resetted, and it is possible to get the time in a string for in-game representation */ class Clock { public: /// default c'tor Clock(); /// starts/unpauses the clock void start(); /// pauses the clock void stop(); /// resets the clock. after this, the clock is paused. void reset(); /// gets whether the clock is currently running bool isRunning() const; /// this function has to be called each frame. It calculates /// the passed time; void step(); /// gets the time in seconds as an integer int getTime() const; /// set the time to a specified value /// \param newTime: new time in seconds void setTime(int newTime); /// returns the time as a string std::string getTimeString() const; private: /// is the clock currently running? bool mRunning; /// recorded time in seconds time_t mGameTime; /// last time that step was called. /// needed to calculate the time difference. time_t mLastTime; }; blobby-1.0rc3/src/NetworkMessage.h0000644000175000017500000001461212042452374020450 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include #include #include "raknet/PacketEnumerations.h" #include "raknet/NetworkTypes.h" enum MessageType { ID_GENERIC_MESSAGE = ID_RESERVED9 + 1, ID_INPUT_UPDATE, ID_PHYSIC_UPDATE, ID_WIN_NOTIFICATION, ID_OPPONENT_DISCONNECTED, ID_BALL_RESET, ID_BALL_GROUND_COLLISION, ID_BALL_PLAYER_COLLISION, ID_GAME_READY, ID_ENTER_GAME, ID_PAUSE, ID_UNPAUSE, ID_BLOBBY_SERVER_PRESENT, ID_VERSION_MISMATCH, ID_CURRENTLY_UNUSED, // this value is to ensure network protocol compatibility between 0.9c and 1.0 ID_REPLAY, ID_CHAT_MESSAGE }; // General Information: // Because the client may choose their side and the server rotates // everything if necessary, PlayerSide informations may not be // correct on all peers. When the server sends a side information // to a client, the information has to be converted into the view // of the client. // ID_INPUT_UPDATE = 63: // Description: // This packet is sent from client to server every frame. // It contains the current input state as three booleans. // Structure: // ID_INPUT_UPDATE // ID_TIMESTAMP // timestamp (int) // left keypress (bool) // right keypress (bool) // up keypress (bool) // // ID_PHYSIC_UPDATE: // Description: // The server sends this information of the current physics state // to all clients every frame. Local physic states will always // be overwritten. // Structure: // ID_PHYSIC_UPDATE // ID_TIMESTAMP // timestamp (int) // packet_number (unsigned char) // Physic data (analysed by PhysicWorld) // // ID_WIN_NOTIFICATION // Description: // Message sent from server to all clients when a player // won the game. The appended enum tells the client which // player won. // Structure: // ID_WIN_NOTIFICATION // winning player (PlayerSide) // // ID_BALL_RESET // Description: // Message sent from server to all clients when the ball // is reset to the starting position. It includes an information // about the current point state and is used to synchronize // the clocks. // Structure: // ID_BALL_RESET // serving player (PlayerSide) // left score (int) // right score (int) // time (int) // // ID_BALL_GROUND_COLLISION // Description: // Message sent from server to all clients when the ball // hits the ground. It is the only valid reason for a whistle // sound during the game. The side attribute tells on which side // the ball hit the ground. // Structure: // ID_BALL_GROUND_COLLISION // side (PlayerSide) // // ID_BALL_PLAYER_COLLISION // Description: // Message sent from server to all clients when the ball // hits a player. It is the only valid reason for a player // collision sound. The player attribute tells which player // hit the ball. // Structure: // ID_BALL_PLAYER_COLLISION // intensity (float) // player (PlayerSide) // // ID_GAME_READY // Description: // Message sent from server to client when all clients are // ready. The input is enabled after this message on the client. // The attribute opponent name carrys the name of the connected // opponent. // Structure: // ID_GAME_READY // gamespeed (int) // opponentname(char[16]) // // ID_ENTER_GAME // Description: // Message sent from client to server after connecting to it. // The side attribute tells the server on which side the client // wants to play. The name attribute reports to players name, // truncated to 16 characters. // Structure: // ID_ENTER_GAME // side (PlayerSide) // name (char[16]) // // ID_PAUSE // Description: // Sent from client to server, this message can be seen as a request // to pause the game. From server to client it is an acknowledgement // of the pause and request demand to display an appropriate dialog. // Structure: // ID_PAUSE // // ID_UNPAUSE // Description: // As ID_PAUSE, this packets is an acknowledgement if sent from a client // and a request if sent from the server. // Structure: // ID_UNPAUSE // // ID_OPPONENTED_DISCONNECTED // Description: // Sent from server to client when an opponent left the game // Structure: // ID_OPPONENT_DISCONNECTED // // ID_BLOBBY_SERVER_PRESENT // Description: // Sent from client to probe a server and from server to client // as answer to the same packet. // Sent with version number since alpha 7 in the first case. // Structure: // ID_BLOBBY_SERVER_PRESENT // major (int) // minor (int) // // ID_VERSION_MISMATCH // Description: // Sent from server to client if the version number // differes from the one of the server. // Structure: // ID_VERSION_MISMATCH // server_major (int) // server_minor (int) // // ID_REPLAY // Description: // Sent from client to server to request a replay // Sent from server to client to transmitt the replay // Structure: // ID_REPLAY // size (int) // data // class UserConfig; struct ServerInfo { ServerInfo(RakNet::BitStream& stream, const char* ip, uint16_t port); ServerInfo(const UserConfig& config); ServerInfo(const std::string& playername); void writeToBitstream(RakNet::BitStream& stream); void setWaitingPlayer(const std::string& name); /// \todo maybe we should define ServerInfo a little bit more /// as e.g., hostname can be left uninitialised on server /// we combine to functionsalities here: server information and server addresses. int activegames; int gamespeed; uint16_t port; char hostname[64]; char name[32]; char waitingplayer[64]; char description[192]; static const size_t BLOBBY_SERVER_PRESENT_PACKET_SIZE; }; bool operator == (const ServerInfo& lval, const ServerInfo& rval); blobby-1.0rc3/src/ReplayDefs.h0000644000175000017500000001126412042452374017550 0ustar danielknobedanielknobe/*============================================================================= Blobby Volley 2 Copyright (C) 2006 Jonathan Sieber (jonathan_sieber@yahoo.de) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA =============================================================================*/ #pragma once #include "DuelMatchState.h" const char validHeader[4] = { 'B', 'V', '2', 'R' }; //!< header of replay file const unsigned char REPLAY_FILE_VERSION_MAJOR = 1; const unsigned char REPLAY_FILE_VERSION_MINOR = 1; struct ReplaySavePoint { DuelMatchState state; // state of the match when the snapshot occured int step; // number of game step this snapshot happened }; /*! \class ChecksumException \brief thrown when actual and expected file checksum mismatch */ struct ChecksumException : public std::exception { ChecksumException(std::string filename, uint32_t expected, uint32_t real); ~ChecksumException() throw(); virtual const char* what() const throw(); std::string error; }; /** \page replay_system Replay System \section rep_file_spec Replay File Specification Blobby Volley Replay files have the following structure: