├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── AMXConfig.cmake └── AddSAMPPlugin.cmake ├── include └── rustext.inc ├── lib ├── RakNet │ ├── BitStream.cpp │ ├── BitStream.h │ └── NetworkTypes.h ├── sdk │ ├── amx │ │ ├── amx.h │ │ ├── amx2.h │ │ ├── getch.c │ │ ├── getch.h │ │ └── sclinux.h │ ├── amxplugin.cpp │ ├── amxplugin2.cpp │ ├── plugin.h │ └── plugincommon.h └── urmem.hpp ├── pawn.json └── src ├── common.hpp ├── converter.cpp ├── converter.hpp ├── exports.def ├── hooks.cpp ├── hooks.hpp ├── main.cpp ├── main.hpp ├── natives.cpp ├── natives.hpp ├── russifier.cpp └── russifier.hpp /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pwn linguist-language=Pawn 2 | *.inc linguist-language=Pawn 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Makefile 6 | cmake_install.cmake 7 | install_manifest.txt 8 | CTestTestfile.cmake 9 | rustext.dir/* 10 | Win32/* 11 | *.so 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (rustext) 2 | set (PLUGIN_VERSION "2.0.10") 3 | 4 | cmake_minimum_required (VERSION 2.8) 5 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 6 | 7 | include (AMXConfig) 8 | include (AddSAMPPlugin) 9 | 10 | include_directories ( 11 | ${CMAKE_CURRENT_SOURCE_DIR}/ 12 | ${CMAKE_CURRENT_SOURCE_DIR}/src 13 | ${CMAKE_CURRENT_SOURCE_DIR}/lib 14 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdk 15 | ${CMAKE_CURRENT_SOURCE_DIR}/lib/sdk/amx 16 | ) 17 | 18 | add_definitions (-DPLUGIN_NAME="${PROJECT_NAME}" -DPLUGIN_VERSION="${PLUGIN_VERSION}") 19 | 20 | set (CMAKE_SHARED_LIBRARY_PREFIX "") 21 | 22 | if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 23 | set (CMAKE_CXX_FLAGS "-std=c++11") 24 | endif () 25 | 26 | add_samp_plugin (${PROJECT_NAME} 27 | lib/sdk/amxplugin.cpp 28 | lib/sdk/amxplugin2.cpp 29 | lib/RakNet/BitStream.cpp 30 | src/main.cpp 31 | src/main.hpp 32 | src/natives.cpp 33 | src/natives.hpp 34 | src/converter.cpp 35 | src/converter.hpp 36 | src/russifier.cpp 37 | src/russifier.hpp 38 | src/hooks.cpp 39 | src/hooks.hpp 40 | src/common.hpp 41 | src/exports.def 42 | ) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Sergei Marochkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rustext 2 | Fix Russian text plugin for SA-MP: GameText's, TextDraw's and Menu's. Default russifier type is SanLtd. 3 | 4 | # Usage 5 | Just add rustext[.so] in your **server.cfg** to **plugins** key. If you want to get additional functionality you should copy **rustext.inc** to your libraries directory and add `#include "rustext"` in your script. 6 | 7 | # Russifiers 8 | You can download any of the usеd russifiers [here](https://mega.nz/#F!tVhlhDCT!FK1xFcBalTA0ySY_JsryMg). 9 | 10 | # Examples 11 | Here you can see how it works with SanLtd russifier: 12 | 13 | ![sanltd](https://cloud.githubusercontent.com/assets/1020099/18914954/db9b4ade-8597-11e6-8d72-783cfd24fb99.png) 14 | 15 | Here you can see how it works with Ukraine localization: 16 | 17 | ![ukraine](https://cloud.githubusercontent.com/assets/1020099/18914955/db9d95dc-8597-11e6-9c2b-6ae5cc05bc72.png) 18 | 19 | # Example of usage 20 | Base script for setting player russifier: 21 | ```Pawn 22 | #include 23 | #include 24 | 25 | static 26 | TextRusTD[RussifierType]; 27 | 28 | const 29 | Float:TEXT_BASE_X = 150.0, 30 | Float:TEXT_BASE_Y = 150.0, 31 | TEXT_RUSSIFIERS_COUNT = 7; // only Russian localizations 32 | 33 | public OnGameModeInit() 34 | { 35 | for (new i; i < TEXT_RUSSIFIERS_COUNT; i++) { 36 | TextRusTD[RussifierType:i] = _:TextDrawCreate(TEXT_BASE_X, 37 | TEXT_BASE_Y + 15.0 * i, 38 | "Я могу прочитать этот текст, это мой русификатор"); 39 | TextDrawSetSelectable(Text:TextRusTD[RussifierType:i], 1); 40 | TextDrawTextSize(Text:TextRusTD[RussifierType:i], 600.0, 10.0); 41 | } 42 | return 1; 43 | } 44 | 45 | public OnPlayerSpawn(playerid) 46 | { 47 | for (new i; i < TEXT_RUSSIFIERS_COUNT; i++) { 48 | SetPlayerRussifierType(playerid, RussifierType:i); 49 | TextDrawShowForPlayer(playerid, Text:TextRusTD[RussifierType:i]); 50 | } 51 | SelectTextDraw(playerid, 0xAA3333FF); 52 | return 1; 53 | } 54 | 55 | public OnPlayerClickTextDraw(playerid, Text:clickedid) 56 | { 57 | for (new i; i < TEXT_RUSSIFIERS_COUNT; i++) { 58 | if (TextRusTD[RussifierType:i] == _:clickedid) { 59 | SetPlayerRussifierType(playerid, RussifierType:i); 60 | CancelSelectTextDraw(playerid); 61 | } 62 | TextDrawHideForPlayer(playerid, Text:TextRusTD[RussifierType:i]); 63 | } 64 | return 0; 65 | } 66 | ``` 67 | 68 | Or you can use [russifier](https://github.com/Open-GTO/russifier) library for this. 69 | ![russifier](https://user-images.githubusercontent.com/1020099/42131912-a08b08b8-7d15-11e8-93f6-aa0478d4b095.png) 70 | 71 | # Types 72 | 73 | ```Pawn 74 | enum RussifierType { 75 | RussifierType_Disabled = -1, 76 | RussifierType_SanLtd = 0, 77 | RussifierType_OneC, 78 | RussifierType_Rush, 79 | RussifierType_Unknown1, 80 | RussifierType_Unknown2, 81 | RussifierType_Unknown3, 82 | RussifierType_MG, 83 | RussifierType_Community, 84 | RussifierType_Ukraininan, 85 | RussifierType_Hungarian, 86 | } 87 | ``` 88 | 89 | # Natives 90 | 91 | ```Pawn 92 | native GetRussifierVersion(version[], const size = sizeof(version)); 93 | native GetRussifierText(RussifierType:type, string[], string_return[], const size = sizeof(string_return)); 94 | 95 | native SetPlayerRussifierType(playerid, RussifierType:type); 96 | native RussifierType:GetPlayerRussifierType(playerid); 97 | 98 | native SetDefaultRussifierType(RussifierType:type); 99 | native RussifierType:GetDefaultRussifierType(); 100 | ``` 101 | 102 | # Build 103 | ```bash 104 | git clone https://github.com/ziggi/rustext.git 105 | mkdir build 106 | cd build 107 | cmake .. 108 | make 109 | ``` 110 | 111 | # Build (Visual Studio) 112 | ```bash 113 | cd FCNPC 114 | mkdir build 115 | cd build 116 | cmake .. -G "Visual Studio 12" 117 | ``` 118 | 119 | # Thanks 120 | 121 | Big thanks to [urShadow](https://github.com/urShadow) for [urmem](https://github.com/urShadow/urmem) and [Pawn.RakNet](https://github.com/urShadow/Pawn.RakNet) on which based rakserver hook code. -------------------------------------------------------------------------------- /cmake/AMXConfig.cmake: -------------------------------------------------------------------------------- 1 | include(CheckIncludeFile) 2 | 3 | check_include_file(alloca.h HAVE_ALLOCA_H) 4 | if(HAVE_ALLOCA_H) 5 | add_definitions(-DHAVE_ALLOCA_H) 6 | endif() 7 | check_include_file(inttypes.h HAVE_INTTYPES_H) 8 | if(HAVE_INTTYPES_H) 9 | add_definitions(-DHAVE_INTTYPES_H) 10 | endif() 11 | check_include_file(malloc.h HAVE_MALLOC_H) 12 | if(HAVE_MALLOC_H) 13 | add_definitions(-DHAVE_MALLOC_H) 14 | endif() 15 | check_include_file(stdint.h HAVE_STDINT_H) 16 | if(HAVE_STDINT_H) 17 | add_definitions(-DHAVE_STDINT_H) 18 | endif() 19 | -------------------------------------------------------------------------------- /cmake/AddSAMPPlugin.cmake: -------------------------------------------------------------------------------- 1 | function(add_samp_plugin name) 2 | add_library(${name} MODULE ${ARGN}) 3 | 4 | set_target_properties(${name} PROPERTIES PREFIX "") 5 | 6 | if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 7 | set_property(TARGET ${name} APPEND_STRING PROPERTY COMPILE_FLAGS " -m32") 8 | set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS " -m32") 9 | endif() 10 | 11 | if(CMAKE_COMPILER_IS_GNUCXX) 12 | set_property(TARGET ${name} APPEND_STRING PROPERTY 13 | COMPILE_FLAGS " -Wno-attributes") 14 | endif() 15 | 16 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 17 | set_property(TARGET ${name} APPEND_STRING PROPERTY 18 | COMPILE_FLAGS " -Wno-ignored-attributes") 19 | endif() 20 | 21 | if(WIN32 AND CMAKE_COMPILER_IS_GNUCC) 22 | set_property(TARGET ${name} APPEND_STRING PROPERTY 23 | LINK_FLAGS " -Wl,--enable-stdcall-fixup") 24 | endif() 25 | 26 | if(CYGWIN) 27 | set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS "WIN32") 28 | set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--kill-at") 29 | elseif(UNIX AND NOT WIN32 AND NOT APPLE) 30 | set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS "LINUX") 31 | endif() 32 | 33 | if(MINGW) 34 | # Work around missing #include in /amx/amx.h. 35 | set_property(TARGET ${name} APPEND_STRING PROPERTY COMPILE_FLAGS " -include stddef.h") 36 | endif() 37 | endfunction() 38 | -------------------------------------------------------------------------------- /include/rustext.inc: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext include 3 | Author: ziggi 4 | */ 5 | 6 | #if defined _rustext_included 7 | #endinput 8 | #endif 9 | #define _rustext_included 10 | #pragma library rustext 11 | 12 | // Enums 13 | enum RussifierType { 14 | RussifierType_Disabled = -1, 15 | RussifierType_SanLtd = 0, 16 | RussifierType_OneC, 17 | RussifierType_Rush, 18 | RussifierType_Unknown1, 19 | RussifierType_Unknown2, 20 | RussifierType_Unknown3, 21 | RussifierType_MG, 22 | RussifierType_Community, 23 | RussifierType_Ukrainian, 24 | RussifierType_Hungarian, 25 | } 26 | 27 | // RussifierType_Ukraine is deprecated 28 | #define RussifierType_Ukraine RussifierType_Ukrainian 29 | 30 | // Natives 31 | native GetRussifierVersion(version[], const size = sizeof(version)); 32 | native GetRussifierText(RussifierType:type, string[], string_return[], const size = sizeof(string_return)); 33 | 34 | native SetPlayerRussifierType(playerid, RussifierType:type); 35 | native RussifierType:GetPlayerRussifierType(playerid); 36 | 37 | native SetDefaultRussifierType(RussifierType:type); 38 | native RussifierType:GetDefaultRussifierType(); 39 | 40 | // Update checker 41 | #if !defined HTTP 42 | #tryinclude 43 | #endif 44 | 45 | #if !defined HTTP 46 | #tryinclude "a_http" 47 | #endif 48 | 49 | #if !defined HTTP 50 | #tryinclude "../a_http" 51 | #endif 52 | 53 | #if defined HTTP && !defined RUSTEXT_DISABLE_VERSION_CHECK 54 | #if defined FILTERSCRIPT 55 | public OnFilterScriptInit() 56 | { 57 | RusText_SendVersionRequest(); 58 | 59 | #if defined RusText_OnFilterScriptInit 60 | return RusText_OnFilterScriptInit(); 61 | #else 62 | return 1; 63 | #endif 64 | } 65 | #if defined _ALS_OnFilterScriptInit 66 | #undef OnFilterScriptInit 67 | #else 68 | #define _ALS_OnFilterScriptInit 69 | #endif 70 | 71 | #define OnFilterScriptInit RusText_OnFilterScriptInit 72 | #if defined RusText_OnFilterScriptInit 73 | forward RusText_OnFilterScriptInit(); 74 | #endif 75 | #else 76 | public OnGameModeInit() 77 | { 78 | RusText_SendVersionRequest(); 79 | 80 | #if defined RusText_OnGameModeInit 81 | return RusText_OnGameModeInit(); 82 | #else 83 | return 1; 84 | #endif 85 | } 86 | #if defined _ALS_OnGameModeInit 87 | #undef OnGameModeInit 88 | #else 89 | #define _ALS_OnGameModeInit 90 | #endif 91 | 92 | #define OnGameModeInit RusText_OnGameModeInit 93 | #if defined RusText_OnGameModeInit 94 | forward RusText_OnGameModeInit(); 95 | #endif 96 | #endif 97 | 98 | static stock RusText_SendVersionRequest() 99 | { 100 | HTTP(0, HTTP_GET, 101 | "ziggi.org/github/lastrelease.php?owner=ziggi&repo=rustext&elem=tag_name", 102 | "", 103 | "RusText_OnRequestResponse"); 104 | } 105 | 106 | forward RusText_OnRequestResponse(index, response_code, data[]); 107 | public RusText_OnRequestResponse(index, response_code, data[]) 108 | { 109 | if (index == 0 && response_code == 200) { 110 | new length = strlen(data); 111 | if (length == 0 || length > 10) { 112 | return; 113 | } 114 | 115 | new version[16]; 116 | strmid(version, data, 1, length); 117 | 118 | if (!('0' <= version[0] <= '9')) { 119 | return; 120 | } 121 | 122 | new plugin_version[16]; 123 | GetRussifierVersion(plugin_version); 124 | 125 | if (strcmp(version, plugin_version) != 0) { 126 | printf("\n** Please update your rustext plugin to %s from here: https://github.com/ziggi/rustext/releases.\n", version); 127 | } 128 | } 129 | } 130 | #endif 131 | -------------------------------------------------------------------------------- /lib/RakNet/BitStream.cpp: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// 3 | /// This file is part of RakNet Copyright 2003 Kevin Jenkins. 4 | /// 5 | /// Usage of RakNet is subject to the appropriate license agreement. 6 | /// Creative Commons Licensees are subject to the 7 | /// license found at 8 | /// http://creativecommons.org/licenses/by-nc/2.5/ 9 | /// Single application licensees are subject to the license found at 10 | /// http://www.rakkarsoft.com/SingleApplicationLicense.html 11 | /// Custom license users are subject to the terms therein. 12 | /// GPL license users are subject to the GNU General Public 13 | /// License as published by the Free 14 | /// Software Foundation; either version 2 of the License, or (at your 15 | /// option) any later version. 16 | 17 | #if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization 18 | #include "BitStream_NoTemplate.cpp" 19 | #else 20 | 21 | #include "BitStream.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef _COMPATIBILITY_1 29 | #include "Compatibility1Includes.h" 30 | #elif defined(_WIN32) 31 | #include // htonl 32 | #pragma comment(lib, "Ws2_32.lib") 33 | #else 34 | #include 35 | #endif 36 | 37 | // Was included for memset which now comes from string.h instead 38 | /* 39 | #if defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) 40 | #include 41 | #elif !defined(_COMPATIBILITY_2) 42 | #include 43 | #endif 44 | 45 | */ 46 | 47 | // MSWin uses _copysign, others use copysign... 48 | #ifndef _WIN32 49 | #define _copysign copysign 50 | #endif 51 | 52 | using namespace RakNet; 53 | 54 | #ifdef _MSC_VER 55 | #pragma warning( push ) 56 | #endif 57 | 58 | BitStream::BitStream() 59 | { 60 | numberOfBitsUsed = 0; 61 | //numberOfBitsAllocated = 32 * 8; 62 | numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; 63 | readOffset = 0; 64 | //data = ( unsigned char* ) malloc( 32 ); 65 | data = (unsigned char*)stackData; 66 | 67 | #ifdef _DEBUG 68 | // assert( data ); 69 | #endif 70 | //memset(data, 0, 32); 71 | copyData = true; 72 | } 73 | 74 | BitStream::BitStream(int initialBytesToAllocate) 75 | { 76 | numberOfBitsUsed = 0; 77 | readOffset = 0; 78 | if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) 79 | { 80 | data = (unsigned char*)stackData; 81 | numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; 82 | } 83 | else 84 | { 85 | data = (unsigned char*)malloc(initialBytesToAllocate); 86 | numberOfBitsAllocated = initialBytesToAllocate << 3; 87 | } 88 | #ifdef _DEBUG 89 | assert(data); 90 | #endif 91 | // memset(data, 0, initialBytesToAllocate); 92 | copyData = true; 93 | } 94 | 95 | BitStream::BitStream(unsigned char* _data, unsigned int lengthInBytes, bool _copyData) 96 | { 97 | numberOfBitsUsed = lengthInBytes << 3; 98 | readOffset = 0; 99 | copyData = _copyData; 100 | numberOfBitsAllocated = lengthInBytes << 3; 101 | 102 | if (copyData) 103 | { 104 | if (lengthInBytes > 0) 105 | { 106 | if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) 107 | { 108 | data = (unsigned char*)stackData; 109 | numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; 110 | } 111 | else 112 | { 113 | data = (unsigned char*)malloc(lengthInBytes); 114 | } 115 | #ifdef _DEBUG 116 | assert(data); 117 | #endif 118 | memcpy(data, _data, lengthInBytes); 119 | } 120 | else 121 | data = 0; 122 | } 123 | else 124 | data = (unsigned char*)_data; 125 | } 126 | 127 | // Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation 128 | void BitStream::SetNumberOfBitsAllocated(const unsigned int lengthInBits) 129 | { 130 | #ifdef _DEBUG 131 | assert(lengthInBits >= (unsigned int)numberOfBitsAllocated); 132 | #endif 133 | numberOfBitsAllocated = lengthInBits; 134 | } 135 | 136 | BitStream::~BitStream() 137 | { 138 | if (copyData && numberOfBitsAllocated > BITSTREAM_STACK_ALLOCATION_SIZE << 3) 139 | free(data); // Use realloc and free so we are more efficient than delete and new for resizing 140 | } 141 | 142 | void BitStream::Reset(void) 143 | { 144 | // Note: Do NOT reallocate memory because BitStream is used 145 | // in places to serialize/deserialize a buffer. Reallocation 146 | // is a dangerous operation (may result in leaks). 147 | 148 | if (numberOfBitsUsed > 0) 149 | { 150 | // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); 151 | } 152 | 153 | // Don't free memory here for speed efficiency 154 | //free(data); // Use realloc and free so we are more efficient than delete and new for resizing 155 | numberOfBitsUsed = 0; 156 | 157 | //numberOfBitsAllocated=8; 158 | readOffset = 0; 159 | 160 | //data=(unsigned char*)malloc(1); 161 | // if (numberOfBitsAllocated>0) 162 | // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); 163 | } 164 | 165 | // Write an array or casted stream 166 | void BitStream::Write(const char* input, const int numberOfBytes) 167 | { 168 | if (numberOfBytes == 0) 169 | return; 170 | 171 | // Optimization: 172 | if ((numberOfBitsUsed & 7) == 0) 173 | { 174 | AddBitsAndReallocate(BYTES_TO_BITS(numberOfBytes)); 175 | memcpy(data + BITS_TO_BYTES(numberOfBitsUsed), input, numberOfBytes); 176 | numberOfBitsUsed += BYTES_TO_BITS(numberOfBytes); 177 | } 178 | else 179 | { 180 | WriteBits((unsigned char*)input, numberOfBytes * 8, true); 181 | } 182 | 183 | } 184 | void BitStream::Write(BitStream *bitStream) 185 | { 186 | Write(bitStream, bitStream->GetNumberOfBitsUsed()); 187 | } 188 | void BitStream::Write(BitStream *bitStream, int numberOfBits) 189 | { 190 | AddBitsAndReallocate(numberOfBits); 191 | int numberOfBitsMod8; 192 | 193 | while (numberOfBits-- > 0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed) 194 | { 195 | numberOfBitsMod8 = numberOfBitsUsed & 7; 196 | if (numberOfBitsMod8 == 0) 197 | { 198 | // New byte 199 | if (bitStream->data[bitStream->readOffset >> 3] & (0x80 >> (bitStream->readOffset++ % 8))) 200 | { 201 | // Write 1 202 | data[numberOfBitsUsed >> 3] = 0x80; 203 | } 204 | else 205 | { 206 | // Write 0 207 | data[numberOfBitsUsed >> 3] = 0; 208 | } 209 | } 210 | else 211 | { 212 | // Existing byte 213 | if (bitStream->data[bitStream->readOffset >> 3] & (0x80 >> (bitStream->readOffset++ % 8))) 214 | data[numberOfBitsUsed >> 3] |= 0x80 >> (numberOfBitsMod8); // Set the bit to 1 215 | // else 0, do nothing 216 | } 217 | 218 | numberOfBitsUsed++; 219 | } 220 | } 221 | 222 | // Read an array or casted stream 223 | bool BitStream::Read(char* output, const int numberOfBytes) 224 | { 225 | // Optimization: 226 | if ((readOffset & 7) == 0) 227 | { 228 | if (readOffset + (numberOfBytes << 3) > numberOfBitsUsed) 229 | return false; 230 | 231 | // Write the data 232 | memcpy(output, data + (readOffset >> 3), numberOfBytes); 233 | 234 | readOffset += numberOfBytes << 3; 235 | return true; 236 | } 237 | else 238 | { 239 | return ReadBits((unsigned char*)output, numberOfBytes * 8); 240 | } 241 | } 242 | 243 | // Sets the read pointer back to the beginning of your data. 244 | void BitStream::ResetReadPointer(void) 245 | { 246 | readOffset = 0; 247 | } 248 | 249 | // Sets the write pointer back to the beginning of your data. 250 | void BitStream::ResetWritePointer(void) 251 | { 252 | numberOfBitsUsed = 0; 253 | } 254 | 255 | // Write a 0 256 | void BitStream::Write0(void) 257 | { 258 | AddBitsAndReallocate(1); 259 | 260 | // New bytes need to be zeroed 261 | if ((numberOfBitsUsed & 7) == 0) 262 | data[numberOfBitsUsed >> 3] = 0; 263 | 264 | numberOfBitsUsed++; 265 | } 266 | 267 | // Write a 1 268 | void BitStream::Write1(void) 269 | { 270 | AddBitsAndReallocate(1); 271 | 272 | int numberOfBitsMod8 = numberOfBitsUsed & 7; 273 | 274 | if (numberOfBitsMod8 == 0) 275 | data[numberOfBitsUsed >> 3] = 0x80; 276 | else 277 | data[numberOfBitsUsed >> 3] |= 0x80 >> (numberOfBitsMod8); // Set the bit to 1 278 | 279 | numberOfBitsUsed++; 280 | } 281 | 282 | #ifdef _MSC_VER 283 | #pragma warning( disable : 4800 ) // warning C4100: : unreferenced formal parameter 284 | #endif 285 | // Returns true if the next data read is a 1, false if it is a 0 286 | bool BitStream::ReadBit(void) 287 | { 288 | return (bool)(data[readOffset >> 3] & (0x80 >> (readOffset++ & 7))); 289 | } 290 | 291 | // Align the bitstream to the byte boundary and then write the specified number of bits. 292 | // This is faster than WriteBits but wastes the bits to do the alignment and requires you to call 293 | // SetReadToByteAlignment at the corresponding read position 294 | void BitStream::WriteAlignedBytes(const unsigned char* input, 295 | const int numberOfBytesToWrite) 296 | { 297 | #ifdef _DEBUG 298 | assert(numberOfBytesToWrite > 0); 299 | #endif 300 | 301 | AlignWriteToByteBoundary(); 302 | Write((const char*)input, numberOfBytesToWrite); 303 | } 304 | 305 | // Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the 306 | // sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence 307 | // unless you byte align the coalesced packets. 308 | bool BitStream::ReadAlignedBytes(unsigned char* output, const int numberOfBytesToRead) 309 | { 310 | #ifdef _DEBUG 311 | assert(numberOfBytesToRead > 0); 312 | #endif 313 | 314 | if (numberOfBytesToRead <= 0) 315 | return false; 316 | 317 | // Byte align 318 | AlignReadToByteBoundary(); 319 | 320 | if (readOffset + (numberOfBytesToRead << 3) > numberOfBitsUsed) 321 | return false; 322 | 323 | // Write the data 324 | memcpy(output, data + (readOffset >> 3), numberOfBytesToRead); 325 | 326 | readOffset += numberOfBytesToRead << 3; 327 | 328 | return true; 329 | } 330 | 331 | // Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons 332 | void BitStream::AlignWriteToByteBoundary(void) 333 | { 334 | if (numberOfBitsUsed) 335 | numberOfBitsUsed += 8 - (((numberOfBitsUsed - 1) & 7) + 1); 336 | } 337 | 338 | // Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons 339 | void BitStream::AlignReadToByteBoundary(void) 340 | { 341 | if (readOffset) 342 | readOffset += 8 - (((readOffset - 1) & 7) + 1); 343 | } 344 | 345 | // Write numberToWrite bits from the input source 346 | void BitStream::WriteBits(const unsigned char *input, int numberOfBitsToWrite, const bool rightAlignedBits) 347 | { 348 | if (numberOfBitsToWrite <= 0) 349 | return; 350 | 351 | AddBitsAndReallocate(numberOfBitsToWrite); 352 | int offset = 0; 353 | unsigned char dataByte; 354 | int numberOfBitsUsedMod8; 355 | 356 | numberOfBitsUsedMod8 = numberOfBitsUsed & 7; 357 | 358 | // Faster to put the while at the top surprisingly enough 359 | while (numberOfBitsToWrite > 0) 360 | //do 361 | { 362 | dataByte = *(input + offset); 363 | 364 | if (numberOfBitsToWrite < 8 && rightAlignedBits) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation) 365 | dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation 366 | 367 | // Writing to a new byte each time 368 | if (numberOfBitsUsedMod8 == 0) 369 | * (data + (numberOfBitsUsed >> 3)) = dataByte; 370 | else 371 | { 372 | // Copy over the new data. 373 | *(data + (numberOfBitsUsed >> 3)) |= dataByte >> (numberOfBitsUsedMod8); // First half 374 | 375 | if (8 - (numberOfBitsUsedMod8) < 8 && 8 - (numberOfBitsUsedMod8) < numberOfBitsToWrite) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half) 376 | { 377 | *(data + (numberOfBitsUsed >> 3) + 1) = (unsigned char)(dataByte << (8 - (numberOfBitsUsedMod8))); // Second half (overlaps byte boundary) 378 | } 379 | } 380 | 381 | if (numberOfBitsToWrite >= 8) 382 | numberOfBitsUsed += 8; 383 | else 384 | numberOfBitsUsed += numberOfBitsToWrite; 385 | 386 | numberOfBitsToWrite -= 8; 387 | 388 | offset++; 389 | } 390 | // } while(numberOfBitsToWrite>0); 391 | } 392 | 393 | // Set the stream to some initial data. For internal use 394 | void BitStream::SetData(unsigned char *input) 395 | { 396 | data = input; 397 | copyData = false; 398 | } 399 | 400 | // Assume the input source points to a native type, compress and write it 401 | void BitStream::WriteCompressed(const unsigned char* input, 402 | const int size, const bool unsignedData) 403 | { 404 | int currentByte = (size >> 3) - 1; // PCs 405 | 406 | unsigned char byteMatch; 407 | 408 | if (unsignedData) 409 | { 410 | byteMatch = 0; 411 | } 412 | 413 | else 414 | { 415 | byteMatch = 0xFF; 416 | } 417 | 418 | // Write upper bytes with a single 1 419 | // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes 420 | while (currentByte > 0) 421 | { 422 | if (input[currentByte] == byteMatch) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted 423 | { 424 | bool b = true; 425 | Write(b); 426 | } 427 | else 428 | { 429 | // Write the remainder of the data after writing 0 430 | bool b = false; 431 | Write(b); 432 | 433 | WriteBits(input, (currentByte + 1) << 3, true); 434 | // currentByte--; 435 | 436 | 437 | return; 438 | } 439 | 440 | currentByte--; 441 | } 442 | 443 | // If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites. 444 | if ((unsignedData && ((*(input + currentByte)) & 0xF0) == 0x00) || 445 | (unsignedData == false && ((*(input + currentByte)) & 0xF0) == 0xF0)) 446 | { 447 | bool b = true; 448 | Write(b); 449 | WriteBits(input + currentByte, 4, true); 450 | } 451 | 452 | else 453 | { 454 | bool b = false; 455 | Write(b); 456 | WriteBits(input + currentByte, 8, true); 457 | } 458 | } 459 | 460 | // Read numberOfBitsToRead bits to the output source 461 | // alignBitsToRight should be set to true to convert internal bitstream data to userdata 462 | // It should be false if you used WriteBits with rightAlignedBits false 463 | bool BitStream::ReadBits(unsigned char* output, int numberOfBitsToRead, const bool alignBitsToRight) 464 | { 465 | #ifdef _DEBUG 466 | assert(numberOfBitsToRead > 0); 467 | #endif 468 | if (numberOfBitsToRead <= 0) 469 | return false; 470 | 471 | if (readOffset + numberOfBitsToRead > numberOfBitsUsed) 472 | return false; 473 | 474 | int readOffsetMod8; 475 | 476 | int offset = 0; 477 | 478 | memset(output, 0, BITS_TO_BYTES(numberOfBitsToRead)); 479 | 480 | readOffsetMod8 = readOffset & 7; 481 | 482 | // do 483 | // Faster to put the while at the top surprisingly enough 484 | while (numberOfBitsToRead > 0) 485 | { 486 | *(output + offset) |= *(data + (readOffset >> 3)) << (readOffsetMod8); // First half 487 | 488 | if (readOffsetMod8 > 0 && numberOfBitsToRead > 8 - (readOffsetMod8)) // If we have a second half, we didn't read enough bytes in the first half 489 | *(output + offset) |= *(data + (readOffset >> 3) + 1) >> (8 - (readOffsetMod8)); // Second half (overlaps byte boundary) 490 | 491 | numberOfBitsToRead -= 8; 492 | 493 | if (numberOfBitsToRead < 0) // Reading a partial byte for the last byte, shift right so the data is aligned on the right 494 | { 495 | 496 | if (alignBitsToRight) 497 | * (output + offset) >>= -numberOfBitsToRead; 498 | 499 | readOffset += 8 + numberOfBitsToRead; 500 | } 501 | else 502 | readOffset += 8; 503 | 504 | offset++; 505 | 506 | } 507 | 508 | //} while(numberOfBitsToRead>0); 509 | 510 | return true; 511 | } 512 | 513 | // Assume the input source points to a compressed native type. Decompress and read it 514 | bool BitStream::ReadCompressed(unsigned char* output, 515 | const int size, const bool unsignedData) 516 | { 517 | int currentByte = (size >> 3) - 1; 518 | 519 | 520 | unsigned char byteMatch, halfByteMatch; 521 | 522 | if (unsignedData) 523 | { 524 | byteMatch = 0; 525 | halfByteMatch = 0; 526 | } 527 | 528 | else 529 | { 530 | byteMatch = 0xFF; 531 | halfByteMatch = 0xF0; 532 | } 533 | 534 | // Upper bytes are specified with a single 1 if they match byteMatch 535 | // From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes 536 | while (currentByte > 0) 537 | { 538 | // If we read a 1 then the data is byteMatch. 539 | 540 | bool b; 541 | 542 | if (Read(b) == false) 543 | return false; 544 | 545 | if (b) // Check that bit 546 | { 547 | output[currentByte] = byteMatch; 548 | currentByte--; 549 | } 550 | else 551 | { 552 | // Read the rest of the bytes 553 | 554 | if (ReadBits(output, (currentByte + 1) << 3) == false) 555 | return false; 556 | 557 | return true; 558 | } 559 | } 560 | 561 | // All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits. 562 | // Otherwise we read a 0 and the 8 bytes 563 | //assert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from 564 | if (readOffset + 1 > numberOfBitsUsed) 565 | return false; 566 | 567 | bool b; 568 | 569 | if (Read(b) == false) 570 | return false; 571 | 572 | if (b) // Check that bit 573 | { 574 | 575 | if (ReadBits(output + currentByte, 4) == false) 576 | return false; 577 | 578 | output[currentByte] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits 579 | } 580 | else 581 | { 582 | if (ReadBits(output + currentByte, 8) == false) 583 | return false; 584 | } 585 | 586 | return true; 587 | } 588 | 589 | // Reallocates (if necessary) in preparation of writing numberOfBitsToWrite 590 | void BitStream::AddBitsAndReallocate(const int numberOfBitsToWrite) 591 | { 592 | if (numberOfBitsToWrite <= 0) 593 | return; 594 | 595 | int newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; 596 | 597 | if (numberOfBitsToWrite + numberOfBitsUsed > 0 && ((numberOfBitsAllocated - 1) >> 3) < ((newNumberOfBitsAllocated - 1) >> 3)) // If we need to allocate 1 or more new bytes 598 | { 599 | #ifdef _DEBUG 600 | // If this assert hits then we need to specify true for the third parameter in the constructor 601 | // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with 602 | assert(copyData == true); 603 | #endif 604 | 605 | // Less memory efficient but saves on news and deletes 606 | newNumberOfBitsAllocated = (numberOfBitsToWrite + numberOfBitsUsed) * 2; 607 | // int newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); 608 | // Use realloc and free so we are more efficient than delete and new for resizing 609 | int amountToAllocate = BITS_TO_BYTES(newNumberOfBitsAllocated); 610 | if (data == (unsigned char*)stackData) 611 | { 612 | if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) 613 | { 614 | data = (unsigned char*)malloc(amountToAllocate); 615 | 616 | // need to copy the stack data over to our new memory area too 617 | memcpy((void *)data, (void *)stackData, BITS_TO_BYTES(numberOfBitsAllocated)); 618 | } 619 | } 620 | else 621 | { 622 | data = (unsigned char*)realloc(data, amountToAllocate); 623 | } 624 | 625 | #ifdef _DEBUG 626 | assert(data); // Make sure realloc succeeded 627 | #endif 628 | // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 629 | } 630 | 631 | if (newNumberOfBitsAllocated > numberOfBitsAllocated) 632 | numberOfBitsAllocated = newNumberOfBitsAllocated; 633 | } 634 | 635 | // Should hit if reads didn't match writes 636 | void BitStream::AssertStreamEmpty(void) 637 | { 638 | assert(readOffset == numberOfBitsUsed); 639 | } 640 | 641 | void BitStream::PrintBits(void) const 642 | { 643 | if (numberOfBitsUsed <= 0) 644 | { 645 | printf("No bits\n"); 646 | return; 647 | } 648 | 649 | for (int counter = 0; counter < BITS_TO_BYTES(numberOfBitsUsed); counter++) 650 | { 651 | int stop; 652 | 653 | if (counter == (numberOfBitsUsed - 1) >> 3) 654 | stop = 8 - (((numberOfBitsUsed - 1) & 7) + 1); 655 | else 656 | stop = 0; 657 | 658 | for (int counter2 = 7; counter2 >= stop; counter2--) 659 | { 660 | if ((data[counter] >> counter2) & 1) 661 | putchar('1'); 662 | else 663 | putchar('0'); 664 | } 665 | 666 | putchar(' '); 667 | } 668 | 669 | putchar('\n'); 670 | } 671 | 672 | 673 | // Exposes the data for you to look at, like PrintBits does. 674 | // Data will point to the stream. Returns the length in bits of the stream. 675 | int BitStream::CopyData(unsigned char** _data) const 676 | { 677 | #ifdef _DEBUG 678 | assert(numberOfBitsUsed > 0); 679 | #endif 680 | 681 | *_data = new unsigned char[BITS_TO_BYTES(numberOfBitsUsed)]; 682 | memcpy(*_data, data, sizeof(unsigned char) * (BITS_TO_BYTES(numberOfBitsUsed))); 683 | return numberOfBitsUsed; 684 | } 685 | 686 | // Ignore data we don't intend to read 687 | void BitStream::IgnoreBits(const int numberOfBits) 688 | { 689 | readOffset += numberOfBits; 690 | } 691 | 692 | // Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! 693 | void BitStream::SetWriteOffset(const int offset) 694 | { 695 | numberOfBitsUsed = offset; 696 | } 697 | 698 | /* 699 | int BitStream::GetWriteOffset( void ) const 700 | { 701 | return numberOfBitsUsed; 702 | } 703 | 704 | // Returns the length in bits of the stream 705 | int BitStream::GetNumberOfBitsUsed( void ) const 706 | { 707 | return GetWriteOffset(); 708 | } 709 | 710 | // Returns the length in bytes of the stream 711 | int BitStream::GetNumberOfBytesUsed( void ) const 712 | { 713 | return BITS_TO_BYTES( numberOfBitsUsed ); 714 | } 715 | 716 | // Returns the number of bits into the stream that we have read 717 | int BitStream::GetReadOffset( void ) const 718 | { 719 | return readOffset; 720 | } 721 | 722 | 723 | // Sets the read bit index 724 | void BitStream::SetReadOffset( int newReadOffset ) 725 | { 726 | readOffset=newReadOffset; 727 | } 728 | 729 | // Returns the number of bits left in the stream that haven't been read 730 | int BitStream::GetNumberOfUnreadBits( void ) const 731 | { 732 | return numberOfBitsUsed - readOffset; 733 | } 734 | // Exposes the internal data 735 | unsigned char* BitStream::GetData( void ) const 736 | { 737 | return data; 738 | } 739 | 740 | */ 741 | // If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied. 742 | void BitStream::AssertCopyData(void) 743 | { 744 | if (copyData == false) 745 | { 746 | copyData = true; 747 | 748 | if (numberOfBitsAllocated > 0) 749 | { 750 | unsigned char * newdata = (unsigned char*)malloc(BITS_TO_BYTES(numberOfBitsAllocated)); 751 | #ifdef _DEBUG 752 | 753 | assert(data); 754 | #endif 755 | 756 | memcpy(newdata, data, BITS_TO_BYTES(numberOfBitsAllocated)); 757 | data = newdata; 758 | } 759 | 760 | else 761 | data = 0; 762 | } 763 | } 764 | void BitStream::ReverseBytes(unsigned char *input, unsigned char *output, int length) 765 | { 766 | for (int i = 0; i < length; i++) 767 | output[i] = input[length - i - 1]; 768 | } 769 | bool BitStream::DoEndianSwap(void) const 770 | { 771 | #ifndef __BITSTREAM_NATIVE_END 772 | static bool swap = htonl(12345) == 12345; 773 | return swap; 774 | #else 775 | return false; 776 | #endif 777 | } 778 | 779 | #ifdef _MSC_VER 780 | #pragma warning( pop ) 781 | #endif 782 | 783 | #endif // #if _MSC_VER < 1299 -------------------------------------------------------------------------------- /lib/RakNet/BitStream.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. 3 | /// 4 | /// This file is part of RakNet Copyright 2003 Kevin Jenkins. 5 | /// 6 | /// Usage of RakNet is subject to the appropriate license agreement. 7 | /// Creative Commons Licensees are subject to the 8 | /// license found at 9 | /// http://creativecommons.org/licenses/by-nc/2.5/ 10 | /// Single application licensees are subject to the license found at 11 | /// http://www.rakkarsoft.com/SingleApplicationLicense.html 12 | /// Custom license users are subject to the terms therein. 13 | /// GPL license users are subject to the GNU General Public 14 | /// License as published by the Free 15 | /// Software Foundation; either version 2 of the License, or (at your 16 | /// option) any later version. 17 | 18 | 19 | #ifndef __BITSTREAM_H 20 | #define __BITSTREAM_H 21 | 22 | #include "NetworkTypes.h" 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef _MSC_VER 28 | #pragma warning( push ) 29 | #endif 30 | 31 | /// Arbitrary size, just picking something likely to be larger than most packets 32 | #define BITSTREAM_STACK_ALLOCATION_SIZE 256 33 | 34 | /// The namespace RakNet is not consistently used. It's only purpose is to avoid compiler errors for classes whose names are very common. 35 | /// For the most part I've tried to avoid this simply by using names very likely to be unique for my classes. 36 | namespace RakNet 37 | { 38 | /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. 39 | /// \sa BitStreamSample.txt 40 | class BitStream 41 | { 42 | 43 | public: 44 | /// Default Constructor 45 | BitStream(); 46 | 47 | /// Create the bitstream, with some number of bytes to immediately allocate. 48 | /// There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. 49 | /// In that case all it does is save you one or more realloc calls. 50 | /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. 51 | BitStream(int initialBytesToAllocate); 52 | 53 | /// Initialize the BitStream, immediately setting the data it contains to a predefined pointer. 54 | /// Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. 55 | /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory 56 | /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows: 57 | /// \code 58 | /// RakNet::BitStream bs(packet->data, packet->length, false); 59 | /// \endcode 60 | /// \param[in] _data An array of bytes. 61 | /// \param[in] lengthInBytes Size of the \a _data. 62 | /// \param[in] _copyData true or false to make a copy of \a _data or not. 63 | BitStream(unsigned char* _data, unsigned int lengthInBytes, bool _copyData); 64 | 65 | /// Destructor 66 | ~BitStream(); 67 | 68 | /// Resets the bitstream for reuse. 69 | void Reset(void); 70 | 71 | /// Bidirectional serialize/deserialize any integral type to/from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 72 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 73 | /// \param[in] var The value to write 74 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 75 | template 76 | bool Serialize(bool writeToBitstream, templateType &var); 77 | 78 | /// Bidirectional serialize/deserialize any integral type to/from a bitstream. If the current value is different from the last value 79 | /// the current value will be written. Otherwise, a single bit will be written 80 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 81 | /// \param[in] currentValue The current value to write 82 | /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. 83 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 84 | template 85 | bool SerializeDelta(bool writeToBitstream, templateType ¤tValue, templateType lastValue); 86 | 87 | /// Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value. 88 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 89 | /// \param[in] currentValue The current value to write 90 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 91 | template 92 | bool SerializeDelta(bool writeToBitstream, templateType ¤tValue); 93 | 94 | /// Bidirectional serialize/deserialize any integral type to/from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 95 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 96 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 97 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 98 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 99 | /// \param[in] var The value to write 100 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 101 | template 102 | bool SerializeCompressed(bool writeToBitstream, templateType &var); 103 | 104 | /// Bidirectional serialize/deserialize any integral type to/from a bitstream. If the current value is different from the last value 105 | /// the current value will be written. Otherwise, a single bit will be written 106 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 107 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 108 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 109 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 110 | /// \param[in] currentValue The current value to write 111 | /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. 112 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 113 | template 114 | bool SerializeCompressedDelta(bool writeToBitstream, templateType ¤tValue, templateType lastValue); 115 | 116 | /// Save as SerializeCompressedDelta(templateType ¤tValue, templateType lastValue) when we have an unknown second parameter 117 | template 118 | bool SerializeCompressedDelta(bool writeToBitstream, templateType ¤tValue); 119 | 120 | /// Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. 121 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 122 | /// \param[in] input a byte buffer 123 | /// \param[in] numberOfBytes the size of \a input in bytes 124 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 125 | bool Serialize(bool writeToBitstream, char* input, const int numberOfBytes); 126 | 127 | /// Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. 128 | /// Accurate to 1/32767.5. 129 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 130 | /// \param[in] x x 131 | /// \param[in] y y 132 | /// \param[in] z z 133 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 134 | template // templateType for this function must be a float or double 135 | bool SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z); 136 | 137 | /// Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12. 138 | /// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. 139 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 140 | /// \param[in] x x 141 | /// \param[in] y y 142 | /// \param[in] z z 143 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 144 | template // templateType for this function must be a float or double 145 | bool SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z); 146 | 147 | /// Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. 148 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 149 | /// \param[in] w w 150 | /// \param[in] x x 151 | /// \param[in] y y 152 | /// \param[in] z z 153 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 154 | template // templateType for this function must be a float or double 155 | bool SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z); 156 | 157 | /// Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each 158 | /// for 6 bytes instead of 36 159 | /// Lossy, although the result is renormalized 160 | template // templateType for this function must be a float or double 161 | bool SerializeOrthMatrix( 162 | bool writeToBitstream, 163 | templateType &m00, templateType &m01, templateType &m02, 164 | templateType &m10, templateType &m11, templateType &m12, 165 | templateType &m20, templateType &m21, templateType &m22); 166 | 167 | /// Bidirectional serialize/deserialize numberToSerialize bits to/from the input. Right aligned 168 | /// data means in the case of a partial byte, the bits are aligned 169 | /// from the right (bit 0) rather than the left (as in the normal 170 | /// internal representation) You would set this to true when 171 | /// writing user data, and false when copying bitstream data, such 172 | /// as writing one bitstream to another 173 | /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data 174 | /// \param[in] input The data 175 | /// \param[in] numberOfBitsToSerialize The number of bits to write 176 | /// \param[in] rightAlignedBits if true data will be right aligned 177 | /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. 178 | bool SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits = true); 179 | 180 | /// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 181 | /// \param[in] var The value to write 182 | template 183 | void Write(templateType var); 184 | 185 | /// Write any integral type to a bitstream. If the current value is different from the last value 186 | /// the current value will be written. Otherwise, a single bit will be written 187 | /// \param[in] currentValue The current value to write 188 | /// \param[in] lastValue The last value to compare against 189 | template 190 | void WriteDelta(templateType currentValue, templateType lastValue); 191 | 192 | /// WriteDelta when you don't know what the last value is, or there is no last value. 193 | /// \param[in] currentValue The current value to write 194 | template 195 | void WriteDelta(templateType currentValue); 196 | 197 | /// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 198 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 199 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 200 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 201 | /// \param[in] var The value to write 202 | template 203 | void WriteCompressed(templateType var); 204 | 205 | /// Write any integral type to a bitstream. If the current value is different from the last value 206 | /// the current value will be written. Otherwise, a single bit will be written 207 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 208 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 209 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 210 | /// \param[in] currentValue The current value to write 211 | /// \param[in] lastValue The last value to compare against 212 | template 213 | void WriteCompressedDelta(templateType currentValue, templateType lastValue); 214 | 215 | /// Save as WriteCompressedDelta(templateType currentValue, templateType lastValue) when we have an unknown second parameter 216 | template 217 | void WriteCompressedDelta(templateType currentValue); 218 | 219 | /// Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. 220 | /// \param[in] var The value to read 221 | template 222 | bool Read(templateType &var); 223 | 224 | /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, 225 | /// var will be updated. Otherwise it will retain the current value. 226 | /// ReadDelta is only valid from a previous call to WriteDelta 227 | /// \param[in] var The value to read 228 | template 229 | bool ReadDelta(templateType &var); 230 | 231 | /// Read any integral type from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 232 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 233 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 234 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 235 | /// \param[in] var The value to read 236 | template 237 | bool ReadCompressed(templateType &var); 238 | 239 | /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, 240 | /// var will be updated. Otherwise it will retain the current value. 241 | /// the current value will be updated. 242 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 243 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 244 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 245 | /// ReadCompressedDelta is only valid from a previous call to WriteDelta 246 | /// \param[in] var The value to read 247 | template 248 | bool ReadCompressedDelta(templateType &var); 249 | 250 | /// Write an array or casted stream or raw data. This does NOT do endian swapping. 251 | /// \param[in] input a byte buffer 252 | /// \param[in] numberOfBytes the size of \a input in bytes 253 | void Write(const char* input, const int numberOfBytes); 254 | 255 | /// Write one bitstream to another 256 | /// \param[in] numberOfBits bits to write 257 | /// \param bitStream the bitstream to copy from 258 | void Write(BitStream *bitStream, int numberOfBits); 259 | void Write(BitStream *bitStream); 260 | 261 | /// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. 262 | /// Accurate to 1/32767.5. 263 | /// \param[in] x x 264 | /// \param[in] y y 265 | /// \param[in] z z 266 | template // templateType for this function must be a float or double 267 | void WriteNormVector(templateType x, templateType y, templateType z); 268 | 269 | /// Write a vector, using 10 bytes instead of 12. 270 | /// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. 271 | /// \param[in] x x 272 | /// \param[in] y y 273 | /// \param[in] z z 274 | template // templateType for this function must be a float or double 275 | void WriteVector(templateType x, templateType y, templateType z); 276 | 277 | /// Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. 278 | /// \param[in] w w 279 | /// \param[in] x x 280 | /// \param[in] y y 281 | /// \param[in] z z 282 | template // templateType for this function must be a float or double 283 | void WriteNormQuat(templateType w, templateType x, templateType y, templateType z); 284 | 285 | /// Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each 286 | /// for 6 bytes instead of 36 287 | /// Lossy, although the result is renormalized 288 | template // templateType for this function must be a float or double 289 | void WriteOrthMatrix( 290 | templateType m00, templateType m01, templateType m02, 291 | templateType m10, templateType m11, templateType m12, 292 | templateType m20, templateType m21, templateType m22); 293 | 294 | /// Read an array or casted stream of byte. The array 295 | /// is raw data. There is no automatic endian conversion with this function 296 | /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. 297 | /// \param[in] numberOfBytes The number of byte to read 298 | /// \return true on success false if there is some missing bytes. 299 | bool Read(char* output, const int numberOfBytes); 300 | 301 | /// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. 302 | /// Accurate to 1/32767.5. 303 | /// \param[in] x x 304 | /// \param[in] y y 305 | /// \param[in] z z 306 | template // templateType for this function must be a float or double 307 | bool ReadNormVector(templateType &x, templateType &y, templateType &z); 308 | 309 | /// Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector 310 | /// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. 311 | /// \param[in] x x 312 | /// \param[in] y y 313 | /// \param[in] z z 314 | template // templateType for this function must be a float or double 315 | bool ReadVector(templateType &x, templateType &y, templateType &z); 316 | 317 | /// Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. 318 | /// \param[in] w w 319 | /// \param[in] x x 320 | /// \param[in] y y 321 | /// \param[in] z z 322 | template // templateType for this function must be a float or double 323 | bool ReadNormQuat(templateType &w, templateType &x, templateType &y, templateType &z); 324 | 325 | /// Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th. 326 | /// for 6 bytes instead of 36 327 | /// Lossy, although the result is renormalized 328 | template // templateType for this function must be a float or double 329 | bool ReadOrthMatrix( 330 | templateType &m00, templateType &m01, templateType &m02, 331 | templateType &m10, templateType &m11, templateType &m12, 332 | templateType &m20, templateType &m21, templateType &m22); 333 | 334 | ///Sets the read pointer back to the beginning of your data. 335 | void ResetReadPointer(void); 336 | 337 | /// Sets the write pointer back to the beginning of your data. 338 | void ResetWritePointer(void); 339 | 340 | ///This is good to call when you are done with the stream to make 341 | /// sure you didn't leave any data left over void 342 | void AssertStreamEmpty(void); 343 | 344 | /// printf the bits in the stream. Great for debugging. 345 | void PrintBits(void) const; 346 | 347 | /// Ignore data we don't intend to read 348 | /// \param[in] numberOfBits The number of bits to ignore 349 | void IgnoreBits(const int numberOfBits); 350 | 351 | ///Move the write pointer to a position on the array. 352 | /// \param[in] offset the offset from the start of the array. 353 | /// \attention 354 | /// Dangerous if you don't know what you are doing! 355 | /// For efficiency reasons you can only write mid-stream if your data is byte aligned. 356 | void SetWriteOffset(const int offset); 357 | 358 | /// Returns the length in bits of the stream 359 | inline int GetNumberOfBitsUsed(void) const { return GetWriteOffset(); } 360 | inline int GetWriteOffset(void) const { return numberOfBitsUsed; } 361 | 362 | ///Returns the length in bytes of the stream 363 | inline int GetNumberOfBytesUsed(void) const { return BITS_TO_BYTES(numberOfBitsUsed); } 364 | 365 | ///Returns the number of bits into the stream that we have read 366 | inline int GetReadOffset(void) const { return readOffset; } 367 | 368 | // Sets the read bit index 369 | inline void SetReadOffset(int newReadOffset) { readOffset = newReadOffset; } 370 | 371 | ///Returns the number of bits left in the stream that haven't been read 372 | inline int GetNumberOfUnreadBits(void) const { return numberOfBitsUsed - readOffset; } 373 | 374 | /// Makes a copy of the internal data for you \a _data will point to 375 | /// the stream. Returns the length in bits of the stream. Partial 376 | /// bytes are left aligned 377 | /// \param[out] _data The allocated copy of GetData() 378 | int CopyData(unsigned char** _data) const; 379 | 380 | /// Set the stream to some initial data. 381 | /// \internal 382 | void SetData(unsigned char *input); 383 | 384 | /// Gets the data that BitStream is writing to / reading from 385 | /// Partial bytes are left aligned. 386 | /// \return A pointer to the internal state 387 | inline unsigned char* GetData(void) const { return data; } 388 | 389 | /// Write numberToWrite bits from the input source Right aligned 390 | /// data means in the case of a partial byte, the bits are aligned 391 | /// from the right (bit 0) rather than the left (as in the normal 392 | /// internal representation) You would set this to true when 393 | /// writing user data, and false when copying bitstream data, such 394 | /// as writing one bitstream to another 395 | /// \param[in] input The data 396 | /// \param[in] numberOfBitsToWrite The number of bits to write 397 | /// \param[in] rightAlignedBits if true data will be right aligned 398 | void WriteBits(const unsigned char* input, int numberOfBitsToWrite, const bool rightAlignedBits = true); 399 | 400 | /// Align the bitstream to the byte boundary and then write the 401 | /// specified number of bits. This is faster than WriteBits but 402 | /// wastes the bits to do the alignment and requires you to call 403 | /// ReadAlignedBits at the corresponding read position. 404 | /// \param[in] input The data 405 | /// \param[in] numberOfBytesToWrite The size of data. 406 | void WriteAlignedBytes(const unsigned char *input, const int numberOfBytesToWrite); 407 | 408 | /// Read bits, starting at the next aligned bits. Note that the 409 | /// modulus 8 starting offset of the sequence must be the same as 410 | /// was used with WriteBits. This will be a problem with packet 411 | /// coalescence unless you byte align the coalesced packets. 412 | /// \param[in] output The byte array larger than @em numberOfBytesToRead 413 | /// \param[in] numberOfBytesToRead The number of byte to read from the internal state 414 | /// \return true if there is enough byte. 415 | bool ReadAlignedBytes(unsigned char *output, const int numberOfBytesToRead); 416 | 417 | /// Align the next write and/or read to a byte boundary. This can 418 | /// be used to 'waste' bits to byte align for efficiency reasons It 419 | /// can also be used to force coalesced bitstreams to start on byte 420 | /// boundaries so so WriteAlignedBits and ReadAlignedBits both 421 | /// calculate the same offset when aligning. 422 | void AlignWriteToByteBoundary(void); 423 | 424 | /// Align the next write and/or read to a byte boundary. This can 425 | /// be used to 'waste' bits to byte align for efficiency reasons It 426 | /// can also be used to force coalesced bitstreams to start on byte 427 | /// boundaries so so WriteAlignedBits and ReadAlignedBits both 428 | /// calculate the same offset when aligning. 429 | void AlignReadToByteBoundary(void); 430 | 431 | /// Read \a numberOfBitsToRead bits to the output source 432 | /// alignBitsToRight should be set to true to convert internal 433 | /// bitstream data to userdata. It should be false if you used 434 | /// WriteBits with rightAlignedBits false 435 | /// \param[in] output The resulting bits array 436 | /// \param[in] numberOfBitsToRead The number of bits to read 437 | /// \param[in] alignBitsToRight if true bits will be right aligned. 438 | /// \return true if there is enough bits to read 439 | bool ReadBits(unsigned char *output, int numberOfBitsToRead, const bool alignBitsToRight = true); 440 | 441 | /// Write a 0 442 | void Write0(void); 443 | 444 | /// Write a 1 445 | void Write1(void); 446 | 447 | /// Reads 1 bit and returns true if that bit is 1 and false if it is 0 448 | bool ReadBit(void); 449 | 450 | /// If we used the constructor version with copy data off, this 451 | /// *makes sure it is set to on and the data pointed to is copied. 452 | void AssertCopyData(void); 453 | 454 | /// Use this if you pass a pointer copy to the constructor 455 | /// *(_copyData==false) and want to overallocate to prevent 456 | /// *reallocation 457 | void SetNumberOfBitsAllocated(const unsigned int lengthInBits); 458 | 459 | /// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite 460 | void AddBitsAndReallocate(const int numberOfBitsToWrite); 461 | 462 | 463 | /// ---- Member function template specialization declarations ---- 464 | private: 465 | /// Assume the input source points to a native type, compress and write it. 466 | void WriteCompressed(const unsigned char* input, const int size, const bool unsignedData); 467 | 468 | /// Assume the input source points to a compressed native type. Decompress and read it. 469 | bool ReadCompressed(unsigned char* output, const int size, const bool unsignedData); 470 | 471 | void ReverseBytes(unsigned char *input, unsigned char *output, int length); 472 | 473 | bool DoEndianSwap(void) const; 474 | 475 | int numberOfBitsUsed; 476 | 477 | int numberOfBitsAllocated; 478 | 479 | int readOffset; 480 | 481 | unsigned char *data; 482 | 483 | /// true if the internal buffer is copy of the data passed to the constructor 484 | bool copyData; 485 | 486 | /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded 487 | unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; 488 | }; 489 | 490 | template 491 | inline bool BitStream::Serialize(bool writeToBitstream, templateType &var) 492 | { 493 | if (writeToBitstream) 494 | Write(var); 495 | else 496 | return Read(var); 497 | return true; 498 | } 499 | 500 | template 501 | inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType ¤tValue, templateType lastValue) 502 | { 503 | if (writeToBitstream) 504 | WriteDelta(currentValue, lastValue); 505 | else 506 | return ReadDelta(currentValue); 507 | return true; 508 | } 509 | 510 | template 511 | inline bool BitStream::SerializeDelta(bool writeToBitstream, templateType ¤tValue) 512 | { 513 | if (writeToBitstream) 514 | WriteDelta(currentValue); 515 | else 516 | return ReadDelta(currentValue); 517 | return true; 518 | } 519 | 520 | template 521 | inline bool BitStream::SerializeCompressed(bool writeToBitstream, templateType &var) 522 | { 523 | if (writeToBitstream) 524 | WriteCompressed(var); 525 | else 526 | return ReadCompressed(var); 527 | return true; 528 | } 529 | 530 | template 531 | inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType ¤tValue, templateType lastValue) 532 | { 533 | if (writeToBitstream) 534 | WriteCompressedDelta(currentValue, lastValue); 535 | else 536 | return ReadCompressedDelta(currentValue); 537 | return true; 538 | } 539 | 540 | template 541 | inline bool BitStream::SerializeCompressedDelta(bool writeToBitstream, templateType ¤tValue) 542 | { 543 | if (writeToBitstream) 544 | WriteCompressedDelta(currentValue); 545 | else 546 | return ReadCompressedDelta(currentValue); 547 | return true; 548 | } 549 | 550 | inline bool BitStream::Serialize(bool writeToBitstream, char* input, const int numberOfBytes) 551 | { 552 | if (writeToBitstream) 553 | Write(input, numberOfBytes); 554 | else 555 | return Read(input, numberOfBytes); 556 | return true; 557 | } 558 | 559 | template 560 | inline bool BitStream::SerializeNormVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z) 561 | { 562 | if (writeToBitstream) 563 | WriteNormVector(x, y, z); 564 | else 565 | return ReadNormVector(x, y, z); 566 | return true; 567 | } 568 | 569 | template 570 | inline bool BitStream::SerializeVector(bool writeToBitstream, templateType &x, templateType &y, templateType &z) 571 | { 572 | if (writeToBitstream) 573 | WriteVector(x, y, z); 574 | else 575 | return ReadVector(x, y, z); 576 | return true; 577 | } 578 | 579 | template 580 | inline bool BitStream::SerializeNormQuat(bool writeToBitstream, templateType &w, templateType &x, templateType &y, templateType &z) 581 | { 582 | if (writeToBitstream) 583 | WriteNormQuat(w, x, y, z); 584 | else 585 | return ReadNormQuat(w, x, y, z); 586 | return true; 587 | } 588 | 589 | template 590 | inline bool BitStream::SerializeOrthMatrix( 591 | bool writeToBitstream, 592 | templateType &m00, templateType &m01, templateType &m02, 593 | templateType &m10, templateType &m11, templateType &m12, 594 | templateType &m20, templateType &m21, templateType &m22) 595 | { 596 | if (writeToBitstream) 597 | WriteOrthMatrix(m00, m01, m02, m10, m11, m12, m20, m21, m22); 598 | else 599 | return ReadOrthMatrix(m00, m01, m02, m10, m11, m12, m20, m21, m22); 600 | return true; 601 | } 602 | 603 | inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits) 604 | { 605 | if (writeToBitstream) 606 | WriteBits(input, numberOfBitsToSerialize, rightAlignedBits); 607 | else 608 | return ReadBits(input, numberOfBitsToSerialize, rightAlignedBits); 609 | return true; 610 | } 611 | 612 | template 613 | inline void BitStream::Write(templateType var) 614 | { 615 | #ifdef _MSC_VER 616 | #pragma warning(disable:4127) // conditional expression is constant 617 | #endif 618 | if (sizeof(var) == 1) 619 | WriteBits((unsigned char*)& var, sizeof(templateType) * 8, true); 620 | else 621 | { 622 | #ifndef __BITSTREAM_NATIVE_END 623 | if (DoEndianSwap()) 624 | { 625 | unsigned char output[sizeof(templateType)]; 626 | ReverseBytes((unsigned char*)&var, output, sizeof(templateType)); 627 | WriteBits((unsigned char*)output, sizeof(templateType) * 8, true); 628 | } 629 | else 630 | #endif 631 | WriteBits((unsigned char*)& var, sizeof(templateType) * 8, true); 632 | } 633 | } 634 | 635 | /// Write a bool to a bitstream 636 | /// \param[in] var The value to write 637 | template <> 638 | inline void BitStream::Write(bool var) 639 | { 640 | if (var) 641 | Write1(); 642 | else 643 | Write0(); 644 | } 645 | 646 | /// Write a playerID to a bitstream 647 | /// \param[in] var The value to write 648 | template <> 649 | inline void BitStream::Write(PlayerID var) 650 | { 651 | Write(var.binaryAddress); 652 | Write(var.port); 653 | } 654 | 655 | /// Write any integral type to a bitstream. If the current value is different from the last value 656 | /// the current value will be written. Otherwise, a single bit will be written 657 | /// \param[in] currentValue The current value to write 658 | /// \param[in] lastValue The last value to compare against 659 | template 660 | inline void BitStream::WriteDelta(templateType currentValue, templateType lastValue) 661 | { 662 | if (currentValue == lastValue) 663 | { 664 | Write(false); 665 | } 666 | else 667 | { 668 | Write(true); 669 | Write(currentValue); 670 | } 671 | } 672 | 673 | /// Write a bool delta. Same thing as just calling Write 674 | /// \param[in] currentValue The current value to write 675 | /// \param[in] lastValue The last value to compare against 676 | template <> 677 | inline void BitStream::WriteDelta(bool currentValue, bool lastValue) 678 | { 679 | #ifdef _MSC_VER 680 | #pragma warning(disable:4100) // warning C4100: 'lastValue' : unreferenced formal parameter 681 | #endif 682 | Write(currentValue); 683 | } 684 | 685 | /// WriteDelta when you don't know what the last value is, or there is no last value. 686 | /// \param[in] currentValue The current value to write 687 | template 688 | inline void BitStream::WriteDelta(templateType currentValue) 689 | { 690 | Write(true); 691 | Write(currentValue); 692 | } 693 | 694 | /// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 695 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 696 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 697 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 698 | /// \param[in] var The value to write 699 | template 700 | inline void BitStream::WriteCompressed(templateType var) 701 | { 702 | #ifdef _MSC_VER 703 | #pragma warning(disable:4127) // conditional expression is constant 704 | #endif 705 | if (sizeof(var) == 1) 706 | WriteCompressed((unsigned char*)& var, sizeof(templateType) * 8, true); 707 | else 708 | { 709 | #ifndef __BITSTREAM_NATIVE_END 710 | #ifdef _MSC_VER 711 | #pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data 712 | #endif 713 | 714 | if (DoEndianSwap()) 715 | { 716 | unsigned char output[sizeof(templateType)]; 717 | ReverseBytes((unsigned char*)&var, output, sizeof(templateType)); 718 | WriteCompressed((unsigned char*)output, sizeof(templateType) * 8, true); 719 | } 720 | else 721 | #endif 722 | WriteCompressed((unsigned char*)& var, sizeof(templateType) * 8, true); 723 | } 724 | } 725 | 726 | template <> 727 | inline void BitStream::WriteCompressed(PlayerID var) 728 | { 729 | Write(var); 730 | } 731 | 732 | template <> 733 | inline void BitStream::WriteCompressed(bool var) 734 | { 735 | Write(var); 736 | } 737 | 738 | /// For values between -1 and 1 739 | template <> 740 | inline void BitStream::WriteCompressed(float var) 741 | { 742 | assert(var > -1.01f && var < 1.01f); 743 | if (var < -1.0f) 744 | var = -1.0f; 745 | if (var > 1.0f) 746 | var = 1.0f; 747 | Write((unsigned short)((var + 1.0f)*32767.5f)); 748 | } 749 | 750 | /// For values between -1 and 1 751 | template <> 752 | inline void BitStream::WriteCompressed(double var) 753 | { 754 | assert(var > -1.01 && var < 1.01); 755 | if (var < -1.0f) 756 | var = -1.0f; 757 | if (var > 1.0f) 758 | var = 1.0f; 759 | #ifdef _DEBUG 760 | assert(sizeof(unsigned long) == 4); 761 | #endif 762 | Write((unsigned long)((var + 1.0)*2147483648.0)); 763 | } 764 | 765 | /// Write any integral type to a bitstream. If the current value is different from the last value 766 | /// the current value will be written. Otherwise, a single bit will be written 767 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 768 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 769 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 770 | /// \param[in] currentValue The current value to write 771 | /// \param[in] lastValue The last value to compare against 772 | template 773 | inline void BitStream::WriteCompressedDelta(templateType currentValue, templateType lastValue) 774 | { 775 | if (currentValue == lastValue) 776 | { 777 | Write(false); 778 | } 779 | else 780 | { 781 | Write(true); 782 | WriteCompressed(currentValue); 783 | } 784 | } 785 | 786 | /// Write a bool delta. Same thing as just calling Write 787 | /// \param[in] currentValue The current value to write 788 | /// \param[in] lastValue The last value to compare against 789 | template <> 790 | inline void BitStream::WriteCompressedDelta(bool currentValue, bool lastValue) 791 | { 792 | #ifdef _MSC_VER 793 | #pragma warning(disable:4100) // warning C4100: 'lastValue' : unreferenced formal parameter 794 | #endif 795 | Write(currentValue); 796 | } 797 | 798 | /// Save as WriteCompressedDelta(templateType currentValue, templateType lastValue) when we have an unknown second parameter 799 | template 800 | inline void BitStream::WriteCompressedDelta(templateType currentValue) 801 | { 802 | Write(true); 803 | WriteCompressed(currentValue); 804 | } 805 | 806 | /// Save as WriteCompressedDelta(bool currentValue, templateType lastValue) when we have an unknown second bool 807 | template <> 808 | inline void BitStream::WriteCompressedDelta(bool currentValue) 809 | { 810 | Write(currentValue); 811 | } 812 | 813 | /// Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. 814 | /// \param[in] var The value to read 815 | template 816 | inline bool BitStream::Read(templateType &var) 817 | { 818 | #ifdef _MSC_VER 819 | #pragma warning(disable:4127) // conditional expression is constant 820 | #endif 821 | if (sizeof(var) == 1) 822 | return ReadBits((unsigned char*)&var, sizeof(templateType) * 8, true); 823 | else 824 | { 825 | #ifndef __BITSTREAM_NATIVE_END 826 | #ifdef _MSC_VER 827 | #pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data 828 | #endif 829 | if (DoEndianSwap()) 830 | { 831 | unsigned char output[sizeof(templateType)]; 832 | if (ReadBits((unsigned char*)output, sizeof(templateType) * 8, true)) 833 | { 834 | ReverseBytes(output, (unsigned char*)&var, sizeof(templateType)); 835 | return true; 836 | } 837 | return false; 838 | } 839 | else 840 | #endif 841 | return ReadBits((unsigned char*)& var, sizeof(templateType) * 8, true); 842 | } 843 | } 844 | 845 | /// Read a bool from a bitstream 846 | /// \param[in] var The value to read 847 | template <> 848 | inline bool BitStream::Read(bool &var) 849 | { 850 | if (readOffset + 1 > numberOfBitsUsed) 851 | return false; 852 | 853 | if (data[readOffset >> 3] & (0x80 >> (readOffset++ % 8))) // Is it faster to just write it out here? 854 | var = true; 855 | else 856 | var = false; 857 | 858 | return true; 859 | } 860 | 861 | /// Read a playerID from a bitstream 862 | /// \param[in] var The value to read 863 | template <> 864 | inline bool BitStream::Read(PlayerID &var) 865 | { 866 | Read(var.binaryAddress); 867 | return Read(var.port); 868 | } 869 | 870 | /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, 871 | /// var will be updated. Otherwise it will retain the current value. 872 | /// ReadDelta is only valid from a previous call to WriteDelta 873 | /// \param[in] var The value to read 874 | template 875 | inline bool BitStream::ReadDelta(templateType &var) 876 | { 877 | bool dataWritten; 878 | bool success; 879 | success = Read(dataWritten); 880 | if (dataWritten) 881 | success = Read(var); 882 | return success; 883 | } 884 | 885 | /// Read a bool from a bitstream 886 | /// \param[in] var The value to read 887 | template <> 888 | inline bool BitStream::ReadDelta(bool &var) 889 | { 890 | return Read(var); 891 | } 892 | 893 | /// Read any integral type from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. 894 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 895 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 896 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 897 | /// \param[in] var The value to read 898 | template 899 | inline bool BitStream::ReadCompressed(templateType &var) 900 | { 901 | #ifdef _MSC_VER 902 | #pragma warning(disable:4127) // conditional expression is constant 903 | #endif 904 | if (sizeof(var) == 1) 905 | return ReadCompressed((unsigned char*)&var, sizeof(templateType) * 8, true); 906 | else 907 | { 908 | #ifndef __BITSTREAM_NATIVE_END 909 | if (DoEndianSwap()) 910 | { 911 | unsigned char output[sizeof(templateType)]; 912 | if (ReadCompressed((unsigned char*)output, sizeof(templateType) * 8, true)) 913 | { 914 | ReverseBytes(output, (unsigned char*)&var, sizeof(templateType)); 915 | return true; 916 | } 917 | return false; 918 | } 919 | else 920 | #endif 921 | return ReadCompressed((unsigned char*)& var, sizeof(templateType) * 8, true); 922 | } 923 | } 924 | 925 | template <> 926 | inline bool BitStream::ReadCompressed(PlayerID &var) 927 | { 928 | return Read(var); 929 | } 930 | 931 | template <> 932 | inline bool BitStream::ReadCompressed(bool &var) 933 | { 934 | return Read(var); 935 | } 936 | 937 | /// For values between -1 and 1 938 | template <> 939 | inline bool BitStream::ReadCompressed(float &var) 940 | { 941 | unsigned short compressedFloat; 942 | if (Read(compressedFloat)) 943 | { 944 | var = ((float)compressedFloat / 32767.5f - 1.0f); 945 | return true; 946 | } 947 | return false; 948 | } 949 | 950 | /// For values between -1 and 1 951 | template <> 952 | inline bool BitStream::ReadCompressed(double &var) 953 | { 954 | unsigned long compressedFloat; 955 | if (Read(compressedFloat)) 956 | { 957 | var = ((double)compressedFloat / 2147483648.0 - 1.0); 958 | return true; 959 | } 960 | return false; 961 | } 962 | 963 | 964 | /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, 965 | /// var will be updated. Otherwise it will retain the current value. 966 | /// the current value will be updated. 967 | /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. 968 | /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type 969 | /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte 970 | /// ReadCompressedDelta is only valid from a previous call to WriteDelta 971 | /// \param[in] var The value to read 972 | template 973 | inline bool BitStream::ReadCompressedDelta(templateType &var) 974 | { 975 | bool dataWritten; 976 | bool success; 977 | success = Read(dataWritten); 978 | if (dataWritten) 979 | success = ReadCompressed(var); 980 | return success; 981 | } 982 | 983 | /// Read a bool from a bitstream 984 | /// \param[in] var The value to read 985 | template <> 986 | inline bool BitStream::ReadCompressedDelta(bool &var) 987 | { 988 | return Read(var); 989 | } 990 | 991 | 992 | template // templateType for this function must be a float or double 993 | void BitStream::WriteNormVector(templateType x, templateType y, templateType z) 994 | { 995 | #ifdef _DEBUG 996 | assert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); 997 | #endif 998 | if (x > 1.0) 999 | x = 1.0; 1000 | if (y > 1.0) 1001 | y = 1.0; 1002 | if (z > 1.0) 1003 | z = 1.0; 1004 | if (x < -1.0) 1005 | x = -1.0; 1006 | if (y < -1.0) 1007 | y = -1.0; 1008 | if (z < -1.0) 1009 | z = -1.0; 1010 | 1011 | Write((bool)(x < 0.0)); 1012 | if (y == 0.0) 1013 | Write(true); 1014 | else 1015 | { 1016 | Write(false); 1017 | WriteCompressed((float)y); 1018 | //Write((unsigned short)((y+1.0f)*32767.5f)); 1019 | } 1020 | if (z == 0.0) 1021 | Write(true); 1022 | else 1023 | { 1024 | Write(false); 1025 | WriteCompressed((float)z); 1026 | //Write((unsigned short)((z+1.0f)*32767.5f)); 1027 | } 1028 | } 1029 | 1030 | template // templateType for this function must be a float or double 1031 | void BitStream::WriteVector(templateType x, templateType y, templateType z) 1032 | { 1033 | templateType magnitude = sqrt(x * x + y * y + z * z); 1034 | Write((float)magnitude); 1035 | if (magnitude > 0.0) 1036 | { 1037 | WriteCompressed((float)(x / magnitude)); 1038 | WriteCompressed((float)(y / magnitude)); 1039 | WriteCompressed((float)(z / magnitude)); 1040 | // Write((unsigned short)((x/magnitude+1.0f)*32767.5f)); 1041 | // Write((unsigned short)((y/magnitude+1.0f)*32767.5f)); 1042 | // Write((unsigned short)((z/magnitude+1.0f)*32767.5f)); 1043 | } 1044 | } 1045 | 1046 | template // templateType for this function must be a float or double 1047 | void BitStream::WriteNormQuat(templateType w, templateType x, templateType y, templateType z) 1048 | { 1049 | Write((bool)(w < 0.0)); 1050 | Write((bool)(x < 0.0)); 1051 | Write((bool)(y < 0.0)); 1052 | Write((bool)(z < 0.0)); 1053 | Write((unsigned short)(fabs(x)*65535.0)); 1054 | Write((unsigned short)(fabs(y)*65535.0)); 1055 | Write((unsigned short)(fabs(z)*65535.0)); 1056 | // Leave out w and calculate it on the target 1057 | } 1058 | 1059 | template // templateType for this function must be a float or double 1060 | void BitStream::WriteOrthMatrix( 1061 | templateType m00, templateType m01, templateType m02, 1062 | templateType m10, templateType m11, templateType m12, 1063 | templateType m20, templateType m21, templateType m22) 1064 | { 1065 | double qw; 1066 | double qx; 1067 | double qy; 1068 | double qz; 1069 | 1070 | #ifdef _MSC_VER 1071 | #pragma warning(disable:4100) // m10, m01 : unreferenced formal parameter 1072 | #endif 1073 | 1074 | // Convert matrix to quat 1075 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ 1076 | float sum; 1077 | sum = 1 + m00 + m11 + m22; 1078 | if (sum < 0.0f) sum = 0.0f; 1079 | qw = sqrt(sum) / 2; 1080 | sum = 1 + m00 - m11 - m22; 1081 | if (sum < 0.0f) sum = 0.0f; 1082 | qx = sqrt(sum) / 2; 1083 | sum = 1 - m00 + m11 - m22; 1084 | if (sum < 0.0f) sum = 0.0f; 1085 | qy = sqrt(sum) / 2; 1086 | sum = 1 - m00 - m11 + m22; 1087 | if (sum < 0.0f) sum = 0.0f; 1088 | qz = sqrt(sum) / 2; 1089 | if (qw < 0.0) qw = 0.0; 1090 | if (qx < 0.0) qx = 0.0; 1091 | if (qy < 0.0) qy = 0.0; 1092 | if (qz < 0.0) qz = 0.0; 1093 | qx = _copysign(qx, m21 - m12); 1094 | qy = _copysign(qy, m02 - m20); 1095 | qz = _copysign(qz, m10 - m01); 1096 | 1097 | WriteNormQuat(qw, qx, qy, qz); 1098 | } 1099 | 1100 | template // templateType for this function must be a float or double 1101 | bool BitStream::ReadNormVector(templateType &x, templateType &y, templateType &z) 1102 | { 1103 | // unsigned short sy, sz; 1104 | bool yZero, zZero; 1105 | bool xNeg; 1106 | float cy, cz; 1107 | 1108 | Read(xNeg); 1109 | 1110 | Read(yZero); 1111 | if (yZero) 1112 | y = 0.0; 1113 | else 1114 | { 1115 | ReadCompressed(cy); 1116 | y = cy; 1117 | //Read(sy); 1118 | //y=((float)sy / 32767.5f - 1.0f); 1119 | } 1120 | 1121 | if (!Read(zZero)) 1122 | return false; 1123 | 1124 | if (zZero) 1125 | z = 0.0; 1126 | else 1127 | { 1128 | // if (!Read(sz)) 1129 | // return false; 1130 | 1131 | // z=((float)sz / 32767.5f - 1.0f); 1132 | if (!ReadCompressed(cz)) 1133 | return false; 1134 | z = cz; 1135 | } 1136 | 1137 | x = (templateType)(sqrtf((templateType)1.0 - y*y - z*z)); 1138 | if (xNeg) 1139 | x = -x; 1140 | return true; 1141 | } 1142 | 1143 | template // templateType for this function must be a float or double 1144 | bool BitStream::ReadVector(templateType &x, templateType &y, templateType &z) 1145 | { 1146 | float magnitude; 1147 | //unsigned short sx,sy,sz; 1148 | if (!Read(magnitude)) 1149 | return false; 1150 | if (magnitude != 0.0) 1151 | { 1152 | // Read(sx); 1153 | // Read(sy); 1154 | // if (!Read(sz)) 1155 | // return false; 1156 | // x=((float)sx / 32767.5f - 1.0f) * magnitude; 1157 | // y=((float)sy / 32767.5f - 1.0f) * magnitude; 1158 | // z=((float)sz / 32767.5f - 1.0f) * magnitude; 1159 | float cx, cy, cz; 1160 | ReadCompressed(cx); 1161 | ReadCompressed(cy); 1162 | if (!ReadCompressed(cz)) 1163 | return false; 1164 | x = cx; 1165 | y = cy; 1166 | z = cz; 1167 | x *= magnitude; 1168 | y *= magnitude; 1169 | z *= magnitude; 1170 | } 1171 | else 1172 | { 1173 | x = 0.0; 1174 | y = 0.0; 1175 | z = 0.0; 1176 | } 1177 | return true; 1178 | } 1179 | 1180 | template // templateType for this function must be a float or double 1181 | bool BitStream::ReadNormQuat(templateType &w, templateType &x, templateType &y, templateType &z) 1182 | { 1183 | bool cwNeg, cxNeg, cyNeg, czNeg; 1184 | unsigned short cx, cy, cz; 1185 | Read(cwNeg); 1186 | Read(cxNeg); 1187 | Read(cyNeg); 1188 | Read(czNeg); 1189 | Read(cx); 1190 | Read(cy); 1191 | if (!Read(cz)) 1192 | return false; 1193 | 1194 | // Calculate w from x,y,z 1195 | x = (templateType)(cx / 65535.0); 1196 | y = (templateType)(cy / 65535.0); 1197 | z = (templateType)(cz / 65535.0); 1198 | if (cxNeg) x = -x; 1199 | if (cyNeg) y = -y; 1200 | if (czNeg) z = -z; 1201 | float difference = 1.0f - x*x - y*y - z*z; 1202 | if (difference < 0.0f) 1203 | difference = 0.0f; 1204 | w = (templateType)(sqrt(difference)); 1205 | if (cwNeg) 1206 | w = -w; 1207 | return true; 1208 | } 1209 | 1210 | template // templateType for this function must be a float or double 1211 | bool BitStream::ReadOrthMatrix( 1212 | templateType &m00, templateType &m01, templateType &m02, 1213 | templateType &m10, templateType &m11, templateType &m12, 1214 | templateType &m20, templateType &m21, templateType &m22) 1215 | { 1216 | float qw, qx, qy, qz; 1217 | if (!ReadNormQuat(qw, qx, qy, qz)) 1218 | return false; 1219 | 1220 | // Quat to orthogonal rotation matrix 1221 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm 1222 | double sqw = (double)qw*(double)qw; 1223 | double sqx = (double)qx*(double)qx; 1224 | double sqy = (double)qy*(double)qy; 1225 | double sqz = (double)qz*(double)qz; 1226 | m00 = (templateType)(sqx - sqy - sqz + sqw); // since sqw + sqx + sqy + sqz =1 1227 | m11 = (templateType)(-sqx + sqy - sqz + sqw); 1228 | m22 = (templateType)(-sqx - sqy + sqz + sqw); 1229 | 1230 | double tmp1 = (double)qx*(double)qy; 1231 | double tmp2 = (double)qz*(double)qw; 1232 | m10 = (templateType)(2.0 * (tmp1 + tmp2)); 1233 | m01 = (templateType)(2.0 * (tmp1 - tmp2)); 1234 | 1235 | tmp1 = (double)qx*(double)qz; 1236 | tmp2 = (double)qy*(double)qw; 1237 | m20 = (templateType)(2.0 * (tmp1 - tmp2)); 1238 | m02 = (templateType)(2.0 * (tmp1 + tmp2)); 1239 | tmp1 = (double)qy*(double)qz; 1240 | tmp2 = (double)qx*(double)qw; 1241 | m21 = (templateType)(2.0 * (tmp1 + tmp2)); 1242 | m12 = (templateType)(2.0 * (tmp1 - tmp2)); 1243 | 1244 | return true; 1245 | } 1246 | } 1247 | 1248 | #ifdef _MSC_VER 1249 | #pragma warning( pop ) 1250 | #endif 1251 | 1252 | #endif -------------------------------------------------------------------------------- /lib/RakNet/NetworkTypes.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief Types used by RakNet, most of which involve user code. 3 | /// 4 | /// This file is part of RakNet Copyright 2003 Kevin Jenkins. 5 | /// 6 | /// Usage of RakNet is subject to the appropriate license agreement. 7 | /// Creative Commons Licensees are subject to the 8 | /// license found at 9 | /// http://creativecommons.org/licenses/by-nc/2.5/ 10 | /// Single application licensees are subject to the license found at 11 | /// http://www.rakkarsoft.com/SingleApplicationLicense.html 12 | /// Custom license users are subject to the terms therein. 13 | /// GPL license users are subject to the GNU General Public 14 | /// License as published by the Free 15 | /// Software Foundation; either version 2 of the License, or (at your 16 | /// option) any later version. 17 | 18 | #ifndef __NETWORK_TYPES_H 19 | #define __NETWORK_TYPES_H 20 | 21 | /// Forward declaration 22 | namespace RakNet 23 | { 24 | class BitStream; 25 | }; 26 | 27 | struct RPCParameters; 28 | 29 | #define BITS_TO_BYTES(x) (((x)+7)>>3) 30 | #define BYTES_TO_BITS(x) ((x)<<3) 31 | 32 | typedef unsigned short PlayerIndex; 33 | typedef unsigned char RPCIndex; 34 | const int MAX_RPC_MAP_SIZE = ((RPCIndex)-1) - 1; 35 | 36 | using RPCFunction = void(*)(RPCParameters *p); 37 | 38 | #pragma pack(push, 1) 39 | 40 | struct PlayerID 41 | { 42 | unsigned int binaryAddress; 43 | unsigned short port; 44 | }; 45 | 46 | struct Packet 47 | { 48 | PlayerIndex playerIndex; 49 | PlayerID playerId; 50 | unsigned int length; 51 | unsigned int bitSize; 52 | unsigned char* data; 53 | bool deleteData; 54 | }; 55 | 56 | struct RPCParameters 57 | { 58 | unsigned char *input; 59 | unsigned int numberOfBitsOfData; 60 | PlayerID sender; 61 | }; 62 | 63 | #pragma pack(pop) 64 | 65 | #endif -------------------------------------------------------------------------------- /lib/sdk/amx/amx.h: -------------------------------------------------------------------------------- 1 | /* Pawn Abstract Machine (for the Pawn language) 2 | * 3 | * Copyright (c) ITB CompuPhase, 1997-2005 4 | * 5 | * This software is provided "as-is", without any express or implied warranty. 6 | * In no event will the authors be held liable for any damages arising from 7 | * the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute it 11 | * freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must not 14 | * claim that you wrote the original software. If you use this software in 15 | * a product, an acknowledgment in the product documentation would be 16 | * appreciated but is not required. 17 | * 2. Altered source versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. 19 | * 3. This notice may not be removed or altered from any source distribution. 20 | * 21 | * Version: $Id: amx.h,v 1.5 2006/03/26 16:56:15 spookie Exp $ 22 | */ 23 | 24 | #if defined FREEBSD && !defined __FreeBSD__ 25 | #define __FreeBSD__ 26 | #endif 27 | #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ 28 | #include 29 | #endif 30 | 31 | #ifndef AMX_H_INCLUDED 32 | #define AMX_H_INCLUDED 33 | 34 | #if defined HAVE_STDINT_H 35 | #include 36 | #else 37 | #if defined __LCC__ || defined __DMC__ || defined LINUX 38 | #if defined HAVE_INTTYPES_H 39 | #include 40 | #else 41 | #include 42 | #endif 43 | #elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L 44 | /* The ISO C99 defines the int16_t and int_32t types. If the compiler got 45 | * here, these types are probably undefined. 46 | */ 47 | #if defined __MACH__ 48 | #include 49 | typedef unsigned short int uint16_t; 50 | typedef unsigned long int uint32_t; 51 | #elif defined __FreeBSD__ 52 | #include 53 | #else 54 | typedef short int int16_t; 55 | typedef unsigned short int uint16_t; 56 | #if defined SN_TARGET_PS2 57 | typedef int int32_t; 58 | typedef unsigned int uint32_t; 59 | #else 60 | typedef long int int32_t; 61 | typedef unsigned long int uint32_t; 62 | #endif 63 | #if defined __WIN32__ || defined _WIN32 || defined WIN32 64 | typedef __int64 int64_t; 65 | typedef unsigned __int64 uint64_t; 66 | #define HAVE_I64 67 | #elif defined __GNUC__ 68 | typedef long long int64_t; 69 | typedef unsigned long long uint64_t; 70 | #define HAVE_I64 71 | #endif 72 | #endif 73 | #endif 74 | #define HAVE_STDINT_H 75 | #endif 76 | #if defined _LP64 || defined WIN64 || defined _WIN64 77 | #if !defined __64BIT__ 78 | #define __64BIT__ 79 | #endif 80 | #endif 81 | 82 | #if HAVE_ALLOCA_H 83 | #include 84 | #endif 85 | #if defined __WIN32__ || defined _WIN32 || defined WIN32 /* || defined __MSDOS__ */ 86 | #if !defined alloca 87 | #define alloca(n) _alloca(n) 88 | #endif 89 | #endif 90 | 91 | #if !defined arraysize 92 | #define arraysize(array) (sizeof(array) / sizeof((array)[0])) 93 | #endif 94 | 95 | #ifdef __cplusplus 96 | extern "C" { 97 | #endif 98 | 99 | #if defined PAWN_DLL 100 | #if !defined AMX_NATIVE_CALL 101 | #define AMX_NATIVE_CALL __stdcall 102 | #endif 103 | #if !defined AMXAPI 104 | #define AMXAPI __stdcall 105 | #endif 106 | #endif 107 | 108 | /* calling convention for native functions */ 109 | #if !defined AMX_NATIVE_CALL 110 | #define AMX_NATIVE_CALL 111 | #endif 112 | /* calling convention for all interface functions and callback functions */ 113 | #if !defined AMXAPI 114 | #if defined STDECL 115 | #define AMXAPI __stdcall 116 | #elif defined CDECL 117 | #define AMXAPI __cdecl 118 | #elif defined GCC_HASCLASSVISIBILITY 119 | #define AMXAPI __attribute__ ((visibility("default"))) 120 | #else 121 | #define AMXAPI 122 | #endif 123 | #endif 124 | #if !defined AMXEXPORT 125 | #define AMXEXPORT 126 | #endif 127 | 128 | /* File format version Required AMX version 129 | * 0 (original version) 0 130 | * 1 (opcodes JUMP.pri, SWITCH and CASETBL) 1 131 | * 2 (compressed files) 2 132 | * 3 (public variables) 2 133 | * 4 (opcodes SWAP.pri/alt and PUSHADDR) 4 134 | * 5 (tagnames table) 4 135 | * 6 (reformatted header) 6 136 | * 7 (name table, opcodes SYMTAG & SYSREQ.D) 7 137 | * 8 (opcode STMT, renewed debug interface) 8 138 | */ 139 | #define CUR_FILE_VERSION 8 /* current file version; also the current AMX version */ 140 | #define MIN_FILE_VERSION 6 /* lowest supported file format version for the current AMX version */ 141 | #define MIN_AMX_VERSION 8 /* minimum AMX version needed to support the current file format */ 142 | 143 | #if !defined PAWN_CELL_SIZE 144 | #define PAWN_CELL_SIZE 32 /* by default, use 32-bit cells */ 145 | #endif 146 | #if PAWN_CELL_SIZE==16 147 | typedef uint16_t ucell; 148 | typedef int16_t cell; 149 | #elif PAWN_CELL_SIZE==32 150 | typedef uint32_t ucell; 151 | typedef int32_t cell; 152 | #elif PAWN_CELL_SIZE==64 153 | typedef uint64_t ucell; 154 | typedef int64_t cell; 155 | #else 156 | #error Unsupported cell size (PAWN_CELL_SIZE) 157 | #endif 158 | 159 | #define UNPACKEDMAX ((1L << (sizeof(cell)-1)*8) - 1) 160 | #define UNLIMITED (~1u >> 1) 161 | 162 | struct tagAMX; 163 | typedef cell (AMX_NATIVE_CALL *AMX_NATIVE)(struct tagAMX *amx, cell *params); 164 | typedef int (AMXAPI *AMX_CALLBACK)(struct tagAMX *amx, cell index, 165 | cell *result, cell *params); 166 | typedef int (AMXAPI *AMX_DEBUG)(struct tagAMX *amx); 167 | #if !defined _FAR 168 | #define _FAR 169 | #endif 170 | 171 | #if defined _MSC_VER 172 | #pragma warning(disable:4103) /* disable warning message 4103 that complains 173 | * about pragma pack in a header file */ 174 | #pragma warning(disable:4100) /* "'%$S' : unreferenced formal parameter" */ 175 | #endif 176 | 177 | /* Some compilers do not support the #pragma align, which should be fine. Some 178 | * compilers give a warning on unknown #pragmas, which is not so fine... 179 | */ 180 | #if (defined SN_TARGET_PS2 || defined __GNUC__) && !defined AMX_NO_ALIGN 181 | #define AMX_NO_ALIGN 182 | #endif 183 | 184 | #if defined __GNUC__ 185 | #define PACKED __attribute__((packed)) 186 | #else 187 | #define PACKED 188 | #endif 189 | 190 | #if !defined AMX_NO_ALIGN 191 | #if defined LINUX || defined __FreeBSD__ 192 | #pragma pack(1) /* structures must be packed (byte-aligned) */ 193 | #elif defined MACOS && defined __MWERKS__ 194 | #pragma options align=mac68k 195 | #else 196 | #pragma pack(push) 197 | #pragma pack(1) /* structures must be packed (byte-aligned) */ 198 | #if defined __TURBOC__ 199 | #pragma option -a- /* "pack" pragma for older Borland compilers */ 200 | #endif 201 | #endif 202 | #endif 203 | 204 | typedef struct tagAMX_NATIVE_INFO { 205 | const char _FAR *name PACKED; 206 | AMX_NATIVE func PACKED; 207 | } PACKED AMX_NATIVE_INFO; 208 | 209 | #define AMX_USERNUM 4 210 | #define sEXPMAX 19 /* maximum name length for file version <= 6 */ 211 | #define sNAMEMAX 31 /* maximum name length of symbol name */ 212 | 213 | typedef struct tagAMX_FUNCSTUB { 214 | ucell address PACKED; 215 | char name[sEXPMAX+1] PACKED; 216 | } PACKED AMX_FUNCSTUB; 217 | 218 | typedef struct tagFUNCSTUBNT { 219 | ucell address PACKED; 220 | uint32_t nameofs PACKED; 221 | } PACKED AMX_FUNCSTUBNT; 222 | 223 | /* The AMX structure is the internal structure for many functions. Not all 224 | * fields are valid at all times; many fields are cached in local variables. 225 | */ 226 | typedef struct tagAMX { 227 | unsigned char _FAR *base PACKED; /* points to the AMX header plus the code, optionally also the data */ 228 | unsigned char _FAR *data PACKED; /* points to separate data+stack+heap, may be NULL */ 229 | AMX_CALLBACK callback PACKED; 230 | AMX_DEBUG debug PACKED; /* debug callback */ 231 | /* for external functions a few registers must be accessible from the outside */ 232 | cell cip PACKED; /* instruction pointer: relative to base + amxhdr->cod */ 233 | cell frm PACKED; /* stack frame base: relative to base + amxhdr->dat */ 234 | cell hea PACKED; /* top of the heap: relative to base + amxhdr->dat */ 235 | cell hlw PACKED; /* bottom of the heap: relative to base + amxhdr->dat */ 236 | cell stk PACKED; /* stack pointer: relative to base + amxhdr->dat */ 237 | cell stp PACKED; /* top of the stack: relative to base + amxhdr->dat */ 238 | int flags PACKED; /* current status, see amx_Flags() */ 239 | /* user data */ 240 | long usertags[AMX_USERNUM] PACKED; 241 | void _FAR *userdata[AMX_USERNUM] PACKED; 242 | /* native functions can raise an error */ 243 | int error PACKED; 244 | /* passing parameters requires a "count" field */ 245 | int paramcount; 246 | /* the sleep opcode needs to store the full AMX status */ 247 | cell pri PACKED; 248 | cell alt PACKED; 249 | cell reset_stk PACKED; 250 | cell reset_hea PACKED; 251 | cell sysreq_d PACKED; /* relocated address/value for the SYSREQ.D opcode */ 252 | #if defined JIT 253 | /* support variables for the JIT */ 254 | int reloc_size PACKED; /* required temporary buffer for relocations */ 255 | long code_size PACKED; /* estimated memory footprint of the native code */ 256 | #endif 257 | } PACKED AMX; 258 | 259 | /* The AMX_HEADER structure is both the memory format as the file format. The 260 | * structure is used internaly. 261 | */ 262 | typedef struct tagAMX_HEADER { 263 | int32_t size PACKED; /* size of the "file" */ 264 | uint16_t magic PACKED; /* signature */ 265 | char file_version PACKED; /* file format version */ 266 | char amx_version PACKED; /* required version of the AMX */ 267 | int16_t flags PACKED; 268 | int16_t defsize PACKED; /* size of a definition record */ 269 | int32_t cod PACKED; /* initial value of COD - code block */ 270 | int32_t dat PACKED; /* initial value of DAT - data block */ 271 | int32_t hea PACKED; /* initial value of HEA - start of the heap */ 272 | int32_t stp PACKED; /* initial value of STP - stack top */ 273 | int32_t cip PACKED; /* initial value of CIP - the instruction pointer */ 274 | int32_t publics PACKED; /* offset to the "public functions" table */ 275 | int32_t natives PACKED; /* offset to the "native functions" table */ 276 | int32_t libraries PACKED; /* offset to the table of libraries */ 277 | int32_t pubvars PACKED; /* the "public variables" table */ 278 | int32_t tags PACKED; /* the "public tagnames" table */ 279 | int32_t nametable PACKED; /* name table */ 280 | } PACKED AMX_HEADER; 281 | 282 | #if PAWN_CELL_SIZE==16 283 | #define AMX_MAGIC 0xf1e2 284 | #elif PAWN_CELL_SIZE==32 285 | #define AMX_MAGIC 0xf1e0 286 | #elif PAWN_CELL_SIZE==64 287 | #define AMX_MAGIC 0xf1e1 288 | #endif 289 | 290 | enum { 291 | AMX_ERR_NONE, 292 | /* reserve the first 15 error codes for exit codes of the abstract machine */ 293 | AMX_ERR_EXIT, /* forced exit */ 294 | AMX_ERR_ASSERT, /* assertion failed */ 295 | AMX_ERR_STACKERR, /* stack/heap collision */ 296 | AMX_ERR_BOUNDS, /* index out of bounds */ 297 | AMX_ERR_MEMACCESS, /* invalid memory access */ 298 | AMX_ERR_INVINSTR, /* invalid instruction */ 299 | AMX_ERR_STACKLOW, /* stack underflow */ 300 | AMX_ERR_HEAPLOW, /* heap underflow */ 301 | AMX_ERR_CALLBACK, /* no callback, or invalid callback */ 302 | AMX_ERR_NATIVE, /* native function failed */ 303 | AMX_ERR_DIVIDE, /* divide by zero */ 304 | AMX_ERR_SLEEP, /* go into sleepmode - code can be restarted */ 305 | AMX_ERR_INVSTATE, /* invalid state for this access */ 306 | 307 | AMX_ERR_MEMORY = 16, /* out of memory */ 308 | AMX_ERR_FORMAT, /* invalid file format */ 309 | AMX_ERR_VERSION, /* file is for a newer version of the AMX */ 310 | AMX_ERR_NOTFOUND, /* function not found */ 311 | AMX_ERR_INDEX, /* invalid index parameter (bad entry point) */ 312 | AMX_ERR_DEBUG, /* debugger cannot run */ 313 | AMX_ERR_INIT, /* AMX not initialized (or doubly initialized) */ 314 | AMX_ERR_USERDATA, /* unable to set user data field (table full) */ 315 | AMX_ERR_INIT_JIT, /* cannot initialize the JIT */ 316 | AMX_ERR_PARAMS, /* parameter error */ 317 | AMX_ERR_DOMAIN, /* domain error, expression result does not fit in range */ 318 | AMX_ERR_GENERAL, /* general error (unknown or unspecific error) */ 319 | }; 320 | 321 | /* AMX_FLAG_CHAR16 0x01 no longer used */ 322 | #define AMX_FLAG_DEBUG 0x02 /* symbolic info. available */ 323 | #define AMX_FLAG_COMPACT 0x04 /* compact encoding */ 324 | #define AMX_FLAG_BYTEOPC 0x08 /* opcode is a byte (not a cell) */ 325 | #define AMX_FLAG_NOCHECKS 0x10 /* no array bounds checking; no STMT opcode */ 326 | #define AMX_FLAG_NTVREG 0x1000 /* all native functions are registered */ 327 | #define AMX_FLAG_JITC 0x2000 /* abstract machine is JIT compiled */ 328 | #define AMX_FLAG_BROWSE 0x4000 /* busy browsing */ 329 | #define AMX_FLAG_RELOC 0x8000 /* jump/call addresses relocated */ 330 | 331 | #define AMX_EXEC_MAIN -1 /* start at program entry point */ 332 | #define AMX_EXEC_CONT -2 /* continue from last address */ 333 | 334 | #define AMX_USERTAG(a,b,c,d) ((a) | ((b)<<8) | ((long)(c)<<16) | ((long)(d)<<24)) 335 | 336 | #if !defined AMX_COMPACTMARGIN 337 | #define AMX_COMPACTMARGIN 64 338 | #endif 339 | 340 | /* for native functions that use floating point parameters, the following 341 | * two macros are convenient for casting a "cell" into a "float" type _without_ 342 | * changing the bit pattern 343 | */ 344 | #if PAWN_CELL_SIZE==32 345 | #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ 346 | #define amx_ctof(c) ( * ((float*)&c) ) /* cell to float */ 347 | #elif PAWN_CELL_SIZE==64 348 | #define amx_ftoc(f) ( * ((cell*)&f) ) /* float to cell */ 349 | #define amx_ctof(c) ( * ((double*)&c) ) /* cell to float */ 350 | #else 351 | #error Unsupported cell size 352 | #endif 353 | 354 | #define amx_StrParam(amx,param,result) \ 355 | do { \ 356 | cell *amx_cstr_; int amx_length_; \ 357 | amx_GetAddr((amx), (param), &amx_cstr_); \ 358 | amx_StrLen(amx_cstr_, &amx_length_); \ 359 | if (amx_length_ > 0 && \ 360 | ((result) = (char*)alloca((amx_length_ + 1) * sizeof(*(result)))) != NULL) \ 361 | amx_GetString((char*)(result), amx_cstr_, sizeof(*(result))>1, amx_length_ + 1); \ 362 | else (result) = NULL; \ 363 | } while (0) 364 | 365 | uint16_t * AMXAPI amx_Align16(uint16_t *v); 366 | uint32_t * AMXAPI amx_Align32(uint32_t *v); 367 | #if defined _I64_MAX || defined HAVE_I64 368 | uint64_t * AMXAPI amx_Align64(uint64_t *v); 369 | #endif 370 | int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); 371 | int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params); 372 | int AMXAPI amx_Cleanup(AMX *amx); 373 | int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data); 374 | int AMXAPI amx_Exec(AMX *amx, cell *retval, int index); 375 | int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index); 376 | int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index); 377 | int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr); 378 | int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname); 379 | int AMXAPI amx_Flags(AMX *amx,uint16_t *flags); 380 | int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr); 381 | int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname); 382 | int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname); 383 | int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr); 384 | int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size); 385 | int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id); 386 | int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr); 387 | int AMXAPI amx_Init(AMX *amx, void *program); 388 | int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code); 389 | int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap); 390 | int AMXAPI amx_NameLength(AMX *amx, int *length); 391 | AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func); 392 | int AMXAPI amx_NumNatives(AMX *amx, int *number); 393 | int AMXAPI amx_NumPublics(AMX *amx, int *number); 394 | int AMXAPI amx_NumPubVars(AMX *amx, int *number); 395 | int AMXAPI amx_NumTags(AMX *amx, int *number); 396 | int AMXAPI amx_Push(AMX *amx, cell value); 397 | int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); 398 | int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); 399 | int AMXAPI amx_RaiseError(AMX *amx, int error); 400 | int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); 401 | int AMXAPI amx_Release(AMX *amx, cell amx_addr); 402 | int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback); 403 | int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug); 404 | int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size); 405 | int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr); 406 | int AMXAPI amx_StrLen(const cell *cstring, int *length); 407 | int AMXAPI amx_UTF8Check(const char *string, int *length); 408 | int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value); 409 | int AMXAPI amx_UTF8Len(const cell *cstr, int *length); 410 | int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value); 411 | 412 | #if PAWN_CELL_SIZE==16 413 | #define amx_AlignCell(v) amx_Align16(v) 414 | #elif PAWN_CELL_SIZE==32 415 | #define amx_AlignCell(v) amx_Align32(v) 416 | #elif PAWN_CELL_SIZE==64 && (defined _I64_MAX || defined HAVE_I64) 417 | #define amx_AlignCell(v) amx_Align64(v) 418 | #else 419 | #error Unsupported cell size 420 | #endif 421 | 422 | #define amx_RegisterFunc(amx, name, func) \ 423 | amx_Register((amx), amx_NativeInfo((name),(func)), 1); 424 | 425 | #if !defined AMX_NO_ALIGN 426 | #if defined LINUX || defined __FreeBSD__ 427 | #pragma pack() /* reset default packing */ 428 | #elif defined MACOS && defined __MWERKS__ 429 | #pragma options align=reset 430 | #else 431 | #pragma pack(pop) /* reset previous packing */ 432 | #endif 433 | #endif 434 | 435 | #ifdef __cplusplus 436 | } 437 | #endif 438 | 439 | #endif /* AMX_H_INCLUDED */ 440 | -------------------------------------------------------------------------------- /lib/sdk/amx/amx2.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2013 SA-MP Team, Dan 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #pragma once 9 | 10 | //---------------------------------------------------------- 11 | 12 | #include 13 | 14 | //---------------------------------------------------------- 15 | 16 | #include "amx.h" 17 | 18 | //---------------------------------------------------------- 19 | 20 | #define USENAMETABLE(hdr) \ 21 | ((hdr)->defsize==sizeof(AMX_FUNCSTUBNT)) 22 | 23 | #define NUMENTRIES(hdr,field,nextfield) \ 24 | (unsigned)(((hdr)->nextfield - (hdr)->field) / (hdr)->defsize) 25 | 26 | #define GETENTRY(hdr,table,index) \ 27 | (AMX_FUNCSTUB *)((unsigned char*)(hdr) + (unsigned)(hdr)->table + (unsigned)index*(hdr)->defsize) 28 | 29 | #define GETENTRYNAME(hdr,entry) \ 30 | (USENAMETABLE(hdr) ? \ 31 | (char *)((unsigned char*)(hdr) + (unsigned)((AMX_FUNCSTUBNT*)(entry))->nameofs) : \ 32 | ((AMX_FUNCSTUB*)(entry))->name) 33 | 34 | //---------------------------------------------------------- 35 | 36 | extern int AMXAPI amx_PushAddress(AMX *amx, cell *address); 37 | extern void AMXAPI amx_Redirect(AMX *amx, const char *from, ucell to, AMX_NATIVE *store); 38 | extern int AMXAPI amx_GetCString(AMX *amx, cell param, char *&dest); 39 | extern void AMXAPI amx_SetCString(AMX *amx, cell param, const char *str, int len); 40 | extern std::string AMXAPI amx_GetCppString(AMX *amx, cell param); 41 | extern void AMXAPI amx_SetCppString(AMX *amx, cell param, std::string str, int len); 42 | 43 | //---------------------------------------------------------- 44 | // EOF 45 | -------------------------------------------------------------------------------- /lib/sdk/amx/getch.c: -------------------------------------------------------------------------------- 1 | /* Extremely inefficient but portable POSIX getch() */ 2 | #ifndef WIN32 3 | 4 | #include 5 | #include 6 | #include /* for tcgetattr() and tcsetattr() */ 7 | #include /* for read() */ 8 | #include 9 | #include 10 | #include 11 | #include "getch.h" 12 | 13 | #ifndef STDIN_FILENO 14 | # define STDIN_FILENO 0 15 | #endif 16 | 17 | int 18 | getch (void) 19 | { 20 | struct termios save_termios; 21 | struct termios ios; 22 | int c = 0; 23 | 24 | if (!isatty (STDIN_FILENO)) 25 | return EOF; 26 | 27 | if (tcgetattr (STDIN_FILENO, &save_termios) < 0) 28 | return EOF; 29 | 30 | ios = save_termios; 31 | ios.c_lflag &= ~(ICANON | ECHO | ISIG); 32 | ios.c_cc[VMIN] = 1; /* read() will return with one char */ 33 | ios.c_cc[VTIME] = 0; /* read() blocks forever */ 34 | 35 | if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) 36 | return EOF; 37 | 38 | if (read (STDIN_FILENO, &c, 1) != 1) 39 | c = EOF; 40 | 41 | tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); 42 | 43 | return c; 44 | } 45 | 46 | int 47 | kbhit (void) 48 | { 49 | struct termios save_termios; 50 | struct termios ios; 51 | fd_set inp; 52 | struct timeval timeout = {0, 0}; 53 | int result; 54 | 55 | if (!isatty (STDIN_FILENO)) 56 | return 0; 57 | 58 | if (tcgetattr (STDIN_FILENO, &save_termios) < 0) 59 | return 0; 60 | 61 | ios = save_termios; 62 | ios.c_lflag &= ~(ICANON | ECHO | ISIG); 63 | ios.c_cc[VMIN] = 1; /* read() will return with one char */ 64 | ios.c_cc[VTIME] = 0; /* read() blocks forever */ 65 | 66 | if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) 67 | return 0; 68 | 69 | /* set up select() args */ 70 | FD_ZERO(&inp); 71 | FD_SET(STDIN_FILENO, &inp); 72 | 73 | result = select (STDIN_FILENO+1, &inp, NULL, NULL, &timeout) == 1; 74 | 75 | tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); 76 | 77 | return result; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /lib/sdk/amx/getch.h: -------------------------------------------------------------------------------- 1 | /* Extremely inefficient but portable POSIX getch(), see getch.c */ 2 | #ifndef GETCH_H 3 | #define GETCH_H 4 | 5 | #if defined __cplusplus 6 | extern "C" { 7 | #endif 8 | int getch(void); 9 | int kbhit(void); 10 | 11 | #if defined __cplusplus 12 | } 13 | #endif 14 | 15 | #endif /* GETCH_H */ 16 | -------------------------------------------------------------------------------- /lib/sdk/amx/sclinux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Things needed to compile under linux. 3 | * 4 | * Should be reworked totally to use GNU's 'configure' 5 | */ 6 | #ifndef SCLINUX_H 7 | #define SCLINUX_H 8 | 9 | /* getchar() is not a 'cool' replacement for MSDOS getch: Linux/unix depends on the features activated or not about the 10 | * controlling terminal's tty. This means that ioctl(2) calls must be performed, for instance to have the controlling 11 | * terminal tty's in 'raw' mode, if we want to be able to fetch a single character. This also means that everything must 12 | * be put back correctly when the function ends. See GETCH.C for an implementation. 13 | * 14 | * For interactive use of SRUN/SDBG if would be much better to use GNU's readline package: the user would be able to 15 | * have a complete emacs/vi like line editing system. 16 | */ 17 | #include "getch.h" 18 | 19 | #define stricmp(a,b) strcasecmp(a,b) 20 | #define strnicmp(a,b,c) strncasecmp(a,b,c) 21 | 22 | /* 23 | * WinWorld wants '\'. Unices do not. 24 | */ 25 | #define DIRECTORY_SEP_CHAR '/' 26 | #define DIRECTORY_SEP_STR "/" 27 | 28 | /* 29 | * SC assumes that a computer is Little Endian unless told otherwise. It uses 30 | * (and defines) the macros BYTE_ORDER and BIG_ENDIAN. 31 | * For Linux, we must overrule these settings with those defined in glibc. 32 | */ 33 | #if !defined __BYTE_ORDER 34 | # include 35 | #endif 36 | 37 | #if defined __OpenBSD__ || defined __FreeBSD__ 38 | # define __BYTE_ORDER BYTE_ORDER 39 | # define __LITTLE_ENDIAN LITTLE_ENDIAN 40 | # define __BIG_ENDIAN BIG_ENDIAN 41 | #endif 42 | 43 | #if !defined __BYTE_ORDER 44 | # error "Can't figure computer byte order (__BYTE_ORDER macro not found)" 45 | #endif 46 | 47 | #endif /* SCLINUX_H */ 48 | -------------------------------------------------------------------------------- /lib/sdk/amxplugin.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2004-2009 SA-MP Team 5 | // 6 | //---------------------------------------------------------- 7 | // 8 | // This provides an interface to call amx library functions 9 | // within samp-server. 10 | // 11 | //---------------------------------------------------------- 12 | 13 | #include "amx/amx.h" 14 | #include "plugincommon.h" 15 | 16 | //---------------------------------------------------------- 17 | 18 | void *pAMXFunctions; 19 | 20 | //---------------------------------------------------------- 21 | 22 | #if (defined __WIN32__ || defined _WIN32 || defined WIN32) && defined _MSC_VER 23 | 24 | // Optimized Inline Assembly Thunks for MS VC++ 25 | 26 | _declspec(naked) uint16_t *AMXAPI amx_Align16(uint16_t *v) 27 | { 28 | _asm mov eax, pAMXFunctions; 29 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Align16 * 4]; 30 | } 31 | 32 | _declspec(naked) uint32_t *AMXAPI amx_Align32(uint32_t *v) 33 | { 34 | _asm mov eax, pAMXFunctions; 35 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Align32 * 4]; 36 | } 37 | 38 | #if defined _I64_MAX || defined HAVE_I64 39 | _declspec(naked) uint64_t *AMXAPI amx_Align64(uint64_t *v) 40 | { 41 | _asm mov eax, pAMXFunctions; 42 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Align64 * 4]; 43 | } 44 | #endif 45 | 46 | _declspec(naked) int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr) 47 | { 48 | _asm mov eax, pAMXFunctions; 49 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Allot * 4]; 50 | } 51 | 52 | _declspec(naked) int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) 53 | { 54 | _asm mov eax, pAMXFunctions; 55 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Callback * 4]; 56 | } 57 | 58 | _declspec(naked) int AMXAPI amx_Cleanup(AMX *amx) 59 | { 60 | _asm mov eax, pAMXFunctions; 61 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Cleanup * 4]; 62 | } 63 | 64 | _declspec(naked) int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) 65 | { 66 | _asm mov eax, pAMXFunctions; 67 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Clone * 4]; 68 | } 69 | 70 | _declspec(naked) int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) 71 | { 72 | _asm mov eax, pAMXFunctions; 73 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Exec * 4]; 74 | } 75 | 76 | _declspec(naked) int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) 77 | { 78 | _asm mov eax, pAMXFunctions; 79 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_FindNative * 4]; 80 | } 81 | 82 | _declspec(naked) int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index) 83 | { 84 | _asm mov eax, pAMXFunctions; 85 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_FindPublic * 4]; 86 | } 87 | 88 | _declspec(naked) int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr) 89 | { 90 | _asm mov eax, pAMXFunctions; 91 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_FindPubVar * 4]; 92 | } 93 | 94 | _declspec(naked) int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname) 95 | { 96 | _asm mov eax, pAMXFunctions; 97 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_FindTagId * 4]; 98 | } 99 | 100 | _declspec(naked) int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) 101 | { 102 | _asm mov eax, pAMXFunctions; 103 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Flags * 4]; 104 | } 105 | 106 | _declspec(naked) int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) 107 | { 108 | _asm mov eax, pAMXFunctions; 109 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetAddr * 4]; 110 | } 111 | 112 | _declspec(naked) int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname) 113 | { 114 | _asm mov eax, pAMXFunctions; 115 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetNative * 4]; 116 | } 117 | 118 | _declspec(naked) int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname) 119 | { 120 | _asm mov eax, pAMXFunctions; 121 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetPublic * 4]; 122 | } 123 | 124 | _declspec(naked) int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr) 125 | { 126 | _asm mov eax, pAMXFunctions; 127 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetPubVar * 4]; 128 | } 129 | 130 | _declspec(naked) int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size) 131 | { 132 | _asm mov eax, pAMXFunctions; 133 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetString * 4]; 134 | } 135 | 136 | _declspec(naked) int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id) 137 | { 138 | _asm mov eax, pAMXFunctions; 139 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetTag * 4]; 140 | } 141 | 142 | _declspec(naked) int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr) 143 | { 144 | _asm mov eax, pAMXFunctions; 145 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_GetUserData * 4]; 146 | } 147 | 148 | _declspec(naked) int AMXAPI amx_Init(AMX *amx, void *program) 149 | { 150 | _asm mov eax, pAMXFunctions; 151 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Init * 4]; 152 | } 153 | 154 | _declspec(naked) int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) 155 | { 156 | _asm mov eax, pAMXFunctions; 157 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_InitJIT * 4]; 158 | } 159 | 160 | _declspec(naked) int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) 161 | { 162 | _asm mov eax, pAMXFunctions; 163 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_MemInfo * 4]; 164 | } 165 | 166 | _declspec(naked) int AMXAPI amx_NameLength(AMX *amx, int *length) 167 | { 168 | _asm mov eax, pAMXFunctions; 169 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_NameLength * 4]; 170 | } 171 | 172 | _declspec(naked) AMX_NATIVE_INFO *AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func) 173 | { 174 | _asm mov eax, pAMXFunctions; 175 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_NativeInfo * 4]; 176 | } 177 | 178 | _declspec(naked) int AMXAPI amx_NumNatives(AMX *amx, int *number) 179 | { 180 | _asm mov eax, pAMXFunctions; 181 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_NumNatives * 4]; 182 | } 183 | 184 | _declspec(naked) int AMXAPI amx_NumPublics(AMX *amx, int *number) 185 | { 186 | _asm mov eax, pAMXFunctions; 187 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_NumPublics * 4]; 188 | } 189 | 190 | _declspec(naked) int AMXAPI amx_NumPubVars(AMX *amx, int *number) 191 | { 192 | _asm mov eax, pAMXFunctions; 193 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_NumPubVars * 4]; 194 | } 195 | 196 | _declspec(naked) int AMXAPI amx_NumTags(AMX *amx, int *number) 197 | { 198 | _asm mov eax, pAMXFunctions; 199 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_NumTags * 4]; 200 | } 201 | 202 | _declspec(naked) int AMXAPI amx_Push(AMX *amx, cell value) 203 | { 204 | _asm mov eax, pAMXFunctions; 205 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Push * 4]; 206 | } 207 | 208 | _declspec(naked) int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells) 209 | { 210 | _asm mov eax, pAMXFunctions; 211 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_PushArray * 4]; 212 | } 213 | 214 | _declspec(naked) int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar) 215 | { 216 | _asm mov eax, pAMXFunctions; 217 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_PushString * 4]; 218 | } 219 | 220 | _declspec(naked) int AMXAPI amx_RaiseError(AMX *amx, int error) 221 | { 222 | _asm mov eax, pAMXFunctions; 223 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_RaiseError * 4]; 224 | } 225 | 226 | _declspec(naked) int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number) 227 | { 228 | _asm mov eax, pAMXFunctions; 229 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Register * 4]; 230 | } 231 | 232 | _declspec(naked) int AMXAPI amx_Release(AMX *amx, cell amx_addr) 233 | { 234 | _asm mov eax, pAMXFunctions; 235 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_Release * 4]; 236 | } 237 | 238 | _declspec(naked) int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback) 239 | { 240 | _asm mov eax, pAMXFunctions; 241 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_SetCallback * 4]; 242 | } 243 | 244 | _declspec(naked) int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug) 245 | { 246 | _asm mov eax, pAMXFunctions; 247 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_SetDebugHook * 4]; 248 | } 249 | 250 | _declspec(naked) int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size) 251 | { 252 | _asm mov eax, pAMXFunctions; 253 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_SetString * 4]; 254 | } 255 | 256 | _declspec(naked) int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr) 257 | { 258 | _asm mov eax, pAMXFunctions; 259 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_SetUserData * 4]; 260 | } 261 | 262 | _declspec(naked) int AMXAPI amx_StrLen(const cell *cstring, int *length) 263 | { 264 | _asm mov eax, pAMXFunctions; 265 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_StrLen * 4]; 266 | } 267 | 268 | _declspec(naked) int AMXAPI amx_UTF8Check(const char *string, int *length) 269 | { 270 | _asm mov eax, pAMXFunctions; 271 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_UTF8Check * 4]; 272 | } 273 | 274 | _declspec(naked) int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) 275 | { 276 | _asm mov eax, pAMXFunctions; 277 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_UTF8Get * 4]; 278 | } 279 | 280 | _declspec(naked) int AMXAPI amx_UTF8Len(const cell *cstr, int *length) 281 | { 282 | _asm mov eax, pAMXFunctions; 283 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_UTF8Len * 4]; 284 | } 285 | 286 | _declspec(naked) int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) 287 | { 288 | _asm mov eax, pAMXFunctions; 289 | _asm jmp dword ptr[eax + PLUGIN_AMX_EXPORT_UTF8Put * 4]; 290 | } 291 | 292 | #else 293 | 294 | // Unoptimized Thunks (Linux/BSD/non MSVC++) 295 | 296 | typedef uint16_t * AMXAPI (*amx_Align16_t)(uint16_t *v); 297 | uint16_t * AMXAPI amx_Align16(uint16_t *v) 298 | { 299 | amx_Align16_t fn = ((amx_Align16_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align16]; 300 | return fn(v); 301 | } 302 | 303 | typedef uint32_t * AMXAPI (*amx_Align32_t)(uint32_t *v); 304 | uint32_t * AMXAPI amx_Align32(uint32_t *v) 305 | { 306 | amx_Align32_t fn = ((amx_Align32_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align32]; 307 | return fn(v); 308 | } 309 | 310 | #if defined _I64_MAX || defined HAVE_I64 311 | typedef uint64_t * AMXAPI (*amx_Align64_t)(uint64_t *v); 312 | uint64_t * AMXAPI amx_Align64(uint64_t *v) 313 | { 314 | amx_Align64_t fn = ((amx_Align64_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Align64]; 315 | return fn(v); 316 | } 317 | #endif 318 | 319 | typedef int AMXAPI (*amx_Allot_t)(AMX *amx, int cells, cell *amx_addr, cell **phys_addr); 320 | int AMXAPI amx_Allot(AMX *amx, int cells, cell *amx_addr, cell **phys_addr) 321 | { 322 | amx_Allot_t fn = ((amx_Allot_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Allot]; 323 | return fn(amx, cells, amx_addr, phys_addr); 324 | } 325 | 326 | typedef int AMXAPI (*amx_Callback_t)(AMX *amx, cell index, cell *result, cell *params); 327 | int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) 328 | { 329 | amx_Callback_t fn = ((amx_Callback_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Callback]; 330 | return fn(amx, index, result, params); 331 | } 332 | 333 | typedef int AMXAPI (*amx_Cleanup_t)(AMX *amx); 334 | int AMXAPI amx_Cleanup(AMX *amx) 335 | { 336 | amx_Cleanup_t fn = ((amx_Cleanup_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Cleanup]; 337 | return fn(amx); 338 | } 339 | 340 | typedef int AMXAPI (*amx_Clone_t)(AMX *amxClone, AMX *amxSource, void *data); 341 | int AMXAPI amx_Clone(AMX *amxClone, AMX *amxSource, void *data) 342 | { 343 | amx_Clone_t fn = ((amx_Clone_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Clone]; 344 | return fn(amxClone, amxSource, data); 345 | } 346 | 347 | typedef int AMXAPI (*amx_Exec_t)(AMX *amx, cell *retval, int index); 348 | int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) 349 | { 350 | amx_Exec_t fn = ((amx_Exec_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Exec]; 351 | return fn(amx, retval, index); 352 | } 353 | 354 | typedef int AMXAPI (*amx_FindNative_t)(AMX *amx, const char *name, int *index); 355 | int AMXAPI amx_FindNative(AMX *amx, const char *name, int *index) 356 | { 357 | amx_FindNative_t fn = ((amx_FindNative_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindNative]; 358 | return fn(amx, name, index); 359 | } 360 | 361 | typedef int AMXAPI (*amx_FindPublic_t)(AMX *amx, const char *funcname, int *index); 362 | int AMXAPI amx_FindPublic(AMX *amx, const char *funcname, int *index) 363 | { 364 | amx_FindPublic_t fn = ((amx_FindPublic_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindPublic]; 365 | return fn(amx, funcname, index); 366 | } 367 | 368 | typedef int AMXAPI (*amx_FindPubVar_t)(AMX *amx, const char *varname, cell *amx_addr); 369 | int AMXAPI amx_FindPubVar(AMX *amx, const char *varname, cell *amx_addr) 370 | { 371 | amx_FindPubVar_t fn = ((amx_FindPubVar_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindPubVar]; 372 | return fn(amx, varname, amx_addr); 373 | } 374 | 375 | typedef int AMXAPI (*amx_FindTagId_t)(AMX *amx, cell tag_id, char *tagname); 376 | int AMXAPI amx_FindTagId(AMX *amx, cell tag_id, char *tagname) 377 | { 378 | amx_FindTagId_t fn = ((amx_FindTagId_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_FindTagId]; 379 | return fn(amx, tag_id, tagname); 380 | } 381 | 382 | typedef int AMXAPI (*amx_Flags_t)(AMX *amx,uint16_t *flags); 383 | int AMXAPI amx_Flags(AMX *amx,uint16_t *flags) 384 | { 385 | amx_Flags_t fn = ((amx_Flags_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Flags]; 386 | return fn(amx,flags); 387 | } 388 | 389 | typedef int AMXAPI (*amx_GetAddr_t)(AMX *amx,cell amx_addr,cell **phys_addr); 390 | int AMXAPI amx_GetAddr(AMX *amx,cell amx_addr,cell **phys_addr) 391 | { 392 | amx_GetAddr_t fn = ((amx_GetAddr_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetAddr]; 393 | return fn(amx,amx_addr,phys_addr); 394 | } 395 | 396 | typedef int AMXAPI (*amx_GetNative_t)(AMX *amx, int index, char *funcname); 397 | int AMXAPI amx_GetNative(AMX *amx, int index, char *funcname) 398 | { 399 | amx_GetNative_t fn = ((amx_GetNative_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetNative]; 400 | return fn(amx, index, funcname); 401 | } 402 | 403 | typedef int AMXAPI (*amx_GetPublic_t)(AMX *amx, int index, char *funcname); 404 | int AMXAPI amx_GetPublic(AMX *amx, int index, char *funcname) 405 | { 406 | amx_GetPublic_t fn = ((amx_GetPublic_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetPublic]; 407 | return fn(amx, index, funcname); 408 | } 409 | 410 | typedef int AMXAPI (*amx_GetPubVar_t)(AMX *amx, int index, char *varname, cell *amx_addr); 411 | int AMXAPI amx_GetPubVar(AMX *amx, int index, char *varname, cell *amx_addr) 412 | { 413 | amx_GetPubVar_t fn = ((amx_GetPubVar_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetPubVar]; 414 | return fn(amx, index, varname, amx_addr); 415 | } 416 | 417 | typedef int AMXAPI (*amx_GetString_t)(char *dest,const cell *source, int use_wchar, size_t size); 418 | int AMXAPI amx_GetString(char *dest,const cell *source, int use_wchar, size_t size) 419 | { 420 | amx_GetString_t fn = ((amx_GetString_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetString]; 421 | return fn(dest,source, use_wchar, size); 422 | } 423 | 424 | typedef int AMXAPI (*amx_GetTag_t)(AMX *amx, int index, char *tagname, cell *tag_id); 425 | int AMXAPI amx_GetTag(AMX *amx, int index, char *tagname, cell *tag_id) 426 | { 427 | amx_GetTag_t fn = ((amx_GetTag_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetTag]; 428 | return fn(amx, index, tagname, tag_id); 429 | } 430 | 431 | typedef int AMXAPI (*amx_GetUserData_t)(AMX *amx, long tag, void **ptr); 432 | int AMXAPI amx_GetUserData(AMX *amx, long tag, void **ptr) 433 | { 434 | amx_GetUserData_t fn = ((amx_GetUserData_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_GetUserData]; 435 | return fn(amx, tag, ptr); 436 | } 437 | 438 | typedef int AMXAPI (*amx_Init_t)(AMX *amx, void *program); 439 | int AMXAPI amx_Init(AMX *amx, void *program) 440 | { 441 | amx_Init_t fn = ((amx_Init_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Init]; 442 | return fn(amx, program); 443 | } 444 | 445 | typedef int AMXAPI (*amx_InitJIT_t)(AMX *amx, void *reloc_table, void *native_code); 446 | int AMXAPI amx_InitJIT(AMX *amx, void *reloc_table, void *native_code) 447 | { 448 | amx_InitJIT_t fn = ((amx_InitJIT_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_InitJIT]; 449 | return fn(amx, reloc_table, native_code); 450 | } 451 | 452 | typedef int AMXAPI (*amx_MemInfo_t)(AMX *amx, long *codesize, long *datasize, long *stackheap); 453 | int AMXAPI amx_MemInfo(AMX *amx, long *codesize, long *datasize, long *stackheap) 454 | { 455 | amx_MemInfo_t fn = ((amx_MemInfo_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_MemInfo]; 456 | return fn(amx, codesize, datasize, stackheap); 457 | } 458 | 459 | typedef int AMXAPI (*amx_NameLength_t)(AMX *amx, int *length); 460 | int AMXAPI amx_NameLength(AMX *amx, int *length) 461 | { 462 | amx_NameLength_t fn = ((amx_NameLength_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NameLength]; 463 | return fn(amx, length); 464 | } 465 | 466 | typedef AMX_NATIVE_INFO * AMXAPI (*amx_NativeInfo_t)(const char *name, AMX_NATIVE func); 467 | AMX_NATIVE_INFO * AMXAPI amx_NativeInfo(const char *name, AMX_NATIVE func) 468 | { 469 | amx_NativeInfo_t fn = ((amx_NativeInfo_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NativeInfo]; 470 | return fn(name, func); 471 | } 472 | 473 | typedef int AMXAPI (*amx_NumNatives_t)(AMX *amx, int *number); 474 | int AMXAPI amx_NumNatives(AMX *amx, int *number) 475 | { 476 | amx_NumNatives_t fn = ((amx_NumNatives_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumNatives]; 477 | return fn(amx, number); 478 | } 479 | 480 | typedef int AMXAPI (*amx_NumPublics_t)(AMX *amx, int *number); 481 | int AMXAPI amx_NumPublics(AMX *amx, int *number) 482 | { 483 | amx_NumPublics_t fn = ((amx_NumPublics_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumPublics]; 484 | return fn(amx, number); 485 | } 486 | 487 | typedef int AMXAPI (*amx_NumPubVars_t)(AMX *amx, int *number); 488 | int AMXAPI amx_NumPubVars(AMX *amx, int *number) 489 | { 490 | amx_NumPubVars_t fn = ((amx_NumPubVars_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumPubVars]; 491 | return fn(amx, number); 492 | } 493 | 494 | typedef int AMXAPI (*amx_NumTags_t)(AMX *amx, int *number); 495 | int AMXAPI amx_NumTags(AMX *amx, int *number) 496 | { 497 | amx_NumTags_t fn = ((amx_NumTags_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_NumTags]; 498 | return fn(amx, number); 499 | } 500 | 501 | typedef int AMXAPI (*amx_Push_t)(AMX *amx, cell value); 502 | int AMXAPI amx_Push(AMX *amx, cell value) 503 | { 504 | amx_Push_t fn = ((amx_Push_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Push]; 505 | return fn(amx, value); 506 | } 507 | 508 | typedef int AMXAPI (*amx_PushArray_t)(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells); 509 | int AMXAPI amx_PushArray(AMX *amx, cell *amx_addr, cell **phys_addr, const cell array[], int numcells) 510 | { 511 | amx_PushArray_t fn = ((amx_PushArray_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_PushArray]; 512 | return fn(amx, amx_addr, phys_addr, array, numcells); 513 | } 514 | 515 | typedef int AMXAPI (*amx_PushString_t)(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar); 516 | int AMXAPI amx_PushString(AMX *amx, cell *amx_addr, cell **phys_addr, const char *string, int pack, int use_wchar) 517 | { 518 | amx_PushString_t fn = ((amx_PushString_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_PushString]; 519 | return fn(amx, amx_addr, phys_addr, string, pack, use_wchar); 520 | } 521 | 522 | typedef int AMXAPI (*amx_RaiseError_t)(AMX *amx, int error); 523 | int AMXAPI amx_RaiseError(AMX *amx, int error) 524 | { 525 | amx_RaiseError_t fn = ((amx_RaiseError_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_RaiseError]; 526 | return fn(amx, error); 527 | } 528 | 529 | typedef int AMXAPI (*amx_Register_t)(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number); 530 | int AMXAPI amx_Register(AMX *amx, const AMX_NATIVE_INFO *nativelist, int number) 531 | { 532 | amx_Register_t fn = ((amx_Register_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Register]; 533 | return fn(amx, nativelist, number); 534 | } 535 | 536 | typedef int AMXAPI (*amx_Release_t)(AMX *amx, cell amx_addr); 537 | int AMXAPI amx_Release(AMX *amx, cell amx_addr) 538 | { 539 | amx_Release_t fn = ((amx_Release_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_Release]; 540 | return fn(amx, amx_addr); 541 | } 542 | 543 | typedef int AMXAPI (*amx_SetCallback_t)(AMX *amx, AMX_CALLBACK callback); 544 | int AMXAPI amx_SetCallback(AMX *amx, AMX_CALLBACK callback) 545 | { 546 | amx_SetCallback_t fn = ((amx_SetCallback_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetCallback]; 547 | return fn(amx, callback); 548 | } 549 | 550 | typedef int AMXAPI (*amx_SetDebugHook_t)(AMX *amx, AMX_DEBUG debug); 551 | int AMXAPI amx_SetDebugHook(AMX *amx, AMX_DEBUG debug) 552 | { 553 | amx_SetDebugHook_t fn = ((amx_SetDebugHook_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetDebugHook]; 554 | return fn(amx, debug); 555 | } 556 | 557 | typedef int AMXAPI (*amx_SetString_t)(cell *dest, const char *source, int pack, int use_wchar, size_t size); 558 | int AMXAPI amx_SetString(cell *dest, const char *source, int pack, int use_wchar, size_t size) 559 | { 560 | amx_SetString_t fn = ((amx_SetString_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetString]; 561 | return fn(dest, source, pack, use_wchar, size); 562 | } 563 | 564 | typedef int AMXAPI (*amx_SetUserData_t)(AMX *amx, long tag, void *ptr); 565 | int AMXAPI amx_SetUserData(AMX *amx, long tag, void *ptr) 566 | { 567 | amx_SetUserData_t fn = ((amx_SetUserData_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_SetUserData]; 568 | return fn(amx, tag, ptr); 569 | } 570 | 571 | typedef int AMXAPI (*amx_StrLen_t)(const cell *cstring, int *length); 572 | int AMXAPI amx_StrLen(const cell *cstring, int *length) 573 | { 574 | amx_StrLen_t fn = ((amx_StrLen_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_StrLen]; 575 | return fn(cstring, length); 576 | } 577 | 578 | typedef int AMXAPI (*amx_UTF8Check_t)(const char *string, int *length); 579 | int AMXAPI amx_UTF8Check(const char *string, int *length) 580 | { 581 | amx_UTF8Check_t fn = ((amx_UTF8Check_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Check]; 582 | return fn(string, length); 583 | } 584 | 585 | typedef int AMXAPI (*amx_UTF8Get_t)(const char *string, const char **endptr, cell *value); 586 | int AMXAPI amx_UTF8Get(const char *string, const char **endptr, cell *value) 587 | { 588 | amx_UTF8Get_t fn = ((amx_UTF8Get_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Get]; 589 | return fn(string, endptr, value); 590 | } 591 | 592 | typedef int AMXAPI (*amx_UTF8Len_t)(const cell *cstr, int *length); 593 | int AMXAPI amx_UTF8Len(const cell *cstr, int *length) 594 | { 595 | amx_UTF8Len_t fn = ((amx_UTF8Len_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Len]; 596 | return fn(cstr, length); 597 | } 598 | 599 | typedef int AMXAPI (*amx_UTF8Put_t)(char *string, char **endptr, int maxchars, cell value); 600 | int AMXAPI amx_UTF8Put(char *string, char **endptr, int maxchars, cell value) 601 | { 602 | amx_UTF8Put_t fn = ((amx_UTF8Put_t*)pAMXFunctions)[PLUGIN_AMX_EXPORT_UTF8Put]; 603 | return fn(string, endptr, maxchars, value); 604 | } 605 | 606 | #endif 607 | 608 | //---------------------------------------------------------- 609 | // EOF 610 | -------------------------------------------------------------------------------- /lib/sdk/amxplugin2.cpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2013 SA-MP Team, Dan 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #include 9 | #include 10 | 11 | //---------------------------------------------------------- 12 | 13 | #include 14 | 15 | //---------------------------------------------------------- 16 | 17 | #include "amx/amx2.h" 18 | 19 | //---------------------------------------------------------- 20 | 21 | int AMXAPI amx_PushAddress(AMX *amx, cell *address) { 22 | AMX_HEADER *hdr; 23 | unsigned char *data; 24 | cell xaddr; 25 | /* reverse relocate the address */ 26 | assert(amx != NULL); 27 | hdr = (AMX_HEADER *) amx->base; 28 | assert(hdr != NULL); 29 | assert(hdr->magic == AMX_MAGIC); 30 | data = (amx->data != NULL) ? amx->data : amx->base + (int) hdr->dat; 31 | xaddr = (cell) ((unsigned char*) address-data); 32 | if ((ucell) xaddr >= (ucell) amx->stp) { 33 | return AMX_ERR_MEMACCESS; 34 | } 35 | return amx_Push(amx,xaddr); 36 | } 37 | 38 | void AMXAPI amx_Redirect(AMX *amx, const char *from, ucell to, AMX_NATIVE *store) { 39 | AMX_HEADER *hdr = (AMX_HEADER*) amx->base; 40 | AMX_FUNCSTUB *func; 41 | for (int idx = 0, num = NUMENTRIES(hdr, natives, libraries); idx != num; ++idx) { 42 | func = GETENTRY(hdr, natives, idx); 43 | if (!strcmp(from, GETENTRYNAME(hdr, func))) { 44 | if (store) { 45 | *store = (AMX_NATIVE) func->address; 46 | } 47 | func->address = to; 48 | return; 49 | } 50 | } 51 | } 52 | 53 | int AMXAPI amx_GetCString(AMX *amx, cell param, char *&dest) { 54 | cell *ptr; 55 | amx_GetAddr(amx, param, &ptr); 56 | int len; 57 | amx_StrLen(ptr, &len); 58 | dest = (char*) malloc((len + 1) * sizeof(char)); 59 | if (dest != NULL) { 60 | amx_GetString(dest, ptr, 0, UNLIMITED); 61 | dest[len] = 0; 62 | return len; 63 | } 64 | return 0; 65 | } 66 | 67 | void AMXAPI amx_SetCString(AMX *amx, cell param, const char *str, int len) { 68 | cell *dest; 69 | amx_GetAddr(amx, param, &dest); 70 | amx_SetString(dest, str, 0, 0, len); 71 | } 72 | 73 | std::string AMXAPI amx_GetCppString(AMX *amx, cell param) { 74 | char *tmp; 75 | amx_StrParam(amx, param, tmp); 76 | if (tmp != NULL) { 77 | return tmp; 78 | } 79 | return ""; 80 | } 81 | 82 | void AMXAPI amx_SetCppString(AMX *amx, cell param, std::string str, int len) { 83 | cell *dest; 84 | amx_GetAddr(amx, param, &dest); 85 | amx_SetString(dest, str.c_str(), 0, 0, len); 86 | } 87 | 88 | //---------------------------------------------------------- 89 | // EOF 90 | -------------------------------------------------------------------------------- /lib/sdk/plugin.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2004-2009 SA-MP Team 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #include "plugincommon.h" 9 | #include "amx/amx.h" 10 | 11 | //---------------------------------------------------------- 12 | // EOF 13 | -------------------------------------------------------------------------------- /lib/sdk/plugincommon.h: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // 3 | // SA-MP Multiplayer Modification For GTA:SA 4 | // Copyright 2004-2009 SA-MP Team 5 | // 6 | //---------------------------------------------------------- 7 | 8 | #pragma once 9 | 10 | //---------------------------------------------------------- 11 | 12 | #define SAMP_PLUGIN_VERSION 0x0200 13 | 14 | //---------------------------------------------------------- 15 | 16 | #ifdef __cplusplus 17 | #define PLUGIN_EXTERN_C extern "C" 18 | #else 19 | #define PLUGIN_EXTERN_C 20 | #endif 21 | 22 | #if defined(LINUX) || defined(FREEBSD) || defined(__FreeBSD__) || defined(__OpenBSD__) 23 | #ifndef __GNUC__ 24 | #pragma message "Warning: Not using a GNU compiler." 25 | #endif 26 | #define PLUGIN_CALL 27 | #ifndef SAMPSVR 28 | // Compile code with -fvisibility=hidden to hide non-exported functions. 29 | #define PLUGIN_EXPORT PLUGIN_EXTERN_C __attribute__((visibility("default"))) 30 | #else 31 | #define PLUGIN_EXPORT PLUGIN_EXTERN_C 32 | #endif 33 | #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) 34 | #ifndef _MSC_VER 35 | #pragma message "Warning: Not using a VC++ compiler." 36 | #endif 37 | #define PLUGIN_CALL __stdcall 38 | #define PLUGIN_EXPORT PLUGIN_EXTERN_C 39 | #else 40 | #error "You must define one of WIN32, LINUX or FREEBSD" 41 | #endif 42 | 43 | //---------------------------------------------------------- 44 | 45 | enum SUPPORTS_FLAGS 46 | { 47 | SUPPORTS_VERSION = SAMP_PLUGIN_VERSION, 48 | SUPPORTS_VERSION_MASK = 0xffff, 49 | SUPPORTS_AMX_NATIVES = 0x10000, 50 | SUPPORTS_PROCESS_TICK = 0x20000 51 | }; 52 | 53 | //---------------------------------------------------------- 54 | 55 | enum PLUGIN_DATA_TYPE 56 | { 57 | // For some debugging 58 | PLUGIN_DATA_LOGPRINTF = 0x00, // void (*logprintf)(char* format, ...) 59 | 60 | // AMX 61 | PLUGIN_DATA_AMX_EXPORTS = 0x10, // void* AmxFunctionTable[] (see PLUGIN_AMX_EXPORT) 62 | PLUGIN_DATA_CALLPUBLIC_FS = 0x11, // int (*AmxCallPublicFilterScript)(char *szFunctionName) 63 | PLUGIN_DATA_CALLPUBLIC_GM = 0x12, // int (*AmxCallPublicGameMode)(char *szFunctionName) 64 | 65 | }; 66 | 67 | //---------------------------------------------------------- 68 | 69 | enum PLUGIN_AMX_EXPORT 70 | { 71 | PLUGIN_AMX_EXPORT_Align16 = 0, 72 | PLUGIN_AMX_EXPORT_Align32 = 1, 73 | PLUGIN_AMX_EXPORT_Align64 = 2, 74 | PLUGIN_AMX_EXPORT_Allot = 3, 75 | PLUGIN_AMX_EXPORT_Callback = 4, 76 | PLUGIN_AMX_EXPORT_Cleanup = 5, 77 | PLUGIN_AMX_EXPORT_Clone = 6, 78 | PLUGIN_AMX_EXPORT_Exec = 7, 79 | PLUGIN_AMX_EXPORT_FindNative = 8, 80 | PLUGIN_AMX_EXPORT_FindPublic = 9, 81 | PLUGIN_AMX_EXPORT_FindPubVar = 10, 82 | PLUGIN_AMX_EXPORT_FindTagId = 11, 83 | PLUGIN_AMX_EXPORT_Flags = 12, 84 | PLUGIN_AMX_EXPORT_GetAddr = 13, 85 | PLUGIN_AMX_EXPORT_GetNative = 14, 86 | PLUGIN_AMX_EXPORT_GetPublic = 15, 87 | PLUGIN_AMX_EXPORT_GetPubVar = 16, 88 | PLUGIN_AMX_EXPORT_GetString = 17, 89 | PLUGIN_AMX_EXPORT_GetTag = 18, 90 | PLUGIN_AMX_EXPORT_GetUserData = 19, 91 | PLUGIN_AMX_EXPORT_Init = 20, 92 | PLUGIN_AMX_EXPORT_InitJIT = 21, 93 | PLUGIN_AMX_EXPORT_MemInfo = 22, 94 | PLUGIN_AMX_EXPORT_NameLength = 23, 95 | PLUGIN_AMX_EXPORT_NativeInfo = 24, 96 | PLUGIN_AMX_EXPORT_NumNatives = 25, 97 | PLUGIN_AMX_EXPORT_NumPublics = 26, 98 | PLUGIN_AMX_EXPORT_NumPubVars = 27, 99 | PLUGIN_AMX_EXPORT_NumTags = 28, 100 | PLUGIN_AMX_EXPORT_Push = 29, 101 | PLUGIN_AMX_EXPORT_PushArray = 30, 102 | PLUGIN_AMX_EXPORT_PushString = 31, 103 | PLUGIN_AMX_EXPORT_RaiseError = 32, 104 | PLUGIN_AMX_EXPORT_Register = 33, 105 | PLUGIN_AMX_EXPORT_Release = 34, 106 | PLUGIN_AMX_EXPORT_SetCallback = 35, 107 | PLUGIN_AMX_EXPORT_SetDebugHook = 36, 108 | PLUGIN_AMX_EXPORT_SetString = 37, 109 | PLUGIN_AMX_EXPORT_SetUserData = 38, 110 | PLUGIN_AMX_EXPORT_StrLen = 39, 111 | PLUGIN_AMX_EXPORT_UTF8Check = 40, 112 | PLUGIN_AMX_EXPORT_UTF8Get = 41, 113 | PLUGIN_AMX_EXPORT_UTF8Len = 42, 114 | PLUGIN_AMX_EXPORT_UTF8Put = 43, 115 | }; 116 | 117 | //---------------------------------------------------------- 118 | // EOF 119 | -------------------------------------------------------------------------------- /lib/urmem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef URMEM_H_ 2 | #define URMEM_H_ 3 | 4 | #ifdef _WIN32 5 | #include 6 | #include 7 | 8 | #pragma intrinsic(_ReturnAddress) 9 | #else 10 | #include 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | class urmem { 23 | public: 24 | using address_t = unsigned long; 25 | using byte_t = unsigned char; 26 | using bytearray_t = std::vector; 27 | 28 | enum class calling_convention { 29 | cdeclcall, 30 | stdcall, 31 | thiscall 32 | }; 33 | 34 | #ifdef _WIN32 35 | __forceinline 36 | #else 37 | __attribute__((always_inline)) 38 | #endif 39 | static address_t get_call_address() { 40 | #ifdef _WIN32 41 | return reinterpret_cast(_ReturnAddress()) - 5; 42 | #else 43 | return reinterpret_cast(__builtin_return_address(0)) - 5; 44 | #endif 45 | } 46 | 47 | template 48 | static Ret call_function(address_t address, Args ... args) { 49 | #ifdef _WIN32 50 | return invoker::call(address, args...); 51 | #else 52 | return (reinterpret_cast(address))(args...); 53 | #endif 54 | } 55 | 56 | template 57 | static address_t get_func_addr(T func) { 58 | union { 59 | T func; 60 | address_t addr; 61 | } u{func}; 62 | 63 | return u.addr; 64 | }; 65 | 66 | static void unprotect_memory(address_t addr, size_t length) { 67 | #ifdef _WIN32 68 | unsigned long original_protect; 69 | 70 | VirtualProtect(reinterpret_cast(addr), length, PAGE_EXECUTE_READWRITE, &original_protect); 71 | #else 72 | auto pagesize = sysconf(_SC_PAGE_SIZE); 73 | 74 | addr = addr & ~(pagesize - 1); 75 | 76 | mprotect(reinterpret_cast(addr), length, PROT_READ | PROT_WRITE | PROT_EXEC); 77 | #endif 78 | } 79 | 80 | template 81 | class bit_manager { 82 | public: 83 | bit_manager(void) = delete; 84 | bit_manager(T &src) :_data(src) {} 85 | 86 | class bit { 87 | public: 88 | bit(void) = delete; 89 | bit(T &src, size_t index) : _data(src), _mask(1 << index), _index(index) {} 90 | 91 | bit &operator=(bool value) { 92 | if (value) { 93 | _data |= _mask; 94 | } else { 95 | _data &= ~_mask; 96 | } 97 | 98 | return *this; 99 | } 100 | 101 | operator bool() const { 102 | return (_data & _mask) != 0; 103 | } 104 | 105 | private: 106 | T &_data; 107 | const T _mask; 108 | const size_t _index; 109 | }; 110 | 111 | bit operator [](size_t index) const { 112 | return bit(_data, index); 113 | }; 114 | 115 | private: 116 | T &_data; 117 | }; 118 | 119 | class pointer { 120 | public: 121 | pointer(void) = delete; 122 | template 123 | pointer(T *address) : pointer{reinterpret_cast(address)} {} 124 | pointer(address_t address) : _pointer(address) {} 125 | 126 | template 127 | T &field(size_t offset) { 128 | return *reinterpret_cast(_pointer + offset); 129 | } 130 | 131 | pointer ptr_field(size_t offset) { 132 | return pointer(field(offset)); 133 | } 134 | 135 | template 136 | operator T *() const { 137 | return reinterpret_cast(_pointer); 138 | } 139 | 140 | private: 141 | const address_t _pointer; 142 | }; 143 | 144 | class unprotect_scope { 145 | public: 146 | unprotect_scope(void) = delete; 147 | unprotect_scope(address_t addr, size_t length) :_addr(addr), _lenght(length) { 148 | #ifdef _WIN32 149 | VirtualProtect(reinterpret_cast(_addr), _lenght, PAGE_EXECUTE_READWRITE, &_original_protect); 150 | #else 151 | auto pagesize = sysconf(_SC_PAGE_SIZE); 152 | 153 | _addr = _addr & ~(pagesize - 1); 154 | 155 | mprotect(reinterpret_cast(_addr), _lenght, PROT_READ | PROT_WRITE | PROT_EXEC); 156 | #endif 157 | } 158 | 159 | ~unprotect_scope(void) { 160 | #ifdef _WIN32 161 | VirtualProtect(reinterpret_cast(_addr), _lenght, _original_protect, nullptr); 162 | #else 163 | mprotect(reinterpret_cast(_addr), _lenght, PROT_READ | PROT_EXEC); 164 | #endif 165 | } 166 | 167 | private: 168 | #ifdef _WIN32 169 | unsigned long _original_protect; 170 | #endif 171 | address_t _addr; 172 | const size_t _lenght; 173 | }; 174 | 175 | class sig_scanner { 176 | public: 177 | bool init(void *addr_in_module) { 178 | return init(reinterpret_cast(addr_in_module)); 179 | } 180 | bool init(address_t addr_in_module) { 181 | #ifdef _WIN32 182 | MEMORY_BASIC_INFORMATION info{}; 183 | if (!VirtualQuery(reinterpret_cast(addr_in_module), &info, sizeof(info))) { 184 | return false; 185 | } 186 | 187 | auto dos = reinterpret_cast(info.AllocationBase); 188 | auto pe = reinterpret_cast(reinterpret_cast(dos) + dos->e_lfanew); 189 | 190 | if (pe->Signature != IMAGE_NT_SIGNATURE) { 191 | return false; 192 | } 193 | 194 | _base = reinterpret_cast(info.AllocationBase); 195 | _size = pe->OptionalHeader.SizeOfImage; 196 | #else 197 | Dl_info info{}; 198 | struct stat buf {}; 199 | 200 | if (!dladdr(reinterpret_cast(addr_in_module), &info)) { 201 | return false; 202 | } 203 | 204 | if (stat(info.dli_fname, &buf) != 0) { 205 | return false; 206 | } 207 | 208 | _base = reinterpret_cast(info.dli_fbase); 209 | _size = buf.st_size; 210 | #endif 211 | return true; 212 | } 213 | 214 | bool find(const char *pattern, const char *mask, address_t &addr) const { 215 | auto current_byte = reinterpret_cast(_base); 216 | auto last_byte = current_byte + _size; 217 | 218 | size_t i{}; 219 | while (current_byte < last_byte) { 220 | for (i = 0; mask[i]; ++i) { 221 | if (¤t_byte[i] >= last_byte || 222 | ((mask[i] != '?') && (static_cast(pattern[i]) != current_byte[i]))) { 223 | break; 224 | } 225 | } 226 | 227 | if (!mask[i]) { 228 | addr = reinterpret_cast(current_byte); 229 | 230 | return true; 231 | } 232 | 233 | ++current_byte; 234 | } 235 | 236 | return false; 237 | } 238 | 239 | private: 240 | address_t _base{}; 241 | size_t _size{}; 242 | }; 243 | 244 | class patch { 245 | public: 246 | patch(void) = delete; 247 | patch(void *addr, const bytearray_t &new_data) 248 | : patch{reinterpret_cast(addr), new_data} {} 249 | patch(address_t addr, const bytearray_t &new_data) 250 | : _patch_addr(addr), _new_data(new_data), _original_data(new_data.size(), 0x90), _enabled(false) { 251 | unprotect_memory(_patch_addr, _new_data.size()); 252 | 253 | enable(); 254 | } 255 | 256 | ~patch(void) { 257 | disable(); 258 | } 259 | 260 | void enable(void) { 261 | if (_enabled) { 262 | return; 263 | } 264 | 265 | std::copy_n( 266 | reinterpret_cast(_patch_addr), 267 | _new_data.size(), 268 | _original_data.data() 269 | ); 270 | 271 | std::copy_n( 272 | _new_data.data(), 273 | _new_data.size(), 274 | reinterpret_cast(_patch_addr) 275 | ); 276 | 277 | _enabled = true; 278 | } 279 | 280 | void disable(void) { 281 | if (!_enabled) { 282 | return; 283 | } 284 | 285 | std::copy_n( 286 | _original_data.data(), 287 | _original_data.size(), 288 | reinterpret_cast(_patch_addr) 289 | ); 290 | 291 | _enabled = false; 292 | } 293 | 294 | bool is_enabled(void) const { 295 | return _enabled; 296 | } 297 | 298 | private: 299 | address_t _patch_addr; 300 | bytearray_t _original_data; 301 | bytearray_t _new_data; 302 | bool _enabled; 303 | }; 304 | 305 | class hook { 306 | public: 307 | enum class type { 308 | jmp, 309 | call 310 | }; 311 | 312 | class raii { 313 | public: 314 | raii(void) = delete; 315 | raii(hook &h) : _hook(h) { 316 | _hook.disable(); 317 | } 318 | 319 | ~raii(void) { 320 | _hook.enable(); 321 | } 322 | 323 | private: 324 | hook &_hook; 325 | }; 326 | 327 | hook(void) = delete; 328 | hook(void *inject_addr, void *handle_addr, hook::type h_type = hook::type::jmp, size_t length = 5) : 329 | hook{reinterpret_cast(inject_addr), reinterpret_cast(handle_addr), h_type, length} {}; 330 | hook(address_t inject_addr, address_t handle_addr, hook::type h_type = hook::type::jmp, size_t length = 5) { 331 | bytearray_t new_bytes(length, 0x90); 332 | 333 | switch (h_type) { 334 | case type::jmp: 335 | { 336 | new_bytes[0] = 0xE9; 337 | _original_addr = inject_addr; 338 | 339 | break; 340 | } 341 | case type::call: 342 | { 343 | new_bytes[0] = 0xE8; 344 | _original_addr = pointer(inject_addr).field(1) + (inject_addr + 5); 345 | 346 | break; 347 | } 348 | } 349 | 350 | *reinterpret_cast(new_bytes.data() + 1) = handle_addr - (inject_addr + 5); 351 | 352 | _patch = std::make_shared(inject_addr, new_bytes); 353 | } 354 | 355 | void enable(void) { 356 | _patch->enable(); 357 | } 358 | 359 | void disable(void) { 360 | _patch->disable(); 361 | } 362 | 363 | bool is_enabled(void) const { 364 | return _patch->is_enabled(); 365 | } 366 | 367 | address_t get_original_addr(void) const { 368 | return _original_addr; 369 | } 370 | 371 | private: 372 | address_t _original_addr{}; 373 | std::shared_ptr _patch; 374 | }; 375 | 376 | template 377 | class smart_hook; 378 | 379 | template 380 | class smart_hook { 381 | public: 382 | using func = std::function; 383 | 384 | smart_hook(void *inject_addr, hook::type h_type = hook::type::jmp, size_t length = 5) : 385 | smart_hook{reinterpret_cast(inject_addr), h_type, length} {}; 386 | smart_hook(address_t inject_addr, hook::type h_type = hook::type::jmp, size_t length = 5) { 387 | get_data() = this; 388 | 389 | _hook = std::make_shared(inject_addr, reinterpret_cast(_interlayer.func), h_type, length); 390 | } 391 | 392 | void attach(const func &f) { 393 | _cb = f; 394 | } 395 | 396 | void detach(void) { 397 | _cb = nullptr; 398 | } 399 | 400 | Ret call(Args ... args) { 401 | return call_function(_hook->get_original_addr(), args...); 402 | } 403 | 404 | private: 405 | static smart_hook *&get_data(void) { 406 | static smart_hook *d{}; 407 | 408 | return d; 409 | } 410 | #ifdef _WIN32 411 | template 412 | struct interlayer; 413 | 414 | template<> 415 | struct interlayer { 416 | static Ret __cdecl func(Args ... args) { 417 | return get_data()->call_cb(args...); 418 | } 419 | }; 420 | 421 | template<> 422 | struct interlayer { 423 | static Ret __stdcall func(Args ... args) { 424 | return get_data()->call_cb(args...); 425 | } 426 | }; 427 | 428 | template<> 429 | struct interlayer { 430 | static Ret __thiscall func(Args ... args) { 431 | return get_data()->call_cb(args...); 432 | } 433 | }; 434 | #else 435 | struct interlayer { 436 | static Ret func(Args ... args) { 437 | return get_data()->call_cb(args...); 438 | } 439 | }; 440 | #endif 441 | 442 | inline Ret call_cb(Args ... args) { 443 | std::lock_guard guard(_mutex); 444 | 445 | hook::raii scope(*_hook); 446 | 447 | return _cb ? _cb(args...) : call(args...); 448 | } 449 | 450 | std::shared_ptr _hook; 451 | std::mutex _mutex; 452 | #ifdef _WIN32 453 | interlayer _interlayer; 454 | #else 455 | interlayer _interlayer; 456 | #endif 457 | func _cb; 458 | }; 459 | 460 | private: 461 | #ifdef _WIN32 462 | template 463 | struct invoker; 464 | 465 | template<> 466 | struct invoker { 467 | template 468 | static inline Ret call(address_t address, Args... args) { 469 | return (reinterpret_cast(address))(args...); 470 | } 471 | }; 472 | 473 | template<> 474 | struct invoker { 475 | template 476 | static inline Ret call(address_t address, Args... args) { 477 | return (reinterpret_cast(address))(args...); 478 | } 479 | }; 480 | 481 | template<> 482 | struct invoker { 483 | template 484 | static inline Ret call(address_t address, Args... args) { 485 | return (reinterpret_cast(address))(args...); 486 | } 487 | }; 488 | #endif 489 | }; 490 | 491 | #endif // URMEM_H_ -------------------------------------------------------------------------------- /pawn.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": "ziggi", 3 | "repo": "rustext", 4 | "resources": [ 5 | { 6 | "name": "^rustext-(.+).zip$", 7 | "platform": "linux", 8 | "archive": true, 9 | "includes": [""], 10 | "plugins": ["rustext.so"] 11 | }, 12 | { 13 | "name": "^rustext-(.+).zip$", 14 | "platform": "windows", 15 | "archive": true, 16 | "includes": [""], 17 | "plugins": ["rustext.dll"] 18 | } 19 | ], 20 | "runtime": { 21 | "plugins": ["ziggi/rustext"] 22 | } 23 | } -------------------------------------------------------------------------------- /src/common.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext common 3 | Author: ziggi 4 | */ 5 | 6 | #ifndef COMMON_H 7 | #define COMMON_H 8 | 9 | #include 10 | 11 | #define MAX_PLAYERS 1000 12 | 13 | typedef void (*logprintf_t)(const char* format, ...); 14 | typedef int (AMX_NATIVE_CALL *amx_Function_t)(AMX *amx, cell *params); 15 | 16 | #ifndef CHECK_PARAMS 17 | #define CHECK_PARAMS(m,n) \ 18 | if (params[0] != (m * 4)) { \ 19 | logprintf(" * " PLUGIN_NAME ": Incorrect parameter count for \"%s\", %d != %d\n", n, m, (static_cast(params[0])) / 4); \ 20 | return 0; \ 21 | } 22 | #endif 23 | 24 | // plugin data 25 | #define PLUGIN_DATA_NETGAME 225 26 | #define PLUGIN_DATA_RAKSERVER 226 27 | #define PLUGIN_DATA_LOADFSCRIPT 227 28 | #define PLUGIN_DATA_CONSOLE 228 29 | #define PLUGIN_DATA_UNLOADFSCRIPT 229 30 | 31 | // rpc data 32 | enum RPCEnumeration { 33 | RPC_DisplayGameText = 73, 34 | RPC_InitMenu = 76, 35 | RPC_TextDrawSetString = 105, 36 | RPC_ShowTextDraw = 134, 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext converter 3 | Author: ziggi 4 | Thanks Fro1sha for SanLtd symbol codes 5 | */ 6 | 7 | #include "converter.hpp" 8 | 9 | Converter::CharMap_t Converter::gCharMap; 10 | 11 | Converter::Converter() 12 | { 13 | gCharMap = {}; 14 | gCharMap[224] = {{97, 97, 97, 97, 65, 65, 65, 97, 97, 0 }}; /* а */ 15 | gCharMap[192] = {{65, 65, 65, 65, 65, 65, 65, 65, 65, 0 }}; /* А */ 16 | gCharMap[225] = {{151, 152, 151, 151, 128, 54, 133, 151, 151, 152}}; /* б */ 17 | gCharMap[193] = {{128, 129, 128, 128, 128, 54, 133, 128, 128, 129}}; /* Б */ 18 | gCharMap[226] = {{162, 162, 152, 152, 129, 66, 66, 171, 171, 0 }}; /* в */ 19 | gCharMap[194] = {{139, 139, 129, 129, 129, 66, 66, 66, 66, 0 }}; /* В */ 20 | gCharMap[227] = {{153, 159, 153, 153, 130, 70, 134, 164, 164, 0 }}; /* г */ 21 | gCharMap[195] = {{130, 136, 130, 130, 130, 70, 134, 141, 141, 0 }}; /* Г */ 22 | gCharMap[228] = {{154, 154, 154, 154, 131, 68, 128, 153, 153, 0 }}; /* д */ 23 | gCharMap[196] = {{131, 131, 131, 131, 131, 68, 128, 130, 130, 0 }}; /* Д */ 24 | gCharMap[229] = {{101, 101, 101, 101, 69, 69, 69, 101, 101, 0 }}; /* е */ 25 | gCharMap[197] = {{69, 69, 69, 69, 69, 69, 69, 69, 69, 0 }}; /* Е */ 26 | gCharMap[184] = {{101, 160, 175, 101, 69, 137, 137, 101, 0, 0 }}; /* ё */ 27 | gCharMap[168] = {{69, 137, 69, 69, 69, 137, 137, 69, 0, 0 }}; /* Ё */ 28 | gCharMap[230] = {{155, 155, 155, 155, 149, 71, 132, 154, 154, 0 }}; /* ж */ 29 | gCharMap[198] = {{132, 132, 132, 132, 149, 71, 132, 131, 131, 0 }}; /* Ж */ 30 | gCharMap[231] = {{159, 156, 159, 159, 138, 51, 51, 150, 150, 0 }}; /* з */ 31 | gCharMap[199] = {{136, 133, 136, 136, 138, 51, 51, 147, 147, 0 }}; /* З */ 32 | gCharMap[232] = {{156, 157, 156, 156, 139, 78, 129, 155, 155, 0 }}; /* и */ 33 | gCharMap[200] = {{133, 134, 133, 133, 139, 78, 129, 132, 132, 0 }}; /* И */ 34 | gCharMap[233] = {{157, 158, 157, 157, 140, 78, 173, 156, 156, 158}}; /* й */ 35 | gCharMap[201] = {{134, 135, 134, 134, 140, 78, 173, 133, 133, 135}}; /* Й */ 36 | gCharMap[234] = {{107, 107, 107, 107, 75, 75, 75, 175, 175, 0 }}; /* к */ 37 | gCharMap[202] = {{75, 75, 75, 75, 75, 75, 75, 75, 75, 0 }}; /* К */ 38 | gCharMap[235] = {{158, 161, 158, 158, 132, 76, 130, 157, 157, 0 }}; /* л */ 39 | gCharMap[203] = {{135, 138, 135, 135, 132, 76, 130, 134, 134, 0 }}; /* Л */ 40 | gCharMap[236] = {{175, 109, 109, 175, 77, 77, 77, 159, 159, 0 }}; /* м */ 41 | gCharMap[204] = {{77, 77, 77, 77, 77, 77, 77, 77, 77, 0 }}; /* М */ 42 | gCharMap[237] = {{174, 164, 174, 174, 72, 72, 72, 172, 172, 162}}; /* н */ 43 | gCharMap[205] = {{72, 141, 72, 72, 72, 72, 72, 72, 72, 139}}; /* Н */ 44 | gCharMap[238] = {{111, 111, 111, 111, 79, 79, 79, 111, 111, 0 }}; /* о */ 45 | gCharMap[206] = {{79, 79, 79, 79, 79, 79, 79, 79, 79, 0 }}; /* О */ 46 | gCharMap[239] = {{163, 163, 163, 163, 150, 90, 146, 174, 174, 0 }}; /* п */ 47 | gCharMap[207] = {{140, 140, 140, 140, 150, 90, 146, 135, 135, 0 }}; /* П */ 48 | gCharMap[240] = {{112, 112, 112, 112, 80, 80, 80, 112, 112, 0 }}; /* р */ 49 | gCharMap[208] = {{80, 80, 80, 80, 80, 80, 80, 80, 80, 0 }}; /* Р */ 50 | gCharMap[241] = {{99, 99, 99, 99, 67, 67, 67, 99, 99, 0 }}; /* с */ 51 | gCharMap[209] = {{67, 67, 67, 67, 67, 67, 67, 67, 67, 0 }}; /* С */ 52 | gCharMap[242] = {{166, 116, 166, 166, 84, 84, 84, 116, 116, 0 }}; /* т */ 53 | gCharMap[210] = {{143, 84, 143, 143, 84, 84, 84, 84, 84, 0 }}; /* Т */ 54 | gCharMap[243] = {{121, 121, 121, 121, 137, 89, 147, 121, 121, 166}}; /* у */ 55 | gCharMap[211] = {{89, 89, 150, 89, 137, 89, 147, 136, 136, 143}}; /* У */ 56 | gCharMap[244] = {{152, 167, 162, 152, 142, 86, 142, 152, 152, 0 }}; /* ф */ 57 | gCharMap[212] = {{129, 144, 139, 129, 142, 86, 142, 129, 129, 0 }}; /* Ф */ 58 | gCharMap[245] = {{120, 120, 120, 120, 88, 88, 88, 120, 120, 167}}; /* х ő */ 59 | gCharMap[213] = {{88, 88, 88, 88, 88, 88, 88, 88, 88, 144}}; /* Х Ő */ 60 | gCharMap[246] = {{160, 168, 160, 160, 134, 81, 148, 161, 161, 168}}; /* ц */ 61 | gCharMap[214] = {{137, 145, 137, 137, 134, 81, 148, 138, 138, 145}}; /* Ц */ 62 | gCharMap[247] = {{164, 153, 164, 164, 135, 52, 52, 162, 162, 0 }}; /* ч */ 63 | gCharMap[215] = {{141, 130, 141, 141, 135, 52, 52, 139, 139, 0 }}; /* Ч */ 64 | gCharMap[248] = {{165, 165, 165, 165, 141, 87, 144, 163, 163, 0 }}; /* ш */ 65 | gCharMap[216] = {{142, 142, 142, 142, 141, 87, 144, 140, 140, 0 }}; /* Ш */ 66 | gCharMap[249] = {{161, 169, 161, 161, 136, 87, 144, 160, 160, 0 }}; /* щ */ 67 | gCharMap[217] = {{138, 146, 138, 138, 136, 87, 144, 137, 137, 0 }}; /* Щ */ 68 | gCharMap[250] = {{167, 170, 167, 167, 143, 74, 136, 165, 0, 170}}; /* ъ */ 69 | gCharMap[218] = {{144, 147, 144, 144, 143, 74, 136, 142, 0, 147}}; /* Ъ */ 70 | gCharMap[251] = {{168, 171, 168, 168, 144, 83, 131, 166, 0, 171}}; /* ы ű */ 71 | gCharMap[219] = {{145, 148, 145, 145, 144, 83, 131, 143, 0, 183}}; /* Ы Ű */ 72 | gCharMap[252] = {{169, 172, 169, 169, 145, 74, 136, 167, 167, 172}}; /* ь */ 73 | gCharMap[220] = {{146, 149, 146, 146, 145, 74, 136, 144, 144, 149}}; /* Ь */ 74 | gCharMap[253] = {{170, 166, 170, 170, 146, 51, 135, 168, 0, 0 }}; /* э */ 75 | gCharMap[221] = {{147, 143, 147, 147, 146, 51, 135, 145, 0, 0 }}; /* Э */ 76 | gCharMap[254] = {{171, 151, 171, 171, 147, 85, 145, 169, 169, 0 }}; /* ю */ 77 | gCharMap[222] = {{148, 128, 148, 148, 147, 85, 145, 146, 146, 0 }}; /* Ю */ 78 | gCharMap[255] = {{172, 174, 172, 172, 148, 82, 149, 170, 170, 0 }}; /* я */ 79 | gCharMap[223] = {{149, 173, 149, 149, 148, 82, 149, 173, 173, 0 }}; /* Я */ 80 | gCharMap[42] = {{93, 93, 93, 93, 93, 93, 93, 93, 93, 93 }}; /* * */ 81 | gCharMap[64] = {{124, 124, 124, 124, 124, 124, 124, 124, 124, 124}}; /* @ */ 82 | // Ukraine 83 | gCharMap[165] = {{0, 0, 0, 0, 0, 0, 0, 0, 141, 0 }}; /* Ѓ */ 84 | gCharMap[180] = {{0, 0, 0, 0, 0, 0, 0, 0, 164, 0 }}; /* ѓ */ 85 | gCharMap[170] = {{0, 0, 0, 0, 0, 0, 0, 0, 145, 0 }}; /* Є */ 86 | gCharMap[186] = {{0, 0, 0, 0, 0, 0, 0, 0, 168, 0 }}; /* є */ 87 | gCharMap[178] = {{0, 0, 0, 0, 0, 0, 0, 0, 73, 0 }}; /* І */ 88 | gCharMap[179] = {{0, 0, 0, 0, 0, 0, 0, 0, 105, 0 }}; /* і */ 89 | gCharMap[175] = {{0, 0, 0, 0, 0, 0, 0, 0, 142, 0 }}; /* Ї */ 90 | gCharMap[191] = {{0, 0, 0, 0, 0, 0, 0, 0, 165, 0 }}; /* ї */ 91 | } 92 | 93 | void Converter::Process(std::string &string, Converter::Types type) 94 | { 95 | uint8_t code; 96 | for (auto &c : string) { 97 | code = gCharMap[c & 0xFF][type]; 98 | if (code != 0) { 99 | c = code; 100 | } 101 | } 102 | } 103 | 104 | void Converter::Process(char *string, uint32_t length, Converter::Types type) 105 | { 106 | uint8_t code; 107 | for (uint32_t i = 0; i < length; i++) { 108 | code = gCharMap[string[i] & 0xFF][type]; 109 | if (code != 0) { 110 | string[i] = code; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/converter.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext converter 3 | Author: ziggi 4 | */ 5 | 6 | #ifndef CONVERTER_H 7 | #define CONVERTER_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class Converter 14 | { 15 | public: 16 | enum Types 17 | { 18 | SanLtd, 19 | OneC, 20 | Rush, 21 | Unknown1, 22 | Unknown2, 23 | Unknown3, 24 | MG, 25 | Community, 26 | Ukraine, 27 | Hungarian, 28 | 29 | TypesCount 30 | }; 31 | typedef std::array, 256> CharMap_t; 32 | 33 | Converter(); 34 | static void Process(std::string &string, Types type); 35 | static void Process(char *string, uint32_t length, Types type); 36 | 37 | private: 38 | static CharMap_t gCharMap; 39 | 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/exports.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | Supports 3 | Load 4 | Unload 5 | AmxLoad 6 | AmxUnload 7 | -------------------------------------------------------------------------------- /src/hooks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext hooks 3 | Author: ziggi 4 | */ 5 | 6 | #include 7 | #include "common.hpp" 8 | #include "hooks.hpp" 9 | #include "russifier.hpp" 10 | 11 | #if _WIN32 12 | #pragma warning(disable: 4996) 13 | #endif 14 | 15 | extern logprintf_t logprintf; 16 | 17 | std::shared_ptr 18 | Hooks::_hook_get_rak_server_interface, 19 | Hooks::_hook_rakserver__rpc; 20 | 21 | urmem::address_t 22 | Hooks::_addr_rakserver, 23 | Hooks::_addr_rpc, 24 | Hooks::_addr_get_index_from_playerid; 25 | 26 | #ifdef _WIN32 27 | const char *pattern = 28 | "\x6A\xFF\x68\x5B\xA4\x4A\x00\x64\xA1\x00\x00" \ 29 | "\x00\x00\x50\x64\x89\x25\x00\x00\x00\x00\x51" \ 30 | "\x68\x18\x0E\x00\x00\xE8\xFF\xFF\xFF\xFF\x83" \ 31 | "\xC4\x04\x89\x04\x24\x85\xC0\xC7\x44\x24\xFF" \ 32 | "\x00\x00\x00\x00\x74\x16"; 33 | 34 | const char *mask = "???????xxxxxxxxxxxxxxxx????x????xxxxxxxxxxx?xxxxxx"; 35 | #else 36 | const char *pattern = 37 | "\x55\x89\xE5\x83\xEC\x18\xC7\x04\x24\xFF\xFF" \ 38 | "\xFF\xFF\x89\x75\xFF\x89\x5D\xFF\xE8\xFF\xFF" \ 39 | "\xFF\xFF\x89\x04\x24\x89\xC6\xE8\xFF\xFF\xFF" \ 40 | "\xFF\x89\xF0\x8B\x5D\xFF\x8B\x75\xFF\x89\xEC" \ 41 | "\x5D\xC3"; 42 | 43 | const char *mask = "?????xxxx????xx?xx?x????xxxxxx????xxxx?xx?xxxx"; 44 | #endif 45 | 46 | bool Hooks::Init(void) 47 | { 48 | urmem::sig_scanner scanner; 49 | 50 | if (scanner.init(urmem::get_func_addr(logprintf))) { 51 | urmem::address_t addr{}; 52 | 53 | if (scanner.find(pattern, mask, addr)) { 54 | _hook_get_rak_server_interface = std::make_shared(addr, urmem::get_func_addr(&HOOK_GetRakServerInterface)); 55 | 56 | return true; 57 | } 58 | } 59 | 60 | return false; 61 | } 62 | 63 | void *Hooks::HOOK_GetRakServerInterface(void) 64 | { 65 | urmem::hook::raii scope(*_hook_get_rak_server_interface); 66 | 67 | auto rakserver = urmem::call_function( 68 | _hook_get_rak_server_interface->get_original_addr()); 69 | 70 | if (auto vmt = urmem::pointer(rakserver).field(0)) { 71 | _addr_rakserver = reinterpret_cast(rakserver); 72 | #ifdef _WIN32 73 | _addr_rpc = vmt[32]; 74 | _addr_get_index_from_playerid = vmt[57]; 75 | #else 76 | _addr_rpc = vmt[35]; 77 | _addr_get_index_from_playerid = vmt[58]; 78 | #endif 79 | _hook_rakserver__rpc = std::make_shared( 80 | _addr_rpc, 81 | urmem::get_func_addr(&HOOK_RakServer__RPC)); 82 | } 83 | 84 | return rakserver; 85 | } 86 | 87 | bool THISCALL Hooks::HOOK_RakServer__RPC(void *_this, RPCIndex *uniqueID, RakNet::BitStream *bitStream, int priority, 88 | int reliability, char orderingChannel, PlayerID playerId, bool broadcast, bool shiftTimestamp) 89 | { 90 | urmem::hook::raii scope(*_hook_rakserver__rpc); 91 | 92 | // validate rpc parameters 93 | if (!uniqueID || !bitStream) { 94 | return RPC(_this, uniqueID, bitStream, priority, reliability, 95 | orderingChannel, playerId, broadcast, shiftTimestamp); 96 | } 97 | 98 | if (*uniqueID != RPC_DisplayGameText && 99 | *uniqueID != RPC_InitMenu && 100 | *uniqueID != RPC_ShowTextDraw && 101 | *uniqueID != RPC_TextDrawSetString) { 102 | return RPC(_this, uniqueID, bitStream, priority, reliability, 103 | orderingChannel, playerId, broadcast, shiftTimestamp); 104 | } 105 | 106 | // remember default offsets 107 | auto read_offset = bitStream->GetReadOffset(); 108 | auto write_offset = bitStream->GetWriteOffset(); 109 | 110 | // check on player valid 111 | int player_id = GetIndexFromPlayerID(playerId); 112 | if (player_id == -1 || !Russifier::IsEnabledForPlayer(player_id)) { 113 | return RPC(_this, uniqueID, bitStream, priority, reliability, 114 | orderingChannel, playerId, broadcast, shiftTimestamp); 115 | } 116 | 117 | if (*uniqueID == RPC_DisplayGameText) { 118 | // skip textdraw id and data 119 | bitStream->SetReadOffset(32 + 32); 120 | 121 | // read text 122 | uint32_t textLen; 123 | bitStream->Read(textLen); 124 | 125 | char *text = new char[textLen]; 126 | bitStream->Read(text, textLen); 127 | 128 | // convert 129 | Converter::Process(text, textLen, Russifier::GetPlayerType(player_id)); 130 | 131 | // write converted text 132 | bitStream->SetWriteOffset(32 + 32 + 32); 133 | bitStream->Write(text, textLen); 134 | 135 | delete [] text; 136 | } else if (*uniqueID == RPC_InitMenu) { 137 | const int MAX_MENU_TEXT_SIZE = 32; 138 | const int MAX_ITEMS = 12; 139 | const int MAX_COLUMNS = 2; 140 | 141 | int offsetToHeader; 142 | uint32_t isTwoColumns; 143 | char title[MAX_MENU_TEXT_SIZE]; 144 | uint8_t itemsCount[MAX_COLUMNS]; 145 | char items[MAX_ITEMS][MAX_COLUMNS][MAX_MENU_TEXT_SIZE]; 146 | char headers[MAX_COLUMNS][MAX_MENU_TEXT_SIZE]; 147 | 148 | // skip menu id 149 | bitStream->SetReadOffset(8); 150 | 151 | // read text 152 | bitStream->Read(isTwoColumns); 153 | bitStream->Read(title, MAX_MENU_TEXT_SIZE); 154 | 155 | // skip menu id, text, pos, width 156 | offsetToHeader = 8 + 32 + MAX_MENU_TEXT_SIZE * 8 + 32 + 32 + 32; 157 | if (isTwoColumns) { 158 | offsetToHeader += 32; 159 | } 160 | // skip interaction menu and rows 161 | offsetToHeader += 32 + MAX_ITEMS * 32; 162 | bitStream->SetReadOffset(offsetToHeader); 163 | 164 | // read headers and items 165 | bitStream->Read(headers[0], MAX_MENU_TEXT_SIZE); 166 | bitStream->Read(itemsCount[0]); 167 | 168 | for (uint8_t i = 0; i < itemsCount[0]; i++) { 169 | bitStream->Read(items[i][0], MAX_MENU_TEXT_SIZE); 170 | } 171 | 172 | if (isTwoColumns) { 173 | bitStream->Read(headers[1], MAX_MENU_TEXT_SIZE); 174 | bitStream->Read(itemsCount[1]); 175 | 176 | for (uint8_t i = 0; i < itemsCount[1]; i++) { 177 | bitStream->Read(items[i][1], MAX_MENU_TEXT_SIZE); 178 | } 179 | } 180 | 181 | // convert 182 | Converter::Types playerRussifierType = Russifier::GetPlayerType(player_id); 183 | 184 | Converter::Process(title, MAX_MENU_TEXT_SIZE, playerRussifierType); 185 | 186 | for (uint8_t i = 0; i < isTwoColumns + 1; i++) { 187 | Converter::Process(headers[i], MAX_MENU_TEXT_SIZE, playerRussifierType); 188 | 189 | for (uint8_t j = 0; j < itemsCount[i]; j++) { 190 | Converter::Process(items[j][i], MAX_MENU_TEXT_SIZE, playerRussifierType); 191 | } 192 | } 193 | 194 | // set write offsets: skip menu id and columns status 195 | bitStream->SetWriteOffset(8 + 32); 196 | // write converted text 197 | bitStream->Write(title, MAX_MENU_TEXT_SIZE); 198 | 199 | // skip menu id, text, pos, width and interaction 200 | bitStream->SetWriteOffset(offsetToHeader); 201 | // write converted headers and items 202 | bitStream->Write(headers[0], MAX_MENU_TEXT_SIZE); 203 | bitStream->Write(itemsCount[0]); 204 | 205 | for (uint8_t i = 0; i < itemsCount[0]; i++) { 206 | bitStream->Write(items[i][0], MAX_MENU_TEXT_SIZE); 207 | } 208 | 209 | if (isTwoColumns) { 210 | bitStream->Write(headers[1], MAX_MENU_TEXT_SIZE); 211 | bitStream->Write(itemsCount[1]); 212 | 213 | for (uint8_t i = 0; i < itemsCount[1]; i++) { 214 | bitStream->Write(items[i][1], MAX_MENU_TEXT_SIZE); 215 | } 216 | } 217 | } else if (*uniqueID == RPC_ShowTextDraw) { 218 | const int offsetToText = 16 + 63 * 8; 219 | 220 | // skip textdraw id and data 221 | bitStream->SetReadOffset(offsetToText); 222 | 223 | // read text 224 | uint16_t textLen; 225 | bitStream->Read(textLen); 226 | 227 | char *text = new char[textLen]; 228 | bitStream->Read(text, textLen); 229 | 230 | // convert 231 | Converter::Process(text, textLen, Russifier::GetPlayerType(player_id)); 232 | 233 | // write converted text 234 | bitStream->SetWriteOffset(offsetToText + 16); 235 | bitStream->Write(text, textLen); 236 | 237 | delete [] text; 238 | } else if (*uniqueID == RPC_TextDrawSetString) { 239 | const int MAX_TEXTDRAW_TEXT = 1024; 240 | const int offsetToText = 16; 241 | 242 | // skip textdraw id and data 243 | bitStream->SetReadOffset(offsetToText); 244 | 245 | // read text 246 | uint16_t textLen; 247 | bitStream->Read(textLen); 248 | 249 | char *text = new char[textLen]; 250 | bitStream->Read(text, textLen); 251 | 252 | // convert 253 | Converter::Process(text, textLen, Russifier::GetPlayerType(player_id)); 254 | 255 | // write converted text 256 | bitStream->SetWriteOffset(offsetToText + 16); 257 | bitStream->Write(text, textLen); 258 | 259 | delete [] text; 260 | } 261 | 262 | // set default offsets 263 | bitStream->SetReadOffset(read_offset); 264 | bitStream->SetWriteOffset(write_offset); 265 | 266 | // call hooked function 267 | return RPC(_this, uniqueID, bitStream, priority, reliability, 268 | orderingChannel, playerId, broadcast, shiftTimestamp); 269 | } 270 | 271 | int Hooks::GetIndexFromPlayerID(const PlayerID &id) 272 | { 273 | return urmem::call_function( 274 | _addr_get_index_from_playerid, _addr_rakserver, id); 275 | } 276 | 277 | bool Hooks::RPC(void *_this, RPCIndex *uniqueID, RakNet::BitStream *bitStream, int priority, 278 | int reliability, char orderingChannel, PlayerID playerId, bool broadcast, bool shiftTimestamp) 279 | { 280 | return urmem::call_function( 281 | _addr_rpc, _this, uniqueID, bitStream, priority, reliability, 282 | orderingChannel, playerId, broadcast, shiftTimestamp); 283 | } 284 | -------------------------------------------------------------------------------- /src/hooks.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext hooks 3 | Author: ziggi 4 | */ 5 | 6 | #ifndef HOOKS_H 7 | #define HOOKS_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef THISCALL 14 | #undef THISCALL 15 | #endif 16 | 17 | #ifdef _WIN32 18 | #define THISCALL __thiscall 19 | #else 20 | #define THISCALL 21 | #endif 22 | 23 | class Hooks { 24 | public: 25 | static bool Init(); 26 | 27 | static int GetIndexFromPlayerID(const PlayerID &); 28 | static bool RPC(void *_this, RPCIndex *uniqueID, RakNet::BitStream *bitStream, int priority, 29 | int reliability, char orderingChannel, PlayerID playerId, bool broadcast, bool shiftTimestamp); 30 | 31 | private: 32 | static void *HOOK_GetRakServerInterface(void); 33 | static bool THISCALL HOOK_RakServer__RPC(void *_this, RPCIndex *uniqueID, RakNet::BitStream *bitStream, int priority, 34 | int reliability, char orderingChannel, PlayerID playerId, bool broadcast, bool shiftTimestamp); 35 | 36 | static std::shared_ptr 37 | _hook_get_rak_server_interface, 38 | _hook_rakserver__rpc; 39 | 40 | static urmem::address_t 41 | _addr_rakserver, 42 | _addr_rpc, 43 | _addr_get_index_from_playerid; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext main 3 | Author: ziggi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "main.hpp" 10 | #include "natives.hpp" 11 | #include "common.hpp" 12 | #include "converter.hpp" 13 | #include "russifier.hpp" 14 | #include "hooks.hpp" 15 | 16 | extern void *pAMXFunctions; 17 | 18 | PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) 19 | { 20 | pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS]; 21 | logprintf = reinterpret_cast(ppData[PLUGIN_DATA_LOGPRINTF]); 22 | 23 | if (!Hooks::Init()) { 24 | logprintf(" " PLUGIN_NAME " " PLUGIN_VERSION " init error."); 25 | return false; 26 | } 27 | Converter(); 28 | Russifier(true, Converter::Types::SanLtd); 29 | 30 | logprintf(""); 31 | logprintf("---------------"); 32 | logprintf(" " PLUGIN_NAME " " PLUGIN_VERSION); 33 | logprintf(" Author: ziggi"); 34 | logprintf("---------------"); 35 | logprintf(""); 36 | return true; 37 | } 38 | 39 | PLUGIN_EXPORT void PLUGIN_CALL Unload() 40 | { 41 | logprintf(" * " PLUGIN_NAME " " PLUGIN_VERSION " unloaded."); 42 | } 43 | 44 | AMX_NATIVE_INFO PluginNatives[] = { 45 | {"GetRussifierVersion", Natives::GetRussifierVersion}, 46 | {"GetRussifierText", Natives::GetRussifierText}, 47 | 48 | {"SetPlayerRussifierType", Natives::SetPlayerRussifierType}, 49 | {"GetPlayerRussifierType", Natives::GetPlayerRussifierType}, 50 | 51 | {"SetDefaultRussifierType", Natives::SetDefaultRussifierType}, 52 | {"GetDefaultRussifierType", Natives::GetDefaultRussifierType}, 53 | 54 | {0, 0} 55 | }; 56 | 57 | PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() 58 | { 59 | return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES; 60 | } 61 | 62 | PLUGIN_EXPORT int PLUGIN_CALL AmxLoad( AMX *amx ) 63 | { 64 | return amx_Register(amx, PluginNatives, -1); 65 | } 66 | 67 | PLUGIN_EXPORT int PLUGIN_CALL AmxUnload( AMX *amx ) 68 | { 69 | return AMX_ERR_NONE; 70 | } 71 | -------------------------------------------------------------------------------- /src/main.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext main 3 | Author: ziggi 4 | */ 5 | 6 | #ifndef MAIN_H 7 | #define MAIN_H 8 | 9 | #include 10 | #include "common.hpp" 11 | #include "converter.hpp" 12 | 13 | logprintf_t logprintf; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/natives.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext natives 3 | Author: ziggi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "natives.hpp" 10 | #include "common.hpp" 11 | #include "converter.hpp" 12 | #include "russifier.hpp" 13 | 14 | extern logprintf_t logprintf; 15 | 16 | // native GetRussifierVersion(version[], const size = sizeof(version)); 17 | cell AMX_NATIVE_CALL Natives::GetRussifierVersion(AMX *amx, cell *params) 18 | { 19 | CHECK_PARAMS(2, "GetRussifierVersion"); 20 | 21 | amx_SetCString(amx, params[1], PLUGIN_VERSION, params[2]); 22 | return 1; 23 | } 24 | 25 | // native GetRussifierText(RussifierType:type, string[], string_return[], const size = sizeof(string_return)); 26 | cell AMX_NATIVE_CALL Natives::GetRussifierText(AMX *amx, cell *params) 27 | { 28 | CHECK_PARAMS(4, "GetRussifierText"); 29 | 30 | char *string; 31 | cell *dest_addr; 32 | 33 | int type = static_cast(params[1]); 34 | uint32_t length = amx_GetCString(amx, params[2], string); 35 | amx_GetAddr(amx, params[3], &dest_addr); 36 | int size = static_cast(params[4]); 37 | 38 | if (type < 0 || type >= Converter::TypesCount) { 39 | return 0; 40 | } 41 | 42 | Converter::Process(string, length, static_cast(type)); 43 | 44 | amx_SetString(dest_addr, string, 0, 0, size); 45 | return 1; 46 | } 47 | 48 | // native SetPlayerRussifierType(playerid, RussifierType:type); 49 | cell AMX_NATIVE_CALL Natives::SetPlayerRussifierType(AMX *amx, cell *params) 50 | { 51 | CHECK_PARAMS(2, "SetPlayerRussifierType"); 52 | 53 | int playerid = static_cast(params[1]); 54 | int type = static_cast(params[2]); 55 | 56 | if (playerid < 0 || playerid >= MAX_PLAYERS) { 57 | return 0; 58 | } 59 | 60 | if (type == -1) { 61 | Russifier::DisablePlayer(playerid); 62 | return 1; 63 | } 64 | 65 | if (type < 0 || type >= Converter::TypesCount) { 66 | return 0; 67 | } 68 | 69 | Russifier::SetPlayerType(playerid, type); 70 | return 1; 71 | } 72 | 73 | // native RussifierType:GetPlayerRussifierType(playerid); 74 | cell AMX_NATIVE_CALL Natives::GetPlayerRussifierType(AMX *amx, cell *params) 75 | { 76 | CHECK_PARAMS(1, "GetPlayerRussifierType"); 77 | 78 | int playerid = static_cast(params[1]); 79 | 80 | if (playerid < 0 || playerid >= MAX_PLAYERS) { 81 | return 0; 82 | } 83 | 84 | if (!Russifier::IsPlayerEnabled(playerid)) { 85 | return -1; 86 | } 87 | 88 | return Russifier::GetPlayerType(playerid); 89 | } 90 | 91 | // native SetDefaultRussifierType(RussifierType:type); 92 | cell AMX_NATIVE_CALL Natives::SetDefaultRussifierType(AMX *amx, cell *params) 93 | { 94 | CHECK_PARAMS(1, "SetDefaultRussifierType"); 95 | 96 | int type = static_cast(params[1]); 97 | 98 | if (type == -1) { 99 | Russifier::DisableDefault(); 100 | return 1; 101 | } 102 | 103 | if (type < 0 || type >= Converter::TypesCount) { 104 | return 0; 105 | } 106 | 107 | Russifier::SetDefaultType(type); 108 | return 1; 109 | } 110 | 111 | // native RussifierType:GetDefaultRussifierType(); 112 | cell AMX_NATIVE_CALL Natives::GetDefaultRussifierType(AMX *amx, cell *params) 113 | { 114 | CHECK_PARAMS(0, "GetDefaultRussifierType"); 115 | 116 | if (!Russifier::IsDefaultEnabled()) { 117 | return -1; 118 | } 119 | 120 | return Russifier::GetDefaultType(); 121 | } 122 | -------------------------------------------------------------------------------- /src/natives.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext natives 3 | Author: ziggi 4 | */ 5 | 6 | #ifndef NATIVES_H 7 | #define NATIVES_H 8 | 9 | class Natives { 10 | public: 11 | static cell AMX_NATIVE_CALL GetRussifierVersion(AMX *amx, cell *params); 12 | static cell AMX_NATIVE_CALL GetRussifierText(AMX *amx, cell *params); 13 | 14 | static cell AMX_NATIVE_CALL SetPlayerRussifierType(AMX *amx, cell *params); 15 | static cell AMX_NATIVE_CALL GetPlayerRussifierType(AMX *amx, cell *params); 16 | 17 | static cell AMX_NATIVE_CALL SetDefaultRussifierType(AMX *amx, cell *params); 18 | static cell AMX_NATIVE_CALL GetDefaultRussifierType(AMX *amx, cell *params); 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/russifier.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext player 3 | Author: ziggi 4 | */ 5 | 6 | #include "russifier.hpp" 7 | 8 | Converter::Types Russifier::gPlayerTypesMap[MAX_PLAYERS]; 9 | Converter::Types Russifier::gDefaultType; 10 | bool Russifier::gIsPlayerEnabled[MAX_PLAYERS]; 11 | bool Russifier::gIsDefaultEnabled; 12 | 13 | Russifier::Russifier(bool enabled, Converter::Types russifier) 14 | { 15 | gIsDefaultEnabled = enabled; 16 | gDefaultType = russifier; 17 | 18 | for (int playerid = 0; playerid < MAX_PLAYERS; playerid++) { 19 | gIsPlayerEnabled[playerid] = false; 20 | gPlayerTypesMap[playerid] = russifier; 21 | } 22 | } 23 | 24 | /* 25 | Player 26 | */ 27 | 28 | void Russifier::SetPlayerType(int playerid, Converter::Types type) 29 | { 30 | gIsPlayerEnabled[playerid] = true; 31 | gPlayerTypesMap[playerid] = type; 32 | } 33 | 34 | void Russifier::SetPlayerType(int playerid, int type) 35 | { 36 | SetPlayerType(playerid, static_cast(type)); 37 | } 38 | 39 | Converter::Types Russifier::GetPlayerType(int playerid) 40 | { 41 | return gPlayerTypesMap[playerid]; 42 | } 43 | 44 | void Russifier::DisablePlayer(int playerid) 45 | { 46 | gIsPlayerEnabled[playerid] = false; 47 | } 48 | 49 | bool Russifier::IsPlayerEnabled(int playerid) 50 | { 51 | return gIsPlayerEnabled[playerid]; 52 | } 53 | 54 | /* 55 | Default 56 | */ 57 | 58 | void Russifier::SetDefaultType(Converter::Types type) 59 | { 60 | gIsDefaultEnabled = true; 61 | gDefaultType = type; 62 | } 63 | 64 | void Russifier::SetDefaultType(int type) 65 | { 66 | SetDefaultType(static_cast(type)); 67 | } 68 | 69 | Converter::Types Russifier::GetDefaultType() 70 | { 71 | return gDefaultType; 72 | } 73 | 74 | void Russifier::DisableDefault() 75 | { 76 | gIsDefaultEnabled = false; 77 | } 78 | 79 | bool Russifier::IsDefaultEnabled() 80 | { 81 | return gIsDefaultEnabled; 82 | } 83 | 84 | /* 85 | Other 86 | */ 87 | 88 | bool Russifier::IsEnabledForPlayer(int playerid) 89 | { 90 | return IsPlayerEnabled(playerid) || IsDefaultEnabled(); 91 | } 92 | -------------------------------------------------------------------------------- /src/russifier.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About: rustext russifier 3 | Author: ziggi 4 | */ 5 | 6 | #ifndef RUSSIFIER_H 7 | #define RUSSIFIER_H 8 | 9 | #include "converter.hpp" 10 | #include "common.hpp" 11 | 12 | class Russifier 13 | { 14 | public: 15 | Russifier(bool enabled, Converter::Types russifier); 16 | 17 | static void SetPlayerType(int playerid, Converter::Types type); 18 | static void SetPlayerType(int playerid, int type); 19 | static Converter::Types GetPlayerType(int playerid); 20 | static void DisablePlayer(int playerid); 21 | static bool IsPlayerEnabled(int playerid); 22 | 23 | static void SetDefaultType(Converter::Types type); 24 | static void SetDefaultType(int type); 25 | static Converter::Types GetDefaultType(); 26 | static void DisableDefault(); 27 | static bool IsDefaultEnabled(); 28 | 29 | static bool IsEnabledForPlayer(int playerid); 30 | 31 | private: 32 | static Converter::Types gPlayerTypesMap[MAX_PLAYERS]; 33 | static Converter::Types gDefaultType; 34 | static bool gIsPlayerEnabled[MAX_PLAYERS]; 35 | static bool gIsDefaultEnabled; 36 | }; 37 | 38 | #endif 39 | --------------------------------------------------------------------------------