diff --git a/README.md b/README.md index c62b51f..bea76d2 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Build dependencies: - bash - a C++ 20 compiler +- GLFW with headers Setup dependencies: diff --git a/resources/README.md b/resources/README.md index ee5d485..15442a0 100644 --- a/resources/README.md +++ b/resources/README.md @@ -27,6 +27,14 @@ Data used to test the NBT library `level.dat_decompressed`: The same data decompressed +## network_capture/ + +Network captures used to get an understanding of the protocol + +`ping.pcapng`: WireShark capture of the multipayer screen server ping +`ping_decoded.txt`: Extracted TCP payloads from the network capture annotated with what’s happening + + ## unicode_data/ Files with unicode data diff --git a/resources/network_capture/ping.pcapng b/resources/network_capture/ping.pcapng new file mode 100644 index 0000000..fea0759 Binary files /dev/null and b/resources/network_capture/ping.pcapng differ diff --git a/resources/network_capture/ping_decoded.txt b/resources/network_capture/ping_decoded.txt new file mode 100644 index 0000000..6973220 --- /dev/null +++ b/resources/network_capture/ping_decoded.txt @@ -0,0 +1,1434 @@ +Connection established by client + +C→S Handshake +-- +13 Length: 19 +00 Packet type ID: 0 +f6 Protocol version: 758 (1.18.2) +05 +0c Host string: (length 12) minecraft.ip +6d m +69 i +6e n +65 e +63 c +72 r +61 a +66 f +74 t +2e . +69 i +70 p +63 Port: 25565 +dd +01 Next state: status + + +C→S Status request +-- +01 Length: 1 +00 Packet type ID: 0 + + +S→C Status response +-- +d9 Length: 1369 +0a +00 Packet type ID: 0 +d6 JSON payload: (length 1366) +0a +7b { +22 " +64 d +65 e +73 s +63 c +72 r +69 i +70 p +74 t +69 +6f +6e +22 +3a +7b +22 +74 +65 +78 +74 +22 +3a +22 +c2 +a7 +61 +53 +75 +72 +76 +69 +76 +61 +6c +20 +6d +6f +64 +65 +20 +73 +65 +72 +76 +65 +72 +2e +c2 +a7 +37 +20 +47 +61 +6d +65 +20 +6d +61 +79 +20 +72 +75 +6e +20 +73 +6c +6f +77 +6c +79 +20 +69 +66 +5c +6e +65 +78 +65 +63 +75 +74 +65 +64 +20 +6f +66 +66 +20 +61 +20 +66 +6c +6f +70 +70 +79 +20 +64 +69 +73 +6b +2e +20 +c2 +a7 +34 +c2 +a7 +6c +4d +6f +64 +65 +6d +20 +72 +65 +71 +75 +69 +72 +65 +64 +2e +22 +7d +2c +22 +70 +6c +61 +79 +65 +72 +73 +22 +3a +7b +22 +6d +61 +78 +22 +3a +31 +30 +2c +22 +6f +6e +6c +69 +6e +65 +22 +3a +30 +7d +2c +22 +76 +65 +72 +73 +69 +6f +6e +22 +3a +7b +22 +6e +61 +6d +65 +22 +3a +22 +31 +2e +31 +38 +2e +32 +22 +2c +22 +70 +72 +6f +74 +6f +63 +6f +6c +22 +3a +37 +35 +38 +7d +2c +22 +66 +61 +76 +69 +63 +6f +6e +22 +3a +22 +64 +61 +74 +61 +3a +69 +6d +61 +67 +65 +2f +70 +6e +67 +3b +62 +61 +73 +65 +36 +34 +2c +69 +56 +42 +4f +52 +77 +30 +4b +47 +67 +6f +41 +41 +41 +41 +4e +53 +55 +68 +45 +55 +67 +41 +41 +41 +45 +41 +41 +41 +41 +42 +41 +43 +41 +59 +41 +41 +41 +43 +71 +61 +58 +48 +65 +41 +41 +41 +44 +46 +55 +6c +45 +51 +56 +52 +34 +58 +74 +32 +62 +50 +57 +34 +55 +51 +52 +43 +46 +78 +39 +71 +55 +41 +2b +77 +42 +6b +4a +42 +38 +45 +6c +49 +6b +62 +6d +41 +4a +6e +34 +47 +59 +68 +4e +51 +33 +38 +41 +6d +63 +45 +42 +4f +52 +63 +41 +38 +4f +51 +64 +6a +51 +62 +66 +65 +71 +39 +70 +76 +71 +36 +75 +6e +64 +2f +70 +76 +39 +70 +43 +65 +4c +2f +71 +76 +33 +61 +6d +61 +6b +48 +53 +39 +65 +6c +73 +59 +34 +35 +77 +37 +75 +63 +67 +34 +38 +62 +78 +63 +77 +52 +53 +31 +59 +5a +79 +72 +2b +2b +37 +75 +6a +34 +59 +62 +63 +73 +66 +34 +77 +76 +42 +6d +36 +36 +38 +6a +59 +52 +74 +44 +4e +4b +4f +69 +72 +4f +54 +51 +77 +43 +2f +54 +5a +42 +42 +61 +64 +44 +66 +71 +74 +43 +6f +76 +4e +43 +6e +31 +58 +67 +55 +56 +6d +68 +2f +36 +76 +67 +6f +66 +76 +42 +65 +61 +34 +43 +42 +36 +36 +4e +35 +69 +6e +43 +42 +36 +32 +56 +35 +68 +72 +45 +7a +77 +6b +34 +71 +64 +61 +71 +53 +56 +4c +43 +64 +77 +73 +43 +64 +4e +2f +66 +31 +52 +58 +70 +6d +77 +56 +6d +44 +4d +4a +4e +30 +62 +43 +6c +47 +4b +2b +6c +6f +7a +53 +56 +57 +42 +4f +46 +57 +36 +53 +68 +47 +6c +68 +74 +70 +5a +36 +4e +63 +43 +7a +57 +4c +6a +4d +69 +30 +31 +59 +38 +6d +62 +30 +38 +32 +39 +58 +54 +66 +4c +63 +44 +71 +52 +66 +6f +4c +69 +53 +68 +43 +56 +4b +41 +79 +49 +4d +74 +6c +58 +79 +33 +42 +36 +63 +70 +33 +37 +44 +58 +58 +6a +31 +4e +52 +6a +77 +62 +50 +32 +66 +39 +30 +45 +63 +6c +2b +64 +33 +59 +48 +30 +58 +63 +41 +55 +4a +53 +35 +51 +47 +6c +47 +71 +53 +42 +71 +7a +76 +41 +69 +34 +67 +59 +55 +6c +68 +41 +79 +49 +63 +31 +79 +54 +50 +37 +38 +46 +46 +34 +61 +30 +47 +33 +4c +38 +63 +54 +35 +4c +68 +49 +31 +78 +50 +73 +55 +59 +50 +69 +68 +75 +51 +43 +71 +38 +31 +49 +43 +64 +4a +48 +4a +4e +31 +65 +6c +43 +6c +41 +54 +49 +34 +78 +62 +55 +4d +4c +68 +6e +57 +41 +4a +66 +35 +76 +58 +30 +30 +64 +57 +30 +44 +50 +4e +78 +4c +73 +56 +59 +48 +44 +74 +6b +71 +4e +43 +55 +44 +65 +52 +69 +61 +44 +53 +41 +4d +50 +62 +67 +42 +4c +6c +74 +46 +75 +2f +31 +4a +48 +48 +2f +36 +39 +47 +34 +56 +79 +6c +6f +76 +64 +66 +2f +31 +47 +4e +54 +39 +4d +65 +41 +41 +30 +52 +71 +51 +55 +71 +6f +42 +6d +6d +4c +67 +71 +4f +63 +76 +48 +34 +4b +6d +61 +67 +42 +76 +53 +5a +71 +57 +79 +6a +57 +41 +36 +32 +4e +67 +69 +6a +56 +62 +59 +31 +61 +67 +47 +5a +71 +56 +59 +6b +43 +4b +36 +31 +4e +69 +7a +64 +61 +59 +46 +65 +54 +74 +53 +4b +4d +74 +31 +66 +4d +78 +4d +43 +76 +4d +32 +41 +41 +2f +56 +69 +6f +4c +63 +7a +59 +61 +69 +57 +5a +36 +79 +58 +6f +45 +35 +50 +77 +57 +61 +57 +64 +49 +7a +4e +6e +53 +59 +72 +57 +6c +6d +59 +2b +65 +32 +44 +52 +4e +71 +54 +4d +6b +35 +75 +7a +4d +44 +65 +41 +36 +47 +5a +70 +7a +46 +75 +59 +73 +4f +7a +70 +43 +4a +49 +77 +70 +7a +61 +4c +38 +4a +39 +47 +72 +47 +35 +44 +43 +48 +2f +72 +72 +35 +2f +64 +71 +79 +70 +6d +55 +62 +47 +31 +41 +56 +4f +35 +73 +64 +5a +5a +58 +59 +51 +5a +64 +34 +38 +31 +43 +6e +51 +32 +62 +6c +47 +36 +4f +45 +6b +4d +77 +6f +4b +55 +63 +66 +73 +58 +71 +64 +54 +67 +32 +34 +50 +48 +68 +34 +79 +62 +52 +63 +4b +6b +65 +48 +37 +36 +64 +2f +54 +73 +2b +76 +79 +2f +48 +39 +58 +50 +4d +67 +46 +74 +6b +38 +50 +72 +66 +38 +44 +67 +61 +68 +68 +53 +6a +6f +79 +51 +74 +6c +6e +70 +54 +34 +70 +30 +49 +34 +61 +30 +47 +38 +45 +71 +6e +78 +4b +4b +31 +70 +54 +58 +41 +2f +38 +79 +4a +65 +30 +6b +49 +37 +31 +45 +6d +56 +69 +5a +47 +53 +6c +72 +55 +76 +44 +45 +30 +35 +31 +4b +63 +47 +75 +44 +42 +52 +4e +6a +4d +4b +35 +30 +53 +69 +39 +61 +57 +74 +4b +65 +46 +31 +4a +54 +37 +48 +48 +41 +57 +33 +6f +50 +4a +36 +53 +53 +39 +4d +61 +77 +6c +75 +56 +65 +79 +45 +4a +66 +34 +61 +73 +78 +50 +38 +59 +4e +4c +44 +37 +31 +61 +57 +75 +50 +48 +53 +36 +57 +77 +2f +6d +72 +4d +77 +31 +55 +65 +50 +7a +78 +4b +4b +62 +6a +4f +6b +73 +61 +53 +77 +69 +58 +75 +67 +68 +74 +44 +76 +2f +6f +52 +72 +72 +34 +31 +6d +46 +65 +46 +6d +32 +34 +46 +35 +6b +7a +43 +6a +62 +63 +43 +63 +35 +70 +77 +38 +39 +35 +68 +76 +6b +33 +77 +6b +4c +33 +43 +58 +45 +58 +77 +73 +4c +33 +42 +50 +42 +66 +42 +51 +2f +63 +43 +63 +31 +77 +46 +44 +35 +38 +64 +2b +71 +38 +43 +69 +38 +77 +4b +66 +56 +65 +46 +78 +57 +61 +44 +66 +70 +76 +41 +6f +72 +4e +41 +6e +38 +32 +68 +67 +56 +48 +51 +56 +31 +66 +63 +32 +42 +63 +6f +2b +38 +57 +6d +4a +39 +34 +4d +33 +54 +56 +6b +6e +75 +41 +61 +64 +46 +73 +4c +31 +74 +6b +46 +54 +76 +6e +65 +6f +59 +44 +6d +66 +7a +37 +2f +44 +32 +57 +46 +75 +32 +6e +76 +39 +37 +4b +38 +41 +41 +41 +41 +41 +45 +6c +46 +54 +6b +53 +75 +51 +6d +43 +43 +22 +7d } + + +C→S Ping +-- +09 Length: 9 +01 Packet type ID: 1 +00 64-bit integer payload: 56701 +00 +00 +00 +00 +00 +dd +7d + +S→C Pong +-- +09 Length: 9 +01 Packet type ID: 1 +00 64-bit integer payload: 56701 +00 +00 +00 +00 +00 +dd +7d + +Disconnect diff --git a/scripts/build.sh b/scripts/build.sh index a5d13f8..c0fff03 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -63,7 +63,7 @@ COMPILE_COMMANDS=( "$CXX_WITH_FLAGS src/tools/arraydump.cpp -I./include -Lbin/lib -l:file.so -l:cli.so -o bin/tools/arraydump" "$CXX_WITH_FLAGS src/tools/baseconvert.cpp -I./include -Lbin/lib -l:cli.so -o bin/tools/baseconvert" "$CXX_WITH_FLAGS -pthread src/tools/hexnet.cpp -I./include -Lbin/lib -l:cli.so -l:libsockpp.so -o bin/tools/hexnet" - "$CXX_WITH_FLAGS src/fossvg.cpp -I./include -Lbin/lib -l:cli.so -o bin/fossvg" + "$CXX_WITH_FLAGS src/fossvg.cpp -I./include -Lbin/lib -l:cli.so -lglfw -o bin/fossvg" "$CXX_WITH_FLAGS src/fossvgd.cpp -I./include -Lbin/lib -l:cli.so -o bin/fossvgd" ) for command in ${!COMPILE_COMMANDS[@]}; do diff --git a/scripts/clean.sh b/scripts/clean.sh index 431c29e..7622929 100755 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -25,6 +25,7 @@ remove .endianness remove resources/check_endianness create_directory ./bin create_directory ./bin/lib +create_directory ./bin/lib/net create_directory ./include if uname -s | tr [:upper:] [:lower:] | grep cygwin >/dev/null; then diff --git a/scripts/test.sh b/scripts/test.sh index 1b07a0b..5761e8f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -39,6 +39,7 @@ COMPILE_COMMANDS=( "$CXX_WITH_FLAGS src/test/nbt_tags.cpp -I./include -Lbin/lib -l:nbt.so -o bin/test/nbt_tags" "$CXX_WITH_FLAGS src/test/nbt_size_helpers.cpp -I./include -Lbin/lib -l:nbt.so -o bin/test/nbt_size_helpers" "$CXX_WITH_FLAGS src/test/file.cpp -I./include -Lbin/lib -l:file.so -o bin/test/file" + "$CXX_WITH_FLAGS src/test/varint.cpp -I./include -Lbin/lib -o bin/test/varint" ) for command in ${!COMPILE_COMMANDS[@]}; do echo "${COMPILE_COMMANDS[command]}" diff --git a/src/fossvg.cpp b/src/fossvg.cpp index c2df751..8380b8c 100644 --- a/src/fossvg.cpp +++ b/src/fossvg.cpp @@ -18,6 +18,10 @@ #include #include +#include +#include + +#include #include "./lib/cli.hpp" @@ -25,6 +29,27 @@ #define EXIT_RUNTIME 1 #define EXIT_USAGE 2 +//TODO: check the TODO above glfwInit() in void main() +// #### Callbacks ############################################################## +void cursorPositionCallback(GLFWwindow* window, double x, double y) { +} + +void keyCallback(GLFWwindow* window, int32_t key, int32_t scancode, int32_t action, int32_t mods) { +} + +void textInputCallback(GLFWwindow* window, uint32_t codepoint) { +} + +void cursorEnterLeaveCallback(GLFWwindow* window, int32_t entered) { +} + +void mouseButtonCallback(GLFWwindow* window, int32_t button, int32_t action, int32_t mods) { +} + +void scrollCallback(GLFWwindow* window, double x, double y) { +} +// #### End Callbacks ########################################################## + int main(int argc, char* argv[]) { std::vector flags; flags.push_back(CLI::Flag('h', "help", "print help and exit")); @@ -63,5 +88,50 @@ int main(int argc, char* argv[]) { return EXIT_USAGE; } + + // TODO: Find a better place for this + // Ideally, the window management and rendering portion of FOSS-VG should + // live in a library so it can be easily reloaded/restarted for things like + // switching from/to fullscreen. For now, I am going to put it here because + // I want to get it going before making it pretty. + { + if (!glfwInit()) { + return EXIT_RUNTIME; + } + + //TODO: allow to set startup window size using CLI options + uint32_t windowWidth = 1366; + uint32_t windowHeight = 768; + //TODO: add a version macro + // (for example Git commit hash passed on the compiler command line) + std::string windowTitle = "FOSS-VG"; + + // Apparently, this also allows to set things like whether the window is full-screen + GLFWwindow* window = glfwCreateWindow(windowWidth, windowHeight, windowTitle.c_str(), nullptr, nullptr); + + if (window == nullptr) { + return EXIT_RUNTIME; + } + + // What dis do? It was in a tutorial. + glfwMakeContextCurrent(window); + + glfwSetCursorPosCallback(window, cursorPositionCallback); + glfwSetKeyCallback(window, keyCallback); + glfwSetCharCallback(window, textInputCallback); + glfwSetCursorEnterCallback(window, cursorEnterLeaveCallback); + glfwSetMouseButtonCallback(window, mouseButtonCallback); + glfwSetScrollCallback(window, scrollCallback); + + while (!glfwWindowShouldClose(window)) { + + glfwSwapBuffers(window); + + glfwPollEvents(); + } + + glfwTerminate(); + } + return EXIT_SUCCESS; } diff --git a/src/lib/cli.cpp b/src/lib/cli.cpp index d5511ca..8956fef 100644 --- a/src/lib/cli.cpp +++ b/src/lib/cli.cpp @@ -202,7 +202,6 @@ namespace CLI { } ArgumentsParser::~ArgumentsParser() { - //TODO: check that this actually runs for (auto const& [shortName, flag]: this->flagsByShortName) { delete flag; } diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 54b341f..207b0ee 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -97,6 +97,9 @@ namespace ErrorCodes { const uint8_t MIXED_TYPES = 12; + // when too much data is available + const uint8_t OVERFLOW = 13; + const uint8_t UNIMPLEMENTED = 254; const uint8_t UNKNOWN = 255; diff --git a/src/lib/file.cpp b/src/lib/file.cpp index bfdd245..c6ffb31 100644 --- a/src/lib/file.cpp +++ b/src/lib/file.cpp @@ -65,6 +65,8 @@ void File::open(){ void File::close(){ this->fileStream.close(); + std::filesystem::path filePath = this->path; + this->size = ErrorOr(std::filesystem::file_size(filePath)); this->isOpen = false; } @@ -225,7 +227,7 @@ ErrorOrVoid File::insertString(tiny_utf8::string string){ this->fileStream.read(reinterpret_cast(buffer), this->size.value); - readData = tiny_utf8::string((char *) buffer); + readData = tiny_utf8::string((char *) buffer, 0, this->size.value); readData.insert(readData.begin()+this->cursorPosition, string); this->fileStream.seekg(0); @@ -255,7 +257,7 @@ ErrorOr File::cutByte(){ std::filesystem::resize_file(this->path, readData.size()); this->fileStream.seekg(0); this->write(readData); - this->cursorPosition++; + //this->cursorPosition++; delete[] buffer; }catch(std::exception& e){ failure = true; @@ -280,7 +282,7 @@ ErrorOr> File::cut(uint64_t length){ std::filesystem::resize_file(this->path, readData.size()); this->fileStream.seekg(0); this->write(readData); - this->cursorPosition += length; + //this->cursorPosition += length; delete[] buffer; }catch(std::exception& e){ failure = true; @@ -289,6 +291,30 @@ ErrorOr> File::cut(uint64_t length){ return failure ? ErrorOr>(true, ErrorCodes::UNKNOWN) :ErrorOr>(bytes); } +ErrorOr File::cutString(uint64_t length){ + bool failure = false; + tiny_utf8::string cutString; + try{ + uint8_t* buffer = new uint8_t[this->size.value]; + tiny_utf8::string readData; + + this->fileStream.read(reinterpret_cast(buffer), this->size.value); + readData = tiny_utf8::string((char *) buffer, 0, this->size.value); + + cutString = readData.substr(this->cursorPosition, length); + + std::filesystem::resize_file(this->path, readData.size()-cutString.size()); + this->fileStream.seekg(0); + this->writeString(readData.substr(this->cursorPosition+length)); + //this->cursorPosition += length; + delete[] buffer; + }catch(std::exception& e){ + failure = true; + } + + return failure ? ErrorOr(true, ErrorCodes::UNKNOWN) : ErrorOr(cutString); +} + ErrorOr File::open(std::string path, char mode, uint64_t startPosition){ if (!std::filesystem::exists(path) && (mode == 'r' || mode == 'm')) { return ErrorOr(true, ErrorCodes::FILE_NOT_FOUND, nullptr); diff --git a/src/lib/file.hpp b/src/lib/file.hpp index c794d04..f739e57 100644 --- a/src/lib/file.hpp +++ b/src/lib/file.hpp @@ -16,6 +16,8 @@ // version 3 along with this program. // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html +#pragma once + #include #include #include diff --git a/src/lib/javacompat.hpp b/src/lib/javacompat.hpp index a8c054b..4121a76 100644 --- a/src/lib/javacompat.hpp +++ b/src/lib/javacompat.hpp @@ -13,6 +13,8 @@ //version 3 along with this program. //If not, see https://www.gnu.org/licenses/agpl-3.0.en.html +#pragma once + #include #include #include "error.hpp" diff --git a/src/lib/nbt.cpp b/src/lib/nbt.cpp index f60a3ca..76aecbf 100644 --- a/src/lib/nbt.cpp +++ b/src/lib/nbt.cpp @@ -976,6 +976,20 @@ namespace NBT { this->endPointer = new End(); } + ErrorOr Compound::constructWithData(tiny_utf8::string name, std::vector data) { + if (data.size() > 0) { + for (uint64_t i=0; igetTagType() == TagType::END && i != data.size()-1) { + return ErrorOr(true, ErrorCodes::NOT_ALLOWED, nullptr); + } + } + if (data[data.size()-1]->getTagType() == TagType::END) { + return ErrorOr(new Compound(name, std::vector(data.begin(), data.end()-1))); + } + } + return ErrorOr(new Compound(name, data)); + } + Compound::~Compound() { for (uint64_t i=0; itags.size(); i++) { delete this->tags.at(i); @@ -1342,7 +1356,7 @@ namespace NBT { returnValue = ErrorOr>(true, nextCompoundData.errorCode); goto returnError; } - contents.push_back(new Tag::Compound("", nextCompoundData.value)); + contents.push_back(reinterpret_cast(Tag::Compound::constructWithData("", nextCompoundData.value).value)); *processedDataSize += *containedDataSize; } delete containedDataSize; @@ -1454,7 +1468,7 @@ namespace NBT { returnValue = ErrorOr>(true, compoundData.errorCode); goto returnNow; } - tags.push_back(new Tag::Compound(tagName.value, compoundData.value)); + tags.push_back(reinterpret_cast(Tag::Compound::constructWithData(tagName.value, compoundData.value).value)); *processedTagSize += (uint64_t) nameSize + 3; } currentPosition += *processedTagSize; diff --git a/src/lib/nbt.hpp b/src/lib/nbt.hpp index c0940e0..5a4e3a8 100644 --- a/src/lib/nbt.hpp +++ b/src/lib/nbt.hpp @@ -248,10 +248,10 @@ namespace NBT { std::vector tags; // built-in end tag End* endPointer; + Compound(tiny_utf8::string name, std::vector data); public: Compound(); Compound(tiny_utf8::string name); - Compound(tiny_utf8::string name, std::vector data); ~Compound() override; @@ -262,6 +262,7 @@ namespace NBT { ErrorOrVoid appendPointer(Generic* pointer); ErrorOrVoid deleteElement(uint64_t position); uint64_t length(); + static ErrorOr constructWithData(tiny_utf8::string name, std::vector data); }; class Int32Array: public Generic { diff --git a/src/lib/net/client.cpp b/src/lib/net/client.cpp new file mode 100644 index 0000000..143a4fc --- /dev/null +++ b/src/lib/net/client.cpp @@ -0,0 +1,16 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html diff --git a/src/lib/net/client.hpp b/src/lib/net/client.hpp new file mode 100644 index 0000000..143a4fc --- /dev/null +++ b/src/lib/net/client.hpp @@ -0,0 +1,16 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html diff --git a/src/lib/net/conversion/position.hpp b/src/lib/net/conversion/position.hpp new file mode 100644 index 0000000..5540bde --- /dev/null +++ b/src/lib/net/conversion/position.hpp @@ -0,0 +1,70 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// BodgeMaster +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html + +#pragma once + +#include +#include + +#include "../../error.hpp" + +namespace Position { + struct Location { + // 26 bit integer + int32_t x; + // 12 bit integer + int16_t y; + // 26 bit integer + int32_t z; + } + + Location fromPosition(uint64_t position) { + Location location; + + location.x = (int32_t) ((0xFFFFFFC000000000 & position) >> 38); + if (location.x >= 0x02000000) { + location.x -= 0x04000000; + } + + location.y = (int16_t) ((0x0000000000000FFF & position)); + if (location.y >= 0x0800) { + location.y -= 0x1000; + } + + location.z = (int32_t) ((0x0000003FFFFFF000 & position) >> 12); + if (location.z >= 0x02000000) { + location.z -= 0x04000000; + } + } + + ErrorOr fromPosition(std::vector data, uint64_t initialPosition=0) { + if (inititalPosition >= data.size()) { + return ErrorOr(true, ErrorCodes::OUT_OF_BOUNDS); + } + if (initialPosition+7 >= data.size()) { + return ErrorOr(true, ErrorCodes::OVERRUN); + } + + uint64_t deserialized = 0; + for (uint8_t i=0; i<8; i++) { + deserialized += (uint64_t) data[initialPosition+i] << i*8; + } + + return ErrorOr(fromPosition(deserialized)); + } +} diff --git a/src/lib/net/conversion/varint.hpp b/src/lib/net/conversion/varint.hpp new file mode 100644 index 0000000..16bfd4a --- /dev/null +++ b/src/lib/net/conversion/varint.hpp @@ -0,0 +1,141 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// BodgeMaster, Shwoomple +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html + +#pragma once + +#include +#include + +#include "../../error.hpp" + +namespace VarInt { + + // up to 5 bytes, least significant byte first, most significant bit + // indicates whether the next byte is still part of the number + ErrorOr fromVar32(std::vector data, uint64_t initialPosition=0, uint8_t* processedBytes=nullptr) { + if (initialPosition >= data.size()) { + return ErrorOr(true, ErrorCodes::OUT_OF_RANGE); + } + + int32_t returnValue = 0; + uint64_t currentPosition = initialPosition; + uint8_t bits = 0; + while (data[currentPosition] & 0b10000000 && currentPosition < initialPosition+4) { + returnValue = returnValue + (((int32_t) 0b01111111 & data[currentPosition]) << bits); + + (*processedBytes)++; + bits += 7; + currentPosition++; + // check after increasing so we don't need to check outside the loop + if (currentPosition >= data.size()) { + return ErrorOr(true, ErrorCodes::OVERRUN); + } + + } + if (data[currentPosition] & 0b10000000) { + return ErrorOr(true, ErrorCodes::OVERFLOW); + } + returnValue = returnValue + (((int32_t) 0b01111111 & data[currentPosition]) << bits); + (*processedBytes)++; + + return ErrorOr(returnValue); + } + + // up to 10 bytes, least significant byte first, most significant bit + // indicates whether the next byte is still part of the number + ErrorOr fromVar64(std::vector data, uint64_t initialPosition=0, uint8_t* processedBytes=nullptr) { + if (initialPosition >= data.size()) { + return ErrorOr(true, ErrorCodes::OUT_OF_RANGE); + } + + int64_t returnValue = 0; + uint64_t currentPosition = initialPosition; + uint8_t bits = 0; + while (data[currentPosition] & 0b10000000 && currentPosition < initialPosition+9) { + returnValue = returnValue + (((int64_t) 0b01111111 & data[currentPosition]) << bits); + + (*processedBytes)++; + bits += 7; + currentPosition++; + // check after increasing so we don't need to check outside the loop + if (currentPosition >= data.size()) { + return ErrorOr(true, ErrorCodes::OVERRUN); + } + } + if (data[currentPosition] & 0b10000000) { + return ErrorOr(true, ErrorCodes::OVERFLOW); + } + returnValue = returnValue + (((int64_t) 0b01111111 & data[currentPosition]) << bits); + (*processedBytes)++; + + return ErrorOr(returnValue); + } + + // appends to the vector + void toVar32(int32_t value, std::vector &data) { + uint8_t nextByte; + uint8_t shift = 0; + // do something else after the 4th shift + while (shift < 4) { + if (shift>0 && value==0) { + return; + } + + nextByte = value & 0b01111111; + + value = (value >> 7) & 0x01FFFFFF; + shift++; + + + if (value>0) { + nextByte = nextByte + 0b10000000; + } + data.push_back(nextByte); + + } + if (value>0) { + data.push_back(value & 0b00001111); + } + } + + // appends to the vector + void toVar64(int64_t value, std::vector &data) { + uint8_t nextByte; + uint8_t shift = 0; + // do something else after the 4th shift + while (shift < 9) { + if (shift>0 && value==0) { + return; + } + + nextByte = value & 0b01111111; + + value = (value >> 7) & 0x01FFFFFFFFFFFFFF; + shift++; + + if (value>0) { + nextByte = nextByte + 0b10000000; + } + data.push_back(nextByte); + + } + if (value>0) { + data.push_back(value & 0b00000001); + } + } +} diff --git a/src/lib/net/packet.hpp b/src/lib/net/packet.hpp new file mode 100644 index 0000000..b600486 --- /dev/null +++ b/src/lib/net/packet.hpp @@ -0,0 +1,43 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// BodgeMaster +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html + +#pragma once + +#include +#include + +namespace Packet { + struct DataContainer { + uint32_t typeID; + std::vector payload; + } + + namespace TypeID { + // These are guessed names for what the package IDs could mean. + // Some package IDs are used multiple times. I assume the different + // connection states (handshaking, status, login, and play) play a role + // in identifying what kind of package has been received. + + // State: handshaking + const uint32_t HANDSHAKE = 0; + + // State: status + const uint32_t STATUS = 0; + const uint32_t PING = 1; + } +} diff --git a/src/lib/net/server.cpp b/src/lib/net/server.cpp new file mode 100644 index 0000000..143a4fc --- /dev/null +++ b/src/lib/net/server.cpp @@ -0,0 +1,16 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html diff --git a/src/lib/net/server.hpp b/src/lib/net/server.hpp new file mode 100644 index 0000000..143a4fc --- /dev/null +++ b/src/lib/net/server.hpp @@ -0,0 +1,16 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html diff --git a/src/test/file.cpp b/src/test/file.cpp index eddc358..0cacab6 100644 --- a/src/test/file.cpp +++ b/src/test/file.cpp @@ -177,6 +177,7 @@ int main(){ tiny_utf8::string cutByteString = readFile->readString(readFile->size.value).value; readFile->close(); + ASSERT(modifyFile->cursorPosition == modifyFile->size.value); ASSERT(cutByte.value == '.'); ASSERT(cutByteString == "Hallo, Hi THE CAKE IS A LIE, Ich bin Shwoomple"); std::cout << "Passed cut byte test." << std::endl; @@ -191,7 +192,24 @@ int main(){ tiny_utf8::string cutBytesString = readFile->readString(readFile->size.value).value; readFile->close(); + + ASSERT(modifyFile->cursorPosition == 9); ASSERT(cutBytes.value == std::vector({' ', 'T', 'H', 'E', ' ', 'C', 'A', 'K', 'E', ' ', 'I', 'S', ' ','A', ' ', 'L', 'I', 'E'})) ASSERT(cutBytesString == "Hallo, Hi, Ich bin Shwoomple"); std::cout << "Passed cut test." << std::endl; + + modifyFile->open(); + modifyFile->cursorPosition = 0; + ErrorOr cutString = modifyFile->cutString(7); + modifyFile->close(); + + readFile->open(); + readFile->cursorPosition = 0; + tiny_utf8::string cutReadString = readFile->readString(readFile->size.value).value; + readFile->close(); + + ASSERT(modifyFile->cursorPosition == 0); + ASSERT(cutString.value == "Hallo, "); + ASSERT(cutReadString == "Hi, Ich bin Shwoomple"); + std::cout << "Passed cutString test." << std::endl; } diff --git a/src/test/nbt_tags.cpp b/src/test/nbt_tags.cpp index 1682c46..90bd1b2 100644 --- a/src/test/nbt_tags.cpp +++ b/src/test/nbt_tags.cpp @@ -773,11 +773,16 @@ int main(){ NBT::Tag::Int8* pointer_2_1 = new NBT::Tag::Int8("entry_2_1", 28); NBT::Tag::Int8* pointer_2_2 = new NBT::Tag::Int8("entry_2_2", 45); NBT::Tag::Int8* pointer_2_3 = new NBT::Tag::Int8("entry_2_3", 78); + NBT::Tag::Int8* pointer_invalid_0 = new NBT::Tag::Int8("ayya", 78); + NBT::Tag::Int16* pointer_invalid_1 = new NBT::Tag::Int16("ellohhh", 78); std::vector listDataVector; listDataVector.push_back(pointer_2_0); listDataVector.push_back(pointer_2_1); listDataVector.push_back(pointer_2_2); listDataVector.push_back(pointer_2_3); + std::vector invalidListDataVector; + invalidListDataVector.push_back(pointer_invalid_0); + invalidListDataVector.push_back(pointer_invalid_1); NBT::Tag::List list_0 = NBT::Tag::List(); ASSERT(list_0.serialize(&vector).isError); @@ -792,8 +797,9 @@ int main(){ NBT::Tag::Int16* pointer_invalid = new NBT::Tag::Int16("invalid", 400); resultError = list_1.appendPointer(pointer_invalid); ASSERT(resultError.isError && resultError.errorCode == ErrorCodes::INVALID_TYPE); - //TODO: Check that constructing with a vector of mixed tags - // results in a clearly defined failure mode (issue #60) + ErrorOr invalid_list_or_error = NBT::Tag::List::constructWithData("list_invalid", invalidListDataVector); + ASSERT(invalid_list_or_error.isError); + ASSERT(invalid_list_or_error.errorCode == ErrorCodes::MIXED_TYPES); ErrorOr list_2_or_error = NBT::Tag::List::constructWithData("list_2", listDataVector); ASSERT(!list_2_or_error.isError); NBT::Tag::List* list_2 = list_2_or_error.value; @@ -886,11 +892,24 @@ int main(){ compoundDataVector.push_back(new NBT::Tag::Int16("will be deleted", 0x1337)); compoundDataVector.push_back(new NBT::Tag::Int16("1", 0x1337)); compoundDataVector.push_back(new NBT::Tag::String("2", "Hello World!")); + std::vector invalidCompoundDataVector; + invalidCompoundDataVector.push_back(new NBT::Tag::End()); + invalidCompoundDataVector.push_back(new NBT::Tag::End()); + std::vector compoundDataVectorWithEnd; + compoundDataVectorWithEnd.push_back(new NBT::Tag::Int8("eeee", 25)); + compoundDataVectorWithEnd.push_back(new NBT::Tag::End()); NBT::Tag::Compound compound_0 = NBT::Tag::Compound(); compound_0.name = "compound_0"; NBT::Tag::Compound compound_1 = NBT::Tag::Compound("compound_1"); - NBT::Tag::Compound compound_2 = NBT::Tag::Compound("compound_2", compoundDataVector); + ErrorOr invalid_compound_or_error = NBT::Tag::Compound::constructWithData("iiiiii", invalidCompoundDataVector); + ASSERT(invalid_compound_or_error.isError); + ASSERT(invalid_compound_or_error.errorCode == ErrorCodes::NOT_ALLOWED); + ErrorOr alternate_compound_or_error = NBT::Tag::Compound::constructWithData("iiiiii", compoundDataVectorWithEnd); + ASSERT(!alternate_compound_or_error.isError); + ErrorOr compound_2_or_error = NBT::Tag::Compound::constructWithData("compound_2", compoundDataVector); + ASSERT(!compound_2_or_error.isError); + NBT::Tag::Compound* compound_2 = compound_2_or_error.value; ASSERT(!compound_1.appendPointer(new NBT::Tag::Int32("0", 69420)).isError); ASSERT(!compound_1.appendPointer(new NBT::Tag::Int8("1", 1)).isError); @@ -906,15 +925,15 @@ int main(){ resultNotAllowed = compound_1.setElementPointerAt(0, new NBT::Tag::End()); ASSERT(resultNotAllowed.isError && resultNotAllowed.errorCode==ErrorCodes::NOT_ALLOWED); ASSERT(compound_0.setElementPointerAt(1, new NBT::Tag::Int8()).isError && compound_0.setElementPointerAt(1, new NBT::Tag::Int8()).errorCode == ErrorCodes::OUT_OF_RANGE); - ASSERT(!compound_2.deleteElement(1).isError); + ASSERT(!compound_2->deleteElement(1).isError); ASSERT(compound_0.deleteElement(0).isError && compound_0.deleteElement(0).errorCode == ErrorCodes::NOT_ALLOWED); ASSERT(compound_0.deleteElement(1).isError && compound_0.deleteElement(1).errorCode == ErrorCodes::OUT_OF_RANGE); ASSERT(compound_0.length() == 1); ASSERT(compound_1.length() == 3); - ASSERT(compound_2.length() == 4); + ASSERT(compound_2->length() == 4); compound_0.serialize(&vector); compound_1.serialize(&vector); - compound_2.serialize(&vector); + compound_2->serialize(&vector); ASSERT(vector.size() == 95); ASSERT( vector.at( 0) == 10 && @@ -1016,7 +1035,7 @@ int main(){ vector.clear(); compound_0.serializeWithoutHeader(&vector); compound_1.serializeWithoutHeader(&vector); - compound_2.serializeWithoutHeader(&vector); + compound_2->serializeWithoutHeader(&vector); ASSERT(vector.size() == 56); ASSERT( vector.at( 0) == 0 && diff --git a/src/test/varint.cpp b/src/test/varint.cpp new file mode 100644 index 0000000..e18ec1a --- /dev/null +++ b/src/test/varint.cpp @@ -0,0 +1,287 @@ +// Copyright 2022, FOSS-VG Developers and Contributers +// +// Author(s): +// BodgeMaster, Shwoomple +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html + +#include +#include +#include + +#include "assert.hpp" +#include "../lib/error.hpp" +#include "../lib/net/conversion/varint.hpp" + +int main() { + + std::cout << "################################################################################" << std::endl; + std::cout << "VarInt tests" << std::endl; + std::cout << "################################################################################" << std::endl; + // examples for numbers + // + // 32 Bit + // 0000 0000 0000 0000 0000 0000 0000 0000 = 0 -> 0000 0000 + // 1111 1111 1111 1111 1111 1111 1111 1111 = -1 (unsigned int32 max) -> 1111 1111 1111 1111 1111 1111 1111 1111 0000 1111 + // 0000 0000 0010 0100 0011 1101 1011 1101 = 2375101 -> 1011 1101 1111 1011 1001 0000 0000 0001 + // 0000 0000 0000 0000 0001 0001 0001 0001 = 4369 -> 1001 0001 0010 0010 + // + // 64 Bit + // 0010 0000 0001 0000 0000 0000 1010 0010 1010 1000 0010 0000 1101 0000 1001 0011 = 2310347307446489235 -> 1101 0011 1010 0001 1000 0011 1100 0001 1010 1010 1001 0100 1000 0000 1000 1000 0010 0000 + // 1000 0000 0100 0000 0010 0000 0001 0000 0000 1000 0000 0100 0000 0010 0000 0001 = -9205322385119247871 -> 1000 0001 1000 0100 1001 0000 1100 0000 1000 0000 1000 0010 1000 1000 1010 0000 1000 0000 0000 0001 + // 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 = -1 (unsigned int64 max) -> 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 0000 0001 + + { + std::vector data = std::vector(); + uint8_t processedBytes; + ErrorOr result = VarInt::fromVar32(data, 0, &processedBytes); + ASSERT(result.isError); + ASSERT(result.errorCode == ErrorCodes::OUT_OF_RANGE); + } + + { + std::vector data = { 0x84 }; + uint8_t processedBytes; + ErrorOr result = VarInt::fromVar32(data, 0, &processedBytes); + ASSERT(result.isError); + ASSERT(result.errorCode == ErrorCodes::OVERRUN); + } + + { + std::vector data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 }; + uint8_t processedBytes; + ErrorOr result = VarInt::fromVar32(data, 0, &processedBytes); + ASSERT(result.isError); + ASSERT(result.errorCode == ErrorCodes::OVERFLOW); + } + + uint8_t zeroProcessedBytes = 0; + std::vector zeroData; + zeroData.push_back(0); + ErrorOr zero = VarInt::fromVar32(zeroData, 0, &zeroProcessedBytes); + ASSERT(!zero.isError); + ASSERT(zero.value == 0); + ASSERT(zeroProcessedBytes == 1); + + uint8_t minusOneProcessedBytes = 0; + std::vector minusOneData; + minusOneData.push_back(255); + minusOneData.push_back(255); + minusOneData.push_back(255); + minusOneData.push_back(255); + minusOneData.push_back(15); + ErrorOr minusOne = VarInt::fromVar32(minusOneData, 0, &minusOneProcessedBytes); + ASSERT(!minusOne.isError); + ASSERT(minusOne.value == -1); + ASSERT(minusOneProcessedBytes == 5); + + uint8_t smallProcessedBytes = 0; + std::vector smallData; + // offset data by 3 to test initialPosition feature + smallData.push_back(0b10010001); + smallData.push_back(0b10010001); + smallData.push_back(0b10010001); + smallData.push_back(0b10010001); + smallData.push_back(0b00100010); + ErrorOr small = VarInt::fromVar32(smallData, 3, &smallProcessedBytes); + ASSERT(!small.isError); + ASSERT(small.value == 4369); + ASSERT(smallProcessedBytes == 2); + + uint8_t bigProcessedBytes = 0; + std::vector bigData; + bigData.push_back(0b10111101); + bigData.push_back(0b11111011); + bigData.push_back(0b10010000); + bigData.push_back(0b00000001); + ErrorOr big = VarInt::fromVar32(bigData, 0, &bigProcessedBytes); + ASSERT(!big.isError); + ASSERT(big.value == 2375101); + ASSERT(bigProcessedBytes == 4); + + //TODO: test error conditions + + std::cout << "Passed fromVar32 test." << std::endl; + + { + std::vector data = std::vector(); + uint8_t processedBytes; + ErrorOr result = VarInt::fromVar64(data, 0, &processedBytes); + ASSERT(result.isError); + ASSERT(result.errorCode == ErrorCodes::OUT_OF_RANGE); + } + + { + std::vector data = { 0x84 }; + uint8_t processedBytes; + ErrorOr result = VarInt::fromVar64(data, 0, &processedBytes); + ASSERT(result.isError); + ASSERT(result.errorCode == ErrorCodes::OVERRUN); + } + + { + std::vector data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 }; + uint8_t processedBytes; + ErrorOr result = VarInt::fromVar64(data, 0, &processedBytes); + ASSERT(result.isError); + ASSERT(result.errorCode == ErrorCodes::OVERFLOW); + } + + uint8_t zero64ProcessedBytes = 0; + std::vector zero64Data; + zero64Data.push_back(0); + ErrorOr zero64 = VarInt::fromVar64(zero64Data, 0, &zero64ProcessedBytes); + ASSERT(!zero64.isError); + ASSERT(zero64.value == 0); + ASSERT(zero64ProcessedBytes == 1); + + uint8_t minusOne64ProcessedBytes = 0; + std::vector minusOne64Data; + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(255); + minusOne64Data.push_back(1); + ErrorOr minusOne64 = VarInt::fromVar64(minusOne64Data, 0, &minusOne64ProcessedBytes); + ASSERT(!minusOne64.isError); + ASSERT(minusOne64.value == -1); + ASSERT(minusOne64ProcessedBytes == 10); + + // 0010 0000 0001 0000 0000 0000 1010 0010 1010 1000 0010 0000 1101 0000 1001 0011 = 2310347307446489235 -> 1101 0011 1010 0001 1000 0011 1100 0001 1010 1010 1001 0100 1000 0000 1000 1000 0010 0000 + uint8_t small64ProcessedBytes = 0; + std::vector small64Data; + // offset data by 3 to test initialPosition feature + small64Data.push_back(0b11010011); + small64Data.push_back(0b11010011); + small64Data.push_back(0b11010011); + small64Data.push_back(0b10010011); + small64Data.push_back(0b10100001); + small64Data.push_back(0b10000011); + small64Data.push_back(0b11000001); + small64Data.push_back(0b10101010); + small64Data.push_back(0b10010100); + small64Data.push_back(0b10000000); + small64Data.push_back(0b10001000); + small64Data.push_back(0b00100000); + ErrorOr small64 = VarInt::fromVar64(small64Data, 3, &small64ProcessedBytes); + ASSERT(!small64.isError); + ASSERT(small64.value == 2310347307446489235); + ASSERT(small64ProcessedBytes == 9); + + // 1000 0000 0100 0000 0010 0000 0001 0000 0000 1000 0000 0100 0000 0010 0000 0001 = -9205322385119247871 -> 1000 0001 1000 0100 1001 0000 1100 0000 1000 0000 1000 0010 1000 1000 1010 0000 1000 0000 0000 0001 + uint8_t big64ProcessedBytes = 0; + std::vector big64Data; + big64Data.push_back(0b10000001); + big64Data.push_back(0b10000100); + big64Data.push_back(0b10010000); + big64Data.push_back(0b11000000); + big64Data.push_back(0b10000000); + big64Data.push_back(0b10000010); + big64Data.push_back(0b10001000); + big64Data.push_back(0b10100000); + big64Data.push_back(0b10000000); + big64Data.push_back(0b00000001); + ErrorOr big64 = VarInt::fromVar64(big64Data, 0, &big64ProcessedBytes); + ASSERT(!big64.isError); + ASSERT(big64.value == -9205322385119247871); + ASSERT(big64ProcessedBytes == 10); + + //TODO: Test error conditions + + std::cout << "Passed fromVar64 test." << std::endl; + + // reversing all the previous tests + std::vector dataDump; + + VarInt::toVar32(0, dataDump); + ASSERT(dataDump[0] == 0); + + VarInt::toVar32(-1, dataDump); + ASSERT( + dataDump[1]==255 && + dataDump[2]==255 && + dataDump[3]==255 && + dataDump[4]==255 && + dataDump[5]==15 + ); + + VarInt::toVar32(4369, dataDump); + ASSERT( + dataDump[6]==0b10010001 && + dataDump[7]==0b00100010 + ); + + VarInt::toVar32(2375101, dataDump); + ASSERT( + dataDump[8]==0b10111101 && + dataDump[9]==0b11111011 && + dataDump[10]==0b10010000 && + dataDump[11]==0b00000001 + ); + + std::cout << "Passed toVar32 test." << std::endl; + + VarInt::toVar64(0, dataDump); + ASSERT(dataDump[12]==0); + + VarInt::toVar64(-1, dataDump); + ASSERT( + dataDump[13]==255 && + dataDump[14]==255 && + dataDump[15]==255 && + dataDump[16]==255 && + dataDump[17]==255 && + dataDump[18]==255 && + dataDump[19]==255 && + dataDump[20]==255 && + dataDump[21]==255 && + dataDump[22]==1 + ); + + VarInt::toVar64(2310347307446489235, dataDump); + ASSERT( + dataDump[23]==0b10010011 && + dataDump[24]==0b10100001 && + dataDump[25]==0b10000011 && + dataDump[26]==0b11000001 && + dataDump[27]==0b10101010 && + dataDump[28]==0b10010100 && + dataDump[29]==0b10000000 && + dataDump[30]==0b10001000 && + dataDump[31]==0b00100000 + ); + + VarInt::toVar64(-9205322385119247871, dataDump); + ASSERT( + dataDump[32]==0b10000001 && + dataDump[33]==0b10000100 && + dataDump[34]==0b10010000 && + dataDump[35]==0b11000000 && + dataDump[36]==0b10000000 && + dataDump[37]==0b10000010 && + dataDump[38]==0b10001000 && + dataDump[39]==0b10100000 && + dataDump[40]==0b10000000 && + dataDump[41]==0b00000001 + ) + + std::cout << "Passed toVar64 test." << std::endl; + + return 0; +}