Compare commits

..

No commits in common. "38fad56965af02ec13fb430abeecf23c0f5a8af2" and "046275645139c237c35deb5193d798849254fad1" have entirely different histories.

51 changed files with 933 additions and 7429 deletions

9
.gitignore vendored
View File

@ -4,13 +4,6 @@
/dependencies/* /dependencies/*
!/dependencies/.placeholder !/dependencies/.placeholder
/include/*
!/include/.placeholder
.download_cache
.localenv.bashrc
# ignore endianness check # ignore endianness check
/.endianness /.endianness
/resources/check_endianness /resources/check_endianness
@ -23,5 +16,3 @@
#vscode #vscode
.vscode .vscode
writeTest

12
Makefile Normal file
View File

@ -0,0 +1,12 @@
all: build
build:
bash ./scripts/build.sh
clean:
bash ./scripts/clean.sh
mrproper:
bash ./scripts/clean.sh
bash ./scripts/clean_dependencies.sh
setup:
bash ./scripts/setup_project.sh
test:
bash ./scripts/test.sh

View File

@ -22,19 +22,13 @@ Immediate goals:
### Prerequisites: ### Prerequisites:
Build dependencies: This project requires bash and a C++20 compiler.
- bash The project setup requires wget or curl, gzip, and tar.
- a C++ 20 compiler
Setup dependencies: Additional requirements for building dependencies:
- wget or curl - sockpp: a C compiler, CMake
- gzip
- sha256sum (or NetBSD's sha256, hashalot's sha256 will not work)
- tar
- a C compiler (for sockpp)
- CMake (for sockpp)
**For people using other shells than bash:** You need to at least have bash **For people using other shells than bash:** You need to at least have bash
installed to use the scripts, but using it as your shell while working on installed to use the scripts, but using it as your shell while working on
@ -51,9 +45,7 @@ point to `bin/lib`.
- `git clone` this repository - `git clone` this repository
- if using bash: - if using bash:
- `source` the file `setupenv.bashrc` from the project's base directory - `source` the file `setupenv.bashrc` from the project's base directory
to load the provided shell environment (local customizations to the to load the provided shell environment
environment can be placed in a fiile `.localenv.bashrc` in the project's
root directory if necessary)
- `setup_project` - `setup_project`
- if not using bash or not using the provided environment: - if not using bash or not using the provided environment:
- `cd` to the project's base directory - `cd` to the project's base directory

View File

@ -48,6 +48,11 @@ universally used.
Use explicitly sized data types where possible. Use explicitly sized data types where possible.
For example, use `int32_t` instead of `int`. For example, use `int32_t` instead of `int`.
When coming up with names, refer to data types by category and size.
For example, refer to double precision floating point numbers as `float64`
instead of `double`.
## Shell Script ## Shell Script
Use the hash bang `#!/usr/bin/env bash`. Use the hash bang `#!/usr/bin/env bash`.

View File

@ -1,8 +1,5 @@
Copyright <year>, FOSS-VG Developers and Contributers Copyright <year>, FOSS-VG Developers and Contributers
Author(s):
<names or pseudonyms here>
This program is free software: you can redistribute it and/or modify it 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 under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, version 3. by the Free Software Foundation, version 3.

View File

@ -24,15 +24,11 @@ Provided aliases:
## setup_project ## setup_project
Cleans the project and its dependencies first (see below), ensuring Cleans the project and its dependencies first, ensuring the project is in
the project is in a known state, then sets up dependencies. a known state, then downloads and extracts dependencies.
This includes downloading (and caching) of files that are not yet This will probably do more in the future (for example compile dependencies
locally cached and compiling dependencies that need to be compiled. that need to be compiled).
This scripts intended purpose is setting up a development environment but
it can also be used to update the locally set-up dependencies to the ones
that the project is using.
# build # build
@ -59,37 +55,14 @@ Accepted environment variables:
- `CXX`: override the default compiler - `CXX`: override the default compiler
- `CXXFLAGS`: override the default compiler flags, must at least specify - `CXXFLAGS`: override the default compiler flags, must at least specify
the C++ version the C++ version
- `SINGLE`: if set to `yes`, causes the test script to compile the tests in
single-threaded mode¹
¹Unit tests are always run one after the other.
# clean # clean
Removes and re-creates `bin/` and `include/`, then re-creates symlinks to Removes and re-creates `bin/`. Also removes the endianness header and
shared object and header files as needed for the build process. the endianness check binary.
Also removes the endianness header and the endianness check binary.
# clean_dependencies # clean_dependencies
Removes and re-creates `dependencies/` and prunes the download cache of files Removes and re-creates `dependencies/`.
that either have been corrupted or are no longer used.
# lib
This is not a script you are supposed to run. It's a library that contains
some functions for the other scripts to work properly on all platforms.
Features it provides:
- `$WAIT_ANYWAY`: a variable containing the `wait` command if `SINGLE=yes`
is set.
- `$CXX_WITH_FLAGS`: deals with `CXX` and `CXXFLAGS` environment variables
so it can be used as a generic compiler command
- function `check_sha256`: deals with `sha256sum` and NetBSD's `sha256`
- function `remove`: better verbosity
- function `create_directory`: alternative to `mkdir -v` because that's not
available on some systems
- function `create_file`: the same but for files

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -23,11 +23,11 @@ A simple tool written in Java that takes an input as UTF-8 and outputs it in Jav
Usage example: `echo -ne "\x00" | java JavaStringGenerator > output_file` Usage example: `echo -ne "\x00" | java JavaStringGenerator > output_file`
## NBT_data ## servers.dat
Data used to test the NBT library. My current servers.dat as pulled from my Minecraft installation. Used for testing the NBT library until we have something better.
`servers.dat`: My current servers.dat as pulled from my Minecraft installation
`servers.dat_nbt_decoded.txt`: The same file manually decoded (I did this to get a better understanding how NBT works, might come in handy in the future.) ## servers.dat_nbt_decoded.txt
`simple_nbt`: A simple NBT file containing all tags
`nested_compounds_and_lists`: A combination of nested compound and list tags intended to be challenging to parse The same file manually decoded. I did this to get a better understanding how NBT works, might come in handy in the future.

Binary file not shown.

View File

@ -1,354 +0,0 @@
#############################################################################
# Examples for all types of tags: #
#############################################################################
[0: End]:
|Header: 1 byte
|Payload: 0 bytes
'Total: 1 byte
[0: 8 Bit Integer] name:
|Header: 7 bytes
|Payload: 1 byte
|Total: 8 bytes
'Value: 0
[0: 16 Bit Integer] name:
|Header: 7 bytes
|Payload: 2 bytes
|Total: 9 bytes
'Value: 0
[0: 32 Bit Integer] name:
|Header: 7 bytes
|Payload: 4 bytes
|Total: 11 bytes
'Value: 0
[0: 64 Bit Integer] name:
|Header: 7 bytes
|Payload: 8 bytes
|Total: 15 bytes
'Value: 0
[0: Float] name:
|Header: 7 bytes
|Payload: 4 bytes
|Total: 11 bytes
'Value: 0
[0: Double] name:
|Header: 7 bytes
|Payload: 8 bytes
|Total: 15 bytes
'Value: 0
[0: Array of 8 Bit Integers] name:
|Header: 7 bytes
|Payload: 7 bytes
|Total: 14 bytes
|Length: 3
'Values:
|0
|0
'0
[0: String] name:
|Header: 7 bytes
|Payload: 14 bytes
|Total: 21 bytes
'Value: Hello World!
[0: List] name:
|Header: 7 bytes
|Payload: 8 bytes
|Total: 15 bytes
|Contained Type: 8 Bit Integer
|Length: 3
|
|[12: 8 Bit Integer]:
| |Payload: 1 byte
| |Total: 1 byte
| 'Value: 0
|
|[13: 8 Bit Integer]:
| |Payload: 1 byte
| |Total: 1 byte
| 'Value: 0
|
'[14: 8 Bit Integer]:
|Payload: 1 byte
|Total: 1 byte
'Value: 0
[0: Compound] name:
|Header: 7 bytes
|Payload: 25 bytes
|Total: 32 bytes
|Length: 4
|
|[7: 8 Bit Integer] name:
| |Header: 7 bytes
| |Payload: 1 byte
| |Total: 8 bytes
| 'Value: 0
|
|[15: 8 Bit Integer] name:
| |Header: 7 bytes
| |Payload: 1 byte
| |Total: 8 bytes
| 'Value: 0
|
|[23: 8 Bit Integer] name:
| |Header: 7 bytes
| |Payload: 1 byte
| |Total: 8 bytes
| 'Value: 0
|
'[31: End]:
|Header: 1 byte
|Payload: 0 bytes
'Total: 1 byte
[0: Array of 32 Bit Integers] name:
|Header: 7 bytes
|Payload: 16 bytes
|Total: 23 bytes
|Length: 3
'Values:
|0
|0
'0
[0: Array of 64 Bit Integers] name:
|Header: 7 bytes
|Payload: 28 bytes
|Total: 35 bytes
|Length: 3
'Values:
|0
|0
'0
#############################################################################
# Output for simple_nbt (what it's supposed to look like) #
#############################################################################
[0: Compound]:
|Header: 3 bytes
|Payload: 475 bytes
|Total: 478 bytes
|Length: 15
|
|[3: String] Spaces and special characters are allowed in tag names, right?:
| |Header: 65 bytes
| |Payload: 24 bytes
| |Total: 89 bytes
| 'Value: Idk. Lets find out.
|
|[92: Compound] compound:
| |Header: 11 bytes
| |Payload: 45 bytes
| |Total: 56 bytes
| |Length: 3
| |
| |[103: 32 Bit Integer] some_number:
| | |Header: 14 bytes
| | |Payload: 4 bytes
| | |Total: 18 bytes
| | 'Value: -754506943
| |
| |[121: String] some_text:
| | |Header: 12 bytes
| | |Payload: 14 bytes
| | |Total: 26 bytes
| | 'Value: eat a cookie
| |
| '[147: End]:
| |Header: 1 byte
| |Payload: 0 bytes
| 'Total: 1 byte
|
|[148: Double] double:
| |Header: 9 bytes
| |Payload: 8 bytes
| |Total: 17 bytes
| 'Value: 623593.6542742235
|
|[165: Float] float:
| |Header: 8 bytes
| |Payload: 4 bytes
| |Total: 12 bytes
| 'Value: 35.2678337097168
|
|[177: 16 Bit Integer] int16:
| |Header: 8 bytes
| |Payload: 2 bytes
| |Total: 10 bytes
| 'Value: 2000
|
|[187: 32 Bit Integer] int32:
| |Header: 8 bytes
| |Payload: 4 bytes
| |Total: 12 bytes
| 'Value: 10101010
|
|[199: Array of 32 Bit Integers] int32_array:
| |Header: 14 bytes
| |Payload: 20 bytes
| |Total: 34 bytes
| |Length: 4
| 'Values:
| |398452796
| |43259
| |2147483647
| '1634890337
|
|[233: 64 Bit Integer] int64:
| |Header: 8 bytes
| |Payload: 8 bytes
| |Total: 16 bytes
| 'Value: 810001800766
|
|[249: Array of 64 Bit Integers] int64_array:
| |Header: 14 bytes
| |Payload: 44 bytes
| |Total: 58 bytes
| |Length: 5
| 'Values:
| |239865
| |23586749
| |9223372036854775807
| |188944201329624
| '3116157694992754
|
|[307: 8 Bit Integer] int8:
| |Header: 7 bytes
| |Payload: 1 byte
| |Total: 8 bytes
| 'Value: 100
|
|[315: Array of 8 Bit Integers] int8_array:
| |Header: 13 bytes
| |Payload: 12 bytes
| |Total: 25 bytes
| |Length: 8
| 'Values:
| |113
| |53
| |119
| |98
| |84
| |100
| |245
| '50
|
|[340: List] list_int8:
| |Header: 12 bytes
| |Payload: 10 bytes
| |Total: 22 bytes
| |Contained Type: 8 Bit Integer
| |Length: 5
| |
| |[357: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 65
| |
| |[358: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 96
| |
| |[359: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 78
| |
| |[360: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 127
| |
| '[361: 8 Bit Integer]:
| |Payload: 1 byte
| |Total: 1 byte
| 'Value: -6
|
|[362: List] list_strings:
| |Header: 15 bytes
| |Payload: 77 bytes
| |Total: 92 bytes
| |Contained Type: String
| |Length: 12
| |
| |[382: String]:
| | |Payload: 8 bytes
| | |Total: 8 bytes
| | 'Value: Pacman
| |
| |[390: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: ate
| |
| |[395: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: all
| |
| |[400: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: the
| |
| |[405: String]:
| | |Payload: 6 bytes
| | |Total: 6 bytes
| | 'Value: dots
| |
| |[411: String]:
| | |Payload: 4 bytes
| | |Total: 4 bytes
| | 'Value: so
| |
| |[415: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: now
| |
| |[420: String]:
| | |Payload: 8 bytes
| | |Total: 8 bytes
| | 'Value: hes
| |
| |[428: String]:
| | |Payload: 8 bytes
| | |Total: 8 bytes
| | 'Value: coming
| |
| |[436: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: for
| |
| |[441: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: the
| |
| '[446: String]:
| |Payload: 8 bytes
| |Total: 8 bytes
| 'Value: words.
|
|[454: String] string:
| |Header: 9 bytes
| |Payload: 14 bytes
| |Total: 23 bytes
| 'Value: Hello World!
|
'[477: End]:
|Header: 1 byte
|Payload: 0 bytes
'Total: 1 byte

View File

@ -15,7 +15,20 @@
# version 3 along with this program. # version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html # If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
source scripts/lib.sh if [ "$(tr '[:upper:]' '[:lower:]' <<< $SINGLE)" = "yes" ]; then
echo "Running build script in single-threaded mode."
WAIT_ANYWAY="wait"
else
WAIT_ANYWAY=""
fi
if [ -z "$CXX" ]; then
CXX="c++"
fi
if [ -z "$CXXFLAGS" ]; then
CXXFLAGS="-std=c++20 -Wall"
fi
CXX_WITH_FLAGS="$CXX $CXXFLAGS"
# if unknown, figure out endianness # if unknown, figure out endianness
if [ -f .endianness ]; then if [ -f .endianness ]; then
@ -33,10 +46,10 @@ fi
# `.cpp` files in src/lib will be automatically picked up and compiled into # `.cpp` files in src/lib will be automatically picked up and compiled into
# dynamically linked libraries. # dynamically linked libraries.
echo ">>> Building libs..." echo "Building libs..."
create_directory bin/lib mkdir -pv bin/lib
for lib in $(find ./src/lib -name "*.cpp"); do for lib in $(find ./src/lib -name "*.cpp"); do
COMPILE_COMMAND="$CXX_WITH_FLAGS -I ./include -fPIC -shared -o $(sed -e 's/^.\/src/.\/bin/;s/cpp$/so/' <<< $lib) $lib" COMPILE_COMMAND="$CXX_WITH_FLAGS -I dependencies/tiny-utf8-4.4.3/include -fPIC -shared -o $(sed -e 's/^.\/src/.\/bin/;s/cpp$/so/' <<< $lib) $lib"
echo $COMPILE_COMMAND echo $COMPILE_COMMAND
$COMPILE_COMMAND & $COMPILE_COMMAND &
$WAIT_ANYWAY $WAIT_ANYWAY
@ -55,14 +68,12 @@ wait
# How to run a tool: specify the library path to use for the dynamic linker # How to run a tool: specify the library path to use for the dynamic linker
# when running a program # when running a program
# Example: LD_LIBRARY_PATH=bin/lib bin/tools/dumpnbt # Example: LD_LIBRARY_PATH=bin/lib bin/tools/dumpnbt
echo ">>> Building tools..." echo "Building tools..."
create_directory bin/tools mkdir -pv bin/tools
# add compile commands to this array # add compile commands to this array
COMPILE_COMMANDS=( COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/tools/dumpnbt.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -l:cli.so -o bin/tools/dumpnbt" "$CXX_WITH_FLAGS src/tools/dumpnbt.cpp -Lbin/lib -l:nbt.so -o bin/tools/dumpnbt"
"$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/hexnet.cpp -Lbin/lib -l:cli.so -l:libsockpp.so -Idependencies/sockpp-0.7.1/include -o bin/tools/hexnet"
"$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"
) )
for command in ${!COMPILE_COMMANDS[@]}; do for command in ${!COMPILE_COMMANDS[@]}; do
echo "${COMPILE_COMMANDS[command]}" echo "${COMPILE_COMMANDS[command]}"

View File

@ -15,24 +15,15 @@
# version 3 along with this program. # version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html # If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
echo ">>> Cleaning build files..." rm -rv ./bin
rm -vf .endianness
source scripts/lib.sh rm -vf resources/check_endianness
mkdir -v ./bin
remove ./bin mkdir -v ./bin/lib
remove ./include
remove .endianness
remove resources/check_endianness
create_directory ./bin
create_directory ./bin/lib
create_directory ./include
ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so bin/lib/ ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so bin/lib/
ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so.0 bin/lib/ ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so.0 bin/lib/
ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so.0.7.1 bin/lib/ ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so.0.7.1 bin/lib/
ln -vs ../dependencies/sockpp-0.7.1/include/sockpp/ ./include/ set -v
ln -vs ../dependencies/tiny-utf8-4.4.3/include/tinyutf8/ ./include/ echo -n "" > ./bin/.placeholder
create_file ./bin/.placeholder
create_file ./include/.placeholder

View File

@ -15,26 +15,7 @@
# version 3 along with this program. # version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html # If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
source scripts/lib.sh rm -rv ./dependencies
mkdir -v ./dependencies
echo ">>> Checking download cache for unneeded or corrupted files..." set -v
for file in $(ls .download_cache); do echo -n "" > ./dependencies/.placeholder
#TODO: remove if unknown shasum
if grep $file scripts/setup_project.sh >/dev/null 2>&1; then
if check_sha256 ".download_cache/$file" "$file"; then
echo -n "."
else
echo -n "!"
rm ".download_cache/$file"
fi
else
echo -n "x"
rm ".download_cache/$file"
fi
done
echo "
>>> Cleaning dependencies..."
remove ./dependencies
create_directory ./dependencies
create_file ./dependencies/.placeholder

View File

@ -1,78 +0,0 @@
#!/bin/echo You are not supposed to run this file.
# Copyright 2022, FOSS-VG Developers and Contributers
#
# 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
if [ "$(tr '[:upper:]' '[:lower:]' <<< $SINGLE)" = "yes" ]; then
echo "Single-threaded mode enabled."
WAIT_ANYWAY="wait"
else
WAIT_ANYWAY=""
fi
if [ -z "$CXX" ]; then
CXX="c++"
fi
if [ -z "$CXXFLAGS" ]; then
CXXFLAGS="-std=c++20 -Wall -Wextra"
fi
CXX_WITH_FLAGS="$CXX $CXXFLAGS"
# automatically find and use an appropriate sha256 sum command
# currently supports sha256sum and NetBSD's sha256
if command -v sha256sum > /dev/null; then
function check_sha256 {
FILE="$1"
CHECKSUM="$2"
sha256sum --check <<< "$CHECKSUM *$FILE" >/dev/null 2>&1
return $?
}
else
if command -v sha256 > /dev/null; then
function check_sha256 {
FILE="$1"
CHECKSUM="$2"
sha256 -c <<< "SHA256 ($FILE) = $CHECKSUM" >/dev/null 2>&1
return $?
}
else
echo "Could not find sha256sum or sha256."
exit 1
fi
fi
# This function exists to make the output more sensible on platforms where
# `rm -v` only prints the names of the removed things instead of a more
# comprehensible message like "removing NAME".
function remove {
echo "Removing $1..."
if [ -d "$1" ]; then
rm -rv "$1"
else
rm -v "$1"
fi
}
# some platforms dont support `mkdir -v`
function create_directory {
echo "Creating directory: $1"
mkdir -p "$1"
}
# `mkfile -v` if you will
function create_file {
echo "Creating file: $1"
touch "$1"
}

View File

@ -15,8 +15,6 @@
# version 3 along with this program. # version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html # If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
source scripts/lib.sh
echo -n "Wget or curl? " echo -n "Wget or curl? "
if command -v wget >/dev/null 2>&1; then if command -v wget >/dev/null 2>&1; then
USE_WGET=yes USE_WGET=yes
@ -34,67 +32,53 @@ fi
function download { function download {
URL="$1" URL="$1"
DESTINATION="$2" DESTINATION="$2"
SHA256SUM="$3" SHA256SUM="$3 *$2"
if [ ! -d .download_cache ]; then
echo "Cache directory missing."
create_directory .download_cache
fi
if [ -f ".download_cache/$SHA256SUM" ]; then
if check_sha256 ".download_cache/$SHA256SUM" "$SHA256SUM"; then
echo "Using locally cached file for $DESTINATION"
cp ".download_cache/$SHA256SUM" "$DESTINATION"
return 0
else
echo "Locally cached file for $DESTINATION is corrupted."
rm ".download_cache/$SHA256SUM"
download "$URL" "$DESTINATION" "$SHA256SUM"
return $?
fi
else
echo -n "Downloading $URL to $DESTINATION... " echo -n "Downloading $URL to $DESTINATION... "
if [ $USE_WGET = yes ]; then if [ $USE_WGET = yes ]; then
wget -O ".download_cache/$SHA256SUM" "$URL" >/dev/null 2>&1 wget -O "$DESTINATION" "$URL" >/dev/null 2>&1
else else
curl -L "$URL" --output ".download_cache/$SHA256SUM" >/dev/null 2>&1 curl -L "$URL" --output "$DESTINATION" >/dev/null 2>&1
fi fi
if check_sha256 ".download_cache/$SHA256SUM" "$SHA256SUM"; then if sha256sum --check <<< $SHA256SUM >/dev/null 2>&1; then
cp ".download_cache/$SHA256SUM" "$DESTINATION"
echo "done." echo "done."
return 0 return 0
else else
echo "error." echo "error."
echo "Checksum verification failed. Your download is either corrupted or the file has been altered. Removing file." echo "Checksum verification failed. Your download is either corrupted or the file has been altered."
rm ".download_cache/$SHA256SUM" rm -v "$DESTINATION"
return 1 return 1
fi fi
fi
} }
echo "Cleaning build files..."
scripts/clean.sh scripts/clean.sh
echo "Cleaning dependencies..."
scripts/clean_dependencies.sh scripts/clean_dependencies.sh
set -e # failures are not acceptable here set -e # failures are not acceptable here
create_directory dependencies/tmp mkdir -v dependencies/tmp
download https://github.com/DuffsDevice/tiny-utf8/archive/refs/tags/v4.4.3.tar.gz dependencies/tmp/tiny-utf8.tar.gz 8e3f61651909c9f3105d3501932a96aa65733127fb6e7cf94cb1b0a2dff42c8f download https://github.com/DuffsDevice/tiny-utf8/archive/refs/tags/v4.4.3.tar.gz dependencies/tmp/tiny-utf8.tar.gz 8e3f61651909c9f3105d3501932a96aa65733127fb6e7cf94cb1b0a2dff42c8f
download https://github.com/fpagliughi/sockpp/archive/refs/tags/v0.7.1.tar.gz dependencies/tmp/sockpp.tar.gz 2e023528bebbd2ac083fc91fbe6d5c4158c3336bedbcff48f594f3b28f53b940 download https://github.com/fpagliughi/sockpp/archive/refs/tags/v0.7.1.tar.gz dependencies/tmp/sockpp.tar.gz 2e023528bebbd2ac083fc91fbe6d5c4158c3336bedbcff48f594f3b28f53b940
#TODO: keep the files somewhere else as a cache and only download the ones
# we don't have or that got corrupted
#TODO: once we cache files properly, have clean_dependencies.sh prune
# unknown files from the cache (for example old versions
# of dependencies)
echo -n ">>> Extracting tiny-utf8... " echo -n "Extracting tiny-utf8... "
gzip -d dependencies/tmp/tiny-utf8.tar.gz gzip -d dependencies/tmp/tiny-utf8.tar.gz
tar -xf dependencies/tmp/tiny-utf8.tar -C dependencies tar -xf dependencies/tmp/tiny-utf8.tar -C dependencies
echo "done" echo "done."
echo -n ">>> Extracting sockpp... " echo -n "Extracting sockpp... "
gzip -d dependencies/tmp/sockpp.tar.gz gzip -d dependencies/tmp/sockpp.tar.gz
tar -xf dependencies/tmp/sockpp.tar -C dependencies tar -xf dependencies/tmp/sockpp.tar -C dependencies
echo "done" echo "done."
echo -n "Building sockpp... "
echo ">>> Building sockpp... "
pushd dependencies/sockpp-0.7.1/ >/dev/null 2>&1 pushd dependencies/sockpp-0.7.1/ >/dev/null 2>&1
cmake -Bbuild . cmake -Bbuild .
cmake --build build cmake --build build
popd >/dev/null 2>&1 popd >/dev/null 2>&1
echo "done."
echo ">>> Cleaning up..." rm -rv dependencies/tmp
remove dependencies/tmp

View File

@ -15,7 +15,13 @@
# version 3 along with this program. # version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html # If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
source scripts/lib.sh if [ -z "$CXX" ]; then
CXX="c++"
fi
if [ -z "$CXXFLAGS" ]; then
CXXFLAGS="-std=c++20 -Wall"
fi
CXX_WITH_FLAGS="$CXX $CXXFLAGS"
echo -n "Using LD_LIBRARY_PATH " echo -n "Using LD_LIBRARY_PATH "
if [ -z "$LD_LIBRARY_PATH" ]; then if [ -z "$LD_LIBRARY_PATH" ]; then
@ -25,38 +31,25 @@ else
fi fi
echo "$LD_LIBRARY_PATH" echo "$LD_LIBRARY_PATH"
echo ">>> Cleaning tests..." mkdir -pv bin/test
remove bin/test
create_directory bin/test
echo ">>> Building tests..." echo "Building tests..."
# add compile commands to this array # add compile commands to this array
COMPILE_COMMANDS=( COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/test/nbt_read_write_helpers.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_read_write_helpers" "$CXX_WITH_FLAGS src/test/nbt_helpers.cpp -Lbin/lib -l:nbt.so -o bin/test/nbt_helpers"
"$CXX_WITH_FLAGS src/test/cli_argument_parser.cpp -Lbin/lib -l:cli.so -o bin/test/cli_argument_parser" "$CXX_WITH_FLAGS src/test/cli_argument_parser.cpp -Lbin/lib -l:cli.so -o bin/test/cli_argument_parser"
"$CXX_WITH_FLAGS src/test/javacompat.cpp -I./include -Lbin/lib -l:javacompat.so -o bin/test/javacompat" "$CXX_WITH_FLAGS src/test/javacompat.cpp -Idependencies/tiny-utf8-4.4.3/include -Lbin/lib -l:javacompat.so -o bin/test/javacompat"
"$CXX_WITH_FLAGS src/test/nbt_tags.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_tags"
"$CXX_WITH_FLAGS src/test/nbt_size_helpers.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.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"
) )
for command in ${!COMPILE_COMMANDS[@]}; do for command in ${!COMPILE_COMMANDS[@]}; do
echo "${COMPILE_COMMANDS[command]}" echo "${COMPILE_COMMANDS[command]}"
${COMPILE_COMMANDS[command]} & ${COMPILE_COMMANDS[command]} &
$WAIT_ANYWAY
done done
wait wait
echo ">>> Running tests..." echo "Running tests..."
# explicitly allow commands to fail at this stage
set +e
for test in $(ls bin/test); do for test in $(ls bin/test); do
bin/test/$test bin/test/$test
done done
for test in $(ls scripts/test); do
scripts/test/$test
done

View File

@ -1,11 +0,0 @@
#!/usr/bin/env bash
echo "================================================================================"
echo -n "Testing \`arraydump\`... "
[ -x bin/tools/arraydump ] \
&& [ "$(bin/tools/arraydump resources/all_bytes)" = "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255}" ] \
&& [ "$(bin/tools/arraydump --hexadecimal resources/all_bytes)" = "{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}" ] \
&& [ "$(bin/tools/arraydump --octal resources/all_bytes)" = "{0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017, 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037, 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047, 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057, 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077, 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137, 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147, 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157, 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207, 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217, 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227, 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237, 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247, 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257, 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307, 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317, 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327, 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357, 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377}" ] \
&& [ "$(bin/tools/arraydump --binary resources/all_bytes)" = "{0b00000000, 0b00000001, 0b00000010, 0b00000011, 0b00000100, 0b00000101, 0b00000110, 0b00000111, 0b00001000, 0b00001001, 0b00001010, 0b00001011, 0b00001100, 0b00001101, 0b00001110, 0b00001111, 0b00010000, 0b00010001, 0b00010010, 0b00010011, 0b00010100, 0b00010101, 0b00010110, 0b00010111, 0b00011000, 0b00011001, 0b00011010, 0b00011011, 0b00011100, 0b00011101, 0b00011110, 0b00011111, 0b00100000, 0b00100001, 0b00100010, 0b00100011, 0b00100100, 0b00100101, 0b00100110, 0b00100111, 0b00101000, 0b00101001, 0b00101010, 0b00101011, 0b00101100, 0b00101101, 0b00101110, 0b00101111, 0b00110000, 0b00110001, 0b00110010, 0b00110011, 0b00110100, 0b00110101, 0b00110110, 0b00110111, 0b00111000, 0b00111001, 0b00111010, 0b00111011, 0b00111100, 0b00111101, 0b00111110, 0b00111111, 0b01000000, 0b01000001, 0b01000010, 0b01000011, 0b01000100, 0b01000101, 0b01000110, 0b01000111, 0b01001000, 0b01001001, 0b01001010, 0b01001011, 0b01001100, 0b01001101, 0b01001110, 0b01001111, 0b01010000, 0b01010001, 0b01010010, 0b01010011, 0b01010100, 0b01010101, 0b01010110, 0b01010111, 0b01011000, 0b01011001, 0b01011010, 0b01011011, 0b01011100, 0b01011101, 0b01011110, 0b01011111, 0b01100000, 0b01100001, 0b01100010, 0b01100011, 0b01100100, 0b01100101, 0b01100110, 0b01100111, 0b01101000, 0b01101001, 0b01101010, 0b01101011, 0b01101100, 0b01101101, 0b01101110, 0b01101111, 0b01110000, 0b01110001, 0b01110010, 0b01110011, 0b01110100, 0b01110101, 0b01110110, 0b01110111, 0b01111000, 0b01111001, 0b01111010, 0b01111011, 0b01111100, 0b01111101, 0b01111110, 0b01111111, 0b10000000, 0b10000001, 0b10000010, 0b10000011, 0b10000100, 0b10000101, 0b10000110, 0b10000111, 0b10001000, 0b10001001, 0b10001010, 0b10001011, 0b10001100, 0b10001101, 0b10001110, 0b10001111, 0b10010000, 0b10010001, 0b10010010, 0b10010011, 0b10010100, 0b10010101, 0b10010110, 0b10010111, 0b10011000, 0b10011001, 0b10011010, 0b10011011, 0b10011100, 0b10011101, 0b10011110, 0b10011111, 0b10100000, 0b10100001, 0b10100010, 0b10100011, 0b10100100, 0b10100101, 0b10100110, 0b10100111, 0b10101000, 0b10101001, 0b10101010, 0b10101011, 0b10101100, 0b10101101, 0b10101110, 0b10101111, 0b10110000, 0b10110001, 0b10110010, 0b10110011, 0b10110100, 0b10110101, 0b10110110, 0b10110111, 0b10111000, 0b10111001, 0b10111010, 0b10111011, 0b10111100, 0b10111101, 0b10111110, 0b10111111, 0b11000000, 0b11000001, 0b11000010, 0b11000011, 0b11000100, 0b11000101, 0b11000110, 0b11000111, 0b11001000, 0b11001001, 0b11001010, 0b11001011, 0b11001100, 0b11001101, 0b11001110, 0b11001111, 0b11010000, 0b11010001, 0b11010010, 0b11010011, 0b11010100, 0b11010101, 0b11010110, 0b11010111, 0b11011000, 0b11011001, 0b11011010, 0b11011011, 0b11011100, 0b11011101, 0b11011110, 0b11011111, 0b11100000, 0b11100001, 0b11100010, 0b11100011, 0b11100100, 0b11100101, 0b11100110, 0b11100111, 0b11101000, 0b11101001, 0b11101010, 0b11101011, 0b11101100, 0b11101101, 0b11101110, 0b11101111, 0b11110000, 0b11110001, 0b11110010, 0b11110011, 0b11110100, 0b11110101, 0b11110110, 0b11110111, 0b11111000, 0b11111001, 0b11111010, 0b11111011, 0b11111100, 0b11111101, 0b11111110, 0b11111111}" ] \
&& echo "PASS" \
|| echo "FAIL"
echo "================================================================================"

View File

@ -1,22 +0,0 @@
#!/usr/bin/env bash
echo "================================================================================"
echo -n "Testing \`baseconvert\`... "
[ $(baseconvert -d "0xFFFFFFFFFFFFFFFF") = "18446744073709551615" ] \
&& [ $(baseconvert -x "0xFFFFFFFFFFFFFFFF") = "0xffffffffffffffff" ] \
&& [ $(baseconvert -o "0xFFFFFFFFFFFFFFFF") = "01777777777777777777777" ] \
&& [ $(baseconvert -b "0xFFFFFFFFFFFFFFFF") = "0b1111111111111111111111111111111111111111111111111111111111111111" ] \
&& [ $(baseconvert -x "0XFFFFFFFFFFFFFFFF") = "0xffffffffffffffff" ] \
&& [ $(baseconvert -d "0") = "0" ] \
&& [ $(baseconvert -x "0") = "0x0" ] \
&& [ $(baseconvert -o "0") = "0" ] \
&& [ $(baseconvert -b "0") = "0b0" ] \
&& [ $(baseconvert -b "4") = "0b100" ] \
&& [ $(baseconvert -x "07777" ) = "0xfff" ] \
&& [ $(baseconvert -x "0o7777" ) = "0xfff" ] \
&& [ $(baseconvert -x "0O7777" ) = "0xfff" ] \
&& [ $(baseconvert -o "0b1000" ) = "010" ] \
&& [ $(baseconvert -o "0B1000" ) = "010" ] \
&& echo "PASS" \
|| echo "FAIL"
echo "================================================================================"

View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
# Copyright 2022, FOSS-VG Developers and Contributers
#
# 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
echo "################################################################################
Testing hexnet
################################################################################"
echo "Test not yet implemented."
#TODO: implement unit test after merging back with master

View File

@ -13,15 +13,9 @@
# version 3 along with this program. # version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html # If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
echo ">>> Loading shell environment for FOSS-VG development..." echo -n "Loading shell environment for FOSS-VG development... "
PROJECT_BASE_DIR="$( cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )" PROJECT_BASE_DIR="$( cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 ; pwd -P )"
echo "Project base directory is $PROJECT_BASE_DIR"
if [ -f "$PROJECT_BASE_DIR/.localenv.bashrc" ]; then
source "$PROJECT_BASE_DIR/.localenv.bashrc"
echo "Applied local environment customizations."
fi
alias clean="pushd \"$PROJECT_BASE_DIR\" >/dev/null 2>&1; scripts/clean.sh; popd >/dev/null 2>&1" alias clean="pushd \"$PROJECT_BASE_DIR\" >/dev/null 2>&1; scripts/clean.sh; popd >/dev/null 2>&1"
alias clean_dependencies="pushd \"$PROJECT_BASE_DIR\" >/dev/null 2>&1; scripts/clean_dependencies.sh; popd >/dev/null 2>&1" alias clean_dependencies="pushd \"$PROJECT_BASE_DIR\" >/dev/null 2>&1; scripts/clean_dependencies.sh; popd >/dev/null 2>&1"
@ -39,75 +33,13 @@ function build {
popd >/dev/null 2>&1 popd >/dev/null 2>&1
} }
echo "Added aliases and functions."
export PATH="$PROJECT_BASE_DIR/bin/tools:$PROJECT_BASE_DIR/scripts/tools:$PATH"
echo "PATH is $PATH"
if [ -z "$LD_LIBRARY_PATH" ]; then if [ -z "$LD_LIBRARY_PATH" ]; then
export LD_LIBRARY_PATH="$PROJECT_BASE_DIR"/bin/lib export LD_LIBRARY_PATH="$PROJECT_BASE_DIR"/bin/lib
else else
export LD_LIBRARY_PATH="$PROJECT_BASE_DIR"/bin/lib:"$LD_LIBRARY_PATH" export LD_LIBRARY_PATH="$PROJECT_BASE_DIR"/bin/lib:"$LD_LIBRARY_PATH"
fi fi
echo "LD_LIBRARY_PATH is $LD_LIBRARY_PATH"
echo ">>> Checking for dependencies..." unset PROJECT_BASE_DIR
MISSING_DEPS=0
if command -v wget > /dev/null 2>&1; then echo "done."
true
else
if command -v curl > /dev/null 2>&1; then
true
else
echo "WARNING: \`wget\` or \`curl\` is needed to download some additional dependencies."
MISSING_DEPS=1
fi
fi
if command -v sha256sum > /dev/null 2>&1; then
true
else
if command -v sha256 > /dev/null 2>&1; then
true
else
echo "WARNING: Coreutils \`sha256sum\` or a \`sha256\` as found on NetBSD is needed to verify downloaded files."
MISSING_DEPS=1
fi
fi
if command -v gzip > /dev/null 2>&1; then
true
else
echo "WARNING: \`gzip\` is needed to decompress downloaded dependencies."
MISSING_DEPS=1
fi
if command -v tar > /dev/null 2>&1; then
true
else
echo "WARNING: \`tar\` is needed to unpack downloaded dependencies."
MISSING_DEPS=1
fi
if command -v cmake > /dev/null 2>&1; then
true
else
echo "WARNING: \`cmake\` is needed to build downloaded dependencies."
MISSING_DEPS=1
fi
if [ -z "$CXX" ]; then
if command -v c++ > /dev/null 2>&1; then
true
else
echo "WARNING: Your system does not appear to have a standard C++ compiler. If you have a C++ compiler installed, but not linked to \`c++\` on your PATH, set it manually using \`CXX=/path/to/your/compiler\`."
MISSING_DEPS=1
fi
fi
if [ "$MISSING_DEPS" -eq 0 ]; then
echo "All set."
fi
unset MISSING_DEPS

View File

@ -1,8 +1,5 @@
// Copyright 2022, FOSS-VG Developers and Contributers // Copyright 2022, FOSS-VG Developers and Contributers
// //
// Author(s):
// BodgeMaster, Shwoomple
//
// This program is free software: you can redistribute it and/or modify it // 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 // under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3. // by the Free Software Foundation, version 3.
@ -16,12 +13,13 @@
// version 3 along with this program. // version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include "cli.h++"
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include "cli.hpp" #include "error.h++"
#include "error.hpp"
namespace CLI { namespace CLI {
Flag::Flag() { Flag::Flag() {
@ -34,10 +32,10 @@ namespace CLI {
this->present = false; this->present = false;
} }
Option::Option() { UnpositionalArgument::UnpositionalArgument() {
this->present = false; this->present = false;
} }
Option::Option(char shortName, std::string longName, std::string placeholder, std::string description) { UnpositionalArgument::UnpositionalArgument(char shortName, std::string longName, std::string placeholder, std::string description) {
this->shortName = shortName; this->shortName = shortName;
this->longName = longName; this->longName = longName;
this->description = description; this->description = description;
@ -45,22 +43,23 @@ namespace CLI {
this->present = false; this->present = false;
} }
Argument::Argument() { PositionalArgument::PositionalArgument() {
this->present = false; this->present = false;
} }
Argument::Argument(std::string placeholder, std::string description) { PositionalArgument::PositionalArgument(std::string placeholder, std::string description) {
this->description = description; this->description = description;
this->placeholder = placeholder; this->placeholder = placeholder;
this->present = false; this->present = false;
} }
// using int here bc that's how main() is defined // using int here bc that's how main() is defined
ArgumentsParser::ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments) { ArgumentsParser::ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments) {
this->wrongUsage = false; this->wrongUsage = false;
this->wrongUsageMessages = std::vector<std::string>(); this->wrongUsageMessages = std::vector<std::string>();
this->programName = std::string(argv[0]); this->programName = std::string(argv[0]);
this->arguments = arguments; this->positionalArguments = positionalArguments;
// create lookup tables for all flags and options by their names // create lookup tables for all flags and unpositional arguments
// by their names
this->flagsByShortName = std::map<char, Flag*>(); this->flagsByShortName = std::map<char, Flag*>();
this->flagsByLongName = std::map<std::string, Flag*>(); this->flagsByLongName = std::map<std::string, Flag*>();
for (Flag flag: flags) { for (Flag flag: flags) {
@ -69,25 +68,25 @@ namespace CLI {
this->flagsByShortName[flag.shortName] = flagPointer; this->flagsByShortName[flag.shortName] = flagPointer;
this->flagsByLongName[flag.longName] = flagPointer; this->flagsByLongName[flag.longName] = flagPointer;
} }
this->optionsByShortName = std::map<char, Option*>(); this->argumentsByShortName = std::map<char, UnpositionalArgument*>();
this->optionsByLongName = std::map<std::string, Option*>(); this->argumentsByLongName = std::map<std::string, UnpositionalArgument*>();
for (Option option: options) { for (UnpositionalArgument unpositionalArgument: unpositionalArguments) {
Option* optionPointer = new Option(); UnpositionalArgument* argumentPointer = new UnpositionalArgument();
*optionPointer = option; *argumentPointer = unpositionalArgument;
this->optionsByShortName[option.shortName] = optionPointer; this->argumentsByShortName[unpositionalArgument.shortName] = argumentPointer;
this->optionsByLongName[option.longName] = optionPointer; this->argumentsByLongName[unpositionalArgument.longName] = argumentPointer;
} }
Option* optionWaitingForValue = nullptr; UnpositionalArgument* argumentWaitingForValue = nullptr;
std::vector<CLI::Argument>::size_type argumentCounter = 0; std::vector<CLI::PositionalArgument>::size_type positionalArgumentCounter = 0;
for (int i=1; i<argc; i++) { for (int i=1; i<argc; i++) {
std::string argument(argv[i]); std::string argument(argv[i]);
if (argument[0]=='-') { if (argument[0]=='-') {
// do we have unfinished business? // do we have unfinished business?
if (optionWaitingForValue!=nullptr) { if (argumentWaitingForValue!=nullptr) {
this->wrongUsage = true; this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+optionWaitingForValue->longName); this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+argumentWaitingForValue->longName);
optionWaitingForValue = nullptr; argumentWaitingForValue = nullptr;
} }
// long name or short name? // long name or short name?
if (argument[1]=='-') { if (argument[1]=='-') {
@ -99,19 +98,15 @@ namespace CLI {
auto position = argument.find("="); auto position = argument.find("=");
if (position==std::string::npos) { if (position==std::string::npos) {
// no =value // no =value
//is option or flag? //is argument or flag?
std::string argumentName = argument.substr(2,argument.length()-2); std::string argumentName = argument.substr(2,argument.length()-2);
if (flagsByLongName.contains(argumentName)) { if (flagsByLongName.contains(argumentName)) {
// flag // flag
flagsByLongName[argumentName]->present = true; flagsByLongName[argumentName]->present = true;
} else if (optionsByLongName.contains(argumentName)) { } else if (argumentsByLongName.contains(argumentName)) {
// option // unpositional argument
optionsByLongName[argumentName]->present = true; argumentsByLongName[argumentName]->present = true;
optionWaitingForValue = optionsByLongName[argumentName]; argumentWaitingForValue = argumentsByLongName[argumentName];
if (i+1 == argc) {
this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+argumentName);
}
} else { } else {
this->wrongUsage = true; this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Unknown argument or flag: ")+argument); this->wrongUsageMessages.push_back(std::string("Unknown argument or flag: ")+argument);
@ -120,9 +115,9 @@ namespace CLI {
// has =value // has =value
std::string value = argument.substr(position+1, argument.length()-position-1); std::string value = argument.substr(position+1, argument.length()-position-1);
std::string argumentName = argument.substr(2, position-2); std::string argumentName = argument.substr(2, position-2);
if (optionsByLongName.contains(argumentName)) { if (argumentsByLongName.contains(argumentName)) {
optionsByLongName[argumentName]->present = true; argumentsByLongName[argumentName]->present = true;
optionsByLongName[argumentName]->value = value; argumentsByLongName[argumentName]->value = value;
} else { } else {
this->wrongUsage = true; this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Unknown argument (or it's a flag that doesn't take a value): ")+argument); this->wrongUsageMessages.push_back(std::string("Unknown argument (or it's a flag that doesn't take a value): ")+argument);
@ -134,29 +129,23 @@ namespace CLI {
// length is defined as // length is defined as
// (std::__cxx11::basic_string<char>::size_type ?) // (std::__cxx11::basic_string<char>::size_type ?)
// starting at 1 because 0 is '-' // starting at 1 because 0 is '-'
for (int j=1; j<(int) argument.length(); j++) { for (int i=1; i<(int) argument.length(); i++) {
// is option or flag? //is argument or flag?
if (flagsByShortName.contains(argument[j])) { if (flagsByShortName.contains(argument[i])) {
// flag flagsByShortName[argument[i]]->present = true;
flagsByShortName[argument[j]]->present = true; } else if (argumentsByShortName.contains(argument[i])) {
} else if (optionsByShortName.contains(argument[j])) { argumentsByShortName[argument[i]]->present = true;
// option
optionsByShortName[argument[j]]->present = true;
//FIXME: see above //FIXME: see above
if (j+1==(int) argument.length()) { if (i+1==(int) argument.length()) {
optionWaitingForValue = optionsByShortName[argument[j]]; argumentWaitingForValue = argumentsByShortName[argument[i]];
if (i+1 == argc) {
this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+this->optionsByShortName[argument[j]]->longName);
}
} else { } else {
//assume the rest of the argv is a concatenated argument value //assume the rest of the argv is a concatenated argument value
optionsByShortName[argument[j]]->value = argument.substr(j+1, argument.length()-j-1); argumentsByShortName[argument[i]]->value = argument.substr(i+1, argument.length()-i-1);
break; break;
} }
} else { } else {
this->wrongUsage = true; this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Unknown argument or flag(s): ")+argument.substr(j, argument.length()-j)); this->wrongUsageMessages.push_back(std::string("Unknown argument or flag(s): ")+argument.substr(i, argument.length()-i));
// err on the side of caution to ensure that // err on the side of caution to ensure that
// no unwanted options get activated on programs // no unwanted options get activated on programs
// that deal gracefully with unrecognized command // that deal gracefully with unrecognized command
@ -166,48 +155,39 @@ namespace CLI {
} }
} }
} else { } else {
// argument or value for option? // positional argument or value for unpositional arg?
if (optionWaitingForValue==nullptr) { if (argumentWaitingForValue==nullptr) {
// argument // positional argument
if (argumentCounter < this->arguments.size()) { if (positionalArgumentCounter < this->positionalArguments.size()) {
this->arguments.at(argumentCounter).present = true; this->positionalArguments.at(positionalArgumentCounter).present = true;
this->arguments.at(argumentCounter).value = argument; this->positionalArguments.at(positionalArgumentCounter).value = argument;
} else { } else {
this->wrongUsage = true; this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Too many arguments! Unexpected encounter of: ")+argument); this->wrongUsageMessages.push_back(std::string("Too many positional arguments. Unexpected encounter of: ")+argument);
} }
argumentCounter++; positionalArgumentCounter++;
} else { } else {
// value for option // value for unpositional argument
optionWaitingForValue->value = argument; argumentWaitingForValue->value = argument;
optionWaitingForValue = nullptr; argumentWaitingForValue = nullptr;
} }
} }
} }
for (Argument const& argument: this->arguments) { for (PositionalArgument const& positionalArgument: this->positionalArguments) {
if (!argument.present) { if (!positionalArgument.present) {
this->wrongUsage = true; this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Too few arguments! Missing: ")+argument.placeholder); this->wrongUsageMessages.push_back(std::string("Too few positional arguments! Missing: ")+positionalArgument.placeholder);
} }
} }
} }
ArgumentsParser::ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments, std::string description): ArgumentsParser::ArgumentsParser(argc, argv, flags, options, arguments) {
this->description = description;
}
ArgumentsParser::ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments, std::string description, std::string additionalInfo): ArgumentsParser::ArgumentsParser(argc, argv, flags, options, arguments) {
this->description = description;
this->additionalInfo = additionalInfo;
}
ArgumentsParser::~ArgumentsParser() { ArgumentsParser::~ArgumentsParser() {
//TODO: check that this actually runs //TODO: check that this actually runs
for (auto const& [shortName, flag]: this->flagsByShortName) { for (auto const& [shortName, flag]: this->flagsByShortName) {
delete flag; delete flag;
} }
for (auto const& [shortName, option]: this->optionsByShortName) { for (auto const& [shortName, unpositionalArgument]: this->argumentsByShortName) {
delete option; delete unpositionalArgument;
} }
} }
@ -231,100 +211,35 @@ namespace CLI {
else return ErrorOr<bool> (false, ErrorCodes::NOT_PRESENT, false); else return ErrorOr<bool> (false, ErrorCodes::NOT_PRESENT, false);
} }
ErrorOr<std::string> ArgumentsParser::getArgument(std::vector<CLI::Argument>::size_type position){ ErrorOr<std::string> ArgumentsParser::getPositionalArgument(std::vector<CLI::PositionalArgument>::size_type position){
if (position >= this->arguments.size()) return ErrorOr<std::string>(true, ErrorCodes::OUT_OF_RANGE, std::string("")); if (position >= this->positionalArguments.size()) return ErrorOr<std::string>(true, ErrorCodes::OUT_OF_RANGE, std::string(""));
if (this->wrongUsage) { if (this->wrongUsage) {
if (this->arguments.at(position).present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->arguments.at(position).value); if (this->positionalArguments.at(position).present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->positionalArguments.at(position).value);
else return ErrorOr<std::string>(true, ErrorCodes::NOT_PRESENT, std::string("")); else return ErrorOr<std::string>(true, ErrorCodes::NOT_PRESENT, std::string(""));
} }
return ErrorOr<std::string>(this->arguments.at(position).value); return ErrorOr<std::string>(this->positionalArguments.at(position).value);
} }
ErrorOr<std::string> ArgumentsParser::getOption(char shortName) { ErrorOr<std::string> ArgumentsParser::getUnpositionalArgument(char shortName) {
if (!this->optionsByShortName.contains(shortName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string("")); if (!this->argumentsByShortName.contains(shortName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string(""));
if (this->wrongUsage) { if (this->wrongUsage) {
if (this->optionsByShortName[shortName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->optionsByShortName[shortName]->value); if (this->argumentsByShortName[shortName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->argumentsByShortName[shortName]->value);
else return ErrorOr<std::string>(true, ErrorCodes::NOT_PRESENT, std::string("")); else return ErrorOr<std::string>(true, ErrorCodes::NOT_PRESENT, std::string(""));
} }
if (this->optionsByShortName[shortName]->present) return ErrorOr<std::string>(this->optionsByShortName[shortName]->value); if (this->argumentsByShortName[shortName]->present) return ErrorOr<std::string>(this->argumentsByShortName[shortName]->value);
// argument is not present, but this is not an error -> false, NOT_PRESENT, "" // argument is not present, but this is not an error -> false, NOT_PRESENT, ""
else return ErrorOr<std::string>(false, ErrorCodes::NOT_PRESENT, std::string("")); else return ErrorOr<std::string>(false, ErrorCodes::NOT_PRESENT, std::string(""));
} }
ErrorOr<std::string> ArgumentsParser::getOption(std::string longName) { ErrorOr<std::string> ArgumentsParser::getUnpositionalArgument(std::string longName) {
if (!this->optionsByLongName.contains(longName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string("")); if (!this->argumentsByLongName.contains(longName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string(""));
if (this->wrongUsage) { if (this->wrongUsage) {
if (this->optionsByLongName[longName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->optionsByLongName[longName]->value); if (this->argumentsByLongName[longName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->argumentsByLongName[longName]->value);
else return ErrorOr<std::string>(true, ErrorCodes::NOT_PRESENT, std::string("")); else return ErrorOr<std::string>(true, ErrorCodes::NOT_PRESENT, std::string(""));
} }
if (this->optionsByLongName[longName]->present) return ErrorOr<std::string>(this->optionsByLongName[longName]->value); if (this->argumentsByLongName[longName]->present) return ErrorOr<std::string>(this->argumentsByLongName[longName]->value);
// argument is not present, but this is not an error -> false, NOT_PRESENT, "" // argument is not present, but this is not an error -> false, NOT_PRESENT, ""
else return ErrorOr<std::string>(false, ErrorCodes::NOT_PRESENT, std::string("")); else return ErrorOr<std::string>(false, ErrorCodes::NOT_PRESENT, std::string(""));
} }
//std::string ArgumentsParser::getUsage();
std::string ArgumentsParser::getUsage(){
std::string usageString = "";
if (this->description != "") {
usageString += "Help: " + this->programName + "\n\n\t" + this->description + "\n\n";
}
usageString += "Usage: " + this->programName + " ";
if(!this->flagsByShortName.empty()){
usageString += "[-";
}
for(const auto& [key, value]: this->flagsByShortName){
usageString.push_back(key);
}
if(!this->flagsByShortName.empty()){
usageString += "] ";
}
for(const auto& [key, value]: this->optionsByShortName){
usageString += "[-";
usageString.push_back(key);
usageString += " " + value->placeholder + "] ";
}
for(const auto& argument: this->arguments){
usageString += argument.placeholder + " ";
}
usageString.push_back('\n');
if(!this->flagsByShortName.empty()){
usageString += "\nFlags:\n";
for(const auto& [key, value]: this->flagsByShortName){
usageString += "\t-";
usageString.push_back(key);
usageString += ", --" + value->longName + "\n\t\t" + value->description + "\n";
}
}
if(!this->optionsByShortName.empty()){
usageString += "\nOptions:\n";
for(const auto& [key, value]: this->optionsByShortName){
usageString += "\t-";
usageString.push_back(key);
usageString += " " + value->placeholder + ", --" + value->longName + "=" + value->placeholder + "\n\t\t" + value->description + "\n";
}
}
if(!this->arguments.empty()){
usageString += "\nArguments:\n";
for(const auto& argument: this->arguments){
usageString += "\t" + argument.placeholder + "\n\t\t" + argument.description + "\n";
}
}
if (this->additionalInfo != "") {
usageString += "\nAdditional Info:\n\n\t" + this->additionalInfo + "\n";
}
return usageString;
}
} }

View File

@ -19,7 +19,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include "error.hpp" #include "error.h++"
namespace CLI { namespace CLI {
@ -35,7 +35,7 @@ namespace CLI {
Flag(char shortName, std::string longName, std::string description); Flag(char shortName, std::string longName, std::string description);
}; };
struct Option { struct UnpositionalArgument {
char shortName; char shortName;
std::string longName; std::string longName;
// used for automatic usage generation // used for automatic usage generation
@ -45,11 +45,11 @@ namespace CLI {
bool present; bool present;
std::string value; std::string value;
Option(); UnpositionalArgument();
Option(char shortName, std::string longName, std::string placeholder, std::string description); UnpositionalArgument(char shortName, std::string longName, std::string placeholder, std::string description);
}; };
struct Argument { struct PositionalArgument {
// used for automatic usage generation // used for automatic usage generation
std::string description; std::string description;
std::string placeholder; // the "HOST" in "ping [-c <COUNT>] <HOST>" std::string placeholder; // the "HOST" in "ping [-c <COUNT>] <HOST>"
@ -57,19 +57,17 @@ namespace CLI {
bool present; bool present;
std::string value; std::string value;
Argument(); PositionalArgument();
Argument(std::string placeholder, std::string description); PositionalArgument(std::string placeholder, std::string description);
}; };
class ArgumentsParser { class ArgumentsParser {
private: private:
std::map<char, Flag*> flagsByShortName; std::map<char, Flag*> flagsByShortName;
std::map<std::string, Flag*> flagsByLongName; std::map<std::string, Flag*> flagsByLongName;
std::map<char, Option*> optionsByShortName; std::map<char, UnpositionalArgument*> argumentsByShortName;
std::map<std::string, Option*> optionsByLongName; std::map<std::string, UnpositionalArgument*> argumentsByLongName;
std::vector<Argument> arguments; std::vector<PositionalArgument> positionalArguments;
std::string description;
std::string additionalInfo;
public: public:
std::string programName; std::string programName;
@ -77,16 +75,14 @@ namespace CLI {
std::vector<std::string> wrongUsageMessages; std::vector<std::string> wrongUsageMessages;
// using int here bc that's how main() is defined // using int here bc that's how main() is defined
ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments); ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments);
ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments, std::string description);
ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments, std::string description, std::string additionalInfo);
~ArgumentsParser(); ~ArgumentsParser();
ErrorOr<bool> getFlag(char shortName); ErrorOr<bool> getFlag(char shortName);
ErrorOr<bool> getFlag(std::string longName); ErrorOr<bool> getFlag(std::string longName);
ErrorOr<std::string> getArgument(std::vector<CLI::Argument>::size_type position); ErrorOr<std::string> getPositionalArgument(std::vector<CLI::PositionalArgument>::size_type position);
ErrorOr<std::string> getOption(char shortName); ErrorOr<std::string> getUnpositionalArgument(char shortName);
ErrorOr<std::string> getOption(std::string longName); ErrorOr<std::string> getUnpositionalArgument(std::string longName);
std::string getUsage(); std::string getUsage();
}; };

View File

@ -23,43 +23,37 @@ struct ErrorOr {
uint8_t errorCode; uint8_t errorCode;
T value; T value;
ErrorOr() { ErrorOr<T>();
ErrorOr<T>(T);
ErrorOr<T>(bool, uint8_t);
ErrorOr<T>(bool, uint8_t, T);
};
template <typename T>
ErrorOr<T>::ErrorOr() {
this->isError = false; this->isError = false;
this->errorCode = 0; this->errorCode = 0;
} }
ErrorOr(T value) { template <typename T>
ErrorOr<T>::ErrorOr(T value) {
this->isError = false; this->isError = false;
this->errorCode = 0; this->errorCode = 0;
this->value = value; this->value = value;
} }
ErrorOr(bool isError, uint8_t errorCode) { template <typename T>
ErrorOr<T>::ErrorOr(bool isError, uint8_t errorCode) {
this->isError = isError; this->isError = isError;
this->errorCode = errorCode; this->errorCode = errorCode;
} }
ErrorOr(bool isError, uint8_t errorCode, T value) { template <typename T>
ErrorOr<T>::ErrorOr(bool isError, uint8_t errorCode, T value) {
this->isError = isError; this->isError = isError;
this->errorCode = errorCode; this->errorCode = errorCode;
this->value = value; this->value = value;
} }
};
struct ErrorOrVoid {
bool isError;
uint8_t errorCode;
ErrorOrVoid() {
this->isError = false;
this->errorCode = 0;
}
ErrorOrVoid(bool isError, uint8_t errorCode) {
this->isError = isError;
this->errorCode = errorCode;
}
};
namespace ErrorCodes { namespace ErrorCodes {
// These are all arbitrary values used to assign error codes to different // These are all arbitrary values used to assign error codes to different
@ -70,10 +64,13 @@ namespace ErrorCodes {
// Ahh yes, very useful. // Ahh yes, very useful.
const uint8_t SUCCESS = 0; const uint8_t SUCCESS = 0;
// IndexOutOfRangeException equivalent
const uint8_t OUT_OF_RANGE = 1; const uint8_t OUT_OF_RANGE = 1;
// like OUT_OF_RANGE but when going out of bounds in a non-predetermined way
// when going out of bounds in a non-predetermined way
const uint8_t OVERRUN = 2; const uint8_t OVERRUN = 2;
// when checking for presence of something, for example CLI arguments
const uint8_t NOT_PRESENT = 3; const uint8_t NOT_PRESENT = 3;
const uint8_t WRONG_USAGE = 4; const uint8_t WRONG_USAGE = 4;
@ -81,20 +78,6 @@ namespace ErrorCodes {
// when dealing with maps // when dealing with maps
const uint8_t UNKNOWN_KEY = 5; const uint8_t UNKNOWN_KEY = 5;
//mismatched size in java strings
const uint8_t MISMATCHEDSIZE = 6;
const uint8_t NOT_YET_KNOWN = 7;
const uint8_t INVALID_TYPE = 8;
const uint8_t FILE_NOT_OPEN = 9;
const uint8_t FILE_NOT_FOUND = 10;
// when performing an operation that would technically be valid but must
// never be performed (like deleting an end tag from an NBT compound)
const uint8_t NOT_ALLOWED = 11;
const uint8_t UNIMPLEMENTED = 254; const uint8_t UNIMPLEMENTED = 254;
const uint8_t UNKNOWN = 255; const uint8_t UNKNOWN = 255;

View File

@ -1,274 +0,0 @@
// 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 <fstream>
#include <filesystem>
#include <exception>
#include <vector>
#include <string>
#include "tinyutf8/tinyutf8.h"
#include "file.hpp"
File::File(std::string path, char mode, uint64_t cursorPosition): mode(mode), path(path), cursorPosition(cursorPosition){
std::filesystem::path filePath = path;
if(this->mode == 'w' || (this->mode == 'a' && !std::filesystem::exists(filePath))){
this->size = ErrorOr<uint64_t>(0);
}else{
this->size = ErrorOr<uint64_t>(std::filesystem::file_size(filePath));
}
this->open();
}
File::~File() {
if (this->isOpen) {
this->fileStream.close();
}
}
void File::open(){
switch(this->mode){
case 'w':
this->fileStream.open(this->path, std::fstream::out | std::fstream::binary);
break;
case 'm':
this->fileStream.open(this->path, std::fstream::out | std::fstream::in | std::fstream::binary);
break;
case 'r':
this->fileStream.open(this->path, std::fstream::in | std::fstream::binary);
break;
case 'a':
this->fileStream.open(this->path, std::fstream::app | std::fstream::binary);
break;
default:
break;
}
std::filesystem::path filePath = this->path;
this->size = ErrorOr<uint64_t>(std::filesystem::file_size(filePath));
this->isOpen = this->fileStream.is_open();
}
void File::close(){
this->fileStream.close();
this->isOpen = false;
}
bool File::eof() {
return !this->size.isError && this->cursorPosition >= this->size.value;
}
ErrorOr<uint8_t> File::readByte(){
if (!this->isOpen) {
return ErrorOr<uint8_t>(true, ErrorCodes::FILE_NOT_OPEN);
}
if (!this->size.isError && this->cursorPosition >= this->size.value) {
return ErrorOr<uint8_t>(true, ErrorCodes::OVERRUN);
}
uint8_t* nextPointer = new uint8_t;
uint8_t nextByte;
bool failure = false;
try {
this->fileStream.seekg(this->cursorPosition);
this->fileStream.read(reinterpret_cast<char*>(nextPointer), 1);
nextByte = *nextPointer;
this->cursorPosition++;
} catch (std::exception& e) {
failure = true;
}
delete nextPointer;
return failure? ErrorOr<uint8_t>(true, ErrorCodes::UNKNOWN) : ErrorOr<uint8_t>(nextByte);
}
ErrorOr<std::vector<uint8_t>> File::read(uint64_t bytes){
if (!this->isOpen) {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::FILE_NOT_OPEN);
}
if (!this->size.isError && this->cursorPosition >= this->size.value+bytes) {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::OVERRUN);
}
uint8_t* buffer = new uint8_t[bytes];
std::vector<uint8_t> data;
bool failure = false;
try {
this->fileStream.seekg(this->cursorPosition);
this->fileStream.read(reinterpret_cast<char*>(buffer), bytes);
data = std::vector<uint8_t>(buffer, buffer+bytes);
this->cursorPosition += bytes;
} catch (std::exception& e) {
failure = true;
}
delete[] buffer;
return failure? ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::UNKNOWN) : ErrorOr<std::vector<uint8_t>>(data);
}
ErrorOr<tiny_utf8::string> File::readString(uint64_t bytes){
ErrorOr<std::vector<uint8_t>> data = this->read(bytes);
if(data.isError){
return ErrorOr<tiny_utf8::string>(true, data.errorCode);
}
std::string s;
for(auto byte: data.value){
s.push_back(byte);
}
return ErrorOr<tiny_utf8::string>(tiny_utf8::string(s));
}
ErrorOrVoid File::writeByte(uint8_t byte){
bool failure = false;
try{
this->fileStream << byte;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOrVoid(true, ErrorCodes::UNKNOWN) : ErrorOrVoid();
}
ErrorOrVoid File::write(std::vector<uint8_t> data){
bool failure = false;
try{
this->fileStream << std::string(data.begin(), data.end());
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOrVoid(true, ErrorCodes::UNKNOWN) : ErrorOrVoid();
}
ErrorOrVoid File::writeString(tiny_utf8::string data){
bool failure = false;
try{
this->fileStream << data;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOrVoid(true, ErrorCodes::UNKNOWN) : ErrorOrVoid();
}
// Naive implementation of insertByte
ErrorOrVoid File::insertByte(uint8_t byte){
bool failure = false;
try{
uint8_t* buffer = new uint8_t[this->size.value];
std::vector<uint8_t> readData;
this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value);
readData = std::vector<uint8_t>(buffer, buffer+this->size.value);
readData.insert(readData.begin()+this->cursorPosition, byte);
this->fileStream.seekg(0);
this->write(readData);
this->cursorPosition++;
delete buffer;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOrVoid(true, ErrorCodes::UNKNOWN) : ErrorOrVoid();
}
ErrorOrVoid File::insert(std::vector<uint8_t> data){
bool failure = false;
try{
uint8_t* buffer = new uint8_t[this->size.value];
std::vector<uint8_t> readData;
this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value);
readData = std::vector<uint8_t>(buffer, buffer+this->size.value);
readData.insert(readData.begin()+this->cursorPosition, data.begin(), data.end());
this->fileStream.seekg(0);
this->write(readData);
this->cursorPosition += data.size();
delete buffer;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOrVoid(true, ErrorCodes::UNKNOWN) : ErrorOrVoid();
}
ErrorOrVoid File::insertString(tiny_utf8::string string){
bool failure = false;
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);
readData.insert(readData.begin()+this->cursorPosition, string);
this->fileStream.seekg(0);
//TODO: fix hack. tinyutf8 appends "_utf-8" when readData is assigned: tiny_utf8::string((char *) buffer);
this->writeString(readData.substr(0, readData.find("_utf-8")));
this->cursorPosition += string.size();
delete buffer;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOrVoid(true, ErrorCodes::UNKNOWN) : ErrorOrVoid();
}
ErrorOr<uint8_t> File::cutByte(){
bool failure = false;
uint8_t byte;
try{
uint8_t* buffer = new uint8_t[this->size.value];
std::vector<uint8_t> readData;
this->fileStream.read(reinterpret_cast<char*>(buffer), this->size.value);
readData = std::vector<uint8_t>(buffer, buffer+this->size.value);
byte = readData[this->cursorPosition];
readData.erase(readData.begin() + this->cursorPosition);
this->fileStream.seekg(0);
this->write(readData);
this->cursorPosition++;
delete buffer;
}catch(std::exception& e){
failure = true;
}
return failure ? ErrorOr<uint8_t>(true, ErrorCodes::UNKNOWN) : ErrorOr<uint8_t>(byte);
}
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);
}
//TODO: check access perms
return ErrorOr<File*>(new File(path, mode, startPosition));
}

View File

@ -1,92 +0,0 @@
// 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 <cstdint>
#include <string>
#include <fstream>
#include <vector>
#include <tinyutf8/tinyutf8.h>
#include "error.hpp"
class File {
private:
//TODO: add other necessary internals to this section as needed
char mode;
std::fstream fileStream;
//TODO: add other necessary details to the constructor as needed
// For example, the fstream (or whatever mechanism is used) needs
// to be handed over from File::open() to the File object.
//
// Remember to add a destructor to the public section if pointers
// are to be used.
File(std::string path, char mode, uint64_t cursorPosition);
public:
bool isOpen;
std::string path;
uint64_t cursorPosition;
// may be error if not a regular file or size cannot be determined
ErrorOr<uint64_t> size;
File() {};
~File();
void open();
void close();
bool eof();
// only applicable to read and edit modes
// moves the cursor to the right of the read section
ErrorOr<uint8_t> readByte();
ErrorOr<std::vector<uint8_t>> read(uint64_t bytes);
ErrorOr<tiny_utf8::string> readString(uint64_t bytes);
// only applicable to write, modify, append, and edit modes
// in modify and edit modes, overwrite whatever is at the
// cursor position if there is anything there
// moves the cursor to the right of the written section
ErrorOrVoid writeByte(uint8_t byte);
ErrorOrVoid write(std::vector<uint8_t> data);
ErrorOrVoid writeString(tiny_utf8::string string);
// only applicable to modify and edit modes
// insert at cursor position and move other contents to the right
// moves the cursor to the right of the inserted section
ErrorOrVoid insertByte(uint8_t byte);
ErrorOrVoid insert(std::vector<uint8_t> data);
ErrorOrVoid insertString(tiny_utf8::string string);
// only applicable to edit mode
// return the cut section, remove cut section from file
// moves the cursor to the right of where it happened
ErrorOr<uint8_t> cutByte();
ErrorOr<std::vector<uint8_t>> cut(uint64_t length);
ErrorOr<tiny_utf8::string> cutString(uint64_t length);
// modes:
// r (read)
// w (write: overwrite file deleting its previous contents)
// a (append: write to end of file)
// m (modify: write to file modifying its previous contents)
//
// A startPosition of 0xFFFFFFFF is considered to be the end of
// the file whatever its size.
static ErrorOr<File*> open(std::string path, char mode, uint64_t startPosition=0);
};

View File

@ -15,30 +15,18 @@
#include <tinyutf8/tinyutf8.h> #include <tinyutf8/tinyutf8.h>
#include <string> #include <string>
#include "error.hpp" #include "error.h++"
#include "javacompat.hpp"
#include "../../.endianness"
#ifdef FOSSVG_ENDIAN_BIG_WORD #include "javacompat.h++"
#error "Honeywell-316-style endianness is not supported. If you feel like it should, feel free to participate in the project to maintain it."
#endif
#ifdef FOSSVG_ENDIAN_LITTLE_WORD
#error "PDP-11-style endianness is not supported. If you feel like it should, feel free to participate in the project to maintain it."
#endif
#ifdef FOSSVG_ENDIAN_UNKNOWN
#error "The endianness of your system could not be determined. Please set it manually. FOSS-VG is currently implemented using some endian-specific functions."
#endif
namespace JavaCompat { namespace JavaCompat {
ErrorOr<tiny_utf8::string> importJavaString(uint8_t data[], uint16_t size) { //FIXME: contrary to what I said, we need to explicitly pass the data
// size because files could have been tampered with or corrupted
tiny_utf8::string importJavaString(uint8_t data[]) {
std::string stdString; std::string stdString;
uint16_t encodedSize = static_cast<uint16_t>(data[0])<<8 | static_cast<uint16_t>(data[1]); uint16_t size = static_cast<uint16_t>(data[0])<<8 | static_cast<uint16_t>(data[1]);
if(encodedSize != size){ for(uint8_t i=2; i<size+2; i++){
return ErrorOr<tiny_utf8::string>(true, ErrorCodes::MISMATCHEDSIZE);
}
for(uint16_t i=2; i<size+2; i++){
if(i != 0){ if(i != 0){
if(data[i] == 0x80 && data[i-1] == 0xc0){ if(data[i] == 0x80 && data[i-1] == 0xc0){
stdString[stdString.length() - 1] = '\0'; stdString[stdString.length() - 1] = '\0';
@ -47,48 +35,11 @@ namespace JavaCompat {
} }
stdString.push_back((char) data[i]); stdString.push_back((char) data[i]);
} }
return ErrorOr<tiny_utf8::string>(tiny_utf8::string(stdString)); return tiny_utf8::string(stdString);
} }
/*
ErrorOr<std::vector<uint8_t>> exportJavaString(tiny_utf8::string data) { ErrorOr<uint8_t*> exportJavaString(tiny_utf8::string data) {
uint16_t* size = new uint16_t; return ErrorOr(nullptr);
uint8_t* sizeBytes = reinterpret_cast<uint8_t*>(size);
std::vector<uint8_t> output = std::vector<uint8_t>();
std::string stdString = data.cpp_str();
if(stdString.size() > 0xFFFF){
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::OVERRUN);
} }
*/
*size = (uint16_t) stdString.size();
//placeholder size bytes
output.push_back(0x00);
output.push_back(0x00);
for(uint16_t i=0; i<stdString.size(); i++){
if((uint8_t) stdString[i] == 0x00){
*size += 1;
output.push_back(0xc0);
output.push_back(0x80);
continue;
}
output.push_back(stdString[i]);
}
//FIXME: endian-dependent implementation
#ifdef FOSSVG_BIG_ENDIAN
output[0] = *sizeBytes;
output[1] = *(sizeBytes+1);
#else
#ifdef FOSSVG_LITTLE_ENDIAN
output[0] = *(sizeBytes+1);
output[1] = *sizeBytes;
#else
#error "JavaCompat::exportJavaString: An implementation for your endianness is unavailable."
#endif
#endif
return ErrorOr(output);
}
} }

