/* * Copyright (C) 2005-2009 MaNGOS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef DBCSTORE_H #define DBCSTORE_H #include "DBCFileLoader.h" #include "Log.h" struct SqlDbc { const std::string * formatString; const std::string * indexName; std::string sqlTableName; int32 indexPos; int32 sqlIndexPos; SqlDbc(const std::string * _filename, const std::string * _format, const std::string * _idname, const char * fmt) :formatString(_format),sqlIndexPos(0), indexName (_idname) { // Convert dbc file name to sql table name sqlTableName = *_filename; for (uint32 i = 0;i< sqlTableName.size();++i) { if (isalpha(sqlTableName[i])) sqlTableName[i] = tolower(sqlTableName[i]); else if (sqlTableName[i] == '.') sqlTableName[i] = '_'; } // Get sql index position DBCFileLoader::GetFormatRecordSize(fmt, &indexPos); if (indexPos>=0) { for(uint32 x=0;x < formatString->size();x++) { // Count only fields present in sql if ((*formatString)[x] == FT_SQL_PRESENT) { if (x == indexPos) break; ++sqlIndexPos; } } } } }; template class DBCStorage { typedef std::list StringPoolList; public: explicit DBCStorage(const char *f) : nCount(0), fieldCount(0), fmt(f), indexTable(NULL), m_dataTable(NULL) { } ~DBCStorage() { Clear(); } T const* LookupEntry(uint32 id) const { return (id>=nCount)?NULL:indexTable[id]; } uint32 GetNumRows() const { return nCount; } char const* GetFormat() const { return fmt; } uint32 GetFieldCount() const { return fieldCount; } bool Load(char const* fn, SqlDbc * sql) { DBCFileLoader dbc; // Check if load was sucessful, only then continue if(!dbc.Load(fn, fmt)) return false; uint32 sqlRecordCount = 0; uint32 sqlHighestIndex = 0; Field *fields = NULL; QueryResult *result = NULL; // Load data from sql if (sql) { std::string query = "SELECT * FROM " + sql->sqlTableName; if (sql->indexPos >= 0) query +=" ORDER BY + " + *sql->indexName + " DESC"; query += ";"; result = WorldDatabase.Query(query.c_str()); if (result) { sqlRecordCount = result->GetRowCount(); if (sql->indexPos >= 0) { fields = result->Fetch(); sqlHighestIndex = fields[sql->sqlIndexPos].GetUInt32(); } // Check if sql index pos is valid if (int32(result->GetFieldCount()-1) < sql->sqlIndexPos) { sLog.outError("Invalid index pos for dbc:'%s'", sql->sqlTableName.c_str()); return false; } } } char * sqlDataTable; fieldCount = dbc.GetCols(); m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable, sqlRecordCount, sqlHighestIndex, sqlDataTable); m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); // Insert sql data into arrays if (result) { if (indexTable) { uint32 offset = 0; uint32 rowIndex = dbc.GetNumRows(); do { if (!fields) fields = result->Fetch(); if(sql->indexPos >= 0) { uint32 id = fields[sql->sqlIndexPos].GetUInt32(); if (indexTable[id]) { sLog.outError("Index %d already exists in dbc:'%s'", id, sql->sqlTableName.c_str()); return false; } indexTable[id]=(T*)&sqlDataTable[offset]; } else indexTable[rowIndex]=(T*)&sqlDataTable[offset]; uint32 columnNumber = 0; uint32 sqlColumnNumber = 0; for(;columnNumber < sql->formatString->size();++columnNumber) { if ((*sql->formatString)[columnNumber] == FT_SQL_ABSENT) { switch(fmt[columnNumber]) { case FT_FLOAT: *((float*)(&sqlDataTable[offset]))= 0.0f; offset+=4; break; case FT_IND: case FT_INT: *((uint32*)(&sqlDataTable[offset]))=uint32(0); offset+=4; break; case FT_BYTE: *((uint8*)(&sqlDataTable[offset]))=uint8(0); offset+=1; break; case FT_STRING: // Beginning of the pool - empty string *((char**)(&sqlDataTable[offset]))=m_stringPoolList.back(); offset+=sizeof(char*); break; } } else if ((*sql->formatString)[columnNumber] == FT_SQL_PRESENT) { bool validSqlColumn = true; switch(fmt[columnNumber]) { case FT_FLOAT: *((float*)(&sqlDataTable[offset]))=fields[sqlColumnNumber].GetFloat(); offset+=4; break; case FT_IND: case FT_INT: *((uint32*)(&sqlDataTable[offset]))=fields[sqlColumnNumber].GetUInt32(); offset+=4; break; case FT_BYTE: *((uint8*)(&sqlDataTable[offset]))=fields[sqlColumnNumber].GetUInt8(); offset+=1; break; case FT_STRING: sLog.outError("Unsupported data type in table '%s' at char %d", sql->sqlTableName.c_str(), columnNumber); delete result; return false; case FT_SORT: break; default: validSqlColumn = false; } if (validSqlColumn) sqlColumnNumber++; } else { sLog.outError("Incorrect sql format string '%s' at char %d", sql->sqlTableName.c_str(), columnNumber); delete result; return false; } } if (sqlColumnNumber != (result->GetFieldCount()-1)) { sLog.outError("SQL and DBC format strings are not matching for table: '%s'", sql->sqlTableName.c_str()); delete result; return false; } fields = NULL; ++rowIndex; }while (result->NextRow()); } delete result; } // error in dbc file at loading if NULL return indexTable!=NULL; } bool LoadStringsFrom(char const* fn) { // DBC must be already loaded using Load if(!indexTable) return false; DBCFileLoader dbc; // Check if load was successful, only then continue if(!dbc.Load(fn, fmt)) return false; m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); return true; } void Clear() { if (!indexTable) return; delete[] ((char*)indexTable); indexTable = NULL; delete[] ((char*)m_dataTable); m_dataTable = NULL; while(!m_stringPoolList.empty()) { delete[] m_stringPoolList.front(); m_stringPoolList.pop_front(); } nCount = 0; } private: char const* fmt; uint32 nCount; uint32 fieldCount; T** indexTable; T* m_dataTable; StringPoolList m_stringPoolList; }; #endif