* rewrote the SCP data storage. note that all database files must now be placed in a directory added with "AddDBPath <path>" and must contain a #dbname tag. the db now converts SCP (text) to SCC (binary) files, which increases access speed a lot. also less RAM used in most cases.
* replaced "LoadSCP" func with "LoadDB <dbname>" (different syntax!); removed all other scp db related funcs except "getscpvalue" * the GUI can now show texts stored in databases * added displaying status to SceneLogin * misc fixes/cleanups
This commit is contained in:
parent
f484de89a9
commit
76ebbe5cf3
@ -41,11 +41,5 @@ ELSE
|
||||
ENDIF
|
||||
UNSET errors
|
||||
|
||||
// Load some SCP files
|
||||
LOADALLSCP
|
||||
|
||||
// do more stuff here in future...
|
||||
|
||||
LOG * StartUp complete!
|
||||
|
||||
LOG *** StartUp complete!
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#dbname=class
|
||||
|
||||
[1]
|
||||
name=Warrior
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// define names of genders
|
||||
#dbname=gender
|
||||
|
||||
[0]
|
||||
name=male
|
||||
|
||||
25
bin/data/scp/gui_login_text.scp
Normal file
25
bin/data/scp/gui_login_text.scp
Normal file
@ -0,0 +1,25 @@
|
||||
#dbname=gui_login_text
|
||||
|
||||
// check src/Client/GUI/SceneData.h for the fields/numbers.
|
||||
// this file covers the texts for SceneLogin.
|
||||
|
||||
// connection info
|
||||
[1]
|
||||
0=Not connected.
|
||||
1=Connecting...
|
||||
2=Could not connect.
|
||||
3=Account does not exist!
|
||||
4=Account is already connected!
|
||||
5=Wrong game version. (Try to set a different version info in PseuWoW.conf)
|
||||
6=Logging in...
|
||||
7=Authenticating...
|
||||
8=Authentication failed! (Wrong password?)
|
||||
9=Requesting realmlist...
|
||||
10=Unknown error!
|
||||
11=Incoming file transfer.
|
||||
|
||||
// msgbox texts. 0: header 1:body
|
||||
[2]
|
||||
0=Oh noes!
|
||||
1=You have to enter account name and password!
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#dbname=language
|
||||
|
||||
[0]
|
||||
name=Global
|
||||
|
||||
|
||||
@ -21,45 +21,6 @@ SET,${v} ${${v}}${@def}
|
||||
// remove the name placeholder
|
||||
UNSET v
|
||||
|
||||
|
||||
//--------------------------------------------------------
|
||||
#script=exloadscp
|
||||
#permission=255
|
||||
//--------------------------------------------------------
|
||||
if ?{fileexists ${@def}}
|
||||
loadscp,{${@0}} ${@def}
|
||||
else
|
||||
logdebug skipped loading of non-existent file '${@def}'
|
||||
endif
|
||||
|
||||
|
||||
//--------------------------------------------------------
|
||||
#script=loadallscp
|
||||
#permission=255
|
||||
//--------------------------------------------------------
|
||||
LOG * Loading SCP data storages...
|
||||
|
||||
// example:
|
||||
// LOADSCP,test data/test.scp
|
||||
|
||||
// load default databases
|
||||
LOADSCP,class data/scp/class.scp
|
||||
LOADSCP,gender data/scp/gender.scp
|
||||
LOADSCP,language data/scp/language.scp
|
||||
LOADSCP,map data/scp/map.scp
|
||||
LOADSCP,race data/scp/race.scp
|
||||
|
||||
// load extended databases if present.
|
||||
EXLOADSCP,sound data/scp/sound.scp
|
||||
EXLOADSCP,emote data/scp/emote.scp
|
||||
EXLOADSCP,zone data/scp/zone.scp
|
||||
EXLOADSCP,creaturedisplayinfo data/scp/creaturedisplayinfo.scp
|
||||
EXLOADSCP,creaturemodeldata data/scp/creaturemodeldata.scp
|
||||
EXLOADSCP,npcsound data/scp/npcsound.scp
|
||||
|
||||
LOG * SCP loaded.
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
#script=string_is_command
|
||||
//-------------------------------------------------
|
||||
|
||||
45
bin/scripts/database_loader.def
Normal file
45
bin/scripts/database_loader.def
Normal file
@ -0,0 +1,45 @@
|
||||
#script=register_db_loader
|
||||
if ?{not ?{IsHooked _startup}}
|
||||
HookStart _startup
|
||||
HookAdd db_loader_load_all
|
||||
HookEnd
|
||||
|
||||
// set up paths; ./data/scp and ./cache are set in the core already
|
||||
// the SCP files placed in this directory are used to override some values
|
||||
// in already present SCP files or to add custom fields or content
|
||||
// note: it DOES matter in which order paths are added!!
|
||||
AddDBPath ./data/scp-patches
|
||||
endif
|
||||
|
||||
//---------------------------------------------------------
|
||||
#script=db_loader_load_all
|
||||
|
||||
log ** Loading / dyncompiling databases...
|
||||
|
||||
// game databases
|
||||
LoadDB race
|
||||
LoadDB class
|
||||
LoadDB gender
|
||||
LoadDB language
|
||||
LoadDB emote
|
||||
LoadDB map
|
||||
LoadDB zone
|
||||
LoadDB creaturedisplayinfo
|
||||
LoadDB creaturemodeldata
|
||||
// LoadDB itemdisplayinfo // not yet used
|
||||
// LoadDB charsections // not yet used
|
||||
// LoadDB sound // not yet used
|
||||
// LoadDB npcsound // not yet used
|
||||
|
||||
// GUI related databases
|
||||
LoadDB gui_login_text
|
||||
|
||||
|
||||
log ** Databases loaded.
|
||||
|
||||
|
||||
//----------------------------------------------------------
|
||||
#script=dummy
|
||||
#onload
|
||||
register_db_loader
|
||||
#/onload
|
||||
@ -29,6 +29,7 @@ struct DefReturnResult
|
||||
DefReturnResult(bool b) { ok=true; mustreturn=false; ret=b?"true":"false"; }
|
||||
DefReturnResult(std::string s) { ok=true; mustreturn=false; ret=s; }
|
||||
DefReturnResult(const char *s) { DefReturnResult(std::string(s)); }
|
||||
DefReturnResult(char *s) { DefReturnResult(std::string(s)); }
|
||||
bool ok; // true if the execution of the current statement was successful
|
||||
bool mustreturn;
|
||||
std::string ret; // return value used by ?{..}
|
||||
|
||||
@ -32,10 +32,6 @@ void DefScriptPackage::_InitDefScriptInterface(void)
|
||||
AddFunc("castspell",&DefScriptPackage::SCcastspell);
|
||||
AddFunc("queryitem",&DefScriptPackage::SCqueryitem);
|
||||
AddFunc("target",&DefScriptPackage::SCtarget);
|
||||
AddFunc("loadscp",&DefScriptPackage::SCloadscp);
|
||||
AddFunc("scpexists",&DefScriptPackage::SCScpExists);
|
||||
AddFunc("scpsectionexists",&DefScriptPackage::SCScpSectionExists);
|
||||
AddFunc("scpentryexists",&DefScriptPackage::SCScpEntryExists);
|
||||
AddFunc("getscpvalue",&DefScriptPackage::SCGetScpValue);
|
||||
AddFunc("getplayerguid",&DefScriptPackage::SCGetPlayerGuid);
|
||||
AddFunc("getname",&DefScriptPackage::SCGetName);
|
||||
@ -61,6 +57,8 @@ void DefScriptPackage::_InitDefScriptInterface(void)
|
||||
AddFunc("switchopcodehandler",&DefScriptPackage::SCSwitchOpcodeHandler);
|
||||
AddFunc("opcodedisabled",&DefScriptPackage::SCOpcodeDisabled);
|
||||
AddFunc("spoofworldpacket",&DefScriptPackage::SCSpoofWorldPacket);
|
||||
AddFunc("loaddb",&DefScriptPackage::SCLoadDB);
|
||||
AddFunc("adddbpath",&DefScriptPackage::SCAddDBPath);
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCshdn(CmdSet& Set)
|
||||
@ -81,15 +79,17 @@ DefReturnResult DefScriptPackage::SCSendChatMessage(CmdSet& Set){
|
||||
DEF_RETURN_ERROR;
|
||||
}
|
||||
std::stringstream ss;
|
||||
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
|
||||
uint32 type=atoi(Set.arg[0].c_str());
|
||||
uint32 lang=atoi(Set.arg[1].c_str());
|
||||
|
||||
ss << lang;
|
||||
if(ss.str()!=Set.arg[1]) // given lang is NOT a number
|
||||
{
|
||||
uint32 dblang;
|
||||
dblang = ((PseuInstance*)parentMethod)->dbmgr.GetDB("language").GetFieldByValue("name",Set.arg[1]);
|
||||
SCPDatabase *langdb = dbmgr.GetDB("language");
|
||||
uint32 dblang = langdb->GetFieldByStringValue("name",(char*)Set.arg[1].c_str());
|
||||
logdev("looking up language id for lang '%s', found %i",Set.arg[1].c_str(),dblang);
|
||||
// TODO: comment this out to enable using addon language??!
|
||||
if(dblang != -1)
|
||||
lang = dblang;
|
||||
}
|
||||
@ -135,12 +135,13 @@ DefReturnResult DefScriptPackage::SCemote(CmdSet& Set){
|
||||
// this supports calls like "emote 126" and "emote ready"
|
||||
uint32 id = uint32(-1);
|
||||
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
|
||||
if(dbmgr.HasDB("emote"))
|
||||
SCPDatabase *emotedb = dbmgr.GetDB("emote");
|
||||
if(emotedb)
|
||||
{
|
||||
SCPDatabase& db = dbmgr.GetDB("emote");
|
||||
id = db.GetFieldByValue("name",DefScriptTools::stringToUpper(Set.defaultarg)); // emote names are always uppercased
|
||||
|
||||
id = emotedb->GetFieldByStringValue("name",(char*)DefScriptTools::stringToUpper(Set.defaultarg).c_str()); // emote names are always uppercased
|
||||
}
|
||||
if(id == uint32(-1))
|
||||
if(id == SCP_INVALID_INT)
|
||||
{
|
||||
id=atoi(Set.defaultarg.c_str());
|
||||
if(!id)
|
||||
@ -339,6 +340,7 @@ DefReturnResult DefScriptPackage::SCtarget(CmdSet& Set)
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
DefReturnResult DefScriptPackage::SCloadscp(CmdSet& Set)
|
||||
{
|
||||
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
|
||||
@ -366,38 +368,9 @@ DefReturnResult DefScriptPackage::SCloadscp(CmdSet& Set)
|
||||
logerror("Failed to load SCP: \"%s\" [%s]",dbname.c_str(),Set.defaultarg.c_str());
|
||||
}
|
||||
}
|
||||
return DefScriptTools::toString((uint64)sections);;
|
||||
return DefScriptTools::toString((uint64)sections);
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCScpExists(CmdSet& Set)
|
||||
{
|
||||
return (!Set.defaultarg.empty()) && ((PseuInstance*)parentMethod)->dbmgr.HasDB(Set.defaultarg);
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCScpSectionExists(CmdSet& Set)
|
||||
{
|
||||
static std::string dbname;
|
||||
if(!Set.arg[0].empty())
|
||||
dbname=Set.arg[0];
|
||||
return (!Set.defaultarg.empty()) && (!dbname.empty())
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.HasDB(dbname)
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).HasField((uint32)DefScriptTools::toUint64(Set.defaultarg));
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCScpEntryExists(CmdSet& Set)
|
||||
{
|
||||
static std::string dbname;
|
||||
static uint32 keyid;
|
||||
if(!Set.arg[0].empty())
|
||||
dbname=Set.arg[0];
|
||||
if(!Set.arg[1].empty())
|
||||
keyid=(uint32)DefScriptTools::toUint64(Set.arg[1]);
|
||||
return (!Set.defaultarg.empty()) && (!dbname.empty())
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.HasDB(dbname)
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).HasField(keyid)
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).GetField(keyid).HasEntry(Set.defaultarg);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
// GetScpValue,db,key entry
|
||||
// db & key will be stored, that multiple calls like GetScpValue entryxyz are possible
|
||||
@ -406,7 +379,7 @@ DefReturnResult DefScriptPackage::SCGetScpValue(CmdSet& Set)
|
||||
static std::string dbname;
|
||||
static uint32 keyid;
|
||||
std::string entry;
|
||||
DefReturnResult r;
|
||||
SCPDatabaseMgr& dbmgr = ((PseuInstance*)parentMethod)->dbmgr;
|
||||
|
||||
if(!Set.arg[0].empty())
|
||||
dbname=Set.arg[0];
|
||||
@ -414,18 +387,36 @@ DefReturnResult DefScriptPackage::SCGetScpValue(CmdSet& Set)
|
||||
keyid=(uint32)DefScriptTools::toUint64(Set.arg[1]);
|
||||
if(!Set.defaultarg.empty())
|
||||
entry=Set.defaultarg;
|
||||
if( (!entry.empty()) && (!dbname.empty())
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.HasDB(dbname)
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).HasField(keyid)
|
||||
&& ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).GetField(keyid).HasEntry(entry))
|
||||
if( (!entry.empty()) && (!dbname.empty()) )
|
||||
{
|
||||
r.ret = ((PseuInstance*)parentMethod)->dbmgr.GetDB(dbname).GetField(keyid).GetString(entry);
|
||||
SCPDatabase *db = dbmgr.GetDB(dbname);
|
||||
if(db)
|
||||
{
|
||||
uint32 ftype = db->GetFieldType((char*)entry.c_str());
|
||||
switch(ftype)
|
||||
{
|
||||
case SCP_TYPE_INT:
|
||||
{
|
||||
return DefScriptTools::toString(db->GetInt(keyid,(char*)entry.c_str()));
|
||||
}
|
||||
case SCP_TYPE_FLOAT:
|
||||
{
|
||||
return DefScriptTools::toString(db->GetFloat(keyid,(char*)entry.c_str()));
|
||||
}
|
||||
case SCP_TYPE_STRING:
|
||||
{
|
||||
return std::string(db->GetString(keyid,(char*)entry.c_str()));
|
||||
}
|
||||
default: logerror("GetSCPValue: field '%s' does not exist in DB '%s'!",entry.c_str(),dbname.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("GetSCPValue: No such DB: '%s'",dbname.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r.ret = "";
|
||||
}
|
||||
return r;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCGetPlayerGuid(CmdSet& Set)
|
||||
@ -825,15 +816,16 @@ DefReturnResult DefScriptPackage::SCGetFileList(CmdSet& Set)
|
||||
DefReturnResult DefScriptPackage::SCPrintScript(CmdSet &Set)
|
||||
{
|
||||
DefScript *sc = GetScript(DefScriptTools::stringToLower(Set.defaultarg));
|
||||
if(sc)
|
||||
if(!sc)
|
||||
return false;
|
||||
|
||||
logcustom(0,GREEN,"== DefScript \"%s\", %u lines: ==",sc->GetName().c_str(),sc->GetLines());
|
||||
for(uint32 i = 0; i < sc->GetLines(); i++)
|
||||
{
|
||||
logcustom(0,GREEN,"== DefScript \"%s\", %u lines: ==",sc->GetName().c_str(),sc->GetLines());
|
||||
for(uint32 i = 0; i < sc->GetLines(); i++)
|
||||
{
|
||||
logcustom(0,GREEN,sc->GetLine(i).c_str());
|
||||
}
|
||||
logcustom(0,GREEN,sc->GetLine(i).c_str());
|
||||
}
|
||||
return "";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCGetObjectValue(CmdSet &Set)
|
||||
@ -1100,7 +1092,7 @@ DefReturnResult DefScriptPackage::SCSwitchOpcodeHandler(CmdSet &Set)
|
||||
WorldSession *ws = ((PseuInstance*)parentMethod)->GetWSession();
|
||||
if(!ws)
|
||||
{
|
||||
logerror("Invalid Script call: SCRemoveOpcodeHandler: WorldSession not valid");
|
||||
logerror("Invalid Script call: SCSwitchOpcodeHandler: WorldSession not valid");
|
||||
DEF_RETURN_ERROR;
|
||||
}
|
||||
uint16 opc = (uint16)DefScriptTools::toUint64(Set.arg[0]);
|
||||
@ -1173,6 +1165,23 @@ DefReturnResult DefScriptPackage::SCSpoofWorldPacket(CmdSet &Set)
|
||||
return false;
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCLoadDB(CmdSet &Set)
|
||||
{
|
||||
PseuInstance *ins = (PseuInstance*)parentMethod;
|
||||
if(ins->dbmgr.GetDB(Set.defaultarg.c_str()))
|
||||
return "exists";
|
||||
logdetail("Loading database '%s'",Set.defaultarg.c_str());
|
||||
uint32 result = ins->dbmgr.SearchAndLoad((char*)Set.defaultarg.c_str(), false);
|
||||
return toString(result);
|
||||
}
|
||||
|
||||
DefReturnResult DefScriptPackage::SCAddDBPath(CmdSet &Set)
|
||||
{
|
||||
PseuInstance *ins = (PseuInstance*)parentMethod;
|
||||
ins->dbmgr.AddSearchPath((char*)Set.defaultarg.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void DefScriptPackage::My_LoadUserPermissions(VarSet &vs)
|
||||
{
|
||||
static char *prefix = "USERS::";
|
||||
|
||||
@ -23,10 +23,6 @@ DefReturnResult SClogerror(CmdSet&);
|
||||
DefReturnResult SCcastspell(CmdSet&);
|
||||
DefReturnResult SCqueryitem(CmdSet&);
|
||||
DefReturnResult SCtarget(CmdSet&);
|
||||
DefReturnResult SCloadscp(CmdSet&);
|
||||
DefReturnResult SCScpExists(CmdSet&);
|
||||
DefReturnResult SCScpSectionExists(CmdSet&);
|
||||
DefReturnResult SCScpEntryExists(CmdSet&);
|
||||
DefReturnResult SCGetScpValue(CmdSet&);
|
||||
DefReturnResult SCGetName(CmdSet&);
|
||||
DefReturnResult SCGetPlayerGuid(CmdSet&);
|
||||
@ -52,6 +48,8 @@ DefReturnResult SCGetObjectDistance(CmdSet&);
|
||||
DefReturnResult SCSwitchOpcodeHandler(CmdSet&);
|
||||
DefReturnResult SCOpcodeDisabled(CmdSet&);
|
||||
DefReturnResult SCSpoofWorldPacket(CmdSet&);
|
||||
DefReturnResult SCLoadDB(CmdSet &Set);
|
||||
DefReturnResult SCAddDBPath(CmdSet &Set);
|
||||
|
||||
|
||||
void my_print(const char *fmt, ...);
|
||||
|
||||
@ -47,11 +47,11 @@ void DrawObject::_Init(void)
|
||||
if(!cube && _obj->IsWorldObject()) // only world objects have coords and can be drawn
|
||||
{
|
||||
uint32 displayid = _obj->IsUnit() ? _obj->GetUInt32Value(UNIT_FIELD_DISPLAYID) : 0; // TODO: in case its GO get it from proto data
|
||||
SCPDatabase& cdi = _instance->dbmgr.GetDB("creaturedisplayinfo");
|
||||
SCPField& crdata = cdi.GetField(displayid);
|
||||
uint32 modelid = crdata.GetInteger("model");
|
||||
std::string modelfile = std::string("data/model/") + _instance->dbmgr.GetDB("creaturemodeldata").GetField(modelid).GetString("file");
|
||||
uint32 opacity = crdata.GetInteger("opacity");
|
||||
SCPDatabase *cdi = _instance->dbmgr.GetDB("creaturedisplayinfo");
|
||||
SCPDatabase *cmd = _instance->dbmgr.GetDB("creaturemodeldata");
|
||||
uint32 modelid = cdi && displayid ? cdi->GetUint32(displayid,"model") : 0;
|
||||
std::string modelfile = std::string("data/model/") + cmd->GetString(modelid,"file");
|
||||
uint32 opacity = cdi && displayid ? cdi->GetUint32(displayid,"opacity") : 255;
|
||||
scene::IAnimatedMesh *mesh = _smgr->getMesh(modelfile.c_str());
|
||||
if(mesh)
|
||||
{
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#define PSEUGUI_H
|
||||
|
||||
#include "irrlicht/irrlicht.h"
|
||||
#include "SceneData.h"
|
||||
#include "DrawObjMgr.h"
|
||||
#include "World.h"
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
Scene::Scene(PseuGUI *g)
|
||||
{
|
||||
memset(scenedata, 0, sizeof(uint32) * SCENEDATA_SIZE);
|
||||
textdb = NULL;
|
||||
gui = g;
|
||||
instance = gui->GetInstance();
|
||||
device = gui->_device;
|
||||
@ -45,3 +46,22 @@ Scene::~Scene()
|
||||
{
|
||||
DEBUG(logdebug("Scene::~Scene()"));
|
||||
}
|
||||
|
||||
core::stringw Scene::GetStringFromDB(u32 index, u32 entry)
|
||||
{
|
||||
core::stringw r = "";
|
||||
if(!textdb)
|
||||
{
|
||||
r += L"<string ";
|
||||
r += index;
|
||||
r += L"/";
|
||||
r += entry;
|
||||
r += L" not found>";
|
||||
return r;
|
||||
}
|
||||
char buf[20];
|
||||
sprintf(buf,"%u",entry);
|
||||
r += textdb->GetString(index, buf);
|
||||
textdb->DumpStructureToFile("DB_textdb.txt");
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ using namespace scene;
|
||||
using namespace video;
|
||||
using namespace io;
|
||||
using namespace gui;
|
||||
|
||||
|
||||
|
||||
class PseuGUI;
|
||||
class CCursorController;
|
||||
@ -22,6 +22,7 @@ class Scene
|
||||
public:
|
||||
Scene(PseuGUI *g);
|
||||
~Scene();
|
||||
core::stringw GetStringFromDB(u32 index, u32 entry);
|
||||
inline void SetState(SceneState sc) { _scenestate = sc; }
|
||||
inline SceneState GetState(void) { return _scenestate; }
|
||||
virtual void OnUpdate(s32);
|
||||
@ -40,6 +41,7 @@ protected:
|
||||
CCursorController *cursor;
|
||||
SceneState _scenestate;
|
||||
uint32 scenedata[SCENEDATA_SIZE]; // generic storage for anything the PseuInstance thread wants to tell us
|
||||
SCPDatabase *textdb;
|
||||
};
|
||||
|
||||
class SceneGuiStart : public Scene
|
||||
@ -51,22 +53,24 @@ private:
|
||||
IGUIImage *irrlogo, *driverlogo;
|
||||
|
||||
};
|
||||
|
||||
class GUIEventReceiver;
|
||||
|
||||
class SceneLogin : public Scene
|
||||
{
|
||||
public:
|
||||
SceneLogin(PseuGUI *gui);
|
||||
void OnUpdate(s32);
|
||||
void OnDelete(void);
|
||||
|
||||
private:
|
||||
gui::IGUIElement* root;
|
||||
IGUIImage *irrlogo, *background;
|
||||
GUIEventReceiver *eventrecv;
|
||||
PseuGUI* _gui;
|
||||
};
|
||||
class GUIEventReceiver;
|
||||
|
||||
class SceneLogin : public Scene
|
||||
{
|
||||
public:
|
||||
SceneLogin(PseuGUI *gui);
|
||||
void OnUpdate(s32);
|
||||
void OnDelete(void);
|
||||
|
||||
private:
|
||||
gui::IGUIElement* root;
|
||||
IGUIImage *irrlogo, *background;
|
||||
GUIEventReceiver *eventrecv;
|
||||
PseuGUI* _gui;
|
||||
gui::IGUIElement *msgbox;
|
||||
uint32 msgbox_textid;
|
||||
};
|
||||
|
||||
class ShTlTerrainSceneNode;
|
||||
class MCameraFPS;
|
||||
@ -119,4 +123,4 @@ private:
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -3,24 +3,31 @@
|
||||
|
||||
#define SCENEDATA_SIZE 255
|
||||
|
||||
// I: index
|
||||
// I: index, enums should start with 1
|
||||
// D: data value
|
||||
// dummy indexes are used to keep space for misc text data in an SCP file
|
||||
|
||||
enum SceneLoginDataIndexes
|
||||
{
|
||||
ISCENE_LOGIN_CONN_STATUS,
|
||||
ISCENE_LOGIN_END
|
||||
ISCENE_LOGIN_CONN_STATUS = 1,
|
||||
ISCENE_LOGIN_MSGBOX_DUMMY = 2,
|
||||
ISCENE_LOGIN_END = 3
|
||||
};
|
||||
|
||||
enum SceneLoginConnStatus
|
||||
{
|
||||
DSCENE_LOGIN_NOT_CONNECTED,
|
||||
DSCENE_LOGIN_CONN_ATTEMPT,
|
||||
DSCENE_LOGIN_CONN_FAILED,
|
||||
DSCENE_LOGIN_LOGGING_IN,
|
||||
DSCENE_LOGIN_AUTHENTICATING,
|
||||
DSCENE_LOGIN_AUTH_FAILED,
|
||||
DSCENE_LOGIN_AUTH_OK
|
||||
DSCENE_LOGIN_NOT_CONNECTED = 0,
|
||||
DSCENE_LOGIN_CONN_ATTEMPT = 1,
|
||||
DSCENE_LOGIN_CONN_FAILED = 2,
|
||||
DSCENE_LOGIN_ACC_NOT_FOUND = 3,
|
||||
DSCENE_LOGIN_ALREADY_CONNECTED = 4,
|
||||
DSCENE_LOGIN_WRONG_VERSION = 5,
|
||||
DSCENE_LOGIN_LOGGING_IN = 6,
|
||||
DSCENE_LOGIN_AUTHENTICATING = 7,
|
||||
DSCENE_LOGIN_AUTH_FAILED = 8,
|
||||
DSCENE_LOGIN_REQ_REALM = 9,
|
||||
DSCENE_LOGIN_UNK_ERROR = 10,
|
||||
DSCENE_LOGIN_FILE_TRANSFER = 11,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -48,7 +48,8 @@ public:
|
||||
|
||||
SceneLogin::SceneLogin(PseuGUI *gui) : Scene(gui)
|
||||
{
|
||||
_gui=gui;
|
||||
textdb = instance->dbmgr.GetDB("gui_login_text");
|
||||
msgbox_textid = 0;
|
||||
eventrecv = new GUIEventReceiver();
|
||||
device->setEventReceiver(eventrecv);
|
||||
root = guienv->getRootGUIElement();
|
||||
@ -67,17 +68,21 @@ SceneLogin::SceneLogin(PseuGUI *gui) : Scene(gui)
|
||||
guienv->addButton(rect<s32>(scrn.Width-120, scrn.Height-40, scrn.Width-10, scrn.Height-10), 0, 4, L"Quit");
|
||||
guienv->addButton(rect<s32>(10, scrn.Height-40, 120, scrn.Height-10), 0, 8, L"Community Site");
|
||||
guienv->addButton(rect<s32>((scrn.Width*0.5f)-60, (scrn.Height*0.3f)+100, (scrn.Width*0.5f)+60, (scrn.Height*0.3f)+130), 0, 16, L"Logon");
|
||||
|
||||
msgbox = guienv->addStaticText(GetStringFromDB(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_NOT_CONNECTED).c_str(),rect<s32>((scrn.Width*0.5f)-90, (scrn.Height*0.3f)+150, (scrn.Width*0.5f)+90, (scrn.Height*0.3f)+180),true,true);
|
||||
|
||||
}
|
||||
|
||||
void SceneLogin::OnUpdate(s32 timepassed)
|
||||
{
|
||||
if(msgbox_textid != scenedata[ISCENE_LOGIN_CONN_STATUS])
|
||||
{
|
||||
msgbox_textid = scenedata[ISCENE_LOGIN_CONN_STATUS];
|
||||
msgbox->setText(GetStringFromDB(ISCENE_LOGIN_CONN_STATUS,msgbox_textid).c_str());
|
||||
}
|
||||
|
||||
if(eventrecv->buttons & BUTTON_QUIT)
|
||||
{
|
||||
_gui->Shutdown();
|
||||
_gui->GetInstance()->Stop();
|
||||
eventrecv->buttons=0;
|
||||
instance->Stop();
|
||||
}
|
||||
if(eventrecv->buttons & BUTTON_LOGON)
|
||||
{
|
||||
@ -90,18 +95,21 @@ void SceneLogin::OnUpdate(s32 timepassed)
|
||||
std::string accpass=tmp.c_str();
|
||||
if(accname.size() && accpass.size())
|
||||
{
|
||||
SetData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_CONN_ATTEMPT);
|
||||
logdebug("Trying to set Logon Data %u, %u", accname.size(), accpass.size());
|
||||
// we can safely override the conf settings
|
||||
_gui->GetInstance()->GetConf()->accname = accname;
|
||||
_gui->GetInstance()->GetConf()->accpass = accpass;
|
||||
_gui->GetInstance()->CreateRealmSession();
|
||||
instance->GetConf()->accname = accname;
|
||||
instance->GetConf()->accpass = accpass;
|
||||
instance->CreateRealmSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
guienv->addMessageBox(L"Oh noes!", L"You have to enter account name and password!");
|
||||
guienv->addMessageBox(GetStringFromDB(ISCENE_LOGIN_MSGBOX_DUMMY,0).c_str(),
|
||||
GetStringFromDB(ISCENE_LOGIN_MSGBOX_DUMMY,1).c_str());
|
||||
}
|
||||
|
||||
}
|
||||
eventrecv->buttons = 0;
|
||||
}
|
||||
|
||||
void SceneLogin::OnDelete(void)
|
||||
|
||||
@ -117,6 +117,9 @@ bool PseuInstance::Init(void)
|
||||
|
||||
_scp->SetPath(_scpdir);
|
||||
|
||||
dbmgr.AddSearchPath("./cache");
|
||||
dbmgr.AddSearchPath("./data/scp");
|
||||
|
||||
_scp->variables.Set("@version_short",_ver_short);
|
||||
_scp->variables.Set("@version",_ver);
|
||||
_scp->variables.Set("@inworld","false");
|
||||
@ -412,9 +415,9 @@ bool PseuInstance::ConnectToRealm(void)
|
||||
_rsession = new RealmSession(this);
|
||||
_rsession->SetLogonData(); // get accname & accpass from PseuInstanceConfig and set it in the realm session
|
||||
_rsession->Connect();
|
||||
if(_rsession->MustDie()) // something failed. it will be deleted in next Update() call
|
||||
if(_rsession->MustDie() || !_rsession->SocketGood()) // something failed. it will be deleted in next Update() call
|
||||
{
|
||||
logerror("Connecting to Realm failed!");
|
||||
logerror("PseuInstance: Connecting to Realm failed!");
|
||||
if(_gui)
|
||||
_gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_CONN_FAILED);
|
||||
return false;
|
||||
|
||||
@ -315,6 +315,8 @@ void RealmSession::SendLogonChallenge(void)
|
||||
GetInstance()->SetError();
|
||||
return;
|
||||
}
|
||||
if(PseuGUI *gui = GetInstance()->GetGUI())
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_LOGGING_IN);
|
||||
std::string acc = stringToUpper(_accname);
|
||||
ByteBuffer packet;
|
||||
packet << (uint8)AUTH_LOGON_CHALLENGE;
|
||||
@ -338,11 +340,14 @@ void RealmSession::SendLogonChallenge(void)
|
||||
|
||||
void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
|
||||
{
|
||||
PseuGUI *gui = GetInstance()->GetGUI();
|
||||
logdebug("RealmSocket: Got AUTH_LOGON_CHALLENGE [%u of %u bytes]",pkt.size(),sizeof(sAuthLogonChallenge_S));
|
||||
if(pkt.size() < 3)
|
||||
{
|
||||
logerror("AUTH_LOGON_CHALLENGE: Recieved incorrect/unknown packet. Hexdump:");
|
||||
DumpInvalidPacket(pkt);
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_UNK_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -353,18 +358,25 @@ void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
|
||||
{
|
||||
case 4:
|
||||
logerror("Realm Server did not find account \"%s\"!",_accname.c_str());
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_ACC_NOT_FOUND);
|
||||
break;
|
||||
case 6:
|
||||
logerror("Account \"%s\" is already logged in!",_accname.c_str());
|
||||
// TODO: wait a certain amount of time before reconnecting? conf option?
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_ALREADY_CONNECTED);
|
||||
break;
|
||||
case 9:
|
||||
logerror("Realm Server doesn't accept this version!");
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_WRONG_VERSION);
|
||||
break;
|
||||
case 0:
|
||||
{
|
||||
pkt.read((uint8*)&lc, sizeof(sAuthLogonChallenge_S));
|
||||
logdetail("Login successful, now calculating proof packet...");
|
||||
if(PseuGUI *gui = GetInstance()->GetGUI())
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_AUTHENTICATING);
|
||||
|
||||
// now lets start calculating
|
||||
BigNumber N,A,B,a,u,x,v,S,salt,unk1,g,k(3); // init BNs, default k to 3
|
||||
@ -501,12 +513,15 @@ void RealmSession::_HandleLogonChallenge(ByteBuffer& pkt)
|
||||
|
||||
void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
|
||||
{
|
||||
PseuGUI *gui = GetInstance()->GetGUI();
|
||||
logdebug("RealmSocket: Got AUTH_LOGON_PROOF [%u of %u bytes]",pkt.size(),26);
|
||||
if(pkt.size() < 2)
|
||||
{
|
||||
logerror("AUTH_LOGON_PROOF: Recieved incorrect/unknown packet. Hexdump:");
|
||||
DumpInvalidPacket(pkt);
|
||||
DieOrReconnect(true);
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_UNK_ERROR);
|
||||
return;
|
||||
}
|
||||
uint8 error = pkt[1];
|
||||
@ -516,11 +531,15 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
|
||||
{
|
||||
case REALM_AUTH_UPDATE_CLIENT:
|
||||
log("The realm server requested client update.");
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_WRONG_VERSION);
|
||||
DieOrReconnect(true);
|
||||
return;
|
||||
|
||||
case REALM_AUTH_NO_MATCH:
|
||||
case REALM_AUTH_UNKNOWN2:
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_AUTH_FAILED);
|
||||
logerror("Wrong password or invalid account information or authentication error");
|
||||
DieOrReconnect(true);
|
||||
return;
|
||||
@ -530,6 +549,8 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
|
||||
if(error != REALM_AUTH_SUCCESS)
|
||||
{
|
||||
logerror("AUTH_LOGON_PROOF: unk error = 0x%X",error);
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_UNK_ERROR);
|
||||
pkt.rpos(2);
|
||||
DieOrReconnect(true);
|
||||
return;
|
||||
@ -542,6 +563,8 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
|
||||
//printchex((char*)&lp, sizeof(sAuthLogonProof_S),true);
|
||||
if(!memcmp(lp.M2,this->_m2,20))
|
||||
{
|
||||
if(PseuGUI *gui = GetInstance()->GetGUI())
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS, DSCENE_LOGIN_REQ_REALM);
|
||||
// auth successful
|
||||
ByteBuffer packet;
|
||||
packet << (uint8)REALM_LIST;
|
||||
@ -553,7 +576,8 @@ void RealmSession::_HandleLogonProof(ByteBuffer& pkt)
|
||||
logcritical("Auth failed, M2 differs!");
|
||||
printf("My M2 :"); printchex((char*)_m2,20,true);
|
||||
printf("Srv M2:"); printchex((char*)lp.M2,20,true);
|
||||
|
||||
if(gui)
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_AUTH_FAILED);
|
||||
DieOrReconnect(true);
|
||||
}
|
||||
}
|
||||
@ -576,6 +600,8 @@ void RealmSession::_HandleTransferInit(ByteBuffer& pkt)
|
||||
pkt >> _file_size;
|
||||
pkt.read(_file_md5,MD5_DIGEST_LENGTH);
|
||||
logcustom(0,GREEN,"TransferInit [%s]: File size: "I64FMTD" KB (MD5: %s)", (char*)type_str, _file_size / 1024L, toHexDump(&_file_md5[0],MD5_DIGEST_LENGTH,false).c_str());
|
||||
if(PseuGUI *gui = GetInstance()->GetGUI())
|
||||
gui->SetSceneData(ISCENE_LOGIN_CONN_STATUS,DSCENE_LOGIN_FILE_TRANSFER);
|
||||
delete [] type_str;
|
||||
ByteBuffer bb(1);
|
||||
bb << uint8(XFER_ACCEPT);
|
||||
@ -708,3 +734,8 @@ void RealmSession::DieOrReconnect(bool err)
|
||||
GetInstance()->SetError();
|
||||
}
|
||||
}
|
||||
|
||||
bool RealmSession::SocketGood(void)
|
||||
{
|
||||
return _socket && _socket->IsOk();
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ public:
|
||||
void SendLogonChallenge(void);
|
||||
bool MustDie(void);
|
||||
void SetMustDie(void);
|
||||
|
||||
bool SocketGood(void);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
@ -1,210 +1,839 @@
|
||||
#include <fstream>
|
||||
#include "common.h"
|
||||
#include "Auth/MD5Hash.h"
|
||||
#include "SCPDatabase.h"
|
||||
|
||||
#define HEADER_SIZE (21*sizeof(uint32))
|
||||
|
||||
uint32 SCPDatabase::LoadFromFile(char *fn)
|
||||
inline char *gettypename(uint32 ty)
|
||||
{
|
||||
std::fstream fh;
|
||||
uint32 size = GetFileSize(fn);
|
||||
if(!size)
|
||||
return 0;
|
||||
|
||||
fh.open(fn,std::ios_base::in | std::ios_base::binary);
|
||||
if( !fh.is_open() )
|
||||
return 0;
|
||||
|
||||
char *buf = new char[size];
|
||||
fh.read(buf,size);
|
||||
fh.close();
|
||||
|
||||
uint32 sections = LoadFromMem(buf,size);
|
||||
delete [] buf;
|
||||
return sections;
|
||||
return (ty==0 ? "INT" : (ty==1 ? "FLOAT" : "STRING"));
|
||||
}
|
||||
|
||||
|
||||
uint32 SCPDatabase::LoadFromMem(char *buf, uint32 size)
|
||||
// file-globally declared pointer holder. NOT multi-instance-safe for now!!
|
||||
struct memblock
|
||||
{
|
||||
std::string line,value,entry,storage;
|
||||
uint32 id=0,sections=0;
|
||||
memblock() : ptr(NULL), size(0) {}
|
||||
memblock(uint8 *p, uint32 s) : size(s), ptr(p) {}
|
||||
~memblock() { if(ptr) delete [] ptr; }
|
||||
uint8 *ptr;
|
||||
uint32 size;
|
||||
};
|
||||
TypeStorage<memblock> Pointers;
|
||||
|
||||
for(uint32 pos = 0; pos < size; pos++)
|
||||
{
|
||||
if(buf[pos] == '\n')
|
||||
{
|
||||
if(line.empty())
|
||||
continue;
|
||||
while(line[0]==' ' || line[0]=='\t')
|
||||
line.erase(0,1);
|
||||
if(line.empty() || (line.length() > 1 && (line[0]=='#' || (line[0]=='/' && line[1]=='/'))) )
|
||||
{
|
||||
line.clear();
|
||||
continue;
|
||||
}
|
||||
if(line[line.size()-1] == 13 || line[line.size()-1] == 10) // this fixes the annoying newline problems on windows + binary mode
|
||||
line[line.size()-1] = 0;
|
||||
if(line[0]=='[')
|
||||
{
|
||||
id=(uint32)toInt(line.c_str()+1); // start reading after '['
|
||||
sections++;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 pos=line.find("=");
|
||||
if(pos!=std::string::npos)
|
||||
{
|
||||
entry=stringToLower(line.substr(0,pos));
|
||||
value=line.substr(pos+1,line.length()-1);
|
||||
_map[id].Set(entry,value);
|
||||
}
|
||||
// else invalid line, must have '='
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
else
|
||||
line += buf[pos]; // fill up line until a newline is reached (see above)
|
||||
}
|
||||
return sections;
|
||||
SCPDatabase::~SCPDatabase()
|
||||
{
|
||||
DEBUG(logdebug("Deleting SCPDatabase '%s'",_name.c_str()));
|
||||
DropAll();
|
||||
}
|
||||
|
||||
bool SCPDatabase::HasField(uint32 id)
|
||||
SCPDatabase::SCPDatabase()
|
||||
{
|
||||
for(SCPFieldMap::iterator i = _map.begin(); i != _map.end(); i++)
|
||||
if(i->first == id)
|
||||
return true;
|
||||
return false;
|
||||
_stringbuf = NULL;
|
||||
_intbuf = NULL;
|
||||
_compact = false;
|
||||
}
|
||||
|
||||
bool SCPField::HasEntry(std::string e)
|
||||
void SCPDatabase::DropAll(void)
|
||||
{
|
||||
for(SCPEntryMap::iterator i = _map.begin(); i != _map.end(); i++)
|
||||
{
|
||||
std::string ch = i->first;
|
||||
if(ch == e)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
DropTextData();
|
||||
if(_stringbuf)
|
||||
delete [] _stringbuf;
|
||||
if(_intbuf)
|
||||
delete [] _intbuf;
|
||||
_indexes.clear();
|
||||
_indexes_reverse.clear();
|
||||
_fielddefs.clear();
|
||||
_stringbuf = NULL;
|
||||
_intbuf = NULL;
|
||||
_stringsize = 0;
|
||||
_compact = false;
|
||||
}
|
||||
|
||||
std::string SCPField::GetString(std::string entry)
|
||||
void SCPDatabase::DropTextData(void)
|
||||
{
|
||||
//return HasEntry(entry) ? _map[entry] : "";
|
||||
return _map[entry];
|
||||
DEBUG(logdebug("Dropping plaintext parts of DB '%s'",_name.c_str()));
|
||||
for(SCPSourceList::iterator it = sources.begin(); it != sources.end(); it++)
|
||||
Pointers.Delete(*it);
|
||||
sources.clear();
|
||||
fields.clear();
|
||||
}
|
||||
|
||||
// note that this can take a while depending on the size of the database!
|
||||
uint32 SCPDatabase::GetFieldByValue(std::string entry, std::string value)
|
||||
void *SCPDatabase::GetPtr(uint32 index, char *entry)
|
||||
{
|
||||
for(SCPFieldMap::iterator fm = _map.begin(); fm != _map.end(); fm++)
|
||||
if(fm->second.HasEntry(entry) && fm->second.GetString(entry)==value)
|
||||
return fm->first;
|
||||
return uint32(-1);
|
||||
std::map<uint32,uint32>::iterator it = _indexes.find(index);
|
||||
if(it == _indexes.end())
|
||||
return NULL;
|
||||
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
|
||||
if(fi == _fielddefs.end())
|
||||
return NULL;
|
||||
|
||||
uint32 target_row = _indexes[index];
|
||||
uint32 field_id = _fielddefs[entry].id;
|
||||
return (void*)&_intbuf[(_fields_per_row * target_row) + field_id];
|
||||
}
|
||||
|
||||
bool SCPDatabaseMgr::HasDB(std::string n)
|
||||
void *SCPDatabase::GetPtrByField(uint32 index, uint32 entry)
|
||||
{
|
||||
for(SCPDatabaseMap::iterator i = _map.begin(); i != _map.end(); i++)
|
||||
if(i->first == n)
|
||||
return true;
|
||||
return false;
|
||||
std::map<uint32,uint32>::iterator it = _indexes.find(index);
|
||||
if(it == _indexes.end())
|
||||
return NULL;
|
||||
|
||||
uint32 target_row = _indexes[index];
|
||||
return (void*)&_intbuf[(_fields_per_row * target_row) + entry];
|
||||
}
|
||||
|
||||
SCPDatabase& SCPDatabaseMgr::GetDB(std::string n)
|
||||
uint32 SCPDatabase::GetFieldByUint32Value(char *entry, uint32 val)
|
||||
{
|
||||
return _map[n];
|
||||
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
|
||||
if(fi == _fielddefs.end())
|
||||
return SCP_INVALID_INT;
|
||||
|
||||
uint32 field_id = _fielddefs[entry].id;
|
||||
return GetFieldByUint32Value(field_id,val);
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldByUint32Value(uint32 entry, uint32 val)
|
||||
{
|
||||
for(uint32 row = 0; row < _rowcount; row++)
|
||||
if(_intbuf[row * _fields_per_row + entry] == val)
|
||||
return _indexes_reverse[row];
|
||||
return SCP_INVALID_INT;
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldByIntValue(char *entry, int32 val)
|
||||
{
|
||||
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
|
||||
if(fi == _fielddefs.end())
|
||||
return SCP_INVALID_INT;
|
||||
|
||||
uint32 field_id = _fielddefs[entry].id;
|
||||
return GetFieldByIntValue(field_id,val);
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldByIntValue(uint32 entry, int32 val)
|
||||
{
|
||||
for(uint32 row = 0; row < _rowcount; row++)
|
||||
if((int)_intbuf[row * _fields_per_row + entry] == val)
|
||||
return _indexes_reverse[row];
|
||||
return (int)SCP_INVALID_INT;
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldByStringValue(char *entry, char *val)
|
||||
{
|
||||
std::map<std::string,SCPFieldDef>::iterator fi = _fielddefs.find(entry);
|
||||
if(fi == _fielddefs.end())
|
||||
return SCP_INVALID_INT;
|
||||
|
||||
uint32 field_id = _fielddefs[entry].id;
|
||||
return GetFieldByStringValue(field_id,val);
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldByStringValue(uint32 entry, char *val)
|
||||
{
|
||||
for(uint32 row = 0; row < _rowcount; row++)
|
||||
if(!stricmp(GetStringByOffset(_intbuf[row * _fields_per_row + entry]), val))
|
||||
return row;
|
||||
return SCP_INVALID_INT;
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldType(char *entry)
|
||||
{
|
||||
std::map<std::string,SCPFieldDef>::iterator it = _fielddefs.find(entry);
|
||||
if(it != _fielddefs.end())
|
||||
return _fielddefs[entry].type;
|
||||
return SCP_INVALID_INT;
|
||||
}
|
||||
|
||||
uint32 SCPDatabase::GetFieldId(char *entry)
|
||||
{
|
||||
std::map<std::string,SCPFieldDef>::iterator it = _fielddefs.find(entry);
|
||||
if(it != _fielddefs.end())
|
||||
return _fielddefs[entry].id;
|
||||
return SCP_INVALID_INT;
|
||||
}
|
||||
|
||||
SCPDatabase *SCPDatabaseMgr::GetDB(std::string n, bool create)
|
||||
{
|
||||
return create ? _map.Get(n) : _map.GetNoCreate(n);
|
||||
}
|
||||
|
||||
uint32 SCPDatabaseMgr::AutoLoadFile(char *fn)
|
||||
{
|
||||
std::fstream fh;
|
||||
uint32 size = GetFileSize(fn);
|
||||
if(!size)
|
||||
return 0;
|
||||
char *buf;
|
||||
uint32 size;
|
||||
|
||||
fh.open(fn,std::ios_base::in | std::ios_base::binary);
|
||||
if( !fh.is_open() )
|
||||
return 0;
|
||||
// check if file was loaded before; use memory data if this is the case
|
||||
if(memblock *mb = Pointers.GetNoCreate(fn))
|
||||
{
|
||||
size = mb->size;
|
||||
buf = (char*)mb->ptr;
|
||||
}
|
||||
else // and if not, read the file from disk
|
||||
{
|
||||
std::fstream fh;
|
||||
size = GetFileSize(fn);
|
||||
if(!size)
|
||||
return 0;
|
||||
|
||||
char *buf = new char[size];
|
||||
fh.open(fn,std::ios_base::in | std::ios_base::binary);
|
||||
if( !fh.is_open() )
|
||||
return 0;
|
||||
|
||||
fh.read(buf,size);
|
||||
fh.close();
|
||||
buf = new char[size];
|
||||
|
||||
std::string line,dbname;
|
||||
fh.read(buf,size);
|
||||
fh.close();
|
||||
|
||||
// store the loaded file buffer so we can reuse it later if necessary
|
||||
Pointers.Assign(fn, new memblock((uint8*)buf,size));
|
||||
}
|
||||
|
||||
std::string line,dbname,entry,value;
|
||||
SCPDatabase *db = NULL;
|
||||
uint32 id = 0, sections = 0;
|
||||
for(uint32 pos = 0; pos < size; pos++)
|
||||
{
|
||||
if(buf[pos] == '\n')
|
||||
if(buf[pos] == '\n' || buf[pos] == 10 || buf[pos] == 13)
|
||||
{
|
||||
if(line.empty())
|
||||
continue;
|
||||
while(line[0]==' ' || line[0]=='\t')
|
||||
while(line.size() && (line[0]==' ' || line[0]=='\t'))
|
||||
line.erase(0,1);
|
||||
if(line[0] == '#')
|
||||
uint32 eq = line.find("=");
|
||||
if(eq != std::string::npos)
|
||||
{
|
||||
uint32 eq = line.find("=");
|
||||
if(eq != std::string::npos)
|
||||
entry=stringToLower(line.substr(0,eq));
|
||||
value=line.substr(eq+1,line.length()-1);
|
||||
|
||||
if(!stricmp(entry.c_str(),"#dbname") && value.size())
|
||||
{
|
||||
std::string info = stringToLower(line.substr(0,pos));
|
||||
std::string value = stringToLower(line.substr(pos+1,line.length()-1));
|
||||
if(info == "#dbname")
|
||||
{
|
||||
dbname = value;
|
||||
break;
|
||||
}
|
||||
dbname = value;
|
||||
db = GetDB(dbname,true); // create db if not existing
|
||||
}
|
||||
else if(db)
|
||||
db->fields[id][entry] = value;
|
||||
}
|
||||
else if(line[0]=='[')
|
||||
{
|
||||
id=(uint32)toInt(line.c_str()+1); // start reading after '['
|
||||
sections++;
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
else
|
||||
line += buf[pos];
|
||||
}
|
||||
delete [] buf;
|
||||
|
||||
if(dbname.empty())
|
||||
return 0;
|
||||
|
||||
uint32 sections = GetDB(dbname).LoadFromMem(buf,size);
|
||||
db->sources.insert(fn);
|
||||
return sections;
|
||||
|
||||
}
|
||||
|
||||
// -- helper functions -- //
|
||||
////////////////////////////////////////////////////////
|
||||
// SCP compiler
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
std::string SCPDatabaseMgr::GetZoneName(uint32 id)
|
||||
|
||||
// check the datatype that will be used for this string value
|
||||
uint32 SCPDatabaseMgr::GetDataTypeFromString(char *s)
|
||||
{
|
||||
return GetDB("zone").GetField(id).GetString("name");
|
||||
bool isint = true, first = true;
|
||||
for(;*s;s++) // check every char until \0 is reached
|
||||
{
|
||||
if(!isdigit(*s) && !(first && (*s=='+' || *s=='-'))) // if not in 0-9 or first beeing "+" or "-" it cant be int...
|
||||
{
|
||||
isint = false;
|
||||
if(*s != '.') // and if the char beeing not int isnt a dot (3.1415), it cant be float either
|
||||
return SCP_TYPE_STRING;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
return isint ? SCP_TYPE_INT : SCP_TYPE_FLOAT;
|
||||
}
|
||||
|
||||
std::string SCPDatabaseMgr::GetRaceName(uint32 id)
|
||||
bool SCPDatabaseMgr::Compact(char *dbname, char *outfile)
|
||||
{
|
||||
std::string r = GetDB("race").GetField(id).GetString("name");
|
||||
//if(r.empty())
|
||||
// r = raceName[id];
|
||||
return r;
|
||||
logdebug("Compacting database '%s' into file '%s'", dbname, outfile);
|
||||
SCPDatabase *db = GetDB(dbname);
|
||||
if(!db || db->fields.empty() || db->sources.empty())
|
||||
{
|
||||
logerror("Compact(\"%s\",\"%s\") failed, DB doesn't exist or is empty",dbname,outfile);
|
||||
return false;
|
||||
}
|
||||
std::map<std::string, SCPFieldDef> fieldIdMap;
|
||||
std::map<uint32,uint32> idToSectionMap;
|
||||
ByteBuffer stringdata(5000);
|
||||
uint32 cur_idx, pass = 0;
|
||||
std::string line;
|
||||
uint32 section=0;
|
||||
uint32 *membuf = NULL; // stores ints, floats and string offsets from ByteBuffer stringdata
|
||||
uint32 blocksize;
|
||||
std::string entry,value;
|
||||
uint32 field_id;
|
||||
|
||||
// some preparations
|
||||
uint32 nStrings = 0, nRows = db->fields.size(), nFields; // pre-declared
|
||||
stringdata << ""; // write an empty string so that offset 0 will always return empty string
|
||||
|
||||
// the whole process is divided into 2 passes:
|
||||
// - pass 0 autodetects the datatypes stored in the dirfferent entries (SCPFieldDef)
|
||||
// - pass 1 creates the data field holding the values and string offsets, and also stuffs string values into the ByteBuffer
|
||||
while(pass < 2)
|
||||
{
|
||||
cur_idx = 1; // stores the current index of the last entry added. index 0 is always field_id!
|
||||
section = 0; // stores how many sections are done already
|
||||
for(SCPFieldMap::iterator fmit = db->fields.begin(); fmit != db->fields.end(); fmit++)
|
||||
{
|
||||
SCPEntryMap& emap = fmit->second;
|
||||
field_id = fmit->first; // stores the field id (e.g. "[154]" in the scp file)
|
||||
idToSectionMap[field_id] = section; // since not every field id exists, store field id and its section number
|
||||
// for faster lookup, less overhead, and smaller files
|
||||
|
||||
// write the field id into row position 0 of the data field
|
||||
if(pass == 1)
|
||||
{
|
||||
ASSERT(membuf != NULL);
|
||||
uint32 pos = section * nFields;
|
||||
ASSERT(pos < blocksize);
|
||||
membuf[pos] = field_id;
|
||||
}
|
||||
|
||||
for(SCPEntryMap::iterator eit = emap.begin(); eit != emap.end(); eit++)
|
||||
{
|
||||
// "entry=value" in the scp file
|
||||
entry = eit->first;
|
||||
value = eit->second;
|
||||
|
||||
// pass 0 - autodetect field types
|
||||
if(pass == 0)
|
||||
{
|
||||
if(fieldIdMap.find(entry) == fieldIdMap.end())
|
||||
{
|
||||
SCPFieldDef d;
|
||||
d.id = cur_idx++;
|
||||
d.type = GetDataTypeFromString((char*)value.c_str());
|
||||
fieldIdMap[entry] = d;
|
||||
DEBUG(logdebug("Found new key: '%s' id: %u type: %s", entry.c_str(), d.id, gettypename(d.type) ));
|
||||
}
|
||||
else
|
||||
{
|
||||
SCPFieldDef& d = fieldIdMap[entry];
|
||||
uint32 _oldtype = d.type;
|
||||
// int can be changed to float or string, float only to string. changing back not allowed.
|
||||
if(d.type == SCP_TYPE_INT)
|
||||
{
|
||||
d.type = GetDataTypeFromString((char*)value.c_str());
|
||||
}
|
||||
else if(d.type == SCP_TYPE_FLOAT)
|
||||
{
|
||||
uint32 newtype = GetDataTypeFromString((char*)value.c_str());
|
||||
if(newtype == SCP_TYPE_STRING)
|
||||
d.type = newtype;
|
||||
}
|
||||
|
||||
if(_oldtype != d.type)
|
||||
{
|
||||
logdebug("Key '%s' id %u changed from %s to %s (field_id: %u)", entry.c_str(), d.id, gettypename(_oldtype), gettypename(d.type),field_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
// pass 1 - create the data field
|
||||
else if(pass == 1)
|
||||
{
|
||||
ASSERT(membuf != NULL);
|
||||
SCPFieldDef d = fieldIdMap[entry];
|
||||
uint32 pos = (section * nFields) + d.id;
|
||||
ASSERT(pos < blocksize);
|
||||
if(d.type == SCP_TYPE_STRING)
|
||||
{
|
||||
membuf[pos] = stringdata.wpos();
|
||||
stringdata << value;
|
||||
nStrings++;
|
||||
}
|
||||
else if(d.type == SCP_TYPE_INT)
|
||||
{
|
||||
membuf[pos] = atoi(value.c_str());
|
||||
}
|
||||
else if(d.type == SCP_TYPE_FLOAT)
|
||||
{
|
||||
((float*)membuf)[pos] = atof(value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
section++;
|
||||
}
|
||||
// if the first pass is done, allocate the data field for the second pass
|
||||
if(!pass)
|
||||
{
|
||||
nFields = fieldIdMap.size() + 1; // add the one field used for the field ID
|
||||
ASSERT(section == nRows);
|
||||
blocksize = nRows * nFields; // +1 because we store the field id here also
|
||||
DEBUG(logdebug("SCP: allocating %u*%u = %u integers (%u bytes)",nRows,fieldIdMap.size()+1,blocksize, blocksize*sizeof(uint32)));
|
||||
membuf = new uint32[blocksize];
|
||||
memset(membuf, 0, blocksize * sizeof(uint32));
|
||||
}
|
||||
pass++;
|
||||
}
|
||||
|
||||
// used header fields, some are pre-declared above, if needed
|
||||
uint32 offsMD5, nMD5, sizeMD5;
|
||||
uint32 offsIndexes, nIndexes, sizeIndexes;
|
||||
uint32 offsFields, sizeFields;
|
||||
uint32 offsData;
|
||||
uint32 offsStrings, sizeStrings;
|
||||
|
||||
// MD5 hashes of source files
|
||||
SCPSourceList& src = db->sources;
|
||||
ByteBuffer md5buf;
|
||||
nMD5 = 0;
|
||||
for(SCPSourceList::iterator it = src.begin(); it != src.end(); it++)
|
||||
{
|
||||
memblock *mb = Pointers.GetNoCreate(*it);
|
||||
if(!mb)
|
||||
{
|
||||
// if we reach this point there was really some big f*** up
|
||||
logerror("SCP Compact: memblock for file '%s' doesn't exist",it->c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
MD5Hash md5;
|
||||
md5.Update(mb->ptr,mb->size);
|
||||
md5.Finalize();
|
||||
md5buf << *it;
|
||||
md5buf.append(md5.GetDigest(),md5.GetLength());
|
||||
nMD5++;
|
||||
}
|
||||
sizeMD5 = md5buf.size();
|
||||
|
||||
// field types, sorted by IDs
|
||||
ByteBuffer fieldbuf;
|
||||
// put the entries and their type into ByteBuffer, sorted by their position in the membuf rows
|
||||
// note that the first field in the data row is always the field id, so the values start from 1
|
||||
for(std::map<std::string,SCPFieldDef>::iterator itf = fieldIdMap.begin(); itf != fieldIdMap.end(); itf++)
|
||||
{
|
||||
fieldbuf << itf->first << itf->second.id << itf->second.type; // entry name, id, type.
|
||||
}
|
||||
sizeFields = fieldbuf.size();
|
||||
|
||||
// index -> ID lookup table, e.g. data with ID 500 will have field index 214, because some IDs in between are missing
|
||||
// it *could* be calculated at load-time from the existing data field, but this way is faster when random-accessing the file itself
|
||||
// (what we dont do anyway, for now)
|
||||
ByteBuffer indexbuf;
|
||||
for(std::map<uint32,uint32>::iterator itx = idToSectionMap.begin(); itx != idToSectionMap.end(); itx++)
|
||||
{
|
||||
indexbuf << itx->first << itx->second; // field id; row number
|
||||
}
|
||||
nIndexes = idToSectionMap.size();
|
||||
sizeIndexes = indexbuf.size();
|
||||
|
||||
// string data
|
||||
// -- most of it is handled somewhere above
|
||||
sizeStrings = stringdata.size();
|
||||
|
||||
// precalc absolute offsets, header is 18 bytes large
|
||||
offsMD5 = HEADER_SIZE;
|
||||
offsIndexes = offsMD5 + sizeMD5;
|
||||
offsFields = offsIndexes + sizeIndexes;
|
||||
offsData = offsFields + sizeFields;
|
||||
offsStrings = offsData + blocksize * sizeof(uint32);
|
||||
|
||||
// buffer the file header
|
||||
ByteBuffer hbuf;
|
||||
hbuf.append("SCPC",4); // identifier
|
||||
hbuf << (uint32)0; // flags, not yet used
|
||||
hbuf << (uint32)0 << (uint32)0 << (uint32)0 << (uint32)0; // padding, not yet used
|
||||
hbuf << offsMD5 << nMD5 << sizeMD5;
|
||||
hbuf << offsIndexes << nIndexes << sizeIndexes;
|
||||
hbuf << offsFields << nFields << sizeFields;
|
||||
hbuf << offsData << section << blocksize * sizeof(uint32);
|
||||
hbuf << offsStrings << nStrings << sizeStrings;
|
||||
|
||||
FILE *fh = fopen(outfile,"wb");
|
||||
if(!fh)
|
||||
return false;
|
||||
if(fh)
|
||||
{
|
||||
fwrite(hbuf.contents(),hbuf.size(),1,fh);
|
||||
if(sizeMD5) // just in case no md5sums are stored
|
||||
fwrite(md5buf.contents(),sizeMD5,1,fh);
|
||||
fwrite(indexbuf.contents(),sizeIndexes,1,fh);
|
||||
fwrite(fieldbuf.contents(),sizeFields,1,fh);
|
||||
fwrite(membuf,sizeof(uint32),blocksize,fh);
|
||||
fwrite(stringdata.contents(),sizeStrings,1,fh);
|
||||
}
|
||||
fclose(fh);
|
||||
|
||||
if(!db)
|
||||
db = GetDB(dbname,true); // create if not exist
|
||||
|
||||
db->_compact = true;
|
||||
db->_name = dbname;
|
||||
|
||||
// drop all data no longer needed if the database is compacted
|
||||
db->DropTextData();
|
||||
|
||||
// we keep the membuf, since the compiled data are now usable as if loaded directly from a file
|
||||
// associate it with the buffers used by the db accessing functions
|
||||
db->_stringbuf = new char[sizeStrings];
|
||||
db->_stringsize = sizeStrings;
|
||||
memcpy(db->_stringbuf,stringdata.contents(),sizeStrings);
|
||||
db->_intbuf = membuf; // <<-- do NOT drop the membuf, its still used and will be deleted with ~SCPDatabase()!!
|
||||
db->_fields_per_row = nFields;
|
||||
db->_rowcount = nRows;
|
||||
db->_indexes = idToSectionMap;
|
||||
db->_fielddefs = fieldIdMap;
|
||||
for(std::map<uint32,uint32>::iterator it = idToSectionMap.begin(); it != idToSectionMap.end(); it++)
|
||||
db->_indexes_reverse[it->second] = it->first;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SCPDatabaseMgr::GetMapName(uint32 id)
|
||||
uint32 SCPDatabaseMgr::SearchAndLoad(char *dbname, bool no_compiled)
|
||||
{
|
||||
return GetDB("map").GetField(id).GetString("name");
|
||||
uint32 count = 0;
|
||||
std::deque<std::string> goodfiles;
|
||||
|
||||
for(std::deque<std::string>::iterator it = _paths.begin(); it != _paths.end(); it++)
|
||||
{
|
||||
std::deque<std::string> files = GetFileList(*it);
|
||||
sort(files.begin(),files.end()); // rough alphabetical sort
|
||||
for(std::deque<std::string>::iterator itf = files.begin(); itf != files.end(); itf++)
|
||||
{
|
||||
std::string& fn = *itf;
|
||||
if(fn.length() < 5)
|
||||
continue;
|
||||
std::string filepath = *it + fn;
|
||||
// check for special case: <dbname>.ccp in this directory? load it and skip the rest
|
||||
if(!no_compiled && !stricmp(std::string(dbname).append(".ccp").c_str(), fn.c_str()))
|
||||
{
|
||||
logdebug("Loading pre-compacted database '%s%s'", it->c_str(), fn.c_str());
|
||||
DropDB(dbname); // if sth got loaded before, remove that
|
||||
// load SCC database file and skip rest
|
||||
if(LoadCompactSCP((char*)filepath.c_str(), dbname))
|
||||
{
|
||||
logdebug("Loaded '%s' -> %s, skipping scp files",filepath.c_str(),dbname);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if(!stricmp(fn.c_str() + fn.length() - 4, ".scp"))
|
||||
{
|
||||
// skip 0-byte files
|
||||
if(GetFileSize(filepath.c_str()))
|
||||
goodfiles.push_back(filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logdetail("Pre-compacted SCC file for '%s' invalid, creating from SCP (%u files total)",dbname,goodfiles.size());
|
||||
|
||||
for(std::deque<std::string>::iterator it = goodfiles.begin(); it != goodfiles.end(); it++)
|
||||
{
|
||||
bool load_it = false;
|
||||
std::fstream fh;
|
||||
fh.open( it->c_str() , std::ios_base::in | std::ios_base::binary);
|
||||
if( !fh.is_open() )
|
||||
continue;
|
||||
|
||||
uint32 size = 1000; // search for #dbname tag in first 1000 bytes
|
||||
char *buf = new char[size];
|
||||
memset(buf,0,size);
|
||||
|
||||
fh.read(buf,size);
|
||||
fh.close();
|
||||
|
||||
std::string line,dbn;
|
||||
for(uint32 pos = 0; pos < size; pos++)
|
||||
{
|
||||
if(buf[pos] == '\n' || buf[pos] == 10 || buf[pos] == 13)
|
||||
{
|
||||
if(line.empty())
|
||||
continue;
|
||||
while(line.size() && (line[0]==' ' || line[0]=='\t'))
|
||||
line.erase(0,1);
|
||||
if(line[0] == '#')
|
||||
{
|
||||
if(!strnicmp(line.c_str(),"#dbname=",8) && !stricmp(line.c_str()+8, dbname))
|
||||
{
|
||||
load_it = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
else
|
||||
line += buf[pos];
|
||||
}
|
||||
delete [] buf;
|
||||
|
||||
if(load_it)
|
||||
{
|
||||
logdebug("File '%s' matching database '%s', loading", it->c_str(), dbname);
|
||||
count++;
|
||||
uint32 sections = AutoLoadFile((char*)it->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
char fn[100];
|
||||
sprintf(fn,"./cache/%s.ccp",dbname);
|
||||
Compact(dbname, fn);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::string SCPDatabaseMgr::GetClassName_(uint32 id)
|
||||
void SCPDatabaseMgr::AddSearchPath(char *path)
|
||||
{
|
||||
std::string r = GetDB("class").GetField(id).GetString("name");
|
||||
//if(r.empty())
|
||||
// r = className[id];
|
||||
return r;
|
||||
std::string p;
|
||||
|
||||
// normalize path, use '/' instead of '\'. needed for that check below
|
||||
uint32 len = strlen(path);
|
||||
for(uint32 i = 0; i < len; i++)
|
||||
{
|
||||
if(path[i] == '\\')
|
||||
p += '/';
|
||||
else
|
||||
p += path[i];
|
||||
}
|
||||
|
||||
if(p[p.size()-1] != '/')
|
||||
p += '/';
|
||||
for(std::deque<std::string>::iterator it = _paths.begin(); it != _paths.end(); it++)
|
||||
{
|
||||
// windows doesnt care about UPPER/lowercase, while *nix does;
|
||||
// a path shouldnt be added twice or unexpected behavior is likely to occur
|
||||
#if PLATFORM == PLATFORM_WIN32
|
||||
if(!stricmp(p.c_str(), it->c_str()))
|
||||
return;
|
||||
#else
|
||||
if(p == *it)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
_paths.push_back(p);
|
||||
}
|
||||
|
||||
std::string SCPDatabaseMgr::GetGenderName(uint32 id)
|
||||
bool SCPDatabaseMgr::LoadCompactSCP(char *fn, char *dbname)
|
||||
{
|
||||
return GetDB("gender").GetField(id).GetString("name");
|
||||
uint32 filesize = GetFileSize(fn);
|
||||
if(filesize < HEADER_SIZE)
|
||||
{
|
||||
logerror("Database file '%s' is too small!",fn);
|
||||
return false;
|
||||
}
|
||||
std::fstream fh;
|
||||
fh.open(fn, std::ios_base::in | std::ios_base::binary);
|
||||
if(!fh.is_open())
|
||||
{
|
||||
logerror("Error opening '%s'",fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
ByteBuffer bb;
|
||||
bb.resize(filesize);
|
||||
fh.read((char*)bb.contents(), filesize);
|
||||
fh.close();
|
||||
|
||||
char tag[4];
|
||||
uint32 flags, padding[4];
|
||||
uint32 offsMD5, nMD5, sizeMD5;
|
||||
uint32 offsIndexes, nIndexes, sizeIndexes;
|
||||
uint32 offsFields, nFields, sizeFields;
|
||||
uint32 offsData, nRows, sizeData;
|
||||
uint32 offsStrings, nStrings, sizeStrings;
|
||||
|
||||
bb.read((uint8*)&tag[0],4);
|
||||
if(memcmp(tag,"SCPC",4))
|
||||
{
|
||||
logerror("'%s' is not a compact database file!",fn);
|
||||
return false;
|
||||
}
|
||||
bb >> flags;
|
||||
bb >> padding[0] >> padding[1] >> padding[2] >> padding[3];
|
||||
bb >> offsMD5 >> nMD5 >> sizeMD5;
|
||||
bb >> offsIndexes >> nIndexes >> sizeIndexes;
|
||||
bb >> offsFields >> nFields >> sizeFields;
|
||||
bb >> offsData >> nRows >> sizeData;
|
||||
bb >> offsStrings >> nStrings >> sizeStrings;
|
||||
|
||||
SCPDatabase *db = GetDB(dbname,true);
|
||||
db->_name = dbname;
|
||||
db->_compact = true;
|
||||
|
||||
ByteBuffer md5buf(sizeMD5);
|
||||
md5buf.resize(sizeMD5);
|
||||
// read MD5 block
|
||||
bb.rpos(offsMD5);
|
||||
if(bb.rpos() == offsMD5)
|
||||
bb.read((uint8*)md5buf.contents(),sizeMD5);
|
||||
else
|
||||
{
|
||||
logerror("'%s' has wrong MD5 offset, can't load",fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(uint32 i = 0; i < nMD5; i++)
|
||||
{
|
||||
// read filename and MD5 hash from compiled database
|
||||
uint8 buf[MD5_DIGEST_LENGTH];
|
||||
std::string refFn;
|
||||
md5buf >> refFn;
|
||||
md5buf.read(buf,MD5_DIGEST_LENGTH);
|
||||
|
||||
// load the file referred to
|
||||
uint32 refFileSize = GetFileSize(refFn.c_str());
|
||||
FILE *refFile = fopen(refFn.c_str(), "rb");
|
||||
if(!refFile)
|
||||
{
|
||||
logdebug("Not loading '%s', file doesn't exist",fn);
|
||||
return false;
|
||||
}
|
||||
uint8 *refFileBuf = new uint8[refFileSize];
|
||||
fread(refFileBuf,sizeof(uint8),refFileSize,refFile);
|
||||
fclose(refFile);
|
||||
db->sources.insert(refFn);
|
||||
Pointers.Assign(refFn, new memblock(refFileBuf,refFileSize));
|
||||
MD5Hash md5;
|
||||
md5.Update(refFileBuf,refFileSize);
|
||||
md5.Finalize();
|
||||
if(memcmp(buf, md5.GetDigest(), MD5_DIGEST_LENGTH))
|
||||
{
|
||||
logdebug("MD5-check: '%s' has changed!", refFn.c_str());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
logdebug("MD5-check: '%s' -> OK",refFn.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// everything good so far? we reached this point? then its likely that the rest of the file is ok, alloc remaining buffers
|
||||
ByteBuffer indexbuf(sizeIndexes);
|
||||
ByteBuffer fieldsbuf(sizeFields);
|
||||
indexbuf.resize(sizeIndexes);
|
||||
fieldsbuf.resize(sizeFields);
|
||||
|
||||
// read indexes block
|
||||
bb.rpos(offsIndexes);
|
||||
if(bb.rpos() == offsIndexes)
|
||||
bb.read((uint8*)indexbuf.contents(),sizeIndexes);
|
||||
else
|
||||
{
|
||||
logerror("'%s' has wrong indexes offset, can't load",fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
// read field definitions buf
|
||||
bb.rpos(offsFields);
|
||||
if(bb.rpos() == offsFields)
|
||||
bb.read((uint8*)fieldsbuf.contents(),sizeFields);
|
||||
else
|
||||
{
|
||||
logerror("'%s' has wrong field defs offset, can't load",fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
// main data and string blocks follow below
|
||||
|
||||
for(uint32 i = 0; i < nIndexes; i++)
|
||||
{
|
||||
uint32 field_id, row;
|
||||
indexbuf >> field_id >> row;
|
||||
db->_indexes[field_id] = row;
|
||||
db->_indexes_reverse[row] = field_id;
|
||||
}
|
||||
|
||||
for(uint32 i = 0; i < nFields - 1; i++) // the first field (index column) is never written to the file!
|
||||
{
|
||||
SCPFieldDef fieldd;
|
||||
std::string fieldn;
|
||||
fieldsbuf >> fieldn >> fieldd.id >> fieldd.type;
|
||||
db->_fielddefs[fieldn] = fieldd;
|
||||
}
|
||||
|
||||
// read main data block
|
||||
ASSERT(nRows * nFields == sizeData / sizeof(uint32));
|
||||
bb.rpos(offsData);
|
||||
if(bb.rpos() == offsData)
|
||||
{
|
||||
db->_intbuf = new uint32[nRows * nFields];
|
||||
bb.read((uint8*)db->_intbuf, sizeData); // load this somewhat fast and without a for loop
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("'%s' has wrong data offset, can't load",fn);
|
||||
return false;
|
||||
}
|
||||
|
||||
// read strings
|
||||
bb.rpos(offsStrings);
|
||||
if(bb.rpos() == offsStrings)
|
||||
{
|
||||
db->_stringbuf = new char[sizeStrings];
|
||||
bb.read((uint8*)db->_stringbuf,sizeStrings);
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("'%s' has wrong strings offset, can't load",fn);
|
||||
return false;
|
||||
}
|
||||
db->_stringsize = sizeStrings;
|
||||
db->_rowcount = nRows;
|
||||
db->_fields_per_row = nFields;
|
||||
|
||||
db->DropTextData(); // delete pointers to file content created at md5 comparison
|
||||
|
||||
logdebug("'%s' loaded successfully",fn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string SCPDatabaseMgr::GetLangName(uint32 id)
|
||||
// used only for debugging
|
||||
void SCPDatabase::DumpStructureToFile(char *fn)
|
||||
{
|
||||
std::string r = GetDB("language").GetField(id).GetString("name");
|
||||
//if(r.empty())
|
||||
// r = LookupName(id,langNames);
|
||||
return r;
|
||||
std::ofstream f;
|
||||
f.open(fn);
|
||||
if(!f.is_open())
|
||||
return;
|
||||
|
||||
uint32 *ftype = new uint32[_fields_per_row];
|
||||
ftype[0] = SCP_TYPE_INT;
|
||||
|
||||
f << "Fields: (0 is always index field)\n";
|
||||
for(std::map<std::string,SCPFieldDef>::iterator it = _fielddefs.begin(); it != _fielddefs.end(); it++)
|
||||
{
|
||||
f << "-> Name: " << it->first << ", ID: " << it->second.id << ", type: " << gettypename(it->second.type) << "\n";
|
||||
ftype[it->second.id] = it->second.type;
|
||||
}
|
||||
f << "\n";
|
||||
|
||||
for(uint32 row = 0; row < _rowcount; row++)
|
||||
{
|
||||
for(uint32 column = 0; column < _fields_per_row; column++)
|
||||
{
|
||||
if(ftype[column] == SCP_TYPE_INT)
|
||||
f << *((int*)&_intbuf[row * _fields_per_row + column]) << "\t";
|
||||
else if(ftype[column] == SCP_TYPE_FLOAT)
|
||||
f << *((float*)&_intbuf[row * _fields_per_row + column]) << "\t";
|
||||
else
|
||||
f << "S_" << _intbuf[row * _fields_per_row + column] << "\t";
|
||||
}
|
||||
f << "\n";
|
||||
}
|
||||
|
||||
f << "\nStrings:\n";
|
||||
for(uint32 i = 0; i < _stringsize; i++)
|
||||
{
|
||||
if(!_stringbuf[i])
|
||||
{
|
||||
i++;
|
||||
if(i >= _stringsize)
|
||||
break;
|
||||
f << "\n" << i << ": ";
|
||||
}
|
||||
f << _stringbuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,113 +1,106 @@
|
||||
#ifndef _SCPDATABASE_H
|
||||
#define _SCPDATABASE_H
|
||||
|
||||
#include <map>
|
||||
#include "DefScript/TypeStorage.h"
|
||||
#include <set>
|
||||
|
||||
/*
|
||||
* Some preparations for compiled databases. i will continue this later. [FG]
|
||||
|
||||
enum SCPTypes
|
||||
enum SCPFieldTypes
|
||||
{
|
||||
SCP_TYPE_STRING = 0,
|
||||
SCP_TYPE_INT,
|
||||
SCP_TYPE_UINT,
|
||||
SCP_TYPE_FLOAT
|
||||
SCP_TYPE_INT = 0,
|
||||
SCP_TYPE_FLOAT = 1,
|
||||
SCP_TYPE_STRING = 2
|
||||
};
|
||||
|
||||
class SCPEntry
|
||||
struct SCPFieldDef
|
||||
{
|
||||
private:
|
||||
char *str;
|
||||
|
||||
public:
|
||||
|
||||
SCPEntry() { str = NULL; }
|
||||
~SCPEntry() { if(str) delete [] str; }
|
||||
|
||||
union
|
||||
{
|
||||
int32 intvalue
|
||||
uint32 uintvalue;
|
||||
float floatvalue;
|
||||
};
|
||||
|
||||
inline char *GetString(uint8 type)
|
||||
{
|
||||
if(str)
|
||||
return str;
|
||||
else
|
||||
{
|
||||
char buf[25];
|
||||
char *fmt;
|
||||
switch (type)
|
||||
{
|
||||
case SCP_TYPE_INT: sprintf(buf,"%d",intvalue); break;
|
||||
case SCP_TYPE_UINT: sprintf(buf,"%u",uintvalue); break;
|
||||
case SCP_TYPE_FLOAT: sprintf(buf,"%f",floatvalue); break;
|
||||
}
|
||||
str = new char[strlen(buf) + 1];
|
||||
memcpy(str,buf,strlen(buf) + 1);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
uint32 id;
|
||||
uint8 type;
|
||||
};
|
||||
|
||||
*/
|
||||
#define SCP_INVALID_INT 0xFFFFFFFF
|
||||
|
||||
typedef std::map<std::string,std::string> SCPEntryMap;
|
||||
|
||||
class SCPField
|
||||
{
|
||||
public:
|
||||
std::string GetString(std::string);
|
||||
inline uint64 GetInteger(std::string entry) { return toInt(GetString(entry)); }
|
||||
inline double GetDouble(std::string entry) { return strtod(GetString(entry).c_str(),NULL); }
|
||||
inline void Set(std::string entry,std::string value) { _map[entry]=value; }
|
||||
bool HasEntry(std::string);
|
||||
|
||||
private:
|
||||
SCPEntryMap _map;
|
||||
};
|
||||
|
||||
typedef std::map<uint32,SCPField> SCPFieldMap;
|
||||
|
||||
typedef std::map<uint32,SCPEntryMap> SCPFieldMap;
|
||||
typedef std::set<std::string> SCPSourceList;
|
||||
|
||||
class SCPDatabase
|
||||
{
|
||||
friend class SCPDatabaseMgr;
|
||||
public:
|
||||
inline SCPField& GetField(uint32 id) { return _map[id]; }
|
||||
bool HasField(uint32 id);
|
||||
uint32 LoadFromFile(char*);
|
||||
uint32 LoadFromMem(char*,uint32);
|
||||
uint32 GetFieldByValue(std::string entry, std::string value);
|
||||
SCPDatabase();
|
||||
~SCPDatabase();
|
||||
|
||||
// if the database is compacted, do not allow any modification from outside
|
||||
SCPSourceList *GetSources(void) { return _compact ? NULL : &sources; }
|
||||
SCPFieldMap *GetFields(void) { return _compact ? NULL : &fields; }
|
||||
inline bool IsCompact(void) { return _compact; }
|
||||
inline std::string GetName(void) { return _name; }
|
||||
void DropAll(void);
|
||||
void DropTextData(void);
|
||||
|
||||
// access funcs
|
||||
void *GetPtr(uint32 index, char *entry);
|
||||
void *GetPtrByField(uint32 index, uint32 entry);
|
||||
inline char *GetStringByOffset(uint32 offs) { return (offs < _stringsize ? _stringbuf + offs : ""); }
|
||||
inline char *GetString(uint32 index, char *entry) { return GetStringByOffset(GetUint32(index,entry)); }
|
||||
inline char *GetString(uint32 index, uint32 entry) { return GetStringByOffset(GetUint32(index,entry)); }
|
||||
inline uint32 GetUint32(uint32 index, char *entry) { uint32 *t = (uint32*)GetPtr(index,entry); return t ? *t : 0; }
|
||||
inline uint32 GetUint32(uint32 index, uint32 entry) { uint32 *t = (uint32*)GetPtrByField(index,entry); return t ? *t : 0; }
|
||||
inline int32 GetInt(uint32 index, char *entry) { int32 *t = (int32*)GetPtr(index,entry); return t ? *t : 0; }
|
||||
inline int32 GetInt(uint32 index, uint32 entry) { int32 *t = (int32*)GetPtrByField(index,entry); return t ? *t : 0; }
|
||||
inline float GetFloat(uint32 index, char *entry) { float *t = (float*)GetPtr(index,entry); return t ? *t : 0; }
|
||||
inline float GetFloat(uint32 index, uint32 entry) { float *t = (float*)GetPtrByField(index,entry); return t ? *t : 0; }
|
||||
uint32 GetFieldType(char *entry);
|
||||
uint32 GetFieldId(char *entry);
|
||||
inline void *GetRowByIndex(uint32 index) { return GetPtrByField(index,0); }
|
||||
uint32 GetFieldByUint32Value(char *entry, uint32 val);
|
||||
uint32 GetFieldByUint32Value(uint32 entry, uint32 val);
|
||||
uint32 GetFieldByIntValue(char *entry, int32 val);
|
||||
uint32 GetFieldByIntValue(uint32 entry, int32 val);
|
||||
uint32 GetFieldByStringValue(char *entry, char *val);
|
||||
uint32 GetFieldByStringValue(uint32 entry, char *val);
|
||||
// float value lookup not necessary
|
||||
inline uint32 GetFieldsCount(void) { return _fields_per_row; }
|
||||
inline uint32 GetRowsCount(void) { return _rowcount; }
|
||||
|
||||
|
||||
void DumpStructureToFile(char *fn);
|
||||
private:
|
||||
SCPFieldMap _map;
|
||||
// text data related
|
||||
SCPSourceList sources;
|
||||
SCPFieldMap fields;
|
||||
|
||||
// binary data related
|
||||
bool _compact;
|
||||
std::string _name;
|
||||
uint32 _rowcount;
|
||||
uint32 _fields_per_row;
|
||||
char *_stringbuf;
|
||||
uint32 _stringsize;
|
||||
uint32 *_intbuf;
|
||||
std::map<uint32,uint32> _indexes, _indexes_reverse; // stores index-to-rowID, rowID-to-index
|
||||
std::map<std::string,SCPFieldDef> _fielddefs;
|
||||
};
|
||||
|
||||
typedef std::map<std::string,SCPDatabase> SCPDatabaseMap;
|
||||
typedef TypeStorage<SCPDatabase> SCPDatabaseMap;
|
||||
|
||||
|
||||
class SCPDatabaseMgr
|
||||
{
|
||||
friend class SCPDatabase;
|
||||
public:
|
||||
bool HasDB(std::string);
|
||||
SCPDatabase& GetDB(std::string);
|
||||
SCPDatabase *GetDB(std::string n, bool create = false);
|
||||
uint32 AutoLoadFile(char *fn);
|
||||
inline void DropDB(std::string s) { _map.erase(stringToLower(s)); }
|
||||
|
||||
//////////////////////
|
||||
// helper functions //
|
||||
//////////////////////
|
||||
std::string GetZoneName(uint32 id);
|
||||
std::string GetRaceName(uint32 id);
|
||||
std::string GetClassName_(uint32 id);
|
||||
std::string GetGenderName(uint32 id);
|
||||
std::string GetMapName(uint32 id);
|
||||
std::string GetLangName(uint32 id);
|
||||
inline void DropDB(std::string s) { _map.Delete(stringToLower(s)); }
|
||||
bool Compact(char *dbname, char *outfile);
|
||||
static uint32 GetDataTypeFromString(char *s);
|
||||
uint32 SearchAndLoad(char*,bool);
|
||||
void AddSearchPath(char*);
|
||||
bool LoadCompactSCP(char*, char*);
|
||||
|
||||
private:
|
||||
SCPDatabaseMap _map;
|
||||
std::deque<std::string> _paths;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -577,15 +577,30 @@ void WorldSession::_HandleCharEnumOpcode(WorldPacket& recvPacket)
|
||||
}
|
||||
bool char_found=false;
|
||||
|
||||
SCPDatabase *zonedb = GetDBMgr().GetDB("zone"),
|
||||
*racedb = GetDBMgr().GetDB("race"),
|
||||
*mapdb = GetDBMgr().GetDB("map"),
|
||||
*classdb = GetDBMgr().GetDB("class");
|
||||
char *zonename, *racename, *mapname, *classname;
|
||||
|
||||
for(unsigned int i=0;i<num;i++)
|
||||
{
|
||||
if(zonedb)
|
||||
zonename = zonedb->GetString(plr[i]._zoneId, "name");
|
||||
if(racedb)
|
||||
racename = racedb->GetString(plr[i]._race, "name");
|
||||
if(mapdb)
|
||||
mapname = mapdb->GetString(plr[i]._mapId, "name");
|
||||
if(classdb)
|
||||
classname = classdb->GetString(plr[i]._class, "name");
|
||||
|
||||
logcustom(0,LGREEN,"## %s (%u) [%s/%s] Map: %s; Zone: %s",
|
||||
plr[i]._name.c_str(),
|
||||
plr[i]._level,
|
||||
GetDBMgr().GetRaceName(plr[i]._race).c_str(),
|
||||
GetDBMgr().GetClassName_(plr[i]._class).c_str(),
|
||||
GetDBMgr().GetMapName(plr[i]._mapId).c_str(),
|
||||
GetDBMgr().GetZoneName(plr[i]._zoneId).c_str());
|
||||
racename,
|
||||
classname,
|
||||
mapname,
|
||||
zonename);
|
||||
|
||||
logdetail("-> coords: map=%u zone=%u x=%f y=%f z=%f",
|
||||
plr[i]._mapId,plr[i]._zoneId,plr[i]._x,plr[i]._y,plr[i]._z);
|
||||
@ -683,8 +698,10 @@ void WorldSession::_HandleMessageChatOpcode(WorldPacket& recvPacket)
|
||||
}
|
||||
recvPacket >> target_guid >> msglen >> msg;
|
||||
|
||||
std::string langname = GetDBMgr().GetLangName(lang);
|
||||
const char* ln = langname.c_str();
|
||||
SCPDatabase *langdb = GetDBMgr().GetDB("language");
|
||||
const char* ln = "";
|
||||
if(langdb)
|
||||
langdb->GetString(lang,"name");
|
||||
std::string name;
|
||||
|
||||
if(type == CHAT_MSG_MONSTER_SAY)
|
||||
@ -1147,9 +1164,9 @@ void WorldSession::_HandleTextEmoteOpcode(WorldPacket& recvPacket)
|
||||
|
||||
logdebug(I64FMT " Emote: name=%s text=%u variation=%i len=%u",guid,name.c_str(),emotetext,emotev,namelen);
|
||||
SCPDatabaseMgr& dbmgr = GetInstance()->dbmgr;
|
||||
if(dbmgr.HasDB("emote"))
|
||||
SCPDatabase *emotedb = dbmgr.GetDB("emote");
|
||||
if(emotedb)
|
||||
{
|
||||
SCPDatabase& db = dbmgr.GetDB("emote");
|
||||
std::string target,target2;
|
||||
bool targeted=false; // if the emote is directed to anyone around or a specific target
|
||||
bool targeted_me=false; // if the emote was targeted to us if it was targeted
|
||||
@ -1196,13 +1213,13 @@ void WorldSession::_HandleTextEmoteOpcode(WorldPacket& recvPacket)
|
||||
target += "general";
|
||||
|
||||
// not all emotes have a female version, so check if there is one in the database
|
||||
if(female && db.GetField(emotetext).HasEntry(target + "female"))
|
||||
if(female && emotedb->GetFieldId((char*)(target + "female").c_str()) != SCP_INVALID_INT)
|
||||
target += "female";
|
||||
|
||||
logdebug("Looking up 'emote' SCP field %u entry '%s'",emotetext,target.c_str());
|
||||
|
||||
std::string etext;
|
||||
etext = db.GetField(emotetext).GetString(target);
|
||||
etext = emotedb->GetString(emotetext,(char*)target.c_str());
|
||||
|
||||
char out[300]; // should be enough
|
||||
|
||||
@ -1278,9 +1295,16 @@ void WorldSession::_HandleWhoOpcode(WorldPacket& recvPacket)
|
||||
std::string zonename;
|
||||
memset(classname,0,sizeof(classname));
|
||||
memset(racename,0,sizeof(racename));
|
||||
zonename = db.GetZoneName(wle.zoneId);
|
||||
strcpy(classname, db.GetClassName_(wle.classId).c_str());
|
||||
strcpy(racename, db.GetRaceName(wle.raceId).c_str());
|
||||
|
||||
SCPDatabase *zonedb = db.GetDB("zone"),
|
||||
*racedb = db.GetDB("race"),
|
||||
*classdb = db.GetDB("class");
|
||||
if(zonedb)
|
||||
zonename = zonedb->GetString(wle.zoneId, "name");
|
||||
if(racedb)
|
||||
strcpy(racename,racedb->GetString(wle.raceId, "name"));
|
||||
if(classdb)
|
||||
strcpy(classname,classdb->GetString(wle.classId, "name"));
|
||||
|
||||
for(uint8 i = strlen(classname); strlen(classname) < 10; i++)
|
||||
classname[i] = ' ';
|
||||
|
||||
@ -207,9 +207,6 @@
|
||||
<File
|
||||
RelativePath=".\Client\SCPDatabase.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Client\SysDefs.h">
|
||||
</File>
|
||||
<Filter
|
||||
Name="DefScript"
|
||||
Filter="">
|
||||
|
||||
@ -181,7 +181,7 @@ class ByteBuffer
|
||||
|
||||
size_t rpos(size_t rpos)
|
||||
{
|
||||
_rpos = rpos;
|
||||
_rpos = rpos < _storage.capacity() ? rpos : _storage.capacity();
|
||||
return _rpos;
|
||||
};
|
||||
|
||||
@ -192,7 +192,7 @@ class ByteBuffer
|
||||
|
||||
size_t wpos(size_t wpos)
|
||||
{
|
||||
_wpos = wpos;
|
||||
_wpos = wpos < _storage.capacity() ? wpos : _storage.capacity();
|
||||
return _wpos;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user