diff options
Diffstat (limited to 'NorthstarDLL/scripts/scriptdatatables.cpp')
-rw-r--r-- | NorthstarDLL/scripts/scriptdatatables.cpp | 909 |
1 files changed, 0 insertions, 909 deletions
diff --git a/NorthstarDLL/scripts/scriptdatatables.cpp b/NorthstarDLL/scripts/scriptdatatables.cpp deleted file mode 100644 index 87a26dca..00000000 --- a/NorthstarDLL/scripts/scriptdatatables.cpp +++ /dev/null @@ -1,909 +0,0 @@ -#include "squirrel/squirrel.h" -#include "core/filesystem/rpakfilesystem.h" -#include "core/convar/convar.h" -#include "dedicated/dedicated.h" -#include "core/filesystem/filesystem.h" -#include "core/math/vector.h" -#include "core/tier0.h" -#include "engine/r2engine.h" -#include <iostream> -#include <sstream> -#include <map> -#include <fstream> -#include <filesystem> - -const uint64_t USERDATA_TYPE_DATATABLE = 0xFFF7FFF700000004; -const uint64_t USERDATA_TYPE_DATATABLE_CUSTOM = 0xFFFCFFFC12345678; - -enum class DatatableType : int -{ - BOOL = 0, - INT, - FLOAT, - VECTOR, - STRING, - ASSET, - UNK_STRING // unknown but deffo a string type -}; - -struct ColumnInfo -{ - char* name; - DatatableType type; - int offset; -}; - -struct Datatable -{ - int numColumns; - int numRows; - ColumnInfo* columnInfo; - char* data; // actually data pointer - int rowInfo; -}; - -ConVar* Cvar_ns_prefer_datatable_from_disk; - -template <ScriptContext context> Datatable* (*SQ_GetDatatableInternal)(HSquirrelVM* sqvm); - -struct CSVData -{ - std::string m_sAssetName; - std::string m_sCSVString; - char* m_pDataBuf; - size_t m_nDataBufSize; - - std::vector<char*> columns; - std::vector<std::vector<char*>> dataPointers; -}; - -std::unordered_map<std::string, CSVData> CSVCache; - -Vector3 StringToVector(char* pString) -{ - Vector3 vRet; - - int length = 0; - while (pString[length]) - { - if ((pString[length] == '<') || (pString[length] == '>')) - pString[length] = '\0'; - length++; - } - - int startOfFloat = 1; - int currentIndex = 1; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.x = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.y = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - while (pString[currentIndex] && (pString[currentIndex] != ',')) - currentIndex++; - pString[currentIndex] = '\0'; - vRet.z = std::stof(&pString[startOfFloat]); - startOfFloat = ++currentIndex; - - return vRet; -} - -// var function GetDataTable( asset path ) -REPLACE_SQFUNC(GetDataTable, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - const char* pAssetName; - g_pSquirrel<context>->getasset(sqvm, 2, &pAssetName); - - if (strncmp(pAssetName, "datatable/", 10)) - { - g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Asset \"{}\" doesn't start with \"datatable/\"", pAssetName).c_str()); - return SQRESULT_ERROR; - } - else if (!Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(pAssetName)) - return g_pSquirrel<context>->m_funcOriginals["GetDataTable"](sqvm); - // either we prefer disk datatables, or we're loading a datatable that wasn't found in rpak - else - { - std::string sAssetPath(fmt::format("scripts/{}", pAssetName)); - - // first, check the cache - if (CSVCache.find(pAssetName) != CSVCache.end()) - { - CSVData** pUserdata = g_pSquirrel<context>->template createuserdata<CSVData*>(sqvm, sizeof(CSVData*)); - g_pSquirrel<context>->setuserdatatypeid(sqvm, -1, USERDATA_TYPE_DATATABLE_CUSTOM); - *pUserdata = &CSVCache[pAssetName]; - - return SQRESULT_NOTNULL; - } - - // check files on disk - // we don't use .rpak as the extension for on-disk datatables, so we need to replace .rpak with .csv in the filename we're reading - fs::path diskAssetPath("scripts"); - if (fs::path(pAssetName).extension() == ".rpak") - diskAssetPath /= fs::path(pAssetName).remove_filename() / (fs::path(pAssetName).stem().string() + ".csv"); - else - diskAssetPath /= fs::path(pAssetName); - - std::string sDiskAssetPath(diskAssetPath.string()); - if ((*g_pFilesystem)->m_vtable2->FileExists(&(*g_pFilesystem)->m_vtable2, sDiskAssetPath.c_str(), "GAME")) - { - std::string sTableCSV = ReadVPKFile(sDiskAssetPath.c_str()); - if (!sTableCSV.size()) - { - g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Datatable \"{}\" is empty", pAssetName).c_str()); - return SQRESULT_ERROR; - } - - // somewhat shit, but ensure we end with a newline to make parsing easier - if (sTableCSV[sTableCSV.length() - 1] != '\n') - sTableCSV += '\n'; - - CSVData csv; - csv.m_sAssetName = pAssetName; - csv.m_sCSVString = sTableCSV; - csv.m_nDataBufSize = sTableCSV.size(); - csv.m_pDataBuf = new char[csv.m_nDataBufSize]; - memcpy(csv.m_pDataBuf, &sTableCSV[0], csv.m_nDataBufSize); - - // parse the csv - // csvs are essentially comma and newline-deliniated sets of strings for parsing, only thing we need to worry about is quoted - // entries when we parse an element of the csv, rather than allocating an entry for it, we just convert that element to a - // null-terminated string i.e., store the ptr to the first char of it, then make the comma that delinates it a nullchar - - bool bHasColumns = false; - bool bInQuotes = false; - - std::vector<char*> vCurrentRow; - char* pElemStart = csv.m_pDataBuf; - char* pElemEnd = nullptr; - - for (int i = 0; i < csv.m_nDataBufSize; i++) - { - if (csv.m_pDataBuf[i] == '\r' && csv.m_pDataBuf[i + 1] == '\n') - { - if (!pElemEnd) - pElemEnd = csv.m_pDataBuf + i; - - continue; // next iteration can handle the \n - } - - // newline, end of a row - if (csv.m_pDataBuf[i] == '\n') - { - // shouldn't have newline in string - if (bInQuotes) - { - g_pSquirrel<context>->raiseerror(sqvm, "Unexpected \\n in string"); - return SQRESULT_ERROR; - } - - // push last entry to current row - if (pElemEnd) - *pElemEnd = '\0'; - else - csv.m_pDataBuf[i] = '\0'; - - vCurrentRow.push_back(pElemStart); - - // newline, push last line to csv data and go from there - if (!bHasColumns) - { - bHasColumns = true; - csv.columns = vCurrentRow; - } - else - csv.dataPointers.push_back(vCurrentRow); - - vCurrentRow.clear(); - // put start of current element at char after newline - pElemStart = csv.m_pDataBuf + i + 1; - pElemEnd = nullptr; - } - // we're starting or ending a quoted string - else if (csv.m_pDataBuf[i] == '"') - { - // start quoted string - if (!bInQuotes) - { - // shouldn't have quoted strings in column names - if (!bHasColumns) - { - g_pSquirrel<context>->raiseerror(sqvm, "Unexpected \" in column name"); - return SQRESULT_ERROR; - } - - bInQuotes = true; - // put start of current element at char after string begin - pElemStart = csv.m_pDataBuf + i + 1; - } - // end quoted string - else - { - pElemEnd = csv.m_pDataBuf + i; - bInQuotes = false; - } - } - // don't parse commas in quotes - else if (bInQuotes) - { - continue; - } - // comma, push new entry to current row - else if (csv.m_pDataBuf[i] == ',') - { - if (pElemEnd) - *pElemEnd = '\0'; - else - csv.m_pDataBuf[i] = '\0'; - - vCurrentRow.push_back(pElemStart); - // put start of next element at char after comma - pElemStart = csv.m_pDataBuf + i + 1; - pElemEnd = nullptr; - } - } - - // add to cache and return - CSVData** pUserdata = g_pSquirrel<context>->template createuserdata<CSVData*>(sqvm, sizeof(CSVData*)); - g_pSquirrel<context>->setuserdatatypeid(sqvm, -1, USERDATA_TYPE_DATATABLE_CUSTOM); - CSVCache[pAssetName] = csv; - *pUserdata = &CSVCache[pAssetName]; - - return SQRESULT_NOTNULL; - } - // the file doesn't exist on disk, check rpak if we haven't already - else if (Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(pAssetName)) - return g_pSquirrel<context>->m_funcOriginals["GetDataTable"](sqvm); - // the file doesn't exist at all, error - else - { - g_pSquirrel<context>->raiseerror(sqvm, fmt::format("Datatable {} not found", pAssetName).c_str()); - return SQRESULT_ERROR; - } - } -} - -// int function GetDataTableColumnByName( var datatable, string columnName ) -REPLACE_SQFUNC(GetDataTableColumnByName, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableColumnByName"](sqvm); - - CSVData* csv = *pData; - const char* pColumnName = g_pSquirrel<context>->getstring(sqvm, 2); - - for (int i = 0; i < csv->columns.size(); i++) - { - if (!strcmp(csv->columns[i], pColumnName)) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - // column not found - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowCount( var datatable ) -REPLACE_SQFUNC(GetDataTableRowCount, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDatatableRowCount"](sqvm); - - CSVData* csv = *pData; - g_pSquirrel<context>->pushinteger(sqvm, csv->dataPointers.size()); - return SQRESULT_NOTNULL; -} - -// string function GetDataTableString( var datatable, int row, int col ) -REPLACE_SQFUNC(GetDataTableString, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableString"](sqvm); - - CSVData* csv = *pData; - const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3); - if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size()) - { - g_pSquirrel<context>->raiseerror( - sqvm, - fmt::format( - "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size()) - .c_str()); - return SQRESULT_ERROR; - } - - g_pSquirrel<context>->pushstring(sqvm, csv->dataPointers[nRow][nCol], -1); - return SQRESULT_NOTNULL; -} - -// asset function GetDataTableAsset( var datatable, int row, int col ) -REPLACE_SQFUNC(GetDataTableAsset, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableAsset"](sqvm); - - CSVData* csv = *pData; - const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3); - if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size()) - { - g_pSquirrel<context>->raiseerror( - sqvm, - fmt::format( - "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size()) - .c_str()); - return SQRESULT_ERROR; - } - - g_pSquirrel<context>->pushasset(sqvm, csv->dataPointers[nRow][nCol], -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableInt( var datatable, int row, int col ) -REPLACE_SQFUNC(GetDataTableInt, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableInt"](sqvm); - - CSVData* csv = *pData; - const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3); - if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size()) - { - g_pSquirrel<context>->raiseerror( - sqvm, - fmt::format( - "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size()) - .c_str()); - return SQRESULT_ERROR; - } - - g_pSquirrel<context>->pushinteger(sqvm, std::stoi(csv->dataPointers[nRow][nCol])); - return SQRESULT_NOTNULL; -} - -// float function GetDataTableFloat( var datatable, int row, int col ) -REPLACE_SQFUNC(GetDataTableFloat, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableFloat"](sqvm); - - CSVData* csv = *pData; - const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3); - if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size()) - { - g_pSquirrel<context>->raiseerror( - sqvm, - fmt::format( - "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size()) - .c_str()); - return SQRESULT_ERROR; - } - - g_pSquirrel<context>->pushfloat(sqvm, std::stof(csv->dataPointers[nRow][nCol])); - return SQRESULT_NOTNULL; -} - -// bool function GetDataTableBool( var datatable, int row, int col ) -REPLACE_SQFUNC(GetDataTableBool, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableBool"](sqvm); - - CSVData* csv = *pData; - const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3); - if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size()) - { - g_pSquirrel<context>->raiseerror( - sqvm, - fmt::format( - "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size()) - .c_str()); - return SQRESULT_ERROR; - } - - g_pSquirrel<context>->pushbool(sqvm, std::stoi(csv->dataPointers[nRow][nCol])); - return SQRESULT_NOTNULL; -} - -// vector function GetDataTableVector( var datatable, int row, int col ) -REPLACE_SQFUNC(GetDataTableVector, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableVector"](sqvm); - - CSVData* csv = *pData; - const int nRow = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nCol = g_pSquirrel<context>->getinteger(sqvm, 3); - if (nRow >= csv->dataPointers.size() || nCol >= csv->dataPointers[nRow].size()) - { - g_pSquirrel<context>->raiseerror( - sqvm, - fmt::format( - "row {} and col {} are outside of range row {} and col {}", nRow, nCol, csv->dataPointers.size(), csv->columns.size()) - .c_str()); - return SQRESULT_ERROR; - } - - g_pSquirrel<context>->pushvector(sqvm, StringToVector(csv->dataPointers[nRow][nCol])); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowMatchingStringValue( var datatable, int col, string value ) -REPLACE_SQFUNC(GetDataTableRowMatchingStringValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingStringValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const char* pStringVal = g_pSquirrel<context>->getstring(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (!strcmp(csv->dataPointers[i][nCol], pStringVal)) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowMatchingAssetValue( var datatable, int col, asset value ) -REPLACE_SQFUNC(GetDataTableMatchingAssetValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingAssetValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const char* pStringVal; - g_pSquirrel<context>->getasset(sqvm, 3, &pStringVal); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (!strcmp(csv->dataPointers[i][nCol], pStringVal)) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowMatchingFloatValue( var datatable, int col, float value ) -REPLACE_SQFUNC(GetDataTableRowMatchingFloatValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingFloatValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const float flFloatVal = g_pSquirrel<context>->getfloat(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (flFloatVal == std::stof(csv->dataPointers[i][nCol])) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowMatchingIntValue( var datatable, int col, int value ) -REPLACE_SQFUNC(GetDataTableRowMatchingIntValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingIntValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nIntVal = g_pSquirrel<context>->getinteger(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (nIntVal == std::stoi(csv->dataPointers[i][nCol])) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowMatchingVectorValue( var datatable, int col, vector value ) -REPLACE_SQFUNC(GetDataTableRowMatchingVectorValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowMatchingVectorValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const Vector3 vVectorVal = g_pSquirrel<context>->getvector(sqvm, 3); - - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (vVectorVal == StringToVector(csv->dataPointers[i][nCol])) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowGreaterThanOrEqualToIntValue( var datatable, int col, int value ) -REPLACE_SQFUNC(GetDataTableRowGreaterThanOrEqualToIntValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowGreaterThanOrEqualToIntValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nIntVal = g_pSquirrel<context>->getinteger(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (nIntVal >= std::stoi(csv->dataPointers[i][nCol])) - { - spdlog::info("datatable not loaded"); - g_pSquirrel<context>->pushinteger(sqvm, 1); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowLessThanOrEqualToIntValue( var datatable, int col, int value ) -REPLACE_SQFUNC(GetDataTableRowLessThanOrEqualToIntValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowLessThanOrEqualToIntValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const int nIntVal = g_pSquirrel<context>->getinteger(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (nIntVal <= std::stoi(csv->dataPointers[i][nCol])) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowGreaterThanOrEqualToFloatValue( var datatable, int col, float value ) -REPLACE_SQFUNC(GetDataTableRowGreaterThanOrEqualToFloatValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowGreaterThanOrEqualToFloatValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const float flFloatVal = g_pSquirrel<context>->getfloat(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (flFloatVal >= std::stof(csv->dataPointers[i][nCol])) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -// int function GetDataTableRowLessThanOrEqualToFloatValue( var datatable, int col, float value ) -REPLACE_SQFUNC(GetDataTableRowLessThanOrEqualToFloatValue, (ScriptContext::UI | ScriptContext::CLIENT | ScriptContext::SERVER)) -{ - CSVData** pData; - uint64_t typeId; - g_pSquirrel<context>->getuserdata(sqvm, 2, &pData, &typeId); - - if (typeId != USERDATA_TYPE_DATATABLE_CUSTOM) - return g_pSquirrel<context>->m_funcOriginals["GetDataTableRowLessThanOrEqualToFloatValue"](sqvm); - - CSVData* csv = *pData; - int nCol = g_pSquirrel<context>->getinteger(sqvm, 2); - const float flFloatVal = g_pSquirrel<context>->getfloat(sqvm, 3); - for (int i = 0; i < csv->dataPointers.size(); i++) - { - if (flFloatVal <= std::stof(csv->dataPointers[i][nCol])) - { - g_pSquirrel<context>->pushinteger(sqvm, i); - return SQRESULT_NOTNULL; - } - } - - g_pSquirrel<context>->pushinteger(sqvm, -1); - return SQRESULT_NOTNULL; -} - -std::string DataTableToString(Datatable* datatable) -{ - std::string sCSVString; - - // write columns - bool bShouldComma = false; - for (int i = 0; i < datatable->numColumns; i++) - { - if (bShouldComma) - sCSVString += ','; - else - bShouldComma = true; - - sCSVString += datatable->columnInfo[i].name; - } - - // write rows - for (int row = 0; row < datatable->numRows; row++) - { - sCSVString += '\n'; - - bool bShouldComma = false; - for (int col = 0; col < datatable->numColumns; col++) - { - if (bShouldComma) - sCSVString += ','; - else - bShouldComma = true; - - // output typed data - ColumnInfo column = datatable->columnInfo[col]; - const void* pUntypedVal = datatable->data + column.offset + row * datatable->rowInfo; - switch (column.type) - { - case DatatableType::BOOL: - { - sCSVString += *(bool*)pUntypedVal ? '1' : '0'; - break; - } - - case DatatableType::INT: - { - sCSVString += std::to_string(*(int*)pUntypedVal); - break; - } - - case DatatableType::FLOAT: - { - sCSVString += std::to_string(*(float*)pUntypedVal); - break; - } - - case DatatableType::VECTOR: - { - Vector3* pVector = (Vector3*)(pUntypedVal); - sCSVString += fmt::format("<{},{},{}>", pVector->x, pVector->y, pVector->z); - break; - } - - case DatatableType::STRING: - case DatatableType::ASSET: - case DatatableType::UNK_STRING: - { - sCSVString += fmt::format("\"{}\"", *(char**)pUntypedVal); - break; - } - } - } - } - - return sCSVString; -} - -void DumpDatatable(const char* pDatatablePath) -{ - Datatable* pDatatable = (Datatable*)g_pPakLoadManager->LoadFile(pDatatablePath); - if (!pDatatable) - { - spdlog::error("couldn't load datatable {} (rpak containing it may not be loaded?)", pDatatablePath); - return; - } - - std::string sOutputPath(fmt::format("{}/scripts/datatable/{}.csv", g_pModName, fs::path(pDatatablePath).stem().string())); - std::string sDatatableContents(DataTableToString(pDatatable)); - - fs::create_directories(fs::path(sOutputPath).remove_filename()); - std::ofstream outputStream(sOutputPath); - outputStream.write(sDatatableContents.c_str(), sDatatableContents.size()); - outputStream.close(); - - spdlog::info("dumped datatable {} {} to {}", pDatatablePath, (void*)pDatatable, sOutputPath); -} - -void ConCommand_dump_datatable(const CCommand& args) -{ - if (args.ArgC() < 2) - { - spdlog::info("usage: dump_datatable datatable/tablename.rpak"); - return; - } - - DumpDatatable(args.Arg(1)); -} - -void ConCommand_dump_datatables(const CCommand& args) -{ - // likely not a comprehensive list, might be missing a couple? - static const std::vector<const char*> VANILLA_DATATABLE_PATHS = { - "datatable/burn_meter_rewards.rpak", - "datatable/burn_meter_store.rpak", - "datatable/calling_cards.rpak", - "datatable/callsign_icons.rpak", - "datatable/camo_skins.rpak", - "datatable/default_pilot_loadouts.rpak", - "datatable/default_titan_loadouts.rpak", - "datatable/faction_leaders.rpak", - "datatable/fd_awards.rpak", - "datatable/features_mp.rpak", - "datatable/non_loadout_weapons.rpak", - "datatable/pilot_abilities.rpak", - "datatable/pilot_executions.rpak", - "datatable/pilot_passives.rpak", - "datatable/pilot_properties.rpak", - "datatable/pilot_weapons.rpak", - "datatable/pilot_weapon_features.rpak", - "datatable/pilot_weapon_mods.rpak", - "datatable/pilot_weapon_mods_common.rpak", - "datatable/playlist_items.rpak", - "datatable/titans_mp.rpak", - "datatable/titan_abilities.rpak", - "datatable/titan_executions.rpak", - "datatable/titan_fd_upgrades.rpak", - "datatable/titan_nose_art.rpak", - "datatable/titan_passives.rpak", - "datatable/titan_primary_mods.rpak", - "datatable/titan_primary_mods_common.rpak", - "datatable/titan_primary_weapons.rpak", - "datatable/titan_properties.rpak", - "datatable/titan_skins.rpak", - "datatable/titan_voices.rpak", - "datatable/unlocks_faction_level.rpak", - "datatable/unlocks_fd_titan_level.rpak", - "datatable/unlocks_player_level.rpak", - "datatable/unlocks_random.rpak", - "datatable/unlocks_titan_level.rpak", - "datatable/unlocks_weapon_level_pilot.rpak", - "datatable/weapon_skins.rpak", - "datatable/xp_per_faction_level.rpak", - "datatable/xp_per_fd_titan_level.rpak", - "datatable/xp_per_player_level.rpak", - "datatable/xp_per_titan_level.rpak", - "datatable/xp_per_weapon_level.rpak", - "datatable/faction_leaders_dropship_anims.rpak", - "datatable/score_events.rpak", - "datatable/startpoints.rpak", - "datatable/sp_levels.rpak", - "datatable/community_entries.rpak", - "datatable/spotlight_images.rpak", - "datatable/death_hints_mp.rpak", - "datatable/flightpath_assets.rpak", - "datatable/earn_meter_mp.rpak", - "datatable/battle_chatter_voices.rpak", - "datatable/battle_chatter.rpak", - "datatable/titan_os_conversations.rpak", - "datatable/faction_dialogue.rpak", - "datatable/grunt_chatter_mp.rpak", - "datatable/spectre_chatter_mp.rpak", - "datatable/pain_death_sounds.rpak", - "datatable/caller_ids_mp.rpak"}; - - for (const char* datatable : VANILLA_DATATABLE_PATHS) - DumpDatatable(datatable); -} - -ON_DLL_LOAD_RELIESON("server.dll", ServerScriptDatatables, ServerSquirrel, (CModule module)) -{ - SQ_GetDatatableInternal<ScriptContext::SERVER> = module.Offset(0x1250f0).RCast<Datatable* (*)(HSquirrelVM*)>(); -} - -ON_DLL_LOAD_RELIESON("client.dll", ClientScriptDatatables, ClientSquirrel, (CModule module)) -{ - SQ_GetDatatableInternal<ScriptContext::CLIENT> = module.Offset(0x1C9070).RCast<Datatable* (*)(HSquirrelVM*)>(); - SQ_GetDatatableInternal<ScriptContext::UI> = SQ_GetDatatableInternal<ScriptContext::CLIENT>; -} - -ON_DLL_LOAD_RELIESON("engine.dll", SharedScriptDataTables, ConVar, (CModule module)) -{ - Cvar_ns_prefer_datatable_from_disk = new ConVar( - "ns_prefer_datatable_from_disk", - IsDedicatedServer() && CommandLine()->CheckParm("-nopakdedi") ? "1" : "0", - FCVAR_NONE, - "whether to prefer loading datatables from disk, rather than rpak"); - - RegisterConCommand("dump_datatables", ConCommand_dump_datatables, "dumps all datatables from a hardcoded list", FCVAR_NONE); - RegisterConCommand("dump_datatable", ConCommand_dump_datatable, "dump a datatable", FCVAR_NONE); -} |