summaryrefslogblamecommitdiffstats
path: root/crawl-ref/source/store.cc
blob: a4afaece9f9a8f0a236d20419dd6f769836502c3 (plain) (tree)
1
2
3
4
5
6
7
8
9

                        

                                                                 
                             


                   
 

                  
                 
                    
                    
                 


                    










                                                                      

                   





























































                                                                          















                                                                     















                                                                         

















                                                            
                                       




                                                  
                                       




                                                   
                                        




                                                  
                                       




                                                   
                                        




                                                         
                                      
 
                   



                                                  
                                      
 
                   



                                                       
                                        
 
                   



                                                      
                                       
 
                   



                                                            
                                       
 
                   



                                                         
                                      
 
                   


                        













                                                       













                                                        












































































                                                                     















                                                          















                                                              
                 

                                                              
 


                                                                   




                         








                                                          






                                                                           







                                                          
 
                 

                 



























                                 







                            

                                                     

              





























                                                            
                                             
 

                                                        




























































                                                                     













                                                          













                                                              
                 
              
 




                             
                                      



                                                           

                                                        

































































                                                    

















                                         

















                                                
                 
              
 




                             
                                            
 
                       














                                                                 
            








                                                                 
      

























                                                                    
                           


























                                                                  
                                               












                                

                         




















































                                                                









                                                         








                                                       
 











































































                                                                      











                                                













                                                











                                                                               

                                                                               








                                      
                   









































                                             









                                           



































































                                                                         











                                                                    











                                                                     


                                                   
                   
























































                                                                            
 
                    

 
                                                           
 






                                                    

 
                                 
 
                                                                        





                                            

 
                                                                        
 


                         








                                                    



                              
                                            








                             
 
                                                                        
 
                                     







                                     
                                     



                      





                                                     
                    















                                                    
                                                         
 


                         
                      
                                                          
 
                                  



                                            
            

                         
 
                                                        


                                  
                                     





                                               
                             





                                                  
                         



                      

                        













                                                               










                                                            














                                                                  
                    
 
                                     
 



                                                 








                                                                              
                             
                      
 


                                                          




















                                                                          



                            



                                  



                             




                                                 
                    
 


                                     
     
            
                                         
                                              
      
 
                           





                            

                         
 

                    




                                                


                             




                                              


                           



                                                            
                             
                      

                             



                                                          
                             
                      









                                    
































                                                                             
                                         















                                              
                     




                      
                                  






























































                                                                    
            







                                                        


                                                            





                                                                
                         



                      

                        






                                        

                                                               

























                                                            
                                                          









                                          
























































                                                                            








































                                                           











                                                                
                      



















































                                                                   

                            
























































                                                                             














                                                                             
/*
 *  File:       store.cc
 *  Summary:    Saveable hash-table and vector capable of storing
 *              multiple types of data.
 *  Written by: Matthew Cline
 */

#include "AppHdr.h"

#include "store.h"

#include "dlua.h"
#include "externs.h"
#include "monster.h"
#include "tags.h"
#include "travel.h"

#include <algorithm>

CrawlStoreValue::CrawlStoreValue()
    : type(SV_NONE), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
}

CrawlStoreValue::CrawlStoreValue(const CrawlStoreValue &other)
{
    ASSERT(other.type >= SV_NONE && other.type < NUM_STORE_VAL_TYPES);

    val.ptr = NULL;

    type  = other.type;
    flags = other.flags;

    if (flags & SFLAG_UNSET)
    {
        val = other.val;
        return;
    }

    switch (type)
    {
    case SV_NONE:
    case SV_BOOL:
    case SV_BYTE:
    case SV_SHORT:
    case SV_LONG:
    case SV_FLOAT:
        val = other.val;
        break;

    case SV_STR:
    {
        std::string* str;
        str = new std::string(*static_cast<std::string*>(other.val.ptr));
        val.ptr = static_cast<void*>(str);
        break;
    }

    case SV_COORD:
    {
        coord_def* coord;
        coord = new coord_def(*static_cast<coord_def*>(other.val.ptr));
        val.ptr = static_cast<void*>(coord);
        break;
    }

    case SV_ITEM:
    {
        item_def* item;
        item = new item_def(*static_cast<item_def*>(other.val.ptr));
        val.ptr = static_cast<void*>(item);
        break;
    }

    case SV_HASH:
    {
        CrawlHashTable* hash;
        CrawlHashTable* tmp = static_cast<CrawlHashTable*>(other.val.ptr);
        hash = new CrawlHashTable(*tmp);
        val.ptr = static_cast<void*>(hash);
        break;
    }

    case SV_VEC:
    {
        CrawlVector* vec;
        CrawlVector* tmp = static_cast<CrawlVector*>(other.val.ptr);
        vec = new CrawlVector(*tmp);
        val.ptr = static_cast<void*>(vec);
        break;
    }

    case SV_LEV_ID:
    {
        level_id* id;
        id = new level_id(*static_cast<level_id*>(other.val.ptr));
        val.ptr = static_cast<void*>(id);
        break;
    }

    case SV_LEV_POS:
    {
        level_pos* pos;
        pos = new level_pos(*static_cast<level_pos*>(other.val.ptr));
        val.ptr = static_cast<void*>(pos);
        break;
    }

    case SV_MONST:
    {
        monsters* mon;
        mon = new monsters(*static_cast<monsters*>(other.val.ptr));
        val.ptr = static_cast<void*>(mon);
        break;
    }

    case SV_LUA:
    {
        dlua_chunk* chunk;
        chunk = new dlua_chunk(*static_cast<dlua_chunk*>(other.val.ptr));
        val.ptr = static_cast<void*>(chunk);
        break;
    }

    case NUM_STORE_VAL_TYPES:
        ASSERT(false);
    }
}

