#include "pch.h" #include "squirrel.h" #include "rpakfilesystem.h" #include "convar.h" #include "filesystem.h" #include #include #include #include #include "tier0.h" struct ColumnInfo { char* name; int type; int offset; }; struct DataTable { int columnAmount; int rowAmount; ColumnInfo* columnInfo; long long data; // actually data pointer int rowInfo; }; ConVar* Cvar_ns_prefer_datatable_from_disk; void datatableReleaseHook(void*, int size); const long long customDatatableTypeId = 0xFFFCFFFC12345678; const long long vanillaDatatableTypeId = 0xFFF7FFF700000004; void* (*getDataTableStructure)(HSquirrelVM* sqvm); std::string DataTableToString(DataTable* datatable); struct csvData { char* name; std::vector> dataPointers; char* fullData = 0; std::vector columnNames; }; std::unordered_map cacheMap; void StringToVector(char* string, float* vector) { int length = 0; while (string[length]) { if ((string[length] == '<') || (string[length] == '>')) string[length] = '\0'; length++; } int startOfFloat = 1; int currentIndex = 1; while (string[currentIndex] && (string[currentIndex] != ',')) currentIndex++; string[currentIndex] = '\0'; vector[0] = std::stof(&string[startOfFloat]); startOfFloat = ++currentIndex; while (string[currentIndex] && (string[currentIndex] != ',')) currentIndex++; string[currentIndex] = '\0'; vector[1] = std::stof(&string[startOfFloat]); startOfFloat = ++currentIndex; while (string[currentIndex] && (string[currentIndex] != ',')) currentIndex++; string[currentIndex] = '\0'; vector[2] = std::stof(&string[startOfFloat]); startOfFloat = ++currentIndex; } template SQRESULT GetDatatable(HSquirrelVM* sqvm) { const char* assetName; g_pSquirrel->getasset(sqvm, 2, &assetName); SQRESULT result = SQRESULT_ERROR; if (strncmp(assetName, "datatable/", 10) != 0) { spdlog::error("Asset \"{}\" doesn't start with \"datatable/\"", assetName); } else if ((!Cvar_ns_prefer_datatable_from_disk->GetBool()) && g_pPakLoadManager->LoadFile(assetName)) { // spdlog::info("Load Datatable {} from rpak", assetName); result = g_pSquirrel->m_funcOriginals["GetDataTable"](sqvm); } else { char assetPath[250]; snprintf(assetPath, 250, "scripts/%s", assetName); if (cacheMap.count(assetName)) { // spdlog::info("Loaded custom Datatable {} from cache", assetName); csvData** dataPointer = (csvData**)g_pSquirrel->createuserdata(sqvm, sizeof(csvData*)); *dataPointer = cacheMap[assetName]; g_pSquirrel->setuserdatatypeid(sqvm, -1, customDatatableTypeId); // sqvm->_stack[sqvm->_top -1]._VAL.asUserdata->releaseHook = datatableReleaseHook; result = SQRESULT_NOTNULL; } else if ((*R2::g_pFilesystem)->m_vtable2->FileExists(&(*R2::g_pFilesystem)->m_vtable2, assetPath, "GAME")) { std::string dataFile = R2::ReadVPKFile(assetPath); if (dataFile.size() == 0) return SQRESULT_ERROR; char* csvFullData = (char*)malloc(dataFile.size()); memcpy(csvFullData, dataFile.c_str(), dataFile.size()); csvData* data = new csvData(); data->fullData = csvFullData; std::vector currentLine; int startIndex = 0; if (csvFullData[0] == '\"') { currentLine.push_back(&csvFullData[1]); startIndex = 1; int i = 1; while (csvFullData[i] != '\"') i++; csvFullData[i] = '\0'; } else { currentLine.push_back(csvFullData); } bool firstLine = true; for (int i = startIndex; i < dataFile.size(); i++) { if (csvFullData[i] == ',') { if (csvFullData[i + 1] == '\"') { currentLine.push_back(&csvFullData[i + 2]); csvFullData[i] = '\0'; csvFullData[i + 1] = '\0'; while (true) { if ((csvFullData[i] == '\n') || (csvFullData[i] == '\r')) return SQRESULT_ERROR; if (csvFullData[i] == '\"') break; i++; } csvFullData[i] = '\0'; } else { currentLine.push_back(&csvFullData[i + 1]); csvFullData[i] = '\0'; } } if ((csvFullData[i] == '\n') || (csvFullData[i] == '\r')) { csvFullData[i] = '\0'; if ((csvFullData[i + 1] == '\n') || (csvFullData[i + 1] == '\r')) { i++; csvFullData[i] = '\0'; } if (firstLine) { data->columnNames = currentLine; firstLine = false; } else { data->dataPointers.push_back(currentLine); } currentLine.clear(); if (i + 1 >= dataFile.size()) break; if (csvFullData[i + 1] == '\"') { currentLine.push_back(&csvFullData[i + 2]); csvFullData[i] = '\0'; csvFullData[i + 1] = '\0'; while (true) { if ((csvFullData[i] == '\n') || (csvFullData[i] == '\r')) return SQRESULT_ERROR; if (csvFullData[i] == '\"') break; i++; } csvFullData[i] = '\0'; } else { currentLine.push_back(&csvFullData[i + 1]); csvFullData[i] = '\0'; } } } if (currentLine.size() != 0) { if (firstLine) { data->columnNames = currentLine; } else { data->dataPointers.push_back(currentLine); } } data->name = (char*)malloc(256); strncpy(data->name, assetName, 256); csvData** dataPointer = (csvData**)g_pSquirrel->createuserdata(sqvm, sizeof(csvData*)); g_pSquirrel->setuserdatatypeid(sqvm, -1, customDatatableTypeId); *dataPointer = data; // vm->_stack[vm->_top -1]._VAL.asUserdata->releaseHook = datatableReleaseHook; cacheMap[assetName] = data; // spdlog::info("Loaded custom Datatable from file at {} with pointer {}", assetPath, (void*)data); result = SQRESULT_NOTNULL; } else if (Cvar_ns_prefer_datatable_from_disk->GetBool() && g_pPakLoadManager->LoadFile(assetName)) { result = g_pSquirrel->m_funcOriginals["GetDataTable"](sqvm); } else { spdlog::error("Datatable {} not found", assetPath); } } return result; } template SQRESULT GetDatatabeColumnByName(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableColumnByName"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableColumnByName"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } // spdlog::info("GetColumnByName form datatable with pointer {}",(void*)data); const char* searchName = g_pSquirrel->getstring(sqvm, 2); int col = 0; for (auto colName : data->columnNames) { if (!strcmp(colName, searchName)) break; col++; } if (col == data->columnNames.size()) col = -1; // spdlog::info("Datatable CoulumnName {} in column {}", std::string(searchName), col); g_pSquirrel->pushinteger(sqvm, col); return SQRESULT_NOTNULL; } template SQRESULT GetDatatabeRowCount(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableRowCount"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDatatableRowCount"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } g_pSquirrel->pushinteger(sqvm, data->dataPointers.size()); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableString(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableString"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableString"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int row = g_pSquirrel->getinteger(sqvm, 2); int col = g_pSquirrel->getinteger(sqvm, 3); if (row >= data->dataPointers.size() || col >= data->dataPointers[row].size()) { spdlog::info("row {} and col {} are outside of range row {} and col {}", row, col, data->dataPointers.size(), data->columnNames.size()); return SQRESULT_ERROR; } g_pSquirrel->pushstring(sqvm, data->dataPointers[row][col], -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableAsset(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableAsset"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableAsset"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int row = g_pSquirrel->getinteger(sqvm, 2); int col = g_pSquirrel->getinteger(sqvm, 3); if (row >= data->dataPointers.size() || col >= data->dataPointers[row].size()) { spdlog::info("row {} and col {} are outside of range row {} and col {}", row, col, data->dataPointers.size(), data->columnNames.size()); return SQRESULT_ERROR; } char* asset = data->dataPointers[row][col]; g_pSquirrel->pushasset(sqvm, asset, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableInt(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableInt"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int row = g_pSquirrel->getinteger(sqvm, 2); int col = g_pSquirrel->getinteger(sqvm, 3); if (row >= data->dataPointers.size() || col >= data->dataPointers[row].size()) { spdlog::info("row {} and col {} are outside of range row {} and col {}", row, col, data->dataPointers.size(), data->columnNames.size()); return SQRESULT_ERROR; } g_pSquirrel->pushinteger(sqvm, std::stoi(data->dataPointers[row][col])); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableFloat(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableFloat"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableFloat"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int row = g_pSquirrel->getinteger(sqvm, 2); int col = g_pSquirrel->getinteger(sqvm, 3); if (row >= data->dataPointers.size() || col >= data->dataPointers[row].size()) { spdlog::info("row {} and col {} are outside of range row {} and col {}", row, col, data->dataPointers.size(), data->columnNames.size()); return SQRESULT_ERROR; } g_pSquirrel->pushfloat(sqvm, std::stof(data->dataPointers[row][col])); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableBool(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableBool"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableBool"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int row = g_pSquirrel->getinteger(sqvm, 2); int col = g_pSquirrel->getinteger(sqvm, 3); if (row >= data->dataPointers.size() || col >= data->dataPointers[row].size()) { spdlog::info("row {} and col {} are outside of range row {} and col {}", row, col, data->dataPointers.size(), data->columnNames.size()); return SQRESULT_ERROR; } g_pSquirrel->pushbool(sqvm, std::stoi(data->dataPointers[row][col])); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableVector(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableVector"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableVector"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } float vector[3]; int row = g_pSquirrel->getinteger(sqvm, 2); int col = g_pSquirrel->getinteger(sqvm, 3); if (row >= data->dataPointers.size() || col >= data->dataPointers[row].size()) { spdlog::info( "row {} and col {} are outside of range row {} and col {}", row, col, data->dataPointers.size(), data->columnNames.size()); return SQRESULT_ERROR; } StringToVector(data->dataPointers[row][col], vector); g_pSquirrel->pushvector(sqvm, vector); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowMatchingStringValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowMatchingStringValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); const char* stringValue = g_pSquirrel->getstring(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (!strcmp(data->dataPointers[i][col], stringValue)) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowMatchingAssetValue(HSquirrelVM* sqvm) { // spdlog::info("start getDatatableRowMatchingAsset"); csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowMatchingAssetValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); const char* stringValue; g_pSquirrel->getasset(sqvm, 3, &stringValue); for (int i = 0; i < data->dataPointers.size(); i++) { if (!strcmp(data->dataPointers[i][col], stringValue)) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowMatchingFloatValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowMatchingFloatValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); float compareValue = g_pSquirrel->getfloat(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (compareValue == std::stof(data->dataPointers[i][col])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowMatchingIntValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowMatchingIntValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); int compareValue = g_pSquirrel->getinteger(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (compareValue == std::stoi(data->dataPointers[i][col])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowGreaterThanOrEqualToIntValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowGreaterThanOrEqualToIntValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); int compareValue = g_pSquirrel->getinteger(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (compareValue >= std::stoi(data->dataPointers[i][col])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowLessThanOrEqualToIntValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowLessThanOrEqualToIntValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); int compareValue = g_pSquirrel->getinteger(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (compareValue <= std::stoi(data->dataPointers[i][col])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowGreaterThanOrEqualToFloatValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowGreaterThanOrEqualToFloatValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); float compareValue = g_pSquirrel->getfloat(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (compareValue >= std::stof(data->dataPointers[i][col])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowLessThanOrEqualToFloatValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowLessThanOrEqualToFloatValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); int compareValue = g_pSquirrel->getfloat(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { if (compareValue <= std::stof(data->dataPointers[i][col])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT GetDataTableRowMatchingVectorValue(HSquirrelVM* sqvm) { csvData** dataPointer; long long typeId; g_pSquirrel->getuserdata(sqvm, 2, (void**)&dataPointer, &typeId); csvData* data = *dataPointer; if (typeId == vanillaDatatableTypeId) { return g_pSquirrel->m_funcOriginals["GetDataTableRowMatchingVectorValue"](sqvm); } if (typeId != customDatatableTypeId) { return SQRESULT_ERROR; } int col = g_pSquirrel->getinteger(sqvm, 2); float* compareValue = g_pSquirrel->getvector(sqvm, 3); for (int i = 0; i < data->dataPointers.size(); i++) { float dataTableVector[3]; StringToVector(data->dataPointers[i][col], dataTableVector); if ((dataTableVector[0] == compareValue[0]) && (dataTableVector[1] == compareValue[1]) && (dataTableVector[2] == compareValue[2])) { g_pSquirrel->pushinteger(sqvm, i); return SQRESULT_NOTNULL; } } g_pSquirrel->pushinteger(sqvm, -1); return SQRESULT_NOTNULL; } template SQRESULT DumpDataTable(HSquirrelVM* sqvm) { DataTable* datatable = (DataTable*)getDataTableStructure(sqvm); if (datatable == 0) { spdlog::info("datatable not loaded"); g_pSquirrel->pushinteger(sqvm, 1); return SQRESULT_NOTNULL; } // spdlog::info("Datatable size row = {} col = {}", datatable->rowAmount, datatable->columnAmount); // std::string header = std::string(datatable->columnInfo[0].name); spdlog::info(DataTableToString(datatable)); return SQRESULT_NULL; } template SQRESULT DumpDataTableToFile(HSquirrelVM* sqvm) { DataTable* datatable = (DataTable*)getDataTableStructure(sqvm); if (datatable == 0) { spdlog::info("datatable not loaded"); g_pSquirrel->pushinteger(sqvm, 1); return SQRESULT_NOTNULL; } // spdlog::info("Datatable size row = {} col = {}", datatable->rowAmount, datatable->columnAmount); // std::string header = std::string(datatable->columnInfo[0].name); const char* pathName = g_pSquirrel->getstring(sqvm, 2); std::ofstream ofs(pathName); std::string data = DataTableToString(datatable); ofs.write(data.c_str(), data.size()); ofs.close(); return SQRESULT_NULL; } std::string DataTableToString(DataTable* datatable) { std::string line = std::string(datatable->columnInfo[0].name); for (int col = 1; col < datatable->columnAmount; col++) { ColumnInfo* colInfo = &datatable->columnInfo[col]; line += "," + std::string(colInfo->name); } line += std::string("\n"); for (int row = 0; row < datatable->rowAmount; row++) { bool seperator = false; for (int col = 0; col < datatable->columnAmount; col++) { if (seperator) { line += std::string(","); } seperator = true; ColumnInfo* colInfo = &datatable->columnInfo[col]; switch (colInfo->type) { case 0: { bool input = *((bool*)(datatable->data + colInfo->offset + row * datatable->rowInfo)); if (input) { line += std::string("1"); } else { line += std::string("0"); } break; } case 1: { int input = *((int*)(datatable->data + colInfo->offset + row * datatable->rowInfo)); line += std::to_string(input); break; } case 2: { float input = *((float*)(datatable->data + colInfo->offset + row * datatable->rowInfo)); line += std::to_string(input); break; } case 3: { float* input = ((float*)(datatable->data + colInfo->offset + row * datatable->rowInfo)); char string[256]; snprintf(string, 256, "\"<%f,%f,%f>\"", input[0], input[1], input[2]); line += std::string(string); break; } case 4: case 5: case 6: { char* string = *((char**)(datatable->data + colInfo->offset + row * datatable->rowInfo)); line += "\"" + std::string(string) + "\""; break; } } } line += std::string("\n"); } return line; } void datatableReleaseHook(void* d, int size) { csvData* data = *(csvData**)d; free(data->fullData); delete data; } template void RegisterDataTableFunctions() { g_pSquirrel->AddFuncOverride("GetDataTable", GetDatatable); g_pSquirrel->AddFuncOverride("GetDataTableColumnByName", GetDatatabeColumnByName); g_pSquirrel->AddFuncOverride("GetDatatableRowCount", GetDatatabeRowCount); g_pSquirrel->AddFuncOverride("GetDataTableString", GetDataTableString); g_pSquirrel->AddFuncOverride("GetDataTableInt", GetDataTableInt); g_pSquirrel->AddFuncOverride("GetDataTableFloat", GetDataTableFloat); g_pSquirrel->AddFuncOverride("GetDataTableBool", GetDataTableBool); g_pSquirrel->AddFuncOverride("GetDataTableAsset", GetDataTableAsset); g_pSquirrel->AddFuncOverride("GetDataTableVector", GetDataTableVector); g_pSquirrel->AddFuncOverride("GetDataTableRowMatchingStringValue", GetDataTableRowMatchingStringValue); g_pSquirrel->AddFuncOverride("GetDataTableRowMatchingAssetValue", GetDataTableRowMatchingAssetValue); g_pSquirrel->AddFuncOverride("GetDataTableRowMatchingFloatValue", GetDataTableRowMatchingFloatValue); g_pSquirrel->AddFuncOverride("GetDataTableRowMatchingIntValue", GetDataTableRowMatchingIntValue); g_pSquirrel->AddFuncOverride("GetDataTableRowMatchingVectorValue", GetDataTableRowMatchingVectorValue); g_pSquirrel->AddFuncOverride( "GetDataTableRowLessThanOrEqualToFloatValue", GetDataTableRowLessThanOrEqualToFloatValue); g_pSquirrel->AddFuncOverride( "GetDataTableRowGreaterThanOrEqualToFloatValue", GetDataTableRowGreaterThanOrEqualToFloatValue); g_pSquirrel->AddFuncOverride("GetDataTableRowLessThanOrEqualToIntValue", GetDataTableRowLessThanOrEqualToIntValue); g_pSquirrel->AddFuncOverride( "GetDataTableRowGreaterThanOrEqualToFloatValue", GetDataTableRowGreaterThanOrEqualToIntValue); } ON_DLL_LOAD_RELIESON("server.dll", ServerScriptDatatables, ServerSquirrel, (CModule module)) { RegisterDataTableFunctions(); g_pSquirrel->AddFuncRegistration( "void", "DumpDataTable", "var", "Dumps rpak datatable contents to console", DumpDataTable); // g_pSquirrel->AddFuncRegistration( "void", "DumpDataTableToFile", "var,string", "Dumps datatable contents to // console", DumpDataTableToFile); getDataTableStructure = module.Offset(0x1250f0).As(); } ON_DLL_LOAD_RELIESON("client.dll", ClientScriptDatatables, ClientSquirrel, (CModule module)) { RegisterDataTableFunctions(); RegisterDataTableFunctions(); } ON_DLL_LOAD_RELIESON("engine.dll", GeneralScriptDataTables, ConCommand, (CModule module)) { Cvar_ns_prefer_datatable_from_disk = new ConVar("ns_prefer_datatable_from_disk", "0", FCVAR_NONE, "whether datatables from disk overwrite rpak datatables"); if (Tier0::CommandLine()->CheckParm("-nopakdedi")) { Cvar_ns_prefer_datatable_from_disk->SetValue(true); } }