View File

@ -13,11 +13,12 @@
//version 3 along with this program. //version 3 along with this program.
//If not, see https://www.gnu.org/licenses/agpl-3.0.en.html //If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include <vector>
#include <tinyutf8/tinyutf8.h> #include <tinyutf8/tinyutf8.h>
#include "error.hpp" #include "error.h++"
namespace JavaCompat { namespace JavaCompat {
ErrorOr<tiny_utf8::string> importJavaString(uint8_t data[], uint16_t size); //FIXME: contrary to what I said, we need to explicitly pass the data
ErrorOr<std::vector<uint8_t>> exportJavaString(tiny_utf8::string data); // size because files could have been tampered with or corrupted
tiny_utf8::string importJavaString(uint8_t data[]);
ErrorOr<uint8_t*> exportJavaString(tiny_utf8::string data);
} }

File diff suppressed because it is too large Load Diff

79
src/lib/nbt.h++ Normal file
View File

@ -0,0 +1,79 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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
// information taken from https://wiki.vg/NBT
// This is an attempt at creating a uniform model for all NBT tags to allow for a uniform interface based on subclassing a single NBT tag super class.
// NBT tags have a type, optionally a name which consists of the name size and the name string, optionally content type, and optionally a payload which can consist of optionally content type, optionally a content size,
// and the stored content. The format in which they are stored is as follows: <type><name size><name><payload>. All numbers are stored in big endian representation.
// All tag types:
// generic representation: Tag(uint8:tag_type, String:name, uint16:name_size, byte[]:content, int32:size)
// None (compound end): Tag( 0, "", 0, None, 0) => used to determine the end of a compound tag, only the type gets stored
// int8: Tag( 1, String:name, uint16:name_size, int8:content, 1) => a single signed byte, size not stored
// int16: Tag( 2, String:name, uint16:name_size, int16:content, 2) => 16 bit signed integer, size not stored
// int32: Tag( 3, String:name, uint16:name_size, int32:content, 4) => 32 bit signed integer, size not stored
// int64: Tag( 4, String:name, uint16:name_size, int64:content, 8) => 64 bit signed integer, size not stored
// float32: Tag( 5, String:name, uint16:name_size, float32:content,4) => 32 bit IEEE754 floating point number, size not stored
// float64: Tag( 6, String:name, uint16:name_size, float64:content,8) => 64 bit IEEE754 floating point number, size not stored
// int8[]: Tag( 7, String:name, uint16:name_size, int8[]:content, int32:size) => content stored prefixed with size
// String: Tag( 8, String:name, uint16:name_size, String:content, uint16:size) => Java style modified UTF-8 string, content stored prefixed with size
// Tag[] (list): Tag<Tag:type>( 9, String:name, uint16:name_size, Tag[]:content, int32:size) => list of tags of the same type with tag type and name information omitted prefixed by (in order) content type and size
// Tag[] (compound): Tag(10, String:name, uint16:name_size, Tag[]:content, int32:size) => list of tags, last tag is always an end tag, size not stored
// int32[]: Tag(11, String:name, uint16:name_size, int32[]:content,int32:size) => list of 32 bit signed integers prefixed with its size, endianness not verified at this point
// int64[]: Tag(12, String:name, uint16:name_size, int64[]:content,int32:size) => list of 64 bit signed integers prefixed with its size, endianness not verified at this point
#pragma once
#include <cstdint>
#include <vector>
#include "error.h++"
namespace NBT {
namespace helper {
ErrorOr<int8_t> readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int16_t> readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int32_t> readInt32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int64_t> readInt64(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
//FIXME: we just assume that float is a single-precision IEEE754
// floating point number
ErrorOr<float> readFloat32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
//FIXME: we just assume that double is a double-precision IEEE754
// floating point number
ErrorOr<double> readFloat64(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<std::vector<int8_t>> readInt8Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
//ErrorOr<> readString(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<std::vector<int32_t>> readInt32Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<std::vector<int64_t>> readInt64Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
void writeInt8(std::vector<uint8_t>* destination, int8_t data);
void writeInt16(std::vector<uint8_t>* destination, int16_t data);
void writeInt32(std::vector<uint8_t>* destination, int32_t data);
void writeInt64(std::vector<uint8_t>* destination, int64_t data);
//FIXME: we just assume that float is a single-precision IEEE754
// floating point number
void writeFloat32(std::vector<uint8_t>* destination, float data);
//FIXME: we just assume that double is a single-precision IEEE754
// floating point number
void writeFloat64(std::vector<uint8_t>* destination, double data);
void writeInt8Array(std::vector<uint8_t>* destination, std::vector<int8_t> data);
void writeInt8Array(std::vector<uint8_t>* destination, int8_t data[], uint32_t dataSize);
//void writeString(std::vector<uint8_t>* destination, <string type> data);
void writeInt32Array(std::vector<uint8_t>* destination, std::vector<int32_t> data);
void writeInt32Array(std::vector<uint8_t>* destination, int32_t data[], uint32_t dataSize);
void writeInt64Array(std::vector<uint8_t>* destination, std::vector<int64_t> data);
void writeInt64Array(std::vector<uint8_t>* destination, int64_t data[], uint32_t dataSize);
}
bool validateRawNBTData(uint8_t data[], int length);
}

View File

@ -1,307 +0,0 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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
// information taken from https://wiki.vg/NBT
// This is an attempt at creating a uniform model for all NBT tags to allow for a uniform interface based on subclassing a single NBT tag super class.
// NBT tags have a type, optionally a name which consists of the name size and the name string, optionally content type, and optionally a payload which can consist of optionally content type, optionally a content size,
// and the stored content. The format in which they are stored is as follows: <type><name size><name><payload>. All numbers are stored in big endian representation.
// All tag types:
// Generic: Tag(uint8:tag_type, String:name, uint16:name_size, byte[]:content, int32:size)
// End: Tag( 0, "", 0, None, 0) => used to determine the end of a compound tag, only the type gets stored
// Int8: Tag( 1, String:name, uint16:name_size, int8:content, 1) => a single signed byte, size not stored
// Int16: Tag( 2, String:name, uint16:name_size, int16:content, 2) => 16 bit signed integer, size not stored
// Int32: Tag( 3, String:name, uint16:name_size, int32:content, 4) => 32 bit signed integer, size not stored
// Int64: Tag( 4, String:name, uint16:name_size, int64:content, 8) => 64 bit signed integer, size not stored
// Float: Tag( 5, String:name, uint16:name_size, float:content, 4) => 32 bit IEEE754 floating point number, size not stored
// Double: Tag( 6, String:name, uint16:name_size, double:content, 8) => 64 bit IEEE754 floating point number, size not stored
// Int8Array: Tag( 7, String:name, uint16:name_size, int8[]:content, int32:length) => content stored prefixed with size
// String: Tag( 8, String:name, uint16:name_size, String:content, uint16:size) => Java style modified UTF-8 string, content stored prefixed with size
// List: Tag<Tag:type>( 9, String:name, uint16:name_size, Tag[]:content, int32:length) => list of tags of the same type with tag type and name information omitted prefixed by (in order) content type and length
// Compound: Tag(10, String:name, uint16:name_size, Tag[]:content, int32:length) => list of tags, last tag is always an end tag, size not stored
// Int32Array: Tag(11, String:name, uint16:name_size, int32[]:content,int32:length) => list of 32 bit signed integers prefixed with its size
// Int64Array: Tag(12, String:name, uint16:name_size, int64[]:content,int32:length) => list of 64 bit signed integers prefixed with its size
#pragma once
#include <cstdint>
#include <vector>
#include <tinyutf8/tinyutf8.h>
#include <mutex>
#include "error.hpp"
namespace NBT {
namespace Helper {
ErrorOr<int8_t> readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int16_t> readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int32_t> readInt32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int64_t> readInt64(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<float> readFloat(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<double> readDouble(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<std::vector<int8_t>> readInt8Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<tiny_utf8::string> readString(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<std::vector<int32_t>> readInt32Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<std::vector<int64_t>> readInt64Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
void writeInt8(std::vector<uint8_t>* destination, int8_t data);
void writeInt16(std::vector<uint8_t>* destination, int16_t data);
void writeInt32(std::vector<uint8_t>* destination, int32_t data);
void writeInt64(std::vector<uint8_t>* destination, int64_t data);
void writeFloat(std::vector<uint8_t>* destination, float data);
void writeDouble(std::vector<uint8_t>* destination, double data);
void writeInt8Array(std::vector<uint8_t>* destination, std::vector<int8_t> data);
void writeInt8Array(std::vector<uint8_t>* destination, int8_t data[], uint32_t dataSize);
ErrorOrVoid writeString(std::vector<uint8_t>* destination, tiny_utf8::string data);
void writeInt32Array(std::vector<uint8_t>* destination, std::vector<int32_t> data);
void writeInt32Array(std::vector<uint8_t>* destination, int32_t data[], uint32_t dataSize);
void writeInt64Array(std::vector<uint8_t>* destination, std::vector<int64_t> data);
void writeInt64Array(std::vector<uint8_t>* destination, int64_t data[], uint32_t dataSize);
ErrorOr<uint64_t> totalTagSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int32_t> containedDataLength(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
}
namespace TagType {
const uint8_t END = 0;
const uint8_t INT8 = 1;
const uint8_t INT16 = 2;
const uint8_t INT32 = 3;
const uint8_t INT64 = 4;
const uint8_t FLOAT = 5;
const uint8_t DOUBLE = 6;
const uint8_t INT8_ARRAY = 7;
const uint8_t STRING = 8;
const uint8_t LIST = 9;
const uint8_t COMPOUND = 10;
const uint8_t INT32_ARRAY= 11;
const uint8_t INT64_ARRAY= 12;
// This is a workaround that's not part of the spec.
const uint8_t INVALID = 255;
}
namespace Tag {
class Generic {
protected:
std::mutex mutex;
uint8_t type;
public:
tiny_utf8::string name;
Generic();
virtual ~Generic();
virtual ErrorOrVoid serialize(std::vector<uint8_t>* rawData);
virtual ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData);
uint8_t getTagType();
};
class End: public Generic {
public:
End();
// This needs a separate serializer because
// END tags have a special header.
ErrorOrVoid serialize(std::vector<uint8_t>* rawData) override;
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
};
class Int8: public Generic {
private:
int8_t value;
public:
Int8();
Int8(tiny_utf8::string name, int8_t value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
int8_t getValue();
void setValue(int8_t value);
};
class Int16: public Generic {
private:
int16_t value;
public:
Int16();
Int16(tiny_utf8::string name, int16_t value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
int16_t getValue();
void setValue(int16_t value);
};
class Int32: public Generic {
private:
int32_t value;
public:
Int32();
Int32(tiny_utf8::string name, int32_t value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
int32_t getValue();
void setValue(int32_t value);
};
class Int64: public Generic {
private:
int64_t value;
public:
Int64();
Int64(tiny_utf8::string name, int64_t value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
int64_t getValue();
void setValue(int64_t value);
};
class Float: public Generic {
private:
float value;
public:
Float();
Float(tiny_utf8::string name, float value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
float getValue();
void setValue(float value);
};
class Double: public Generic {
private:
double value;
public:
Double();
Double(tiny_utf8::string name, double value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
double getValue();
void setValue(double value);
};
class Int8Array: public Generic {
private:
std::vector<int8_t> data;
public:
Int8Array();
Int8Array(tiny_utf8::string name, std::vector<int8_t> data);
Int8Array(tiny_utf8::string name, uint64_t length, int8_t data[]);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
std::vector<int8_t> getData();
ErrorOr<int8_t> getValue(uint64_t position);
void setData(std::vector<int8_t> newData);
ErrorOrVoid setValue(uint64_t position, int8_t value);
uint64_t length();
void addElement(int8_t element);
ErrorOrVoid removeElement(uint64_t position);
};
class String: public Generic {
private:
tiny_utf8::string value;
public:
String();
String(tiny_utf8::string name, tiny_utf8::string value);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
tiny_utf8::string getValue();
void setValue(tiny_utf8::string value);
};
class List: public Generic {
private:
std::vector<Generic*> tags;
uint8_t containedType;
public:
List();
List(tiny_utf8::string name, uint8_t type);
List(tiny_utf8::string name, std::vector<Generic*> data);
~List() override;
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
uint8_t getContainedType();
ErrorOr<Generic*> getElementPointer(uint64_t position);
ErrorOrVoid setElementPointerAt(uint64_t position, Generic* pointer);
ErrorOrVoid appendPointer(Generic* pointer);
ErrorOrVoid deleteElement(uint64_t position);
uint64_t length();
};
class Compound: public Generic {
private:
std::vector<Generic*> tags;
// built-in end tag
End* endPointer;
public:
Compound();
Compound(tiny_utf8::string name);
Compound(tiny_utf8::string name, std::vector<Generic*> data);
~Compound() override;
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
ErrorOr<Generic*> getElementPointer(uint64_t position);
ErrorOrVoid setElementPointerAt(uint64_t position, Generic* pointer);
ErrorOrVoid appendPointer(Generic* pointer);
ErrorOrVoid deleteElement(uint64_t position);
uint64_t length();
};
class Int32Array: public Generic {
private:
std::vector<int32_t> data;
public:
Int32Array();
Int32Array(tiny_utf8::string name, std::vector<int32_t> data);
Int32Array(tiny_utf8::string name, uint64_t length, int32_t data[]);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
std::vector<int32_t> getData();
ErrorOr<int32_t> getValue(uint64_t position);
void setData(std::vector<int32_t> newData);
ErrorOrVoid setValue(uint64_t position, int32_t value);
uint64_t length();
void addElement(int32_t element);
ErrorOrVoid removeElement(uint64_t position);
};
class Int64Array: public Generic {
private:
std::vector<int64_t> data;
public:
Int64Array();
Int64Array(tiny_utf8::string name, std::vector<int64_t> data);
Int64Array(tiny_utf8::string name, uint64_t length, int64_t data[]);
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
std::vector<int64_t> getData();
ErrorOr<int64_t> getValue(uint64_t position);
void setData(std::vector<int64_t> newData);
ErrorOrVoid setValue(uint64_t position, int64_t value);
uint64_t length();
void addElement(int64_t element);
ErrorOrVoid removeElement(uint64_t position);
};
}
ErrorOr<std::vector<Tag::Generic*>> deserialize(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr);
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr);
}

View File

@ -15,4 +15,4 @@
#include <iostream> #include <iostream>
#define ASSERT(truth) if ((truth)); else { std::cout << "On line " << __LINE__ << ": Assertion failed: " << #truth << std::endl; return 1; } #define ASSERT(truth) if ((truth)); else { std::cout << "Assertion failed: " << #truth << std::endl; return 1; }

View File

@ -16,14 +16,14 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "assert.hpp" #include "assert.h++"
#include "../lib/error.hpp" #include "../lib/error.h++"
#include "../lib/cli.hpp" #include "../lib/cli.h++"
int main() { int main(int argc, char* argv[]) {
std::cout << "################################################################################" << std::endl; std::cout << "################################################################################" << std::endl;
std::cout << "CLI argument parsing tests" << std::endl; std::cout << "CLI argument parser tests" << std::endl;
std::cout << "################################################################################" << std::endl; std::cout << "################################################################################" << std::endl;
// Valid parameter test ############################################ // Valid parameter test ############################################
@ -31,7 +31,7 @@ int main() {
// with many variations of valid input on a single command line. // with many variations of valid input on a single command line.
// //
// simulated command line: // simulated command line:
// test -0 -ab -12345 --long-flag -cconcatenated -d separate-value -efdouble-concatenated "argument 0" -gh concatenated-separate-value --long-argument-with-value-included="included value" --long-argument-with-value-separated "separate value" "argument 1" "argument 2" // test -0 -ab -12345 --long-flag -cconcatenated -d separate-value -efdouble-concatenated "positional argument 0" -gh concatenated-separate-value --long-argument-with-value-included="included value" --long-argument-with-value-separated "separate value" "positional argument 1" "positional argument 2"
std::vector<CLI::Flag> validTestFlags; std::vector<CLI::Flag> validTestFlags;
validTestFlags.push_back(CLI::Flag('0', "00000", "a short flag on its own")); validTestFlags.push_back(CLI::Flag('0', "00000", "a short flag on its own"));
@ -47,19 +47,19 @@ int main() {
validTestFlags.push_back(CLI::Flag('g', "ggggg", "short flags concatenated with an argument that has a separate value")); validTestFlags.push_back(CLI::Flag('g', "ggggg", "short flags concatenated with an argument that has a separate value"));
validTestFlags.push_back(CLI::Flag('6', "66666", "unused flag")); validTestFlags.push_back(CLI::Flag('6', "66666", "unused flag"));
std::vector<CLI::Option> validTestOptions; std::vector<CLI::UnpositionalArgument> validTestUnpositionalArguments;
validTestOptions.push_back(CLI::Option('c', "ccccc", "VALUE", "short argument concatenated with its value")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('c', "ccccc", "VALUE", "short argument concatenated with its value"));
validTestOptions.push_back(CLI::Option('d', "ddddd", " VALUE", "short argument with separate value")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('d', "ddddd", " VALUE", "short argument with separate value"));
validTestOptions.push_back(CLI::Option('f', "fffff", "VALUE", "short argument concatenated with a flag and its value")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('f', "fffff", "VALUE", "short argument concatenated with a flag and its value"));
validTestOptions.push_back(CLI::Option('h', "hhhhh", " VALUE", "short argument concatenated with a flag with separate value")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('h', "hhhhh", " VALUE", "short argument concatenated with a flag with separate value"));
validTestOptions.push_back(CLI::Option('x', "long-argument-with-value-included", "VALUE", "long argument with its value included using =")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('x', "long-argument-with-value-included", "VALUE", "long argument with its value included using ="));
validTestOptions.push_back(CLI::Option('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value"));
validTestOptions.push_back(CLI::Option('z', "zzzzz", "NOPE", "unused argument")); validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('z', "zzzzz", "NOPE", "unused argument"));
std::vector<CLI::Argument> validTestArguments; std::vector<CLI::PositionalArgument> validTestPositionalArguments;
validTestArguments.push_back(CLI::Argument("argument0", "argument between optional parameters")); validTestPositionalArguments.push_back(CLI::PositionalArgument("argument0", "positional argument between optional parameters"));
validTestArguments.push_back(CLI::Argument("argument1", "argument")); validTestPositionalArguments.push_back(CLI::PositionalArgument("argument1", "positional argument"));
validTestArguments.push_back(CLI::Argument("argument2", "argument")); validTestPositionalArguments.push_back(CLI::PositionalArgument("argument2", "positional argument"));
const char** validTestParameterList = new const char*[17]; const char** validTestParameterList = new const char*[17];
validTestParameterList[ 0] = "test"; validTestParameterList[ 0] = "test";
@ -71,17 +71,17 @@ int main() {
validTestParameterList[ 6] = "-d"; validTestParameterList[ 6] = "-d";
validTestParameterList[ 7] = "separate-value"; validTestParameterList[ 7] = "separate-value";
validTestParameterList[ 8] = "-efdouble-concatenated"; validTestParameterList[ 8] = "-efdouble-concatenated";
validTestParameterList[ 9] = "argument 0"; validTestParameterList[ 9] = "positional argument 0";
validTestParameterList[10] = "-gh"; validTestParameterList[10] = "-gh";
validTestParameterList[11] = "concatenated-separate-value"; validTestParameterList[11] = "concatenated-separate-value";
validTestParameterList[12] = "--long-argument-with-value-included=included value"; validTestParameterList[12] = "--long-argument-with-value-included=included value";
validTestParameterList[13] = "--long-argument-with-value-separated"; validTestParameterList[13] = "--long-argument-with-value-separated";
validTestParameterList[14] = "separate value"; validTestParameterList[14] = "separate value";
validTestParameterList[15] = "argument 1"; validTestParameterList[15] = "positional argument 1";
validTestParameterList[16] = "argument 2"; validTestParameterList[16] = "positional argument 2";
int validTestParameterCount = 17; int validTestParameterCount = 17;
CLI::ArgumentsParser validTestParameterParser = CLI::ArgumentsParser(validTestParameterCount, validTestParameterList, validTestFlags, validTestOptions, validTestArguments); CLI::ArgumentsParser validTestParameterParser = CLI::ArgumentsParser(validTestParameterCount, validTestParameterList, validTestFlags, validTestUnpositionalArguments, validTestPositionalArguments);
ASSERT(!validTestParameterParser.wrongUsage); ASSERT(!validTestParameterParser.wrongUsage);
ASSERT(validTestParameterParser.programName == std::string("test")); ASSERT(validTestParameterParser.programName == std::string("test"));
@ -115,50 +115,49 @@ int main() {
ASSERT(!validTestParameterParser.getFlag('6').isError); ASSERT(!validTestParameterParser.getFlag('6').isError);
ASSERT(!validTestParameterParser.getFlag('6').value); ASSERT(!validTestParameterParser.getFlag('6').value);
ASSERT(!validTestParameterParser.getFlag(std::string("66666")).value); ASSERT(!validTestParameterParser.getFlag(std::string("66666")).value);
ASSERT(!validTestParameterParser.getOption('c').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('c').isError);
ASSERT(validTestParameterParser.getOption('c').value==std::string("concatenated")); ASSERT(validTestParameterParser.getUnpositionalArgument('c').value==std::string("concatenated"));
ASSERT(validTestParameterParser.getOption(std::string("ccccc")).value==std::string("concatenated")); ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("ccccc")).value==std::string("concatenated"));
ASSERT(!validTestParameterParser.getOption('d').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('d').isError);
ASSERT(validTestParameterParser.getOption('d').value==std::string("separate-value")); ASSERT(validTestParameterParser.getUnpositionalArgument('d').value==std::string("separate-value"));
ASSERT(validTestParameterParser.getOption(std::string("ddddd")).value==std::string("separate-value")); ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("ddddd")).value==std::string("separate-value"));
ASSERT(!validTestParameterParser.getFlag('e').isError); ASSERT(!validTestParameterParser.getFlag('e').isError);
ASSERT(validTestParameterParser.getFlag('e').value); ASSERT(validTestParameterParser.getFlag('e').value);
ASSERT(validTestParameterParser.getFlag(std::string("eeeee")).value); ASSERT(validTestParameterParser.getFlag(std::string("eeeee")).value);
ASSERT(!validTestParameterParser.getOption('f').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('f').isError);
ASSERT(validTestParameterParser.getOption('f').value==std::string("double-concatenated")); ASSERT(validTestParameterParser.getUnpositionalArgument('f').value==std::string("double-concatenated"));
ASSERT(validTestParameterParser.getOption(std::string("fffff")).value==std::string("double-concatenated")); ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("fffff")).value==std::string("double-concatenated"));
ASSERT(!validTestParameterParser.getArgument(0).isError); ASSERT(!validTestParameterParser.getPositionalArgument(0).isError);
ASSERT(validTestParameterParser.getArgument(0).value=="argument 0"); ASSERT(validTestParameterParser.getPositionalArgument(0).value=="positional argument 0");
ASSERT(!validTestParameterParser.getFlag('g').isError); ASSERT(!validTestParameterParser.getFlag('g').isError);
ASSERT(validTestParameterParser.getFlag('g').value); ASSERT(validTestParameterParser.getFlag('g').value);
ASSERT(validTestParameterParser.getFlag(std::string("ggggg")).value); ASSERT(validTestParameterParser.getFlag(std::string("ggggg")).value);
ASSERT(!validTestParameterParser.getOption('h').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('h').isError);
ASSERT(validTestParameterParser.getOption('h').value==std::string("concatenated-separate-value")); ASSERT(validTestParameterParser.getUnpositionalArgument('h').value==std::string("concatenated-separate-value"));
ASSERT(validTestParameterParser.getOption(std::string("hhhhh")).value==std::string("concatenated-separate-value")); ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("hhhhh")).value==std::string("concatenated-separate-value"));
ASSERT(!validTestParameterParser.getOption('x').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('x').isError);
ASSERT(validTestParameterParser.getOption('x').value==std::string("included value")); ASSERT(validTestParameterParser.getUnpositionalArgument('x').value==std::string("included value"));
ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-included")).value==std::string("included value")); ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("long-argument-with-value-included")).value==std::string("included value"));
ASSERT(!validTestParameterParser.getOption('y').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('y').isError);
ASSERT(validTestParameterParser.getOption('y').value==std::string("separate value")); ASSERT(validTestParameterParser.getUnpositionalArgument('y').value==std::string("separate value"));
ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-separated")).value==std::string("separate value")); ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("long-argument-with-value-separated")).value==std::string("separate value"));
ASSERT(!validTestParameterParser.getArgument(1).isError); ASSERT(!validTestParameterParser.getPositionalArgument(1).isError);
ASSERT(validTestParameterParser.getArgument(1).value=="argument 1"); ASSERT(validTestParameterParser.getPositionalArgument(1).value=="positional argument 1");
ASSERT(!validTestParameterParser.getArgument(2).isError); ASSERT(!validTestParameterParser.getPositionalArgument(2).isError);
ASSERT(validTestParameterParser.getArgument(2).value=="argument 2"); ASSERT(validTestParameterParser.getPositionalArgument(2).value=="positional argument 2");
ASSERT(!validTestParameterParser.getOption('z').isError); ASSERT(!validTestParameterParser.getUnpositionalArgument('z').isError);
ASSERT(validTestParameterParser.getOption('z').errorCode == ErrorCodes::NOT_PRESENT); ASSERT(validTestParameterParser.getUnpositionalArgument('z').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(validTestParameterParser.getOption('z').value == std::string("")); ASSERT(validTestParameterParser.getUnpositionalArgument('z').value == std::string(""));
ASSERT(validTestParameterParser.wrongUsageMessages.size() == 0);
delete[] validTestParameterList; delete[] validTestParameterList;
std::cout << "Passed valid command line test." << std::endl; std::cout << "Passed valid input test." << std::endl;
// empty input (valid and invalid) ################################# // empty input (valid and invalid) #################################
std::vector<CLI::Flag> emptyTestFlags; std::vector<CLI::Flag> emptyTestFlags;
std::vector<CLI::Option> emptyTestOptions; std::vector<CLI::UnpositionalArgument> emptyTestUnpositionalArguments;
std::vector<CLI::Argument> emptyTestArguments; std::vector<CLI::PositionalArgument> emptyTestPositionalArguments;
const char** emptyTestParameterList = new const char*[1]; const char** emptyTestParameterList = new const char*[1];
// The command is always a part of a command line, even if it is empty // The command is always a part of a command line, even if it is empty
@ -168,26 +167,27 @@ int main() {
int emptyTestParameterCount = 1; int emptyTestParameterCount = 1;
// valid // valid
CLI::ArgumentsParser validEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestOptions, emptyTestArguments); CLI::ArgumentsParser validEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestUnpositionalArguments, emptyTestPositionalArguments);
ASSERT(!validEmptyTestParameterParser.wrongUsage); ASSERT(!validEmptyTestParameterParser.wrongUsage);
ASSERT(validEmptyTestParameterParser.programName == std::string("test")); ASSERT(validEmptyTestParameterParser.programName == std::string("test"));
ASSERT(validEmptyTestParameterParser.wrongUsageMessages.size() == 0);
//invalid //invalid
emptyTestArguments.push_back(CLI::Argument("argument", "argument")); emptyTestPositionalArguments.push_back(CLI::PositionalArgument("argument", "positional argument"));
CLI::ArgumentsParser invalidEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestOptions, emptyTestArguments); CLI::ArgumentsParser invalidEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestUnpositionalArguments, emptyTestPositionalArguments);
ASSERT(invalidEmptyTestParameterParser.wrongUsage); ASSERT(invalidEmptyTestParameterParser.wrongUsage);
ASSERT(invalidEmptyTestParameterParser.programName == std::string("test")); ASSERT(invalidEmptyTestParameterParser.programName == std::string("test"));
ASSERT(invalidEmptyTestParameterParser.getArgument(0).isError); ASSERT(invalidEmptyTestParameterParser.getPositionalArgument(0).isError);
ASSERT(invalidEmptyTestParameterParser.getArgument(0).errorCode == ErrorCodes::NOT_PRESENT); ASSERT(invalidEmptyTestParameterParser.getPositionalArgument(0).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(invalidEmptyTestParameterParser.wrongUsageMessages.size() == 1);
ASSERT(invalidEmptyTestParameterParser.wrongUsageMessages.at(0) == "Too few arguments! Missing: argument");
delete[] emptyTestParameterList; delete[] emptyTestParameterList;
std::cout << "Passed empty command line test." << std::endl; std::cout << "Passed empty input test." << std::endl;
// unknown flag #################################################### //TODO add tests for invalid input
// test cases for invalid input:
// - unknown flags, arguments (mixed with other input and standalone)
// - too few, too many positional arguments (mixed with other input and standalone)
// - check the produced error messages
std::vector<CLI::Flag> unknownFlagTestFlags; std::vector<CLI::Flag> unknownFlagTestFlags;
unknownFlagTestFlags.push_back(CLI::Flag('0', "zero", "test flag")); unknownFlagTestFlags.push_back(CLI::Flag('0', "zero", "test flag"));
@ -197,11 +197,11 @@ int main() {
unknownFlagTestFlags.push_back(CLI::Flag('4', "four", "test flag")); unknownFlagTestFlags.push_back(CLI::Flag('4', "four", "test flag"));
unknownFlagTestFlags.push_back(CLI::Flag('5', "five", "test flag")); unknownFlagTestFlags.push_back(CLI::Flag('5', "five", "test flag"));
std::vector<CLI::Option> unknownFlagTestOptions; std::vector<CLI::UnpositionalArgument> unknownFlagTestUnpositionalArguments;
std::vector<CLI::Argument> unknownFlagTestArguments; std::vector<CLI::PositionalArgument> unknownFlagTestPositionalArguments;
const char** unknownFlagTestParameterList = new const char*[7]; const char** unknownFlagTestParameterList = new const char*[3];
unknownFlagTestParameterList[0] = "test"; unknownFlagTestParameterList[0] = "test";
unknownFlagTestParameterList[1] = "-a"; unknownFlagTestParameterList[1] = "-a";
unknownFlagTestParameterList[2] = "-1"; unknownFlagTestParameterList[2] = "-1";
@ -211,554 +211,20 @@ int main() {
unknownFlagTestParameterList[6] = "-5"; unknownFlagTestParameterList[6] = "-5";
int unknownFlagStandaloneTestParameterCount = 2; int unknownFlagStandaloneTestParameterCount = 2;
CLI::ArgumentsParser unknownFlagStandaloneTestParser = CLI::ArgumentsParser(unknownFlagStandaloneTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestOptions, unknownFlagTestArguments); CLI::ArgumentsParser unknownFlagStandaloneTestParser = CLI::ArgumentsParser(unknownFlagStandaloneTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestUnpositionalArguments, unknownFlagTestPositionalArguments);
ASSERT(unknownFlagStandaloneTestParser.getFlag('1').isError); ASSERT(unknownFlagStandaloneTestParser.getFlag('1').isError);
ASSERT(unknownFlagStandaloneTestParser.getFlag('1').errorCode == ErrorCodes::NOT_PRESENT); ASSERT(unknownFlagStandaloneTestParser.getFlag('1').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(!unknownFlagStandaloneTestParser.getFlag('1').value); ASSERT(!unknownFlagStandaloneTestParser.getFlag('1').value);
ASSERT(unknownFlagStandaloneTestParser.getFlag(std::string("four")).isError);
ASSERT(unknownFlagStandaloneTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(!unknownFlagStandaloneTestParser.getFlag(std::string("four")).value);
ASSERT(unknownFlagStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownFlagStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a");
int unknownFlagMixedTestParameterCount = 7; int unknownFlagMixedTestParameterCount = 7;
CLI::ArgumentsParser unknownFlagMixedTestParser = CLI::ArgumentsParser(unknownFlagMixedTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestOptions, unknownFlagTestArguments); CLI::ArgumentsParser unknownFlagMixedTestParser = CLI::ArgumentsParser(unknownFlagMixedTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestUnpositionalArguments, unknownFlagTestPositionalArguments);
ASSERT(unknownFlagMixedTestParser.getFlag('1').isError); ASSERT(unknownFlagMixedTestParser.getFlag('1').isError);
ASSERT(unknownFlagMixedTestParser.getFlag('1').errorCode == ErrorCodes::WRONG_USAGE); ASSERT(unknownFlagMixedTestParser.getFlag('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownFlagMixedTestParser.getFlag('1').value); ASSERT(unknownFlagMixedTestParser.getFlag('1').value);
ASSERT(unknownFlagMixedTestParser.getFlag(std::string("four")).isError);
ASSERT(unknownFlagMixedTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownFlagMixedTestParser.getFlag(std::string("four")).value);
ASSERT(unknownFlagMixedTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownFlagMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a");
delete[] unknownFlagTestParameterList; delete[] unknownFlagTestParameterList;
std::vector<CLI::Flag> unknownLongFlagTestFlags;
unknownLongFlagTestFlags.push_back(CLI::Flag('0', "zero", "test flag"));
unknownLongFlagTestFlags.push_back(CLI::Flag('1', "one", "test flag"));
unknownLongFlagTestFlags.push_back(CLI::Flag('2', "two", "test flag"));
unknownLongFlagTestFlags.push_back(CLI::Flag('3', "three","test flag"));
unknownLongFlagTestFlags.push_back(CLI::Flag('4', "four", "test flag"));
unknownLongFlagTestFlags.push_back(CLI::Flag('5', "five", "test flag"));
std::vector<CLI::Option> unknownLongFlagTestOptions;
std::vector<CLI::Argument> unknownLongFlagTestArguments;
const char** unknownLongFlagTestParameterList = new const char*[7];
unknownLongFlagTestParameterList[0] = "test";
unknownLongFlagTestParameterList[1] = "--a";
unknownLongFlagTestParameterList[2] = "-1";
unknownLongFlagTestParameterList[3] = "-2";
unknownLongFlagTestParameterList[4] = "-3";
unknownLongFlagTestParameterList[5] = "-4";
unknownLongFlagTestParameterList[6] = "-5";
int unknownLongFlagStandaloneTestParameterCount = 2;
CLI::ArgumentsParser unknownLongFlagStandaloneTestParser = CLI::ArgumentsParser(unknownLongFlagStandaloneTestParameterCount, unknownLongFlagTestParameterList, unknownLongFlagTestFlags, unknownLongFlagTestOptions, unknownLongFlagTestArguments);
ASSERT(unknownLongFlagStandaloneTestParser.getFlag('1').isError);
ASSERT(unknownLongFlagStandaloneTestParser.getFlag('1').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(!unknownLongFlagStandaloneTestParser.getFlag('1').value);
ASSERT(unknownLongFlagStandaloneTestParser.getFlag(std::string("four")).isError);
ASSERT(unknownLongFlagStandaloneTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(!unknownLongFlagStandaloneTestParser.getFlag(std::string("four")).value);
ASSERT(unknownLongFlagStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownLongFlagStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag: --a");
int unknownLongFlagMixedTestParameterCount = 7;
CLI::ArgumentsParser unknownLongFlagMixedTestParser = CLI::ArgumentsParser(unknownLongFlagMixedTestParameterCount, unknownLongFlagTestParameterList, unknownLongFlagTestFlags, unknownLongFlagTestOptions, unknownLongFlagTestArguments);
ASSERT(unknownLongFlagMixedTestParser.getFlag('1').isError);
ASSERT(unknownLongFlagMixedTestParser.getFlag('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownLongFlagMixedTestParser.getFlag('1').value);
ASSERT(unknownLongFlagMixedTestParser.getFlag(std::string("four")).isError);
ASSERT(unknownLongFlagMixedTestParser.getFlag(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownLongFlagMixedTestParser.getFlag(std::string("four")).value);
ASSERT(unknownLongFlagMixedTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownLongFlagMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag: --a");
delete[] unknownLongFlagTestParameterList;
std::cout << "Passed unknown flag test." << std::endl; std::cout << "Passed unknown flag test." << std::endl;
// unknown argument ################################################
std::vector<CLI::Flag> unknownOptionTestFlags;
std::vector<CLI::Option> unknownOptionTestOptions;
unknownOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test argument"));
unknownOptionTestOptions.push_back(CLI::Option('1', "one", "one", "test argument"));
unknownOptionTestOptions.push_back(CLI::Option('2', "two", "two", "test argument"));
unknownOptionTestOptions.push_back(CLI::Option('3', "three","three","test argument"));
unknownOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test argument"));
unknownOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test argument"));
std::vector<CLI::Argument> unknownOptionTestArguments;
const char** unknownOptionTestParameterList = new const char*[7];
unknownOptionTestParameterList[0] = "test";
unknownOptionTestParameterList[1] = "-a123";
unknownOptionTestParameterList[2] = "-1a";
unknownOptionTestParameterList[3] = "-2b";
unknownOptionTestParameterList[4] = "-3c";
unknownOptionTestParameterList[5] = "-4d";
unknownOptionTestParameterList[6] = "-5e";
int unknownOptionStandaloneTestParameterCount = 2;
CLI::ArgumentsParser unknownOptionStandaloneTestParser = CLI::ArgumentsParser(unknownOptionStandaloneTestParameterCount, unknownOptionTestParameterList, unknownOptionTestFlags, unknownOptionTestOptions, unknownOptionTestArguments);
ASSERT(unknownOptionStandaloneTestParser.getOption('1').isError);
ASSERT(unknownOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(unknownOptionStandaloneTestParser.getOption('1').value == "");
ASSERT(unknownOptionStandaloneTestParser.getOption(std::string("four")).isError);
ASSERT(unknownOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(unknownOptionStandaloneTestParser.getOption(std::string("four")).value == "");
ASSERT(unknownOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a123");
int unknownOptionMixedTestParameterCount = 7;
CLI::ArgumentsParser unknownOptionMixedTestParser = CLI::ArgumentsParser(unknownOptionMixedTestParameterCount, unknownOptionTestParameterList, unknownOptionTestFlags, unknownOptionTestOptions, unknownOptionTestArguments);
ASSERT(unknownOptionMixedTestParser.getOption('1').isError);
ASSERT(unknownOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownOptionMixedTestParser.getOption('1').value == "a");
ASSERT(unknownOptionMixedTestParser.getOption(std::string("four")).isError);
ASSERT(unknownOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownOptionMixedTestParser.getOption(std::string("four")).value == "d");
ASSERT(unknownOptionMixedTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownOptionMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument or flag(s): a123");
delete[] unknownOptionTestParameterList;
std::vector<CLI::Flag> unknownLongOptionTestFlags;
std::vector<CLI::Option> unknownLongOptionTestOptions;
unknownLongOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test argument"));
unknownLongOptionTestOptions.push_back(CLI::Option('1', "one", "one", "test argument"));
unknownLongOptionTestOptions.push_back(CLI::Option('2', "two", "two", "test argument"));
unknownLongOptionTestOptions.push_back(CLI::Option('3', "three","three","test argument"));
unknownLongOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test argument"));
unknownLongOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test argument"));
std::vector<CLI::Argument> unknownLongOptionTestArguments;
const char** unknownLongOptionTestParameterList = new const char*[7];
unknownLongOptionTestParameterList[0] = "test";
unknownLongOptionTestParameterList[1] = "--a=123";
unknownLongOptionTestParameterList[2] = "-1a";
unknownLongOptionTestParameterList[3] = "-2b";
unknownLongOptionTestParameterList[4] = "-3c";
unknownLongOptionTestParameterList[5] = "-4d";
unknownLongOptionTestParameterList[6] = "-5e";
int unknownLongOptionStandaloneTestParameterCount = 2;
CLI::ArgumentsParser unknownLongOptionStandaloneTestParser = CLI::ArgumentsParser(unknownLongOptionStandaloneTestParameterCount, unknownLongOptionTestParameterList, unknownLongOptionTestFlags, unknownLongOptionTestOptions, unknownLongOptionTestArguments);
ASSERT(unknownLongOptionStandaloneTestParser.getOption('1').isError);
ASSERT(unknownLongOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(unknownLongOptionStandaloneTestParser.getOption('1').value == "");
ASSERT(unknownLongOptionStandaloneTestParser.getOption(std::string("four")).isError);
ASSERT(unknownLongOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(unknownLongOptionStandaloneTestParser.getOption(std::string("four")).value == "");
ASSERT(unknownLongOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownLongOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Unknown argument (or it's a flag that doesn't take a value): --a=123");
int unknownLongOptionMixedTestParameterCount = 7;
CLI::ArgumentsParser unknownLongOptionMixedTestParser = CLI::ArgumentsParser(unknownLongOptionMixedTestParameterCount, unknownLongOptionTestParameterList, unknownLongOptionTestFlags, unknownLongOptionTestOptions, unknownLongOptionTestArguments);
ASSERT(unknownLongOptionMixedTestParser.getOption('1').isError);
ASSERT(unknownLongOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownLongOptionMixedTestParser.getOption('1').value == "a");
ASSERT(unknownLongOptionMixedTestParser.getOption(std::string("four")).isError);
ASSERT(unknownLongOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(unknownLongOptionMixedTestParser.getOption(std::string("four")).value == "d");
ASSERT(unknownLongOptionMixedTestParser.wrongUsageMessages.size() == 1);
ASSERT(unknownLongOptionMixedTestParser.wrongUsageMessages.at(0) == "Unknown argument (or it's a flag that doesn't take a value): --a=123");
delete[] unknownLongOptionTestParameterList;
std::cout << "Passed unknown option test." << std::endl;
// incomplete option ###############################################
std::vector<CLI::Flag> incompleteOptionTestFlags;
std::vector<CLI::Option> incompleteOptionTestOptions;
incompleteOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test option"));
incompleteOptionTestOptions.push_back(CLI::Option('1', "one", "one", "test option"));
incompleteOptionTestOptions.push_back(CLI::Option('2', "two", "two", "test option"));
incompleteOptionTestOptions.push_back(CLI::Option('3', "three","three","test option"));
incompleteOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test option"));
incompleteOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test option"));
std::vector<CLI::Argument> incompleteOptionTestArguments;
const char** incompleteOptionTestParameterList = new const char*[6];
incompleteOptionTestParameterList[0] = "test";
incompleteOptionTestParameterList[1] = "-1";
incompleteOptionTestParameterList[2] = "--two="; // value ""
incompleteOptionTestParameterList[3] = "-3c";
incompleteOptionTestParameterList[4] = "-4";
incompleteOptionTestParameterList[5] = "-5e";
int incompleteOptionStandaloneTestParameterCount = 2;
CLI::ArgumentsParser incompleteOptionStandaloneTestParser = CLI::ArgumentsParser(incompleteOptionStandaloneTestParameterCount, incompleteOptionTestParameterList, incompleteOptionTestFlags, incompleteOptionTestOptions, incompleteOptionTestArguments);
ASSERT(incompleteOptionStandaloneTestParser.getOption('1').isError);
ASSERT(incompleteOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteOptionStandaloneTestParser.getOption('1').value == "");
ASSERT(incompleteOptionStandaloneTestParser.getOption(std::string("four")).isError);
ASSERT(incompleteOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(incompleteOptionStandaloneTestParser.getOption(std::string("four")).value == "");
ASSERT(incompleteOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(incompleteOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");
int incompleteOptionMixedTestParameterCount = 6;
CLI::ArgumentsParser incompleteOptionMixedTestParser = CLI::ArgumentsParser(incompleteOptionMixedTestParameterCount, incompleteOptionTestParameterList, incompleteOptionTestFlags, incompleteOptionTestOptions, incompleteOptionTestArguments);
ASSERT(incompleteOptionMixedTestParser.getOption('1').isError);
ASSERT(incompleteOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteOptionMixedTestParser.getOption('1').value == "");
ASSERT(incompleteOptionMixedTestParser.getOption(std::string("four")).isError);
ASSERT(incompleteOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteOptionMixedTestParser.getOption(std::string("four")).value == "");
ASSERT(incompleteOptionMixedTestParser.getOption('5').isError);
ASSERT(incompleteOptionMixedTestParser.getOption('5').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteOptionMixedTestParser.getOption('5').value == "e");
ASSERT(incompleteOptionMixedTestParser.wrongUsageMessages.size() == 2);
ASSERT(incompleteOptionMixedTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");
ASSERT(incompleteOptionMixedTestParser.wrongUsageMessages.at(1) == "Argument expects value but has none: four");
delete[] incompleteOptionTestParameterList;
std::vector<CLI::Flag> incompleteLongOptionTestFlags;
std::vector<CLI::Option> incompleteLongOptionTestOptions;
incompleteLongOptionTestOptions.push_back(CLI::Option('0', "zero", "zero", "test argument"));
incompleteLongOptionTestOptions.push_back(CLI::Option('1', "one", "one", "test argument"));
incompleteLongOptionTestOptions.push_back(CLI::Option('2', "two", "two", "test argument"));
incompleteLongOptionTestOptions.push_back(CLI::Option('3', "three","three","test argument"));
incompleteLongOptionTestOptions.push_back(CLI::Option('4', "four", "four", "test argument"));
incompleteLongOptionTestOptions.push_back(CLI::Option('5', "five", "five", "test argument"));
std::vector<CLI::Argument> incompleteLongOptionTestArguments;
const char** incompleteLongOptionTestParameterList = new const char*[8];
incompleteLongOptionTestParameterList[0] = "test";
incompleteLongOptionTestParameterList[1] = "--one";
incompleteLongOptionTestParameterList[2] = "--two";
incompleteLongOptionTestParameterList[3] = "b";
incompleteLongOptionTestParameterList[4] = "-3c";
incompleteLongOptionTestParameterList[5] = "arg";
incompleteLongOptionTestParameterList[6] = "--four";
incompleteLongOptionTestParameterList[7] = "--five=e";
int incompleteLongOptionStandaloneTestParameterCount = 2;
CLI::ArgumentsParser incompleteLongOptionStandaloneTestParser = CLI::ArgumentsParser(incompleteLongOptionStandaloneTestParameterCount, incompleteLongOptionTestParameterList, incompleteLongOptionTestFlags, incompleteLongOptionTestOptions, incompleteLongOptionTestArguments);
ASSERT(incompleteLongOptionStandaloneTestParser.getOption('1').isError);
ASSERT(incompleteLongOptionStandaloneTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteLongOptionStandaloneTestParser.getOption('1').value == "");
ASSERT(incompleteLongOptionStandaloneTestParser.getOption(std::string("four")).isError);
ASSERT(incompleteLongOptionStandaloneTestParser.getOption(std::string("four")).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(incompleteLongOptionStandaloneTestParser.getOption(std::string("four")).value == "");
ASSERT(incompleteLongOptionStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(incompleteLongOptionStandaloneTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");
int incompleteLongOptionMixedTestParameterCount = 8;
incompleteLongOptionTestArguments.push_back(CLI::Argument("arg", "argument"));
CLI::ArgumentsParser incompleteLongOptionMixedTestParser = CLI::ArgumentsParser(incompleteLongOptionMixedTestParameterCount, incompleteLongOptionTestParameterList, incompleteLongOptionTestFlags, incompleteLongOptionTestOptions, incompleteLongOptionTestArguments);
ASSERT(incompleteLongOptionMixedTestParser.getOption('1').isError);
ASSERT(incompleteLongOptionMixedTestParser.getOption('1').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteLongOptionMixedTestParser.getOption('1').value == "");
ASSERT(incompleteLongOptionMixedTestParser.getOption('2').isError);
ASSERT(incompleteLongOptionMixedTestParser.getOption('2').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteLongOptionMixedTestParser.getOption('2').value == "b");
ASSERT(incompleteLongOptionMixedTestParser.getOption(std::string("four")).isError);
ASSERT(incompleteLongOptionMixedTestParser.getOption(std::string("four")).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteLongOptionMixedTestParser.getOption(std::string("four")).value == "");
ASSERT(incompleteLongOptionMixedTestParser.getOption('5').isError);
ASSERT(incompleteLongOptionMixedTestParser.getOption('5').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(incompleteLongOptionMixedTestParser.getOption('5').value == "e");
ASSERT(incompleteLongOptionMixedTestParser.wrongUsageMessages.size() == 2);
ASSERT(incompleteLongOptionMixedTestParser.wrongUsageMessages.at(0) == "Argument expects value but has none: one");
ASSERT(incompleteLongOptionMixedTestParser.wrongUsageMessages.at(1) == "Argument expects value but has none: four");
delete[] incompleteLongOptionTestParameterList;
std::cout << "Passed incomplete option test." << std::endl;
// too few arguments ###############################################
std::vector<CLI::Flag> tooFewArgumentsTestFlags;
tooFewArgumentsTestFlags.push_back(CLI::Flag('a', "aaaaa", "test flag"));
std::vector<CLI::Option> tooFewArgumentsTestOptions;
tooFewArgumentsTestOptions.push_back(CLI::Option('b', "bbbbb", "b", "test argument"));
tooFewArgumentsTestOptions.push_back(CLI::Option('c', "ccccc", "c", "test argument"));
tooFewArgumentsTestOptions.push_back(CLI::Option('d', "ddddd", "d", "test argument"));
std::vector<CLI::Argument> tooFewArgumentsTestArguments;
tooFewArgumentsTestArguments.push_back(CLI::Argument("argument0", "test argument"));
tooFewArgumentsTestArguments.push_back(CLI::Argument("argument1", "test argument"));
tooFewArgumentsTestArguments.push_back(CLI::Argument("argument2", "test argument"));
tooFewArgumentsTestArguments.push_back(CLI::Argument("argument3", "test argument"));
tooFewArgumentsTestArguments.push_back(CLI::Argument("argument4", "test argument"));
const char** tooFewArgumentsTestParameterList = new const char*[10];
tooFewArgumentsTestParameterList[0] = "test";
tooFewArgumentsTestParameterList[1] = "arg_0";
tooFewArgumentsTestParameterList[2] = "-a";//
tooFewArgumentsTestParameterList[3] = "-b";
tooFewArgumentsTestParameterList[4] = "1";
tooFewArgumentsTestParameterList[5] = "-c5";
tooFewArgumentsTestParameterList[6] = "arg_1";
tooFewArgumentsTestParameterList[7] = "--ddddd";
tooFewArgumentsTestParameterList[8] = "ddd";
tooFewArgumentsTestParameterList[9] = "arg_2";
int tooFewArgumentsStandaloneTestParameterCount = 2;
CLI::ArgumentsParser tooFewArgumentsStandaloneTestParser = CLI::ArgumentsParser(tooFewArgumentsStandaloneTestParameterCount, tooFewArgumentsTestParameterList, tooFewArgumentsTestFlags, tooFewArgumentsTestOptions, tooFewArgumentsTestArguments);
ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(0).isError);
ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(0).value == "arg_0");
ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(1).isError);
ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(1).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooFewArgumentsStandaloneTestParser.getArgument(1).value == "");
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('b').isError);
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('b').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('b').value == "");
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('c').isError);
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('c').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('c').value == "");
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('d').isError);
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('d').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooFewArgumentsStandaloneTestParser.getOption('d').value == "");
ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.size() == 4);
ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(0) == "Too few arguments! Missing: argument1");
ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(1) == "Too few arguments! Missing: argument2");
ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(2) == "Too few arguments! Missing: argument3");
ASSERT(tooFewArgumentsStandaloneTestParser.wrongUsageMessages.at(3) == "Too few arguments! Missing: argument4");
int tooFewArgumentsMixedTestParameterCount = 10;
CLI::ArgumentsParser tooFewArgumentsMixedTestParser = CLI::ArgumentsParser(tooFewArgumentsMixedTestParameterCount, tooFewArgumentsTestParameterList, tooFewArgumentsTestFlags, tooFewArgumentsTestOptions, tooFewArgumentsTestArguments);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(0).isError);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(0).value == "arg_0");
ASSERT(tooFewArgumentsMixedTestParser.getArgument(1).isError);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(1).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(1).value == "arg_1");
ASSERT(tooFewArgumentsMixedTestParser.getArgument(2).isError);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(2).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(2).value == "arg_2");
ASSERT(tooFewArgumentsMixedTestParser.getArgument(3).isError);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(3).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(3).value == "");
ASSERT(tooFewArgumentsMixedTestParser.getArgument(4).isError);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(4).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooFewArgumentsMixedTestParser.getArgument(4).value == "");
ASSERT(tooFewArgumentsMixedTestParser.getOption('b').isError);
ASSERT(tooFewArgumentsMixedTestParser.getOption('b').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsMixedTestParser.getOption('b').value == "1");
ASSERT(tooFewArgumentsMixedTestParser.getOption('c').isError);
ASSERT(tooFewArgumentsMixedTestParser.getOption('c').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsMixedTestParser.getOption('c').value == "5");
ASSERT(tooFewArgumentsMixedTestParser.getOption('d').isError);
ASSERT(tooFewArgumentsMixedTestParser.getOption('d').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooFewArgumentsMixedTestParser.getOption('d').value == "ddd");
ASSERT(tooFewArgumentsMixedTestParser.wrongUsageMessages.size() == 2);
ASSERT(tooFewArgumentsMixedTestParser.wrongUsageMessages.at(0) == "Too few arguments! Missing: argument3");
ASSERT(tooFewArgumentsMixedTestParser.wrongUsageMessages.at(1) == "Too few arguments! Missing: argument4");
delete[] tooFewArgumentsTestParameterList;
std::cout << "Passed too few arguments test." << std::endl;
// too many arguments ##############################################
std::vector<CLI::Flag> tooManyArgumentsTestFlags;
tooManyArgumentsTestFlags.push_back(CLI::Flag('a', "aaaaa", "test flag"));
std::vector<CLI::Option> tooManyArgumentsTestOptions;
tooManyArgumentsTestOptions.push_back(CLI::Option('b', "bbbbb", "b", "test argument"));
tooManyArgumentsTestOptions.push_back(CLI::Option('c', "ccccc", "c", "test argument"));
tooManyArgumentsTestOptions.push_back(CLI::Option('d', "ddddd", "d", "test argument"));
std::vector<CLI::Argument> tooManyArgumentsTestArguments;
tooManyArgumentsTestArguments.push_back(CLI::Argument("argument0", "test argument"));
const char** tooManyArgumentsTestParameterList = new const char*[10];
tooManyArgumentsTestParameterList[0] = "test";
tooManyArgumentsTestParameterList[1] = "arg_0";
tooManyArgumentsTestParameterList[2] = "arg_1";
tooManyArgumentsTestParameterList[3] = "-a";//
tooManyArgumentsTestParameterList[4] = "-b";
tooManyArgumentsTestParameterList[5] = "1";
tooManyArgumentsTestParameterList[6] = "-c5";
tooManyArgumentsTestParameterList[7] = "--ddddd";
tooManyArgumentsTestParameterList[8] = "ddd";
tooManyArgumentsTestParameterList[9] = "arg_2";
int tooManyArgumentsStandaloneTestParameterCount = 3;
CLI::ArgumentsParser tooManyArgumentsStandaloneTestParser = CLI::ArgumentsParser(tooManyArgumentsStandaloneTestParameterCount, tooManyArgumentsTestParameterList, tooManyArgumentsTestFlags, tooManyArgumentsTestOptions, tooManyArgumentsTestArguments);
ASSERT(tooManyArgumentsStandaloneTestParser.getArgument(0).isError);
ASSERT(tooManyArgumentsStandaloneTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooManyArgumentsStandaloneTestParser.getArgument(0).value == "arg_0");
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('b').isError);
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('b').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('b').value == "");
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('c').isError);
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('c').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('c').value == "");
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('d').isError);
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('d').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(tooManyArgumentsStandaloneTestParser.getOption('d').value == "");
ASSERT(tooManyArgumentsStandaloneTestParser.wrongUsageMessages.size() == 1);
ASSERT(tooManyArgumentsStandaloneTestParser.wrongUsageMessages.at(0) == "Too many arguments! Unexpected encounter of: arg_1");
int tooManyArgumentsMixedTestParameterCount = 10;
CLI::ArgumentsParser tooManyArgumentsMixedTestParser = CLI::ArgumentsParser(tooManyArgumentsMixedTestParameterCount, tooManyArgumentsTestParameterList, tooManyArgumentsTestFlags, tooManyArgumentsTestOptions, tooManyArgumentsTestArguments);
ASSERT(tooManyArgumentsMixedTestParser.getArgument(0).isError);
ASSERT(tooManyArgumentsMixedTestParser.getArgument(0).errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooManyArgumentsMixedTestParser.getArgument(0).value == "arg_0");
ASSERT(tooManyArgumentsMixedTestParser.getOption('b').isError);
ASSERT(tooManyArgumentsMixedTestParser.getOption('b').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooManyArgumentsMixedTestParser.getOption('b').value == "1");
ASSERT(tooManyArgumentsMixedTestParser.getOption('c').isError);
ASSERT(tooManyArgumentsMixedTestParser.getOption('c').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooManyArgumentsMixedTestParser.getOption('c').value == "5");
ASSERT(tooManyArgumentsMixedTestParser.getOption('d').isError);
ASSERT(tooManyArgumentsMixedTestParser.getOption('d').errorCode == ErrorCodes::WRONG_USAGE);
ASSERT(tooManyArgumentsMixedTestParser.getOption('d').value == "ddd");
ASSERT(tooManyArgumentsMixedTestParser.wrongUsageMessages.size() == 2);
ASSERT(tooManyArgumentsMixedTestParser.wrongUsageMessages.at(0) == "Too many arguments! Unexpected encounter of: arg_1");
ASSERT(tooManyArgumentsMixedTestParser.wrongUsageMessages.at(1) == "Too many arguments! Unexpected encounter of: arg_2");
delete[] tooManyArgumentsTestParameterList;
std::cout << "Passed too many arguments test." << std::endl;
std::cout << "################################################################################" << std::endl;
std::cout << "CLI argument parser help tests" << std::endl;
std::cout << "################################################################################" << std::endl;
// normal usage with all types of CLI input ########################
std::vector<CLI::Flag> helpTestFlags;
helpTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
helpTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> helpTestOptions;
helpTestOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
helpTestOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> helpTestArguments;
helpTestArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
helpTestArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** helpTestCommand = new const char*[1];
helpTestCommand[0] = "universecreator";
CLI::ArgumentsParser helpTestParser = CLI::ArgumentsParser(1, helpTestCommand, helpTestFlags, helpTestOptions, helpTestArguments, "Create a universe with a banana and an apple.");
ASSERT(helpTestParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
std::cout << "Passed normal usage test." << std::endl;
// no description ##################################################
std::vector<CLI::Flag> usageOnlyTestFlags;
usageOnlyTestFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
usageOnlyTestFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> usageOnlyTestOptions;
usageOnlyTestOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
usageOnlyTestOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> usageOnlyTestArguments;
usageOnlyTestArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
usageOnlyTestArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** usageOnlyTestCommand = new const char*[1];
usageOnlyTestCommand[0] = "universecreator";
CLI::ArgumentsParser usageOnlyTestParser = CLI::ArgumentsParser(1, usageOnlyTestCommand, usageOnlyTestFlags, usageOnlyTestOptions, usageOnlyTestArguments);
ASSERT(usageOnlyTestParser.getUsage() == "Usage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
std::cout << "Passed normal usage without description test." << std::endl;
// no flags ########################################################
std::vector<CLI::Flag> noFlagsFlags;
std::vector<CLI::Option> noFlagsOptions;
noFlagsOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
noFlagsOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> noFlagsArguments;
noFlagsArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
noFlagsArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** noFlagsCommand = new const char*[1];
noFlagsCommand[0] = "universecreator";
CLI::ArgumentsParser noFlagsParser = CLI::ArgumentsParser(1, noFlagsCommand, noFlagsFlags, noFlagsOptions, noFlagsArguments, "Create a universe with a banana and an apple.");
ASSERT(noFlagsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-b BANANA] [-c CORNHUBBBBB] WASH SHAKE \n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
// no options ######################################################
std::vector<CLI::Flag> noOptionsFlags;
noOptionsFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
noOptionsFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> noOptionsOptions;
std::vector<CLI::Argument> noOptionsArguments;
noOptionsArguments.push_back(CLI::Argument("WASH", "Number of times to wash my shark."));
noOptionsArguments.push_back(CLI::Argument("SHAKE", "Number of times to shake fist at cloud."));
const char** noOptionsCommand = new const char*[1];
noOptionsCommand[0] = "universecreator";
CLI::ArgumentsParser noOptionsParser = CLI::ArgumentsParser(1, noOptionsCommand, noOptionsFlags, noOptionsOptions, noOptionsArguments, "Create a universe with a banana and an apple.");
ASSERT(noOptionsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] WASH SHAKE \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nArguments:\n\tWASH\n\t\tNumber of times to wash my shark.\n\tSHAKE\n\t\tNumber of times to shake fist at cloud.\n");
// no arguments ####################################################
std::vector<CLI::Flag> noArgumentsFlags;
noArgumentsFlags.push_back(CLI::Flag('a', "apple", "throws an apple on Newton's head."));
noArgumentsFlags.push_back(CLI::Flag('y', "yapple", "throws an yapple on Newton's head."));
std::vector<CLI::Option> noArgumentsOptions;
noArgumentsOptions.push_back(CLI::Option('b', "banana", "BANANA", "smack someone with a ripe banana."));
noArgumentsOptions.push_back(CLI::Option('c', "corn", "CORNHUBBBBB", "visit cornhub."));
std::vector<CLI::Argument> noArgumentsArguments;
const char** noArgumentsCommand = new const char*[1];
noArgumentsCommand[0] = "universecreator";
CLI::ArgumentsParser noArgumentsParser = CLI::ArgumentsParser(1, noArgumentsCommand, noArgumentsFlags, noArgumentsOptions, noArgumentsArguments, "Create a universe with a banana and an apple.");
ASSERT(noArgumentsParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator [-ay] [-b BANANA] [-c CORNHUBBBBB] \n\nFlags:\n\t-a, --apple\n\t\tthrows an apple on Newton's head.\n\t-y, --yapple\n\t\tthrows an yapple on Newton's head.\n\nOptions:\n\t-b BANANA, --banana=BANANA\n\t\tsmack someone with a ripe banana.\n\t-c CORNHUBBBBB, --corn=CORNHUBBBBB\n\t\tvisit cornhub.\n");
std::cout << "Passed absent section usage test." << std::endl;
// no CLI input ####################################################
std::vector<CLI::Flag> noCLIFlags;
std::vector<CLI::Option> noCLIOptions;
std::vector<CLI::Argument> noCLIArguments;
const char** noCLICommand = new const char*[1];
noCLICommand[0] = "universecreator";
CLI::ArgumentsParser noCLIParser = CLI::ArgumentsParser(1, noCLICommand, noCLIFlags, noCLIOptions, noCLIArguments, "Create a universe with a banana and an apple.");
ASSERT(noCLIParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator \n");
std::cout << "Passed no CLI input usage test." << std::endl;
// additional info test ############################################
std::vector<CLI::Flag> additionalInfoCLIFlags;
std::vector<CLI::Option> additionalInfoCLIOptions;
std::vector<CLI::Argument> additionalInfoCLIArguments;
const char** additionalInfoCLICommand = new const char*[1];
additionalInfoCLICommand[0] = "universecreator";
CLI::ArgumentsParser additionalInfoCLIParser = CLI::ArgumentsParser(1, additionalInfoCLICommand, additionalInfoCLIFlags, additionalInfoCLIOptions, additionalInfoCLIArguments, "Create a universe with a banana and an apple.", "Rick Astley was here.");
ASSERT(additionalInfoCLIParser.getUsage() == "Help: universecreator\n\n\tCreate a universe with a banana and an apple.\n\nUsage: universecreator \n\nAdditional Info:\n\n\tRick Astley was here.\n");
std::cout << "Passed additional info test." << std::endl;
return 0; return 0;
} }

View File

@ -1,183 +0,0 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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 <vector>
#include "assert.hpp"
#include "tinyutf8/tinyutf8.h"
#include "../lib/file.hpp"
int main(){
std::cout << "################################################################################" << std::endl;
std::cout << "File tests" << std::endl;
std::cout << "################################################################################" << std::endl;
File* file;
//readByte test
file = File::open("resources/unicode_data/normal_utf-8", 'r').value;
uint8_t byte = file->readByte().value;
ASSERT(byte == 'T');
std::cout << "Passed read byte test." << std::endl;
//read test
std::vector<uint8_t> bytes = file->read(5).value;
//cursorPosition has already moved forward by one
ASSERT(bytes == std::vector<uint8_t>({'e', 's', 't', ' ', 's'}));
std::cout << "Passed read test." << std::endl;
//readString test
tiny_utf8::string data = file->readString(5).value;
ASSERT(data == "tring");
std::cout << "Passed read string test." << std::endl;
file->close();
delete file;
//Write Tests
File *writeFile;
File *readFile;
//writeByte test
writeFile = File::open("resources/writeTest", 'w').value;
writeFile->writeByte('a');
writeFile->close();
readFile = File::open("resources/writeTest", 'r').value;
uint8_t testByte = readFile->readByte().value;
readFile->close();
ASSERT(testByte == 'a');
std::cout << "Passed write byte test." << std::endl;
//write test
writeFile->open();
writeFile->write(std::vector<uint8_t>({'a', 'b', 'c'}));
writeFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string testBytes = readFile->readString(3).value;
readFile->close();
ASSERT(testBytes == "abc");
std::cout << "Passed write test." << std::endl;
writeFile->open();
writeFile->writeString(tiny_utf8::string("Hallo"));
writeFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string testString = readFile->readString(5).value;
readFile->close();
ASSERT(testString == "Hallo");
std::cout << "Passed write string test." << std::endl;
delete writeFile;
File *appendFile;
appendFile = File::open("resources/writeTest", 'a').value;
appendFile->writeByte(',');
appendFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string appendByteString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(appendByteString == "Hallo,");
std::cout << "Passed append byte test." << std::endl;
appendFile->open();
appendFile->write(std::vector<uint8_t>({' ', 'I', 'c', 'h'}));
appendFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string appendBytesString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(appendBytesString == "Hallo, Ich");
std::cout << "Passed append test" << std::endl;
appendFile->open();
appendFile->writeString(" bin Shwoomple.");
appendFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string appendString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(appendString == "Hallo, Ich bin Shwoomple.");
std::cout << "Passed append string test" << std::endl;
delete appendFile;
//test insert functions
File *modifyFile;
modifyFile = File::open("resources/writeTest", 'm').value;
modifyFile->cursorPosition = 5;
modifyFile->insertByte(',');
modifyFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string modifyByteString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(modifyByteString == "Hallo,, Ich bin Shwoomple.");
std::cout << "Passed modify byte test" << std::endl;
modifyFile->open();
modifyFile->cursorPosition = 6;
modifyFile->insert(std::vector<uint8_t>({' ', 'H', 'i'}));
modifyFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string modifyBytesString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(modifyBytesString == "Hallo, Hi, Ich bin Shwoomple.");
std::cout << "Passed modify test" << std::endl;
modifyFile->open();
modifyFile->cursorPosition = 9;
modifyFile->insertString(" THE CAKE IS A LIE");
modifyFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string modifyString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(modifyString == "Hallo, Hi THE CAKE IS A LIE, Ich bin Shwoomple.");
std::cout << "Passed modify string test" << std::endl;
//test cut functions
modifyFile->open();
modifyFile->cursorPosition = modifyFile->size.value-1;
ErrorOr<uint8_t> cutByte = modifyFile->cutByte();
modifyFile->close();
readFile->open();
readFile->cursorPosition = 0;
tiny_utf8::string cutByteString = readFile->readString(readFile->size.value).value;
readFile->close();
ASSERT(cutByte.value == '.');
ASSERT(cutByteString == "Hallo, Hi THE CAKE IS A LIE, Ich bin Shwoomple");
std::cout << "Passed cut byte test." << std::endl;
}

View File

@ -13,12 +13,12 @@
// version 3 along with this program. // version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include "assert.h++"
#include "../lib/javacompat.h++"
#include <iostream> #include <iostream>
#include <tinyutf8/tinyutf8.h> #include <tinyutf8/tinyutf8.h>
#include <fstream> #include <fstream>
#include <vector> #include <vector>
#include "assert.hpp"
#include "../lib/javacompat.hpp"
int main(){ int main(){
@ -28,7 +28,7 @@ int main(){
// java-style_unicode: 119 bytes, 85 characters ?? // java-style_unicode: 119 bytes, 85 characters ??
std::cout << "################################################################################" << std::endl; std::cout << "################################################################################" << std::endl;
std::cout << "Javacompat tests" << std::endl; std::cout << "Java String Tests" << std::endl;
std::cout << "################################################################################" << std::endl; std::cout << "################################################################################" << std::endl;
char* nextChar = new char; char* nextChar = new char;
@ -52,7 +52,7 @@ int main(){
return 2; return 2;
} }
tiny_utf8::string importedString = JavaCompat::importJavaString(reinterpret_cast<uint8_t*>(javaStdString.data()), 0x75).value; tiny_utf8::string importedString = JavaCompat::importJavaString(reinterpret_cast<uint8_t*>(javaStdString.data()));
std::streampos normalSize; std::streampos normalSize;
std::string normalStdString; std::string normalStdString;
@ -75,30 +75,9 @@ int main(){
tiny_utf8::string normalString = tiny_utf8::string(normalStdString); tiny_utf8::string normalString = tiny_utf8::string(normalStdString);
// check for normal operation
ASSERT(normalString == importedString); ASSERT(normalString == importedString);
// check for mismatched size error
std::string javaStdStringCopy = javaStdString;
javaStdStringCopy[0]='b';
ErrorOr<tiny_utf8::string> errorString = JavaCompat::importJavaString(reinterpret_cast<uint8_t*>(javaStdStringCopy.data()), 0x75);
ASSERT(errorString.isError);
ASSERT(errorString.errorCode == ErrorCodes::MISMATCHEDSIZE);
std::cout << "Passed Import Java string test." << std::endl; std::cout << "Passed Import Java string test." << std::endl;
//using normalString from when we read the file earlier
//there's no need to read the same file twice
std::vector<uint8_t> data = JavaCompat::exportJavaString(normalString).value; return 0;
std::string exportedString = std::string();
for(int i=0; i<(int) data.size(); i++){
exportedString.push_back(data[i]);
}
ASSERT(exportedString == javaStdString);
std::cout << "Passed Export Java string test." << std::endl;
delete nextChar;
return 0;
} }

446
src/test/nbt_helpers.cpp Normal file
View File

@ -0,0 +1,446 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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.h++"
#include "../lib/nbt.h++"
#include "../lib/error.h++"
int main(){
std::cout << "################################################################################" << std::endl;
std::cout << "NBT helper tests" << std::endl;
std::cout << "################################################################################" << std::endl;
// used for all integer tests
uint8_t dataForIntTest[] = {30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
uint64_t dataSize = 10;
// int8 ############################################################
// read successfully
uint64_t currentPosition = 5;
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).value == 35);
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).value == 30);
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 9;
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).value == 39);
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == false);
// out of bounds
currentPosition = 10;
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt8(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt8 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt8TestResult = new std::vector<uint8_t>();
NBT::helper::writeInt8(writeInt8TestResult, (int8_t) 8);
std::vector<uint8_t> dereferencedWriteInt8TestResult = *writeInt8TestResult;
delete writeInt8TestResult;
ASSERT(dereferencedWriteInt8TestResult.back() == (uint8_t) 8);
std::cout << "Passed writeInt8 NBT helper test" << std::endl;
// int16 ###########################################################
// read successfully
currentPosition = 5;
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).value == 8996);
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).value == 7711);
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 8;
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).value == 9767);
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == false);
// partially out of bounds
currentPosition = 9;
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// fully out of bounds
currentPosition = 10;
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt16(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt16 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt16TestResult = new std::vector<uint8_t>();
NBT::helper::writeInt16(writeInt16TestResult, (int16_t) 0xABCD);
std::vector<uint8_t> dereferencedWriteInt16TestResult = *writeInt16TestResult;
delete writeInt16TestResult;
ASSERT(dereferencedWriteInt16TestResult[0] == (uint8_t) 0xAB && dereferencedWriteInt16TestResult[1] == (uint8_t) 0xCD);
std::cout << "Passed writeInt16 NBT helper test" << std::endl;
// int32 ###########################################################
// read successfully
currentPosition = 5;
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).value == 589571366);
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).value == 505356321);
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 6;
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).value == 606414375);
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == false);
// partially out of bounds
currentPosition = 7;
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// fully out of bounds
currentPosition = 10;
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt32(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt32 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt32TestResult = new std::vector<uint8_t>();
NBT::helper::writeInt32(writeInt32TestResult, (int32_t) 0x12345678);
std::vector<uint8_t> dereferencedWriteInt32TestResult = *writeInt32TestResult;
delete writeInt32TestResult;
ASSERT(
dereferencedWriteInt32TestResult[0] == (uint8_t) 0x12 &&
dereferencedWriteInt32TestResult[1] == (uint8_t) 0x34 &&
dereferencedWriteInt32TestResult[2] == (uint8_t) 0x56 &&
dereferencedWriteInt32TestResult[3] == (uint8_t) 0x78
);
std::cout << "Passed writeInt32 NBT helper test" << std::endl;
// int64 ###########################################################
// read successfully
currentPosition = 1;
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).value == 2242829044932683046);
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).value == 2170488872094606373);
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 2;
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).value == 2315169217770759719);
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == false);
// partially out of bounds
currentPosition = 3;
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// fully out of bounds
currentPosition = 10;
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt64(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt64 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt64TestResult = new std::vector<uint8_t>();
NBT::helper::writeInt64(writeInt64TestResult, (int64_t) 0x0123456789ABCDEF);
std::vector<uint8_t> dereferencedWriteInt64TestResult = *writeInt64TestResult;
delete writeInt64TestResult;
ASSERT(
dereferencedWriteInt64TestResult[0] == (uint8_t) 0x01 &&
dereferencedWriteInt64TestResult[1] == (uint8_t) 0x23 &&
dereferencedWriteInt64TestResult[2] == (uint8_t) 0x45 &&
dereferencedWriteInt64TestResult[3] == (uint8_t) 0x67 &&
dereferencedWriteInt64TestResult[4] == (uint8_t) 0x89 &&
dereferencedWriteInt64TestResult[5] == (uint8_t) 0xAB &&
dereferencedWriteInt64TestResult[6] == (uint8_t) 0xCD &&
dereferencedWriteInt64TestResult[7] == (uint8_t) 0xEF
);
std::cout << "Passed writeInt64 NBT helper test" << std::endl;
//##################################################################
// used for integer "array" tests
uint8_t dataForIntArrayTest[] = {
0, 0, 0, 20,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
0, 0, 0, 5,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
0, 0, 0, 10,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,
0, 0, 0, 0
};
dataSize = 116;
// int8 "array" ####################################################
// read successfully
currentPosition = 0;
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}));
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read empty
currentPosition = 112;
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int8_t>());
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 20;
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read with size partially out of bounds
currentPosition = 114;
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read out of bounds
currentPosition = 200;
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt8Array NBT helper test" << std::endl;
std::vector<uint8_t>* int8ArrayTestOutput = new std::vector<uint8_t>();
int8_t writeDataTest[] = {1,2,3,4};
NBT::helper::writeInt8Array(int8ArrayTestOutput, writeDataTest, (uint32_t)4);
ASSERT(
int8ArrayTestOutput->at(0) == 0 &&
int8ArrayTestOutput->at(1) == 0 &&
int8ArrayTestOutput->at(2) == 0 &&
int8ArrayTestOutput->at(3) == 4 &&
int8ArrayTestOutput->at(4) == 1 &&
int8ArrayTestOutput->at(5) == 2 &&
int8ArrayTestOutput->at(6) == 3 &&
int8ArrayTestOutput->at(7) == 4
);
int8ArrayTestOutput->clear();
NBT::helper::writeInt8Array(int8ArrayTestOutput, std::vector<int8_t>({1,2,3,4}));
ASSERT(
int8ArrayTestOutput->at(0) == 0 &&
int8ArrayTestOutput->at(1) == 0 &&
int8ArrayTestOutput->at(2) == 0 &&
int8ArrayTestOutput->at(3) == 4 &&
int8ArrayTestOutput->at(4) == 1 &&
int8ArrayTestOutput->at(5) == 2 &&
int8ArrayTestOutput->at(6) == 3 &&
int8ArrayTestOutput->at(7) == 4
);
delete int8ArrayTestOutput;
std::cout << "Passed writeInt8Array NBT helper test" << std::endl;
// int32 "array" ###################################################
// read successfully
currentPosition = 68;
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int32_t>({1027489600, 1094861636, 1162233672, 1229605708, 1296977744, 1364349780, 1431721816, 1499093852, 1566465888, 1633837924}));
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read empty
currentPosition = 112;
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int32_t>());
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 20;
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read with size partially out of bounds
currentPosition = 114;
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read out of bounds
currentPosition = 200;
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt32Array NBT helper test" << std::endl;
std::vector<uint8_t>* int32ArrayTestOutput = new std::vector<uint8_t>();
int32_t input32[] = {0x01234567, 0x01234567};
NBT::helper::writeInt32Array(int32ArrayTestOutput, input32, 2);
ASSERT(
int32ArrayTestOutput->at(0) == 0x00 &&
int32ArrayTestOutput->at(1) == 0x00 &&
int32ArrayTestOutput->at(2) == 0x00 &&
int32ArrayTestOutput->at(3) == 0x02 &&
int32ArrayTestOutput->at(4) == 0x01 &&
int32ArrayTestOutput->at(5) == 0x23 &&
int32ArrayTestOutput->at(6) == 0x45 &&
int32ArrayTestOutput->at(7) == 0x67 &&
int32ArrayTestOutput->at(8) == 0x01 &&
int32ArrayTestOutput->at(9) == 0x23 &&
int32ArrayTestOutput->at(10) == 0x45 &&
int32ArrayTestOutput->at(11) == 0x67
);
int32ArrayTestOutput->clear();
NBT::helper::writeInt32Array(int32ArrayTestOutput, std::vector<int32_t>({0x01234567, 0x01234567}));
ASSERT(
int32ArrayTestOutput->at(0) == 0x00 &&
int32ArrayTestOutput->at(1) == 0x00 &&
int32ArrayTestOutput->at(2) == 0x00 &&
int32ArrayTestOutput->at(3) == 0x02 &&
int32ArrayTestOutput->at(4) == 0x01 &&
int32ArrayTestOutput->at(5) == 0x23 &&
int32ArrayTestOutput->at(6) == 0x45 &&
int32ArrayTestOutput->at(7) == 0x67 &&
int32ArrayTestOutput->at(8) == 0x01 &&
int32ArrayTestOutput->at(9) == 0x23 &&
int32ArrayTestOutput->at(10) == 0x45 &&
int32ArrayTestOutput->at(11) == 0x67
);
delete int32ArrayTestOutput;
std::cout << "Passed writeInt32Array NBT helper test" << std::endl;
// int64 "array" ###################################################
// read successfully
currentPosition = 44;
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int64_t>({2966230773313449776, 3544952156018063160, 4123673537695186954, 4413034230074983236, 4991755612779596620}));
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read empty
currentPosition = 112;
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int64_t>());
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 20;
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read with size partially out of bounds
currentPosition = 114;
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read out of bounds
currentPosition = 200;
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readInt64Array NBT helper test" << std::endl;
std::vector<uint8_t>* int64ArrayTestOutput = new std::vector<uint8_t>();
int64_t input64[] = {0x0123456789ABCDEF, 0x0123456789ABCDEF};
NBT::helper::writeInt64Array(int64ArrayTestOutput, input64, 2);
ASSERT(
int64ArrayTestOutput->at(0) == 0x00 &&
int64ArrayTestOutput->at(1) == 0x00 &&
int64ArrayTestOutput->at(2) == 0x00 &&
int64ArrayTestOutput->at(3) == 0x02 &&
int64ArrayTestOutput->at(4) == 0x01 &&
int64ArrayTestOutput->at(5) == 0x23 &&
int64ArrayTestOutput->at(6) == 0x45 &&
int64ArrayTestOutput->at(7) == 0x67 &&
int64ArrayTestOutput->at(8) == 0x89 &&
int64ArrayTestOutput->at(9) == 0xAB &&
int64ArrayTestOutput->at(10) == 0xCD &&
int64ArrayTestOutput->at(11) == 0xEF &&
int64ArrayTestOutput->at(12) == 0x01 &&
int64ArrayTestOutput->at(13) == 0x23 &&
int64ArrayTestOutput->at(14) == 0x45 &&
int64ArrayTestOutput->at(15) == 0x67 &&
int64ArrayTestOutput->at(16) == 0x89 &&
int64ArrayTestOutput->at(17) == 0xAB &&
int64ArrayTestOutput->at(18) == 0xCD &&
int64ArrayTestOutput->at(19) == 0xEF
);
int64ArrayTestOutput->clear();
NBT::helper::writeInt64Array(int64ArrayTestOutput, std::vector<int64_t>({0x0123456789ABCDEF, 0x0123456789ABCDEF}));
ASSERT(
int64ArrayTestOutput->at(0) == 0x00 &&
int64ArrayTestOutput->at(1) == 0x00 &&
int64ArrayTestOutput->at(2) == 0x00 &&
int64ArrayTestOutput->at(3) == 0x02 &&
int64ArrayTestOutput->at(4) == 0x01 &&
int64ArrayTestOutput->at(5) == 0x23 &&
int64ArrayTestOutput->at(6) == 0x45 &&
int64ArrayTestOutput->at(7) == 0x67 &&
int64ArrayTestOutput->at(8) == 0x89 &&
int64ArrayTestOutput->at(9) == 0xAB &&
int64ArrayTestOutput->at(10) == 0xCD &&
int64ArrayTestOutput->at(11) == 0xEF &&
int64ArrayTestOutput->at(12) == 0x01 &&
int64ArrayTestOutput->at(13) == 0x23 &&
int64ArrayTestOutput->at(14) == 0x45 &&
int64ArrayTestOutput->at(15) == 0x67 &&
int64ArrayTestOutput->at(16) == 0x89 &&
int64ArrayTestOutput->at(17) == 0xAB &&
int64ArrayTestOutput->at(18) == 0xCD &&
int64ArrayTestOutput->at(19) == 0xEF &&
true
);
delete int64ArrayTestOutput;
std::cout << "Passed writeInt32Array NBT helper test" << std::endl;
// float32 ["float" in the current implementation :( ] #############
uint8_t dataForFloat32Test[] = {0xC7, 0x77, 0x77, 0x77};
dataSize = 4;
currentPosition = 0;
// read successfully
ASSERT(NBT::helper::readFloat32(dataForFloat32Test, dataSize, currentPosition).value == -63351.46484375f);
ASSERT(NBT::helper::readFloat32(dataForFloat32Test, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 1;
ASSERT(NBT::helper::readFloat32(dataForFloat32Test, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readFloat32(dataForFloat32Test, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 4;
ASSERT(NBT::helper::readFloat32(dataForFloat32Test, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readFloat32(dataForFloat32Test, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readFloat32 NBT helper test" << std::endl;
std::vector<uint8_t>* writeFloat32TestResult = new std::vector<uint8_t>();
NBT::helper::writeFloat32(writeFloat32TestResult, (float) -63351.46484375f);
std::vector<uint8_t> dereferencedWriteFloat32TestResult = *writeFloat32TestResult;
delete writeFloat32TestResult;
ASSERT(
dereferencedWriteFloat32TestResult[0] == (uint8_t) 0xC7 &&
dereferencedWriteFloat32TestResult[1] == (uint8_t) 0x77 &&
dereferencedWriteFloat32TestResult[2] == (uint8_t) 0x77 &&
dereferencedWriteFloat32TestResult[3] == (uint8_t) 0x77
);
std::cout << "Passed writeFloat32 NBT helper test" << std::endl;
// float64 ["double" in the current implementation :( ] ############
uint8_t dataForFloat64Test[] = {0xC0, 0x34, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00};
dataSize = 8;
currentPosition = 0;
// read successfully
ASSERT(NBT::helper::readFloat64(dataForFloat64Test, dataSize, currentPosition).value == -20.015625476837158203125);
ASSERT(NBT::helper::readFloat64(dataForFloat64Test, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 1;
ASSERT(NBT::helper::readFloat64(dataForFloat64Test, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readFloat64(dataForFloat64Test, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 8;
ASSERT(NBT::helper::readFloat64(dataForFloat64Test, dataSize, currentPosition).isError == true);
ASSERT(NBT::helper::readFloat64(dataForFloat64Test, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
std::cout << "Passed readFloat64 NBT helper test" << std::endl;
std::vector<uint8_t>* writeFloat64TestResult = new std::vector<uint8_t>();
NBT::helper::writeFloat64(writeFloat64TestResult, (double) -20.015625476837158203125);
std::vector<uint8_t> dereferencedWriteFloat64TestResult = *writeFloat64TestResult;
delete writeFloat64TestResult;
ASSERT(
dereferencedWriteFloat64TestResult[0] == (uint8_t) 0xC0 &&
dereferencedWriteFloat64TestResult[1] == (uint8_t) 0x34 &&
dereferencedWriteFloat64TestResult[2] == (uint8_t) 0x04 &&
dereferencedWriteFloat64TestResult[3] == (uint8_t) 0x00 &&
dereferencedWriteFloat64TestResult[4] == (uint8_t) 0x08 &&
dereferencedWriteFloat64TestResult[5] == (uint8_t) 0x00 &&
dereferencedWriteFloat64TestResult[6] == (uint8_t) 0x00 &&
dereferencedWriteFloat64TestResult[7] == (uint8_t) 0x00
);
std::cout << "Passed writeInt64 NBT helper test" << std::endl;
return 0;
}

View File

@ -1,584 +0,0 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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 <fstream>
#include "assert.hpp"
#include "../lib/nbt.hpp"
#include "../lib/error.hpp"
#include "../lib/javacompat.hpp"
int main(){
std::cout << "################################################################################" << std::endl;
std::cout << "NBT read/write helper tests" << std::endl;
std::cout << "################################################################################" << std::endl;
uint8_t validNBTDataBlob[] = {0x0a, 0x00, 0x00, 0x08, 0x00, 0x3e, 0x53, 0x70, 0x61, 0x63, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x2c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x3f, 0x00, 0x16, 0x49, 0x64, 0x6b, 0x2e, 0x20, 0x4c, 0x65, 0x74, 0xe2, 0x80, 0x99, 0x73, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x0a, 0x00, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x75, 0x6e, 0x64, 0x03, 0x00, 0x0b, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xd3, 0x07, 0x23, 0x41, 0x08, 0x00, 0x09, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x0c, 0x65, 0x61, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x00, 0x06, 0x00, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x41, 0x23, 0x07, 0xd3, 0x4e, 0xfd, 0x07, 0xf1, 0x05, 0x00, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x42, 0x0d, 0x12, 0x43, 0x02, 0x00, 0x05, 0x69, 0x6e, 0x74, 0x31, 0x36, 0x07, 0xd0, 0x03, 0x00, 0x05, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x00, 0x9a, 0x21, 0x12, 0x0b, 0x00, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x00, 0x04, 0x17, 0xbf, 0xe8, 0x3c, 0x00, 0x00, 0xa8, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0x61, 0x72, 0x72, 0x61, 0x04, 0x00, 0x05, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x00, 0x00, 0x00, 0xbc, 0x97, 0xde, 0x9e, 0x3e, 0x0c, 0x00, 0x0b, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xa8, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x01, 0x67, 0xe7, 0xbd, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xab, 0xd8, 0x00, 0x00, 0xab, 0xd8, 0x00, 0x0b, 0x12, 0x21, 0x00, 0x79, 0x61, 0x72, 0x01, 0x00, 0x04, 0x69, 0x6e, 0x74, 0x38, 0x64, 0x07, 0x00, 0x0a, 0x69, 0x6e, 0x74, 0x38, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x00, 0x08, 0x71, 0x35, 0x77, 0x62, 0x54, 0x64, 0xf5, 0x32, 0x09, 0x00, 0x09, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x38, 0x01, 0x00, 0x00, 0x00, 0x05, 0x41, 0x60, 0x4e, 0x7f, 0xfa, 0x09, 0x00, 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x50, 0x61, 0x63, 0x6d, 0x61, 0x6e, 0x00, 0x03, 0x61, 0x74, 0x65, 0x00, 0x03, 0x61, 0x6c, 0x6c, 0x00, 0x03, 0x74, 0x68, 0x65, 0x00, 0x04, 0x64, 0x6f, 0x74, 0x73, 0x00, 0x02, 0x73, 0x6f, 0x00, 0x03, 0x6e, 0x6f, 0x77, 0x00, 0x06, 0x68, 0x65, 0xe2, 0x80, 0x99, 0x73, 0x00, 0x06, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x00, 0x03, 0x66, 0x6f, 0x72, 0x00, 0x03, 0x74, 0x68, 0x65, 0x00, 0x06, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x08, 0x00, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x00};
uint64_t validNBTDataBlobSize = 478;
uint64_t currentPositionInBlob;
// used for all integer tests
uint8_t dataForIntTest[] = {30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
uint64_t dataSize = 10;
uint64_t currentPosition;
// int8 ############################################################
// read successfully
currentPosition = 5;
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).value == 35);
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).value == 30);
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 9;
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).value == 39);
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == false);
// out of bounds
currentPosition = 10;
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt8(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0x13A;
ASSERT(NBT::Helper::readInt8(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt8(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == 100);
std::cout << "Passed readInt8 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt8TestResult = new std::vector<uint8_t>();
NBT::Helper::writeInt8(writeInt8TestResult, (int8_t) 8);
std::vector<uint8_t> dereferencedWriteInt8TestResult = *writeInt8TestResult;
delete writeInt8TestResult;
ASSERT(dereferencedWriteInt8TestResult.back() == (uint8_t) 8);
std::cout << "Passed writeInt8 NBT helper test" << std::endl;
// int16 ###########################################################
// read successfully
currentPosition = 5;
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).value == 8996);
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).value == 7711);
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 8;
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).value == 9767);
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == false);
// partially out of bounds
currentPosition = 9;
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// fully out of bounds
currentPosition = 10;
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt16(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0xb9;
ASSERT(NBT::Helper::readInt16(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt16(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == 2000);
std::cout << "Passed readInt16 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt16TestResult = new std::vector<uint8_t>();
NBT::Helper::writeInt16(writeInt16TestResult, (int16_t) 0xABCD);
std::vector<uint8_t> dereferencedWriteInt16TestResult = *writeInt16TestResult;
delete writeInt16TestResult;
ASSERT(dereferencedWriteInt16TestResult[0] == (uint8_t) 0xAB && dereferencedWriteInt16TestResult[1] == (uint8_t) 0xCD);
std::cout << "Passed writeInt16 NBT helper test" << std::endl;
// int32 ###########################################################
// read successfully
currentPosition = 5;
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).value == 589571366);
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).value == 505356321);
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 6;
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).value == 606414375);
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == false);
// partially out of bounds
currentPosition = 7;
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// fully out of bounds
currentPosition = 10;
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt32(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0xc3;
ASSERT(NBT::Helper::readInt32(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt32(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == 10101010);
std::cout << "Passed readInt32 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt32TestResult = new std::vector<uint8_t>();
NBT::Helper::writeInt32(writeInt32TestResult, (int32_t) 0x12345678);
// delete before checking assertions so we don't leak
std::vector<uint8_t> dereferencedWriteInt32TestResult = *writeInt32TestResult;
delete writeInt32TestResult;
ASSERT(
dereferencedWriteInt32TestResult[0] == (uint8_t) 0x12 &&
dereferencedWriteInt32TestResult[1] == (uint8_t) 0x34 &&
dereferencedWriteInt32TestResult[2] == (uint8_t) 0x56 &&
dereferencedWriteInt32TestResult[3] == (uint8_t) 0x78
);
std::cout << "Passed writeInt32 NBT helper test" << std::endl;
// int64 ###########################################################
// read successfully
currentPosition = 1;
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).value == 2242829044932683046);
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == false);
// begin of data
currentPosition = 0;
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).value == 2170488872094606373);
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == false);
// end of data
currentPosition = 2;
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).value == 2315169217770759719);
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == false);
// partially out of bounds
currentPosition = 3;
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// fully out of bounds
currentPosition = 10;
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt64(dataForIntTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0xf1;
ASSERT(NBT::Helper::readInt64(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt64(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == 810001800766);
std::cout << "Passed readInt64 NBT helper test" << std::endl;
std::vector<uint8_t>* writeInt64TestResult = new std::vector<uint8_t>();
NBT::Helper::writeInt64(writeInt64TestResult, (int64_t) 0x0123456789ABCDEF);
// delete before checking assertions so we don't leak
std::vector<uint8_t> dereferencedWriteInt64TestResult = *writeInt64TestResult;
delete writeInt64TestResult;
ASSERT(
dereferencedWriteInt64TestResult[0] == (uint8_t) 0x01 &&
dereferencedWriteInt64TestResult[1] == (uint8_t) 0x23 &&
dereferencedWriteInt64TestResult[2] == (uint8_t) 0x45 &&
dereferencedWriteInt64TestResult[3] == (uint8_t) 0x67 &&
dereferencedWriteInt64TestResult[4] == (uint8_t) 0x89 &&
dereferencedWriteInt64TestResult[5] == (uint8_t) 0xAB &&
dereferencedWriteInt64TestResult[6] == (uint8_t) 0xCD &&
dereferencedWriteInt64TestResult[7] == (uint8_t) 0xEF
);
std::cout << "Passed writeInt64 NBT helper test" << std::endl;
//##################################################################
// used for integer "array" tests
uint8_t dataForIntArrayTest[] = {
0, 0, 0, 20,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
0, 0, 0, 5,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
0, 0, 0, 10,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,
0, 0, 0, 0
};
dataSize = 116;
// int8 "array" ####################################################
// read successfully
currentPosition = 0;
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int8_t>({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}));
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read empty
currentPosition = 112;
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int8_t>());
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 20;
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read with size partially out of bounds
currentPosition = 114;
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 200;
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt8Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0x148;
ASSERT(NBT::Helper::readInt8Array(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt8Array(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == std::vector<int8_t>({(int8_t) 0x71, (int8_t) 0x35, (int8_t) 0x77, (int8_t) 0x62, (int8_t) 0x54, (int8_t) 0x64, (int8_t) 0xf5, (int8_t) 0x32}));
std::cout << "Passed readInt8Array NBT helper test" << std::endl;
std::vector<uint8_t>* int8ArrayTestOutput = new std::vector<uint8_t>();
int8_t writeDataTest[] = {1,2,3,4};
NBT::Helper::writeInt8Array(int8ArrayTestOutput, writeDataTest, (uint32_t)4);
ASSERT(
int8ArrayTestOutput->at(0) == 0 &&
int8ArrayTestOutput->at(1) == 0 &&
int8ArrayTestOutput->at(2) == 0 &&
int8ArrayTestOutput->at(3) == 4 &&
int8ArrayTestOutput->at(4) == 1 &&
int8ArrayTestOutput->at(5) == 2 &&
int8ArrayTestOutput->at(6) == 3 &&
int8ArrayTestOutput->at(7) == 4
);
int8ArrayTestOutput->clear();
NBT::Helper::writeInt8Array(int8ArrayTestOutput, std::vector<int8_t>({1,2,3,4}));
ASSERT(
int8ArrayTestOutput->at(0) == 0 &&
int8ArrayTestOutput->at(1) == 0 &&
int8ArrayTestOutput->at(2) == 0 &&
int8ArrayTestOutput->at(3) == 4 &&
int8ArrayTestOutput->at(4) == 1 &&
int8ArrayTestOutput->at(5) == 2 &&
int8ArrayTestOutput->at(6) == 3 &&
int8ArrayTestOutput->at(7) == 4
);
delete int8ArrayTestOutput;
std::cout << "Passed writeInt8Array NBT helper test" << std::endl;
// int32 "array" ###################################################
// read successfully
currentPosition = 68;
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int32_t>({1027489600, 1094861636, 1162233672, 1229605708, 1296977744, 1364349780, 1431721816, 1499093852, 1566465888, 1633837924}));
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read empty
currentPosition = 112;
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int32_t>());
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 20;
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read with size partially out of bounds
currentPosition = 114;
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 200;
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt32Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0xd5;
ASSERT(NBT::Helper::readInt32Array(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt32Array(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == std::vector<int32_t>({(int32_t) 0x17bfe83c, (int32_t) 0xa8fb, (int32_t) 0x7fffffff, (int32_t) 0x61727261}));
std::cout << "Passed readInt32Array NBT helper test" << std::endl;
std::vector<uint8_t>* int32ArrayTestOutput = new std::vector<uint8_t>();
int32_t input32[] = {0x01234567, 0x01234567};
NBT::Helper::writeInt32Array(int32ArrayTestOutput, input32, 2);
ASSERT(
int32ArrayTestOutput->at(0) == 0x00 &&
int32ArrayTestOutput->at(1) == 0x00 &&
int32ArrayTestOutput->at(2) == 0x00 &&
int32ArrayTestOutput->at(3) == 0x02 &&
int32ArrayTestOutput->at(4) == 0x01 &&
int32ArrayTestOutput->at(5) == 0x23 &&
int32ArrayTestOutput->at(6) == 0x45 &&
int32ArrayTestOutput->at(7) == 0x67 &&
int32ArrayTestOutput->at(8) == 0x01 &&
int32ArrayTestOutput->at(9) == 0x23 &&
int32ArrayTestOutput->at(10) == 0x45 &&
int32ArrayTestOutput->at(11) == 0x67
);
int32ArrayTestOutput->clear();
NBT::Helper::writeInt32Array(int32ArrayTestOutput, std::vector<int32_t>({0x01234567, 0x01234567}));
ASSERT(
int32ArrayTestOutput->at(0) == 0x00 &&
int32ArrayTestOutput->at(1) == 0x00 &&
int32ArrayTestOutput->at(2) == 0x00 &&
int32ArrayTestOutput->at(3) == 0x02 &&
int32ArrayTestOutput->at(4) == 0x01 &&
int32ArrayTestOutput->at(5) == 0x23 &&
int32ArrayTestOutput->at(6) == 0x45 &&
int32ArrayTestOutput->at(7) == 0x67 &&
int32ArrayTestOutput->at(8) == 0x01 &&
int32ArrayTestOutput->at(9) == 0x23 &&
int32ArrayTestOutput->at(10) == 0x45 &&
int32ArrayTestOutput->at(11) == 0x67
);
delete int32ArrayTestOutput;
std::cout << "Passed writeInt32Array NBT helper test" << std::endl;
// int64 "array" ###################################################
// read successfully
currentPosition = 44;
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int64_t>({2966230773313449776, 3544952156018063160, 4123673537695186954, 4413034230074983236, 4991755612779596620}));
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read empty
currentPosition = 112;
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).value == std::vector<int64_t>());
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 20;
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read with size partially out of bounds
currentPosition = 114;
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 200;
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readInt64Array(dataForIntArrayTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0x107;
ASSERT(NBT::Helper::readInt64Array(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readInt64Array(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == std::vector<int64_t>({(int64_t) 239865, (int64_t) 23586749, (int64_t) 9223372036854775807, (int64_t) 188944201329624, (int64_t) 3116157694992754}));
std::cout << "Passed readInt64Array NBT helper test" << std::endl;
std::vector<uint8_t>* int64ArrayTestOutput = new std::vector<uint8_t>();
int64_t input64[] = {0x0123456789ABCDEF, 0x0123456789ABCDEF};
NBT::Helper::writeInt64Array(int64ArrayTestOutput, input64, 2);
ASSERT(
int64ArrayTestOutput->at(0) == 0x00 &&
int64ArrayTestOutput->at(1) == 0x00 &&
int64ArrayTestOutput->at(2) == 0x00 &&
int64ArrayTestOutput->at(3) == 0x02 &&
int64ArrayTestOutput->at(4) == 0x01 &&
int64ArrayTestOutput->at(5) == 0x23 &&
int64ArrayTestOutput->at(6) == 0x45 &&
int64ArrayTestOutput->at(7) == 0x67 &&
int64ArrayTestOutput->at(8) == 0x89 &&
int64ArrayTestOutput->at(9) == 0xAB &&
int64ArrayTestOutput->at(10) == 0xCD &&
int64ArrayTestOutput->at(11) == 0xEF &&
int64ArrayTestOutput->at(12) == 0x01 &&
int64ArrayTestOutput->at(13) == 0x23 &&
int64ArrayTestOutput->at(14) == 0x45 &&
int64ArrayTestOutput->at(15) == 0x67 &&
int64ArrayTestOutput->at(16) == 0x89 &&
int64ArrayTestOutput->at(17) == 0xAB &&
int64ArrayTestOutput->at(18) == 0xCD &&
int64ArrayTestOutput->at(19) == 0xEF
);
int64ArrayTestOutput->clear();
NBT::Helper::writeInt64Array(int64ArrayTestOutput, std::vector<int64_t>({0x0123456789ABCDEF, 0x0123456789ABCDEF}));
ASSERT(
int64ArrayTestOutput->at(0) == 0x00 &&
int64ArrayTestOutput->at(1) == 0x00 &&
int64ArrayTestOutput->at(2) == 0x00 &&
int64ArrayTestOutput->at(3) == 0x02 &&
int64ArrayTestOutput->at(4) == 0x01 &&
int64ArrayTestOutput->at(5) == 0x23 &&
int64ArrayTestOutput->at(6) == 0x45 &&
int64ArrayTestOutput->at(7) == 0x67 &&
int64ArrayTestOutput->at(8) == 0x89 &&
int64ArrayTestOutput->at(9) == 0xAB &&
int64ArrayTestOutput->at(10) == 0xCD &&
int64ArrayTestOutput->at(11) == 0xEF &&
int64ArrayTestOutput->at(12) == 0x01 &&
int64ArrayTestOutput->at(13) == 0x23 &&
int64ArrayTestOutput->at(14) == 0x45 &&
int64ArrayTestOutput->at(15) == 0x67 &&
int64ArrayTestOutput->at(16) == 0x89 &&
int64ArrayTestOutput->at(17) == 0xAB &&
int64ArrayTestOutput->at(18) == 0xCD &&
int64ArrayTestOutput->at(19) == 0xEF &&
true
);
delete int64ArrayTestOutput;
std::cout << "Passed writeInt64Array NBT helper test" << std::endl;
// float ###########################################################
uint8_t dataForFloatTest[] = {0xC7, 0x77, 0x77, 0x77};
dataSize = 4;
currentPosition = 0;
// read successfully
ASSERT(NBT::Helper::readFloat(dataForFloatTest, dataSize, currentPosition).value == -63351.46484375f);
ASSERT(NBT::Helper::readFloat(dataForFloatTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 1;
ASSERT(NBT::Helper::readFloat(dataForFloatTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readFloat(dataForFloatTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 4;
ASSERT(NBT::Helper::readFloat(dataForFloatTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readFloat(dataForFloatTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0xad;
ASSERT(NBT::Helper::readFloat(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readFloat(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == 35.2678337097168);
std::cout << "Passed readFloat NBT helper test" << std::endl;
std::vector<uint8_t>* writeFloatTestResult = new std::vector<uint8_t>();
NBT::Helper::writeFloat(writeFloatTestResult, (float) -63351.46484375f);
std::vector<uint8_t> dereferencedWriteFloatTestResult = *writeFloatTestResult;
delete writeFloatTestResult;
ASSERT(
dereferencedWriteFloatTestResult[0] == (uint8_t) 0xC7 &&
dereferencedWriteFloatTestResult[1] == (uint8_t) 0x77 &&
dereferencedWriteFloatTestResult[2] == (uint8_t) 0x77 &&
dereferencedWriteFloatTestResult[3] == (uint8_t) 0x77
);
std::cout << "Passed writeFloat NBT helper test" << std::endl;
// double ##########################################################
uint8_t dataForDoubleTest[] = {0xC0, 0x34, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00};
dataSize = 8;
currentPosition = 0;
// read successfully
ASSERT(NBT::Helper::readDouble(dataForDoubleTest, dataSize, currentPosition).value == -20.015625476837158203125);
ASSERT(NBT::Helper::readDouble(dataForDoubleTest, dataSize, currentPosition).isError == false);
// read overrun
currentPosition = 1;
ASSERT(NBT::Helper::readDouble(dataForDoubleTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readDouble(dataForDoubleTest, dataSize, currentPosition).errorCode == ErrorCodes::OVERRUN);
// read out of bounds
currentPosition = 8;
ASSERT(NBT::Helper::readDouble(dataForDoubleTest, dataSize, currentPosition).isError == true);
ASSERT(NBT::Helper::readDouble(dataForDoubleTest, dataSize, currentPosition).errorCode == ErrorCodes::OUT_OF_RANGE);
// read from a bigger blob
currentPositionInBlob = 0x9d;
ASSERT(NBT::Helper::readDouble(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readDouble(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == 623593.6542742235);
std::cout << "Passed readDouble NBT helper test" << std::endl;
std::vector<uint8_t>* writeDoubleTestResult = new std::vector<uint8_t>();
NBT::Helper::writeDouble(writeDoubleTestResult, (double) -20.015625476837158203125);
std::vector<uint8_t> dereferencedWriteDoubleTestResult = *writeDoubleTestResult;
delete writeDoubleTestResult;
ASSERT(
dereferencedWriteDoubleTestResult[0] == (uint8_t) 0xC0 &&
dereferencedWriteDoubleTestResult[1] == (uint8_t) 0x34 &&
dereferencedWriteDoubleTestResult[2] == (uint8_t) 0x04 &&
dereferencedWriteDoubleTestResult[3] == (uint8_t) 0x00 &&
dereferencedWriteDoubleTestResult[4] == (uint8_t) 0x08 &&
dereferencedWriteDoubleTestResult[5] == (uint8_t) 0x00 &&
dereferencedWriteDoubleTestResult[6] == (uint8_t) 0x00 &&
dereferencedWriteDoubleTestResult[7] == (uint8_t) 0x00
);
std::cout << "Passed writeDouble NBT helper test" << std::endl;
//readString test
char* nextChar = new char;
//reading data from the java modified utf8 file
std::streampos javaSize;
std::string javaStdString;
const char* javaFilePath = "./resources/unicode_data/java-style_unicode";
std::ifstream javaFile(javaFilePath, std::ios::in | std::ios::binary | std::ios::ate);
if(javaFile.is_open()){
javaSize = javaFile.tellg();
javaFile.seekg(0, std::ios::beg);
for (int i=0; i<javaSize; i++) {
javaFile.read(nextChar, 1);
javaStdString.push_back(*nextChar);
}
javaFile.close();
} else {
std::cerr << "Failed to open file: " << javaFilePath << std::endl;
return 2;
}
//reading data from the normal utf8 file
std::streampos normalSize;
std::string normalStdString;
const char* normalFilePath = "./resources/unicode_data/normal_utf-8";
std::ifstream normalFile(normalFilePath, std::ios::in | std::ios::binary | std::ios::ate);
if(normalFile.is_open()){
normalSize = normalFile.tellg();
normalFile.seekg(0, std::ios::beg);
for (int i=0; i<normalSize; i++) {
normalFile.read(nextChar, 1);
normalStdString.push_back(*nextChar);
}
normalFile.close();
} else {
std::cerr << "Failed to open file: " << normalFilePath << std::endl;
return 2;
}
tiny_utf8::string normalString = tiny_utf8::string(normalStdString);
delete nextChar;
ASSERT(normalString == NBT::Helper::readString(reinterpret_cast<uint8_t*>(javaStdString.data()), javaStdString.size(), 0).value)
javaStdString[0] = '1';
ASSERT(NBT::Helper::readString(reinterpret_cast<uint8_t*>(javaStdString.data()), javaStdString.size(), 0).errorCode == ErrorCodes::OVERRUN);
ASSERT(NBT::Helper::readString(reinterpret_cast<uint8_t*>(javaStdString.data()), javaStdString.size(), javaStdString.size()).errorCode == ErrorCodes::OUT_OF_RANGE);
//reading data from the blob at the top of this file
currentPositionInBlob = 0x1cf;
ASSERT(NBT::Helper::readString(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).isError == false);
ASSERT(NBT::Helper::readString(validNBTDataBlob, validNBTDataBlobSize, currentPositionInBlob).value == "Hello World!");
std::cout << "Passed readString NBT helper test." << std::endl;
char* nextChar1 = new char;
//reading data from the java modified utf8 file
std::streampos javaSize1;
std::vector<uint8_t> javaStdString1;
const char* javaFilePath1 = "./resources/unicode_data/java-style_unicode";
std::ifstream javaFile1(javaFilePath1, std::ios::in | std::ios::binary | std::ios::ate);
if(javaFile1.is_open()){
javaSize1 = javaFile1.tellg();
javaFile1.seekg(0, std::ios::beg);
for (int i=0; i<javaSize1; i++) {
javaFile1.read(nextChar1, 1);
javaStdString1.push_back(*nextChar1);
}
javaFile1.close();
} else {
std::cerr << "Failed to open file: " << javaFilePath1 << std::endl;
return 2;
}
delete nextChar1;
std::vector<uint8_t>* exportedString = new std::vector<uint8_t>();
NBT::Helper::writeString(exportedString, normalString);
ASSERT(javaStdString1 == *exportedString);
//check that we get an error when trying to write a string that is too long
std::string overrunString = std::string(0x10000, '.');
ASSERT(NBT::Helper::writeString(exportedString, tiny_utf8::string(overrunString)).isError);
ASSERT(NBT::Helper::writeString(exportedString, tiny_utf8::string(overrunString)).errorCode == ErrorCodes::OVERRUN);
std::cout << "Passed writeString NBT helper test." << std::endl;
return 0;
}

View File

@ -1,257 +0,0 @@
// Copyright 2022, FOSS-VG Developers and Contributers
//
// 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 "assert.hpp"
#include "../lib/nbt.hpp"
#include "../lib/error.hpp"
int main() {
std::cout
<< "################################################################################\n"
<< "NBT size helper tests\n"
<< "################################################################################"
<< std::endl;
// a full copy of /resources/NBT_data/simple_nbt
//
// This contains all tag types. It is most likely not suitable for
// tests chasing edge cases as it is - as the name says - really
// basic NBT data.
uint8_t simpleNBTData[] = {
0x0a, 0x00, 0x00, 0x08, 0x00, 0x3e, 0x53, 0x70,
0x61, 0x63, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64,
0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c,
0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74,
0x65, 0x72, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20,
0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x20,
0x69, 0x6e, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6e,
0x61, 0x6d, 0x65, 0x73, 0x2c, 0x20, 0x72, 0x69,
0x67, 0x68, 0x74, 0x3f, 0x00, 0x16, 0x49, 0x64,
0x6b, 0x2e, 0x20, 0x4c, 0x65, 0x74, 0xe2, 0x80,
0x99, 0x73, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x20,
0x6f, 0x75, 0x74, 0x2e, 0x0a, 0x00, 0x08, 0x63,
0x6f, 0x6d, 0x70, 0x6f, 0x75, 0x6e, 0x64, 0x03,
0x00, 0x0b, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x6e,
0x75, 0x6d, 0x62, 0x65, 0x72, 0xd3, 0x07, 0x23,
0x41, 0x08, 0x00, 0x09, 0x73, 0x6f, 0x6d, 0x65,
0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x0c, 0x65,
0x61, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6f,
0x6b, 0x69, 0x65, 0x00, 0x06, 0x00, 0x06, 0x64,
0x6f, 0x75, 0x62, 0x6c, 0x65, 0x41, 0x23, 0x07,
0xd3, 0x4e, 0xfd, 0x07, 0xf1, 0x05, 0x00, 0x05,
0x66, 0x6c, 0x6f, 0x61, 0x74, 0x42, 0x0d, 0x12,
0x43, 0x02, 0x00, 0x05, 0x69, 0x6e, 0x74, 0x31,
0x36, 0x07, 0xd0, 0x03, 0x00, 0x05, 0x69, 0x6e,
0x74, 0x33, 0x32, 0x00, 0x9a, 0x21, 0x12, 0x0b,
0x00, 0x0b, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x5f,
0x61, 0x72, 0x72, 0x61, 0x79, 0x00, 0x00, 0x00,
0x04, 0x17, 0xbf, 0xe8, 0x3c, 0x00, 0x00, 0xa8,
0xfb, 0x7f, 0xff, 0xff, 0xff, 0x61, 0x72, 0x72,
0x61, 0x04, 0x00, 0x05, 0x69, 0x6e, 0x74, 0x36,
0x34, 0x00, 0x00, 0x00, 0xbc, 0x97, 0xde, 0x9e,
0x3e, 0x0c, 0x00, 0x0b, 0x69, 0x6e, 0x74, 0x36,
0x34, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x00,
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xa8, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x01,
0x67, 0xe7, 0xbd, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00, 0x00, 0xab, 0xd8, 0x00,
0x00, 0xab, 0xd8, 0x00, 0x0b, 0x12, 0x21, 0x00,
0x79, 0x61, 0x72, 0x01, 0x00, 0x04, 0x69, 0x6e,
0x74, 0x38, 0x64, 0x07, 0x00, 0x0a, 0x69, 0x6e,
0x74, 0x38, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79,
0x00, 0x00, 0x00, 0x08, 0x71, 0x35, 0x77, 0x62,
0x54, 0x64, 0xf5, 0x32, 0x09, 0x00, 0x09, 0x6c,
0x69, 0x73, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x38,
0x01, 0x00, 0x00, 0x00, 0x05, 0x41, 0x60, 0x4e,
0x7f, 0xfa, 0x09, 0x00, 0x0c, 0x6c, 0x69, 0x73,
0x74, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06,
0x50, 0x61, 0x63, 0x6d, 0x61, 0x6e, 0x00, 0x03,
0x61, 0x74, 0x65, 0x00, 0x03, 0x61, 0x6c, 0x6c,
0x00, 0x03, 0x74, 0x68, 0x65, 0x00, 0x04, 0x64,
0x6f, 0x74, 0x73, 0x00, 0x02, 0x73, 0x6f, 0x00,
0x03, 0x6e, 0x6f, 0x77, 0x00, 0x06, 0x68, 0x65,
0xe2, 0x80, 0x99, 0x73, 0x00, 0x06, 0x63, 0x6f,
0x6d, 0x69, 0x6e, 0x67, 0x00, 0x03, 0x66, 0x6f,
0x72, 0x00, 0x03, 0x74, 0x68, 0x65, 0x00, 0x06,
0x77, 0x6f, 0x72, 0x64, 0x73, 0x2e, 0x08, 0x00,
0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x00,
0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57,
0x6f, 0x72, 0x6c, 0x64, 0x21, 0x00
};
uint64_t simpleNBTDataSize = 478;
//##################################################################
// Basic valid data tests
//##################################################################
// const uint8_t END = 0;
ErrorOr<uint64_t> totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x93);
ErrorOr<int32_t> dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x93);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 1);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 0);
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 477);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 477);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 1);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 0);
//const uint8_t INT8 = 1;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x133);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x133);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 8);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
//const uint8_t INT16 = 2;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0xb1);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0xb1);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 10);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
//const uint8_t INT32 = 3;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0xbb);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0xbb);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 12);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x67);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x67);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 18);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
//const uint8_t INT64 = 4;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0xe9);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0xe9);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 16);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
//const uint8_t FLOAT = 5;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0xa5);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0xa5);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 12);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
//const uint8_t DOUBLE = 6;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x94);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x94);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 17);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 1);
//const uint8_t INT8_ARRAY = 7;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x13b);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x13b);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 25);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 8);
//const uint8_t STRING = 8;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x1c6);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x1c6);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 23);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 12);
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x79);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x79);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 26);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 12);
//const uint8_t LIST = 9;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x154);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x154);
ASSERT(totalSize.isError);
ASSERT(totalSize.errorCode == ErrorCodes::NOT_YET_KNOWN);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 5);
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x16a);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x16a);
ASSERT(totalSize.isError);
ASSERT(totalSize.errorCode == ErrorCodes::NOT_YET_KNOWN);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 12);
//const uint8_t COMPOUND = 10;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0x5c);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0x5c);
ASSERT(totalSize.isError);
ASSERT(totalSize.errorCode == ErrorCodes::NOT_YET_KNOWN);
ASSERT(dataLength.isError);
ASSERT(dataLength.errorCode == ErrorCodes::NOT_YET_KNOWN);
//const uint8_t INT32_ARRAY= 11;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0xc7);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0xc7);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 34);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 4);
//const uint8_t INT64_ARRAY= 12;
totalSize = NBT::Helper::totalTagSize(simpleNBTData, simpleNBTDataSize, 0xf9);
dataLength = NBT::Helper::containedDataLength(simpleNBTData, simpleNBTDataSize, 0xf9);
ASSERT(!totalSize.isError);
ASSERT(totalSize.value == 58);
ASSERT(!dataLength.isError);
ASSERT(dataLength.value == 5);
std::cout << "Passed simple valid data test." << std::endl;
//TODO: add tests for errors and edge cases
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +0,0 @@
// 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
#include <bitset>
#include <iomanip>
#include <iostream>
#include <vector>
#include "../lib/cli.hpp"
#include "../lib/file.hpp"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
int main(int argc, char* argv[]) {
std::vector<CLI::Flag> flags;
flags.push_back(CLI::Flag('x', "hexadecimal", "write output as hexadecimal numbers"));
flags.push_back(CLI::Flag('o', "octal", "write output as octal numbers"));
flags.push_back(CLI::Flag('b', "binary", "write output as binary numbers"));
flags.push_back(CLI::Flag('h', "help", "print help and exit"));
flags.push_back(CLI::Flag('l', "license", "print license information and exit"));
std::vector<CLI::Option> options;
std::vector<CLI::Argument> arguments;
arguments.push_back(CLI::Argument("FILE", "path of the file to dump"));
CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, options, arguments, "Dump files as C++ array literals");
if (cliParser.getFlag("help").value) {
std::cout << cliParser.getUsage() << std::endl;
return EXIT_SUCCESS;
}
if (cliParser.getFlag("license").value){
std::cout
<< "Copyright 2022, FOSS-VG Developers and Contributers\n"
<< "\n"
<< "ArrayDump is part of the FOSS-VG development tool suite.\n"
<< "\n"
<< "This program is free software: you can redistribute it and/or modify it\n"
<< "under the terms of the GNU Affero General Public License as published\n"
<< "by the Free Software Foundation, version 3.\n"
<< "\n"
<< "This program is distributed in the hope that it will be useful,\n"
<< "but WITHOUT ANY WARRANTY; without even the implied\n"
<< "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
<< "See the GNU Affero General Public License for more details.\n"
<< "\n"
<< "You should have received a copy of the GNU Affero General Public License\n"
<< "version 3 along with this program.\n"
<< "If not, see https://www.gnu.org/licenses/agpl-3.0.en.html"
<< std::endl;
return EXIT_SUCCESS;
}
if (cliParser.wrongUsage) {
std::cout << cliParser.getUsage() << std::endl;
return EXIT_USAGE;
}
ErrorOr<File*> filePointer = File::open(cliParser.getArgument(0).value, 'r');
if (filePointer.isError) {
std::cout << "Failed to open file: " << cliParser.getArgument(0).value << std::endl;
return EXIT_RUNTIME;
}
File* file = filePointer.value;
std::cout << "{";
if (cliParser.getFlag("hexadecimal").value) {
if (!file->eof()) {
std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int) file->readByte().value;
}
while (!file->eof()) {
std::cout << ", 0x" << std::setw(2) << std::setfill('0') << std::hex << (int) file->readByte().value;
}
} else if (cliParser.getFlag("octal").value) {
if (!file->eof()) {
std::cout << "0" << std::setw(3) << std::setfill('0') << std::oct << (int) file->readByte().value;
}
while (!file->eof()) {
std::cout << ", 0" << std::setw(3) << std::setfill('0') << std::oct << (int) file->readByte().value;
}
} else if (cliParser.getFlag("binary").value) {
if (!file->eof()) {
std::cout << "0b" << std::bitset<8>((int) file->readByte().value);
}
while (!file->eof()) {
std::cout << ", 0b" << std::bitset<8>((int) file->readByte().value);
}
} else {
if (!file->eof()) {
std::cout << (int) file->readByte().value;
}
while (!file->eof()) {
std::cout << ", " << (int) file->readByte().value;
}
}
std::cout << "}" << std::endl;
delete file;
return EXIT_SUCCESS;
}