CrawlStoreValue::CrawlStoreValue(const store_flags _flags,
                                 const store_val_type _type)
    : type(_type), flags(_flags)
{
    ASSERT(type >= SV_NONE && type < NUM_STORE_VAL_TYPES);
    ASSERT(!(flags & SFLAG_UNSET));

    flags   |= SFLAG_UNSET;
    val.ptr  = NULL;
}

// Conversion constructors
CrawlStoreValue::CrawlStoreValue(const bool _val)
    : type(SV_BOOL), flags(SFLAG_UNSET)
{
    get_bool() = _val;
}

CrawlStoreValue::CrawlStoreValue(const char &_val)
    : type(SV_BYTE), flags(SFLAG_UNSET)
{
    get_byte() = _val;
}

CrawlStoreValue::CrawlStoreValue(const short &_val)
    : type(SV_SHORT), flags(SFLAG_UNSET)
{
    get_short() = _val;
}

CrawlStoreValue::CrawlStoreValue(const long &_val)
    : type(SV_LONG), flags(SFLAG_UNSET)
{
    get_long() = _val;
}

CrawlStoreValue::CrawlStoreValue(const float &_val)
    : type(SV_FLOAT), flags(SFLAG_UNSET)
{
    get_float() = _val;
}

CrawlStoreValue::CrawlStoreValue(const std::string &_val)
    : type(SV_STR), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_string() = _val;
}

CrawlStoreValue::CrawlStoreValue(const char* _val)
    : type(SV_STR), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_string() = _val;
}

CrawlStoreValue::CrawlStoreValue(const coord_def &_val)
    : type(SV_COORD), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_coord() = _val;
}

CrawlStoreValue::CrawlStoreValue(const item_def &_val)
    : type(SV_ITEM), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_item() = _val;
}

CrawlStoreValue::CrawlStoreValue(const CrawlHashTable &_val)
    : type(SV_HASH), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_table() = _val;
}

CrawlStoreValue::CrawlStoreValue(const CrawlVector &_val)
    : type(SV_VEC), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_vector() = _val;
}

CrawlStoreValue::CrawlStoreValue(const level_id &_val)
    : type(SV_LEV_ID), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_level_id() = _val;
}

CrawlStoreValue::CrawlStoreValue(const level_pos &_val)
    : type(SV_LEV_POS), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_level_pos() = _val;
}

CrawlStoreValue::CrawlStoreValue(const monsters &_val)
    : type(SV_MONST), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_monster() = _val;
}

CrawlStoreValue::CrawlStoreValue(const dlua_chunk &_val)
    : type(SV_LUA), flags(SFLAG_UNSET)
{
    val.ptr = NULL;
    get_lua() = _val;
}

CrawlStoreValue::~CrawlStoreValue()
{
    unset(true);
}

void CrawlStoreValue::unset(bool force)
{
    if (flags & SFLAG_UNSET)
        return;

    if (force)
        flags &= ~SFLAG_NO_ERASE;

    ASSERT(!(flags & SFLAG_NO_ERASE));

    switch (type)
    {
    case SV_BOOL:
        val.boolean = false;
        break;

    case SV_BYTE:
        val.byte = 0;
        break;

    case SV_SHORT:
        val._short = 0;
        break;

    case SV_LONG:
        val._long = 0;
        break;

    case SV_FLOAT:
        val._float = 0.0;
        break;

    case SV_STR:
    {
        std::string* str = static_cast<std::string*>(val.ptr);
        delete str;
        val.ptr = NULL;
        break;
    }

    case SV_COORD:
    {
        coord_def* coord = static_cast<coord_def*>(val.ptr);
        delete coord;
        val.ptr = NULL;
        break;
    }

    case SV_ITEM:
    {
        item_def* item = static_cast<item_def*>(val.ptr);
        delete item;
        val.ptr = NULL;
        break;
    }

    case SV_HASH:
    {
        CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);
        delete hash;
        val.ptr = NULL;
        break;
    }

    case SV_VEC:
    {
        CrawlVector* vec = static_cast<CrawlVector*>(val.ptr);
        delete vec;
        val.ptr = NULL;
        break;
    }

    case SV_LEV_ID:
    {
        level_id* id = static_cast<level_id*>(val.ptr);
        delete id;
        val.ptr = NULL;
        break;
    }

    case SV_LEV_POS:
    {
        level_pos* pos = static_cast<level_pos*>(val.ptr);
        delete pos;
        val.ptr = NULL;
        break;
    }

    case SV_MONST:
    {
        monsters* mon = static_cast<monsters*>(val.ptr);
        delete mon;
        val.ptr = NULL;
        break;
    }

    case SV_LUA:
    {
        dlua_chunk* chunk = static_cast<dlua_chunk*>(val.ptr);
        delete chunk;
        val.ptr = NULL;
        break;
    }

    case SV_NONE:
        DEBUGSTR("CrawlStoreValue::unset: unsetting nothing");
        break;

    default:
        DEBUGSTR("CrawlStoreValue::unset: unsetting invalid type");
        break;
    }

    flags |= SFLAG_UNSET;
}

#define COPY_PTR(ptr_type) \
    { \
        ptr_type *ptr = static_cast<ptr_type*>(val.ptr); \
        if (ptr != NULL) \
            delete ptr; \
        ptr = static_cast<ptr_type*>(other.val.ptr); \
        val.ptr = (void*) new ptr_type (*ptr);  \
    }

