diff --git a/amxmodx/AMBuilder b/amxmodx/AMBuilder index 034ff488..78389dcf 100644 --- a/amxmodx/AMBuilder +++ b/amxmodx/AMBuilder @@ -92,6 +92,8 @@ binary.sources = [ 'CFlagManager.cpp', 'datastructs.cpp', 'trie_natives.cpp', + 'CDataPack.cpp', + 'datapacks.cpp' ] if builder.target_platform == 'windows': diff --git a/amxmodx/CDataPack.cpp b/amxmodx/CDataPack.cpp new file mode 100644 index 00000000..8e6c45b7 --- /dev/null +++ b/amxmodx/CDataPack.cpp @@ -0,0 +1,259 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "CDataPack.h" + +#define DATAPACK_INITIAL_SIZE 512 + +CDataPack::CDataPack() +{ + m_pBase = (char *)malloc(DATAPACK_INITIAL_SIZE); + m_capacity = DATAPACK_INITIAL_SIZE; + Initialize(); +} + +CDataPack::~CDataPack() +{ + free(m_pBase); +} + +void CDataPack::Initialize() +{ + m_curptr = m_pBase; + m_size = 0; +} + +void CDataPack::CheckSize(size_t typesize) +{ + if (m_curptr - m_pBase + typesize <= m_capacity) + { + return; + } + + size_t pos = m_curptr - m_pBase; + do + { + m_capacity *= 2; + m_pBase = (char *)realloc(m_pBase, m_capacity); + m_curptr = m_pBase + pos; + } while (m_curptr - m_pBase + typesize > m_capacity); +} + +void CDataPack::ResetSize() +{ + m_size = 0; +} + +size_t CDataPack::CreateMemory(size_t size, void **addr) +{ + CheckSize(sizeof(size_t) + size); + size_t pos = m_curptr - m_pBase; + + *(size_t *)m_curptr = size; + m_curptr += sizeof(size_t); + + if (addr) + { + *addr = m_curptr; + } + + m_curptr += size; + m_size += sizeof(size_t) + size; + + return pos; +} + +void CDataPack::PackCell(cell cells) +{ + CheckSize(sizeof(size_t) + sizeof(cell)); + + *(size_t *)m_curptr = sizeof(cell); + m_curptr += sizeof(size_t); + + *(cell *)m_curptr = cells; + m_curptr += sizeof(cell); + + m_size += sizeof(size_t) + sizeof(cell); +} + +void CDataPack::PackFloat(float val) +{ + CheckSize(sizeof(size_t) + sizeof(float)); + + *(size_t *)m_curptr = sizeof(float); + m_curptr += sizeof(size_t); + + *(float *)m_curptr = val; + m_curptr += sizeof(float); + + m_size += sizeof(size_t) + sizeof(float); +} + +void CDataPack::PackString(const char *string) +{ + size_t len = strlen(string); + size_t maxsize = sizeof(size_t) + len + 1; + CheckSize(maxsize); + + // Pack the string length first for buffer overrun checking. + *(size_t *)m_curptr = len; + m_curptr += sizeof(size_t); + + // Now pack the string. + memcpy(m_curptr, string, len); + m_curptr[len] = '\0'; + m_curptr += len + 1; + + m_size += maxsize; +} + +void CDataPack::Reset() const +{ + m_curptr = m_pBase; +} + +size_t CDataPack::GetPosition() const +{ + return static_cast(m_curptr - m_pBase); +} + +bool CDataPack::SetPosition(size_t pos) const +{ + if (pos > m_size-1) + { + return false; + } + m_curptr = m_pBase + pos; + + return true; +} + +cell CDataPack::ReadCell() const +{ + if (!IsReadable(sizeof(size_t) + sizeof(cell))) + { + return 0; + } + if (*reinterpret_cast(m_curptr) != sizeof(cell)) + { + return 0; + } + + m_curptr += sizeof(size_t); + + cell val = *reinterpret_cast(m_curptr); + m_curptr += sizeof(cell); + return val; +} + +float CDataPack::ReadFloat() const +{ + if (!IsReadable(sizeof(size_t) + sizeof(float))) + { + return 0; + } + if (*reinterpret_cast(m_curptr) != sizeof(float)) + { + return 0; + } + + m_curptr += sizeof(size_t); + + float val = *reinterpret_cast(m_curptr); + m_curptr += sizeof(float); + return val; +} + +bool CDataPack::IsReadable(size_t bytes) const +{ + return (bytes + (m_curptr - m_pBase) > m_size) ? false : true; +} + +const char *CDataPack::ReadString(size_t *len) const +{ + if (!IsReadable(sizeof(size_t))) + { + return NULL; + } + + size_t real_len = *(size_t *)m_curptr; + + m_curptr += sizeof(size_t); + char *str = (char *)m_curptr; + + if ((strlen(str) != real_len) || !(IsReadable(real_len+1))) + { + return NULL; + } + + if (len) + { + *len = real_len; + } + + m_curptr += real_len + 1; + + return str; +} + +void *CDataPack::GetMemory() const +{ + return m_curptr; +} + +void *CDataPack::ReadMemory(size_t *size) const +{ + if (!IsReadable(sizeof(size_t))) + { + return NULL; + } + + size_t bytecount = *(size_t *)m_curptr; + m_curptr += sizeof(size_t); + + if (!IsReadable(bytecount)) + { + return NULL; + } + + void *ptr = m_curptr; + + if (size) + { + *size = bytecount; + } + + m_curptr += bytecount; + + return ptr; +} diff --git a/amxmodx/CDataPack.h b/amxmodx/CDataPack.h new file mode 100644 index 00000000..491b2920 --- /dev/null +++ b/amxmodx/CDataPack.h @@ -0,0 +1,244 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_CDATAPACK_H_ +#define _INCLUDE_SOURCEMOD_CDATAPACK_H_ + +#include "amxmodx.h" + +/** + * @brief Contains functions for packing data abstractly to/from plugins. + */ +class CDataPack +{ +public: + CDataPack(); + ~CDataPack(); + +public: + /** + * @brief Resets the position in the data stream to the beginning. + */ + void Reset() const; + + /** + * @brief Retrieves the current stream position. + * + * @return Index into the stream. + */ + size_t GetPosition() const; + + /** + * @brief Sets the current stream position. + * + * @param pos Index to set the stream at. + * @return True if succeeded, false if out of bounds. + */ + bool SetPosition(size_t pos) const; + + /** + * @brief Reads one cell from the data stream. + * + * @return A cell read from the current position. + */ + cell ReadCell() const; + + /** + * @brief Reads one float from the data stream. + * + * @return A float read from the current position. + */ + float ReadFloat() const; + + /** + * @brief Returns whether or not a specified number of bytes from the current stream + * position to the end can be read. + * + * @param bytes Number of bytes to simulate reading. + * @return True if can be read, false otherwise. + */ + bool IsReadable(size_t bytes) const; + + /** + * @brief Reads a string from the data stream. + * + * @param len Optional pointer to store the string length. + * @return Pointer to the string, or NULL if out of bounds. + */ + const char *ReadString(size_t *len) const; + + /** + * @brief Reads the current position as a generic address. + * + * @return Pointer to the memory. + */ + void *GetMemory() const; + + /** + * @brief Reads the current position as a generic data type. + * + * @param size Optional pointer to store the size of the data type. + * @return Pointer to the data, or NULL if out of bounds. + */ + void *ReadMemory(size_t *size) const; + +public: + /** + * @brief Resets the used size of the stream back to zero. + */ + void ResetSize(); + + /** + * @brief Packs one cell into the data stream. + * + * @param cell Cell value to write. + */ + void PackCell(cell cells); + + /** + * @brief Packs one float into the data stream. + * + * @param val Float value to write. + */ + void PackFloat(float val); + + /** + * @brief Packs one string into the data stream. + * The length is recorded as well for buffer overrun protection. + * + * @param string String to write. + */ + void PackString(const char *string); + + /** + * @brief Creates a generic block of memory in the stream. + * + * Note that the pointer it returns can be invalidated on further + * writing, since the stream size may grow. You may need to double back + * and fetch the pointer again. + * + * @param size Size of the memory to create in the stream. + * @param addr Optional pointer to store the relocated memory address. + * @return Current position of the stream beforehand. + */ + size_t CreateMemory(size_t size, void **addr); + +public: + void Initialize(); + +private: + void CheckSize(size_t sizetype); + +private: + char *m_pBase; + mutable char *m_curptr; + size_t m_capacity; + size_t m_size; +}; + +class CDataPackHandles +{ +private: + CVector m_packs; + +public: + CDataPackHandles() {} + ~CDataPackHandles() + { + this->clear(); + } + + void clear() + { + for (size_t i = 0; i < m_packs.size(); i++) + { + if (m_packs[i] != NULL) + { + delete m_packs[i]; + } + } + + m_packs.clear(); + } + + CDataPack *lookup(int handle) + { + handle--; + + if (handle < 0 || handle >= static_cast(m_packs.size())) + { + return NULL; + } + + return m_packs[handle]; + } + + int create() + { + for (size_t i = 0; i < m_packs.size(); ++i) + { + if (m_packs[i] == NULL) + { + // reuse handle + m_packs[i] = new CDataPack; + + return static_cast(i) + 1; + } + } + m_packs.push_back(new CDataPack); + return m_packs.size(); + } + + bool destroy(int handle) + { + handle--; + + if (handle < 0 || handle >= static_cast(m_packs.size())) + { + return false; + } + + if (m_packs[handle] == NULL) + { + return false; + } + + delete m_packs[handle]; + m_packs[handle] = NULL; + + return true; + } +}; + +extern CDataPackHandles g_DataPackHandles; +extern AMX_NATIVE_INFO g_DatapackNatives[]; + +#endif //_INCLUDE_SOURCEMOD_CDATAPACK_H_ diff --git a/amxmodx/Makefile b/amxmodx/Makefile index fceded03..71581592 100755 --- a/amxmodx/Makefile +++ b/amxmodx/Makefile @@ -21,7 +21,7 @@ OBJECTS = meta_api.cpp CFile.cpp CVault.cpp vault.cpp float.cpp file.cpp modules CMenu.cpp util.cpp amx.cpp amxdbg.cpp natives.cpp newmenus.cpp debugger.cpp \ optimizer.cpp format.cpp messages.cpp libraries.cpp vector.cpp sorting.cpp \ amxmod_compat.cpp nongpl_matches.cpp CFlagManager.cpp datastructs.cpp \ - trie_natives.cpp + trie_natives.cpp CDataPack.cpp datapacks.cpp ############################################## ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### diff --git a/amxmodx/datapacks.cpp b/amxmodx/datapacks.cpp new file mode 100644 index 00000000..baab9c37 --- /dev/null +++ b/amxmodx/datapacks.cpp @@ -0,0 +1,250 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "CDataPack.h" + +CDataPackHandles g_DataPackHandles; + +static cell AMX_NATIVE_CALL CreateDataPack(AMX* amx, cell* params) +{ + return static_cast(g_DataPackHandles.create()); +} + +static cell AMX_NATIVE_CALL WritePackCell(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + d->PackCell(params[2]); + + return 1; +} + +static cell AMX_NATIVE_CALL WritePackFloat(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + d->PackFloat(amx_ctof(params[2])); + + return 1; +} + +static cell AMX_NATIVE_CALL WritePackString(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + int len; + const char *str = get_amxstring(amx, params[2], 0, len); + + d->PackString(str); + + return len; +} + +static cell AMX_NATIVE_CALL ReadPackCell(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + if (!d->IsReadable(sizeof(size_t) + sizeof(cell))) + { + LogError(amx, AMX_ERR_NATIVE, "DataPack operation is out of bounds."); + return 0; + } + + return d->ReadCell(); +} + +static cell AMX_NATIVE_CALL ReadPackFloat(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + if (!d->IsReadable(sizeof(size_t) + sizeof(float))) + { + LogError(amx, AMX_ERR_NATIVE, "DataPack operation is out of bounds."); + return 0; + } + + float value = d->ReadFloat(); + + return amx_ftoc(value); +} + +static cell AMX_NATIVE_CALL ReadPackString(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + const char *str; + if (!(str = d->ReadString(NULL))) + { + LogError(amx, AMX_ERR_NATIVE, "DataPack operation is out of bounds."); + return 0; + } + + return set_amxstring(amx, params[2], str, params[3]); +} + +static cell AMX_NATIVE_CALL ResetPack(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + d->Reset(); + + if (params[2]) + { + d->ResetSize(); + } + + return 1; +} + +static cell AMX_NATIVE_CALL GetPackPosition(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + return static_cast(d->GetPosition()); +} + +static cell AMX_NATIVE_CALL SetPackPosition(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + if (!d->SetPosition(params[2])) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid DataPack position, %d is out of bounds", params[2]); + return 0; + } + + return 1; +} + +static cell AMX_NATIVE_CALL IsPackReadable(AMX* amx, cell* params) +{ + CDataPack *d = g_DataPackHandles.lookup(params[1]); + + if (d == NULL) + { + LogError(amx, AMX_ERR_NATIVE, "Invalid datapack handle provided (%d)", params[1]); + return 0; + } + + return d->IsReadable(params[2]) ? 1 : 0; +} + +static cell AMX_NATIVE_CALL DestroyDataPack(AMX* amx, cell* params) +{ + cell *ptr = get_amxaddr(amx, params[1]); + + CDataPack *d = g_DataPackHandles.lookup(*ptr); + + if (d == NULL) + { + return 0; + } + + if (g_DataPackHandles.destroy(*ptr)) + { + *ptr = 0; + return 1; + } + + return 0; +} + + +AMX_NATIVE_INFO g_DatapackNatives[] = +{ + { "CreateDataPack", CreateDataPack }, + { "WritePackCell", WritePackCell }, + { "WritePackFloat", WritePackFloat }, + { "WritePackString", WritePackString }, + { "ReadPackCell", ReadPackCell }, + { "ReadPackFloat", ReadPackFloat }, + { "ReadPackString", ReadPackString }, + { "ResetPack", ResetPack }, + { "GetPackPosition", GetPackPosition }, + { "SetPackPosition", SetPackPosition }, + { "IsPackReadable", IsPackReadable }, + { "DestroyDataPack", DestroyDataPack }, + {NULL, NULL} +}; diff --git a/amxmodx/meta_api.cpp b/amxmodx/meta_api.cpp index 0e5e194c..204f4069 100755 --- a/amxmodx/meta_api.cpp +++ b/amxmodx/meta_api.cpp @@ -52,6 +52,7 @@ #include "CFlagManager.h" #include #include "trie_natives.h" +#include "CDataPack.h" plugin_info_t Plugin_info = { @@ -413,6 +414,8 @@ int C_Spawn(edict_t *pent) VectorHolder.clear(); g_TrieHandles.clear(); + g_DataPackHandles.clear(); + char map_pluginsfile_path[256]; char prefixed_map_pluginsfile[256]; char configs_dir[256]; diff --git a/amxmodx/modules.cpp b/amxmodx/modules.cpp index 23e6a6ff..c7f8e0cf 100755 --- a/amxmodx/modules.cpp +++ b/amxmodx/modules.cpp @@ -53,6 +53,7 @@ #include "messages.h" #include "amxmod_compat.h" #include "trie_natives.h" +#include "CDataPack.h" CList g_modules; CList g_loadedscripts; @@ -587,6 +588,7 @@ int set_amxnatives(AMX* amx, char error[128]) amx_Register(amx, g_SortNatives, -1); amx_Register(amx, g_DataStructNatives, -1); amx_Register(amx, trie_Natives, -1); + amx_Register(amx, g_DatapackNatives, -1); if (amx->flags & AMX_FLAG_OLDFILE) { diff --git a/amxmodx/msvc10/amxmodx_mm.vcxproj b/amxmodx/msvc10/amxmodx_mm.vcxproj index c9ab3453..814224ba 100644 --- a/amxmodx/msvc10/amxmodx_mm.vcxproj +++ b/amxmodx/msvc10/amxmodx_mm.vcxproj @@ -295,6 +295,7 @@ true + @@ -307,6 +308,7 @@ + @@ -354,6 +356,7 @@ + @@ -397,6 +400,7 @@ + diff --git a/amxmodx/msvc10/amxmodx_mm.vcxproj.filters b/amxmodx/msvc10/amxmodx_mm.vcxproj.filters index 5e572549..88fe2a64 100644 --- a/amxmodx/msvc10/amxmodx_mm.vcxproj.filters +++ b/amxmodx/msvc10/amxmodx_mm.vcxproj.filters @@ -174,6 +174,12 @@ Source Files + + Source Files + + + Source Files + @@ -314,6 +320,9 @@ SDK + + Header Files + @@ -423,6 +432,9 @@ Pawn Includes + + Pawn Includes + diff --git a/plugins/include/amxmodx.inc b/plugins/include/amxmodx.inc index 6478ef94..ba84ce51 100755 --- a/plugins/include/amxmodx.inc +++ b/plugins/include/amxmodx.inc @@ -23,6 +23,7 @@ #include #include #include +#include #include /* Function is called just after server activation. diff --git a/plugins/include/datapack.inc b/plugins/include/datapack.inc new file mode 100644 index 00000000..c141b088 --- /dev/null +++ b/plugins/include/datapack.inc @@ -0,0 +1,130 @@ + +#if defined _datapack_included + #endinput +#endif +#define _datapack_included + +/** + * DataPacks are a way to store and move around various types of data in AMX Mod X Scripting. + * Since some things are not possible in AMX Mod X, such as a function consuming a String, + * DataPacks help us get these Strings and other items where they need to go. + */ + +enum DataPack +{ + Invalid_DataPack = 0 +}; + +/** + * Creates a new data pack. + * + * @return A Handle to the data pack. + */ +native DataPack:CreateDataPack(); + +/** + * Packs a normal cell into a data pack. + * + * @param pack Handle to the data pack. + * @param cell Cell to add. + * @noreturn + * @error Invalid handle. + */ +native WritePackCell(DataPack:pack, cell); + +/** + * Packs a float into a data pack. + * + * @param pack Handle to the data pack. + * @param val Float to add. + * @noreturn + * @error Invalid handle. + */ +native WritePackFloat(DataPack:pack, Float:val); + +/** + * Packs a string into a data pack. + * + * @param pack Handle to the data pack. + * @param str String to add. + * @return Length of copied string. + * @error Invalid handle. + */ +native WritePackString(DataPack:pack, const str[]); + +/** + * Reads a cell from a data pack. + * + * @param pack Handle to the data pack. + * @return Cell value. + * @error Invalid handle, or bounds error. + */ +native ReadPackCell(DataPack:pack); + +/** + * Reads a float from a data pack. + * + * @param pack Handle to the data pack. + * @return Float value. + * @error Invalid handle, or bounds error. + */ +native Float:ReadPackFloat(DataPack:pack); + +/** + * Reads a string from a data pack. + * + * @param pack Handle to the data pack. + * @param buffer Destination string buffer. + * @param maxlen Maximum length of output string buffer. + * @return Length of output string. + * @error Invalid handle, or bounds error. + */ +native ReadPackString(DataPack:pack, buffer[], maxlen); + +/** + * Resets the position in a data pack. + * + * @param pack Handle to the data pack. + * @param clear If true, clears the contained data. + * @noreturn + * @error Invalid handle. + */ +native ResetPack(DataPack:pack, bool:clear=false); + +/** + * Returns the read or write position in a data pack. + * + * @param pack Handle to the data pack. + * @return Numerical position in the data pack. + * @error Invalid handle. + */ +native GetPackPosition(DataPack:pack); + +/** + * Sets the read/write position in a data pack. + * + * @param pack Handle to the data pack. + * @param position New position to set. + * @noreturn + * @error Invalid handle, or position is beyond the pack bounds. + */ +native SetPackPosition(DataPack:pack, position); + +/** + * Returns whether or not a specified number of bytes from the data pack + * position to the end can be read. + * + * @param pack Handle to the data pack. + * @param bytes Number of bytes to simulate reading. + * @return True if can be read, false otherwise. + * @error Invalid handle. + */ +native bool:IsPackReadable(DataPack:pack, bytes); + +/** + * Disposes of a data pack. + * + * @param pack Handle to the data pack. + * @return True if disposed, false otherwise. + */ +native DestroyDataPack(&DataPack:pack); diff --git a/plugins/testsuite/datapack_test.sma b/plugins/testsuite/datapack_test.sma new file mode 100644 index 00000000..1e17bc48 --- /dev/null +++ b/plugins/testsuite/datapack_test.sma @@ -0,0 +1,82 @@ +#include + +new failcount; +new passcount; + +public plugin_init() +{ + register_plugin("Datapack Test", AMXX_VERSION_STR, "AMXX Dev Team"); + register_srvcmd("datapacktest", "datapacktest"); +} + +test(const testname[], bool:pass) +{ + if (!pass) + { + server_print("[FAIL] %s", testname); + failcount++; + } + else + { + server_print("[PASS] %s", testname); + passcount++; + } +} + +stock done() +{ + server_print("Finished. %d tests, %d failed", failcount + passcount, failcount); +} + +public datapacktest() +{ + failcount = 0; + passcount = 0; + + new DataPack:pack = CreateDataPack(); + new DataPack:oldPack = pack; // Makes sure that the trie handle system recycles old handles + + new refCell = 23; + new Float:refFloat = 42.42; + new refString[] = "I'm a little teapot."; + + // Write + WritePackCell(pack, refCell); // 8 + WritePackString(pack, refString); // 25 (sizeof string + 4) + WritePackFloat(pack, refFloat); // 8 + + test("Position #1 test", .pass = GetPackPosition(pack) == 41); + test("Readable #1 test", .pass = !IsPackReadable(pack, 41)); + + //resets the index to the beginning, necessary for read. + ResetPack(pack); + + test("Position #2 test", .pass = GetPackPosition(pack) == 0 ); + test("Readable #2 test", .pass = IsPackReadable(pack, 15)); + + // Read + new cellValue = ReadPackCell(pack); + new buffer[1024]; + ReadPackString(pack, buffer, 1024); + new Float:floatvalue = ReadPackFloat(pack); + + test("Cell test", .pass = cellValue == refCell); + test("String test", .pass = bool:equal(buffer, refString)); + test("Float test #1", .pass = floatvalue == refFloat); + + SetPackPosition(pack, 33); + test("Set Position test", .pass = GetPackPosition(pack) == 33); + + WritePackFloat(pack, refFloat + 1); + SetPackPosition(pack, 33); + test("Float test #2", .pass = ReadPackFloat(pack) == refFloat + 1); + + ResetPack(pack, .clear = true); + test("Clear test", .pass = !IsPackReadable(pack, 15)); + + DestroyDataPack(pack); + + test("Recycle handles", CreateDataPack() == oldPack); + + done(); +}