View File

@ -1,130 +0,0 @@
// 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
#include <bitset>
#include <cstdint>
#include <iostream>
#include <vector>
#include "../lib/cli.hpp"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
int main(int argc, char* argv[]) {
std::vector<CLI::Flag> flags;
flags.push_back(CLI::Flag('x', "hexadecimal", "base 16"));
flags.push_back(CLI::Flag('o', "octal", "base 8"));
flags.push_back(CLI::Flag('b', "binary", "base 2"));
flags.push_back(CLI::Flag('d', "decimal", "base 10"));
flags.push_back(CLI::Flag('h', "help", "print help and exit"));
flags.push_back(CLI::Flag('l', "license", "print license information and exit"));
std::vector<CLI::Option> options;
std::vector<CLI::Argument> arguments;
arguments.push_back(CLI::Argument("NUMBER", "the number to convert"));
CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, options, arguments, "Convert numbers between predefined bases", "This only works for unsigned numbers.\n\tBaseConvert understands the prefixes 0x, 0b, 0o, and 0 in input numbers.");
if (cliParser.getFlag("help").value) {
std::cout << cliParser.getUsage() << std::endl;
return EXIT_SUCCESS;
}
if (cliParser.getFlag("license").value){
std::cout
<< "Copyright 2022, FOSS-VG Developers and Contributers\n"
<< "\n"
<< "BaseConvert is part of the FOSS-VG development tool suite.\n"
<< "\n"
<< "This program is free software: you can redistribute it and/or modify it\n"
<< "under the terms of the GNU Affero General Public License as published\n"
<< "by the Free Software Foundation, version 3.\n"
<< "\n"
<< "This program is distributed in the hope that it will be useful,\n"
<< "but WITHOUT ANY WARRANTY; without even the implied\n"
<< "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
<< "See the GNU Affero General Public License for more details.\n"
<< "\n"
<< "You should have received a copy of the GNU Affero General Public License\n"
<< "version 3 along with this program.\n"
<< "If not, see https://www.gnu.org/licenses/agpl-3.0.en.html"
<< std::endl;
return EXIT_SUCCESS;
}
if (cliParser.wrongUsage) {
std::cout << cliParser.getUsage() << std::endl;
return EXIT_USAGE;
}
std::string numberString = cliParser.getArgument(0).value;
uint8_t base = 10;
uint64_t* number = new uint64_t;
if (numberString == "") {
std::cerr << "Empty string is not a valid number." << std::endl;
return EXIT_RUNTIME;
}
if (numberString[0] == '0') {
if (numberString.substr(0, 2)=="0x" || numberString.substr(0, 2)=="0X") {
base = 16;
numberString = numberString.substr(2, numberString.length()-2);
} else if (numberString.substr(0, 2)=="0b" || numberString.substr(0, 2)=="0B") {
base = 2;
numberString = numberString.substr(2, numberString.length()-2);
} else if (numberString.substr(0, 2)=="0o" || numberString.substr(0, 2)=="0O") {
base = 8;
numberString = numberString.substr(2, numberString.length()-2);
} else {
base = 8;
}
}
*number = std::stoull(numberString, nullptr, base);
if (cliParser.getFlag("hexadecimal").value) {
std::cout << "0x" << std::hex << *number << std::endl;
} else if (cliParser.getFlag("octal").value) {
*number==0? std::cout << "0" << std::endl : std::cout << "0" << std::oct << *number << std::endl;
} else if (cliParser.getFlag("binary").value) {
std::bitset<64> bitset = std::bitset<64>(*number);
bool foundFirstBit = false;
std::cout << "0b";
for (uint8_t i=0; i<64; i++) {
if (bitset[63-i]) {
std::cout << "1";
foundFirstBit = true;
} else if (foundFirstBit) {
std::cout << "0";
}
}
if (!foundFirstBit) {
std::cout << "0";
}
std::cout << std::endl;
} else if (cliParser.getFlag("decimal").value) {
std::cout << *number << std::endl;
} else {
std::cout << cliParser.getUsage() << std::endl;
return EXIT_USAGE;
}
delete number;
return EXIT_SUCCESS;
}