CrawlStoreValue &CrawlStoreValue::operator = (const CrawlStoreValue &other)
{
    ASSERT(other.type >= SV_NONE && other.type < NUM_STORE_VAL_TYPES);
    ASSERT(other.type != SV_NONE || type == SV_NONE);

    // NOTE: We don't bother checking SFLAG_CONST_VAL, since the
    // asignment operator is used when swapping two elements.
    if (!(flags & SFLAG_UNSET))
    {
        if (flags & SFLAG_CONST_TYPE)
            ASSERT(type == SV_NONE || type == other.type);
    }

    type  = other.type;
    flags = other.flags;

    switch (type)
    {
    case SV_NONE:
    case SV_BOOL:
    case SV_BYTE:
    case SV_SHORT:
    case SV_LONG:
    case SV_FLOAT:
        val = other.val;
        break;

    case SV_STR:
        COPY_PTR(std::string);
        break;

    case SV_COORD:
        COPY_PTR(coord_def);
        break;

    case SV_ITEM:
        COPY_PTR(item_def);
        break;

    case SV_HASH:
        COPY_PTR(CrawlHashTable);
        break;

    case SV_VEC:
        COPY_PTR(CrawlVector);
        break;

    case SV_LEV_ID:
        COPY_PTR(level_id);
        break;

     case SV_LEV_POS:
        COPY_PTR(level_pos);
        break;

     default:
        DEBUGSTR("CrawlStoreValue has invalid type");
        break;
    }

    return (*this);
}

///////////////////////////////////
// Meta-data accessors and changers
store_flags CrawlStoreValue::get_flags() const
{
    return flags;
}

store_flags CrawlStoreValue::set_flags(store_flags _flags)
{
    flags |= _flags;
    return flags;
}

store_flags CrawlStoreValue::unset_flags(store_flags _flags)
{
    flags &= ~_flags;
    return flags;
}

store_val_type CrawlStoreValue::get_type() const
{
    return type;
}

//////////////////////////////
// Read/write from/to savefile
void CrawlStoreValue::write(writer &th) const
{
    ASSERT(type != SV_NONE || (flags & SFLAG_UNSET));
    ASSERT(!(flags & SFLAG_UNSET) || (type == SV_NONE));

    marshallByte(th,  (char) type);
    marshallByte(th, (char) flags);

    switch (type)
    {
    case SV_BOOL:
        marshallBoolean(th, val.boolean);
        break;

    case SV_BYTE:
        marshallByte(th, val.byte);
        break;

    case SV_SHORT:
        marshallShort(th, val._short);
        break;

    case SV_LONG:
        marshallLong(th, val._long);
        break;

    case SV_FLOAT:
        marshallFloat(th, val._float);
        break;

    case SV_STR:
    {
        std::string* str = static_cast<std::string*>(val.ptr);
        marshallString(th, *str);
        break;
    }

    case SV_COORD:
    {
        coord_def* coord = static_cast<coord_def*>(val.ptr);
        marshallCoord(th, *coord);
        break;
    }

    case SV_ITEM:
    {
        item_def* item = static_cast<item_def*>(val.ptr);
        marshallItem(th, *item);
        break;
    }

    case SV_HASH:
    {
        CrawlHashTable* hash = static_cast<CrawlHashTable*>(val.ptr);
        hash->write(th);
        break;
    }

    case SV_VEC:
    {
        CrawlVector* vec = static_cast<CrawlVector*>(val.ptr);
        vec->write(th);
        break;
    }

    case SV_LEV_ID:
    {
        level_id* id = static_cast<level_id*>(val.ptr);
        id->save(th);
        break;
    }

    case SV_LEV_POS:
    {
        level_pos* pos = static_cast<level_pos*>(val.ptr);
        pos->save(th);
        break;
    }

    case SV_MONST:
    {
        monsters* mon = static_cast<monsters*>(val.ptr);
        marshallMonster(th, *mon);
        break;
    }

    case SV_LUA:
    {
        dlua_chunk* chunk = static_cast<dlua_chunk*>(val.ptr);
        chunk->write(th);
        break;
    }

    case SV_NONE:
        break;

    case NUM_STORE_VAL_TYPES:
        ASSERT(false);
    }
}

void CrawlStoreValue::read(reader &th)
{
    type = static_cast<store_val_type>(unmarshallByte(th));
    flags = (store_flags) unmarshallByte(th);

    ASSERT(type != SV_NONE || (flags & SFLAG_UNSET));
    ASSERT(!(flags & SFLAG_UNSET) || (type == SV_NONE));

    switch (type)
    {
    case SV_BOOL:
        val.boolean = unmarshallBoolean(th);
        break;

    case SV_BYTE:
        val.byte = unmarshallByte(th);
        break;

    case SV_SHORT:
        val._short = unmarshallShort(th);
        break;

    case SV_LONG:
        val._long = unmarshallLong(th);
        break;

    case SV_FLOAT:
        val._float = unmarshallFloat(th);
        break;

    case SV_STR:
    {
        std::string str = unmarshallString(th);
        val.ptr = (void*) new std::string(str);
        break;
    }

    case SV_COORD:
    {
        coord_def coord;
        unmarshallCoord(th, coord);
        val.ptr = (void*) new coord_def(coord);

        break;
    }

    case SV_ITEM:
    {
        item_def item;
        unmarshallItem(th, item);
        val.ptr = (void*) new item_def(item);

        break;
    }

    case SV_HASH:
    {
        CrawlHashTable* hash = new CrawlHashTable();
        hash->read(th);
        val.ptr = (void*) hash;

        break;
    }

    case SV_VEC:
    {
        CrawlVector* vec = new CrawlVector();
        vec->read(th);
        val.ptr = (void*) vec;

        break;
    }

    case SV_LEV_ID:
    {
        level_id* id = new level_id();
        id->load(th);
        val.ptr = (void*) id;

        break;
    }

    case SV_LEV_POS:
    {
        level_pos* pos = new level_pos();
        pos->load(th);
        val.ptr = (void*) pos;

        break;
    }

    case SV_MONST:
    {
        monsters mon;
        unmarshallMonster(th, mon);
        val.ptr = (void*) new monsters(mon);

        break;
    }

    case SV_LUA:
    {
        dlua_chunk chunk;
        chunk.read(th);
        val.ptr = (void*) new dlua_chunk(chunk);

        break;
    }

    case SV_NONE:
        break;

    case NUM_STORE_VAL_TYPES:
        ASSERT(false);
    }
}

