If you have comments or questions concerning this source file, discuss them in the forum.
/*
Copyright (c) 2002 Nicolai Haehnle

See the license.txt for details. If that file was not included in the
source distributions, please email <prefect@rtts.org>
*/
// library.h -- collection of useful functions and classes

#ifdef _WIN32 // eliminate dumb warnings
#pragma warning(disable : 4244) // conversion from 'double' to 'float'
#pragma warning(disable : 4305) // shorten 'const double' to 'float'
#endif

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include <setjmp.h>
#include <assert.h>

#include <math.h>

#include <algorithm>
#include <vector>
#include <list>


#ifdef _WIN32 // some more windows fixups
#define snprintf _snprintf
#define vsnprintf _vsnprintf

#define strcasecmp stricmp
#define strncasecmp strnicmp

#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif

#ifdef _DEBUG
#ifndef DEBUG
#define DEBUG
#endif
#endif

/*
==============================================================================

DATA TYPES

==============================================================================
*/

typedef unsigned char   byte;
typedef unsigned short  u16;
typedef signed short    s16;
typedef unsigned long   u32;
typedef signed long     s32;

#ifndef offsetof
#define offsetof(_struct,_field) ((int)(&((_struct *)0)->_field))
#endif

#define atoffset(_type,_struct,_idx) (*(_type *)((byte *)&(_struct) + (_idx)))
#define arraysize(_array) (sizeof((_array)) / sizeof((_array)[0]))

#define P_LITTLE_ENDIAN
#undef P_BIG_ENDIAN

#ifdef P_LITTLE_ENDIAN
#define LittleShort(x)  (x)
#define LittleInt(x)    (x)
#define LittleFloat(x)  (x)
#define BigShort(x)     SwapShort((x))
#define BigInt(x)       SwapInt((x))
#define BigFloat(x)     SwapFloat((x))
#endif

#ifdef P_BIG_ENDIAN
#define LittleShort(x)  SwapShort((x))
#define LittleInt(x)    SwapInt((x))
#define LittleFloat(x)  SwapFloat((x))
#define BigShort(x)     (x)
#define BigInt(x)       (x)
#define BigFloat(x)     (x)
#endif

short SwapShort(short x);
int SwapInt(int x);
float SwapFloat(float x);


/*
==============================================================================

MATH LIBRARY

==============================================================================
*/

#ifndef M_PI
#define M_PI 3.14159265358979323846 // from glibc's math.h
#endif

// Return a floating point variable in the range [0..1[
#define RandFloat() ((double)rand() / (double)RAND_MAX)


// A non-negative integer rectangle
template<class T>
class LRectTmpl {
public:
    T       pos[2];
    T       size[2];

public:
    LRectTmpl() { }
    LRectTmpl(T x, T y, T w, T h) {
        pos[0] = x;
        pos[1] = y;
        size[0] = w;
        size[1] = h;
    }

    inline bool Contains(T x, T y) {
        if (x < pos[0] || y < pos[1] || x >= pos[0]+size[0] || y >= pos[1]+size[1])
            return false;
        return true;
    }

    template<class P>
    inline void WorldToRect(P *x, P *y) {
        *x -= pos[0];
        *y -= pos[1];
    }
    template<class P>
    inline void RectToWorld(P *x, P *y) {
        *x += pos[0];
        *y += pos[1];
    }

    template<class P>
    inline void ClipLocal(LRectTmpl<P> *rc) {
        if (rc->pos[0] < 0) {
            rc->size[0] += rc->pos[0];
            rc->pos[0] = 0;
        }
        if (rc->pos[0]+rc->size[0] > size[0])
            rc->size[0] = size[0] - rc->pos[0];

        if (rc->pos[1] < 0) {
            rc->size[1] += rc->pos[1];
            rc->pos[1] = 0;
        }
        if (rc->pos[1]+rc->size[1] > size[1])
            rc->size[1] = size[1] - rc->pos[1];
    }

