Merge branch 'master' into cygwin

windows
BodgeMaster 2023-01-09 21:54:23 +01:00
commit 4c651d1b6f
25 changed files with 2219 additions and 15 deletions

View File

@ -26,6 +26,7 @@ Build dependencies:
- bash
- a C++ 20 compiler
- GLFW with headers
Setup dependencies:

View File

@ -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 whats happening
## unicode_data/
Files with unicode data

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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]}"

View File

@ -18,6 +18,10 @@
#include <iostream>
#include <vector>
#include <cstdint>
#include <string>
#include <GLFW/glfw3.h>
#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<CLI::Flag> 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;
}

View File

@ -202,7 +202,6 @@ namespace CLI {
}
ArgumentsParser::~ArgumentsParser() {
//TODO: check that this actually runs
for (auto const& [shortName, flag]: this->flagsByShortName) {
delete flag;
}

View File

@ -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;

View File

@ -65,6 +65,8 @@ void File::open(){
void File::close(){
this->fileStream.close();
std::filesystem::path filePath = this->path;
this->size = ErrorOr<uint64_t>(std::filesystem::file_size(filePath));
this->isOpen = false;
}
@ -225,7 +227,7 @@ ErrorOrVoid File::insertString(tiny_utf8::string string){
this->fileStream.read(reinterpret_cast<char*>(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<uint8_t> 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<std::vector<uint8_t>> 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<std::vector<uint8_t>> File::cut(uint64_t length){
return failure ? ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::UNKNOWN) :ErrorOr<std::vector<uint8_t>>(bytes);
}
ErrorOr<tiny_utf8::string> 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<char*>(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<tiny_utf8::string>(true, ErrorCodes::UNKNOWN) : ErrorOr<tiny_utf8::string>(cutString);
}
ErrorOr<File*> File::open(std::string path, char mode, uint64_t startPosition){
if (!std::filesystem::exists(path) && (mode == 'r' || mode == 'm')) {
return ErrorOr<File*>(true, ErrorCodes::FILE_NOT_FOUND, nullptr);

View File

@ -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 <cstdint>
#include <string>
#include <fstream>

View File

@ -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 <tinyutf8/tinyutf8.h>
#include <string>
#include "error.hpp"

View File

@ -976,6 +976,20 @@ namespace NBT {
this->endPointer = new End();
}
ErrorOr<Compound*> Compound::constructWithData(tiny_utf8::string name, std::vector<Generic*> data) {
if (data.size() > 0) {
for (uint64_t i=0; i<data.size(); i++) {
if (data[i]->getTagType() == TagType::END && i != data.size()-1) {
return ErrorOr<Compound*>(true, ErrorCodes::NOT_ALLOWED, nullptr);
}
}
if (data[data.size()-1]->getTagType() == TagType::END) {
return ErrorOr<Compound*>(new Compound(name, std::vector(data.begin(), data.end()-1)));
}
}
return ErrorOr<Compound*>(new Compound(name, data));
}
Compound::~Compound() {
for (uint64_t i=0; i<this->tags.size(); i++) {
delete this->tags.at(i);
@ -1342,7 +1356,7 @@ namespace NBT {
returnValue = ErrorOr<std::vector<Tag::Generic*>>(true, nextCompoundData.errorCode);
goto returnError;
}
contents.push_back(new Tag::Compound("", nextCompoundData.value));
contents.push_back(reinterpret_cast<Tag::Generic*>(Tag::Compound::constructWithData("", nextCompoundData.value).value));
*processedDataSize += *containedDataSize;
}
delete containedDataSize;
@ -1454,7 +1468,7 @@ namespace NBT {
returnValue = ErrorOr<std::vector<Tag::Generic*>>(true, compoundData.errorCode);
goto returnNow;
}
tags.push_back(new Tag::Compound(tagName.value, compoundData.value));
tags.push_back(reinterpret_cast<Tag::Generic*>(Tag::Compound::constructWithData(tagName.value, compoundData.value).value));
*processedTagSize += (uint64_t) nameSize + 3;
}
currentPosition += *processedTagSize;

View File

@ -248,10 +248,10 @@ namespace NBT {
std::vector<Generic*> tags;
// built-in end tag
End* endPointer;
Compound(tiny_utf8::string name, std::vector<Generic*> data);
public:
Compound();
Compound(tiny_utf8::string name);
Compound(tiny_utf8::string name, std::vector<Generic*> data);
~Compound() override;
@ -262,6 +262,7 @@ namespace NBT {
ErrorOrVoid appendPointer(Generic* pointer);
ErrorOrVoid deleteElement(uint64_t position);
uint64_t length();
static ErrorOr<Compound*> constructWithData(tiny_utf8::string name, std::vector<Generic*> data);
};
class Int32Array: public Generic {

16
src/lib/net/client.cpp Normal file
View File

@ -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

16
src/lib/net/client.hpp Normal file
View File

@ -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

View File

@ -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 <cstdint>
#include <vector>
#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<Location> fromPosition(std::vector data, uint64_t initialPosition=0) {
if (inititalPosition >= data.size()) {
return ErrorOr<Location>(true, ErrorCodes::OUT_OF_BOUNDS);
}
if (initialPosition+7 >= data.size()) {
return ErrorOr<Location>(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<Location>(fromPosition(deserialized));
}
}

View File

@ -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 <cstdint>
#include <vector>
#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<int32_t> fromVar32(std::vector<uint8_t> data, uint64_t initialPosition=0, uint8_t* processedBytes=nullptr) {
if (initialPosition >= data.size()) {
return ErrorOr<int32_t>(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<int32_t>(true, ErrorCodes::OVERRUN);
}
}
if (data[currentPosition] & 0b10000000) {
return ErrorOr<int32_t>(true, ErrorCodes::OVERFLOW);
}
returnValue = returnValue + (((int32_t) 0b01111111 & data[currentPosition]) << bits);
(*processedBytes)++;
return ErrorOr<int32_t>(returnValue);
}
// up to 10 bytes, least significant byte first, most significant bit
// indicates whether the next byte is still part of the number
ErrorOr<int64_t> fromVar64(std::vector<uint8_t> data, uint64_t initialPosition=0, uint8_t* processedBytes=nullptr) {
if (initialPosition >= data.size()) {
return ErrorOr<int64_t>(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<int64_t>(true, ErrorCodes::OVERRUN);
}
}
if (data[currentPosition] & 0b10000000) {
return ErrorOr<int64_t>(true, ErrorCodes::OVERFLOW);
}
returnValue = returnValue + (((int64_t) 0b01111111 & data[currentPosition]) << bits);
(*processedBytes)++;
return ErrorOr<int64_t>(returnValue);
}
// appends to the vector
void toVar32(int32_t value, std::vector<uint8_t> &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<uint8_t> &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);
}
}
}

43
src/lib/net/packet.hpp Normal file
View File

@ -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 <vector>
#include <cstdint>
namespace Packet {
struct DataContainer {
uint32_t typeID;
std::vector<uint8_t> 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;
}
}

16
src/lib/net/server.cpp Normal file
View File

@ -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

16
src/lib/net/server.hpp Normal file
View File

@ -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

View File

@ -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<uint8_t>({' ', '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<tiny_utf8::string> 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;
}

View File

@ -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<NBT::Tag::Generic*> 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<NBT::Tag::Generic*> 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<NBT::Tag::List*> 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<NBT::Tag::List*> 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<NBT::Tag::Generic*> invalidCompoundDataVector;
invalidCompoundDataVector.push_back(new NBT::Tag::End());
invalidCompoundDataVector.push_back(new NBT::Tag::End());
std::vector<NBT::Tag::Generic*> 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<NBT::Tag::Compound*> 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<NBT::Tag::Compound*> alternate_compound_or_error = NBT::Tag::Compound::constructWithData("iiiiii", compoundDataVectorWithEnd);
ASSERT(!alternate_compound_or_error.isError);
ErrorOr<NBT::Tag::Compound*> 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 &&

287
src/test/varint.cpp Normal file
View File

@ -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 <iostream>
#include <cstdint>
#include <vector>
#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<uint8_t> data = std::vector<uint8_t>();
uint8_t processedBytes;
ErrorOr<int32_t> result = VarInt::fromVar32(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OUT_OF_RANGE);
}
{
std::vector<uint8_t> data = { 0x84 };
uint8_t processedBytes;
ErrorOr<int32_t> result = VarInt::fromVar32(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERRUN);
}
{
std::vector<uint8_t> data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
uint8_t processedBytes;
ErrorOr<int32_t> result = VarInt::fromVar32(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERFLOW);
}
uint8_t zeroProcessedBytes = 0;
std::vector<uint8_t> zeroData;
zeroData.push_back(0);
ErrorOr<int32_t> zero = VarInt::fromVar32(zeroData, 0, &zeroProcessedBytes);
ASSERT(!zero.isError);
ASSERT(zero.value == 0);
ASSERT(zeroProcessedBytes == 1);
uint8_t minusOneProcessedBytes = 0;
std::vector<uint8_t> minusOneData;
minusOneData.push_back(255);
minusOneData.push_back(255);
minusOneData.push_back(255);
minusOneData.push_back(255);
minusOneData.push_back(15);
ErrorOr<int32_t> minusOne = VarInt::fromVar32(minusOneData, 0, &minusOneProcessedBytes);
ASSERT(!minusOne.isError);
ASSERT(minusOne.value == -1);
ASSERT(minusOneProcessedBytes == 5);
uint8_t smallProcessedBytes = 0;
std::vector<uint8_t> 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<int32_t> small = VarInt::fromVar32(smallData, 3, &smallProcessedBytes);
ASSERT(!small.isError);
ASSERT(small.value == 4369);
ASSERT(smallProcessedBytes == 2);
uint8_t bigProcessedBytes = 0;
std::vector<uint8_t> bigData;
bigData.push_back(0b10111101);
bigData.push_back(0b11111011);
bigData.push_back(0b10010000);
bigData.push_back(0b00000001);
ErrorOr<int32_t> 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<uint8_t> data = std::vector<uint8_t>();
uint8_t processedBytes;
ErrorOr<int64_t> result = VarInt::fromVar64(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OUT_OF_RANGE);
}
{
std::vector<uint8_t> data = { 0x84 };
uint8_t processedBytes;
ErrorOr<int64_t> result = VarInt::fromVar64(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERRUN);
}
{
std::vector<uint8_t> data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 };
uint8_t processedBytes;
ErrorOr<int64_t> result = VarInt::fromVar64(data, 0, &processedBytes);
ASSERT(result.isError);
ASSERT(result.errorCode == ErrorCodes::OVERFLOW);
}
uint8_t zero64ProcessedBytes = 0;
std::vector<uint8_t> zero64Data;
zero64Data.push_back(0);
ErrorOr<int64_t> zero64 = VarInt::fromVar64(zero64Data, 0, &zero64ProcessedBytes);
ASSERT(!zero64.isError);
ASSERT(zero64.value == 0);
ASSERT(zero64ProcessedBytes == 1);
uint8_t minusOne64ProcessedBytes = 0;
std::vector<uint8_t> 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<int64_t> 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<uint8_t> 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<int64_t> 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<uint8_t> 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<int64_t> 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<uint8_t> 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;
}