CrawlHashTable &CrawlStoreValue::new_table()
{
    return get_table();
}

////////////////////////////////////////////////////////////////
// Setup a new vector with the given flags and/or type; assert if
// a vector already exists.
CrawlVector &CrawlStoreValue::new_vector(store_flags _flags,
                                         vec_size max_size)
{
    return new_vector(SV_NONE, flags, max_size);
}

CrawlVector &CrawlStoreValue::new_vector(store_val_type _type,
                                         store_flags _flags,
                                         vec_size _max_size)
{
#ifdef DEBUG
    CrawlVector* old_vector = static_cast<CrawlVector*>(val.ptr);

    ASSERT(flags & SFLAG_UNSET);
    ASSERT(type == SV_NONE
           || (type == SV_VEC
               && old_vector->size() == 0
               && old_vector->get_type() == SV_NONE
               && old_vector->get_default_flags() == 0
               && old_vector->get_max_size() == VEC_MAX_SIZE));
#endif

    CrawlVector &vector = get_vector();

    vector.default_flags = _flags;
    vector.type          = _type;

    type   =  SV_VEC;
    flags &= ~SFLAG_UNSET;

    return vector;
}

///////////////////////////////////////////
// Dynamic type-checking accessor functions
#define GET_VAL(x, _type, field, value)                            \
    ASSERT((flags & SFLAG_UNSET) || !(flags & SFLAG_CONST_VAL)); \
    if (type != (x)) \
    { \
        if (type == SV_NONE) \
        { \
            type  = (x); \
            field = (value); \
        } \
        else \
        { \
            ASSERT(!(flags & SFLAG_CONST_TYPE)); \
            switch (type) \
            { \
            case SV_BOOL: \
                field = (_type) val.boolean; \
                break; \
            case SV_BYTE: \
                field = (_type) val.byte; \
                break; \
            case SV_SHORT: \
                field = (_type) val._short; \
                break; \
            case SV_LONG: \
                field = (_type) val._long; \
                break; \
            case SV_FLOAT: \
                field = (_type) val._float; \
                break; \
            default: \
                ASSERT(false); \
            } \
            type = (x); \
        } \
    } \
    flags &= ~SFLAG_UNSET; \
    return field;

#define GET_VAL_PTR(x, _type, value) \
    ASSERT((flags & SFLAG_UNSET) || !(flags & SFLAG_CONST_VAL)); \
    if (type != (x) || (flags & SFLAG_UNSET)) \
    { \
        if (type == SV_NONE) \
        { \
            type    = (x); \
            val.ptr = (value); \
        } \
        else \
        { \
            unset(); \
            val.ptr = (value); \
            type    = (x); \
        } \
    } \
    else \
        delete (value); \
    flags &= ~SFLAG_UNSET; \
    return *((_type) val.ptr);

bool &CrawlStoreValue::get_bool()
{
    GET_VAL(SV_BOOL, bool, val.boolean, false);
}

char &CrawlStoreValue::get_byte()
{
    GET_VAL(SV_BYTE, char, val.byte, 0);
}

short &CrawlStoreValue::get_short()
{
    GET_VAL(SV_SHORT, short, val._short, 0);
}

long &CrawlStoreValue::get_long()
{
    GET_VAL(SV_LONG, long, val._long, 0);
}

float &CrawlStoreValue::get_float()
{
    GET_VAL(SV_FLOAT, float, val._float, 0.0);
}

std::string &CrawlStoreValue::get_string()
{
    GET_VAL_PTR(SV_STR, std::string*, new std::string(""));
}

coord_def &CrawlStoreValue::get_coord()
{
    GET_VAL_PTR(SV_COORD, coord_def*, new coord_def());
}

item_def &CrawlStoreValue::get_item()
{
    GET_VAL_PTR(SV_ITEM, item_def*, new item_def());
}

CrawlHashTable &CrawlStoreValue::get_table()
{
    GET_VAL_PTR(SV_HASH, CrawlHashTable*, new CrawlHashTable());
}

CrawlVector &CrawlStoreValue::get_vector()
{
    GET_VAL_PTR(SV_VEC, CrawlVector*, new CrawlVector());
}

level_id &CrawlStoreValue::get_level_id()
{
    GET_VAL_PTR(SV_LEV_ID, level_id*, new level_id());
}

level_pos &CrawlStoreValue::get_level_pos()
{
    GET_VAL_PTR(SV_LEV_POS, level_pos*, new level_pos());
}

monsters &CrawlStoreValue::get_monster()
{
    GET_VAL_PTR(SV_MONST, monsters*, new monsters());
}

dlua_chunk &CrawlStoreValue::get_lua()
{
    GET_VAL_PTR(SV_LUA, dlua_chunk*, new dlua_chunk());
}