    template<class P, class Q>
    inline void ClipLocalOffset(LRectTmpl<P> *rc, Q *ofsx, Q *ofsy) {
        if (rc->pos[0] < 0) {
            rc->size[0] += rc->pos[0];
            *ofsx -= rc->pos[0];
            rc->pos[0] = 0;
        }
        if (rc->pos[0]+rc->size[0] > size[0])
            rc->size[0] = size[0] - rc->pos[0];

        if (rc->pos[1] < 0) {
            rc->size[1] += rc->pos[1];
            *ofsy -= rc->pos[1];
            rc->pos[1] = 0;
        }
        if (rc->pos[1]+rc->size[1] > size[1])
            rc->size[1] = size[1] - rc->pos[1];
    }
};

typedef LRectTmpl<int>      LRect;
typedef LRectTmpl<float>    LFloatRect;

/*
==============================================================================

ERRORS

==============================================================================
*/

#define ERR_FATAL       0 // default
#define ERR_CONT        1 // it's possible to continue normally

class LError {
public:
    int     m_iSeverity;
    char    m_szMessage[256];

public:
    LError(const char *fmt, ...);
    LError(int iSeverity, const char *fmt, ...);

    const char *Get() const { return m_szMessage; }
};

#ifdef DEBUG
#define lassert(expr) do { \
    if (!(expr)) throw LError("%s, line %i: assert %s", __FILE__, __LINE__, #expr ); \
} while(0)
#else
#define lassert(expr) /* */
#endif

/*
==============================================================================

HEAP MANAGEMENT

==============================================================================
*/

// 100 and above are free for you to use
#define TAG_CLASS           0
#define TAG_POOL            1
#define TAG_FILES           2
#define TAG_PARSER          3
#define TAG_IMAGE           4
#define TAG_SIGNAL          5
#define TAG_CONFIG          6

#define TAG_HAL             10
#define TAG_SPRITE          11

#define TAG_GUI             20

//
// lib_heap.cpp
//
void *L_Malloc(int bytes, int tag);
char *L_Strdup(const char *string, int tag);
void L_Free(void *block);
void *L_Realloc(void *block, int bytes, int tag);
void L_TagFree(int start, int end);


/*
==============================================================================

CONSOLE

==============================================================================
*/

//
// lib_console.cpp
//
void L_Printf(const char *fmt, ...);


/*
==============================================================================

MISCELLANEOUS LIBRARY

==============================================================================
*/

// misc constants and functions
#define MAX_OSPATH      128

//
// lib_main.cpp
//
void *memnchr(const void *buf, int c, int bytes);
void xstrcpy(char *dest, int size, const char *src);
void xstrcat(char *dest, int size, const char *src);

int BitMaskSect(byte *mask0, int w0, int h0, byte *mask1, int w1, int h1,
                int dx, int dy);

int L_Tokenize(char **tokv, int max_tokc, char *string);


/*
==============================================================================

LObject -- SIGNAL/SLOT CAPABLE CLASS

==============================================================================
*/

class LObject;
class LSignalMgr;

typedef void (LObject::*l_objectfn_t)(void);

typedef struct l_connection_s {
    struct l_connection_s   *signext;
    LObject                 *obj;
    l_objectfn_t            fn;
    struct l_connection_s   *objnext;
    LSignalMgr              *signal;
} l_connection_t;

class LObject {
    friend class LSignalMgr;

public:
    LObject() { conns = 0; }
    virtual ~LObject();

    void Disconnect(LSignalMgr *pSig, l_objectfn_t fn);

private:
    l_connection_t  *conns;

    void RemoveConn(l_connection_t *conn);
};

class LSignalMgr {
    friend class LObject;

protected:
    l_connection_t  *conns;
public:
    LSignalMgr() { conns = 0; }
    ~LSignalMgr();

    void AddSlot(LObject *pObj, l_objectfn_t fn);
    void DelSlot(LObject *pObj, l_objectfn_t fn);

private:
    void RemoveConn(l_connection_t *conn);
};

#define impSIGNAL_head(plist) \
public: \
    typedef void (LObject::*fntype) plist;

#define impSIGNAL_conn(plist) \
    template<class T> \
    inline void Connect(LObject *pObj, void (T::*fn)plist) \
{ AddSlot(pObj, (l_objectfn_t)static_cast<void (LObject::*)plist>(fn)); } \
    template<class T> \
    inline void Disconnect(LObject *pObj, void (T::*fn)plist) \
        { DelSlot(pObj, (l_objectfn_t)static_cast<void (LObject::*)plist>(fn)); }

