Compare commits
	
		
			183 Commits 
		
	
	
		
			0462756451
			...
			38fad56965
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  Shwoomple | 38fad56965 | |
|  BodgeMaster | f6b965040d | |
|  BodgeMaster | b5c18cd0de | |
|  BodgeMaster | a1ba08b7db | |
|  BodgeMaster | 1d7e98d0b3 | |
|  BodgeMaster | a7e07d2c3c | |
|  BodgeMaster | 7ae843039c | |
|  BodgeMaster | 5574cdb4bf | |
|  BodgeMaster | 89cfb9d850 | |
|  BodgeMaster | f681c54c82 | |
|  BodgeMaster | ee0ebb273c | |
|  Shwoomple | 8dea1f2d31 | |
|  Shwoomple | c825c73afd | |
|  Shwoomple | e7711a3d59 | |
|  Shwoomple | e7ce6f5cd4 | |
|  BodgeMaster | ee9b5d4f67 | |
|  BodgeMaster | b84130344d | |
|  BodgeMaster | 25d7806f6d | |
|  Shwoomple | 6e57a86338 | |
|  Shwoomple | c54eb48887 | |
|  BodgeMaster | c14504ce0b | |
|  BodgeMaster | 92cf81c1b4 | |
|  BodgeMaster | df35243ee9 | |
|  BodgeMaster | 36dcf0a0f5 | |
|  BodgeMaster | 93fdcb7b65 | |
|  BodgeMaster | 8d2f3f2fa5 | |
|  BodgeMaster | cdc23e7468 | |
|  BodgeMaster | e9bfb6eeee | |
|  BodgeMaster | 8b62ec9c88 | |
|  BodgeMaster | ca0af3306f | |
|  BodgeMaster | 9b21dfaee5 | |
|  BodgeMaster | 996154fbbc | |
|  BodgeMaster | 77dd79398f | |
|  BodgeMaster | fd5fe3967f | |
|  BodgeMaster | 374466f26c | |
|  BodgeMaster | 3b56a52085 | |
|  BodgeMaster | 7be73f86d4 | |
|  BodgeMaster | 71834e1018 | |
|  BodgeMaster | ccce564219 | |
|  BodgeMaster | 53279c6905 | |
|  BodgeMaster | f8dd10d301 | |
|  BodgeMaster | 6672a4f149 | |
|  BodgeMaster | d5ce50a4a9 | |
|  BodgeMaster | f0092b78d3 | |
|  BodgeMaster | b1ba33b39f | |
|  BodgeMaster | 833c09e2da | |
|  BodgeMaster | b53999a548 | |
|  BodgeMaster | 379903d751 | |
|  BodgeMaster | 17792ec5bf | |
|  BodgeMaster | 9ce35b5c6b | |
|  BodgeMaster | be08a97275 | |
|  BodgeMaster | 6bdf99c897 | |
|  BodgeMaster | 936def1a65 | |
|  BodgeMaster | 3cc1222de9 | |
|  BodgeMaster | ecf3b14b5a | |
|  BodgeMaster | 8da758becd | |
|  BodgeMaster | 1e5051b503 | |
|  BodgeMaster | 3b4c125ca2 | |
|  BodgeMaster | 4cb1206839 | |
|  BodgeMaster | 39c5940200 | |
|  BodgeMaster | f3e03710f6 | |
|  BodgeMaster | 72fc923839 | |
|  BodgeMaster | 5059bd0193 | |
|  BodgeMaster | 8bb633f118 | |
|  BodgeMaster | ec44ac9531 | |
|  BodgeMaster | 341b4c187e | |
|  BodgeMaster | 79650e390e | |
|  BodgeMaster | 2d2b67373c | |
|  BodgeMaster | 4c4366f7e6 | |
|  BodgeMaster | 4ef1d2c44f | |
|  Shwoomple | 5920d1d004 | |
|  Shwoomple | 056c1e6b11 | |
|  BodgeMaster | 8b1491c311 | |
|  BodgeMaster | fc2caf3bc0 | |
|  BodgeMaster | 89baeebc65 | |
|  Shwoomple | c204aa7d76 | |
|  BodgeMaster | 09503d3dc7 | |
|  BodgeMaster | 9fb06c998e | |
|  BodgeMaster | aa97154474 | |
|  BodgeMaster | 9abfd9e5a9 | |
|  Shwoomple | 150ce826ba | |
|  Shwoomple | 3449e3b9c4 | |
|  Charlie Root | 213d28a9b8 | |
|  Shwoomple | c1d7801436 | |
|  Shwoomple | 9610f4a4a2 | |
|  BodgeMaster | 434c976cc5 | |
|  BodgeMaster | 47fd1f8970 | |
|  BodgeMaster | 10b1d9fa0c | |
|  Shwoomple | e8d41efeef | |
|  Shwoomple | 508b5e67e7 | |
|  BodgeMaster | 398321e415 | |
|  BodgeMaster | 53878c3e2b | |
|  BodgeMaster | ad291ee77d | |
|  BodgeMaster | 6149418f52 | |
|  BodgeMaster | ac12bcf865 | |
|  BodgeMaster | 8b92d24ab9 | |
|  BodgeMaster | aab91a2523 | |
|  BodgeMaster | 58b1199e38 | |
|  Shwoomple | 48f8a7dcf2 | |
|  BodgeMaster | 209d0828b4 | |
|  BodgeMaster | b4d4ce77b2 | |
|  BodgeMaster | ee5048331c | |
|  BodgeMaster | 629c999336 | |
|  BodgeMaster | cdd17045d1 | |
|  BodgeMaster | bb40f6553e | |
|  BodgeMaster | a9759e3bc2 | |
|  BodgeMaster | ab1164557d | |
|  BodgeMaster | 1b8819ffe5 | |
|  BodgeMaster | 4934a78aaa | |
|  BodgeMaster | 327ad9a9b5 | |
|  BodgeMaster | bddab2e9f8 | |
|  BodgeMaster | 017c8a61f8 | |
|  BodgeMaster | 5272636cb8 | |
|  BodgeMaster | 91d16ea451 | |
|  BodgeMaster | a1fc0ce4b4 | |
|  Shwoomple | ca7b121c4d | |
|  BodgeMaster | 25bec4c587 | |
|  BodgeMaster | 589cf1ddaf | |
|  BodgeMaster | 884a5239c6 | |
|  BodgeMaster | 9190cad80d | |
|  BodgeMaster | a862590370 | |
|  BodgeMaster | 3995e97f03 | |
|  BodgeMaster | c9ec524db1 | |
|  BodgeMaster | 73ae58e522 | |
|  BodgeMaster | acc19ae100 | |
|  BodgeMaster | 149285c357 | |
|  BodgeMaster | 0c92bdf8fd | |
|  BodgeMaster | 86f1ef596f | |
|  BodgeMaster | 027f324f03 | |
|  BodgeMaster | 7a2c1d7d57 | |
|  BodgeMaster | 6fecb2cdb7 | |
|  BodgeMaster | e882a09099 | |
|  BodgeMaster | 748c91c375 | |
|  BodgeMaster | f5d85da98c | |
|  BodgeMaster | 396b9673fd | |
|  BodgeMaster | 68fbf3ae20 | |
|  BodgeMaster | 4363432025 | |
|  BodgeMaster | 5400790e78 | |
|  BodgeMaster | c7dd5471dd | |
|  BodgeMaster | 8048dc8891 | |
|  BodgeMaster | a1f16e6f6b | |
|  Shwoomple | cb7b5ddba7 | |
|  Shwoomple | e0648720bb | |
|  Shwoomple | ebcf436a18 | |
|  BodgeMaster | c59a1ac723 | |
|  BodgeMaster | aef91fe7cd | |
|  BodgeMaster | 4af9003761 | |
|  Milan Suman | 704b440d5a | |
|  BodgeMaster | 608767f5c2 | |
|  BodgeMaster | e31bff0802 | |
|  BodgeMaster | 5c73308934 | |
|  BodgeMaster | b59fe1857e | |
|  BodgeMaster | 69f15e928a | |
|  BodgeMaster | d0d02fc8d2 | |
|  BodgeMaster | 1308327fae | |
|  BodgeMaster | 4582c3e595 | |
|  BodgeMaster | 28719072bb | |
|  BodgeMaster | 800fd66044 | |
|  BodgeMaster | d90e7f16bd | |
|  BodgeMaster | 845b3fb922 | |
|  Milan Suman | 4f1ad714bd | |
|  BodgeMaster | adc9a7f36b | |
|  BodgeMaster | b044503951 | |
|  BodgeMaster | d97e1a8336 | |
|  BodgeMaster | 6baff11ebd | |
|  BodgeMaster | c9d6cf0b5e | |
|  BodgeMaster | 7d7ce2ba6b | |
|  BodgeMaster | a51c65c9f2 | |
|  BodgeMaster | 8bb0732cc6 | |
|  BodgeMaster | 0dcc579bb5 | |
|  BodgeMaster | db3b133f88 | |
|  BodgeMaster | 44a20c875a | |
|  BodgeMaster | 1804433a9f | |
|  BodgeMaster | 2cc2543b2a | |
|  BodgeMaster | a0d2974f0a | |
|  Milan Suman | 4659946a2f | |
|  Milan Suman | b5312aeb58 | |
|  BodgeMaster | 9562ae7be9 | |
|  BodgeMaster | b1733bc007 | |
|  Milan Suman | d315c6fcfc | |
|  Milan Suman | 2f38636a27 | |
|  BodgeMaster | 21310fecc7 | |
|  BodgeMaster | 09e2030a55 | 
|  | @ -4,6 +4,13 @@ | ||||||
| /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 | ||||||
|  | @ -16,3 +23,5 @@ | ||||||
| 
 | 
 | ||||||