CrawlStoreValue &CrawlStoreValue::operator [] (const std::string &key)
{
    return get_table()[key];
}

CrawlStoreValue &CrawlStoreValue::operator [] (const vec_size &index)
{
    return get_vector()[index];
}

///////////////////////////
// Const accessor functions
#define GET_CONST_SETUP(x) \
    ASSERT(!(flags & SFLAG_UNSET)); \
    ASSERT(type == (x));

bool CrawlStoreValue::get_bool() const
{
    GET_CONST_SETUP(SV_BOOL);
    return val.boolean;
}

char CrawlStoreValue::get_byte() const
{
    GET_CONST_SETUP(SV_BYTE);
    return val.byte;
}

short CrawlStoreValue::get_short() const
{
    GET_CONST_SETUP(SV_SHORT);
    return val._short;
}

long CrawlStoreValue::get_long() const
{
    GET_CONST_SETUP(SV_LONG);
    return val._long;
}

float CrawlStoreValue::get_float() const
{
    GET_CONST_SETUP(SV_FLOAT);
    return val._float;
}

std::string CrawlStoreValue::get_string() const
{
    GET_CONST_SETUP(SV_STR);
    return *((std::string*)val.ptr);
}

coord_def CrawlStoreValue::get_coord() const
{
    GET_CONST_SETUP(SV_COORD);
    return *((coord_def*)val.ptr);
}

const item_def& CrawlStoreValue::get_item() const
{
    GET_CONST_SETUP(SV_ITEM);
    return *((item_def*)val.ptr);
}

const CrawlHashTable& CrawlStoreValue::get_table() const
{
    GET_CONST_SETUP(SV_HASH);
    return *((CrawlHashTable*)val.ptr);
}

const CrawlVector& CrawlStoreValue::get_vector() const
{
    GET_CONST_SETUP(SV_VEC);
    return *((CrawlVector*)val.ptr);
}

level_id CrawlStoreValue::get_level_id() const
{
    GET_CONST_SETUP(SV_LEV_ID);
    return *((level_id*)val.ptr);
}

level_pos CrawlStoreValue::get_level_pos() const
{
    GET_CONST_SETUP(SV_LEV_POS);
    return *((level_pos*)val.ptr);
}

const CrawlStoreValue &CrawlStoreValue::operator
    [] (const std::string &key) const
{
    return get_table()[key];
}

const CrawlStoreValue &CrawlStoreValue::operator
    [](const vec_size &index) const
{
    return get_vector()[index];
}

/////////////////////
// Typecast operators
CrawlStoreValue::operator bool&()                  { return get_bool();       }
CrawlStoreValue::operator char&()                  { return get_byte();       }
CrawlStoreValue::operator short&()                 { return get_short();      }
CrawlStoreValue::operator float&()                 { return get_float();      }
CrawlStoreValue::operator long&()                  { return get_long();       }
CrawlStoreValue::operator std::string&()           { return get_string();     }
CrawlStoreValue::operator coord_def&()             { return get_coord();      }
CrawlStoreValue::operator CrawlHashTable&()        { return get_table();      }
CrawlStoreValue::operator CrawlVector&()           { return get_vector();     }
CrawlStoreValue::operator item_def&()              { return get_item();       }
CrawlStoreValue::operator level_id&()              { return get_level_id();   }
CrawlStoreValue::operator level_pos&()             { return get_level_pos();  }
CrawlStoreValue::operator monsters&()              { return get_monster();    }
CrawlStoreValue::operator dlua_chunk&()            { return get_lua(); }

///////////////////////////
// Const typecast operators
CrawlStoreValue::operator bool() const
{
    return get_bool();
}

#define CONST_INT_CAST() \
    switch (type) \
    { \
    case SV_BYTE: \
        return get_byte(); \
    case SV_SHORT: \
        return get_short(); \
    case SV_LONG: \
        return get_long(); \
    default: \
        ASSERT(false); \
        return 0; \
    }

CrawlStoreValue::operator char() const
{
    CONST_INT_CAST();
}

CrawlStoreValue::operator short() const
{
    CONST_INT_CAST();
}

CrawlStoreValue::operator long() const
{
    CONST_INT_CAST();
}

CrawlStoreValue::operator float() const
{
    return get_float();
}

CrawlStoreValue::operator std::string() const
{
    return get_string();
}

CrawlStoreValue::operator coord_def() const
{
    return get_coord();
}

CrawlStoreValue::operator level_id() const
{
    return get_level_id();
}

CrawlStoreValue::operator level_pos() const
{
    return get_level_pos();
}

///////////////////////
// Assignment operators
CrawlStoreValue &CrawlStoreValue::operator = (const bool &_val)
{
    get_bool() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const char &_val)
{
    get_byte() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const short &_val)
{
    get_short() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const long &_val)
{
    get_long() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const float &_val)
{
    get_float() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const std::string &_val)
{
    get_string() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const char* _val)
{
    get_string() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const coord_def &_val)
{
    get_coord() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const CrawlHashTable &_val)
{
    get_table() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const CrawlVector &_val)
{
    get_vector() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const item_def &_val)
{
    get_item() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const level_id &_val)
{
    get_level_id() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const level_pos &_val)
{
    get_level_pos() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const monsters &_val)
{
    get_monster() = _val;
    return (*this);
}

CrawlStoreValue &CrawlStoreValue::operator = (const dlua_chunk &_val)
{
    get_lua() = _val;
    return (*this);
}

