/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "DBCDatabaseLoader.h"
#include "DatabaseEnv.h"
#include "Errors.h"
#include "QueryResult.h"
#include "StringFormat.h"
DBCDatabaseLoader::DBCDatabaseLoader(char const* tableName, char const* dbcFormatString, std::vector& stringPool)
: _sqlTableName(tableName),
_dbcFormat(dbcFormatString),
_sqlIndexPos(0),
_recordSize(0),
_stringPool(stringPool)
{
// Get sql index position
int32 indexPos = -1;
_recordSize = DBCFileLoader::GetFormatRecordSize(_dbcFormat, &indexPos);
ASSERT(_recordSize);
}
char* DBCDatabaseLoader::Load(uint32& records, char**& indexTable)
{
std::string query = Acore::StringFormat("SELECT * FROM `{}` ORDER BY `ID` DESC", _sqlTableName);
// no error if empty set
QueryResult result = WorldDatabase.Query(query);
if (!result)
return nullptr;
// Check if sql index pos is valid
if (int32(result->GetFieldCount() - 1) < _sqlIndexPos)
{
ASSERT(false, "Invalid index pos for dbc: '{}'", _sqlTableName);
return nullptr;
}
// Resize index table
// database query *MUST* contain ORDER BY `index_field` DESC clause
uint32 indexTableSize = std::max(records, (*result)[_sqlIndexPos].Get() + 1);
if (indexTableSize > records)
{
char** tmpIdxTable = new char* [indexTableSize];
memset(tmpIdxTable, 0, indexTableSize * sizeof(char*));
memcpy(tmpIdxTable, indexTable, records * sizeof(char*));
delete[] indexTable;
indexTable = tmpIdxTable;
}
std::unique_ptr dataTable = std::make_unique(result->GetRowCount() * _recordSize);
std::unique_ptr newIndexes = std::make_unique(result->GetRowCount());
uint32 newRecords = 0;
// Insert sql data into the data array
do
{
Field* fields = result->Fetch();
uint32 indexValue = fields[_sqlIndexPos].Get();
char* dataValue = indexTable[indexValue];
// If exist in DBC file override from DB
newIndexes[newRecords] = indexValue;
dataValue = &dataTable[newRecords++ * _recordSize];
uint32 dataOffset = 0;
uint32 sqlColumnNumber = 0;
char const* dbcFormat = _dbcFormat;
for (; (*dbcFormat); ++dbcFormat)
{
switch (*dbcFormat)
{
case FT_FLOAT:
*reinterpret_cast(&dataValue[dataOffset]) = fields[sqlColumnNumber].Get();
dataOffset += sizeof(float);
break;
case FT_IND:
case FT_INT:
*reinterpret_cast(&dataValue[dataOffset]) = fields[sqlColumnNumber].Get();
dataOffset += sizeof(uint32);
break;
case FT_BYTE:
*reinterpret_cast(&dataValue[dataOffset]) = fields[sqlColumnNumber].Get();
dataOffset += sizeof(uint8);
break;
case FT_STRING:
*reinterpret_cast(&dataValue[dataOffset]) = CloneStringToPool(fields[sqlColumnNumber].Get());
dataOffset += sizeof(char*);
break;
case FT_SORT:
case FT_NA:
case FT_NA_BYTE:
break;
default:
ASSERT(false, "Unsupported data type '{}' in table '{}'", *dbcFormat, _sqlTableName);
return nullptr;
}
++sqlColumnNumber;
}
ASSERT(sqlColumnNumber == result->GetFieldCount(), "SQL format string does not match database for table: '{}'", _sqlTableName);
ASSERT(dataOffset == _recordSize);
} while (result->NextRow());
ASSERT(newRecords == result->GetRowCount());
// insert new records to index table
for (uint32 i = 0; i < newRecords; ++i)
{
// cppcheck-suppress autoVariables
indexTable[newIndexes[i]] = &dataTable[i * _recordSize];
}
records = indexTableSize;
return dataTable.release();
}
char* DBCDatabaseLoader::CloneStringToPool(std::string const& str)
{
char* buf = new char[str.size() + 1];
memcpy(buf, str.c_str(), str.size() + 1);
_stringPool.push_back(buf);
return buf;
}