| #vscode | #vscode | ||||||
| .vscode | .vscode | ||||||
|  | 
 | ||||||
|  | writeTest | ||||||
							
								
								
									
										12
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										12
									
								
								Makefile
								
								
								
								
							|  | @ -1,12 +0,0 @@ | ||||||
| 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 |  | ||||||
							
								
								
									
										18
									
								
								README.md
								
								
								
								
							
							
						
						
									
										18
									
								
								README.md
								
								
								
								
							|  | @ -22,13 +22,19 @@ Immediate goals: | ||||||
| 
 | 
 | ||||||
| ### Prerequisites: | ### Prerequisites: | ||||||
| 
 | 
 | ||||||
| This project requires bash and a C++20 compiler. | Build dependencies: | ||||||
| 
 | 
 | ||||||
| The project setup requires wget or curl, gzip, and tar. | - bash | ||||||
|  | - a C++ 20 compiler | ||||||
| 
 | 
 | ||||||
| Additional requirements for building dependencies: | Setup dependencies: | ||||||
| 
 | 
 | ||||||
| - sockpp: a C compiler, CMake | - wget or curl | ||||||
|  | - 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 | ||||||
|  | @ -45,7 +51,9 @@ 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 |     to load the provided shell environment (local customizations to the | ||||||
|  |     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 | ||||||
|  |  | ||||||
|  | @ -48,11 +48,6 @@ 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`. | ||||||
|  |  | ||||||
|  | @ -1,5 +1,8 @@ | ||||||
| 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. | ||||||
|  |  | ||||||
|  | @ -24,11 +24,15 @@ Provided aliases: | ||||||
| 
 | 
 | ||||||
| ## setup_project | ## setup_project | ||||||
| 
 | 
 | ||||||
| Cleans the project and its dependencies first, ensuring the project is in | Cleans the project and its dependencies first (see below), ensuring | ||||||
| a known state, then downloads and extracts dependencies. | the project is in a known state, then sets up dependencies. | ||||||
| 
 | 
 | ||||||
| This will probably do more in the future (for example compile dependencies | This includes downloading (and caching) of files that are not yet | ||||||
| that need to be compiled). | locally cached and compiling dependencies that need to be compiled. | ||||||
|  | 
 | ||||||
|  | This script’s 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 | ||||||
|  | @ -55,14 +59,37 @@ 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/`. Also removes the endianness header and | Removes and re-creates `bin/` and `include/`, then re-creates symlinks to | ||||||
| the endianness check binary. | shared object and header files as needed for the build process. | ||||||
|  | Also removes the endianness header and the endianness check binary. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # clean_dependencies | # clean_dependencies | ||||||
| 
 | 
 | ||||||
| Removes and re-creates `dependencies/`. | Removes and re-creates `dependencies/` and prunes the download cache of files | ||||||
|  | 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 | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -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` | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## servers.dat | ## NBT_data | ||||||
| 
 | 
 | ||||||
| My current servers.dat as pulled from my Minecraft installation. Used for testing the NBT library until we have something better. | Data used to test the NBT library. | ||||||
| 
 | 
 | ||||||
| 
 | `servers.dat`: My current servers.dat as pulled from my Minecraft installation | ||||||
| ## servers.dat_nbt_decoded.txt | `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.) | ||||||
| 
 | `simple_nbt`: A simple NBT file containing all tags | ||||||
| The same file manually decoded. I did this to get a better understanding how NBT works, might come in handy in the future. | `nested_compounds_and_lists`: A combination of nested compound and list tags intended to be challenging to parse | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,354 @@ | ||||||
|  | ############################################################################# | ||||||
|  | # 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. Let’s 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: he’s | ||||||
|  | | | | ||||||
|  | | |–[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 | ||||||
|  | @ -15,20 +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 | ||||||
| 
 | 
 | ||||||
| if [ "$(tr '[:upper:]' '[:lower:]' <<< $SINGLE)" = "yes" ]; then | source scripts/lib.sh | ||||||
|     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 | ||||||
|  | @ -46,10 +33,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..." | ||||||
| mkdir -pv bin/lib | create_directory 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 dependencies/tiny-utf8-4.4.3/include -fPIC -shared -o $(sed -e 's/^.\/src/.\/bin/;s/cpp$/so/' <<< $lib) $lib" |     COMPILE_COMMAND="$CXX_WITH_FLAGS -I ./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 | ||||||
|  | @ -68,12 +55,14 @@ 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..." | ||||||
| mkdir -pv bin/tools | create_directory 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 -Lbin/lib -l:nbt.so -o bin/tools/dumpnbt" |     "$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/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/arraydump.cpp -I./include -Lbin/lib -l:file.so -l:cli.so -o bin/tools/arraydump" | ||||||
|  |     "$CXX_WITH_FLAGS src/tools/baseconvert.cpp -I./include -Lbin/lib -l:cli.so -o bin/tools/baseconvert" | ||||||
|  |     "$CXX_WITH_FLAGS -pthread src/tools/hexnet.cpp -I./include -Lbin/lib -l:cli.so -l:libsockpp.so -o bin/tools/hexnet" | ||||||
| ) | ) | ||||||
| for command in ${!COMPILE_COMMANDS[@]}; do | for command in ${!COMPILE_COMMANDS[@]}; do | ||||||
|     echo "${COMPILE_COMMANDS[command]}" |     echo "${COMPILE_COMMANDS[command]}" | ||||||
|  |  | ||||||
|  | @ -15,15 +15,24 @@ | ||||||
| # 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 | ||||||
| 
 | 
 | ||||||
| rm -rv ./bin | echo ">>> Cleaning build files..." | ||||||
| rm -vf .endianness | 
 | ||||||