///////////////////////////////////////////////////
// Non-assignment operators which affect the lvalue
#define INT_OPERATOR_UNARY(op) \
    switch (type) \
    { \
    case SV_BYTE: \
    { \
        char &temp = get_byte(); \
        temp op; \
        return temp; \
    } \
 \
    case SV_SHORT: \
    { \
        short &temp = get_short(); \
        temp op; \
        return temp; \
    } \
    case SV_LONG: \
    { \
        long &temp = get_long(); \
        temp op; \
        return temp; \
    } \
 \
    default: \
        ASSERT(false); \
        return 0; \
    }

// Prefix
long CrawlStoreValue::operator ++ ()
{
    INT_OPERATOR_UNARY(++);
}

long CrawlStoreValue::operator -- ()
{
    INT_OPERATOR_UNARY(--);
}

// Postfix
long CrawlStoreValue::operator ++ (int)
{
    INT_OPERATOR_UNARY(++);
}

long CrawlStoreValue::operator -- (int)
{
    INT_OPERATOR_UNARY(--);
}

std::string &CrawlStoreValue::operator += (const std::string &_val)
{
    return (get_string() += _val);
}

////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////

CrawlHashTable::CrawlHashTable()
{
    hash_map = NULL;
}

CrawlHashTable::CrawlHashTable(const CrawlHashTable& other)
{
    if (other.hash_map == NULL)
    {
        hash_map = NULL;
        return;
    }

    hash_map = new hash_map_type(*(other.hash_map));
}

CrawlHashTable::~CrawlHashTable()
{
    // NOTE: Not using std::auto_ptr because making hash_map an auto_ptr
    // causes compile weirdness in externs.h
    if (hash_map == NULL)
        return;

    delete hash_map;
    hash_map = NULL;
}

CrawlHashTable &CrawlHashTable::operator = (const CrawlHashTable &other)
{
    if (hash_map != NULL)
        delete hash_map;

    if (other.hash_map == NULL)
    {
        hash_map = NULL;
        return (*this);
    }

    hash_map = new hash_map_type(*(other.hash_map));

    return (*this);
}

//////////////////////////////
// Read/write from/to savefile
void CrawlHashTable::write(writer &th) const
{
    assert_validity();
    if (empty())
    {
        marshallByte(th, 0);
        return;
    }

    marshallByte(th, size());

    CrawlHashTable::hash_map_type::const_iterator i = hash_map->begin();

    for (; i != hash_map->end(); i++)
    {
        marshallString(th, i->first);
        i->second.write(th);
    }

    assert_validity();
}

void CrawlHashTable::read(reader &th)
{
    assert_validity();

    ASSERT(empty());

    hash_size _size = (hash_size) unmarshallByte(th);

    if (_size == 0)
        return;

    init_hash_map();

    for (hash_size i = 0; i < _size; i++)
    {
        std::string      key = unmarshallString(th);
        CrawlStoreValue &val = (*this)[key];

        val.read(th);
    }

    assert_validity();
}


//////////////////
// Misc functions

bool CrawlHashTable::exists(const std::string &key) const
{
    if (hash_map == NULL)
        return (false);

    assert_validity();
    hash_map_type::const_iterator i = hash_map->find(key);

    return (i != hash_map->end());
}

void CrawlHashTable::assert_validity() const
{
#ifdef DEBUG
    if (hash_map == NULL)
        return;

    hash_map_type::const_iterator i = hash_map->begin();

    unsigned long actual_size = 0;

    for (; i != hash_map->end(); i++)
    {
        actual_size++;

        const std::string    &key = i->first;
        const CrawlStoreValue &val = i->second;

        ASSERT(!key.empty());
        std::string trimmed = trimmed_string(key);
        ASSERT(key == trimmed);

        ASSERT(val.type != SV_NONE);
        ASSERT(!(val.flags & SFLAG_UNSET));

        switch (val.type)
        {
        case SV_STR:
        case SV_COORD:
        case SV_ITEM:
        case SV_LEV_ID:
        case SV_LEV_POS:
            ASSERT(val.val.ptr != NULL);
            break;

        case SV_HASH:
        {
            ASSERT(val.val.ptr != NULL);

            CrawlHashTable* nested;
            nested = static_cast<CrawlHashTable*>(val.val.ptr);

            nested->assert_validity();
            break;
        }

        case SV_VEC:
        {
            ASSERT(val.val.ptr != NULL);

            CrawlVector* nested;
            nested = static_cast<CrawlVector*>(val.val.ptr);

            nested->assert_validity();
            break;
        }

        default:
            break;
        }
    }

    ASSERT(size() == actual_size);
#endif
}

////////////////////////////////
// Accessors to contained values

CrawlStoreValue& CrawlHashTable::get_value(const std::string &key)
{
    assert_validity();
    init_hash_map();

    iterator i = hash_map->find(key);

    if (i == hash_map->end())
    {
        (*hash_map)[key]     = CrawlStoreValue();
        CrawlStoreValue &val = (*hash_map)[key];

        return (val);
    }

    return (i->second);
}

const CrawlStoreValue& CrawlHashTable::get_value(const std::string &key) const
{
    ASSERT(hash_map != NULL);
    assert_validity();

    hash_map_type::const_iterator i = hash_map->find(key);

    ASSERT(i != hash_map->end());
    ASSERT(i->second.type != SV_NONE);
    ASSERT(!(i->second.flags & SFLAG_UNSET));

    return (i->second);
}

CrawlStoreValue& CrawlHashTable::operator[] (const std::string &key)
{
    return get_value(key);
}

const CrawlStoreValue& CrawlHashTable::operator[] (const std::string &key)
    const
{
    return get_value(key);
}

///////////////////////////
// std::map style interface
hash_size CrawlHashTable::size() const
{
    if (hash_map == NULL)
        return (0);

    return hash_map->size();
}

bool CrawlHashTable::empty() const
{
    if (hash_map == NULL)
        return (true);

    return hash_map->empty();
}

void CrawlHashTable::erase(const std::string key)
{
    assert_validity();
    init_hash_map();

    iterator i = hash_map->find(key);

    if (i != hash_map->end())
    {
#ifdef DEBUG
        CrawlStoreValue &val = i->second;
        ASSERT(!(val.flags & SFLAG_NO_ERASE));
#endif

        hash_map->erase(i);
    }
}

void CrawlHashTable::clear()
{
    assert_validity();
    if (hash_map == NULL)
        return;

    delete hash_map;
    hash_map = NULL;
}

CrawlHashTable::iterator CrawlHashTable::begin()
{
    assert_validity();
    init_hash_map();

    return hash_map->begin();
}

CrawlHashTable::iterator CrawlHashTable::end()
{
    assert_validity();
    init_hash_map();

    return hash_map->end();
}

CrawlHashTable::const_iterator CrawlHashTable::begin() const
{
    ASSERT(hash_map != NULL);
    assert_validity();

    return hash_map->begin();
}

CrawlHashTable::const_iterator CrawlHashTable::end() const
{
    ASSERT(hash_map != NULL);
    assert_validity();

    return hash_map->end();
}

void CrawlHashTable::init_hash_map()
{
    if (hash_map != NULL)
        return;

    hash_map = new hash_map_type();
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

CrawlVector::CrawlVector()
    : type(SV_NONE), default_flags(0), max_size(VEC_MAX_SIZE)
{
}

CrawlVector::CrawlVector(store_flags flags, vec_size _max_size)
    : type(SV_NONE), default_flags(flags), max_size(_max_size)
{
    ASSERT(!(default_flags & SFLAG_UNSET));
    ASSERT(max_size > 0);
}

CrawlVector::CrawlVector(store_val_type _type, store_flags flags,
                         vec_size _max_size)
    : type(_type), default_flags(flags), max_size(_max_size)
{
    ASSERT(type >= SV_NONE && type < NUM_STORE_VAL_TYPES);
    ASSERT(!(default_flags & SFLAG_UNSET));
    ASSERT(max_size > 0);
}

CrawlVector::~CrawlVector()
{
    assert_validity();
}

//////////////////////////////
// Read/write from/to savefile
void CrawlVector::write(writer &th) const
{
    assert_validity();
    if (empty())
    {
        marshallByte(th, 0);
        return;
    }

    marshallByte(th, (char) size());
    marshallByte(th, (char) max_size);
    marshallByte(th, static_cast<char>(type));
    marshallByte(th, (char) default_flags);

    for (vec_size i = 0; i < size(); i++)
    {
        CrawlStoreValue val = vector[i];
       val.write(th);
    }

    assert_validity();
}

void CrawlVector::read(reader &th)
{
    assert_validity();

    ASSERT(empty());
    ASSERT(type == SV_NONE);
    ASSERT(default_flags == 0);
    ASSERT(max_size == VEC_MAX_SIZE);

    vec_size _size = (vec_size) unmarshallByte(th);

    if (_size == 0)
        return;

    max_size      = static_cast<vec_size>(unmarshallByte(th));
    type          = static_cast<store_val_type>(unmarshallByte(th));
    default_flags = static_cast<store_flags>(unmarshallByte(th));

    ASSERT(_size <= max_size);

    vector.resize(_size);

    for (vec_size i = 0; i < _size; i++)
        vector[i].read(th);

    assert_validity();
}


//////////////////
// Misc functions

store_flags CrawlVector::get_default_flags() const
{
    assert_validity();
    return default_flags;
}

store_flags CrawlVector::set_default_flags(store_flags flags)
{
    assert_validity();
    ASSERT(!(flags & SFLAG_UNSET));
    default_flags |= flags;

    return default_flags;
}

store_flags CrawlVector::unset_default_flags(store_flags flags)
{
    assert_validity();
    ASSERT(!(flags & SFLAG_UNSET));
    default_flags &= ~flags;

    return default_flags;
}

store_val_type CrawlVector::get_type() const
{
    assert_validity();
    return type;
}

void CrawlVector::assert_validity() const
{
#ifdef DEBUG
    ASSERT(!(default_flags & SFLAG_UNSET));
    ASSERT(max_size > 0);
    ASSERT(max_size >= size());

    for (vec_size i = 0, _size = size(); i < _size; i++)
    {
        const CrawlStoreValue &val = vector[i];

        if (type != SV_NONE)
            ASSERT(val.type == SV_NONE || val.type == type);

        // A vector might be resize()'d and filled up with unset
        // values, which are then set one by one, so we can't
        // assert over that here.
        if (val.type == SV_NONE || (val.flags & SFLAG_UNSET))
            continue;

        switch (val.type)
        {
        case SV_STR:
        case SV_COORD:
        case SV_ITEM:
        case SV_LEV_ID:
        case SV_LEV_POS:
            ASSERT(val.val.ptr != NULL);
            break;

        case SV_HASH:
        {
            ASSERT(val.val.ptr != NULL);

            CrawlHashTable* nested;
            nested = static_cast<CrawlHashTable*>(val.val.ptr);

            nested->assert_validity();
            break;
        }

        case SV_VEC:
        {
            ASSERT(val.val.ptr != NULL);

            CrawlVector* nested;
            nested = static_cast<CrawlVector*>(val.val.ptr);

            nested->assert_validity();
            break;
        }

        default:
            break;
        }
    }
#endif
}

void CrawlVector::set_max_size(vec_size _size)
{
    ASSERT(_size > 0);
    ASSERT(max_size == VEC_MAX_SIZE || max_size == _size);
    max_size = _size;

    vector.reserve(max_size);
}

vec_size CrawlVector::get_max_size() const
{
    return max_size;
}

////////////////////////////////
// Accessors to contained values

CrawlStoreValue& CrawlVector::get_value(const vec_size &index)
{
    assert_validity();

    ASSERT(index <= max_size);
    ASSERT(index <= vector.size());

    return vector[index];
}

const CrawlStoreValue& CrawlVector::get_value(const vec_size &index) const
{
    assert_validity();

    ASSERT(index <= max_size);
    ASSERT(index <= vector.size());

    return vector[index];
}

CrawlStoreValue& CrawlVector::operator[] (const vec_size &index)
{
    return get_value(index);
}

const CrawlStoreValue& CrawlVector::operator[] (const vec_size &index) const
{
    return get_value(index);
}

///////////////////////////
// std::vector style interface
vec_size CrawlVector::size() const
{
    return vector.size();
}

bool CrawlVector::empty() const
{
    return vector.empty();
}

CrawlStoreValue& CrawlVector::pop_back()
{
    assert_validity();
    ASSERT(vector.size() > 0);

    CrawlStoreValue& val = vector[vector.size() - 1];
    vector.pop_back();
    return val;
}

void CrawlVector::push_back(CrawlStoreValue val)
{
#ifdef DEBUG
    if (type != SV_NONE)
        ASSERT(type == val.type);

    switch (val.type)
    {
    case SV_STR:
    case SV_COORD:
    case SV_ITEM:
    case SV_LEV_ID:
    case SV_LEV_POS:
        ASSERT(val.val.ptr != NULL);
        break;

    case SV_HASH:
    {
        ASSERT(val.val.ptr != NULL);

        CrawlHashTable* nested;
        nested = static_cast<CrawlHashTable*>(val.val.ptr);

        nested->assert_validity();
        break;
    }

    case SV_VEC:
    {
        ASSERT(val.val.ptr != NULL);

        CrawlVector* nested;
        nested = static_cast<CrawlVector*>(val.val.ptr);

        nested->assert_validity();
        break;
    }

    default:
        break;
    }
#endif

    assert_validity();
    ASSERT(vector.size() < max_size);
    ASSERT(type == SV_NONE
           || (val.type == SV_NONE && (val.flags & SFLAG_UNSET))
           || (val.type == type));
    val.flags |= default_flags;
    if (type != SV_NONE)
    {
        val.type   = type;
        val.flags |= SFLAG_CONST_TYPE;
    }
    vector.push_back(val);
    assert_validity();
}

void CrawlVector::insert(const vec_size index, CrawlStoreValue val)
{
    assert_validity();
    ASSERT(vector.size() < max_size);
    ASSERT(type == SV_NONE
           || (val.type == SV_NONE && (val.flags & SFLAG_UNSET))
           || (val.type == type));
    val.flags |= default_flags;
    if (type != SV_NONE)
    {
        val.type   = type;
        val.flags |= SFLAG_CONST_TYPE;
    }
    vector.insert(vector.begin() + index, val);
}

void CrawlVector::resize(const vec_size _size)
{
    assert_validity();
    ASSERT(max_size == VEC_MAX_SIZE);
    ASSERT(_size < max_size);

    vec_size old_size = size();
    vector.resize(_size);

    for (vec_size i = old_size; i < _size; i++)
    {
        vector[i].flags = SFLAG_UNSET | default_flags;
        vector[i].type  = SV_NONE;
    }
}

void CrawlVector::erase(const vec_size index)
{
    assert_validity();
    ASSERT(index <= max_size);
    ASSERT(index <= vector.size());

    vector.erase(vector.begin() + index);
}

void CrawlVector::clear()
{
    assert_validity();
    ASSERT(!(default_flags & SFLAG_NO_ERASE));

    for (vec_size i = 0, _size = size(); i < _size; i++)
        ASSERT(!(vector[i].flags & SFLAG_NO_ERASE));

    vector.clear();
    default_flags = 0;
    type          = SV_NONE;
}

CrawlVector::iterator CrawlVector::begin()
{
    assert_validity();
    return vector.begin();
}

CrawlVector::iterator CrawlVector::end()
{
    assert_validity();
    return vector.end();
}

CrawlVector::const_iterator CrawlVector::begin() const
{
    assert_validity();
    return vector.begin();
}

CrawlVector::const_iterator CrawlVector::end() const
{
    assert_validity();
    return vector.end();
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

template <typename T, store_val_type TYPE>
CrawlTableWrapper<T, TYPE>::CrawlTableWrapper()
{
    table = NULL;
}

template <typename T, store_val_type TYPE>
CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable& _table)
{
    wrap(_table);
}

template <typename T, store_val_type TYPE>
CrawlTableWrapper<T, TYPE>::CrawlTableWrapper(CrawlHashTable* _table)
{
    wrap(_table);
}

template <typename T, store_val_type TYPE>
void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable& _table)
{
    wrap(&_table);
}

template <typename T, store_val_type TYPE>
void CrawlTableWrapper<T, TYPE>::wrap(CrawlHashTable* _table)
{
    ASSERT(_table != NULL);

    table = _table;
}

template <typename T, store_val_type TYPE>
T& CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key)
{
    return (T&) (*table)[key];
}

template <typename T, store_val_type TYPE>
const T CrawlTableWrapper<T, TYPE>::operator[] (const std::string &key) const
{
    return (T) (*table)[key];
}