View File

@ -13,360 +13,8 @@
// version 3 along with this program. // version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include <iostream> #include "../lib/nbt.h++"
#include <fstream>
#include <vector>
#include <tinyutf8/tinyutf8.h>
#include "../lib/nbt.hpp"
#include "../lib/cli.hpp"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
#define EXIT_UNIMPLEMENTED 3
void printTagTypeName(NBT::Tag::Generic* tag, uint64_t offsetBytes) {
std::cout << "[" << offsetBytes << ": ";
switch (tag->getTagType()) {
case NBT::TagType::END:
std::cout << "End";
break;
case NBT::TagType::INT8:
std::cout << "8 Bit Integer";
break;
case NBT::TagType::INT16:
std::cout << "16 Bit Integer";
break;
case NBT::TagType::INT32:
std::cout << "32 Bit Integer";
break;
case NBT::TagType::INT64:
std::cout << "64 Bit Integer";
break;
case NBT::TagType::FLOAT:
std::cout << "Float";
break;
case NBT::TagType::DOUBLE:
std::cout << "Double";
break;
case NBT::TagType::INT8_ARRAY:
std::cout << "Array of 8 Bit Integers";
break;
case NBT::TagType::STRING:
std::cout << "String";
break;
case NBT::TagType::LIST:
std::cout << "List";
break;
case NBT::TagType::COMPOUND:
std::cout << "Compound";
break;
case NBT::TagType::INT32_ARRAY:
std::cout << "Array of 32 Bit Integers";
break;
case NBT::TagType::INT64_ARRAY:
std::cout << "Array of 64 Bit Integers";
break;
default:
// WTF? How'd you even get here?
std::cout << "Unknown Type";
}
std::cout << "]";
if (tag->name == "") {
std::cout << ":";
} else {
std::cout << " " << tag->name << ":";
}
}
void printNBytes(uint64_t bytes) {
std::cout << bytes << (bytes==1? " byte":" bytes");
}
void drawTree(NBT::Tag::Generic* tag, tiny_utf8::string prefix, uint64_t offsetBytes, bool listMode=false) {
std::vector<uint8_t> serialized;
uint64_t headerSize = 0;
if (listMode) {
tag->serializeWithoutHeader(&serialized);
std::cout << prefix << "|Payload: ";
printNBytes(serialized.size());
std::cout << std::endl;
} else {
tag->serialize(&serialized);
if (tag->getTagType() == NBT::TagType::END) {
headerSize = 1;
} else {
headerSize = (uint64_t) NBT::Helper::readInt16(serialized.data(), serialized.size(), 1).value+3;
}
std::cout << prefix << "|Header: ";
printNBytes(headerSize);
std::cout << std::endl;
std::cout << prefix << "|Payload: ";
printNBytes(serialized.size() - headerSize);
std::cout << std::endl;
}
if (tag->getTagType() == NBT::TagType::END) {
std::cout << prefix << "'Total: ";
} else {
std::cout << prefix << "|Total: ";
}
printNBytes(serialized.size());
std::cout << std::endl;
switch (tag->getTagType()) {
case NBT::TagType::END:
break;
case NBT::TagType::INT8:
std::cout << prefix << "'Value: " << (int32_t) reinterpret_cast<NBT::Tag::Int8*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::INT16:
std::cout << prefix << "'Value: " << (int32_t) reinterpret_cast<NBT::Tag::Int16*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::INT32:
std::cout << prefix << "'Value: " << reinterpret_cast<NBT::Tag::Int32*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::INT64:
std::cout << prefix << "'Value: " << reinterpret_cast<NBT::Tag::Int64*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::FLOAT:
std::cout << prefix << "'Value: " << reinterpret_cast<NBT::Tag::Float*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::DOUBLE:
std::cout << prefix << "'Value: " << reinterpret_cast<NBT::Tag::Double*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::INT8_ARRAY: {
NBT::Tag::Int8Array* array = reinterpret_cast<NBT::Tag::Int8Array*>(tag);
std::cout << prefix << "|Length: " << array->length() << std::endl;
std::cout << prefix << "'Values: " << std::endl;
for (uint64_t i=0; i<array->length(); i++) {
if (i == array->length()-1) {
std::cout << prefix << " '" << (int64_t) array->getValue(i).value << std::endl;
} else {
std::cout << prefix << " |" << (int64_t) array->getValue(i).value << std::endl;
}
}
break;
}
case NBT::TagType::STRING:
std::cout << prefix << "'Value: " << reinterpret_cast<NBT::Tag::String*>(tag)->getValue() << std::endl;
break;
case NBT::TagType::LIST: {
NBT::Tag::List* list = reinterpret_cast<NBT::Tag::List*>(tag);
std::cout << prefix << "|Contained Type: ";
switch (list->getContainedType()) {
case NBT::TagType::END:
std::cout << "End";
break;
case NBT::TagType::INT8:
std::cout << "8 Bit Integer";
break;
case NBT::TagType::INT16:
std::cout << "16 Bit Integer";
break;
case NBT::TagType::INT32:
std::cout << "32 Bit Integer";
break;
case NBT::TagType::INT64:
std::cout << "64 Bit Integer";
break;
case NBT::TagType::FLOAT:
std::cout << "Float";
break;
case NBT::TagType::DOUBLE:
std::cout << "Double";
break;
case NBT::TagType::INT8_ARRAY:
std::cout << "Array of 8 Bit Integers";
break;
case NBT::TagType::STRING:
std::cout << "String";
break;
case NBT::TagType::LIST:
std::cout << "List";
break;
case NBT::TagType::COMPOUND:
std::cout << "Compound";
break;
case NBT::TagType::INT32_ARRAY:
std::cout << "Array of 32 Bit Integers";
break;
case NBT::TagType::INT64_ARRAY:
std::cout << "Array of 64 Bit Integers";
break;
default:
// WTF? How'd you even get here?
std::cout << "Unknown Type";
}
std::cout << std::endl;
if (list->length() > 0) {
std::cout << prefix << "|Length: " << list->length() << std::endl;
std::cout << prefix << "|" << std::endl;
} else {
std::cout << prefix << "'Length: " << list->length() << std::endl;
}
offsetBytes = offsetBytes + headerSize + 5;
for (uint64_t i=0; i<list->length(); i++) {
if (i == list->length()-1) {
std::cout << prefix << "'";
printTagTypeName(list->getElementPointer(i).value, offsetBytes);
std::cout << std::endl;
drawTree(list->getElementPointer(i).value, prefix+" ", offsetBytes, true);
} else {
std::cout << prefix << "|";
printTagTypeName(list->getElementPointer(i).value, offsetBytes);
std::cout << std::endl;
drawTree(list->getElementPointer(i).value, prefix+"| ", offsetBytes, true);
}
std::vector<uint8_t> serializedElement;
list->getElementPointer(i).value->serializeWithoutHeader(&serializedElement);
offsetBytes += serializedElement.size();
}
break;
}
case NBT::TagType::COMPOUND: {
NBT::Tag::Compound* compound = reinterpret_cast<NBT::Tag::Compound*>(tag);
std::cout << prefix << "|Length: " << compound->length() << std::endl;
std::cout << prefix << "|" << std::endl;
offsetBytes = offsetBytes + headerSize;
for (uint64_t i=0; i<compound->length(); i++) {
if (i == compound->length()-1) {
std::cout << prefix << "'";
printTagTypeName(compound->getElementPointer(i).value, offsetBytes);
std::cout << std::endl;
drawTree(compound->getElementPointer(i).value, prefix+" ", offsetBytes);
} else {
std::cout << prefix << "|";
printTagTypeName(compound->getElementPointer(i).value, offsetBytes);
std::cout << std::endl;
drawTree(compound->getElementPointer(i).value, prefix+"| ", offsetBytes);
}
std::vector<uint8_t> serializedElement;
compound->getElementPointer(i).value->serialize(&serializedElement);
offsetBytes += serializedElement.size();
}
break;
}
case NBT::TagType::INT32_ARRAY: {
NBT::Tag::Int32Array* array = reinterpret_cast<NBT::Tag::Int32Array*>(tag);
std::cout << prefix << "|Length: " << array->length() << std::endl;
std::cout << prefix << "'Values: " << std::endl;
for (uint64_t i=0; i<array->length(); i++) {
if (i == array->length()-1) {
std::cout << prefix << " '" << array->getValue(i).value << std::endl;
} else {
std::cout << prefix << " |" << array->getValue(i).value << std::endl;
}
}
break;
}
case NBT::TagType::INT64_ARRAY: {
NBT::Tag::Int64Array* array = reinterpret_cast<NBT::Tag::Int64Array*>(tag);
std::cout << prefix << "|Length: " << array->length() << std::endl;
std::cout << prefix << "'Values: " << std::endl;
for (uint64_t i=0; i<array->length(); i++) {
if (i == array->length()-1) {
std::cout << prefix << " '" << array->getValue(i).value << std::endl;
} else {
std::cout << prefix << " |" << array->getValue(i).value << std::endl;
}
}
break;
}
default:
// WTF? How'd you even get here?
std::cout << prefix << "'???" << std::endl;
}
std::cout << prefix << std::endl;
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::vector<CLI::Flag> flags; return 0;
flags.push_back(CLI::Flag('h', "help", "print help and exit"));
flags.push_back(CLI::Flag('l', "license", "print license information and exit"));
flags.push_back(CLI::Flag('x', "hexadecimal", "print numbers in hex format"));
std::vector<CLI::Option> options;
std::vector<CLI::Argument> arguments;
arguments.push_back(CLI::Argument("FILE", "path of the file to dump"));
CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, options, arguments, "Present NBT in human or machine readable formats");
if (cliParser.getFlag("help").value){
std::cout << cliParser.getUsage() << std::endl;
return EXIT_SUCCESS;
}
if (cliParser.getFlag("license").value){
std::cout
<< "Copyright 2022, FOSS-VG Developers and Contributers\n"
<< "\n"
<< "DumpNBT is part of the FOSS-VG development tool suite.\n"
<< "\n"
<< "This program is free software: you can redistribute it and/or modify it\n"
<< "under the terms of the GNU Affero General Public License as published\n"
<< "by the Free Software Foundation, version 3.\n"
<< "\n"
<< "This program is distributed in the hope that it will be useful,\n"
<< "but WITHOUT ANY WARRANTY; without even the implied\n"
<< "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
<< "See the GNU Affero General Public License for more details.\n"
<< "\n"
<< "You should have received a copy of the GNU Affero General Public License\n"
<< "version 3 along with this program.\n"
<< "If not, see https://www.gnu.org/licenses/agpl-3.0.en.html"
<< std::endl;
return EXIT_SUCCESS;
}
if (cliParser.wrongUsage) {
std::cerr << cliParser.getUsage() << std::endl;
return EXIT_USAGE;
}
std::ifstream fileStream;
fileStream.open(cliParser.getArgument(0).value, std::ios::in | std::ios::binary | std::ios::ate);
if (!fileStream.is_open()) {
std::cerr << argv[0] << ": Could not open file: " << cliParser.getArgument(0).value << std::endl;
return EXIT_RUNTIME;
}
uint64_t fileSize = fileStream.tellg();
fileStream.seekg(0, std::ios::beg);
uint8_t data[fileSize];
uint8_t* nextByte = new uint8_t;
for (uint64_t i=0; i<fileSize; i++) {
fileStream.read(reinterpret_cast<char*>(nextByte), 1);
data[i] = *nextByte;
}
fileStream.close();
ErrorOr<std::vector<NBT::Tag::Generic*>> tags = NBT::deserialize(data, fileSize);
if (tags.isError) {
std::cerr << "Invalid data." << std::endl;
return EXIT_RUNTIME;
}
uint64_t offsetBytes = 0;
for (uint64_t i=0; i<tags.value.size(); i++) {
printTagTypeName(tags.value[i], offsetBytes);
std::cout << std::endl;
drawTree(tags.value[i], "", offsetBytes);
std::vector<uint8_t> serialized;
tags.value[i]->serialize(&serialized);
offsetBytes += serialized.size();
}
return EXIT_SUCCESS;
} }