| rm -vf resources/check_endianness | source scripts/lib.sh | ||||||
| mkdir -v ./bin | 
 | ||||||
| mkdir -v ./bin/lib | remove ./bin | ||||||
|  | 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/ | ||||||
| 
 | 
 | ||||||
| set -v | ln -vs ../dependencies/sockpp-0.7.1/include/sockpp/ ./include/ | ||||||
| echo -n "" > ./bin/.placeholder | ln -vs ../dependencies/tiny-utf8-4.4.3/include/tinyutf8/ ./include/ | ||||||
|  | 
 | ||||||
|  | create_file ./bin/.placeholder | ||||||
|  | create_file ./include/.placeholder | ||||||
|  |  | ||||||
|  | @ -15,7 +15,26 @@ | ||||||
| # 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 | ||||||
| 
 | 
 | ||||||
| rm -rv ./dependencies | source scripts/lib.sh | ||||||
| mkdir -v ./dependencies | 
 | ||||||
| set -v | echo ">>> Checking download cache for unneeded or corrupted files..." | ||||||
| echo -n "" > ./dependencies/.placeholder | for file in $(ls .download_cache); do | ||||||
|  |     #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 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | #!/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 don’t 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" | ||||||
|  | } | ||||||
|  | @ -15,6 +15,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 | ||||||
| 
 | 
 | ||||||