#define impSIGNAL_tail(plist,pcall) \
    inline void Emit plist { \
        for(l_connection_t *c = conns; c; c = c->signext) \
            (c->obj->*(fntype)(c->fn)) pcall; \
    } \
};

// LSignal
class LSignal : public LSignalMgr {
impSIGNAL_head(())
impSIGNAL_conn(())
impSIGNAL_tail((),())

template<class P1>
class LSignal1 : public LSignalMgr {
impSIGNAL_head((P1 p1))
impSIGNAL_conn((P1 p1))
impSIGNAL_tail((P1 p1),(p1))

template<class P1, class P2>
class LSignal2 : public LSignalMgr {
impSIGNAL_head((P1 p1, P2 p2))
impSIGNAL_conn((P1 p1, P2 p2))
impSIGNAL_tail((P1 p1, P2 p2),(p1, p2))


/*
==============================================================================

FILE SYSTEM

==============================================================================
*/

//
// lib_files.c
//
char *L_AutoExtension(char *buf, int bufsize, const char *ext);
char *L_StripExtension(char *fname);
char *L_RelativePath(char *buf, int buflen, const char *basefile, const char *filename);

int L_FindFiles(const char *path, const char *pattern, char ***results);
void L_FreeFindFiles(char **results);

bool L_FileExists(const char *path);

class LFileRead {
public:
    void    *data;
    int     filepos;
    int     length;

public:
    LFileRead();
    ~LFileRead();

    void Open(const char *fname);
    bool TryOpen(const char *fname);
    void Close();

    void SetFilePos(int pos);

    byte Byte(int pos = -1) { return *(byte *)Data(1, pos); }
    short Short(int pos = -1) { return LittleShort(*(short *)Data(2, pos)); }
    int Integer(int pos = -1) { return LittleInt(*(int *)Data(4, pos)); }
    float Float(int pos = -1) { return LittleFloat(*(float *)Data(4, pos)); }
    char *CString(int pos = -1);

    void *Data(int bytes, int pos = -1) {
        int i;

        lassert(data);

        i = pos;
        if (pos < 0) {
            i = filepos;
            filepos += bytes;
        }
        if (i+bytes > length)
            throw LError("File boundary exceeded");

        return (byte *)data + i;
    }
};

class LFileWrite {
public:
    void    *data;
    int     length;
    int     maxsize;
    int     filepos;

public:
    LFileWrite();
    ~LFileWrite();

    void Write(const char *filename);
    bool TryWrite(const char *filename);
    void Clear();

    void SetFilePos(int pos);
    void Data(const void *data, int size, int pos = -1);

    void Printf(const char *fmt, ...);

    void Byte(byte x, int pos = -1) { Data(&x, 1, pos); }
    void Short(short x, int pos = -1) { short y = LittleShort(x); Data(&y, 2, pos); }
    void Integer(int x, int pos = -1) { int y = LittleInt(x); Data(&y, 4, pos); }
    void Float(float x, int pos = -1) { float y = LittleFloat(x); Data(&y, 4, pos); }
    void CString(const char *x, int pos = -1) { Data(x, strlen(x)+1, pos); }
};

/*
==============================================================================

PARSER

==============================================================================
*/

#define TOK_EOF             0
#define TOK_STRING          (-1)
#define TOK_QUOTED          (-2)
#define TOK_INT             (-3)
#define TOK_FLOAT           (-4)

typedef struct l_token_s {
    int     type;   // positive values are special chars like '{'
    union {
        double  f;
    } v;
    char    *string; // the original, uninterpreted string
} l_token_t;

struct l_buffer_s;
typedef struct l_buffer_s l_buffer_t;

class LParser {
public:
    const char      *m_pszControlChars;
    l_buffer_t      *m_pBuffer;
    char            m_szBestName[MAX_OSPATH];

    l_token_t       tok;

public:
    LParser();
    ~LParser();

    LError Error(const char *fmt, ...);

    void AddString(const char *pszString);
    void AddFile(const char *pszFilename);

    bool TryNextToken();
    void NextToken();

    void Expect(const char *pszExpect);
    const char *AnyString();
    const char *TryString();
    const char *String();
    float Float();
    int Integer();
    unsigned Unsigned();

    void RelativePath(char *buf, int buflen, const char *fname);

    inline void SetControlChars(const char *pszControlChars) {
        m_pszControlChars = pszControlChars;
    }

private:
    void AddBuffer(const char *pszName, const char *pszData);
    void EndBuffer();
};

/*
==============================================================================

CONFIG SYSTEM

==============================================================================
*/

struct l_configitem_s;
typedef struct l_configitem_s l_configitem_t;

typedef struct l_configrecord_s {
    l_configitem_t  *items;
} l_configrecord_t;

#define CI_STRING           0
#define CI_INT              1
#define CI_FLOAT            2
#define CI_RECORD           3
#define CI_BOOL             4

typedef struct l_configitem_s {
    int                 type;
    char                *name;
    char                *dflt;
    l_configrecord_t    *record;
} l_configitem_t;

class LConfig {
public:
    char        *m_pszName;

public:
    LConfig(const char *pszName);
    virtual ~LConfig();

    bool TryLoad(const char *filename);
    bool TrySave(const char *filename);

    char *GetString(const char *name) const;
    int GetInt(const char *name) const;
    float GetFloat(const char *name) const;
    bool GetBool(const char *name) const;
    const LConfig *GetItem(const char *name) const;
    LConfig *GetItem(const char *name);

    void SetString(const char *name, const char *value);
    void SetInt(const char *name, int value);
    void SetFloat(const char *name, float value);
    void SetBool(const char *name, bool value);

public:
    static LConfig *Create(l_configrecord_t *definition);

    virtual void Parse(LParser *p) = 0;
    virtual void Store(LFileWrite *fw) = 0;
    virtual bool MustStore() = 0;

    virtual char *intGetString() const;
    virtual int intGetInt() const;
    virtual float intGetFloat() const;
    virtual bool intGetBool() const;
    virtual LConfig *intGetItem(const char *name) const;

    virtual void intSetString(const char *value);
    virtual void intSetInt(int value);
    virtual void intSetFloat(float value);
    virtual void intSetBool(bool value);

    LConfig     *m_pPrev;   // these are maintained by the parent record!
    LConfig     *m_pNext;
};

/*
==============================================================================

IMAGE LIBRARY

==============================================================================
*/

//
// lib_image.cpp
//
void L_ScaleImage(byte *out, int outw, int outh, const byte *in, int inw, int inh);
u32 *L_ImageBorder(const u32 *pixels, const int *size);
byte *L_ImageBitmask(const byte *pixels, const int *size);

void LoadPCX(const char *fname, int *sizes, byte **ppal, byte **pdata);
void LoadTGA(const char *fname, int *sizes, byte **ppal, byte **pdata, bool *palpha);
void LoadBMP(const char *fname, int *sizes, byte **ppal, byte **pdata);

void WriteBMP(const char *fname, const int *size, const byte *pal, const byte *data,
              bool alpha);

/*
==============================================================================

STRUCTURES

==============================================================================
*/
/* -- apparently, recreating placement operators is not allowed --
inline void *operator new(unsigned int iBytes, void *block)
{
    return block;
}

inline void operator delete(void *pBlock, void *block)
{
}
*/

/*
The pool is an extremely simple expandable packed array of items;
Indices will change when items are removed.
*/
template<class T>
class LPool {
public:
    int     size;
    T       *items;

private:
    int     max;

public:
    LPool() { size = 0; max = 0; items = 0; }
    ~LPool() { Resize(0); }

    void Add(const T &it) {
        if (size >= max)
            Resize(max + 10);
        new(&items[size]) T;
        items[size++] = it;
    }
    void RemoveAt(int idx) {
        delete(&items[size]) &items[size];
        size--;
        if (idx != size)
            memcpy(&items[idx], &items[size], sizeof(T));
    }

    int Find(const T &it) {
        int i;
        for(i = 0; i < size; i++)
            if (items[i] == it)
                return i;
        return -1;
    }

private:
    void Resize(int newsize) {
        max = newsize;
        items = (T *)L_Realloc(items, sizeof(T)*max, TAG_POOL);
    }
};