View File

@ -1,8 +1,5 @@
//Copyright 2022, FOSS-VG Developers and Contributers //Copyright 2022, FOSS-VG Developers and Contributers
// //
// Author(s):
// BodgeMaster, Shwoomple
//
//This program is free software: you can redistribute it and/or modify it //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 //under the terms of the GNU Affero General Public License as published
//by the Free Software Foundation, version 3. //by the Free Software Foundation, version 3.
@ -16,479 +13,68 @@
//version 3 along with this program. //version 3 along with this program.
//If not, see https://www.gnu.org/licenses/agpl-3.0.en.html //If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include <chrono>
#include <csignal>
#include <iomanip>
#include <iostream> #include <iostream>
#include <mutex>
#include <string> #include <string>
#include <sockpp/tcp_acceptor.h> #include <cstdint>
#include <sockpp/tcp_connector.h> #include <cctype>
#include <sockpp/tcp6_acceptor.h>
#include <sockpp/tcp6_connector.h>
#include <sockpp/udp_socket.h>
#include <sockpp/udp6_socket.h>
#include <thread>
#include <vector>
#include "../lib/cli.hpp" #include "../lib/error.h++"
#include "../lib/error.hpp" #include "../lib/cli.h++"
#define EXIT_SUCCESS 0 #define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1 #define EXIT_RUNTIME 1
#define EXIT_USAGE 2 #define EXIT_USAGE 2
#define EXIT_UNIMPLEMENTED 3 #define EXIT_UNIMPLEMENTED 3
#define EXIT_SIGNAL 4
// TCP v4 server int main(int argc, char* argv[]){
sockpp::tcp_socket tcpSocket;
sockpp::tcp_acceptor tcpAcceptor;
// TCP v4 client
sockpp::tcp_connector tcpConnector;
// TCP v6 server
sockpp::tcp6_socket tcp6Socket;
sockpp::tcp6_acceptor tcp6Acceptor;
// TCP v6 client
sockpp::tcp6_connector tcp6Connector;
// UDP v4 server and client
sockpp::udp_socket udpSocket;
// UDP v6 server and client
sockpp::udp6_socket udp6Socket;
bool exitProgram = false; // Argument parsing ################################################
bool ipv4 = true;
bool ipv4 = false; bool ipv6 = true;
bool ipv6 = false; bool tcp = true;
bool tcp = false; bool udp = true;
bool udp = false; bool listen = false;
bool outgoing = false; std::string host;
std::string host; uint16_t port;
in_port_t port;
// This is probably bigger than the MTU on any given network.
// This should allow us to read entire packets at once when they arrive
// slowly enough to be read individually.
const uint32_t bufferSize = 2048;
uint8_t networkBuffer[bufferSize];
std::string readByteFromStdin() {
std::string input = "";
// read 2 characters from stdin
char characterInput;
bool readByte = false;
uint16_t iterationsSinceLastInput = 0;
while (input.length() < 2 && !exitProgram) {
if (std::cin.good()) {
std::cin.get(characterInput);
readByte = true;
} else {
readByte = false;
}
// ignore space, tabs, and newlines
if (readByte && characterInput!=' ' && characterInput!='\n' && characterInput!='\r' && characterInput!='\t') {
input.push_back(characterInput);
}
iterationsSinceLastInput++;
if (readByte) {
iterationsSinceLastInput = 0;
}
if (iterationsSinceLastInput>1024) {
// prevent integer overflow
iterationsSinceLastInput = 1024;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
return exitProgram? "" : std::string(1, (char) std::stoi(input, nullptr, 16));
}
void signalHandler(int signal) {
// shut down gracefully
exitProgram = true;
// tell sockpp to close TCP socket if open because it blocks when trying
// to read and there is no data
tcpAcceptor.shutdown();
if (tcpSocket) {
tcpSocket.shutdown(SHUT_RD);
}
if (tcpConnector) {
tcpConnector.shutdown(SHUT_RD);
}
if (tcp6Socket) {
tcp6Socket.shutdown(SHUT_RD);
}
if (tcp6Connector) {
tcp6Connector.shutdown(SHUT_RD);
}
if (udpSocket) {
udpSocket.shutdown(SHUT_RD);
}
if (udp6Socket) {
udp6Socket.shutdown(SHUT_RD);
}
std::cerr << "Received signal " << signal << ", bye!" << std::endl;
std::exit(EXIT_SIGNAL);
}
void readFromTCP() {
ssize_t byteCount;
while (!exitProgram && (outgoing? (byteCount = tcpConnector.read(networkBuffer, bufferSize)) > 0 : (byteCount = tcpSocket.read(networkBuffer, bufferSize)) > 0)) {
for (ssize_t i=0; i<byteCount; i++) {
std::cout << std::hex << std::setfill('0') << std::setw(2) << (short) networkBuffer[i] << " ";
}
std::cout.flush();
}
}
void writeToTCP() {
while (!exitProgram) {
if (outgoing) {
if (tcpConnector.write(readByteFromStdin()) == -1 && !exitProgram) {
exitProgram = true;
tcpConnector.shutdown(SHUT_RD);
std::cerr << "Error while sending data: " << tcpConnector.last_error_str() << std::endl;
return;
}
} else {
if (tcpSocket.write(readByteFromStdin()) == -1 && !exitProgram) {
exitProgram = true;
tcpSocket.shutdown(SHUT_RD);
std::cerr << "Error while sending data: " << tcpSocket.last_error_str() << std::endl;
return;
}
}
}
}
void readFromTCP6() {
ssize_t byteCount;
while (!exitProgram && (outgoing? (byteCount = tcp6Connector.read(networkBuffer, bufferSize)) > 0 : (byteCount = tcp6Socket.read(networkBuffer, bufferSize)) > 0)) {
for (ssize_t i=0; i<byteCount; i++) {
std::cout << std::hex << std::setfill('0') << std::setw(2) << (short) networkBuffer[i] << " ";
}
std::cout.flush();
}
}
void writeToTCP6() {
while (!exitProgram) {
if (outgoing) {
if (tcp6Connector.write(readByteFromStdin()) == -1 && !exitProgram) {
exitProgram = true;
tcp6Connector.shutdown(SHUT_RD);
std::cerr << "Error while sending data: " << tcp6Connector.last_error_str() << std::endl;
return;
}
} else {
if (tcp6Socket.write(readByteFromStdin()) == -1 && !exitProgram) {
exitProgram = true;
tcp6Socket.shutdown(SHUT_RD);
std::cerr << "Error while sending data: " << tcp6Socket.last_error_str() << std::endl;
return;
}
}
}
}
void readFromUDP() {
ssize_t byteCount;
sockpp::udp_socket::addr_t peer;
while ((byteCount = udpSocket.recv_from(networkBuffer, bufferSize, &peer)) > 0) {
std::cout << peer << ": ";
for (ssize_t i=0; i<byteCount; i++) {
std::cout << std::hex << std::setfill('0') << std::setw(2) << (short) networkBuffer[i] << " ";
}
std::cout << std::dec << std::endl;
}
}
void writeToUDP(sockpp::udp_socket::addr_t peer) {
while (!exitProgram) {
if (udpSocket.send_to(readByteFromStdin(), peer) == -1 && !exitProgram) {
exitProgram = true;
udpSocket.shutdown(SHUT_RD);
std::cerr << "Error while sending data: " << udpSocket.last_error_str() << std::endl;
return;
}
}
}
void readFromUDP6() {
ssize_t byteCount;
sockpp::udp6_socket::addr_t peer;
while ((byteCount = udp6Socket.recv_from(networkBuffer, bufferSize, &peer)) > 0) {
std::cout << peer << ": ";
for (ssize_t i=0; i<byteCount; i++) {
std::cout << std::hex << std::setfill('0') << std::setw(2) << (short) networkBuffer[i] << " ";
}
std::cout << std::dec << std::endl;
}
}
void writeToUDP6(sockpp::udp6_socket::addr_t peer) {
while (!exitProgram) {
if (udp6Socket.send_to(readByteFromStdin(), peer) == -1 && !exitProgram) {
exitProgram = true;
udp6Socket.shutdown(SHUT_RD);
std::cerr << "Error while sending data: " << udp6Socket.last_error_str() << std::endl;
return;
}
}
}
int main(int argc, char* argv[]) {
std::signal(SIGINT, signalHandler);
std::signal(SIGTERM, signalHandler);
std::vector<CLI::Flag> flags; std::vector<CLI::Flag> flags;
flags.push_back(CLI::Flag('4', "ipv4", "use IPv4, either this or IPv6 has to be specified")); flags.push_back(CLI::Flag('4', "ipv4", "use IPv4, defaults to both when -4 and -6 are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('6', "ipv6", "use IPv6, either this or IPv4 has to be specified")); flags.push_back(CLI::Flag('6', "ipv6", "use IPv6, defaults to both when -4 and -6 are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('t', "tcp", "use TCP, either this or UDP has to be specified")); flags.push_back(CLI::Flag('t', "tcp", "use TCP, defaults to both when -t and -u are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('u', "udp", "use UDP, either this or TCP has to be specified")); flags.push_back(CLI::Flag('u', "udp", "use UDP, defaults to both when -t and -u are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('h', "help", "print this information and exit"));
flags.push_back(CLI::Flag('l', "license", "print license information and exit"));
std::vector<CLI::Option> options; std::vector<CLI::UnpositionalArgument> unpositionalArguments;
options.push_back(CLI::Option('c', "connect", "HOST", "make an outgoing connection to HOST instead of listening for an incoming connection")); unpositionalArguments.push_back(CLI::UnpositionalArgument('c', "connect", "HOST", "connect to HOST, listen for incoming connections if omitted"));
options.push_back(CLI::Option('b', "bind", "ADDRESS", "(UDP only) bind to ADDRESS instead of localhost"));
std::vector<CLI::Argument> arguments; std::vector<CLI::PositionalArgument> positionalArguments;
arguments.push_back(CLI::Argument("PORT", "the port to lsiten on (or connect to)")); positionalArguments.push_back(CLI::PositionalArgument("PORT", "the port to use"));
CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, options, arguments, "Arbitrary TCP/UDP connections in hex format", "Spaces, tabs, newlines, and carriage returns in the input are ignored.\n\tYou may want to disable input echoing using `stty -echo` (reenable using `stty echo`).\n\tNote that many terminals do line buffering on the input by default."); CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, unpositionalArguments, positionalArguments);
if (cliParser.getFlag("help").value) {
std::cout << cliParser.getUsage() << std::endl;
return EXIT_SUCCESS;
}
if (cliParser.getFlag("license").value) {
std::cout
<< "Copyright 2022, FOSS-VG Developers and Contributers\n"
<< "\n"
<< "Hexnet is part of the FOSS-VG development tool suite.\n"
<< "\n"
<< "This program is free software: you can redistribute it and/or modify it\n"
<< "under the terms of the GNU Affero General Public License as published\n"
<< "by the Free Software Foundation, version 3.\n"
<< "\n"
<< "This program is distributed in the hope that it will be useful,\n"
<< "but WITHOUT ANY WARRANTY; without even the implied\n"
<< "warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
<< "See the GNU Affero General Public License for more details.\n"
<< "\n"
<< "You should have received a copy of the GNU Affero General Public License\n"
<< "version 3 along with this program.\n"
<< "If not, see https://www.gnu.org/licenses/agpl-3.0.en.html"
<< std::endl;
return EXIT_SUCCESS;
}
if (cliParser.wrongUsage) { if (cliParser.wrongUsage) {
std::cout << cliParser.getUsage() << std::endl; //TODO: spit out usage information generated by the parser
return EXIT_USAGE; return EXIT_USAGE;
} }
if (cliParser.getFlag('4').value || cliParser.getFlag('6').value) {
ipv4 = cliParser.getFlag("ipv4").value; ipv4 = cliParser.getFlag('4').value;
ipv6 = cliParser.getFlag("ipv6").value; ipv6 = cliParser.getFlag('6').value;
tcp = cliParser.getFlag("tcp").value;
udp = cliParser.getFlag("udp").value;
if (cliParser.getOption("connect").errorCode != ErrorCodes::NOT_PRESENT) {
outgoing = true;
host = cliParser.getOption("connect").value;
} }
if (cliParser.getFlag('t').value || cliParser.getFlag('u').value) {
if (!(ipv4 || ipv6) || (ipv4 && ipv6) || !(tcp || udp) || (tcp && udp)) { tcp = cliParser.getFlag('t').value;
std::cout << "Please specify which protocols to use (one of IPv4/IPv6, one of TCP/UDP)." << std::endl; udp = cliParser.getFlag('u').value;
return EXIT_USAGE;
} }
if (cliParser.getUnpositionalArgument('c').errorCode == ErrorCodes::NOT_PRESENT) {
port = (in_port_t) std::stoi(cliParser.getArgument(0).value); listen = true;
if (outgoing) {
if (tcp) {
std::cerr << "Connecting to " << host << " on port " << (int) port << " (TCP)..." << std::endl;
if (ipv4) {
// TCP v4 out
tcpConnector = sockpp::tcp_connector({host, port});
if (!tcpConnector) {
std::cerr << "Error connecting to " << host << " on port " << port << std::endl;
std::cerr << tcpConnector.last_error_str() << std::endl;
return EXIT_RUNTIME;
} }
host = cliParser.getUnpositionalArgument('c').value;
//FIXME: use a function that returns a fixed-width data type instead
port = (uint16_t) std::stoi(cliParser.getPositionalArgument(0).value);
std::thread threadReadFromTCP = std::thread(readFromTCP); std::cerr << "Port: " << (int) port << std::endl;
std::thread threadWriteToTCP = std::thread(writeToTCP); std::cerr << (listen? "Listening" : "Host: ") << host << std::endl;
std::cerr << "IPv4: " << (ipv4? "yes" : "no") << std::endl;
threadReadFromTCP.join(); std::cerr << "IPv6: " << (ipv6? "yes" : "no") << std::endl;
threadWriteToTCP.join(); std::cerr << "TCP: " << ( tcp? "yes" : "no") << std::endl;
std::cerr << "UDP: " << ( udp? "yes" : "no") << std::endl;
std::cout << std::endl; return EXIT_UNIMPLEMENTED;
return EXIT_SUCCESS;
} else {
// TCP v6 out
tcp6Connector = sockpp::tcp6_connector({host, port});
if (!tcp6Connector) {
std::cerr << "Error connecting to " << host << " on port " << port << std::endl;
std::cerr << tcp6Connector.last_error_str() << std::endl;
return EXIT_RUNTIME;
}
std::thread threadReadFromTCP6 = std::thread(readFromTCP6);
std::thread threadWriteToTCP6 = std::thread(writeToTCP6);
threadReadFromTCP6.join();
threadWriteToTCP6.join();
std::cout << std::endl;
return EXIT_SUCCESS;
}
} else {
std::cerr << "Talking to " << host << " on port " << (int) port << " (UDP)..." << std::endl;
if (ipv4) {
// UDP v4 out
if (!udpSocket) {
std::cerr << "Error creating UDP socket: " << udpSocket.last_error_str() << std::endl;
}
// Btw: Did you know that UDP has no concept of a connection?
sockpp::udp_socket::addr_t peer = sockpp::inet_address(host, port);
if (!udpSocket.connect(peer)) {
std::cerr << "Error associating socket with " << host << " port " << port << std::endl;
std::cerr << udpSocket.last_error_str() << std::endl;
}
std::thread threadReadFromUDP = std::thread(readFromUDP);
std::thread threadWriteToUDP = std::thread(writeToUDP, peer);
threadReadFromUDP.join();
threadWriteToUDP.join();
std::cout << std::endl;
return EXIT_SUCCESS;
} else {
// UDP v6 out
if (!udp6Socket) {
std::cerr << "Error creating UDP socket: " << udp6Socket.last_error_str() << std::endl;
}
// Btw: Did you know that UDP has no concept of a connection?
sockpp::udp6_socket::addr_t peer = sockpp::inet6_address(host, port);
if (!udp6Socket.connect(peer)) {
std::cerr << "Error associating socket with " << host << " port " << port << std::endl;
std::cerr << udp6Socket.last_error_str() << std::endl;
}
std::thread threadReadFromUDP6 = std::thread(readFromUDP6);
std::thread threadWriteToUDP6 = std::thread(writeToUDP6, peer);
threadReadFromUDP6.join();
threadWriteToUDP6.join();
std::cout << std::endl;
return EXIT_SUCCESS;
}
}
} else {
if (tcp) {
std::cerr << "Listening on port " << (int) port << " (TCP)..." << std::endl;
if (ipv4) {
// TCP v4 in
tcpAcceptor = sockpp::tcp_acceptor(port);
if (!tcpAcceptor) {
std::cerr << "Error while creating TCP acceptor: " << tcpAcceptor.last_error_str() << std::endl;
return EXIT_RUNTIME;
}
sockpp::inet_address peer;
tcpSocket = tcpAcceptor.accept(&peer);
std::cerr << "Incoming connection from " << peer << std::endl;
if (!tcpSocket) {
std::cerr << "Error on incoming connection: " << tcpAcceptor.last_error_str() << std::endl;
return EXIT_RUNTIME;
}
std::thread threadReadFromTCP = std::thread(readFromTCP);
std::thread threadWriteToTCP = std::thread(writeToTCP);
threadReadFromTCP.join();
threadWriteToTCP.join();
std::cout << std::endl;
return EXIT_SUCCESS;
} else {
// TCP v6 in
tcp6Acceptor = sockpp::tcp6_acceptor(port);
if (!tcp6Acceptor) {
std::cerr << "Error while creating TCP acceptor: " << tcp6Acceptor.last_error_str() << std::endl;
return EXIT_RUNTIME;
}
sockpp::inet6_address peer;
tcp6Socket = tcp6Acceptor.accept(&peer);
std::cerr << "Incoming connection from " << peer << std::endl;
if (!tcp6Socket) {
std::cerr << "Error on incoming connection: " << tcp6Acceptor.last_error_str() << std::endl;
return EXIT_RUNTIME;
}
std::thread threadReadFromTCP6 = std::thread(readFromTCP6);
std::thread threadWriteToTCP6 = std::thread(writeToTCP6);
threadReadFromTCP6.join();
threadWriteToTCP6.join();
std::cout << std::endl;
return EXIT_SUCCESS;
}
} else {
std::string address = "localhost";
if (cliParser.getOption("bind").errorCode != ErrorCodes::NOT_PRESENT) {
address = cliParser.getOption("bind").value;
}
std::cerr << "Listening on " << address << " port " << (int) port << " (UDP)..." << std::endl;
if (ipv4) {
// UDP v4 in
if (!udpSocket) {
std::cerr << "Error creating UDP socket: " << udpSocket.last_error_str() << std::endl;
}
if (!udpSocket.bind(sockpp::inet_address(address, port))) {
std::cerr << "Error binding UDP socket to " << address << " port " << port << ": " << udpSocket.last_error_str() << std::endl;
}
std::thread threadReadFromUDP = std::thread(readFromUDP);
// Can't send bc we have no idea where to send to.
//std::thread threadWriteToUDP = std::thread(writeToUDP);
threadReadFromUDP.join();
//threadWriteToUDP.join();
std::cout << std::endl;
return EXIT_SUCCESS;
} else {
// UDP v6 in
if (!udp6Socket) {
std::cerr << "Error creating UDP socket: " << udp6Socket.last_error_str() << std::endl;
}
if (!udp6Socket.bind(sockpp::inet6_address(address, port))) {
std::cerr << "Error binding UDP socket to " << address << " port " << port << ": " << udp6Socket.last_error_str() << std::endl;
}
std::thread threadReadFromUDP6 = std::thread(readFromUDP6);
// Can't send bc we have no idea where to send to.
//std::thread threadWriteToUDP6 = std::thread(writeToUDP6);
threadReadFromUDP6.join();
//threadWriteToUDP6.join();
std::cout << std::endl;
return EXIT_SUCCESS;
}
}
}
} }