|  | 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 | ||||||
|  | @ -32,53 +34,67 @@ fi | ||||||
| function download { | function download { | ||||||
|     URL="$1" |     URL="$1" | ||||||
|     DESTINATION="$2" |     DESTINATION="$2" | ||||||
|     SHA256SUM="$3 *$2" |     SHA256SUM="$3" | ||||||
|     echo -n "Downloading $URL to $DESTINATION... " | 
 | ||||||
|     if [ $USE_WGET = yes ]; then |     if [ ! -d .download_cache ]; then | ||||||
|         wget -O "$DESTINATION" "$URL" >/dev/null 2>&1 |         echo "Cache directory missing." | ||||||
|     else |         create_directory .download_cache | ||||||
|         curl -L "$URL" --output "$DESTINATION" >/dev/null 2>&1 |  | ||||||
|     fi |     fi | ||||||
|     if sha256sum --check <<< $SHA256SUM >/dev/null 2>&1; then | 
 | ||||||
|         echo "done." |     if [ -f ".download_cache/$SHA256SUM" ]; then | ||||||
|         return 0 |         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 |     else | ||||||
|         echo "error." |         echo -n "Downloading $URL to $DESTINATION... " | ||||||
|         echo "Checksum verification failed. Your download is either corrupted or the file has been altered." |         if [ $USE_WGET = yes ]; then | ||||||
|         rm -v "$DESTINATION" |             wget -O ".download_cache/$SHA256SUM" "$URL" >/dev/null 2>&1 | ||||||
|         return 1 |         else | ||||||
|  |             curl -L "$URL" --output ".download_cache/$SHA256SUM" >/dev/null 2>&1 | ||||||
|  |         fi | ||||||
|  |         if check_sha256 ".download_cache/$SHA256SUM" "$SHA256SUM"; then | ||||||
|  |             cp ".download_cache/$SHA256SUM" "$DESTINATION" | ||||||
|  |             echo "done." | ||||||
|  |             return 0 | ||||||
|  |         else | ||||||
|  |             echo "error." | ||||||
|  |             echo "Checksum verification failed. Your download is either corrupted or the file has been altered. Removing file." | ||||||
|  |             rm ".download_cache/$SHA256SUM" | ||||||
|  |             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 | ||||||
| mkdir -v dependencies/tmp | create_directory 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." |  | ||||||
| 
 | 
 | ||||||
| rm -rv dependencies/tmp | echo ">>> Cleaning up..." | ||||||
|  | remove dependencies/tmp | ||||||
|  |  | ||||||
|  | @ -15,13 +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 | ||||||
| 
 | 
 | ||||||
| if [ -z "$CXX" ]; then | source scripts/lib.sh | ||||||
|     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 | ||||||
|  | @ -31,25 +25,38 @@ else | ||||||
| fi | fi | ||||||
| echo "$LD_LIBRARY_PATH" | echo "$LD_LIBRARY_PATH" | ||||||
| 
 | 
 | ||||||
| mkdir -pv bin/test | echo ">>> Cleaning tests..." | ||||||
|  | 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_helpers.cpp -Lbin/lib -l:nbt.so -o bin/test/nbt_helpers" |     "$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/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 -Idependencies/tiny-utf8-4.4.3/include -Lbin/lib -l:javacompat.so -o bin/test/javacompat" |     "$CXX_WITH_FLAGS src/test/javacompat.cpp -I./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 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,11 @@ | ||||||
|  | #!/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 "================================================================================" | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | #!/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 "================================================================================" | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | #!/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 | ||||||
|  | @ -13,9 +13,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 -n "Loading shell environment for FOSS-VG development... " | echo ">>> 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" | ||||||
|  | @ -33,13 +39,75 @@ 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" | ||||||
| 
 | 
 | ||||||
| unset PROJECT_BASE_DIR | echo ">>> Checking for dependencies..." | ||||||
|  | MISSING_DEPS=0 | ||||||
| 
 | 
 | ||||||
| echo "done." | if command -v wget > /dev/null 2>&1; then | ||||||
|  |     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 | ||||||
|  |  | ||||||
							
								
								
									
										225
									
								
								src/lib/cli.cpp
								
								
								
								
							
							
						
						
									
										225
									
								
								src/lib/cli.cpp
								
								
								
								
							|  | @ -1,5 +1,8 @@ | ||||||
| // 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.
 | ||||||
|  | @ -13,13 +16,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 "cli.h++" |  | ||||||
| 
 |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <map> | #include <map> | ||||||
| 
 | 
 | ||||||
| #include "error.h++" | #include "cli.hpp" | ||||||
|  | #include "error.hpp" | ||||||
| 
 | 
 | ||||||
| namespace CLI { | namespace CLI { | ||||||
|     Flag::Flag() { |     Flag::Flag() { | ||||||
|  | @ -32,10 +34,10 @@ namespace CLI { | ||||||
|         this->present = false; |         this->present = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     UnpositionalArgument::UnpositionalArgument() { |     Option::Option() { | ||||||
|         this->present = false; |         this->present = false; | ||||||
|     } |     } | ||||||
|     UnpositionalArgument::UnpositionalArgument(char shortName, std::string longName, std::string placeholder, std::string description) { |     Option::Option(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; | ||||||
|  | @ -43,23 +45,22 @@ namespace CLI { | ||||||
|         this->present = false; |         this->present = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     PositionalArgument::PositionalArgument() { |     Argument::Argument() { | ||||||
|         this->present = false; |         this->present = false; | ||||||
|     } |     } | ||||||
|     PositionalArgument::PositionalArgument(std::string placeholder, std::string description) { |     Argument::Argument(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<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments) { |     ArgumentsParser::ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<Option> options, std::vector<Argument> arguments) { | ||||||
|         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->positionalArguments = positionalArguments; |         this->arguments = arguments; | ||||||
|         // create lookup tables for all flags and unpositional arguments
 |         // create lookup tables for all flags and options by their names
 | ||||||
|         // 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) { | ||||||
|  | @ -68,25 +69,25 @@ namespace CLI { | ||||||
|             this->flagsByShortName[flag.shortName] = flagPointer; |             this->flagsByShortName[flag.shortName] = flagPointer; | ||||||
|             this->flagsByLongName[flag.longName]   = flagPointer; |             this->flagsByLongName[flag.longName]   = flagPointer; | ||||||
|         } |         } | ||||||
|         this->argumentsByShortName = std::map<char, UnpositionalArgument*>(); |         this->optionsByShortName = std::map<char, Option*>(); | ||||||
|         this->argumentsByLongName = std::map<std::string, UnpositionalArgument*>(); |         this->optionsByLongName = std::map<std::string, Option*>(); | ||||||
|         for (UnpositionalArgument unpositionalArgument: unpositionalArguments) { |         for (Option option: options) { | ||||||
|             UnpositionalArgument* argumentPointer = new UnpositionalArgument(); |             Option* optionPointer = new Option(); | ||||||
|             *argumentPointer = unpositionalArgument; |             *optionPointer = option; | ||||||
|             this->argumentsByShortName[unpositionalArgument.shortName] = argumentPointer; |             this->optionsByShortName[option.shortName] = optionPointer; | ||||||
|             this->argumentsByLongName[unpositionalArgument.longName]   = argumentPointer; |             this->optionsByLongName[option.longName]   = optionPointer; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         UnpositionalArgument* argumentWaitingForValue = nullptr; |         Option* optionWaitingForValue = nullptr; | ||||||
|         std::vector<CLI::PositionalArgument>::size_type positionalArgumentCounter = 0; |         std::vector<CLI::Argument>::size_type argumentCounter = 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 (argumentWaitingForValue!=nullptr) { |                 if (optionWaitingForValue!=nullptr) { | ||||||
|                     this->wrongUsage = true; |                     this->wrongUsage = true; | ||||||
|                     this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+argumentWaitingForValue->longName); |                     this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+optionWaitingForValue->longName); | ||||||
|                     argumentWaitingForValue = nullptr; |                     optionWaitingForValue = nullptr; | ||||||
|                 } |                 } | ||||||
|                 // long name or short name?
 |                 // long name or short name?
 | ||||||
|                 if (argument[1]=='-') { |                 if (argument[1]=='-') { | ||||||
|  | @ -98,15 +99,19 @@ 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 argument or flag?
 |                         //is option 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 (argumentsByLongName.contains(argumentName)) { |                         } else if (optionsByLongName.contains(argumentName)) { | ||||||
|                             // unpositional argument
 |                             // option
 | ||||||
|                             argumentsByLongName[argumentName]->present = true; |                             optionsByLongName[argumentName]->present = true; | ||||||
|                             argumentWaitingForValue = argumentsByLongName[argumentName]; |                             optionWaitingForValue = optionsByLongName[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); | ||||||
|  | @ -115,9 +120,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 (argumentsByLongName.contains(argumentName)) { |                         if (optionsByLongName.contains(argumentName)) { | ||||||
|                             argumentsByLongName[argumentName]->present = true; |                             optionsByLongName[argumentName]->present = true; | ||||||
|                             argumentsByLongName[argumentName]->value = value; |                             optionsByLongName[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); | ||||||
|  | @ -129,23 +134,29 @@ 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 i=1; i<(int) argument.length(); i++) { |                     for (int j=1; j<(int) argument.length(); j++) { | ||||||
|                         //is argument or flag?
 |                         // is option or flag?
 | ||||||
|                         if (flagsByShortName.contains(argument[i])) { |                         if (flagsByShortName.contains(argument[j])) { | ||||||
|                             flagsByShortName[argument[i]]->present = true; |                             // flag
 | ||||||
|                         } else if (argumentsByShortName.contains(argument[i])) { |                             flagsByShortName[argument[j]]->present = true; | ||||||
|                             argumentsByShortName[argument[i]]->present = true; |                         } else if (optionsByShortName.contains(argument[j])) { | ||||||
|  |                             // option
 | ||||||
|  |                             optionsByShortName[argument[j]]->present = true; | ||||||
|                             //FIXME: see above
 |                             //FIXME: see above
 | ||||||
|                             if (i+1==(int) argument.length()) { |                             if (j+1==(int) argument.length()) { | ||||||
|                                 argumentWaitingForValue = argumentsByShortName[argument[i]]; |                                 optionWaitingForValue = optionsByShortName[argument[j]]; | ||||||
|  |                                 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
 | ||||||
|                                 argumentsByShortName[argument[i]]->value = argument.substr(i+1, argument.length()-i-1); |                                 optionsByShortName[argument[j]]->value = argument.substr(j+1, argument.length()-j-1); | ||||||
|                                 break; |                                 break; | ||||||
|                             } |                             } | ||||||
|                         } else { |                         } else { | ||||||
|                             this->wrongUsage = true; |                             this->wrongUsage = true; | ||||||
|                             this->wrongUsageMessages.push_back(std::string("Unknown argument or flag(s): ")+argument.substr(i, argument.length()-i)); |                             this->wrongUsageMessages.push_back(std::string("Unknown argument or flag(s): ")+argument.substr(j, argument.length()-j)); | ||||||
|                             // 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
 | ||||||
|  | @ -155,39 +166,48 @@ namespace CLI { | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 // positional argument or value for unpositional arg?
 |                 // argument or value for option?
 | ||||||
|                 if (argumentWaitingForValue==nullptr) { |                 if (optionWaitingForValue==nullptr) { | ||||||
|                     // positional argument
 |                     // argument
 | ||||||
|                     if (positionalArgumentCounter < this->positionalArguments.size()) { |                     if (argumentCounter < this->arguments.size()) { | ||||||
|                         this->positionalArguments.at(positionalArgumentCounter).present = true; |                         this->arguments.at(argumentCounter).present = true; | ||||||
|                         this->positionalArguments.at(positionalArgumentCounter).value = argument; |                         this->arguments.at(argumentCounter).value = argument; | ||||||
|                     } else { |                     } else { | ||||||
|                         this->wrongUsage = true; |                         this->wrongUsage = true; | ||||||
|                         this->wrongUsageMessages.push_back(std::string("Too many positional arguments. Unexpected encounter of: ")+argument); |                         this->wrongUsageMessages.push_back(std::string("Too many arguments! Unexpected encounter of: ")+argument); | ||||||
|                     } |                     } | ||||||
|                     positionalArgumentCounter++; |                     argumentCounter++; | ||||||
|                 } else { |                 } else { | ||||||
|                     // value for unpositional argument
 |                     // value for option
 | ||||||
|                     argumentWaitingForValue->value = argument; |                     optionWaitingForValue->value = argument; | ||||||
|                     argumentWaitingForValue = nullptr; |                     optionWaitingForValue = nullptr; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         for (PositionalArgument const& positionalArgument: this->positionalArguments) { |         for (Argument const& argument: this->arguments) { | ||||||
|             if (!positionalArgument.present) { |             if (!argument.present) { | ||||||
|                 this->wrongUsage = true; |                 this->wrongUsage = true; | ||||||
|                 this->wrongUsageMessages.push_back(std::string("Too few positional arguments! Missing: ")+positionalArgument.placeholder); |                 this->wrongUsageMessages.push_back(std::string("Too few arguments! Missing: ")+argument.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, unpositionalArgument]: this->argumentsByShortName) { |         for (auto const& [shortName, option]: this->optionsByShortName) { | ||||||
|             delete unpositionalArgument; |             delete option; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -211,35 +231,100 @@ namespace CLI { | ||||||
|         else return ErrorOr<bool> (false, ErrorCodes::NOT_PRESENT, false); |         else return ErrorOr<bool> (false, ErrorCodes::NOT_PRESENT, false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ErrorOr<std::string> ArgumentsParser::getPositionalArgument(std::vector<CLI::PositionalArgument>::size_type position){ |     ErrorOr<std::string> ArgumentsParser::getArgument(std::vector<CLI::Argument>::size_type position){ | ||||||
|         if (position >= this->positionalArguments.size()) return ErrorOr<std::string>(true, ErrorCodes::OUT_OF_RANGE, std::string("")); |         if (position >= this->arguments.size()) return ErrorOr<std::string>(true, ErrorCodes::OUT_OF_RANGE, std::string("")); | ||||||
|         if (this->wrongUsage) { |         if (this->wrongUsage) { | ||||||
|             if (this->positionalArguments.at(position).present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->positionalArguments.at(position).value); |             if (this->arguments.at(position).present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->arguments.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->positionalArguments.at(position).value); |         return ErrorOr<std::string>(this->arguments.at(position).value); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ErrorOr<std::string> ArgumentsParser::getUnpositionalArgument(char shortName) { |     ErrorOr<std::string> ArgumentsParser::getOption(char shortName) { | ||||||
|         if (!this->argumentsByShortName.contains(shortName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string("")); |         if (!this->optionsByShortName.contains(shortName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string("")); | ||||||
|         if (this->wrongUsage) { |         if (this->wrongUsage) { | ||||||
|             if (this->argumentsByShortName[shortName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->argumentsByShortName[shortName]->value); |             if (this->optionsByShortName[shortName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->optionsByShortName[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->argumentsByShortName[shortName]->present) return ErrorOr<std::string>(this->argumentsByShortName[shortName]->value); |         if (this->optionsByShortName[shortName]->present) return ErrorOr<std::string>(this->optionsByShortName[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::getUnpositionalArgument(std::string longName) { |     ErrorOr<std::string> ArgumentsParser::getOption(std::string longName) { | ||||||
|         if (!this->argumentsByLongName.contains(longName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string("")); |         if (!this->optionsByLongName.contains(longName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string("")); | ||||||
|         if (this->wrongUsage) { |         if (this->wrongUsage) { | ||||||
|             if (this->argumentsByLongName[longName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->argumentsByLongName[longName]->value); |             if (this->optionsByLongName[longName]->present) return ErrorOr<std::string>(true, ErrorCodes::WRONG_USAGE, this->optionsByLongName[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->argumentsByLongName[longName]->present) return ErrorOr<std::string>(this->argumentsByLongName[longName]->value); |         if (this->optionsByLongName[longName]->present) return ErrorOr<std::string>(this->optionsByLongName[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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <map> | #include <map> | ||||||
| 
 | 
 | ||||||
| #include "error.h++" | #include "error.hpp" | ||||||
| 
 | 
 | ||||||
| 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 UnpositionalArgument { |     struct Option { | ||||||
|         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; | ||||||
| 
 | 
 | ||||||
|         UnpositionalArgument(); |         Option(); | ||||||
|         UnpositionalArgument(char shortName, std::string longName, std::string placeholder, std::string description); |         Option(char shortName, std::string longName, std::string placeholder, std::string description); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     struct PositionalArgument { |     struct Argument { | ||||||
|         // 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,17 +57,19 @@ namespace CLI { | ||||||
|         bool present; |         bool present; | ||||||
|         std::string value; |         std::string value; | ||||||
| 
 | 
 | ||||||
|         PositionalArgument(); |         Argument(); | ||||||
|         PositionalArgument(std::string placeholder, std::string description); |         Argument(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, UnpositionalArgument*> argumentsByShortName; |             std::map<char, Option*> optionsByShortName; | ||||||
|             std::map<std::string, UnpositionalArgument*> argumentsByLongName; |             std::map<std::string, Option*> optionsByLongName; | ||||||
|             std::vector<PositionalArgument> positionalArguments; |             std::vector<Argument> arguments; | ||||||
|  |             std::string description; | ||||||
|  |             std::string additionalInfo; | ||||||
| 
 | 
 | ||||||
|         public: |         public: | ||||||
|             std::string programName; |             std::string programName; | ||||||
|  | @ -75,14 +77,16 @@ 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<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); | ||||||
|  |             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> getPositionalArgument(std::vector<CLI::PositionalArgument>::size_type position); |             ErrorOr<std::string> getArgument(std::vector<CLI::Argument>::size_type position); | ||||||
|             ErrorOr<std::string> getUnpositionalArgument(char shortName); |             ErrorOr<std::string> getOption(char shortName); | ||||||
|             ErrorOr<std::string> getUnpositionalArgument(std::string longName); |             ErrorOr<std::string> getOption(std::string longName); | ||||||
| 
 | 
 | ||||||
|             std::string getUsage(); |             std::string getUsage(); | ||||||
|     }; |     }; | ||||||
|  | @ -23,37 +23,43 @@ struct ErrorOr { | ||||||
|     uint8_t errorCode; |     uint8_t errorCode; | ||||||
|     T value; |     T value; | ||||||
| 
 | 
 | ||||||
|     ErrorOr<T>(); |     ErrorOr() { | ||||||
|     ErrorOr<T>(T); |         this->isError = false; | ||||||
|     ErrorOr<T>(bool, uint8_t); |         this->errorCode = 0; | ||||||
|     ErrorOr<T>(bool, uint8_t, T); |     } | ||||||
|  | 
 | ||||||
|  |     ErrorOr(T value) { | ||||||
|  |         this->isError = false; | ||||||
|  |         this->errorCode = 0; | ||||||
|  |         this->value = value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ErrorOr(bool isError, uint8_t errorCode) { | ||||||
|  |         this->isError = isError; | ||||||
|  |         this->errorCode = errorCode; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ErrorOr(bool isError, uint8_t errorCode, T value) { | ||||||
|  |         this->isError = isError; | ||||||
|  |         this->errorCode = errorCode; | ||||||
|  |         this->value = value; | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename T> | struct ErrorOrVoid { | ||||||
| ErrorOr<T>::ErrorOr() { |     bool isError; | ||||||
|     this->isError = false; |     uint8_t errorCode; | ||||||
|     this->errorCode = 0; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| template <typename T> |     ErrorOrVoid() { | ||||||
| ErrorOr<T>::ErrorOr(T value) { |         this->isError = false; | ||||||
|     this->isError = false; |         this->errorCode = 0; | ||||||
|     this->errorCode = 0; |     } | ||||||
|     this->value = value; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| template <typename T> |     ErrorOrVoid(bool isError, uint8_t errorCode) { | ||||||
| ErrorOr<T>::ErrorOr(bool isError, uint8_t errorCode) { |         this->isError = isError; | ||||||
|     this->isError = isError; |         this->errorCode = errorCode; | ||||||
|     this->errorCode = errorCode; |     } | ||||||
| } | }; | ||||||
| 
 |  | ||||||
| template <typename T> |  | ||||||
| ErrorOr<T>::ErrorOr(bool isError, uint8_t errorCode, T value) { |  | ||||||
|     this->isError = isError; |  | ||||||
|     this->errorCode = errorCode; |  | ||||||
|     this->value = value; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| 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
 | ||||||
|  | @ -64,13 +70,10 @@ 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; | ||||||
|  | @ -78,6 +81,20 @@ 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; | ||||||
|  | @ -0,0 +1,274 @@ | ||||||
|  | // 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)); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,92 @@ | ||||||
|  | // 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); | ||||||
|  | }; | ||||||
|  | @ -15,18 +15,30 @@ | ||||||
| 
 | 
 | ||||||
| #include <tinyutf8/tinyutf8.h> | #include <tinyutf8/tinyutf8.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include "error.h++" | #include "error.hpp" | ||||||
|  | #include "javacompat.hpp" | ||||||
|  | #include "../../.endianness" | ||||||
| 
 | 
 | ||||||
| #include "javacompat.h++" | #ifdef FOSSVG_ENDIAN_BIG_WORD | ||||||
|  |     #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 { | ||||||
|     //FIXME: contrary to what I said, we need to explicitly pass the data
 |     ErrorOr<tiny_utf8::string> importJavaString(uint8_t data[], uint16_t size) { | ||||||
|     // 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 size = static_cast<uint16_t>(data[0])<<8 | static_cast<uint16_t>(data[1]); |         uint16_t encodedSize = static_cast<uint16_t>(data[0])<<8 | static_cast<uint16_t>(data[1]); | ||||||
| 
 | 
 | ||||||
|         for(uint8_t i=2; i<size+2; i++){ |         if(encodedSize != size){ | ||||||
|  |             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'; | ||||||
|  | @ -35,11 +47,48 @@ namespace JavaCompat { | ||||||
|             } |             } | ||||||
|             stdString.push_back((char) data[i]); |             stdString.push_back((char) data[i]); | ||||||
|         } |         } | ||||||
|         return tiny_utf8::string(stdString); |         return ErrorOr<tiny_utf8::string>(tiny_utf8::string(stdString)); | ||||||
|     } |     } | ||||||
|     /*
 | 
 | ||||||
|     ErrorOr<uint8_t*> exportJavaString(tiny_utf8::string data) { |     ErrorOr<std::vector<uint8_t>> exportJavaString(tiny_utf8::string data) { | ||||||
|         return ErrorOr(nullptr); |         uint16_t* size = new uint16_t; | ||||||
|  |         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); | ||||||
|     } |     } | ||||||
|     */ | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -13,12 +13,11 @@ | ||||||
| //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.h++" | #include "error.hpp" | ||||||
| 
 | 
 | ||||||
| namespace JavaCompat { | namespace JavaCompat { | ||||||
|     //FIXME: contrary to what I said, we need to explicitly pass the data
 |     ErrorOr<tiny_utf8::string> importJavaString(uint8_t data[], uint16_t size); | ||||||
|     // size because files could have been tampered with or corrupted
 |     ErrorOr<std::vector<uint8_t>> exportJavaString(tiny_utf8::string data); | ||||||
|     tiny_utf8::string importJavaString(uint8_t data[]); |  | ||||||
|     ErrorOr<uint8_t*> exportJavaString(tiny_utf8::string data); |  | ||||||
| } | } | ||||||
							
								
								
									
										1548
									
								
								src/lib/nbt.cpp
								
								
								
								
							
							
						
						
									
										1548
									
								
								src/lib/nbt.cpp
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,79 +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 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); |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,307 @@ | ||||||
|  | // 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); | ||||||
|  | } | ||||||
|  | @ -15,4 +15,4 @@ | ||||||
| 
 | 
 | ||||||
| #include <iostream> | #include <iostream> | ||||||
| 
 | 
 | ||||||
| #define ASSERT(truth) if ((truth)); else { std::cout << "Assertion failed: " << #truth << std::endl; return 1; } | #define ASSERT(truth) if ((truth)); else { std::cout << "On line " << __LINE__ << ": Assertion failed: " << #truth << std::endl; return 1; } | ||||||
|  | @ -16,14 +16,14 @@ | ||||||
| #include <string> | #include <string> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "assert.h++" | #include "assert.hpp" | ||||||
| #include "../lib/error.h++" | #include "../lib/error.hpp" | ||||||
| 
 | 
 | ||||||
| #include "../lib/cli.h++" | #include "../lib/cli.hpp" | ||||||
| 
 | 
 | ||||||
| int main(int argc, char* argv[]) { | int main() { | ||||||
|     std::cout << "################################################################################" << std::endl; |     std::cout << "################################################################################" << std::endl; | ||||||
|     std::cout << "CLI argument parser tests" << std::endl; |     std::cout << "CLI argument parsing tests" << std::endl; | ||||||
|     std::cout << "################################################################################" << std::endl; |     std::cout << "################################################################################" << std::endl; | ||||||
| 
 | 
 | ||||||
|     // Valid parameter test ############################################
 |     // Valid parameter test ############################################
 | ||||||
|  | @ -31,7 +31,7 @@ int main(int argc, char* argv[]) { | ||||||
|     // 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 "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"
 |     // 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"
 | ||||||
| 
 | 
 | ||||||
|     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(int argc, char* argv[]) { | ||||||
|     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::UnpositionalArgument> validTestUnpositionalArguments; |     std::vector<CLI::Option> validTestOptions; | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('c', "ccccc", "VALUE", "short argument concatenated with its value")); |     validTestOptions.push_back(CLI::Option('c', "ccccc", "VALUE", "short argument concatenated with its value")); | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('d', "ddddd", " VALUE", "short argument with separate value")); |     validTestOptions.push_back(CLI::Option('d', "ddddd", " VALUE", "short argument with separate value")); | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('f', "fffff", "VALUE", "short argument concatenated with a flag and its value")); |     validTestOptions.push_back(CLI::Option('f', "fffff", "VALUE", "short argument concatenated with a flag and its value")); | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('h', "hhhhh", " VALUE", "short argument concatenated with a flag with separate value")); |     validTestOptions.push_back(CLI::Option('h', "hhhhh", " VALUE", "short argument concatenated with a flag with separate value")); | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('x', "long-argument-with-value-included", "VALUE", "long argument with its value included using =")); |     validTestOptions.push_back(CLI::Option('x', "long-argument-with-value-included", "VALUE", "long argument with its value included using =")); | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value")); |     validTestOptions.push_back(CLI::Option('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value")); | ||||||
|     validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('z', "zzzzz", "NOPE", "unused argument")); |     validTestOptions.push_back(CLI::Option('z', "zzzzz", "NOPE", "unused argument")); | ||||||
| 
 | 
 | ||||||
|     std::vector<CLI::PositionalArgument> validTestPositionalArguments; |     std::vector<CLI::Argument> validTestArguments; | ||||||
|     validTestPositionalArguments.push_back(CLI::PositionalArgument("argument0", "positional argument between optional parameters")); |     validTestArguments.push_back(CLI::Argument("argument0", "argument between optional parameters")); | ||||||
|     validTestPositionalArguments.push_back(CLI::PositionalArgument("argument1", "positional argument")); |     validTestArguments.push_back(CLI::Argument("argument1", "argument")); | ||||||
|     validTestPositionalArguments.push_back(CLI::PositionalArgument("argument2", "positional argument")); |     validTestArguments.push_back(CLI::Argument("argument2", "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(int argc, char* argv[]) { | ||||||
|     validTestParameterList[ 6] = "-d"; |     validTestParameterList[ 6] = "-d"; | ||||||
|     validTestParameterList[ 7] = "separate-value"; |     validTestParameterList[ 7] = "separate-value"; | ||||||
|     validTestParameterList[ 8] = "-efdouble-concatenated"; |     validTestParameterList[ 8] = "-efdouble-concatenated"; | ||||||
|     validTestParameterList[ 9] = "positional argument 0"; |     validTestParameterList[ 9] = "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] = "positional argument 1"; |     validTestParameterList[15] = "argument 1"; | ||||||
|     validTestParameterList[16] = "positional argument 2"; |     validTestParameterList[16] = "argument 2"; | ||||||
|     int validTestParameterCount = 17; |     int validTestParameterCount = 17; | ||||||
| 
 | 
 | ||||||
|     CLI::ArgumentsParser validTestParameterParser = CLI::ArgumentsParser(validTestParameterCount, validTestParameterList, validTestFlags, validTestUnpositionalArguments, validTestPositionalArguments); |     CLI::ArgumentsParser validTestParameterParser = CLI::ArgumentsParser(validTestParameterCount, validTestParameterList, validTestFlags, validTestOptions, validTestArguments); | ||||||
| 
 | 
 | ||||||
|     ASSERT(!validTestParameterParser.wrongUsage); |     ASSERT(!validTestParameterParser.wrongUsage); | ||||||
|     ASSERT(validTestParameterParser.programName == std::string("test")); |     ASSERT(validTestParameterParser.programName == std::string("test")); | ||||||
|  | @ -115,49 +115,50 @@ int main(int argc, char* argv[]) { | ||||||
|     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.getUnpositionalArgument('c').isError); |     ASSERT(!validTestParameterParser.getOption('c').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('c').value==std::string("concatenated")); |     ASSERT(validTestParameterParser.getOption('c').value==std::string("concatenated")); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("ccccc")).value==std::string("concatenated")); |     ASSERT(validTestParameterParser.getOption(std::string("ccccc")).value==std::string("concatenated")); | ||||||
|     ASSERT(!validTestParameterParser.getUnpositionalArgument('d').isError); |     ASSERT(!validTestParameterParser.getOption('d').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('d').value==std::string("separate-value")); |     ASSERT(validTestParameterParser.getOption('d').value==std::string("separate-value")); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("ddddd")).value==std::string("separate-value")); |     ASSERT(validTestParameterParser.getOption(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.getUnpositionalArgument('f').isError); |     ASSERT(!validTestParameterParser.getOption('f').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('f').value==std::string("double-concatenated")); |     ASSERT(validTestParameterParser.getOption('f').value==std::string("double-concatenated")); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("fffff")).value==std::string("double-concatenated")); |     ASSERT(validTestParameterParser.getOption(std::string("fffff")).value==std::string("double-concatenated")); | ||||||
|     ASSERT(!validTestParameterParser.getPositionalArgument(0).isError); |     ASSERT(!validTestParameterParser.getArgument(0).isError); | ||||||
|     ASSERT(validTestParameterParser.getPositionalArgument(0).value=="positional argument 0"); |     ASSERT(validTestParameterParser.getArgument(0).value=="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.getUnpositionalArgument('h').isError); |     ASSERT(!validTestParameterParser.getOption('h').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('h').value==std::string("concatenated-separate-value")); |     ASSERT(validTestParameterParser.getOption('h').value==std::string("concatenated-separate-value")); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("hhhhh")).value==std::string("concatenated-separate-value")); |     ASSERT(validTestParameterParser.getOption(std::string("hhhhh")).value==std::string("concatenated-separate-value")); | ||||||
|     ASSERT(!validTestParameterParser.getUnpositionalArgument('x').isError); |     ASSERT(!validTestParameterParser.getOption('x').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('x').value==std::string("included value")); |     ASSERT(validTestParameterParser.getOption('x').value==std::string("included value")); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("long-argument-with-value-included")).value==std::string("included value")); |     ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-included")).value==std::string("included value")); | ||||||
|     ASSERT(!validTestParameterParser.getUnpositionalArgument('y').isError); |     ASSERT(!validTestParameterParser.getOption('y').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('y').value==std::string("separate value")); |     ASSERT(validTestParameterParser.getOption('y').value==std::string("separate value")); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("long-argument-with-value-separated")).value==std::string("separate value")); |     ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-separated")).value==std::string("separate value")); | ||||||
|     ASSERT(!validTestParameterParser.getPositionalArgument(1).isError); |     ASSERT(!validTestParameterParser.getArgument(1).isError); | ||||||
|     ASSERT(validTestParameterParser.getPositionalArgument(1).value=="positional argument 1"); |     ASSERT(validTestParameterParser.getArgument(1).value=="argument 1"); | ||||||
|     ASSERT(!validTestParameterParser.getPositionalArgument(2).isError); |     ASSERT(!validTestParameterParser.getArgument(2).isError); | ||||||
|     ASSERT(validTestParameterParser.getPositionalArgument(2).value=="positional argument 2"); |     ASSERT(validTestParameterParser.getArgument(2).value=="argument 2"); | ||||||
|     ASSERT(!validTestParameterParser.getUnpositionalArgument('z').isError); |     ASSERT(!validTestParameterParser.getOption('z').isError); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('z').errorCode == ErrorCodes::NOT_PRESENT); |     ASSERT(validTestParameterParser.getOption('z').errorCode == ErrorCodes::NOT_PRESENT); | ||||||
|     ASSERT(validTestParameterParser.getUnpositionalArgument('z').value == std::string("")); |     ASSERT(validTestParameterParser.getOption('z').value == std::string("")); | ||||||
|  |     ASSERT(validTestParameterParser.wrongUsageMessages.size() == 0); | ||||||
| 
 | 
 | ||||||
|     delete[] validTestParameterList; |     delete[] validTestParameterList; | ||||||
|     std::cout << "Passed valid input test." << std::endl; |     std::cout << "Passed valid command line 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::UnpositionalArgument> emptyTestUnpositionalArguments; |     std::vector<CLI::Option> emptyTestOptions; | ||||||
| 
 | 
 | ||||||
|     std::vector<CLI::PositionalArgument> emptyTestPositionalArguments; |     std::vector<CLI::Argument> emptyTestArguments; | ||||||
| 
 | 
 | ||||||
|     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
 | ||||||
|  | @ -167,27 +168,26 @@ int main(int argc, char* argv[]) { | ||||||
|     int emptyTestParameterCount = 1; |     int emptyTestParameterCount = 1; | ||||||
| 
 | 
 | ||||||
|     // valid
 |     // valid
 | ||||||
|     CLI::ArgumentsParser validEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestUnpositionalArguments, emptyTestPositionalArguments); |     CLI::ArgumentsParser validEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestOptions, emptyTestArguments); | ||||||
|     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
 | ||||||
|     emptyTestPositionalArguments.push_back(CLI::PositionalArgument("argument", "positional argument")); |     emptyTestArguments.push_back(CLI::Argument("argument", "argument")); | ||||||
| 
 | 
 | ||||||
|     CLI::ArgumentsParser invalidEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestUnpositionalArguments, emptyTestPositionalArguments); |     CLI::ArgumentsParser invalidEmptyTestParameterParser = CLI::ArgumentsParser(emptyTestParameterCount, emptyTestParameterList, emptyTestFlags, emptyTestOptions, emptyTestArguments); | ||||||
|     ASSERT(invalidEmptyTestParameterParser.wrongUsage); |     ASSERT(invalidEmptyTestParameterParser.wrongUsage); | ||||||
|     ASSERT(invalidEmptyTestParameterParser.programName == std::string("test")); |     ASSERT(invalidEmptyTestParameterParser.programName == std::string("test")); | ||||||
|     ASSERT(invalidEmptyTestParameterParser.getPositionalArgument(0).isError); |     ASSERT(invalidEmptyTestParameterParser.getArgument(0).isError); | ||||||
|     ASSERT(invalidEmptyTestParameterParser.getPositionalArgument(0).errorCode == ErrorCodes::NOT_PRESENT); |     ASSERT(invalidEmptyTestParameterParser.getArgument(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 input test." << std::endl; |     std::cout << "Passed empty command line test." << std::endl; | ||||||
| 
 | 
 | ||||||
|     //TODO add tests for invalid input
 |     // unknown flag ####################################################
 | ||||||
|     // 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(int argc, char* argv[]) { | ||||||
|     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::UnpositionalArgument> unknownFlagTestUnpositionalArguments; |     std::vector<CLI::Option> unknownFlagTestOptions; | ||||||
| 
 | 
 | ||||||
|     std::vector<CLI::PositionalArgument> unknownFlagTestPositionalArguments; |     std::vector<CLI::Argument> unknownFlagTestArguments; | ||||||
| 
 | 
 | ||||||
|     const char** unknownFlagTestParameterList = new const char*[3]; |     const char** unknownFlagTestParameterList = new const char*[7]; | ||||||
|     unknownFlagTestParameterList[0] = "test"; |     unknownFlagTestParameterList[0] = "test"; | ||||||
|     unknownFlagTestParameterList[1] = "-a"; |     unknownFlagTestParameterList[1] = "-a"; | ||||||
|     unknownFlagTestParameterList[2] = "-1"; |     unknownFlagTestParameterList[2] = "-1"; | ||||||
|  | @ -211,20 +211,554 @@ int main(int argc, char* argv[]) { | ||||||
|     unknownFlagTestParameterList[6] = "-5"; |     unknownFlagTestParameterList[6] = "-5"; | ||||||
|     int unknownFlagStandaloneTestParameterCount = 2; |     int unknownFlagStandaloneTestParameterCount = 2; | ||||||
| 
 | 
 | ||||||
|     CLI::ArgumentsParser unknownFlagStandaloneTestParser = CLI::ArgumentsParser(unknownFlagStandaloneTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestUnpositionalArguments, unknownFlagTestPositionalArguments); |     CLI::ArgumentsParser unknownFlagStandaloneTestParser = CLI::ArgumentsParser(unknownFlagStandaloneTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestOptions, unknownFlagTestArguments); | ||||||
|     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, unknownFlagTestUnpositionalArguments, unknownFlagTestPositionalArguments); |     CLI::ArgumentsParser unknownFlagMixedTestParser = CLI::ArgumentsParser(unknownFlagMixedTestParameterCount, unknownFlagTestParameterList, unknownFlagTestFlags, unknownFlagTestOptions, unknownFlagTestArguments); | ||||||
|     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; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,183 @@ | ||||||
|  | // 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; | ||||||
|  | } | ||||||
|  | @ -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 << "Java String Tests" << std::endl; |     std::cout << "Javacompat 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())); |     tiny_utf8::string importedString = JavaCompat::importJavaString(reinterpret_cast<uint8_t*>(javaStdString.data()), 0x75).value; | ||||||
| 
 | 
 | ||||||
|     std::streampos normalSize; |     std::streampos normalSize; | ||||||
|     std::string normalStdString; |     std::string normalStdString; | ||||||
|  | @ -75,9 +75,30 @@ 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
 | ||||||
| 
 | 
 | ||||||
| return 0; |     std::vector<uint8_t> data = JavaCompat::exportJavaString(normalString).value; | ||||||
|  |     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; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,446 +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 "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; |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,584 @@ | ||||||
|  | // 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; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,257 @@ | ||||||
|  | // 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
											
										
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | // 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; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,130 @@ | ||||||
|  | // 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; | ||||||
|  | } | ||||||
|  | @ -13,8 +13,360 @@ | ||||||
| // 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 "../lib/nbt.h++" | #include <iostream> | ||||||
|  | #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[]) { | ||||||
|     return 0; |     std::vector<CLI::Flag> flags; | ||||||
|  |     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; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,8 @@ | ||||||
| //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.
 | ||||||
|  | @ -13,68 +16,479 @@ | ||||||
| //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 <cstdint> | #include <sockpp/tcp_acceptor.h> | ||||||
| #include <cctype> | #include <sockpp/tcp_connector.h> | ||||||
|  | #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/error.h++" | #include "../lib/cli.hpp" | ||||||
| #include "../lib/cli.h++" | #include "../lib/error.hpp" | ||||||
| 
 | 
 | ||||||
| #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 | ||||||
| 
 | 
 | ||||||
| int main(int argc, char* argv[]){ | // TCP v4 server
 | ||||||
|  | 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; | ||||||
| 
 | 
 | ||||||
|     // Argument parsing ################################################
 | bool exitProgram = false; | ||||||
|     bool ipv4 = true; | 
 | ||||||
|     bool ipv6 = true; | bool ipv4 = false; | ||||||
|     bool tcp = true; | bool ipv6 = false; | ||||||
|     bool udp = true; | bool tcp = false; | ||||||
|     bool listen = false; | bool udp = false; | ||||||
|     std::string host; | bool outgoing = false; | ||||||
|     uint16_t port; | std::string host; | ||||||
|  | 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, defaults to both when -4 and -6 are omitted, otherwise uses what is specified")); |     flags.push_back(CLI::Flag('4', "ipv4", "use IPv4, either this or IPv6 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('6', "ipv6", "use IPv6, either this or IPv4 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('t', "tcp", "use TCP, either this or UDP 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('u', "udp", "use UDP, either this or TCP has to be 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::UnpositionalArgument> unpositionalArguments; |     std::vector<CLI::Option> options; | ||||||
|     unpositionalArguments.push_back(CLI::UnpositionalArgument('c', "connect", "HOST", "connect to HOST, listen for incoming connections if omitted")); |     options.push_back(CLI::Option('c', "connect", "HOST", "make an outgoing connection to HOST instead of listening for an incoming connection")); | ||||||
|  |     options.push_back(CLI::Option('b', "bind", "ADDRESS", "(UDP only) bind to ADDRESS instead of localhost")); | ||||||
| 
 | 
 | ||||||
|     std::vector<CLI::PositionalArgument> positionalArguments; |     std::vector<CLI::Argument> arguments; | ||||||
|     positionalArguments.push_back(CLI::PositionalArgument("PORT", "the port to use")); |     arguments.push_back(CLI::Argument("PORT", "the port to lsiten on (or connect to)")); | ||||||
| 
 | 
 | ||||||
|     CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, unpositionalArguments, positionalArguments); |     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."); | ||||||
| 
 | 
 | ||||||
|  |     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) { | ||||||
|         //TODO: spit out usage information generated by the parser
 |         std::cout << cliParser.getUsage() << std::endl; | ||||||
|         return EXIT_USAGE; |         return EXIT_USAGE; | ||||||
|     } |     } | ||||||
|     if (cliParser.getFlag('4').value || cliParser.getFlag('6').value) { |  | ||||||
|         ipv4 = cliParser.getFlag('4').value; |  | ||||||
|         ipv6 = cliParser.getFlag('6').value; |  | ||||||
|     } |  | ||||||
|     if (cliParser.getFlag('t').value || cliParser.getFlag('u').value) { |  | ||||||
|         tcp = cliParser.getFlag('t').value; |  | ||||||
|         udp = cliParser.getFlag('u').value; |  | ||||||
|     } |  | ||||||
|     if (cliParser.getUnpositionalArgument('c').errorCode == ErrorCodes::NOT_PRESENT) { |  | ||||||
|         listen = true; |  | ||||||
|     } |  | ||||||
|     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::cerr << "Port: " << (int) port << std::endl; |     ipv4 = cliParser.getFlag("ipv4").value; | ||||||
|     std::cerr << (listen? "Listening" : "Host: ") << host << std::endl; |     ipv6 = cliParser.getFlag("ipv6").value; | ||||||
|     std::cerr << "IPv4: " << (ipv4? "yes" : "no") << std::endl; |     tcp = cliParser.getFlag("tcp").value; | ||||||
|     std::cerr << "IPv6: " << (ipv6? "yes" : "no") << std::endl; |     udp = cliParser.getFlag("udp").value; | ||||||
|     std::cerr << "TCP:  " << ( tcp? "yes" : "no") << std::endl; | 
 | ||||||
|     std::cerr << "UDP:  " << ( udp? "yes" : "no") << std::endl; |     if (cliParser.getOption("connect").errorCode != ErrorCodes::NOT_PRESENT) { | ||||||
|     return EXIT_UNIMPLEMENTED; |         outgoing = true; | ||||||
|  |         host = cliParser.getOption("connect").value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!(ipv4 || ipv6) || (ipv4 && ipv6) || !(tcp || udp) || (tcp && udp)) { | ||||||
|  |         std::cout << "Please specify which protocols to use (one of IPv4/IPv6, one of TCP/UDP)." << std::endl; | ||||||
|  |         return EXIT_USAGE; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     port = (in_port_t) std::stoi(cliParser.getArgument(0).value); | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 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 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; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue