├── cmake └── Config.cmake.in ├── example ├── CMakeLists.txt ├── detail.hpp ├── emul.hpp ├── events.hpp └── main.cpp ├── include ├── RakHook │ ├── samp.hpp │ ├── detail.hpp │ ├── offsets.hpp │ ├── rakhook.hpp │ └── hooked_rakclient_interface.hpp └── RakNet │ ├── DS_HuffmanEncodingTreeNode.h │ ├── NetworkTypes.h │ ├── PacketPriority.h │ ├── DS_HuffmanEncodingTree.h │ ├── PacketEnumerations.h │ ├── StringCompressor.h │ ├── RakClientInterface.h │ ├── PluginInterface.h │ ├── DS_OrderedList.h │ ├── DS_Queue.h │ ├── DS_Map.h │ ├── DS_List.h │ └── DS_LinkedList.h ├── .gitignore ├── .clang-format ├── src ├── CMakeLists.txt ├── RakHook │ ├── samp.cpp │ ├── offsets.cpp │ └── rakhook.cpp └── RakNet │ ├── PluginInterface.cpp │ ├── StringCompressor.cpp │ ├── DS_HuffmanEncodingTree.cpp │ └── BitStream.cpp ├── LICENSE ├── README.md └── CMakeLists.txt /cmake/Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(example SHARED) 2 | target_sources(example PRIVATE "main.cpp") 3 | target_link_libraries(example PRIVATE rakhook cyanide::cyanide) 4 | set_property(TARGET example PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -------------------------------------------------------------------------------- /include/RakHook/samp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAKHOOK_SAMP_HPP 2 | #define RAKHOOK_SAMP_HPP 3 | 4 | #include 5 | 6 | namespace rakhook { 7 | enum class samp_ver { 8 | unknown = -1, 9 | 10 | v037r1 = 0, 11 | v037r31, 12 | v037r4, 13 | v03dlr1 14 | }; 15 | 16 | std::uintptr_t samp_addr(std::uintptr_t offset = 0); 17 | samp_ver samp_version(); 18 | } // namespace rakhook 19 | 20 | #endif // RAKHOOK_SAMP_HPP -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Debug info 35 | *.pdb 36 | 37 | # Building directory 38 | build*/ 39 | 40 | # IDE 41 | .idea 42 | .vscode -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | Standard: c++20 3 | IndentWidth: 4 4 | AccessModifierOffset: -4 5 | BasedOnStyle: LLVM 6 | AlignEscapedNewlines: Left 7 | AlwaysBreakTemplateDeclarations: Yes 8 | SpaceBeforeRangeBasedForLoopColon: false 9 | UseTab: Never 10 | SortIncludes: Never 11 | AllowShortFunctionsOnASingleLine: Empty 12 | IndentWrappedFunctionNames: false 13 | AlwaysBreakAfterDefinitionReturnType: None 14 | ColumnLimit: 160 15 | AllowShortLambdasOnASingleLine: Empty 16 | IndentRequires: true 17 | AlignConsecutiveAssignments: Consecutive 18 | AlignConsecutiveDeclarations: Consecutive -------------------------------------------------------------------------------- /include/RakHook/detail.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAKHOOK_DETAIL_HPP 2 | #define RAKHOOK_DETAIL_HPP 3 | 4 | #include 5 | #include 6 | #ifdef __cpp_lib_to_underlying 7 | #include 8 | #endif 9 | 10 | namespace rakhook::detail { 11 | template 12 | constexpr std::underlying_type_t to_underlying(Enum e) noexcept { 13 | #ifdef __cpp_lib_to_underlying 14 | return std::to_underlying(e); 15 | #else 16 | return static_cast>(e); 17 | #endif 18 | } 19 | } // namespace rakhook::detail 20 | 21 | #endif // RAKHOOK_DETAIL_HPP -------------------------------------------------------------------------------- /example/detail.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHEXAMPLE_DETAIL_H 2 | #define RHEXAMPLE_DETAIL_H 3 | 4 | #include 5 | 6 | #include "RakNet/BitStream.h" 7 | 8 | template 9 | std::string read_with_size(RakNet::BitStream *bs) { 10 | T size; 11 | if (!bs->Read(size)) 12 | return {}; 13 | std::string str(size, '\0'); 14 | bs->Read(str.data(), size); 15 | return str; 16 | } 17 | 18 | template 19 | void write_with_size(RakNet::BitStream *bs, std::string_view str) { 20 | T size = static_cast(str.size()); 21 | bs->Write(size); 22 | bs->Write(str.data(), size); 23 | } 24 | 25 | #endif // RHEXAMPLE_DETAIL_H 26 | -------------------------------------------------------------------------------- /include/RakHook/offsets.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAKHOOK_OFFSETS_HPP 2 | #define RAKHOOK_OFFSETS_HPP 3 | 4 | #include 5 | 6 | namespace rakhook::offsets { 7 | std::uintptr_t samp_info(bool base = false); 8 | std::uintptr_t rakclient_interface(bool base = false); 9 | 10 | std::uintptr_t destroy_interface(bool base = false); 11 | std::uintptr_t handle_rpc_packet(bool base = false); 12 | 13 | std::uintptr_t alloc_packet(bool base = false); 14 | std::uintptr_t offset_packets(bool base = false); 15 | std::uintptr_t write_lock(bool base = false); 16 | std::uintptr_t write_unlock(bool base = false); 17 | } // namespace rakhook::offsets 18 | 19 | #endif // RAKHOOK_OFFSETS_HPP -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | include(GNUInstallDirs) 3 | 4 | set(CYANIDE_INSTALL ${RAKHOOK_INSTALL}) 5 | 6 | FetchContent_Declare( 7 | cyanide 8 | GIT_REPOSITORY https://github.com/imring/cyanide.git 9 | GIT_TAG 959fd05d56a779b584e7020b7243ff600799fc46 10 | ) 11 | FetchContent_MakeAvailable(cyanide) 12 | 13 | add_library(rakhook STATIC) 14 | add_library(rakhook::rakhook ALIAS rakhook) 15 | 16 | target_include_directories(rakhook PUBLIC 17 | $ 18 | $ 19 | ) 20 | target_compile_features(rakhook PUBLIC cxx_std_20) 21 | target_link_libraries(rakhook PUBLIC cyanide::cyanide) 22 | 23 | target_sources(rakhook PRIVATE 24 | "RakHook/offsets.cpp" 25 | "RakHook/rakhook.cpp" 26 | "RakHook/samp.cpp" 27 | 28 | "RakNet/BitStream.cpp" 29 | "RakNet/DS_HuffmanEncodingTree.cpp" 30 | "RakNet/PluginInterface.cpp" 31 | "RakNet/StringCompressor.cpp" 32 | ) -------------------------------------------------------------------------------- /include/RakNet/DS_HuffmanEncodingTreeNode.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] A single node in the Huffman Encoding Tree. 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 __HUFFMAN_ENCODING_TREE_NODE 19 | #define __HUFFMAN_ENCODING_TREE_NODE 20 | 21 | struct HuffmanEncodingTreeNode 22 | { 23 | unsigned char value; 24 | unsigned weight; 25 | HuffmanEncodingTreeNode *left; 26 | HuffmanEncodingTreeNode *right; 27 | HuffmanEncodingTreeNode *parent; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2022 Vitaliy Vorobets 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. -------------------------------------------------------------------------------- /src/RakHook/samp.cpp: -------------------------------------------------------------------------------- 1 | #include "RakHook/samp.hpp" 2 | 3 | #include 4 | 5 | #ifdef UNICODE 6 | constexpr auto module_name = L"samp.dll"; 7 | #else 8 | constexpr auto module_name = "samp.dll"; 9 | #endif 10 | 11 | std::uintptr_t rakhook::samp_addr(std::uintptr_t offset) { 12 | static auto samp_module = reinterpret_cast(GetModuleHandle(module_name)); 13 | return samp_module + offset; 14 | } 15 | 16 | rakhook::samp_ver rakhook::samp_version() { 17 | static bool init = false; 18 | static samp_ver v = samp_ver::unknown; 19 | 20 | if (!init) { 21 | std::uintptr_t base = samp_addr(); 22 | auto *ntheader = reinterpret_cast(base + reinterpret_cast(base)->e_lfanew); 23 | std::uintptr_t ep = ntheader->OptionalHeader.AddressOfEntryPoint; 24 | switch (ep) { 25 | case 0x31DF13: v = samp_ver::v037r1; break; 26 | case 0xCC4D0: v = samp_ver::v037r31; break; 27 | case 0xCBCB0: v = samp_ver::v037r4; break; 28 | case 0xFDB60: v = samp_ver::v03dlr1; break; 29 | default: break; 30 | } 31 | 32 | init = true; 33 | } 34 | 35 | return v; 36 | } -------------------------------------------------------------------------------- /example/emul.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHEXAMPLE_EMUL_HPP 2 | #define RHEXAMPLE_EMUL_HPP 3 | 4 | #include "RakHook/rakhook.hpp" 5 | 6 | #include "detail.hpp" 7 | 8 | inline void change_name() { 9 | RakNet::BitStream rpc; 10 | rpc.Write(0); // playerId 11 | write_with_size(&rpc, "test_name"); // name 12 | rpc.Write(1); // success 13 | 14 | rakhook::emul_rpc(11, rpc); // SETPLAYERNAME 15 | } 16 | 17 | inline void emul_player_sync() { 18 | RakNet::BitStream bs; 19 | float vec[3] = {0}; 20 | float quat[4] = {0}; 21 | 22 | bs.Write(ID_PLAYER_SYNC); 23 | bs.Write(0); // playerId 24 | bs.Write0(); // w/o leftRightKeys 25 | bs.Write0(); // w/o upDownKeys 26 | bs.Write(0); // keysData 27 | bs.Write(vec); // position 28 | bs.Write(quat); // quaternion 29 | bs.Write(255); // health & armor 30 | bs.Write(0); // weapon 31 | bs.Write(0); // specialAction 32 | bs.Write(0); // moveSpeed 33 | bs.Write0(); // w/o surfingVehicleId & surfingOffsets 34 | bs.Write0(); // w/o animationId & animationFlags 35 | 36 | rakhook::emul_packet(bs); 37 | } 38 | 39 | #endif // RHEXAMPLE_EMUL_HPP 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RakHook 2 | RakHook is a library that adds RakNet events (incoming/outgoing Packets & RPC), emulation and sending Packets & RPC. 3 | There is support for versions 0.3.7-R1, 0.3.7-R3-1, 0.3.7-R4 and 0.3DL-R1. 4 | 5 | ## Functions 6 | 7 | ### SA:MP 8 | - `std::uintptr_t rakhook::samp_addr(std::uintptr_t offset = 0)` - Get SA:MP module address with an offset. 9 | - `samp_ver rakhook::samp_version()` - Get SA:MP version supported by RakHook. 10 | 11 | ### Events 12 | - `bool rakhook::initialize()` - Initialize RakHook. 13 | - `void rakhook::destroy()` - Destroy RakHook. 14 | - `on_event rakhook::on_send_packet` - Outgoing the packet. 15 | - `on_event rakhook::on_receive_packet` - Incoming the packet. 16 | - `on_event rakhook::on_send_rpc` - Outgoing RPC. 17 | - `on_event rakhook::on_receive_rpc` - Incoming RPC. 18 | 19 | ### Send/Emulate 20 | - `bool rakhook::send(RakNet::BitStream *bs, PacketPriority priority, PacketReliability reliability, char ord_channel)` - Send the packet. 21 | - `bool rakhook::send_rpc(int id, RakNet::BitStream *bs, PacketPriority priority, PacketReliability reliability, char ord_channel, bool sh_timestamp)` - Send RPC. 22 | - `bool rakhook::emul_packet(RakNet::BitStream &pbs)` - Emulate the packet. 23 | - `bool rakhook::emul_rpc(unsigned char id, RakNet::BitStream &rpc_bs)` - Emulate RPC. 24 | 25 | ## Example 26 | You can learn the example [here](./example/). 27 | 28 | ## Use in projects 29 | Recommended way to link the library - FetchContent, but you can use others (submodule, install, etc). -------------------------------------------------------------------------------- /src/RakHook/offsets.cpp: -------------------------------------------------------------------------------- 1 | #include "RakHook/samp.hpp" 2 | #include "RakHook/detail.hpp" 3 | #include "RakHook/offsets.hpp" 4 | 5 | constexpr std::uintptr_t samp_info[] = {0x21a0f8, 0x26e8dc, 0x26ea0c, 0x2aca24}; 6 | constexpr std::uintptr_t rakclient_interface[] = {0x3c9, 0x2c, 0x2c, 0x2c}; 7 | constexpr std::uintptr_t destroy_interface[] = {0x342d0, 0x37680, 0x37d70, 0x37880}; 8 | constexpr std::uintptr_t handle_rpc_packet[] = {0x372f0, 0x3a6a0, 0x3ad90, 0x3a8a0}; 9 | constexpr std::uintptr_t alloc_packet[] = {0x347e0, 0x37b90, 0x38280, 0x37d90}; 10 | constexpr std::uintptr_t offset_packets[] = {0xdb6, 0xdb6, 0xdb6, 0xdb6}; 11 | constexpr std::uintptr_t write_lock[] = {0x35b10, 0x38ec0, 0x395b0, 0x390c0}; 12 | constexpr std::uintptr_t write_unlock[] = {0x35b50, 0x38f00, 0x395f0, 0x39100}; 13 | 14 | std::uintptr_t get_offset(const std::uintptr_t addr[], bool base) { 15 | const rakhook::samp_ver v = rakhook::samp_version(); 16 | if (v == rakhook::samp_ver::unknown) 17 | return 0; 18 | std::uintptr_t res = addr[rakhook::detail::to_underlying(v)]; 19 | if (base) 20 | res += rakhook::samp_addr(); 21 | return res; 22 | } 23 | 24 | #define new_offsets(name) \ 25 | uintptr_t name(bool base) { return get_offset(::name, base); } 26 | 27 | namespace rakhook::offsets { 28 | new_offsets(samp_info); 29 | new_offsets(rakclient_interface); 30 | new_offsets(destroy_interface); 31 | new_offsets(handle_rpc_packet); 32 | new_offsets(alloc_packet); 33 | new_offsets(offset_packets); 34 | new_offsets(write_lock); 35 | new_offsets(write_unlock); 36 | } // namespace rakhook::offsets 37 | 38 | #undef new_offsets -------------------------------------------------------------------------------- /include/RakHook/rakhook.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RAKHOOK_HPP 2 | #define RAKHOOK_HPP 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "RakHook/samp.hpp" 9 | #include "RakNet/RakClientInterface.h" 10 | 11 | static_assert(sizeof(std::size_t) == 4, "Only 32-bit builds are supported"); 12 | 13 | namespace rakhook { 14 | template 15 | struct on_event : public std::vector> { 16 | on_event &operator+=(std::function func) { 17 | this->push_back(func); 18 | return *this; 19 | } 20 | }; 21 | 22 | using send_t = bool(RakNet::BitStream *bs, PacketPriority &priority, PacketReliability &reliability, char &ord_channel); 23 | using receive_t = bool(Packet *packet); 24 | using send_rpc_t = bool(int &id, RakNet::BitStream *bs, PacketPriority &priority, PacketReliability &reliability, char &ord_channel, bool &sh_timestamp); 25 | using receive_rpc_t = bool(unsigned char &id, RakNet::BitStream *bs); 26 | 27 | inline bool initialized = false; 28 | inline RakClientInterface *orig = nullptr; 29 | 30 | inline on_event on_send_packet; 31 | inline on_event on_receive_packet; 32 | inline on_event on_send_rpc; 33 | inline on_event on_receive_rpc; 34 | 35 | bool initialize(); 36 | void destroy(); 37 | 38 | bool send(RakNet::BitStream *bs, PacketPriority priority, PacketReliability reliability, char ord_channel); 39 | bool send_rpc(int id, RakNet::BitStream *bs, PacketPriority priority, PacketReliability reliability, char ord_channel, bool sh_timestamp); 40 | 41 | bool emul_rpc(unsigned char id, RakNet::BitStream &rpc_bs); 42 | bool emul_packet(RakNet::BitStream &pbs); 43 | } // namespace rakhook 44 | 45 | #endif // RAKHOOK_HPP -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(RakHook 3 | VERSION 1.0 4 | LANGUAGES CXX C 5 | ) 6 | 7 | if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 8 | set(RAKHOOK_MASTER_PROJECT ON) 9 | endif () 10 | 11 | option(RAKHOOK_INSTALL "Install targets" ${RAKHOOK_MASTER_PROJECT}) 12 | option(RAKHOOK_EXAMPLE "Build an example" ${RAKHOOK_MASTER_PROJECT}) 13 | 14 | add_subdirectory(src) 15 | 16 | if (RAKHOOK_INSTALL) 17 | include(GNUInstallDirs) 18 | include(CMakePackageConfigHelpers) 19 | 20 | install( 21 | TARGETS rakhook 22 | EXPORT ${PROJECT_NAME}Targets 23 | 24 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 25 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 26 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 27 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 28 | ) 29 | 30 | install( 31 | DIRECTORY include/RakHook include/RakNet 32 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 33 | ) 34 | 35 | set(config_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake) 36 | set(version_file ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake) 37 | set(config_install_destination lib/cmake/${PROJECT_NAME}) 38 | 39 | configure_package_config_file( 40 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in 41 | ${config_file} 42 | INSTALL_DESTINATION ${config_install_destination} 43 | ) 44 | 45 | write_basic_package_version_file( 46 | ${version_file} 47 | COMPATIBILITY SameMajorVersion 48 | ) 49 | 50 | install( 51 | FILES ${config_file} ${version_file} 52 | DESTINATION ${config_install_destination} 53 | ) 54 | 55 | install( 56 | EXPORT ${PROJECT_NAME}Targets 57 | NAMESPACE rakhook:: 58 | DESTINATION ${config_install_destination} 59 | ) 60 | endif () 61 | 62 | if (RAKHOOK_EXAMPLE) 63 | add_subdirectory(example) 64 | endif () -------------------------------------------------------------------------------- /example/events.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RHEXAMPLE_EVENTS_HPP 2 | #define RHEXAMPLE_EVENTS_HPP 3 | 4 | #include 5 | 6 | #include "RakNet/BitStream.h" 7 | #include "RakNet/StringCompressor.h" 8 | #include "RakNet/PacketEnumerations.h" 9 | 10 | #include "detail.hpp" 11 | 12 | inline bool on_show_dialog(unsigned char &id, RakNet::BitStream *bs) { 13 | if (id != 61) // RPC_ShowDialog 14 | return true; 15 | unsigned short did; 16 | unsigned char style; 17 | std::string title, but1, but2, text(4096, 0); 18 | 19 | // read 20 | bs->Read(did); 21 | bs->Read(style); 22 | title = read_with_size(bs); 23 | but1 = read_with_size(bs); 24 | but2 = read_with_size(bs); 25 | StringCompressor::Instance()->DecodeString(text.data(), 4096, bs); 26 | 27 | title = std::to_string(id) + " | " + title; 28 | text = "[HOOKED] " + text; 29 | size_t pos = text.find('\0'); 30 | if (pos != std::string::npos) 31 | text.insert(pos, " [HOOKED]"); 32 | text.resize(4096); 33 | 34 | // write 35 | bs->Reset(); 36 | bs->Write(did); 37 | bs->Write(style); 38 | write_with_size(bs, title); 39 | write_with_size(bs, but1); 40 | write_with_size(bs, but2); 41 | StringCompressor::Instance()->EncodeString(text.data(), 4096, bs); 42 | return true; 43 | } 44 | 45 | inline bool on_client_msg(unsigned char &id, RakNet::BitStream *bs) { 46 | if (id != 93) // RPC_ClientMessage 47 | return true; 48 | unsigned long color; 49 | std::string msg; 50 | 51 | // read 52 | bs->Read(color); 53 | msg = read_with_size(bs); 54 | 55 | msg = "[HOOKED] " + msg; 56 | 57 | // write 58 | bs->Reset(); 59 | bs->Write(color); 60 | write_with_size(bs, msg); 61 | return true; 62 | } 63 | 64 | inline bool nop_player_sync(Packet *p) { 65 | return *p->data != ID_PLAYER_SYNC; 66 | } 67 | 68 | #endif // RHEXAMPLE_EVENTS_HPP 69 | -------------------------------------------------------------------------------- /include/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 | #ifdef __GET_TIME_64BIT 39 | typedef long long RakNetTime; 40 | typedef long long RakNetTimeNS; 41 | #else 42 | typedef unsigned int RakNetTime; 43 | typedef long long RakNetTimeNS; 44 | #endif 45 | 46 | #pragma pack(push, 1) 47 | 48 | struct PlayerID 49 | { 50 | unsigned int binaryAddress; 51 | unsigned short port; 52 | }; 53 | 54 | struct NetworkID 55 | { 56 | PlayerID playerId; 57 | unsigned short localSystemId; 58 | }; 59 | 60 | struct Packet 61 | { 62 | PlayerIndex playerIndex; 63 | PlayerID playerId; 64 | unsigned int length; 65 | unsigned int bitSize; 66 | unsigned char* data; 67 | bool deleteData; 68 | }; 69 | 70 | struct RPCParameters 71 | { 72 | unsigned char *input; 73 | unsigned int numberOfBitsOfData; 74 | PlayerID sender; 75 | }; 76 | 77 | const PlayerID UNASSIGNED_PLAYER_ID = {0xFFFFFFFF, 0xFFFF}; 78 | 79 | #pragma pack(pop) 80 | 81 | #endif -------------------------------------------------------------------------------- /include/RakNet/PacketPriority.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief This file contains enumerations for packet priority and reliability enumerations. 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 __PACKET_PRIORITY_H 19 | #define __PACKET_PRIORITY_H 20 | 21 | /// These enumerations are used to describe when packets are delivered. 22 | enum PacketPriority 23 | { 24 | SYSTEM_PRIORITY, /// \internal Used by RakNet to send above-high priority messages. 25 | HIGH_PRIORITY, /// High priority messages are send before medium priority messages. 26 | MEDIUM_PRIORITY, /// Medium priority messages are send before low priority messages. 27 | LOW_PRIORITY, /// Low priority messages are only sent when no other messages are waiting. 28 | NUMBER_OF_PRIORITIES 29 | }; 30 | 31 | /// These enumerations are used to describe how packets are delivered. 32 | /// \note Note to self: I write this with 3 bits in the stream. If I add more remember to change that 33 | enum PacketReliability 34 | { 35 | UNRELIABLE = 6, /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length. 36 | UNRELIABLE_SEQUENCED, /// Regular UDP with a sequence counter. Out of order messages will be discarded. This adds an additional 13 bits on top what is used for UNRELIABLE. 37 | RELIABLE, /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE. 38 | RELIABLE_ORDERED, /// This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED. 39 | RELIABLE_SEQUENCED /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/RakNet/DS_HuffmanEncodingTree.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression. 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 __HUFFMAN_ENCODING_TREE 19 | #define __HUFFMAN_ENCODING_TREE 20 | 21 | #include "DS_HuffmanEncodingTreeNode.h" 22 | #include "BitStream.h" 23 | #include "DS_LinkedList.h" 24 | 25 | /// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 26 | class HuffmanEncodingTree 27 | { 28 | 29 | public: 30 | HuffmanEncodingTree(); 31 | ~HuffmanEncodingTree(); 32 | 33 | /// Pass an array of bytes to array and a preallocated BitStream to receive the output 34 | /// \param [in] input Array of bytes to encode 35 | /// \param [in] sizeInBytes size of \a input 36 | /// \param [out] output The bitstream to write to 37 | void EncodeArray( unsigned char *input, unsigned sizeInBytes, RakNet::BitStream * output ); 38 | 39 | // Decodes an array encoded by EncodeArray() 40 | unsigned DecodeArray( RakNet::BitStream * input, unsigned sizeInBits, unsigned maxCharsToWrite, unsigned char *output ); 41 | void DecodeArray( unsigned char *input, unsigned sizeInBits, RakNet::BitStream * output ); 42 | 43 | /// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree 44 | void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); 45 | 46 | /// Free the memory used by the tree 47 | void FreeMemory( void ); 48 | 49 | private: 50 | 51 | /// The root node of the tree 52 | 53 | HuffmanEncodingTreeNode *root; 54 | 55 | /// Used to hold bit encoding for one character 56 | 57 | 58 | struct CharacterEncoding 59 | { 60 | unsigned char* encoding; 61 | unsigned short bitLength; 62 | }; 63 | 64 | CharacterEncoding encodingTable[ 256 ]; 65 | 66 | void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /include/RakNet/PacketEnumerations.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief All the packet identifiers used by RakNet. Packet identifiers comprise the first byte of any message. 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 __PACKET_ENUMERATIONS_H 19 | #define __PACKET_ENUMERATIONS_H 20 | 21 | /// You should not edit the file PacketEnumerations.h as it is a part of RakNet static library 22 | /// To define your own message id, define an enum following the code example that follows. 23 | /// 24 | /// \code 25 | /// enum { 26 | /// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM 27 | /// ID_MYPROJECT_MSG_2, 28 | /// ... 29 | /// }; 30 | /// \endcode 31 | /// 32 | /// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream 33 | 34 | enum PacketEnumeration 35 | { 36 | ID_INTERNAL_PING = 6, 37 | ID_PING, 38 | ID_PING_OPEN_CONNECTIONS, 39 | ID_CONNECTED_PONG, 40 | ID_REQUEST_STATIC_DATA, 41 | ID_CONNECTION_REQUEST, 42 | ID_AUTH_KEY, 43 | ID_BROADCAST_PINGS = 14, 44 | ID_SECURED_CONNECTION_RESPONSE, 45 | ID_SECURED_CONNECTION_CONFIRMATION, 46 | ID_RPC_MAPPING, 47 | ID_SET_RANDOM_NUMBER_SEED = 19, 48 | ID_RPC, 49 | ID_RPC_REPLY, 50 | ID_DETECT_LOST_CONNECTIONS = 23, 51 | ID_OPEN_CONNECTION_REQUEST, 52 | ID_OPEN_CONNECTION_REPLY, 53 | ID_OPEN_CONNECTION_COOKIE, 54 | ID_RSA_PUBLIC_KEY_MISMATCH = 28, 55 | ID_CONNECTION_ATTEMPT_FAILED, 56 | ID_NEW_INCOMING_CONNECTION = 30, 57 | ID_NO_FREE_INCOMING_CONNECTIONS = 31, 58 | ID_DISCONNECTION_NOTIFICATION, 59 | ID_CONNECTION_LOST, 60 | ID_CONNECTION_REQUEST_ACCEPTED, 61 | ID_CONNECTION_BANNED = 36, 62 | ID_INVALID_PASSWORD, 63 | ID_MODIFIED_PACKET, 64 | ID_PONG, 65 | ID_TIMESTAMP, 66 | ID_RECEIVED_STATIC_DATA, 67 | ID_REMOTE_DISCONNECTION_NOTIFICATION, 68 | ID_REMOTE_CONNECTION_LOST, 69 | ID_REMOTE_NEW_INCOMING_CONNECTION, 70 | ID_REMOTE_EXISTING_CONNECTION, 71 | ID_REMOTE_STATIC_DATA, 72 | ID_ADVERTISE_SYSTEM = 55, 73 | 74 | ID_PLAYER_SYNC = 207, 75 | ID_MARKERS_SYNC = 208, 76 | ID_UNOCCUPIED_SYNC = 209, 77 | ID_TRAILER_SYNC = 210, 78 | ID_PASSENGER_SYNC = 211, 79 | ID_SPECTATOR_SYNC = 212, 80 | ID_AIM_SYNC = 203, 81 | ID_VEHICLE_SYNC = 200, 82 | ID_RCON_COMMAND = 201, 83 | ID_RCON_RESPONCE = 202, 84 | ID_WEAPONS_UPDATE = 204, 85 | ID_STATS_UPDATE = 205, 86 | ID_BULLET_SYNC = 206, 87 | }; 88 | 89 | #endif 90 | 91 | -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "RakHook/rakhook.hpp" 4 | #include "RakNet/StringCompressor.h" 5 | 6 | #include "events.hpp" 7 | #include "emul.hpp" 8 | 9 | using game_loop_t = void (*)(); 10 | using wndproc_t = LRESULT(CALLBACK *)(HWND, UINT, WPARAM, LPARAM); 11 | 12 | void game_loop(game_loop_t orig) { 13 | orig(); 14 | 15 | static bool initialized = false; 16 | if (initialized || !rakhook::initialize()) 17 | return; 18 | StringCompressor::AddReference(); 19 | 20 | // print incoming/outgoing packets/rpc 21 | rakhook::on_send_rpc += 22 | [](int &id, RakNet::BitStream *bs, PacketPriority &priority, PacketReliability &reliability, char &ord_channel, bool &sh_timestamp) -> bool { 23 | std::cout << "send rpc: " << id << ' ' << bs << ' ' << priority << ' ' << reliability << ' ' << +ord_channel << ' ' << std::boolalpha << sh_timestamp 24 | << std::noboolalpha << '\n'; 25 | return true; 26 | }; 27 | 28 | rakhook::on_receive_packet += [](Packet *p) -> bool { 29 | std::cout << "receive packet: " << +(*p->data) << ' ' << static_cast(p->data) << '\n'; 30 | return true; 31 | }; 32 | 33 | rakhook::on_send_packet += [](RakNet::BitStream *bs, PacketPriority &priority, PacketReliability &reliability, char &ord_channel) -> bool { 34 | std::cout << "send packet: " << +(*bs->GetData()) << ' ' << bs << ' ' << priority << ' ' << reliability << ' ' << +ord_channel << '\n'; 35 | return true; 36 | }; 37 | 38 | rakhook::on_receive_rpc += [](unsigned char &id, RakNet::BitStream *bs) -> bool { 39 | std::cout << "receive rpc: " << +id << ' ' << bs << '\n'; 40 | return true; 41 | }; 42 | 43 | // modify some packets/rpc 44 | rakhook::on_receive_rpc += on_show_dialog; 45 | rakhook::on_receive_rpc += on_client_msg; 46 | rakhook::on_receive_packet += nop_player_sync; 47 | 48 | initialized = true; 49 | } 50 | 51 | LRESULT wndproc_hooked(wndproc_t orig, HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam) { 52 | if (Message == WM_KEYUP) { 53 | if (wparam == VK_NUMPAD4) { 54 | change_name(); 55 | } 56 | } else if (Message == WM_KEYDOWN) { 57 | if (wparam == VK_NUMPAD5) { 58 | emul_player_sync(); 59 | } 60 | } 61 | return orig(hwnd, Message, wparam, lparam); 62 | } 63 | 64 | std::unique_ptr> game_loop_hook; 65 | std::unique_ptr> wndproc_hook; 66 | 67 | class rakhook_example { 68 | public: 69 | rakhook_example() { 70 | if (!rakhook::samp_addr() || rakhook::samp_version() == rakhook::samp_ver::unknown) 71 | return; 72 | 73 | game_loop_hook = std::make_unique(std::bit_cast(0x53BEE0), std::move(&game_loop)); 74 | wndproc_hook = std::make_unique(std::bit_cast(0x747EB0), std::move(&wndproc_hooked)); 75 | 76 | game_loop_hook->install(); 77 | wndproc_hook->install(); 78 | } 79 | } rakhook_example_; -------------------------------------------------------------------------------- /src/RakNet/PluginInterface.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 | #include "../../include/RakNet/PluginInterface.h" 18 | 19 | #ifdef _MSC_VER 20 | #pragma warning( push ) 21 | #endif 22 | 23 | #ifdef _MSC_VER 24 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 25 | #endif 26 | void PluginInterface::OnAttach(RakPeerInterface *peer) 27 | { 28 | } 29 | #ifdef _MSC_VER 30 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 31 | #endif 32 | void PluginInterface::OnDetach(RakPeerInterface *peer) 33 | { 34 | } 35 | #ifdef _MSC_VER 36 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 37 | #endif 38 | void PluginInterface::OnInitialize(RakPeerInterface *peer) 39 | { 40 | } 41 | #ifdef _MSC_VER 42 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 43 | #endif 44 | void PluginInterface::Update(RakPeerInterface *peer) 45 | { 46 | } 47 | #ifdef _MSC_VER 48 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 49 | #endif 50 | PluginReceiveResult PluginInterface::OnReceive(RakPeerInterface *peer, Packet *packet) 51 | { 52 | return RR_CONTINUE_PROCESSING; 53 | } 54 | #ifdef _MSC_VER 55 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 56 | #endif 57 | void PluginInterface::OnDisconnect(RakPeerInterface *peer) 58 | { 59 | } 60 | #ifdef _MSC_VER 61 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 62 | #endif 63 | void PluginInterface::OnCloseConnection(RakPeerInterface *peer, PlayerID playerId) 64 | { 65 | } 66 | #ifdef _MSC_VER 67 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 68 | #endif 69 | void PluginInterface::OnDirectSocketSend(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID) 70 | { 71 | } 72 | #ifdef _MSC_VER 73 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 74 | #endif 75 | void PluginInterface::OnDirectSocketReceive(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID) 76 | { 77 | } 78 | #ifdef _MSC_VER 79 | #pragma warning( disable : 4100 ) // warning C4100: : unreferenced formal parameter 80 | #endif 81 | void PluginInterface::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, PlayerID remoteSystemID, RakNetTime time, bool isSend) 82 | { 83 | } 84 | 85 | #ifdef _MSC_VER 86 | #pragma warning( pop ) 87 | #endif 88 | -------------------------------------------------------------------------------- /include/RakNet/StringCompressor.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b Compresses/Decompresses ASCII strings and writes/reads them to BitStream class instances. You can use this to easily serialize and deserialize your own strings. 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 __STRING_COMPRESSOR_H 19 | #define __STRING_COMPRESSOR_H 20 | 21 | #include "DS_Map.h" 22 | 23 | /// Forward declaration 24 | namespace RakNet 25 | { 26 | class BitStream; 27 | }; 28 | 29 | class HuffmanEncodingTree; 30 | 31 | /// \brief Writes and reads strings to and from bitstreams. 32 | /// 33 | /// Only works with ASCII strings. The default compression is for English. 34 | /// You can call GenerateTreeFromStrings to compress and decompress other languages efficiently as well. 35 | class StringCompressor 36 | { 37 | public: 38 | 39 | /// Destructor 40 | ~StringCompressor(); 41 | 42 | /// static function because only static functions can access static members 43 | /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. 44 | /// \return the unique instance of the StringCompressor 45 | static StringCompressor* Instance(void); 46 | 47 | /// Given an array of strings, such as a chat log, generate the optimal encoding tree for it. 48 | /// This function is optional and if it is not called a default tree will be used instead. 49 | /// \param[in] input An array of bytes which should point to text. 50 | /// \param[in] inputLength Length of \a input 51 | /// \param[in] languageID An identifier for the language / string table to generate the tree for. English is automatically created with ID 0 in the constructor. 52 | void GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, int languageID ); 53 | 54 | /// Writes input to output, compressed. Takes care of the null terminator for you. 55 | /// \param[in] input Pointer to an ASCII string 56 | /// \param[in] maxCharsToWrite The max number of bytes to write of \a input. Use 0 to mean no limit. 57 | /// \param[out] output The bitstream to write the compressed string to 58 | /// \param[in] languageID Which language to use 59 | void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, int languageID=0 ); 60 | 61 | /// Writes input to output, uncompressed. Takes care of the null terminator for you. 62 | /// \param[out] output A block of bytes to receive the output 63 | /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. 64 | /// \param[in] input The bitstream containing the compressed string 65 | /// \param[in] languageID Which language to use 66 | bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, int languageID=0 ); 67 | 68 | /// Used so I can allocate and deallocate this singleton at runtime 69 | static void AddReference(void); 70 | 71 | /// Used so I can allocate and deallocate this singleton at runtime 72 | static void RemoveReference(void); 73 | 74 | private: 75 | 76 | /// Private Constructor 77 | StringCompressor(); 78 | 79 | /// Singleton instance 80 | static StringCompressor *instance; 81 | 82 | /// Pointer to the huffman encoding trees. 83 | DataStructures::Map huffmanEncodingTrees; 84 | 85 | static int referenceCount; 86 | }; 87 | 88 | /// Define to more easily reference the string compressor instance. 89 | /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. 90 | #define stringCompressor StringCompressor::Instance() 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /include/RakNet/RakClientInterface.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief An interface for RakClient. Simply contains all user functions as pure virtuals. 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 __RAK_CLIENT_INTERFACE_H 19 | #define __RAK_CLIENT_INTERFACE_H 20 | 21 | #include "NetworkTypes.h" 22 | #include "PacketPriority.h" 23 | #include "PluginInterface.h" 24 | #include "BitStream.h" 25 | 26 | /// This is a user-interface class to act as a game client. All it does is implement some functionality on top of RakPeer. 27 | /// See the individual functions for what the class can do. 28 | /// \brief Defines the functions used by a game client 29 | class RakClientInterface 30 | { 31 | 32 | public: 33 | virtual ~RakClientInterface() {}; 34 | virtual bool Connect(const char* host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer); 35 | virtual void Disconnect(unsigned int blockDuration, unsigned char orderingChannel = 0); 36 | virtual void InitializeSecurity(const char *privKeyP, const char *privKeyQ); 37 | virtual void SetPassword(const char *_password); 38 | virtual bool HasPassword(void) const; 39 | virtual bool Send(const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel); 40 | virtual bool Send(RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel); 41 | virtual Packet* Receive(void); 42 | virtual void DeallocatePacket(Packet *packet); 43 | virtual void PingServer(void); 44 | virtual void PingServer(const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections); 45 | virtual int GetAveragePing(void); 46 | virtual int GetLastPing(void) const; 47 | virtual int GetLowestPing(void) const; 48 | virtual int GetPlayerPing(const PlayerID playerId); 49 | virtual void StartOccasionalPing(void); 50 | virtual void StopOccasionalPing(void); 51 | virtual bool IsConnected(void) const; 52 | virtual unsigned int GetSynchronizedRandomInteger(void) const; 53 | virtual bool GenerateCompressionLayer(unsigned int inputFrequencyTable[256], bool inputLayer); 54 | virtual bool DeleteCompressionLayer(bool inputLayer); 55 | virtual void RegisterAsRemoteProcedureCall(int* uniqueID, void(*functionPointer) (RPCParameters *rpcParms)); 56 | virtual void RegisterClassMemberRPC(int* uniqueID, void *functionPointer); 57 | virtual void UnregisterAsRemoteProcedureCall(int* uniqueID); 58 | virtual bool RPC(int* uniqueID, const char *data, unsigned int bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp); 59 | virtual bool RPC(int* uniqueID, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp); 60 | virtual bool RPC_(int* uniqueID, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp, NetworkID networkID); 61 | virtual void SetTrackFrequencyTable(bool b); 62 | virtual bool GetSendFrequencyTable(unsigned int outputFrequencyTable[256]); 63 | virtual float GetCompressionRatio(void) const; 64 | virtual float GetDecompressionRatio(void) const; 65 | virtual void AttachPlugin(void *messageHandler); 66 | virtual void DetachPlugin(void *messageHandler); 67 | virtual RakNet::BitStream * GetStaticServerData(void); 68 | virtual void SetStaticServerData(const char *data, const int length); 69 | virtual RakNet::BitStream * GetStaticClientData(const PlayerID playerId); 70 | virtual void SetStaticClientData(const PlayerID playerId, const char *data, const int length); 71 | virtual void SendStaticClientDataToServer(void); 72 | virtual PlayerID GetServerID(void) const; 73 | virtual PlayerID GetPlayerID(void) const; 74 | virtual PlayerID GetInternalID(void) const; 75 | virtual const char* PlayerIDToDottedIP(const PlayerID playerId) const; 76 | virtual void PushBackPacket(Packet *packet, bool pushAtHead); 77 | virtual void SetRouterInterface(void *routerInterface); 78 | virtual void RemoveRouterInterface(void *routerInterface); 79 | virtual void SetTimeoutTime(RakNetTime timeMS); 80 | virtual bool SetMTUSize(int size); 81 | virtual int GetMTUSize(void) const; 82 | virtual void AllowConnectionResponseIPMigration(bool allow); 83 | virtual void AdvertiseSystem(const char *host, unsigned short remotePort, const char *data, int dataLength); 84 | virtual void * const GetStatistics(void); 85 | virtual void ApplyNetworkSimulator(double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance); 86 | virtual bool IsNetworkSimulatorActive(void); 87 | virtual PlayerIndex GetPlayerIndex(void); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /include/RakNet/PluginInterface.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b RakNet's plugin functionality system. You can derive from this to create your own plugins. 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 __PLUGIN_INTERFACE_H 19 | #define __PLUGIN_INTERFACE_H 20 | 21 | class RakPeerInterface; 22 | struct Packet; 23 | struct InternalPacket; 24 | 25 | #include "NetworkTypes.h" 26 | 27 | enum PluginReceiveResult 28 | { 29 | // The plugin used this message and it shouldn't be given to the user. 30 | RR_STOP_PROCESSING_AND_DEALLOCATE=0, 31 | 32 | // This message will be processed by other plugins, and at last by the user. 33 | RR_CONTINUE_PROCESSING, 34 | 35 | // The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. 36 | RR_STOP_PROCESSING, 37 | }; 38 | 39 | /// \defgroup PLUGINS_GROUP PluginInterface 40 | 41 | /// \brief PluginInterface provides a mechanism to add functionality in a modular way. 42 | /// MessageHandlers should derive from PluginInterface and be attached to RakPeer using the function AttachPlugin 43 | /// On a user call to Receive, OnReceive is called for every PluginInterface, which can then take action based on the message 44 | /// passed to it. This is used to transparently add game-independent functional modules, similar to browser plugins 45 | /// 46 | /// \sa ReplicaManager 47 | /// \sa FullyConnectedMesh 48 | /// \sa PacketLogger 49 | /// \ingroup PLUGINS_GROUP 50 | class PluginInterface 51 | { 52 | public: 53 | /// Called when the interface is attached 54 | /// \param[in] peer the instance of RakPeer that is calling Receive 55 | virtual void OnAttach(RakPeerInterface *peer); 56 | 57 | /// Called when the interface is detached 58 | /// \param[in] peer the instance of RakPeer that is calling Receive 59 | virtual void OnDetach(RakPeerInterface *peer); 60 | 61 | /// Called when RakPeer is initialized 62 | /// \param[in] peer the instance of RakPeer that is calling Receive 63 | virtual void OnInitialize(RakPeerInterface *peer); 64 | 65 | /// Update is called every time a packet is checked for . 66 | /// \param[in] peer - the instance of RakPeer that is calling Receive 67 | virtual void Update(RakPeerInterface *peer); 68 | 69 | /// OnReceive is called for every packet. 70 | /// \param[in] peer the instance of RakPeer that is calling Receive 71 | /// \param[in] packet the packet that is being returned to the user 72 | /// \return True to allow the game and other plugins to get this message, false to absorb it 73 | virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); 74 | 75 | /// Called when RakPeer is shutdown 76 | /// \param[in] peer the instance of RakPeer that is calling Receive 77 | virtual void OnDisconnect(RakPeerInterface *peer); 78 | 79 | /// Called when a connection is dropped because the user called RakPeer::CloseConnection() for a particular system 80 | /// \param[in] peer the instance of RakPeer that is calling Receive 81 | /// \param[in] playerId The system whose connection was closed 82 | virtual void OnCloseConnection(RakPeerInterface *peer, PlayerID playerId); 83 | 84 | /// Called on a send to the socket, per datagram, that does not go through the reliability layer 85 | /// \param[in] data The data being sent 86 | /// \param[in] bitsUsed How many bits long \a data is 87 | /// \param[in] remoteSystemID Which system this message is being sent to 88 | virtual void OnDirectSocketSend(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID); 89 | 90 | /// Called on a receive from the socket, per datagram, that does not go through the reliability layer 91 | /// \param[in] data The data being sent 92 | /// \param[in] bitsUsed How many bits long \a data is 93 | /// \param[in] remoteSystemID Which system this message is being sent to 94 | virtual void OnDirectSocketReceive(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID); 95 | 96 | /// Called on a send or recieve within the reliability layer 97 | /// \param[in] internalPacket The user message, along with all send data. 98 | /// \param[in] frameNumber The number of frames sent or received so far for this player depending on \a isSend . Indicates the frame of this user message. 99 | /// \param[in] remoteSystemID The player we sent or got this packet from 100 | /// \param[in] time The current time as returned by RakNet::GetTime() 101 | /// \param[in] isSend Is this callback representing a send event or receive event? 102 | virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, PlayerID remoteSystemID, RakNetTime time, bool isSend); 103 | }; 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/RakHook/rakhook.cpp: -------------------------------------------------------------------------------- 1 | #include "RakHook/rakhook.hpp" 2 | #include "RakHook/detail.hpp" 3 | #include "RakHook/offsets.hpp" 4 | #include "RakHook/hooked_rakclient_interface.hpp" 5 | 6 | #include "RakNet/PacketEnumerations.h" 7 | 8 | #include 9 | 10 | #ifndef MAX_ALLOCA_STACK_ALLOCATION 11 | #define MAX_ALLOCA_STACK_ALLOCATION 1048576 12 | #endif 13 | 14 | hooked_rakclient_interface *hooked_interface = nullptr; 15 | void *rakpeer = nullptr; 16 | PlayerID gplayerid; 17 | 18 | using destroy_ri_t = void(__cdecl *)(void *); 19 | using handle_rpc_packet_t = bool(__thiscall *)(void *, const char *, int, PlayerID); 20 | 21 | // callbacks 22 | void destroy_rakclient_interface(destroy_ri_t orig, void *rakclient_interface) { 23 | if (rakclient_interface == hooked_interface) { 24 | rakclient_interface = rakhook::orig; 25 | delete hooked_interface; 26 | } 27 | return orig(rakclient_interface); 28 | } 29 | 30 | bool handle_rpc_packet(handle_rpc_packet_t orig, void *rp, const char *data, int length, PlayerID playerid) { 31 | rakpeer = rp; 32 | gplayerid = playerid; 33 | 34 | RakNet::BitStream incoming{std::bit_cast(const_cast(data)), static_cast(length), true}; 35 | unsigned char id = 0; 36 | unsigned char *input = nullptr; 37 | unsigned int bits_data = 0; 38 | std::shared_ptr callback_bs{std::make_shared()}; 39 | 40 | incoming.IgnoreBits(8); 41 | if (data[0] == ID_TIMESTAMP) 42 | incoming.IgnoreBits(8 * (sizeof(RakNetTime) + sizeof(unsigned char))); 43 | 44 | int offset = incoming.GetReadOffset(); 45 | incoming.Read(id); 46 | 47 | if (!incoming.ReadCompressed(bits_data)) 48 | return false; 49 | 50 | if (bits_data) { 51 | bool used_alloca = false; 52 | if (BITS_TO_BYTES(incoming.GetNumberOfUnreadBits()) < MAX_ALLOCA_STACK_ALLOCATION) { 53 | input = std::bit_cast(alloca(BITS_TO_BYTES(incoming.GetNumberOfUnreadBits()))); 54 | used_alloca = true; 55 | } else 56 | input = new unsigned char[BITS_TO_BYTES(incoming.GetNumberOfUnreadBits())]; 57 | 58 | if (!incoming.ReadBits(input, bits_data, false)) { 59 | if (!used_alloca) 60 | delete[] input; 61 | 62 | return false; 63 | } 64 | 65 | callback_bs = std::make_shared(input, BITS_TO_BYTES(bits_data), true); 66 | 67 | if (!used_alloca) 68 | delete[] input; 69 | } 70 | 71 | for (auto it = rakhook::on_receive_rpc.begin(); it != rakhook::on_receive_rpc.end();) { 72 | if (auto f = *it) { 73 | if (!f(id, callback_bs.get())) 74 | return false; 75 | it++; 76 | } else { 77 | it = rakhook::on_receive_rpc.erase(it); 78 | } 79 | } 80 | 81 | incoming.SetWriteOffset(offset); 82 | incoming.Write(id); 83 | bits_data = BYTES_TO_BITS(callback_bs->GetNumberOfBytesUsed()); 84 | incoming.WriteCompressed(bits_data); 85 | if (bits_data) 86 | incoming.WriteBits(callback_bs->GetData(), bits_data, false); 87 | 88 | return orig(rp, std::bit_cast(incoming.GetData()), incoming.GetNumberOfBytesUsed(), playerid); 89 | } 90 | 91 | std::unique_ptr> destroy_ri_hook; 92 | std::unique_ptr> handle_rpc_hook; 93 | 94 | bool rakhook::initialize() { 95 | if (initialized) 96 | return true; 97 | 98 | if (!samp_addr()) 99 | return false; 100 | const uintptr_t samp_info = *std::bit_cast(offsets::samp_info(true)); 101 | if (!samp_info) 102 | return false; 103 | 104 | auto **rakclient_interface = std::bit_cast(samp_info + offsets::rakclient_interface()); 105 | if (!*rakclient_interface) 106 | return false; 107 | 108 | orig = *rakclient_interface; 109 | hooked_interface = new hooked_rakclient_interface(orig); 110 | *rakclient_interface = std::bit_cast(hooked_interface); 111 | 112 | if (!static_cast(destroy_ri_hook)) { 113 | auto func = std::bit_cast(offsets::destroy_interface(true)); 114 | destroy_ri_hook = std::make_unique(std::move(func), std::move(&destroy_rakclient_interface)); 115 | } 116 | if (!static_cast(handle_rpc_hook)) { 117 | auto func = std::bit_cast(offsets::handle_rpc_packet(true)); 118 | handle_rpc_hook = std::make_unique(std::move(func), std::move(&handle_rpc_packet)); 119 | } 120 | 121 | destroy_ri_hook->install(); 122 | handle_rpc_hook->install(); 123 | 124 | initialized = true; 125 | return true; 126 | } 127 | 128 | void rakhook::destroy() { 129 | if (!initialized) 130 | return; 131 | 132 | const uintptr_t samp_info = *std::bit_cast(samp_addr(offsets::samp_info())); 133 | if (!samp_info) 134 | return; 135 | 136 | auto **rakclient_interface = std::bit_cast(samp_info + offsets::rakclient_interface()); 137 | *rakclient_interface = orig; 138 | 139 | destroy_ri_hook.reset(); 140 | handle_rpc_hook.reset(); 141 | delete hooked_interface; 142 | } 143 | 144 | bool rakhook::send(RakNet::BitStream *bs, PacketPriority priority, PacketReliability reliability, char ord_channel) { 145 | if (!initialized) 146 | return false; 147 | return orig->Send(bs, priority, reliability, ord_channel); 148 | } 149 | 150 | bool rakhook::send_rpc(int id, RakNet::BitStream *bs, PacketPriority priority, PacketReliability reliability, char ord_channel, bool sh_timestamp) { 151 | if (!initialized) 152 | return false; 153 | return orig->RPC(&id, bs, priority, reliability, ord_channel, sh_timestamp); 154 | } 155 | 156 | bool rakhook::emul_rpc(unsigned char id, RakNet::BitStream &rpc_bs) { 157 | if (!initialized || !rakpeer) 158 | return false; 159 | 160 | RakNet::BitStream bs; 161 | bs.Write(ID_RPC); 162 | bs.Write(id); 163 | bs.WriteCompressed(BYTES_TO_BITS(rpc_bs.GetNumberOfBytesUsed())); 164 | bs.WriteBits(rpc_bs.GetData(), BYTES_TO_BITS(rpc_bs.GetNumberOfBytesUsed()), false); 165 | 166 | handle_rpc_packet_t handle_rpc; 167 | if (handle_rpc_hook && handle_rpc_hook->get_trampoline()) { 168 | handle_rpc = std::bit_cast(handle_rpc_hook->get_trampoline()); 169 | } else { 170 | handle_rpc = std::bit_cast(offsets::handle_rpc_packet(true)); 171 | } 172 | return handle_rpc(rakpeer, std::bit_cast(bs.GetData()), bs.GetNumberOfBytesUsed(), gplayerid); 173 | } 174 | 175 | bool rakhook::emul_packet(RakNet::BitStream &pbs) { 176 | if (!initialized || !rakpeer) 177 | return false; 178 | Packet *send_packet = std::bit_cast(samp_addr(offsets::alloc_packet()))(pbs.GetNumberOfBytesUsed()); 179 | memcpy(send_packet->data, pbs.GetData(), send_packet->length); 180 | 181 | // RakPeer::AddPacketToProducer 182 | char *packets = static_cast(rakpeer) + offsets::offset_packets(); 183 | auto write_lock = std::bit_cast(samp_addr(offsets::write_lock())); 184 | auto write_unlock = std::bit_cast(samp_addr(offsets::write_unlock())); 185 | 186 | *write_lock(packets) = send_packet; 187 | write_unlock(packets); 188 | 189 | return true; 190 | } -------------------------------------------------------------------------------- /src/RakNet/StringCompressor.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 | #include "../../include/RakNet/StringCompressor.h" 18 | #include "../../include/RakNet/DS_HuffmanEncodingTree.h" 19 | #include "../../include/RakNet/BitStream.h" 20 | #include 21 | #include 22 | #include 23 | 24 | StringCompressor* StringCompressor::instance=0; 25 | int StringCompressor::referenceCount=0; 26 | 27 | void StringCompressor::AddReference(void) 28 | { 29 | if (++referenceCount==1) 30 | { 31 | instance = new StringCompressor; 32 | } 33 | } 34 | void StringCompressor::RemoveReference(void) 35 | { 36 | assert(referenceCount > 0); 37 | 38 | if (referenceCount > 0) 39 | { 40 | if (--referenceCount==0) 41 | { 42 | delete instance; 43 | instance=0; 44 | } 45 | } 46 | } 47 | 48 | StringCompressor* StringCompressor::Instance(void) 49 | { 50 | return instance; 51 | } 52 | 53 | unsigned int englishCharacterFrequencies[ 256 ] = 54 | { 55 | 0, 56 | 0, 57 | 0, 58 | 0, 59 | 0, 60 | 0, 61 | 0, 62 | 0, 63 | 0, 64 | 0, 65 | 722, 66 | 0, 67 | 0, 68 | 2, 69 | 0, 70 | 0, 71 | 0, 72 | 0, 73 | 0, 74 | 0, 75 | 0, 76 | 0, 77 | 0, 78 | 0, 79 | 0, 80 | 0, 81 | 0, 82 | 0, 83 | 0, 84 | 0, 85 | 0, 86 | 0, 87 | 11084, 88 | 58, 89 | 63, 90 | 1, 91 | 0, 92 | 31, 93 | 0, 94 | 317, 95 | 64, 96 | 64, 97 | 44, 98 | 0, 99 | 695, 100 | 62, 101 | 980, 102 | 266, 103 | 69, 104 | 67, 105 | 56, 106 | 7, 107 | 73, 108 | 3, 109 | 14, 110 | 2, 111 | 69, 112 | 1, 113 | 167, 114 | 9, 115 | 1, 116 | 2, 117 | 25, 118 | 94, 119 | 0, 120 | 195, 121 | 139, 122 | 34, 123 | 96, 124 | 48, 125 | 103, 126 | 56, 127 | 125, 128 | 653, 129 | 21, 130 | 5, 131 | 23, 132 | 64, 133 | 85, 134 | 44, 135 | 34, 136 | 7, 137 | 92, 138 | 76, 139 | 147, 140 | 12, 141 | 14, 142 | 57, 143 | 15, 144 | 39, 145 | 15, 146 | 1, 147 | 1, 148 | 1, 149 | 2, 150 | 3, 151 | 0, 152 | 3611, 153 | 845, 154 | 1077, 155 | 1884, 156 | 5870, 157 | 841, 158 | 1057, 159 | 2501, 160 | 3212, 161 | 164, 162 | 531, 163 | 2019, 164 | 1330, 165 | 3056, 166 | 4037, 167 | 848, 168 | 47, 169 | 2586, 170 | 2919, 171 | 4771, 172 | 1707, 173 | 535, 174 | 1106, 175 | 152, 176 | 1243, 177 | 100, 178 | 0, 179 | 2, 180 | 0, 181 | 10, 182 | 0, 183 | 0, 184 | 0, 185 | 0, 186 | 0, 187 | 0, 188 | 0, 189 | 0, 190 | 0, 191 | 0, 192 | 0, 193 | 0, 194 | 0, 195 | 0, 196 | 0, 197 | 0, 198 | 0, 199 | 0, 200 | 0, 201 | 0, 202 | 0, 203 | 0, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 0, 209 | 0, 210 | 0, 211 | 0, 212 | 0, 213 | 0, 214 | 0, 215 | 0, 216 | 0, 217 | 0, 218 | 0, 219 | 0, 220 | 0, 221 | 0, 222 | 0, 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 0, 228 | 0, 229 | 0, 230 | 0, 231 | 0, 232 | 0, 233 | 0, 234 | 0, 235 | 0, 236 | 0, 237 | 0, 238 | 0, 239 | 0, 240 | 0, 241 | 0, 242 | 0, 243 | 0, 244 | 0, 245 | 0, 246 | 0, 247 | 0, 248 | 0, 249 | 0, 250 | 0, 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 0, 257 | 0, 258 | 0, 259 | 0, 260 | 0, 261 | 0, 262 | 0, 263 | 0, 264 | 0, 265 | 0, 266 | 0, 267 | 0, 268 | 0, 269 | 0, 270 | 0, 271 | 0, 272 | 0, 273 | 0, 274 | 0, 275 | 0, 276 | 0, 277 | 0, 278 | 0, 279 | 0, 280 | 0, 281 | 0, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 0, 287 | 0, 288 | 0, 289 | 0, 290 | 0, 291 | 0, 292 | 0, 293 | 0, 294 | 0, 295 | 0, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 0, 301 | 0, 302 | 0, 303 | 0, 304 | 0, 305 | 0, 306 | 0, 307 | 0, 308 | 0, 309 | 0, 310 | 0 311 | }; 312 | 313 | StringCompressor::StringCompressor() 314 | { 315 | DataStructures::Map::IMPLEMENT_DEFAULT_COMPARISON(); 316 | 317 | // Make a default tree immediately, since this is used for RPC possibly from multiple threads at the same time 318 | HuffmanEncodingTree *huffmanEncodingTree = new HuffmanEncodingTree; 319 | huffmanEncodingTree->GenerateFromFrequencyTable( englishCharacterFrequencies ); 320 | huffmanEncodingTrees.Set(0, huffmanEncodingTree); 321 | } 322 | void StringCompressor::GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, int languageID ) 323 | { 324 | HuffmanEncodingTree *huffmanEncodingTree; 325 | if (huffmanEncodingTrees.Has(languageID)) 326 | { 327 | huffmanEncodingTree = huffmanEncodingTrees.Get(languageID); 328 | delete huffmanEncodingTree; 329 | } 330 | 331 | unsigned index; 332 | unsigned int frequencyTable[ 256 ]; 333 | 334 | if ( inputLength == 0 ) 335 | return ; 336 | 337 | // Zero out the frequency table 338 | memset( frequencyTable, 0, sizeof( frequencyTable ) ); 339 | 340 | // Generate the frequency table from the strings 341 | for ( index = 0; index < inputLength; index++ ) 342 | frequencyTable[ input[ index ] ] ++; 343 | 344 | // Build the tree 345 | huffmanEncodingTree = new HuffmanEncodingTree; 346 | huffmanEncodingTree->GenerateFromFrequencyTable( frequencyTable ); 347 | huffmanEncodingTrees.Set(languageID, huffmanEncodingTree); 348 | } 349 | 350 | StringCompressor::~StringCompressor() 351 | { 352 | for (unsigned i=0; i < huffmanEncodingTrees.Size(); i++) 353 | delete huffmanEncodingTrees[i]; 354 | } 355 | 356 | void StringCompressor::EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, int languageID ) 357 | { 358 | HuffmanEncodingTree *huffmanEncodingTree; 359 | if (huffmanEncodingTrees.Has(languageID)==false) 360 | return; 361 | huffmanEncodingTree=huffmanEncodingTrees.Get(languageID); 362 | 363 | if ( input == 0 ) 364 | { 365 | output->WriteCompressed( (unsigned short) 0 ); 366 | return; 367 | } 368 | 369 | RakNet::BitStream encodedBitStream; 370 | 371 | unsigned short stringBitLength; 372 | 373 | int charsToWrite; 374 | 375 | if ( maxCharsToWrite<=0 || ( int ) strlen( input ) < maxCharsToWrite ) 376 | charsToWrite = ( int ) strlen( input ); 377 | else 378 | charsToWrite = maxCharsToWrite - 1; 379 | 380 | huffmanEncodingTree->EncodeArray( ( unsigned char* ) input, charsToWrite, &encodedBitStream ); 381 | 382 | stringBitLength = ( unsigned short ) encodedBitStream.GetNumberOfBitsUsed(); 383 | 384 | output->WriteCompressed( stringBitLength ); 385 | 386 | output->WriteBits( encodedBitStream.GetData(), stringBitLength ); 387 | } 388 | 389 | bool StringCompressor::DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, int languageID ) 390 | { 391 | HuffmanEncodingTree *huffmanEncodingTree; 392 | if (huffmanEncodingTrees.Has(languageID)==false) 393 | return false; 394 | huffmanEncodingTree=huffmanEncodingTrees.Get(languageID); 395 | 396 | unsigned short stringBitLength; 397 | int bytesInStream; 398 | 399 | output[ 0 ] = 0; 400 | 401 | if ( input->ReadCompressed( stringBitLength ) == false ) 402 | return false; 403 | 404 | if ( input->GetNumberOfUnreadBits() < stringBitLength ) 405 | return false; 406 | 407 | bytesInStream = huffmanEncodingTree->DecodeArray( input, stringBitLength, maxCharsToWrite, ( unsigned char* ) output ); 408 | 409 | if ( bytesInStream < maxCharsToWrite ) 410 | output[ bytesInStream ] = 0; 411 | else 412 | output[ maxCharsToWrite - 1 ] = 0; 413 | 414 | return true; 415 | } 416 | -------------------------------------------------------------------------------- /include/RakNet/DS_OrderedList.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] Quicksort ordered list. 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 | #include "DS_List.h" 19 | 20 | #ifndef __ORDERED_LIST_H 21 | #define __ORDERED_LIST_H 22 | 23 | /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures 24 | /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. 25 | namespace DataStructures 26 | { 27 | template 28 | int defaultOrderedListComparison(const key_type &a, const data_type &b) 29 | { 30 | if (a > 35 | class OrderedList 36 | { 37 | public: 38 | static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison(key_type(),data_type());} 39 | 40 | OrderedList(); 41 | ~OrderedList(); 42 | OrderedList( const OrderedList& original_copy ); 43 | OrderedList& operator= ( const OrderedList& original_copy ); 44 | 45 | /// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 46 | /// If the data type has comparison operators already defined then you can just use defaultComparison 47 | bool HasData(const key_type &key) const; 48 | unsigned GetIndexFromKey(const key_type &key, bool *objectExists) const; 49 | data_type GetElementFromKey(const key_type &key); 50 | unsigned Insert(const key_type &key, const data_type &data); 51 | unsigned Remove(const key_type &key); 52 | data_type& operator[] ( const unsigned int position ) const; 53 | void RemoveAtIndex(const unsigned index); 54 | void InsertAtIndex(const data_type &data, const unsigned index); 55 | void InsertAtEnd(const data_type &data); 56 | void Del(const unsigned num=1); 57 | void Clear(void); 58 | unsigned Size(void) const; 59 | 60 | protected: 61 | DataStructures::List orderedList; 62 | }; 63 | 64 | template 65 | OrderedList::OrderedList() 66 | { 67 | } 68 | 69 | template 70 | OrderedList::~OrderedList() 71 | { 72 | Clear(); 73 | } 74 | 75 | template 76 | OrderedList::OrderedList( const OrderedList& original_copy ) 77 | { 78 | orderedList=original_copy.orderedList; 79 | } 80 | 81 | template 82 | OrderedList& OrderedList::operator= ( const OrderedList& original_copy ) 83 | { 84 | orderedList=original_copy.orderedList; 85 | return *this; 86 | } 87 | 88 | template 89 | bool OrderedList::HasData(const key_type &key) const 90 | { 91 | bool objectExists; 92 | unsigned index; 93 | index = GetIndexFromKey(key, &objectExists); 94 | return objectExists; 95 | } 96 | 97 | template 98 | data_type OrderedList::GetElementFromKey(const key_type &key) 99 | { 100 | bool objectExists; 101 | unsigned index; 102 | index = GetIndexFromKey(key, &objectExists); 103 | assert(objectExists); 104 | return orderedList[index]; 105 | } 106 | 107 | template 108 | unsigned OrderedList::GetIndexFromKey(const key_type &key, bool *objectExists) const 109 | { 110 | int index, upperBound, lowerBound; 111 | int res; 112 | 113 | if (orderedList.Size()==0) 114 | { 115 | *objectExists=false; 116 | return 0; 117 | } 118 | 119 | upperBound=(int)orderedList.Size()-1; 120 | lowerBound=0; 121 | index = (int)orderedList.Size()/2; 122 | 123 | #ifdef _MSC_VER 124 | #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant 125 | #endif 126 | while (1) 127 | { 128 | res = comparison_function(key,orderedList[index]); 129 | if (res==0) 130 | { 131 | *objectExists=true; 132 | return index; 133 | } 134 | else if (res<0) 135 | { 136 | upperBound=index-1; 137 | } 138 | else// if (res>0) 139 | { 140 | lowerBound=index+1; 141 | } 142 | 143 | index=lowerBound+(upperBound-lowerBound)/2; 144 | 145 | if (lowerBound>upperBound) 146 | { 147 | *objectExists=false; 148 | return lowerBound; // No match 149 | } 150 | } 151 | } 152 | 153 | template 154 | unsigned OrderedList::Insert(const key_type &key, const data_type &data) 155 | { 156 | bool objectExists; 157 | unsigned index; 158 | index = GetIndexFromKey(key, &objectExists); 159 | 160 | // Don't allow duplicate insertion. 161 | if (objectExists) 162 | return (unsigned)-1; 163 | 164 | if (index>=orderedList.Size()) 165 | { 166 | orderedList.Insert(data); 167 | return orderedList.Size()-1; 168 | } 169 | else 170 | { 171 | orderedList.Insert(data,index); 172 | return index; 173 | } 174 | } 175 | 176 | template 177 | unsigned OrderedList::Remove(const key_type &key) 178 | { 179 | bool objectExists; 180 | unsigned index; 181 | index = GetIndexFromKey(key, &objectExists); 182 | 183 | // Can't find the element to remove if this assert hits 184 | assert(objectExists==true); 185 | if (objectExists==false) 186 | return 0; 187 | 188 | orderedList.RemoveAtIndex(index); 189 | return index; 190 | } 191 | 192 | template 193 | void OrderedList::RemoveAtIndex(const unsigned index) 194 | { 195 | orderedList.RemoveAtIndex(index); 196 | } 197 | 198 | template 199 | void OrderedList::InsertAtIndex(const data_type &data, const unsigned index) 200 | { 201 | orderedList.Insert(data, index); 202 | } 203 | 204 | template 205 | void OrderedList::InsertAtEnd(const data_type &data) 206 | { 207 | orderedList.Insert(data); 208 | } 209 | 210 | template 211 | void OrderedList::Del(const unsigned num) 212 | { 213 | orderedList.Del(num); 214 | } 215 | 216 | template 217 | void OrderedList::Clear(void) 218 | { 219 | orderedList.Clear(); 220 | } 221 | 222 | template 223 | data_type& OrderedList::operator[]( const unsigned int position ) const 224 | { 225 | return orderedList[position]; 226 | } 227 | 228 | template 229 | unsigned OrderedList::Size(void) const 230 | { 231 | return orderedList.Size(); 232 | } 233 | } 234 | 235 | #endif 236 | -------------------------------------------------------------------------------- /src/RakNet/DS_HuffmanEncodingTree.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 | #include "../../include/RakNet/DS_HuffmanEncodingTree.h" 18 | #include "../../include/RakNet/DS_Queue.h" 19 | #include "../../include/RakNet/BitStream.h" 20 | #include 21 | 22 | #ifdef _MSC_VER 23 | #pragma warning( push ) 24 | #endif 25 | 26 | HuffmanEncodingTree::HuffmanEncodingTree() 27 | { 28 | root = 0; 29 | } 30 | 31 | HuffmanEncodingTree::~HuffmanEncodingTree() 32 | { 33 | FreeMemory(); 34 | } 35 | 36 | void HuffmanEncodingTree::FreeMemory( void ) 37 | { 38 | if ( root == 0 ) 39 | return ; 40 | 41 | // Use an in-order traversal to delete the tree 42 | DataStructures::Queue nodeQueue; 43 | 44 | HuffmanEncodingTreeNode *node; 45 | 46 | nodeQueue.Push( root ); 47 | 48 | while ( nodeQueue.Size() > 0 ) 49 | { 50 | node = nodeQueue.Pop(); 51 | 52 | if ( node->left ) 53 | nodeQueue.Push( node->left ); 54 | 55 | if ( node->right ) 56 | nodeQueue.Push( node->right ); 57 | 58 | delete node; 59 | } 60 | 61 | // Delete the encoding table 62 | for ( int i = 0; i < 256; i++ ) 63 | delete [] encodingTable[ i ].encoding; 64 | 65 | root = 0; 66 | } 67 | 68 | 69 | ////#include 70 | 71 | // Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree 72 | void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ) 73 | { 74 | int counter; 75 | HuffmanEncodingTreeNode * node; 76 | HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier 77 | // 1. Make 256 trees each with a weight equal to the frequency of the corresponding character 78 | DataStructures::LinkedList huffmanEncodingTreeNodeList; 79 | 80 | FreeMemory(); 81 | 82 | for ( counter = 0; counter < 256; counter++ ) 83 | { 84 | node = new HuffmanEncodingTreeNode; 85 | node->left = 0; 86 | node->right = 0; 87 | node->value = (unsigned char) counter; 88 | node->weight = frequencyTable[ counter ]; 89 | 90 | if ( node->weight == 0 ) 91 | node->weight = 1; // 0 weights are illegal 92 | 93 | leafList[ counter ] = node; // Used later to generate the encryption table 94 | 95 | InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order. 96 | } 97 | 98 | 99 | // 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right 100 | // children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes. 101 | #ifdef _MSC_VER 102 | #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant 103 | #endif 104 | while ( 1 ) 105 | { 106 | huffmanEncodingTreeNodeList.Beginning(); 107 | HuffmanEncodingTreeNode *lesser, *greater; 108 | lesser = huffmanEncodingTreeNodeList.Pop(); 109 | greater = huffmanEncodingTreeNodeList.Pop(); 110 | node = new HuffmanEncodingTreeNode; 111 | node->left = lesser; 112 | node->right = greater; 113 | node->weight = lesser->weight + greater->weight; 114 | lesser->parent = node; // This is done to make generating the encryption table easier 115 | greater->parent = node; // This is done to make generating the encryption table easier 116 | 117 | if ( huffmanEncodingTreeNodeList.Size() == 0 ) 118 | { 119 | // 3. Assign the one remaining node in the list to the root node. 120 | root = node; 121 | root->parent = 0; 122 | break; 123 | } 124 | 125 | // Put the new node back into the list at the correct spot to maintain the sort. Linear search time 126 | InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); 127 | } 128 | 129 | bool tempPath[ 256 ]; // Maximum path length is 256 130 | unsigned short tempPathLength; 131 | HuffmanEncodingTreeNode *currentNode; 132 | RakNet::BitStream bitStream; 133 | 134 | // Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents. 135 | // This can be done more efficiently but this isn't bad and it's way easier to program and debug 136 | 137 | for ( counter = 0; counter < 256; counter++ ) 138 | { 139 | // Already done at the end of the loop and before it! 140 | tempPathLength = 0; 141 | 142 | // Set the current node at the leaf 143 | currentNode = leafList[ counter ]; 144 | 145 | do 146 | { 147 | if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root 148 | tempPath[ tempPathLength++ ] = false; 149 | else 150 | tempPath[ tempPathLength++ ] = true; 151 | 152 | currentNode = currentNode->parent; 153 | } 154 | 155 | while ( currentNode != root ); 156 | 157 | // Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf 158 | while ( tempPathLength-- > 0 ) 159 | { 160 | if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want 161 | bitStream.Write1(); 162 | else 163 | bitStream.Write0(); 164 | } 165 | 166 | // Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer 167 | encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding ); 168 | 169 | // Reset the bitstream for the next iteration 170 | bitStream.Reset(); 171 | } 172 | } 173 | 174 | // Pass an array of bytes to array and a preallocated BitStream to receive the output 175 | void HuffmanEncodingTree::EncodeArray( unsigned char *input, unsigned sizeInBytes, RakNet::BitStream * output ) 176 | { 177 | unsigned counter; 178 | 179 | // For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation 180 | for ( counter = 0; counter < sizeInBytes; counter++ ) 181 | { 182 | output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned 183 | } 184 | 185 | // Byte align the output so the unassigned remaining bits don't equate to some actual value 186 | if ( output->GetNumberOfBitsUsed() % 8 != 0 ) 187 | { 188 | // Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned. 189 | unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) ); 190 | 191 | for ( counter = 0; counter < 256; counter++ ) 192 | if ( encodingTable[ counter ].bitLength > remainingBits ) 193 | { 194 | output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned 195 | break; 196 | } 197 | 198 | #ifdef _DEBUG 199 | assert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits 200 | 201 | #endif 202 | 203 | } 204 | } 205 | 206 | unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, unsigned sizeInBits, unsigned maxCharsToWrite, unsigned char *output ) 207 | { 208 | HuffmanEncodingTreeNode * currentNode; 209 | 210 | unsigned outputWriteIndex; 211 | outputWriteIndex = 0; 212 | currentNode = root; 213 | 214 | // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root 215 | 216 | for ( unsigned counter = 0; counter < sizeInBits; counter++ ) 217 | { 218 | if ( input->ReadBit() == false ) // left! 219 | currentNode = currentNode->left; 220 | else 221 | currentNode = currentNode->right; 222 | 223 | if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf 224 | { 225 | 226 | if ( outputWriteIndex < maxCharsToWrite ) 227 | output[ outputWriteIndex ] = currentNode->value; 228 | 229 | outputWriteIndex++; 230 | 231 | currentNode = root; 232 | } 233 | } 234 | 235 | return outputWriteIndex; 236 | } 237 | 238 | // Pass an array of encoded bytes to array and a preallocated BitStream to receive the output 239 | void HuffmanEncodingTree::DecodeArray( unsigned char *input, unsigned sizeInBits, RakNet::BitStream * output ) 240 | { 241 | HuffmanEncodingTreeNode * currentNode; 242 | 243 | if ( sizeInBits <= 0 ) 244 | return ; 245 | 246 | RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false ); 247 | 248 | currentNode = root; 249 | 250 | // For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root 251 | for ( unsigned counter = 0; counter < sizeInBits; counter++ ) 252 | { 253 | if ( bitStream.ReadBit() == false ) // left! 254 | currentNode = currentNode->left; 255 | else 256 | currentNode = currentNode->right; 257 | 258 | if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf 259 | { 260 | output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING 261 | currentNode = root; 262 | } 263 | } 264 | } 265 | 266 | // Insertion sort. Slow but easy to write in this case 267 | void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const 268 | { 269 | if ( huffmanEncodingTreeNodeList->Size() == 0 ) 270 | { 271 | huffmanEncodingTreeNodeList->Insert( node ); 272 | return ; 273 | } 274 | 275 | huffmanEncodingTreeNodeList->Beginning(); 276 | 277 | unsigned counter = 0; 278 | #ifdef _MSC_VER 279 | #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant 280 | #endif 281 | while ( 1 ) 282 | { 283 | if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight ) 284 | ++( *huffmanEncodingTreeNodeList ); 285 | else 286 | { 287 | huffmanEncodingTreeNodeList->Insert( node ); 288 | break; 289 | } 290 | 291 | // Didn't find a spot in the middle - add to the end 292 | if ( ++counter == huffmanEncodingTreeNodeList->Size() ) 293 | { 294 | huffmanEncodingTreeNodeList->End(); 295 | 296 | huffmanEncodingTreeNodeList->Add( node ) 297 | 298 | ; // Add to the end 299 | break; 300 | } 301 | } 302 | } 303 | 304 | #ifdef _MSC_VER 305 | #pragma warning( pop ) 306 | #endif 307 | -------------------------------------------------------------------------------- /include/RakNet/DS_Queue.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] A queue used by RakNet. 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 __QUEUE_H 19 | #define __QUEUE_H 20 | 21 | // Template classes have to have all the code in the header file 22 | #include 23 | 24 | /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures 25 | /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. 26 | namespace DataStructures 27 | { 28 | /// \brief A queue implemented as an array with a read and write index. 29 | template 30 | class Queue 31 | { 32 | public: 33 | Queue(); 34 | ~Queue(); 35 | Queue( Queue& original_copy ); 36 | bool operator= ( const Queue& original_copy ); 37 | void Push( const queue_type& input ); 38 | void PushAtHead( const queue_type& input, unsigned index=0 ); 39 | queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency 40 | void Del( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency 41 | inline queue_type Peek( void ) const; 42 | inline queue_type Pop( void ); 43 | inline unsigned int Size( void ) const; 44 | inline bool IsEmpty(void) const; 45 | inline unsigned int AllocationSize( void ) const; 46 | inline void Clear( void ); 47 | void Compress( void ); 48 | bool Find ( queue_type q ); 49 | void ClearAndForceAllocation( int size ); // Force a memory allocation to a certain larger size 50 | 51 | private: 52 | queue_type* array; 53 | unsigned int head; // Array index for the head of the queue 54 | unsigned int tail; // Array index for the tail of the queue 55 | unsigned int allocation_size; 56 | }; 57 | 58 | 59 | template 60 | inline unsigned int Queue::Size( void ) const 61 | { 62 | if ( head <= tail ) 63 | return tail -head; 64 | else 65 | return allocation_size -head + tail; 66 | } 67 | 68 | template 69 | inline bool Queue::IsEmpty(void) const 70 | { 71 | return head==tail; 72 | } 73 | 74 | template 75 | inline unsigned int Queue::AllocationSize( void ) const 76 | { 77 | return allocation_size; 78 | } 79 | 80 | template 81 | Queue::Queue() 82 | { 83 | allocation_size = 16; 84 | array = new queue_type[ allocation_size ]; 85 | head = 0; 86 | tail = 0; 87 | } 88 | 89 | template 90 | Queue::~Queue() 91 | { 92 | if (allocation_size>0) 93 | delete [] array; 94 | } 95 | 96 | template 97 | inline queue_type Queue::Pop( void ) 98 | { 99 | #ifdef _DEBUG 100 | assert( allocation_size > 0 && Size() >= 0 && head != tail); 101 | #endif 102 | //head=(head+1) % allocation_size; 103 | 104 | if ( ++head == allocation_size ) 105 | head = 0; 106 | 107 | if ( head == 0 ) 108 | return ( queue_type ) array[ allocation_size -1 ]; 109 | 110 | return ( queue_type ) array[ head -1 ]; 111 | } 112 | 113 | template 114 | void Queue::PushAtHead( const queue_type& input, unsigned index ) 115 | { 116 | if ( allocation_size == 0 ) 117 | { 118 | array = new queue_type[ 16 ]; 119 | head = 0; 120 | tail = 1; 121 | array[ 0 ] = input; 122 | allocation_size = 16; 123 | return ; 124 | } 125 | 126 | if ( head == 0 ) 127 | head = allocation_size - 1; 128 | else 129 | --head; 130 | 131 | unsigned count=0; 132 | while (count < index) 133 | { 134 | array[head+count]=array[head+count+1]; 135 | count++; 136 | } 137 | array[ head+count ] = input; 138 | 139 | if ( tail == head ) 140 | { 141 | // unsigned int index=tail; 142 | 143 | // Need to allocate more memory. 144 | queue_type * new_array; 145 | new_array = new queue_type[ allocation_size * 2 ]; 146 | #ifdef _DEBUG 147 | 148 | assert( new_array ); 149 | #endif 150 | 151 | for ( unsigned int counter = 0; counter < allocation_size; ++counter ) 152 | new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; 153 | 154 | head = 0; 155 | 156 | tail = allocation_size; 157 | 158 | allocation_size *= 2; 159 | 160 | // Delete the old array and move the pointer to the new array 161 | delete [] array; 162 | 163 | array = new_array; 164 | } 165 | } 166 | 167 | 168 | template 169 | inline queue_type Queue::Peek( void ) const 170 | { 171 | #ifdef _DEBUG 172 | assert( head != tail ); 173 | assert( allocation_size > 0 && Size() >= 0 ); 174 | #endif 175 | 176 | return ( queue_type ) array[ head ]; 177 | } 178 | 179 | template 180 | void Queue::Push( const queue_type& input ) 181 | { 182 | if ( allocation_size == 0 ) 183 | { 184 | array = new queue_type[ 16 ]; 185 | head = 0; 186 | tail = 1; 187 | array[ 0 ] = input; 188 | allocation_size = 16; 189 | return ; 190 | } 191 | 192 | array[ tail++ ] = input; 193 | 194 | if ( tail == allocation_size ) 195 | tail = 0; 196 | 197 | if ( tail == head ) 198 | { 199 | // unsigned int index=tail; 200 | 201 | // Need to allocate more memory. 202 | queue_type * new_array; 203 | new_array = new queue_type[ allocation_size * 2 ]; 204 | #ifdef _DEBUG 205 | 206 | assert( new_array ); 207 | #endif 208 | 209 | for ( unsigned int counter = 0; counter < allocation_size; ++counter ) 210 | new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; 211 | 212 | head = 0; 213 | 214 | tail = allocation_size; 215 | 216 | allocation_size *= 2; 217 | 218 | // Delete the old array and move the pointer to the new array 219 | delete [] array; 220 | 221 | array = new_array; 222 | } 223 | 224 | } 225 | 226 | template 227 | Queue::Queue( Queue& original_copy ) 228 | { 229 | // Allocate memory for copy 230 | 231 | if ( original_copy.Size() == 0 ) 232 | { 233 | allocation_size = 0; 234 | } 235 | 236 | else 237 | { 238 | array = new queue_type [ original_copy.Size() + 1 ]; 239 | 240 | for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) 241 | array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; 242 | 243 | head = 0; 244 | 245 | tail = original_copy.Size(); 246 | 247 | allocation_size = original_copy.Size() + 1; 248 | } 249 | } 250 | 251 | template 252 | bool Queue::operator= ( const Queue& original_copy ) 253 | { 254 | if ( ( &original_copy ) == this ) 255 | return false; 256 | 257 | Clear(); 258 | 259 | // Allocate memory for copy 260 | if ( original_copy.Size() == 0 ) 261 | { 262 | allocation_size = 0; 263 | } 264 | 265 | else 266 | { 267 | array = new queue_type [ original_copy.Size() + 1 ]; 268 | 269 | for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter ) 270 | array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ]; 271 | 272 | head = 0; 273 | 274 | tail = original_copy.Size(); 275 | 276 | allocation_size = original_copy.Size() + 1; 277 | } 278 | 279 | return true; 280 | } 281 | 282 | template 283 | inline void Queue::Clear ( void ) 284 | { 285 | if ( allocation_size == 0 ) 286 | return ; 287 | 288 | if (allocation_size > 32) 289 | { 290 | delete[] array; 291 | allocation_size = 0; 292 | } 293 | 294 | head = 0; 295 | tail = 0; 296 | } 297 | 298 | template 299 | void Queue::Compress ( void ) 300 | { 301 | queue_type* new_array; 302 | unsigned int newAllocationSize; 303 | if (allocation_size==0) 304 | return; 305 | 306 | newAllocationSize=1; 307 | while (newAllocationSize <= Size()) 308 | newAllocationSize<<=1; // Must be a better way to do this but I'm too dumb to figure it out quickly :) 309 | 310 | new_array = new queue_type [newAllocationSize]; 311 | 312 | for (unsigned int counter=0; counter < Size(); ++counter) 313 | new_array[counter] = array[(head + counter)%(allocation_size)]; 314 | 315 | tail=Size(); 316 | allocation_size=newAllocationSize; 317 | head=0; 318 | 319 | // Delete the old array and move the pointer to the new array 320 | delete [] array; 321 | array=new_array; 322 | } 323 | 324 | template 325 | bool Queue::Find ( queue_type q ) 326 | { 327 | if ( allocation_size == 0 ) 328 | return false; 329 | 330 | unsigned int counter = head; 331 | 332 | while ( counter != tail ) 333 | { 334 | if ( array[ counter ] == q ) 335 | return true; 336 | 337 | counter = ( counter + 1 ) % allocation_size; 338 | } 339 | 340 | return false; 341 | } 342 | 343 | template 344 | void Queue::ClearAndForceAllocation( int size ) 345 | { 346 | delete [] array; 347 | array = new queue_type[ size ]; 348 | allocation_size = size; 349 | head = 0; 350 | tail = 0; 351 | } 352 | 353 | template 354 | inline queue_type& Queue::operator[] ( unsigned int position ) const 355 | { 356 | #ifdef _DEBUG 357 | assert( position < Size() ); 358 | #endif 359 | //return array[(head + position) % allocation_size]; 360 | 361 | if ( head + position >= allocation_size ) 362 | return array[ head + position - allocation_size ]; 363 | else 364 | return array[ head + position ]; 365 | } 366 | 367 | template 368 | void Queue::Del( unsigned int position ) 369 | { 370 | #ifdef _DEBUG 371 | assert( position < Size() ); 372 | assert( head != tail ); 373 | #endif 374 | 375 | if ( head == tail || position >= Size() ) 376 | return ; 377 | 378 | unsigned int index; 379 | 380 | unsigned int next; 381 | 382 | //index = (head + position) % allocation_size; 383 | if ( head + position >= allocation_size ) 384 | index = head + position - allocation_size; 385 | else 386 | index = head + position; 387 | 388 | //next = (index + 1) % allocation_size; 389 | next = index + 1; 390 | 391 | if ( next == allocation_size ) 392 | next = 0; 393 | 394 | while ( next != tail ) 395 | { 396 | // Overwrite the previous element 397 | array[ index ] = array[ next ]; 398 | index = next; 399 | //next = (next + 1) % allocation_size; 400 | 401 | if ( ++next == allocation_size ) 402 | next = 0; 403 | } 404 | 405 | // Move the tail back 406 | if ( tail == 0 ) 407 | tail = allocation_size - 1; 408 | else 409 | --tail; 410 | } 411 | } // End namespace 412 | 413 | #endif 414 | 415 | -------------------------------------------------------------------------------- /include/RakHook/hooked_rakclient_interface.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | PROJECT: mod_sa 3 | LICENSE: See LICENSE in the top level directory 4 | COPYRIGHT: Copyright we_sux, BlastHack 5 | 6 | mod_sa is available from https://github.com/BlastHackNet/mod_s0beit_sa/ 7 | 8 | mod_sa is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | mod_sa is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with mod_sa. If not, see . 20 | */ 21 | 22 | #ifndef RAKHOOK_INTERFACE_HPP 23 | #define RAKHOOK_INTERFACE_HPP 24 | 25 | #include "RakHook/rakhook.hpp" 26 | #include "../RakNet/RakClientInterface.h" 27 | 28 | class hooked_rakclient_interface { 29 | public: 30 | hooked_rakclient_interface(RakClientInterface *rc){}; 31 | virtual ~hooked_rakclient_interface() = default; 32 | 33 | virtual bool Connect(const char *host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer) { 34 | return rakhook::orig->Connect(host, serverPort, clientPort, depreciated, threadSleepTimer); 35 | } 36 | 37 | virtual void Disconnect(unsigned int blockDuration, unsigned char orderingChannel = 0) { 38 | rakhook::orig->Disconnect(blockDuration, orderingChannel); 39 | } 40 | 41 | virtual void InitializeSecurity(const char *privKeyP, const char *privKeyQ) { 42 | rakhook::orig->InitializeSecurity(privKeyP, privKeyQ); 43 | } 44 | 45 | virtual void SetPassword(const char *_password) { 46 | rakhook::orig->SetPassword(_password); 47 | } 48 | 49 | virtual bool HasPassword(void) const { 50 | return rakhook::orig->HasPassword(); 51 | } 52 | 53 | virtual bool Send(const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel) { 54 | return rakhook::orig->Send(data, length, priority, reliability, orderingChannel); 55 | } 56 | 57 | virtual bool Send(RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel) { 58 | for (auto it = rakhook::on_send_packet.begin(); it != rakhook::on_send_packet.end();) { 59 | if (auto f = *it) { 60 | if (!f(bitStream, priority, reliability, orderingChannel)) 61 | return false; 62 | it++; 63 | } else { 64 | it = rakhook::on_send_packet.erase(it); 65 | } 66 | } 67 | return rakhook::orig->Send(bitStream, priority, reliability, orderingChannel); 68 | } 69 | 70 | virtual Packet *Receive(void) { 71 | Packet *packet = rakhook::orig->Receive(); 72 | if (!packet) 73 | return nullptr; 74 | 75 | for (auto it = rakhook::on_receive_packet.begin(); it != rakhook::on_receive_packet.end();) { 76 | if (auto f = *it) { 77 | if (!f(packet)) { 78 | rakhook::orig->DeallocatePacket(packet); 79 | return nullptr; 80 | } 81 | it++; 82 | } else { 83 | it = rakhook::on_receive_packet.erase(it); 84 | } 85 | } 86 | return packet; 87 | } 88 | 89 | virtual void DeallocatePacket(Packet *packet) { 90 | rakhook::orig->DeallocatePacket(packet); 91 | } 92 | 93 | virtual void PingServer(void) { 94 | rakhook::orig->PingServer(); 95 | } 96 | 97 | virtual void PingServer(const char *host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections) { 98 | rakhook::orig->PingServer(host, serverPort, clientPort, onlyReplyOnAcceptingConnections); 99 | } 100 | 101 | virtual int GetAveragePing(void) { 102 | return rakhook::orig->GetAveragePing(); 103 | } 104 | 105 | virtual int GetLastPing(void) const { 106 | return rakhook::orig->GetLastPing(); 107 | } 108 | 109 | virtual int GetLowestPing(void) const { 110 | return rakhook::orig->GetLowestPing(); 111 | } 112 | 113 | virtual int GetPlayerPing(const PlayerID playerId) { 114 | return rakhook::orig->GetPlayerPing(playerId); 115 | } 116 | 117 | virtual void StartOccasionalPing(void) { 118 | rakhook::orig->StartOccasionalPing(); 119 | } 120 | 121 | virtual void StopOccasionalPing(void) { 122 | rakhook::orig->StopOccasionalPing(); 123 | } 124 | 125 | virtual bool IsConnected(void) const { 126 | return rakhook::orig->IsConnected(); 127 | } 128 | 129 | virtual unsigned int GetSynchronizedRandomInteger(void) const { 130 | return rakhook::orig->GetSynchronizedRandomInteger(); 131 | } 132 | 133 | virtual bool GenerateCompressionLayer(unsigned int inputFrequencyTable[256], bool inputLayer) { 134 | return rakhook::orig->GenerateCompressionLayer(inputFrequencyTable, inputLayer); 135 | } 136 | 137 | virtual bool DeleteCompressionLayer(bool inputLayer) { 138 | return rakhook::orig->DeleteCompressionLayer(inputLayer); 139 | }; 140 | 141 | virtual void RegisterAsRemoteProcedureCall(int *uniqueID, void (*functionPointer)(RPCParameters *rpcParms)) { 142 | rakhook::orig->RegisterAsRemoteProcedureCall(uniqueID, functionPointer); 143 | }; 144 | 145 | virtual void RegisterClassMemberRPC(int *uniqueID, void *functionPointer) { 146 | rakhook::orig->RegisterClassMemberRPC(uniqueID, functionPointer); 147 | } 148 | 149 | virtual void UnregisterAsRemoteProcedureCall(int *uniqueID) { 150 | rakhook::orig->UnregisterAsRemoteProcedureCall(uniqueID); 151 | } 152 | 153 | virtual bool RPC(int *uniqueID, const char *data, unsigned int bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, 154 | bool shiftTimestamp) { 155 | return rakhook::orig->RPC(uniqueID, data, bitLength, priority, reliability, orderingChannel, shiftTimestamp); 156 | }; 157 | 158 | virtual bool RPC(int *uniqueID, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, 159 | bool shiftTimestamp) { 160 | if (!uniqueID) 161 | return rakhook::orig->RPC(uniqueID, bitStream, priority, reliability, orderingChannel, shiftTimestamp); 162 | 163 | int _uniqueID = *uniqueID; 164 | for (auto it = rakhook::on_send_rpc.begin(); it != rakhook::on_send_rpc.end();) { 165 | if (auto f = *it) { 166 | if (!f(_uniqueID, bitStream, priority, reliability, orderingChannel, shiftTimestamp)) 167 | return false; 168 | it++; 169 | } else { 170 | it = rakhook::on_send_rpc.erase(it); 171 | } 172 | } 173 | return rakhook::orig->RPC(&_uniqueID, bitStream, priority, reliability, orderingChannel, shiftTimestamp); 174 | } 175 | 176 | virtual bool RPC_(int *uniqueID, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, 177 | bool shiftTimestamp, NetworkID networkID) { 178 | return rakhook::orig->RPC_(uniqueID, bitStream, priority, reliability, orderingChannel, shiftTimestamp, networkID); 179 | } 180 | 181 | virtual void SetTrackFrequencyTable(bool b) { 182 | return rakhook::orig->SetTrackFrequencyTable(b); 183 | } 184 | 185 | virtual bool GetSendFrequencyTable(unsigned int outputFrequencyTable[256]) { 186 | return rakhook::orig->GetSendFrequencyTable(outputFrequencyTable); 187 | } 188 | 189 | virtual float GetCompressionRatio(void) const { 190 | return rakhook::orig->GetCompressionRatio(); 191 | } 192 | 193 | virtual float GetDecompressionRatio(void) const { 194 | return rakhook::orig->GetDecompressionRatio(); 195 | } 196 | 197 | virtual void AttachPlugin(void *messageHandler) { 198 | return rakhook::orig->AttachPlugin(messageHandler); 199 | } 200 | 201 | virtual void DetachPlugin(void *messageHandler) { 202 | return rakhook::orig->DetachPlugin(messageHandler); 203 | } 204 | 205 | virtual RakNet::BitStream *GetStaticServerData(void) { 206 | return rakhook::orig->GetStaticServerData(); 207 | } 208 | 209 | virtual void SetStaticServerData(const char *data, const int length) { 210 | return rakhook::orig->SetStaticServerData(data, length); 211 | } 212 | 213 | virtual RakNet::BitStream *GetStaticClientData(const PlayerID playerId) { 214 | return rakhook::orig->GetStaticClientData(playerId); 215 | } 216 | 217 | virtual void SetStaticClientData(const PlayerID playerId, const char *data, const int length) { 218 | return rakhook::orig->SetStaticClientData(playerId, data, length); 219 | } 220 | 221 | virtual void SendStaticClientDataToServer(void) { 222 | return rakhook::orig->SendStaticClientDataToServer(); 223 | } 224 | 225 | virtual PlayerID GetServerID(void) const { 226 | return rakhook::orig->GetServerID(); 227 | } 228 | 229 | virtual PlayerID GetPlayerID(void) const { 230 | return rakhook::orig->GetPlayerID(); 231 | } 232 | 233 | virtual PlayerID GetInternalID(void) const { 234 | return rakhook::orig->GetInternalID(); 235 | } 236 | 237 | virtual const char *PlayerIDToDottedIP(const PlayerID playerId) const { 238 | return rakhook::orig->PlayerIDToDottedIP(playerId); 239 | } 240 | 241 | virtual void PushBackPacket(Packet *packet, bool pushAtHead) { 242 | return rakhook::orig->PushBackPacket(packet, pushAtHead); 243 | } 244 | 245 | virtual void SetRouterInterface(void *routerInterface) { 246 | return rakhook::orig->SetRouterInterface(routerInterface); 247 | } 248 | 249 | virtual void RemoveRouterInterface(void *routerInterface) { 250 | return rakhook::orig->RemoveRouterInterface(routerInterface); 251 | } 252 | 253 | virtual void SetTimeoutTime(RakNetTime timeMS) { 254 | return rakhook::orig->SetTimeoutTime(timeMS); 255 | } 256 | 257 | virtual bool SetMTUSize(int size) { 258 | return rakhook::orig->SetMTUSize(size); 259 | } 260 | 261 | virtual int GetMTUSize(void) const { 262 | return rakhook::orig->GetMTUSize(); 263 | } 264 | 265 | virtual void AllowConnectionResponseIPMigration(bool allow) { 266 | return rakhook::orig->AllowConnectionResponseIPMigration(allow); 267 | } 268 | 269 | virtual void AdvertiseSystem(const char *host, unsigned short remotePort, const char *data, int dataLength) { 270 | return rakhook::orig->AdvertiseSystem(host, remotePort, data, dataLength); 271 | } 272 | 273 | virtual void *const GetStatistics(void) { 274 | return rakhook::orig->GetStatistics(); 275 | } 276 | 277 | virtual void ApplyNetworkSimulator(double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance) { 278 | return rakhook::orig->ApplyNetworkSimulator(maxSendBPS, minExtraPing, extraPingVariance); 279 | } 280 | 281 | virtual bool IsNetworkSimulatorActive(void) { 282 | return rakhook::orig->IsNetworkSimulatorActive(); 283 | } 284 | 285 | virtual PlayerIndex GetPlayerIndex(void) { 286 | return rakhook::orig->GetPlayerIndex(); 287 | } 288 | }; 289 | 290 | #endif // RAKHOOK_INTERFACE_HPP -------------------------------------------------------------------------------- /include/RakNet/DS_Map.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] Map 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 __RAKNET_MAP_H 19 | #define __RAKNET_MAP_H 20 | 21 | #include "DS_OrderedList.h" 22 | 23 | // If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html 24 | // This makes insertions and deletions faster. But then traversals are slow, while they are currently fast. 25 | 26 | /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures 27 | /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. 28 | namespace DataStructures 29 | { 30 | /// The default comparison has to be first so it can be called as a default parameter. 31 | /// It then is followed by MapNode, followed by NodeComparisonFunc 32 | template 33 | int defaultMapKeyComparison(const key_type &a, const key_type &b) 34 | { 35 | if (a > 40 | class Map 41 | { 42 | public: 43 | static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(key_type(),key_type());} 44 | 45 | struct MapNode 46 | { 47 | MapNode() {} 48 | MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {} 49 | key_type mapNodeKey; 50 | data_type mapNodeData; 51 | }; 52 | 53 | // Has to be a static because the comparison callback for DataStructures::OrderedList is a C function 54 | static int NodeComparisonFunc(const key_type &a, const MapNode &b) 55 | { 56 | #ifdef _MSC_VER 57 | #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant 58 | #endif 59 | return key_comparison_func(a, b.mapNodeKey); 60 | } 61 | 62 | Map(); 63 | ~Map(); 64 | Map( const Map& original_copy ); 65 | Map& operator= ( const Map& original_copy ); 66 | 67 | data_type& Get(const key_type &key); 68 | data_type Pop(const key_type &key); 69 | // Add if needed 70 | void Set(const key_type &key, const data_type &data); 71 | // Must already exist 72 | void SetExisting(const key_type &key, const data_type &data); 73 | // Must add 74 | void SetNew(const key_type &key, const data_type &data); 75 | bool Has(const key_type &key); 76 | bool Delete(const key_type &key); 77 | data_type& operator[] ( const unsigned int position ) const; 78 | key_type GetKeyAtIndex( const unsigned int position ) const; 79 | unsigned GetIndexAtKey( const key_type &key ); 80 | void RemoveAtIndex(const unsigned index); 81 | void Clear(void); 82 | unsigned Size(void) const; 83 | 84 | protected: 85 | DataStructures::OrderedList< key_type,MapNode,Map::NodeComparisonFunc > mapNodeList; 86 | 87 | void SaveLastSearch(const key_type &key, unsigned index); 88 | bool HasSavedSearchResult(const key_type &key) const; 89 | 90 | unsigned lastSearchIndex; 91 | key_type lastSearchKey; 92 | bool lastSearchIndexValid; 93 | }; 94 | 95 | template 96 | Map::Map() 97 | { 98 | lastSearchIndexValid=false; 99 | } 100 | 101 | template 102 | Map::~Map() 103 | { 104 | Clear(); 105 | } 106 | 107 | template 108 | Map::Map( const Map& original_copy ) 109 | { 110 | mapNodeList=original_copy.mapNodeList; 111 | lastSearchIndex=original_copy.lastSearchIndex; 112 | lastSearchKey=original_copy.lastSearchKey; 113 | lastSearchIndexValid=original_copy.lastSearchIndexValid; 114 | } 115 | 116 | template 117 | Map& Map::operator= ( const Map& original_copy ) 118 | { 119 | mapNodeList=original_copy.mapNodeList; 120 | lastSearchIndex=original_copy.lastSearchIndex; 121 | lastSearchKey=original_copy.lastSearchKey; 122 | lastSearchIndexValid=original_copy.lastSearchIndexValid; 123 | return *this; 124 | } 125 | 126 | template 127 | data_type& Map::Get(const key_type &key) 128 | { 129 | if (HasSavedSearchResult(key)) 130 | return mapNodeList[lastSearchIndex].mapNodeData; 131 | 132 | bool objectExists; 133 | unsigned index; 134 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 135 | assert(objectExists); 136 | SaveLastSearch(key,index); 137 | return mapNodeList[index].mapNodeData; 138 | } 139 | 140 | template 141 | unsigned Map::GetIndexAtKey( const key_type &key ) 142 | { 143 | if (HasSavedSearchResult(key)) 144 | return lastSearchIndex; 145 | 146 | bool objectExists; 147 | unsigned index; 148 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 149 | assert(objectExists); 150 | SaveLastSearch(key,index); 151 | return index; 152 | } 153 | 154 | template 155 | void Map::RemoveAtIndex(const unsigned index) 156 | { 157 | mapNodeList.RemoveAtIndex(index); 158 | lastSearchIndexValid=false; 159 | } 160 | 161 | template 162 | data_type Map::Pop(const key_type &key) 163 | { 164 | bool objectExists; 165 | unsigned index; 166 | if (HasSavedSearchResult(key)) 167 | index=lastSearchIndex; 168 | else 169 | { 170 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 171 | assert(objectExists); 172 | } 173 | data_type tmp = mapNodeList[index].mapNodeData; 174 | mapNodeList.RemoveAtIndex(index); 175 | lastSearchIndexValid=false; 176 | return tmp; 177 | } 178 | 179 | template 180 | void Map::Set(const key_type &key, const data_type &data) 181 | { 182 | bool objectExists; 183 | unsigned index; 184 | 185 | if (HasSavedSearchResult(key)) 186 | { 187 | mapNodeList[lastSearchIndex].mapNodeData=data; 188 | return; 189 | } 190 | 191 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 192 | 193 | if (objectExists) 194 | { 195 | SaveLastSearch(key,index); 196 | mapNodeList[index].mapNodeData=data; 197 | } 198 | else 199 | { 200 | SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data))); 201 | } 202 | } 203 | 204 | template 205 | void Map::SetExisting(const key_type &key, const data_type &data) 206 | { 207 | bool objectExists; 208 | unsigned index; 209 | 210 | if (HasSavedSearchResult(key)) 211 | { 212 | index=lastSearchIndex; 213 | } 214 | else 215 | { 216 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 217 | assert(objectExists); 218 | SaveLastSearch(key,index); 219 | } 220 | 221 | mapNodeList[index].mapNodeData=data; 222 | } 223 | 224 | template 225 | void Map::SetNew(const key_type &key, const data_type &data) 226 | { 227 | #ifdef _DEBUG 228 | unsigned index; 229 | bool objectExists; 230 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 231 | assert(objectExists==false); 232 | #endif 233 | SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data))); 234 | } 235 | 236 | template 237 | bool Map::Has(const key_type &key) 238 | { 239 | if (HasSavedSearchResult(key)) 240 | return true; 241 | 242 | bool objectExists; 243 | unsigned index; 244 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 245 | if (objectExists) 246 | SaveLastSearch(key,index); 247 | return objectExists; 248 | } 249 | 250 | template 251 | bool Map::Delete(const key_type &key) 252 | { 253 | if (HasSavedSearchResult(key)) 254 | { 255 | lastSearchIndexValid=false; 256 | mapNodeList.RemoveAtIndex(lastSearchIndex); 257 | return true; 258 | } 259 | 260 | bool objectExists; 261 | unsigned index; 262 | index=mapNodeList.GetIndexFromKey(key, &objectExists); 263 | if (objectExists) 264 | { 265 | lastSearchIndexValid=false; 266 | mapNodeList.RemoveAtIndex(index); 267 | return true; 268 | } 269 | else 270 | return false; 271 | } 272 | 273 | template 274 | void Map::Clear(void) 275 | { 276 | lastSearchIndexValid=false; 277 | mapNodeList.Clear(); 278 | } 279 | 280 | template 281 | data_type& Map::operator[]( const unsigned int position ) const 282 | { 283 | return mapNodeList[position].mapNodeData; 284 | } 285 | 286 | template 287 | key_type Map::GetKeyAtIndex( const unsigned int position ) const 288 | { 289 | return mapNodeList[position].mapNodeKey; 290 | } 291 | 292 | template 293 | unsigned Map::Size(void) const 294 | { 295 | return mapNodeList.Size(); 296 | } 297 | 298 | template 299 | void Map::SaveLastSearch(const key_type &key, const unsigned index) 300 | { 301 | lastSearchIndex=index; 302 | lastSearchKey=key; 303 | lastSearchIndexValid=true; 304 | } 305 | 306 | template 307 | bool Map::HasSavedSearchResult(const key_type &key) const 308 | { 309 | return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0; 310 | } 311 | } 312 | 313 | #endif 314 | -------------------------------------------------------------------------------- /include/RakNet/DS_List.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] Array based list. Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access. 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 __LIST_H 19 | #define __LIST_H 20 | 21 | #include 22 | #include // memmove 23 | 24 | /// Maximum unsigned long 25 | static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; 26 | 27 | /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures 28 | /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. 29 | namespace DataStructures 30 | { 31 | /// \brief Array based implementation of a list. 32 | template 33 | class List 34 | { 35 | public: 36 | /// Default constructor 37 | List(); 38 | 39 | /// Destructor 40 | ~List(); 41 | 42 | /// Copy constructor 43 | /// \param[in] original_copy The list to duplicate 44 | List( const List& original_copy ); 45 | 46 | /// Assign one list to another 47 | List& operator= ( const List& original_copy ); 48 | 49 | /// Access an element by its index in the array 50 | /// \param[in] position The index into the array. 51 | /// \return The element at position \a position. 52 | list_type& operator[] ( const unsigned int position ) const; 53 | 54 | /// Insert an element at position \a position in the list 55 | /// \param[in] input The new element. 56 | /// \param[in] position The position of the new element. 57 | void Insert( const list_type input, const unsigned int position ); 58 | 59 | /// Insert at the end of the list. 60 | /// \param[in] input The new element. 61 | void Insert( const list_type input ); 62 | 63 | /// Replace the value at \a position by \a input. If the size of 64 | /// the list is less than @em position, it increase the capacity of 65 | /// the list and fill slot with @em filler. 66 | /// \param[in] input The element to replace at position @em position. 67 | /// \param[in] filler The element use to fill new allocated capacity. 68 | /// \param[in] position The position of input in the list. 69 | void Replace( const list_type input, const list_type filler, const unsigned int position ); 70 | 71 | /// Replace the last element of the list by \a input . 72 | /// \param[in] input The element used to replace the last element. 73 | void Replace( const list_type input ); 74 | 75 | /// Delete the element at position \a position. 76 | /// \param[in] position The index of the element to delete 77 | void RemoveAtIndex( const unsigned int position ); 78 | 79 | /// Delete the element at the end of the list 80 | void Del(const unsigned num=1); 81 | 82 | /// Returns the index of the specified item or MAX_UNSIGNED_LONG if not found 83 | /// \param[in] input The element to check for 84 | /// \return The index or position of @em input in the list. 85 | /// \retval MAX_UNSIGNED_LONG The object is not in the list 86 | /// \retval [Integer] The index of the element in the list 87 | unsigned int GetIndexOf( const list_type input ); 88 | 89 | /// \return The number of elements in the list 90 | unsigned int Size( void ) const; 91 | 92 | /// Clear the list 93 | void Clear( bool doNotDeallocate=false ); 94 | 95 | /// Frees overallocated members, to use the minimum memory necessary 96 | /// \attention 97 | /// This is a slow operation 98 | void Compress( void ); 99 | 100 | private: 101 | /// An array of user values 102 | list_type* listArray; 103 | 104 | /// Number of elements in the list 105 | unsigned int list_size; 106 | 107 | /// Size of \a array 108 | unsigned int allocation_size; 109 | }; 110 | template 111 | List::List() 112 | { 113 | allocation_size = 0; 114 | listArray = 0; 115 | list_size = 0; 116 | } 117 | 118 | template 119 | List::~List() 120 | { 121 | if (allocation_size>0) 122 | delete [] listArray; 123 | } 124 | 125 | 126 | template 127 | List::List( const List& original_copy ) 128 | { 129 | // Allocate memory for copy 130 | 131 | if ( original_copy.list_size == 0 ) 132 | { 133 | list_size = 0; 134 | allocation_size = 0; 135 | } 136 | else 137 | { 138 | listArray = new list_type [ original_copy.list_size ]; 139 | 140 | //for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) 141 | // listArray[ counter ] = original_copy.listArray[ counter ]; 142 | 143 | // Don't call constructors, assignment operators, etc. 144 | memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); 145 | 146 | list_size = allocation_size = original_copy.list_size; 147 | } 148 | } 149 | 150 | template 151 | List& List::operator= ( const List& original_copy ) 152 | { 153 | if ( ( &original_copy ) != this ) 154 | { 155 | Clear(); 156 | 157 | // Allocate memory for copy 158 | 159 | if ( original_copy.list_size == 0 ) 160 | { 161 | list_size = 0; 162 | allocation_size = 0; 163 | } 164 | 165 | else 166 | { 167 | listArray = new list_type [ original_copy.list_size ]; 168 | 169 | //for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter ) 170 | // listArray[ counter ] = original_copy.listArray[ counter ]; 171 | // Don't call constructors, assignment operators, etc. 172 | memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type)); 173 | 174 | list_size = allocation_size = original_copy.list_size; 175 | } 176 | } 177 | 178 | return *this; 179 | } 180 | 181 | 182 | template 183 | inline list_type& List::operator[] ( const unsigned int position ) const 184 | { 185 | #ifdef _DEBUG 186 | assert ( position < list_size ); 187 | #endif 188 | return listArray[ position ]; 189 | } 190 | 191 | template 192 | void List::Insert( const list_type input, const unsigned int position ) 193 | { 194 | #ifdef _DEBUG 195 | assert( position <= list_size ); 196 | #endif 197 | 198 | // Reallocate list if necessary 199 | if ( list_size == allocation_size ) 200 | { 201 | // allocate twice the currently allocated memory 202 | list_type * new_array; 203 | 204 | if ( allocation_size == 0 ) 205 | allocation_size = 16; 206 | else 207 | allocation_size *= 2; 208 | 209 | new_array = new list_type [ allocation_size ]; 210 | 211 | // copy old array over 212 | //for ( unsigned int counter = 0; counter < list_size; ++counter ) 213 | // new_array[ counter ] = listArray[ counter ]; 214 | 215 | // Don't call constructors, assignment operators, etc. 216 | memcpy(new_array, listArray, list_size*sizeof(list_type)); 217 | 218 | // set old array to point to the newly allocated and twice as large array 219 | delete[] listArray; 220 | 221 | listArray = new_array; 222 | } 223 | 224 | // Move the elements in the list to make room 225 | //for ( unsigned int counter = list_size; counter != position; counter-- ) 226 | // listArray[ counter ] = listArray[ counter - 1 ]; 227 | 228 | // Don't call constructors, assignment operators, etc. 229 | memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type)); 230 | 231 | // Insert the new item at the correct spot 232 | listArray[ position ] = input; 233 | 234 | ++list_size; 235 | 236 | } 237 | 238 | 239 | template 240 | void List::Insert( const list_type input ) 241 | { 242 | // Reallocate list if necessary 243 | 244 | if ( list_size == allocation_size ) 245 | { 246 | // allocate twice the currently allocated memory 247 | list_type * new_array; 248 | 249 | if ( allocation_size == 0 ) 250 | allocation_size = 16; 251 | else 252 | allocation_size *= 2; 253 | 254 | new_array = new list_type [ allocation_size ]; 255 | 256 | // copy old array over 257 | // for ( unsigned int counter = 0; counter < list_size; ++counter ) 258 | // new_array[ counter ] = listArray[ counter ]; 259 | 260 | // Don't call constructors, assignment operators, etc. 261 | memcpy(new_array, listArray, list_size*sizeof(list_type)); 262 | 263 | // set old array to point to the newly allocated and twice as large array 264 | delete[] listArray; 265 | 266 | listArray = new_array; 267 | } 268 | 269 | // Insert the new item at the correct spot 270 | listArray[ list_size ] = input; 271 | 272 | ++list_size; 273 | } 274 | 275 | template 276 | inline void List::Replace( const list_type input, const list_type filler, const unsigned int position ) 277 | { 278 | if ( ( list_size > 0 ) && ( position < list_size ) ) 279 | { 280 | // Direct replacement 281 | listArray[ position ] = input; 282 | } 283 | else 284 | { 285 | if ( position >= allocation_size ) 286 | { 287 | // Reallocate the list to size position and fill in blanks with filler 288 | list_type * new_array; 289 | allocation_size = position + 1; 290 | 291 | new_array = new list_type [ allocation_size ]; 292 | 293 | // copy old array over 294 | 295 | //for ( unsigned int counter = 0; counter < list_size; ++counter ) 296 | // new_array[ counter ] = listArray[ counter ]; 297 | 298 | // Don't call constructors, assignment operators, etc. 299 | memcpy(new_array, listArray, list_size*sizeof(list_type)); 300 | 301 | // set old array to point to the newly allocated array 302 | delete[] listArray; 303 | 304 | listArray = new_array; 305 | } 306 | 307 | // Fill in holes with filler 308 | while ( list_size < position ) 309 | listArray[ list_size++ ] = filler; 310 | 311 | // Fill in the last element with the new item 312 | listArray[ list_size++ ] = input; 313 | 314 | #ifdef _DEBUG 315 | 316 | assert( list_size == position + 1 ); 317 | 318 | #endif 319 | 320 | } 321 | } 322 | 323 | template 324 | inline void List::Replace( const list_type input ) 325 | { 326 | if ( list_size > 0 ) 327 | listArray[ list_size - 1 ] = input; 328 | } 329 | 330 | template 331 | void List::RemoveAtIndex( const unsigned int position ) 332 | { 333 | #ifdef _DEBUG 334 | assert( position < list_size ); 335 | #endif 336 | 337 | if ( position < list_size ) 338 | { 339 | // Compress the array 340 | /* 341 | for ( unsigned int counter = position; counter < list_size - 1 ; ++counter ) 342 | listArray[ counter ] = listArray[ counter + 1 ]; 343 | */ 344 | memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type)); 345 | 346 | Del(); 347 | } 348 | } 349 | 350 | template 351 | inline void List::Del( const unsigned num ) 352 | { 353 | // Delete the last elements on the list. No compression needed 354 | #ifdef _DEBUG 355 | assert(list_size>=num); 356 | #endif 357 | list_size-=num; 358 | } 359 | 360 | template 361 | unsigned int List::GetIndexOf( const list_type input ) 362 | { 363 | for ( unsigned int i = 0; i < list_size; ++i ) 364 | if ( listArray[ i ] == input ) 365 | return i; 366 | 367 | return MAX_UNSIGNED_LONG; 368 | } 369 | 370 | template 371 | inline unsigned int List::Size( void ) const 372 | { 373 | return list_size; 374 | } 375 | 376 | template 377 | void List::Clear( bool doNotDeallocate ) 378 | { 379 | if ( allocation_size == 0 ) 380 | return; 381 | 382 | if (allocation_size>512 && doNotDeallocate==false) 383 | { 384 | delete [] listArray; 385 | allocation_size = 0; 386 | listArray = 0; 387 | } 388 | list_size = 0; 389 | } 390 | 391 | template 392 | void List::Compress( void ) 393 | { 394 | list_type * new_array; 395 | 396 | if ( allocation_size == 0 ) 397 | return ; 398 | 399 | new_array = new list_type [ allocation_size ]; 400 | 401 | // copy old array over 402 | //for ( unsigned int counter = 0; counter < list_size; ++counter ) 403 | // new_array[ counter ] = listArray[ counter ]; 404 | 405 | // Don't call constructors, assignment operators, etc. 406 | memcpy(new_array, listArray, list_size*sizeof(list_type)); 407 | 408 | // set old array to point to the newly allocated array 409 | delete[] listArray; 410 | 411 | listArray = new_array; 412 | } 413 | 414 | } // End namespace 415 | 416 | #endif 417 | -------------------------------------------------------------------------------- /src/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 "../../include/RakNet/BitStream.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #ifdef _COMPATIBILITY_1 30 | #include "Compatibility1Includes.h" 31 | #elif defined(_WIN32) 32 | #include // htonl 33 | #pragma comment(lib, "Ws2_32.lib") 34 | #else 35 | #include 36 | #endif 37 | 38 | // Was included for memset which now comes from string.h instead 39 | /* 40 | #if defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) 41 | #include 42 | #elif !defined(_COMPATIBILITY_2) 43 | #include 44 | #endif 45 | 46 | */ 47 | 48 | // MSWin uses _copysign, others use copysign... 49 | #ifndef _WIN32 50 | #define _copysign copysign 51 | #endif 52 | 53 | using namespace RakNet; 54 | 55 | #ifdef _MSC_VER 56 | #pragma warning( push ) 57 | #endif 58 | 59 | BitStream::BitStream() 60 | { 61 | numberOfBitsUsed = 0; 62 | //numberOfBitsAllocated = 32 * 8; 63 | numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; 64 | readOffset = 0; 65 | //data = ( unsigned char* ) malloc( 32 ); 66 | data = (unsigned char*)stackData; 67 | 68 | #ifdef _DEBUG 69 | // assert( data ); 70 | #endif 71 | //memset(data, 0, 32); 72 | copyData = true; 73 | } 74 | 75 | BitStream::BitStream(int initialBytesToAllocate) 76 | { 77 | numberOfBitsUsed = 0; 78 | readOffset = 0; 79 | if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE) 80 | { 81 | data = (unsigned char*)stackData; 82 | numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8; 83 | } 84 | else 85 | { 86 | data = (unsigned char*)malloc(initialBytesToAllocate); 87 | numberOfBitsAllocated = initialBytesToAllocate << 3; 88 | } 89 | #ifdef _DEBUG 90 | assert(data); 91 | #endif 92 | // memset(data, 0, initialBytesToAllocate); 93 | copyData = true; 94 | } 95 | 96 | BitStream::BitStream(unsigned char* _data, unsigned int lengthInBytes, bool _copyData) 97 | { 98 | numberOfBitsUsed = lengthInBytes << 3; 99 | readOffset = 0; 100 | copyData = _copyData; 101 | numberOfBitsAllocated = lengthInBytes << 3; 102 | 103 | if (copyData) 104 | { 105 | if (lengthInBytes > 0) 106 | { 107 | if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE) 108 | { 109 | data = (unsigned char*)stackData; 110 | numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3; 111 | } 112 | else 113 | { 114 | data = (unsigned char*)malloc(lengthInBytes); 115 | } 116 | #ifdef _DEBUG 117 | assert(data); 118 | #endif 119 | memcpy(data, _data, lengthInBytes); 120 | } 121 | else 122 | data = 0; 123 | } 124 | else 125 | data = (unsigned char*)_data; 126 | } 127 | 128 | // Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation 129 | void BitStream::SetNumberOfBitsAllocated(const unsigned int lengthInBits) 130 | { 131 | #ifdef _DEBUG 132 | assert(lengthInBits >= (unsigned int)numberOfBitsAllocated); 133 | #endif 134 | numberOfBitsAllocated = lengthInBits; 135 | } 136 | 137 | BitStream::~BitStream() 138 | { 139 | if (copyData && numberOfBitsAllocated > BITSTREAM_STACK_ALLOCATION_SIZE << 3) 140 | free(data); // Use realloc and free so we are more efficient than delete and new for resizing 141 | } 142 | 143 | void BitStream::Reset(void) 144 | { 145 | // Note: Do NOT reallocate memory because BitStream is used 146 | // in places to serialize/deserialize a buffer. Reallocation 147 | // is a dangerous operation (may result in leaks). 148 | 149 | if (numberOfBitsUsed > 0) 150 | { 151 | // memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed)); 152 | } 153 | 154 | // Don't free memory here for speed efficiency 155 | //free(data); // Use realloc and free so we are more efficient than delete and new for resizing 156 | numberOfBitsUsed = 0; 157 | 158 | //numberOfBitsAllocated=8; 159 | readOffset = 0; 160 | 161 | //data=(unsigned char*)malloc(1); 162 | // if (numberOfBitsAllocated>0) 163 | // memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated)); 164 | } 165 | 166 | // Write an array or casted stream 167 | void BitStream::Write(const char* input, const int numberOfBytes) 168 | { 169 | if (numberOfBytes == 0) 170 | return; 171 | 172 | // Optimization: 173 | if ((numberOfBitsUsed & 7) == 0) 174 | { 175 | AddBitsAndReallocate(BYTES_TO_BITS(numberOfBytes)); 176 | memcpy(data + BITS_TO_BYTES(numberOfBitsUsed), input, numberOfBytes); 177 | numberOfBitsUsed += BYTES_TO_BITS(numberOfBytes); 178 | } 179 | else 180 | { 181 | WriteBits((unsigned char*)input, numberOfBytes * 8, true); 182 | } 183 | 184 | } 185 | void BitStream::Write(BitStream *bitStream) 186 | { 187 | Write(bitStream, bitStream->GetNumberOfBitsUsed()); 188 | } 189 | void BitStream::Write(BitStream *bitStream, int numberOfBits) 190 | { 191 | AddBitsAndReallocate(numberOfBits); 192 | int numberOfBitsMod8; 193 | 194 | while (numberOfBits-- > 0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed) 195 | { 196 | numberOfBitsMod8 = numberOfBitsUsed & 7; 197 | if (numberOfBitsMod8 == 0) 198 | { 199 | // New byte 200 | if (bitStream->data[bitStream->readOffset >> 3] & (0x80 >> (bitStream->readOffset++ % 8))) 201 | { 202 | // Write 1 203 | data[numberOfBitsUsed >> 3] = 0x80; 204 | } 205 | else 206 | { 207 | // Write 0 208 | data[numberOfBitsUsed >> 3] = 0; 209 | } 210 | } 211 | else 212 | { 213 | // Existing byte 214 | if (bitStream->data[bitStream->readOffset >> 3] & (0x80 >> (bitStream->readOffset++ % 8))) 215 | data[numberOfBitsUsed >> 3] |= 0x80 >> (numberOfBitsMod8); // Set the bit to 1 216 | // else 0, do nothing 217 | } 218 | 219 | numberOfBitsUsed++; 220 | } 221 | } 222 | 223 | // Read an array or casted stream 224 | bool BitStream::Read(char* output, const int numberOfBytes) 225 | { 226 | // Optimization: 227 | if ((readOffset & 7) == 0) 228 | { 229 | if (readOffset + (numberOfBytes << 3) > numberOfBitsUsed) 230 | return false; 231 | 232 | // Write the data 233 | memcpy(output, data + (readOffset >> 3), numberOfBytes); 234 | 235 | readOffset += numberOfBytes << 3; 236 | return true; 237 | } 238 | else 239 | { 240 | return ReadBits((unsigned char*)output, numberOfBytes * 8); 241 | } 242 | } 243 | 244 | // Sets the read pointer back to the beginning of your data. 245 | void BitStream::ResetReadPointer(void) 246 | { 247 | readOffset = 0; 248 | } 249 | 250 | // Sets the write pointer back to the beginning of your data. 251 | void BitStream::ResetWritePointer(void) 252 | { 253 | numberOfBitsUsed = 0; 254 | } 255 | 256 | // Write a 0 257 | void BitStream::Write0(void) 258 | { 259 | AddBitsAndReallocate(1); 260 | 261 | // New bytes need to be zeroed 262 | if ((numberOfBitsUsed & 7) == 0) 263 | data[numberOfBitsUsed >> 3] = 0; 264 | 265 | numberOfBitsUsed++; 266 | } 267 | 268 | // Write a 1 269 | void BitStream::Write1(void) 270 | { 271 | AddBitsAndReallocate(1); 272 | 273 | int numberOfBitsMod8 = numberOfBitsUsed & 7; 274 | 275 | if (numberOfBitsMod8 == 0) 276 | data[numberOfBitsUsed >> 3] = 0x80; 277 | else 278 | data[numberOfBitsUsed >> 3] |= 0x80 >> (numberOfBitsMod8); // Set the bit to 1 279 | 280 | numberOfBitsUsed++; 281 | } 282 | 283 | #ifdef _MSC_VER 284 | #pragma warning( disable : 4800 ) // warning C4100: : unreferenced formal parameter 285 | #endif 286 | // Returns true if the next data read is a 1, false if it is a 0 287 | bool BitStream::ReadBit(void) 288 | { 289 | return (bool)(data[readOffset >> 3] & (0x80 >> (readOffset++ & 7))); 290 | } 291 | 292 | // Align the bitstream to the byte boundary and then write the specified number of bits. 293 | // This is faster than WriteBits but wastes the bits to do the alignment and requires you to call 294 | // SetReadToByteAlignment at the corresponding read position 295 | void BitStream::WriteAlignedBytes(const unsigned char* input, 296 | const int numberOfBytesToWrite) 297 | { 298 | #ifdef _DEBUG 299 | assert(numberOfBytesToWrite > 0); 300 | #endif 301 | 302 | AlignWriteToByteBoundary(); 303 | Write((const char*)input, numberOfBytesToWrite); 304 | } 305 | 306 | // Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the 307 | // sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence 308 | // unless you byte align the coalesced packets. 309 | bool BitStream::ReadAlignedBytes(unsigned char* output, const int numberOfBytesToRead) 310 | { 311 | #ifdef _DEBUG 312 | assert(numberOfBytesToRead > 0); 313 | #endif 314 | 315 | if (numberOfBytesToRead <= 0) 316 | return false; 317 | 318 | // Byte align 319 | AlignReadToByteBoundary(); 320 | 321 | if (readOffset + (numberOfBytesToRead << 3) > numberOfBitsUsed) 322 | return false; 323 | 324 | // Write the data 325 | memcpy(output, data + (readOffset >> 3), numberOfBytesToRead); 326 | 327 | readOffset += numberOfBytesToRead << 3; 328 | 329 | return true; 330 | } 331 | 332 | // Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons 333 | void BitStream::AlignWriteToByteBoundary(void) 334 | { 335 | if (numberOfBitsUsed) 336 | numberOfBitsUsed += 8 - (((numberOfBitsUsed - 1) & 7) + 1); 337 | } 338 | 339 | // Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons 340 | void BitStream::AlignReadToByteBoundary(void) 341 | { 342 | if (readOffset) 343 | readOffset += 8 - (((readOffset - 1) & 7) + 1); 344 | } 345 | 346 | // Write numberToWrite bits from the input source 347 | void BitStream::WriteBits(const unsigned char *input, int numberOfBitsToWrite, const bool rightAlignedBits) 348 | { 349 | if (numberOfBitsToWrite <= 0) 350 | return; 351 | 352 | AddBitsAndReallocate(numberOfBitsToWrite); 353 | int offset = 0; 354 | unsigned char dataByte; 355 | int numberOfBitsUsedMod8; 356 | 357 | numberOfBitsUsedMod8 = numberOfBitsUsed & 7; 358 | 359 | // Faster to put the while at the top surprisingly enough 360 | while (numberOfBitsToWrite > 0) 361 | //do 362 | { 363 | dataByte = *(input + offset); 364 | 365 | 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) 366 | dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation 367 | 368 | // Writing to a new byte each time 369 | if (numberOfBitsUsedMod8 == 0) 370 | * (data + (numberOfBitsUsed >> 3)) = dataByte; 371 | else 372 | { 373 | // Copy over the new data. 374 | *(data + (numberOfBitsUsed >> 3)) |= dataByte >> (numberOfBitsUsedMod8); // First half 375 | 376 | 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) 377 | { 378 | *(data + (numberOfBitsUsed >> 3) + 1) = (unsigned char)(dataByte << (8 - (numberOfBitsUsedMod8))); // Second half (overlaps byte boundary) 379 | } 380 | } 381 | 382 | if (numberOfBitsToWrite >= 8) 383 | numberOfBitsUsed += 8; 384 | else 385 | numberOfBitsUsed += numberOfBitsToWrite; 386 | 387 | numberOfBitsToWrite -= 8; 388 | 389 | offset++; 390 | } 391 | // } while(numberOfBitsToWrite>0); 392 | } 393 | 394 | // Set the stream to some initial data. For internal use 395 | void BitStream::SetData(unsigned char *input) 396 | { 397 | data = input; 398 | copyData = false; 399 | } 400 | 401 | // Assume the input source points to a native type, compress and write it 402 | void BitStream::WriteCompressed(const unsigned char* input, 403 | const int size, const bool unsignedData) 404 | { 405 | int currentByte = (size >> 3) - 1; // PCs 406 | 407 | unsigned char byteMatch; 408 | 409 | if (unsignedData) 410 | { 411 | byteMatch = 0; 412 | } 413 | 414 | else 415 | { 416 | byteMatch = 0xFF; 417 | } 418 | 419 | // Write upper bytes with a single 1 420 | // 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 421 | while (currentByte > 0) 422 | { 423 | if (input[currentByte] == byteMatch) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted 424 | { 425 | bool b = true; 426 | Write(b); 427 | } 428 | else 429 | { 430 | // Write the remainder of the data after writing 0 431 | bool b = false; 432 | Write(b); 433 | 434 | WriteBits(input, (currentByte + 1) << 3, true); 435 | // currentByte--; 436 | 437 | 438 | return; 439 | } 440 | 441 | currentByte--; 442 | } 443 | 444 | // 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. 445 | if ((unsignedData && ((*(input + currentByte)) & 0xF0) == 0x00) || 446 | (unsignedData == false && ((*(input + currentByte)) & 0xF0) == 0xF0)) 447 | { 448 | bool b = true; 449 | Write(b); 450 | WriteBits(input + currentByte, 4, true); 451 | } 452 | 453 | else 454 | { 455 | bool b = false; 456 | Write(b); 457 | WriteBits(input + currentByte, 8, true); 458 | } 459 | } 460 | 461 | // Read numberOfBitsToRead bits to the output source 462 | // alignBitsToRight should be set to true to convert internal bitstream data to userdata 463 | // It should be false if you used WriteBits with rightAlignedBits false 464 | bool BitStream::ReadBits(unsigned char* output, int numberOfBitsToRead, const bool alignBitsToRight) 465 | { 466 | #ifdef _DEBUG 467 | assert(numberOfBitsToRead > 0); 468 | #endif 469 | if (numberOfBitsToRead <= 0) 470 | return false; 471 | 472 | if (readOffset + numberOfBitsToRead > numberOfBitsUsed) 473 | return false; 474 | 475 | int readOffsetMod8; 476 | 477 | int offset = 0; 478 | 479 | memset(output, 0, BITS_TO_BYTES(numberOfBitsToRead)); 480 | 481 | readOffsetMod8 = readOffset & 7; 482 | 483 | // do 484 | // Faster to put the while at the top surprisingly enough 485 | while (numberOfBitsToRead > 0) 486 | { 487 | *(output + offset) |= *(data + (readOffset >> 3)) << (readOffsetMod8); // First half 488 | 489 | if (readOffsetMod8 > 0 && numberOfBitsToRead > 8 - (readOffsetMod8)) // If we have a second half, we didn't read enough bytes in the first half 490 | *(output + offset) |= *(data + (readOffset >> 3) + 1) >> (8 - (readOffsetMod8)); // Second half (overlaps byte boundary) 491 | 492 | numberOfBitsToRead -= 8; 493 | 494 | if (numberOfBitsToRead < 0) // Reading a partial byte for the last byte, shift right so the data is aligned on the right 495 | { 496 | 497 | if (alignBitsToRight) 498 | * (output + offset) >>= -numberOfBitsToRead; 499 | 500 | readOffset += 8 + numberOfBitsToRead; 501 | } 502 | else 503 | readOffset += 8; 504 | 505 | offset++; 506 | 507 | } 508 | 509 | //} while(numberOfBitsToRead>0); 510 | 511 | return true; 512 | } 513 | 514 | // Assume the input source points to a compressed native type. Decompress and read it 515 | bool BitStream::ReadCompressed(unsigned char* output, 516 | const int size, const bool unsignedData) 517 | { 518 | int currentByte = (size >> 3) - 1; 519 | 520 | 521 | unsigned char byteMatch, halfByteMatch; 522 | 523 | if (unsignedData) 524 | { 525 | byteMatch = 0; 526 | halfByteMatch = 0; 527 | } 528 | 529 | else 530 | { 531 | byteMatch = 0xFF; 532 | halfByteMatch = 0xF0; 533 | } 534 | 535 | // Upper bytes are specified with a single 1 if they match byteMatch 536 | // 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 537 | while (currentByte > 0) 538 | { 539 | // If we read a 1 then the data is byteMatch. 540 | 541 | bool b; 542 | 543 | if (Read(b) == false) 544 | return false; 545 | 546 | if (b) // Check that bit 547 | { 548 | output[currentByte] = byteMatch; 549 | currentByte--; 550 | } 551 | else 552 | { 553 | // Read the rest of the bytes 554 | 555 | if (ReadBits(output, (currentByte + 1) << 3) == false) 556 | return false; 557 | 558 | return true; 559 | } 560 | } 561 | 562 | // 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. 563 | // Otherwise we read a 0 and the 8 bytes 564 | //assert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from 565 | if (readOffset + 1 > numberOfBitsUsed) 566 | return false; 567 | 568 | bool b; 569 | 570 | if (Read(b) == false) 571 | return false; 572 | 573 | if (b) // Check that bit 574 | { 575 | 576 | if (ReadBits(output + currentByte, 4) == false) 577 | return false; 578 | 579 | output[currentByte] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits 580 | } 581 | else 582 | { 583 | if (ReadBits(output + currentByte, 8) == false) 584 | return false; 585 | } 586 | 587 | return true; 588 | } 589 | 590 | // Reallocates (if necessary) in preparation of writing numberOfBitsToWrite 591 | void BitStream::AddBitsAndReallocate(const int numberOfBitsToWrite) 592 | { 593 | if (numberOfBitsToWrite <= 0) 594 | return; 595 | 596 | int newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed; 597 | 598 | if (numberOfBitsToWrite + numberOfBitsUsed > 0 && ((numberOfBitsAllocated - 1) >> 3) < ((newNumberOfBitsAllocated - 1) >> 3)) // If we need to allocate 1 or more new bytes 599 | { 600 | #ifdef _DEBUG 601 | // If this assert hits then we need to specify true for the third parameter in the constructor 602 | // It needs to reallocate to hold all the data and can't do it unless we allocated to begin with 603 | assert(copyData == true); 604 | #endif 605 | 606 | // Less memory efficient but saves on news and deletes 607 | newNumberOfBitsAllocated = (numberOfBitsToWrite + numberOfBitsUsed) * 2; 608 | // int newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated ); 609 | // Use realloc and free so we are more efficient than delete and new for resizing 610 | int amountToAllocate = BITS_TO_BYTES(newNumberOfBitsAllocated); 611 | if (data == (unsigned char*)stackData) 612 | { 613 | if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) 614 | { 615 | data = (unsigned char*)malloc(amountToAllocate); 616 | 617 | // need to copy the stack data over to our new memory area too 618 | memcpy((void *)data, (void *)stackData, BITS_TO_BYTES(numberOfBitsAllocated)); 619 | } 620 | } 621 | else 622 | { 623 | if (copyData) 624 | { 625 | data = (unsigned char*)realloc(data, amountToAllocate); 626 | } 627 | else 628 | { 629 | auto originalData = data; 630 | 631 | if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE) 632 | { 633 | data = (unsigned char*)malloc(amountToAllocate); 634 | } 635 | else 636 | { 637 | data = (unsigned char*)stackData; 638 | } 639 | 640 | memcpy(data, originalData, BITS_TO_BYTES(numberOfBitsAllocated)); 641 | 642 | copyData = true; 643 | } 644 | } 645 | 646 | #ifdef _DEBUG 647 | assert(data); // Make sure realloc succeeded 648 | #endif 649 | // memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0 650 | } 651 | 652 | if (newNumberOfBitsAllocated > numberOfBitsAllocated) 653 | numberOfBitsAllocated = newNumberOfBitsAllocated; 654 | } 655 | 656 | // Should hit if reads didn't match writes 657 | void BitStream::AssertStreamEmpty(void) 658 | { 659 | assert(readOffset == numberOfBitsUsed); 660 | } 661 | 662 | void BitStream::PrintBits(void) const 663 | { 664 | if (numberOfBitsUsed <= 0) 665 | { 666 | printf("No bits\n"); 667 | return; 668 | } 669 | 670 | for (int counter = 0; counter < BITS_TO_BYTES(numberOfBitsUsed); counter++) 671 | { 672 | int stop; 673 | 674 | if (counter == (numberOfBitsUsed - 1) >> 3) 675 | stop = 8 - (((numberOfBitsUsed - 1) & 7) + 1); 676 | else 677 | stop = 0; 678 | 679 | for (int counter2 = 7; counter2 >= stop; counter2--) 680 | { 681 | if ((data[counter] >> counter2) & 1) 682 | putchar('1'); 683 | else 684 | putchar('0'); 685 | } 686 | 687 | putchar(' '); 688 | } 689 | 690 | putchar('\n'); 691 | } 692 | 693 | 694 | // Exposes the data for you to look at, like PrintBits does. 695 | // Data will point to the stream. Returns the length in bits of the stream. 696 | int BitStream::CopyData(unsigned char** _data) const 697 | { 698 | #ifdef _DEBUG 699 | assert(numberOfBitsUsed > 0); 700 | #endif 701 | 702 | *_data = new unsigned char[BITS_TO_BYTES(numberOfBitsUsed)]; 703 | memcpy(*_data, data, sizeof(unsigned char) * (BITS_TO_BYTES(numberOfBitsUsed))); 704 | return numberOfBitsUsed; 705 | } 706 | 707 | // Ignore data we don't intend to read 708 | void BitStream::IgnoreBits(const int numberOfBits) 709 | { 710 | readOffset += numberOfBits; 711 | } 712 | 713 | // Move the write pointer to a position on the array. Dangerous if you don't know what you are doing! 714 | void BitStream::SetWriteOffset(const int offset) 715 | { 716 | numberOfBitsUsed = offset; 717 | } 718 | 719 | /* 720 | int BitStream::GetWriteOffset( void ) const 721 | { 722 | return numberOfBitsUsed; 723 | } 724 | 725 | // Returns the length in bits of the stream 726 | int BitStream::GetNumberOfBitsUsed( void ) const 727 | { 728 | return GetWriteOffset(); 729 | } 730 | 731 | // Returns the length in bytes of the stream 732 | int BitStream::GetNumberOfBytesUsed( void ) const 733 | { 734 | return BITS_TO_BYTES( numberOfBitsUsed ); 735 | } 736 | 737 | // Returns the number of bits into the stream that we have read 738 | int BitStream::GetReadOffset( void ) const 739 | { 740 | return readOffset; 741 | } 742 | 743 | 744 | // Sets the read bit index 745 | void BitStream::SetReadOffset( int newReadOffset ) 746 | { 747 | readOffset=newReadOffset; 748 | } 749 | 750 | // Returns the number of bits left in the stream that haven't been read 751 | int BitStream::GetNumberOfUnreadBits( void ) const 752 | { 753 | return numberOfBitsUsed - readOffset; 754 | } 755 | // Exposes the internal data 756 | unsigned char* BitStream::GetData( void ) const 757 | { 758 | return data; 759 | } 760 | 761 | */ 762 | // 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. 763 | void BitStream::AssertCopyData(void) 764 | { 765 | if (copyData == false) 766 | { 767 | copyData = true; 768 | 769 | if (numberOfBitsAllocated > 0) 770 | { 771 | unsigned char * newdata = (unsigned char*)malloc(BITS_TO_BYTES(numberOfBitsAllocated)); 772 | #ifdef _DEBUG 773 | 774 | assert(data); 775 | #endif 776 | 777 | memcpy(newdata, data, BITS_TO_BYTES(numberOfBitsAllocated)); 778 | data = newdata; 779 | } 780 | 781 | else 782 | data = 0; 783 | } 784 | } 785 | void BitStream::ReverseBytes(unsigned char *input, unsigned char *output, int length) 786 | { 787 | for (int i = 0; i < length; i++) 788 | output[i] = input[length - i - 1]; 789 | } 790 | bool BitStream::DoEndianSwap(void) const 791 | { 792 | #ifndef __BITSTREAM_NATIVE_END 793 | static bool swap = htonl(12345) == 12345; 794 | return swap; 795 | #else 796 | return false; 797 | #endif 798 | } 799 | 800 | #ifdef _MSC_VER 801 | #pragma warning( pop ) 802 | #endif 803 | 804 | #endif // #if _MSC_VER < 1299 -------------------------------------------------------------------------------- /include/RakNet/DS_LinkedList.h: -------------------------------------------------------------------------------- 1 | /// \file 2 | /// \brief \b [Internal] Straightforward linked list data structure. 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 __LINKED_LIST_H 19 | #define __LINKED_LIST_H 20 | 21 | #ifdef _MSC_VER 22 | #pragma warning( push ) 23 | #endif 24 | 25 | /// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures 26 | /// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. 27 | namespace DataStructures 28 | { 29 | // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class 30 | template 31 | class LinkedList; 32 | 33 | /** 34 | * \brief (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - 35 | * 36 | * By Kevin Jenkins (http://www.rakkar.org) 37 | * Initilize with the following command 38 | * LinkedList 39 | * OR 40 | * CircularLinkedList 41 | * 42 | * Has the following member functions 43 | * - size: returns number of elements in the linked list 44 | * - insert(item): inserts @em item at the current position in 45 | * the LinkedList. 46 | * - add(item): inserts @em item after the current position in 47 | * the LinkedList. Does not increment the position 48 | * - replace(item): replaces the element at the current position @em item. 49 | * - peek: returns the element at the current position 50 | * - pop: returns the element at the current position and deletes it 51 | * - del: deletes the current element. Does nothing for an empty list. 52 | * - clear: empties the LinkedList and returns storage 53 | * - bool IsInitem): Does a linear search for @em item. Does not set 54 | * the position to it, only returns true on item found, false otherwise 55 | * - bool find(item): Does a linear search for @em item and sets the current 56 | * position to point to it if and only if the item is found. Returns true 57 | * on item found, false otherwise 58 | * - sort: Sorts the elements of the list with a mergesort and sets the 59 | * current pointer to the first element 60 | * - concatenate(list L): This appends L to the current list 61 | * - ++(prefix): moves the pointer one element up in the list and returns the 62 | * appropriate copy of the element in the list 63 | * - --(prefix): moves the pointer one element back in the list and returns 64 | * the appropriate copy of the element in the list 65 | * - beginning - moves the pointer to the start of the list. For circular 66 | * linked lists this is first 'position' created. You should call this 67 | * after the sort function to read the first value. 68 | * - end - moves the pointer to the end of the list. For circular linked 69 | * lists this is one less than the first 'position' created 70 | * The assignment and copy constructor operators are defined 71 | * 72 | * \note 73 | * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList 74 | * won't let you wrap around the root and lets you jump to two positions 75 | * relative to the root/ 76 | * 2. Postfix ++ and -- can be used but simply call the prefix versions. 77 | * 78 | * 79 | * EXAMPLE: 80 | * @code 81 | * LinkedList A; // Creates a Linked List of integers called A 82 | * CircularLinkedList B; // Creates a Circular Linked List of 83 | * // integers called B 84 | * 85 | * A.Insert(20); // Adds 20 to A. A: 20 - current is 20 86 | * A.Insert(5); // Adds 5 to A. A: 5 20 - current is 5 87 | * A.Insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 88 | * 89 | * A.IsIn1); // returns true 90 | * A.IsIn200); // returns false 91 | * A.Find(5); // returns true and sets current to 5 92 | * A.Peek(); // returns 5 93 | * A.Find(1); // returns true and sets current to 1 94 | * 95 | * (++A).Peek(); // Returns 5 96 | * A.Peek(); // Returns 5 97 | * 98 | * A.Replace(10); // Replaces 5 with 10. 99 | * A.Peek(); // Returns 10 100 | * 101 | * A.Beginning(); // Current points to the beginning of the list at 1 102 | * 103 | * (++A).Peek(); // Returns 5 104 | * A.Peek(); // Returns 10 105 | * 106 | * A.Del(); // Deletes 10. Current points to the next element, which is 20 107 | * A.Peek(); // Returns 20 108 | * 109 | * A.Beginning(); // Current points to the beginning of the list at 1 110 | * 111 | * (++A).Peek(); // Returns 5 112 | * A.Peek(); // Returns 20 113 | * 114 | * A.Clear(); // Deletes all nodes in A 115 | * 116 | * A.Insert(5); // A: 5 - current is 5 117 | * A.Insert(6); // A: 6 5 - current is 6 118 | * A.Insert(7); // A: 7 6 5 - current is 7 119 | * 120 | * A.Clear(); 121 | * B.Clear(); 122 | * 123 | * B.Add(10); 124 | * B.Add(20); 125 | * B.Add(30); 126 | * B.Add(5); 127 | * B.Add(2); 128 | * B.Add(25); 129 | * // Sorts the numbers in the list and sets the current pointer to the 130 | * // first element 131 | * B.sort(); 132 | * 133 | * // Postfix ++ just calls the prefix version and has no functional 134 | * // difference. 135 | * B.Peek(); // Returns 2 136 | * B++; 137 | * B.Peek(); // Returns 5 138 | * B++; 139 | * B.Peek(); // Returns 10 140 | * B++; 141 | * B.Peek(); // Returns 20 142 | * B++; 143 | * B.Peek(); // Returns 25 144 | * B++; 145 | * B.Peek(); // Returns 30 146 | * @endcode 147 | */ 148 | template 149 | 150 | class CircularLinkedList 151 | { 152 | 153 | public: 154 | 155 | struct node 156 | { 157 | CircularLinkedListType item; 158 | 159 | node* previous; 160 | node* next; 161 | }; 162 | 163 | CircularLinkedList(); 164 | ~CircularLinkedList(); 165 | CircularLinkedList( const CircularLinkedList& original_copy ); 166 | // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type 167 | bool operator= ( const CircularLinkedList& original_copy ); 168 | CircularLinkedList& operator++(); // CircularLinkedList A; ++A; 169 | CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; 170 | CircularLinkedList& operator--(); // CircularLinkedList A; --A; 171 | CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; 172 | bool IsIn( const CircularLinkedListType& input ); 173 | bool Find( const CircularLinkedListType& input ); 174 | void Insert( const CircularLinkedListType& input ); 175 | 176 | CircularLinkedListType& Add ( const CircularLinkedListType& input ) 177 | 178 | ; // Adds after the current position 179 | void Replace( const CircularLinkedListType& input ); 180 | 181 | void Del( void ); 182 | 183 | unsigned int Size( void ); 184 | 185 | CircularLinkedListType& Peek( void ); 186 | 187 | CircularLinkedListType Pop( void ); 188 | 189 | void Clear( void ); 190 | 191 | void Sort( void ); 192 | 193 | void Beginning( void ); 194 | 195 | void End( void ); 196 | 197 | void Concatenate( const CircularLinkedList& L ); 198 | 199 | protected: 200 | unsigned int list_size; 201 | 202 | node *root; 203 | 204 | node *position; 205 | 206 | node* FindPointer( const CircularLinkedListType& input ); 207 | 208 | private: 209 | CircularLinkedList Merge( CircularLinkedList L1, CircularLinkedList L2 ); 210 | 211 | CircularLinkedList Mergesort( const CircularLinkedList& L ); 212 | }; 213 | 214 | template 215 | 216 | class LinkedList : public CircularLinkedList 217 | { 218 | 219 | public: 220 | LinkedList() 221 | {} 222 | 223 | LinkedList( const LinkedList& original_copy ); 224 | ~LinkedList(); 225 | bool operator= ( const LinkedList& original_copy ); 226 | LinkedList& operator++(); // LinkedList A; ++A; 227 | LinkedList& operator++( int ); // Linked List A; A++; 228 | LinkedList& operator--(); // LinkedList A; --A; 229 | LinkedList& operator--( int ); // Linked List A; A--; 230 | 231 | private: 232 | LinkedList Merge( LinkedList L1, LinkedList L2 ); 233 | LinkedList Mergesort( const LinkedList& L ); 234 | 235 | }; 236 | 237 | 238 | template 239 | inline void CircularLinkedList::Beginning( void ) 240 | { 241 | if ( this->root ) 242 | this->position = this->root; 243 | } 244 | 245 | template 246 | inline void CircularLinkedList::End( void ) 247 | { 248 | if ( this->root ) 249 | this->position = this->root->previous; 250 | } 251 | 252 | template 253 | bool LinkedList::operator= ( const LinkedList& original_copy ) 254 | { 255 | typename LinkedList::node * original_copy_pointer, *last, *save_position; 256 | 257 | if ( ( &original_copy ) != this ) 258 | { 259 | 260 | this->Clear(); 261 | 262 | 263 | if ( original_copy.list_size == 0 ) 264 | { 265 | this->root = 0; 266 | this->position = 0; 267 | this->list_size = 0; 268 | } 269 | 270 | else 271 | if ( original_copy.list_size == 1 ) 272 | { 273 | this->root = new typename LinkedList::node; 274 | // root->item = new LinkedListType; 275 | this->root->next = this->root; 276 | this->root->previous = this->root; 277 | this->list_size = 1; 278 | this->position = this->root; 279 | // *(root->item)=*((original_copy.root)->item); 280 | this->root->item = original_copy.root->item; 281 | } 282 | 283 | else 284 | { 285 | // Setup the first part of the root node 286 | original_copy_pointer = original_copy.root; 287 | this->root = new typename LinkedList::node; 288 | // root->item = new LinkedListType; 289 | this->position = this->root; 290 | // *(root->item)=*((original_copy.root)->item); 291 | this->root->item = original_copy.root->item; 292 | 293 | if ( original_copy_pointer == original_copy.position ) 294 | save_position = this->position; 295 | 296 | do 297 | { 298 | 299 | 300 | // Save the current element 301 | last = this->position; 302 | 303 | // Point to the next node in the source list 304 | original_copy_pointer = original_copy_pointer->next; 305 | 306 | // Create a new node and point position to it 307 | this->position = new typename LinkedList::node; 308 | // position->item = new LinkedListType; 309 | 310 | // Copy the item to the new node 311 | // *(position->item)=*(original_copy_pointer->item); 312 | this->position->item = original_copy_pointer->item; 313 | 314 | if ( original_copy_pointer == original_copy.position ) 315 | save_position = this->position; 316 | 317 | 318 | // Set the previous pointer for the new node 319 | ( this->position->previous ) = last; 320 | 321 | // Set the next pointer for the old node to the new node 322 | ( last->next ) = this->position; 323 | 324 | } 325 | 326 | while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); 327 | 328 | // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node 329 | this->position->next = this->root; 330 | 331 | this->root->previous = this->position; 332 | 333 | this->list_size = original_copy.list_size; 334 | 335 | this->position = save_position; 336 | } 337 | } 338 | 339 | return true; 340 | } 341 | 342 | 343 | template 344 | CircularLinkedList::CircularLinkedList() 345 | { 346 | this->root = 0; 347 | this->position = 0; 348 | this->list_size = 0; 349 | } 350 | 351 | template 352 | CircularLinkedList::~CircularLinkedList() 353 | { 354 | this->Clear(); 355 | } 356 | 357 | template 358 | LinkedList::~LinkedList() 359 | { 360 | this->Clear(); 361 | } 362 | 363 | template 364 | LinkedList::LinkedList( const LinkedList& original_copy ) 365 | { 366 | typename LinkedList::node * original_copy_pointer, *last, *save_position; 367 | 368 | if ( original_copy.list_size == 0 ) 369 | { 370 | this->root = 0; 371 | this->position = 0; 372 | this->list_size = 0; 373 | return ; 374 | } 375 | 376 | else 377 | if ( original_copy.list_size == 1 ) 378 | { 379 | this->root = new typename LinkedList::node; 380 | // root->item = new CircularLinkedListType; 381 | this->root->next = this->root; 382 | this->root->previous = this->root; 383 | this->list_size = 1; 384 | this->position = this->root; 385 | // *(root->item) = *((original_copy.root)->item); 386 | this->root->item = original_copy.root->item; 387 | } 388 | 389 | else 390 | { 391 | // Setup the first part of the root node 392 | original_copy_pointer = original_copy.root; 393 | this->root = new typename LinkedList::node; 394 | // root->item = new CircularLinkedListType; 395 | this->position = this->root; 396 | // *(root->item)=*((original_copy.root)->item); 397 | this->root->item = original_copy.root->item; 398 | 399 | if ( original_copy_pointer == original_copy.position ) 400 | save_position = this->position; 401 | 402 | do 403 | { 404 | // Save the current element 405 | last = this->position; 406 | 407 | // Point to the next node in the source list 408 | original_copy_pointer = original_copy_pointer->next; 409 | 410 | // Create a new node and point position to it 411 | this->position = new typename LinkedList::node; 412 | // position->item = new CircularLinkedListType; 413 | 414 | // Copy the item to the new node 415 | // *(position->item)=*(original_copy_pointer->item); 416 | this->position->item = original_copy_pointer->item; 417 | 418 | if ( original_copy_pointer == original_copy.position ) 419 | save_position = this->position; 420 | 421 | // Set the previous pointer for the new node 422 | ( this->position->previous ) = last; 423 | 424 | // Set the next pointer for the old node to the new node 425 | ( last->next ) = this->position; 426 | 427 | } 428 | 429 | while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); 430 | 431 | // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node 432 | this->position->next = this->root; 433 | 434 | this->root->previous = this->position; 435 | 436 | this->list_size = original_copy.list_size; 437 | 438 | this->position = save_position; 439 | } 440 | } 441 | 442 | #ifdef _MSC_VER 443 | #pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized 444 | #endif 445 | template 446 | CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) 447 | { 448 | node * original_copy_pointer; 449 | node *last; 450 | node *save_position; 451 | 452 | if ( original_copy.list_size == 0 ) 453 | { 454 | this->root = 0; 455 | this->position = 0; 456 | this->list_size = 0; 457 | return ; 458 | } 459 | 460 | else 461 | if ( original_copy.list_size == 1 ) 462 | { 463 | this->root = new typename CircularLinkedList::node; 464 | // root->item = new CircularLinkedListType; 465 | this->root->next = this->root; 466 | this->root->previous = this->root; 467 | this->list_size = 1; 468 | this->position = this->root; 469 | // *(root->item) = *((original_copy.root)->item); 470 | this->root->item = original_copy.root->item; 471 | } 472 | 473 | else 474 | { 475 | // Setup the first part of the root node 476 | original_copy_pointer = original_copy.root; 477 | this->root = new typename CircularLinkedList::node; 478 | // root->item = new CircularLinkedListType; 479 | this->position = this->root; 480 | // *(root->item)=*((original_copy.root)->item); 481 | this->root->item = original_copy.root->item; 482 | 483 | if ( original_copy_pointer == original_copy.position ) 484 | save_position = this->position; 485 | 486 | do 487 | { 488 | 489 | 490 | // Save the current element 491 | last = this->position; 492 | 493 | // Point to the next node in the source list 494 | original_copy_pointer = original_copy_pointer->next; 495 | 496 | // Create a new node and point position to it 497 | this->position = new typename CircularLinkedList::node; 498 | // position->item = new CircularLinkedListType; 499 | 500 | // Copy the item to the new node 501 | // *(position->item)=*(original_copy_pointer->item); 502 | this->position->item = original_copy_pointer->item; 503 | 504 | if ( original_copy_pointer == original_copy.position ) 505 | save_position = position; 506 | 507 | // Set the previous pointer for the new node 508 | ( this->position->previous ) = last; 509 | 510 | // Set the next pointer for the old node to the new node 511 | ( last->next ) = this->position; 512 | 513 | } 514 | 515 | while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); 516 | 517 | // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node 518 | this->position->next = this->root; 519 | 520 | this->root->previous = position; 521 | 522 | this->list_size = original_copy.list_size; 523 | 524 | this->position = save_position; 525 | } 526 | } 527 | 528 | #ifdef _MSC_VER 529 | #pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized 530 | #endif 531 | template 532 | bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) 533 | { 534 | node * original_copy_pointer; 535 | node *last; 536 | node *save_position; 537 | 538 | if ( ( &original_copy ) != this ) 539 | { 540 | 541 | this->Clear(); 542 | 543 | 544 | if ( original_copy.list_size == 0 ) 545 | { 546 | this->root = 0; 547 | this->position = 0; 548 | this->list_size = 0; 549 | } 550 | 551 | else 552 | if ( original_copy.list_size == 1 ) 553 | { 554 | this->root = new typename CircularLinkedList::node; 555 | // root->item = new CircularLinkedListType; 556 | this->root->next = this->root; 557 | this->root->previous = this->root; 558 | this->list_size = 1; 559 | this->position = this->root; 560 | // *(root->item)=*((original_copy.root)->item); 561 | this->root->item = original_copy.root->item; 562 | } 563 | 564 | else 565 | { 566 | // Setup the first part of the root node 567 | original_copy_pointer = original_copy.root; 568 | this->root = new typename CircularLinkedList::node; 569 | // root->item = new CircularLinkedListType; 570 | this->position = this->root; 571 | // *(root->item)=*((original_copy.root)->item); 572 | this->root->item = original_copy.root->item; 573 | 574 | if ( original_copy_pointer == original_copy.position ) 575 | save_position = this->position; 576 | 577 | do 578 | { 579 | // Save the current element 580 | last = this->position; 581 | 582 | // Point to the next node in the source list 583 | original_copy_pointer = original_copy_pointer->next; 584 | 585 | // Create a new node and point position to it 586 | this->position = new typename CircularLinkedList::node; 587 | // position->item = new CircularLinkedListType; 588 | 589 | // Copy the item to the new node 590 | // *(position->item)=*(original_copy_pointer->item); 591 | this->position->item = original_copy_pointer->item; 592 | 593 | if ( original_copy_pointer == original_copy.position ) 594 | save_position = this->position; 595 | 596 | // Set the previous pointer for the new node 597 | ( this->position->previous ) = last; 598 | 599 | // Set the next pointer for the old node to the new node 600 | ( last->next ) = this->position; 601 | 602 | } 603 | 604 | while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); 605 | 606 | // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node 607 | this->position->next = this->root; 608 | 609 | this->root->previous = this->position; 610 | 611 | this->list_size = original_copy.list_size; 612 | 613 | this->position = save_position; 614 | } 615 | } 616 | 617 | return true; 618 | } 619 | 620 | template 621 | void CircularLinkedList::Insert( const CircularLinkedListType& input ) 622 | { 623 | node * new_node; 624 | 625 | if ( list_size == 0 ) 626 | { 627 | this->root = new typename CircularLinkedList::node; 628 | // root->item = new CircularLinkedListType; 629 | //*(root->item)=input; 630 | this->root->item = input; 631 | this->root->next = this->root; 632 | this->root->previous = this->root; 633 | this->list_size = 1; 634 | this->position = this->root; 635 | } 636 | 637 | else 638 | if ( list_size == 1 ) 639 | { 640 | this->position = new typename CircularLinkedList::node; 641 | // position->item = new CircularLinkedListType; 642 | this->root->next = this->position; 643 | this->root->previous = this->position; 644 | this->position->previous = this->root; 645 | this->position->next = this->root; 646 | // *(position->item)=input; 647 | this->position->item = input; 648 | this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item 649 | this->list_size = 2; 650 | } 651 | 652 | else 653 | { 654 | /* 655 | 656 | B 657 | | 658 | A --- C 659 | 660 | position->previous=A 661 | new_node=B 662 | position=C 663 | 664 | Note that the order of the following statements is important */ 665 | 666 | new_node = new typename CircularLinkedList::node; 667 | // new_node->item = new CircularLinkedListType; 668 | 669 | // *(new_node->item)=input; 670 | new_node->item = input; 671 | 672 | // Point next of A to B 673 | ( this->position->previous ) ->next = new_node; 674 | 675 | // Point last of B to A 676 | new_node->previous = this->position->previous; 677 | 678 | // Point last of C to B 679 | this->position->previous = new_node; 680 | 681 | // Point next of B to C 682 | new_node->next = this->position; 683 | 684 | // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root 685 | 686 | if ( this->position == this->root ) 687 | { 688 | this->root = new_node; 689 | this->position = this->root; 690 | } 691 | 692 | // Increase the recorded size of the list by one 693 | this->list_size++; 694 | } 695 | } 696 | 697 | template 698 | CircularLinkedListType& CircularLinkedList::Add ( const CircularLinkedListType& input ) 699 | { 700 | node * new_node; 701 | 702 | if ( this->list_size == 0 ) 703 | { 704 | this->root = new typename CircularLinkedList::node; 705 | // root->item = new CircularLinkedListType; 706 | // *(root->item)=input; 707 | this->root->item = input; 708 | this->root->next = this->root; 709 | this->root->previous = this->root; 710 | this->list_size = 1; 711 | this->position = this->root; 712 | // return *(position->item); 713 | return this->position->item; 714 | } 715 | 716 | else 717 | if ( list_size == 1 ) 718 | { 719 | this->position = new typename CircularLinkedList::node; 720 | // position->item = new CircularLinkedListType; 721 | this->root->next = this->position; 722 | this->root->previous = this->position; 723 | this->position->previous = this->root; 724 | this->position->next = this->root; 725 | // *(position->item)=input; 726 | this->position->item = input; 727 | this->list_size = 2; 728 | this->position = this->root; // Don't move the position from the root 729 | // return *(position->item); 730 | return this->position->item; 731 | } 732 | 733 | else 734 | { 735 | /* 736 | 737 | B 738 | | 739 | A --- C 740 | 741 | new_node=B 742 | position=A 743 | position->next=C 744 | 745 | Note that the order of the following statements is important */ 746 | 747 | new_node = new typename CircularLinkedList::node; 748 | // new_node->item = new CircularLinkedListType; 749 | 750 | // *(new_node->item)=input; 751 | new_node->item = input; 752 | 753 | // Point last of B to A 754 | new_node->previous = this->position; 755 | 756 | // Point next of B to C 757 | new_node->next = ( this->position->next ); 758 | 759 | // Point last of C to B 760 | ( this->position->next ) ->previous = new_node; 761 | 762 | // Point next of A to B 763 | ( this->position->next ) = new_node; 764 | 765 | // Increase the recorded size of the list by one 766 | this->list_size++; 767 | 768 | // return *(new_node->item); 769 | return new_node->item; 770 | } 771 | } 772 | 773 | template 774 | inline void CircularLinkedList::Replace( const CircularLinkedListType& input ) 775 | { 776 | if ( this->list_size > 0 ) 777 | // *(position->item)=input; 778 | this->position->item = input; 779 | } 780 | 781 | template 782 | void CircularLinkedList::Del() 783 | { 784 | node * new_position; 785 | 786 | if ( this->list_size == 0 ) 787 | return ; 788 | 789 | else 790 | if ( this->list_size == 1 ) 791 | { 792 | // delete root->item; 793 | delete this->root; 794 | this->root = this->position = 0; 795 | this->list_size = 0; 796 | } 797 | 798 | else 799 | { 800 | ( this->position->previous ) ->next = this->position->next; 801 | ( this->position->next ) ->previous = this->position->previous; 802 | new_position = this->position->next; 803 | 804 | if ( this->position == this->root ) 805 | this->root = new_position; 806 | 807 | // delete position->item; 808 | delete this->position; 809 | 810 | this->position = new_position; 811 | 812 | this->list_size--; 813 | } 814 | } 815 | 816 | template 817 | bool CircularLinkedList::IsIn(const CircularLinkedListType& input ) 818 | { 819 | node * return_value, *old_position; 820 | 821 | old_position = this->position; 822 | 823 | return_value = FindPointer( input ); 824 | this->position = old_position; 825 | 826 | if ( return_value != 0 ) 827 | return true; 828 | else 829 | return false; // Can't find the item don't do anything 830 | } 831 | 832 | template 833 | bool CircularLinkedList::Find( const CircularLinkedListType& input ) 834 | { 835 | node * return_value; 836 | 837 | return_value = FindPointer( input ); 838 | 839 | if ( return_value != 0 ) 840 | { 841 | this->position = return_value; 842 | return true; 843 | } 844 | 845 | else 846 | return false; // Can't find the item don't do anything 847 | } 848 | 849 | template 850 | typename CircularLinkedList::node* CircularLinkedList::FindPointer( const CircularLinkedListType& input ) 851 | { 852 | node * current; 853 | 854 | if ( this->list_size == 0 ) 855 | return 0; 856 | 857 | current = this->root; 858 | 859 | // Search for the item starting from the root node and incrementing the pointer after every check 860 | // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 861 | do 862 | { 863 | // if (*(current->item) == input) return current; 864 | 865 | if ( current->item == input ) 866 | return current; 867 | 868 | current = current->next; 869 | } 870 | 871 | while ( current != this->root ); 872 | 873 | return 0; 874 | 875 | } 876 | 877 | template 878 | inline unsigned int CircularLinkedList::Size( void ) 879 | { 880 | return this->list_size; 881 | } 882 | 883 | template 884 | inline CircularLinkedListType& CircularLinkedList::Peek( void ) 885 | { 886 | // return *(position->item); 887 | return this->position->item; 888 | } 889 | 890 | template 891 | CircularLinkedListType CircularLinkedList::Pop( void ) 892 | { 893 | CircularLinkedListType element; 894 | element = Peek(); 895 | Del(); 896 | return CircularLinkedListType( element ); // return temporary 897 | } 898 | 899 | // Prefix 900 | template 901 | CircularLinkedList& CircularLinkedList::operator++() 902 | { 903 | if ( this->list_size != 0 ) 904 | position = position->next; 905 | 906 | return *this; 907 | } 908 | 909 | /* 910 | // Postfix 911 | template 912 | CircularLinkedList& CircularLinkedList::operator++(int) 913 | { 914 | CircularLinkedList before; 915 | before=*this; 916 | operator++(); 917 | return before; 918 | } 919 | */ 920 | 921 | template 922 | CircularLinkedList& CircularLinkedList::operator++( int ) 923 | { 924 | return this->operator++(); 925 | } 926 | 927 | // Prefix 928 | template 929 | CircularLinkedList& CircularLinkedList::operator--() 930 | { 931 | if ( this->list_size != 0 ) 932 | this->position = this->position->previous; 933 | 934 | return *this; 935 | } 936 | 937 | /* 938 | // Postfix 939 | template 940 | CircularLinkedList& CircularLinkedList::operator--(int) 941 | { 942 | CircularLinkedList before; 943 | before=*this; 944 | operator--(); 945 | return before; 946 | } 947 | */ 948 | 949 | template 950 | CircularLinkedList& CircularLinkedList::operator--( int ) 951 | { 952 | return this->operator--(); 953 | } 954 | 955 | template 956 | void CircularLinkedList::Clear( void ) 957 | { 958 | if ( this->list_size == 0 ) 959 | return ; 960 | else 961 | if ( this->list_size == 1 ) // {delete root->item; delete root;} 962 | { 963 | delete this->root; 964 | } 965 | 966 | else 967 | { 968 | node* current; 969 | node* temp; 970 | 971 | current = this->root; 972 | 973 | do 974 | { 975 | temp = current; 976 | current = current->next; 977 | // delete temp->item; 978 | delete temp; 979 | } 980 | 981 | while ( current != this->root ); 982 | } 983 | 984 | this->list_size = 0; 985 | this->root = 0; 986 | this->position = 0; 987 | } 988 | 989 | template 990 | inline void CircularLinkedList::Concatenate( const CircularLinkedList& L ) 991 | { 992 | unsigned int counter; 993 | node* ptr; 994 | 995 | if ( L.list_size == 0 ) 996 | return ; 997 | 998 | if ( this->list_size == 0 ) 999 | * this = L; 1000 | 1001 | ptr = L.root; 1002 | 1003 | this->position = this->root->previous; 1004 | 1005 | // Cycle through each element in L and add it to the current list 1006 | for ( counter = 0; counter < L.list_size; counter++ ) 1007 | { 1008 | // Add item after the current item pointed to 1009 | // add(*(ptr->item)); 1010 | 1011 | Add ( ptr->item ); 1012 | 1013 | // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer 1014 | ptr = ptr->next; 1015 | 1016 | this->position = this->position->next; 1017 | } 1018 | } 1019 | 1020 | template 1021 | inline void CircularLinkedList::Sort( void ) 1022 | { 1023 | if ( this->list_size <= 1 ) 1024 | return ; 1025 | 1026 | // Call equal operator to assign result of mergesort to current object 1027 | *this = Mergesort( *this ); 1028 | 1029 | this->position = this->root; 1030 | } 1031 | 1032 | template 1033 | CircularLinkedList CircularLinkedList::Mergesort( const CircularLinkedList& L ) 1034 | { 1035 | unsigned int counter; 1036 | node* location; 1037 | CircularLinkedList L1; 1038 | CircularLinkedList L2; 1039 | 1040 | location = L.root; 1041 | 1042 | // Split the list into two equal size sublists, L1 and L2 1043 | 1044 | for ( counter = 0; counter < L.list_size / 2; counter++ ) 1045 | { 1046 | // L1.add (*(location->item)); 1047 | L1.Add ( location->item ); 1048 | location = location->next; 1049 | } 1050 | 1051 | for ( ;counter < L.list_size; counter++ ) 1052 | { 1053 | // L2.Add(*(location->item)); 1054 | L2.Add ( location->item ); 1055 | location = location->next; 1056 | } 1057 | 1058 | // Recursively sort the sublists 1059 | if ( L1.list_size > 1 ) 1060 | L1 = Mergesort( L1 ); 1061 | 1062 | if ( L2.list_size > 1 ) 1063 | L2 = Mergesort( L2 ); 1064 | 1065 | // Merge the two sublists 1066 | return Merge( L1, L2 ); 1067 | } 1068 | 1069 | template 1070 | CircularLinkedList CircularLinkedList::Merge( CircularLinkedList L1, CircularLinkedList L2 ) 1071 | { 1072 | CircularLinkedList X; 1073 | CircularLinkedListType element; 1074 | L1.position = L1.root; 1075 | L2.position = L2.root; 1076 | 1077 | // While neither list is empty 1078 | 1079 | while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) 1080 | { 1081 | // Compare the first items of L1 and L2 1082 | // Remove the smaller of the two items from the list 1083 | 1084 | if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) 1085 | // if ((*((L1.root)->item)) < (*((L2.root)->item))) 1086 | { 1087 | // element = *((L1.root)->item); 1088 | element = ( L1.root ) ->item; 1089 | L1.Del(); 1090 | } 1091 | else 1092 | { 1093 | // element = *((L2.root)->item); 1094 | element = ( L2.root ) ->item; 1095 | L2.Del(); 1096 | } 1097 | 1098 | // Add this item to the end of X 1099 | X.Add( element ); 1100 | 1101 | X++; 1102 | } 1103 | 1104 | // Add the remaining list to X 1105 | if ( L1.list_size != 0 ) 1106 | X.Concatenate( L1 ); 1107 | else 1108 | X.Concatenate( L2 ); 1109 | 1110 | return X; 1111 | } 1112 | 1113 | template 1114 | LinkedList LinkedList::Mergesort( const LinkedList& L ) 1115 | { 1116 | unsigned int counter; 1117 | typename LinkedList::node* location; 1118 | LinkedList L1; 1119 | LinkedList L2; 1120 | 1121 | location = L.root; 1122 | 1123 | // Split the list into two equal size sublists, L1 and L2 1124 | 1125 | for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) 1126 | { 1127 | // L1.add (*(location->item)); 1128 | L1.Add ( location->item ); 1129 | location = location->next; 1130 | } 1131 | 1132 | for ( ;counter < L.LinkedList_size; counter++ ) 1133 | { 1134 | // L2.Add(*(location->item)); 1135 | L2.Add ( location->item ); 1136 | location = location->next; 1137 | } 1138 | 1139 | // Recursively sort the sublists 1140 | if ( L1.list_size > 1 ) 1141 | L1 = Mergesort( L1 ); 1142 | 1143 | if ( L2.list_size > 1 ) 1144 | L2 = Mergesort( L2 ); 1145 | 1146 | // Merge the two sublists 1147 | return Merge( L1, L2 ); 1148 | } 1149 | 1150 | template 1151 | LinkedList LinkedList::Merge( LinkedList L1, LinkedList L2 ) 1152 | { 1153 | LinkedList X; 1154 | LinkedListType element; 1155 | L1.position = L1.root; 1156 | L2.position = L2.root; 1157 | 1158 | // While neither list is empty 1159 | 1160 | while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) 1161 | { 1162 | // Compare the first items of L1 and L2 1163 | // Remove the smaller of the two items from the list 1164 | 1165 | if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) 1166 | // if ((*((L1.root)->item)) < (*((L2.root)->item))) 1167 | { 1168 | element = ( L1.root ) ->item; 1169 | // element = *((L1.root)->item); 1170 | L1.Del(); 1171 | } 1172 | else 1173 | { 1174 | element = ( L2.root ) ->item; 1175 | // element = *((L2.root)->item); 1176 | L2.Del(); 1177 | } 1178 | 1179 | // Add this item to the end of X 1180 | X.Add( element ); 1181 | } 1182 | 1183 | // Add the remaining list to X 1184 | if ( L1.LinkedList_size != 0 ) 1185 | X.concatenate( L1 ); 1186 | else 1187 | X.concatenate( L2 ); 1188 | 1189 | return X; 1190 | } 1191 | 1192 | 1193 | // Prefix 1194 | template 1195 | LinkedList& LinkedList::operator++() 1196 | { 1197 | if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) 1198 | this->position = this->position->next; 1199 | 1200 | return *this; 1201 | } 1202 | 1203 | /* 1204 | // Postfix 1205 | template 1206 | LinkedList& LinkedList::operator++(int) 1207 | { 1208 | LinkedList before; 1209 | before=*this; 1210 | operator++(); 1211 | return before; 1212 | } 1213 | */ 1214 | // Postfix 1215 | template 1216 | LinkedList& LinkedList::operator++( int ) 1217 | { 1218 | return this->operator++(); 1219 | } 1220 | 1221 | // Prefix 1222 | template 1223 | LinkedList& LinkedList::operator--() 1224 | { 1225 | if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) 1226 | this->position = this->position->previous; 1227 | 1228 | return *this; 1229 | } 1230 | 1231 | /* 1232 | // Postfix 1233 | template 1234 | LinkedList& LinkedList::operator--(int) 1235 | { 1236 | LinkedList before; 1237 | before=*this; 1238 | operator--(); 1239 | return before; 1240 | } 1241 | */ 1242 | 1243 | // Postfix 1244 | template 1245 | LinkedList& LinkedList::operator--( int ) 1246 | { 1247 | return this->operator--(); 1248 | } 1249 | 1250 | } // End namespace 1251 | 1252 | #ifdef _MSC_VER 1253 | #pragma warning( pop ) 1254 | #endif 1255 | 1256 | #endif 1257 | --------------------------------------------------------------------------------