Compare commits

...

183 Commits

Author SHA1 Message Date
Shwoomple 38fad56965 lib/file.cpp: Implement cutByte function 2022-10-26 19:18:11 +05:30
BodgeMaster f6b965040d tools/hexnet: Compile with POSIX threads
Because apparently that’s not the default on NetBSD for some reason.
2022-10-23 03:06:48 +02:00
BodgeMaster b5c18cd0de tools/hexnet: Implement bi-directional communication 2022-10-23 01:48:47 +02:00
BodgeMaster a1ba08b7db tools/hexnet: Shut down connections when gracefully handling signals 2022-10-23 01:47:44 +02:00
BodgeMaster 1d7e98d0b3 tools/hexnet: Implement function for reading bytes from stdin 2022-10-23 01:45:59 +02:00
BodgeMaster a7e07d2c3c tools/hexnet: Implement UDP support (and minor refactoring) 2022-10-22 18:51:29 +02:00
BodgeMaster 7ae843039c tools/hexnet: Implement TCP v6 handler, rename readFromTCPSocket to readFromTCP 2022-10-22 01:18:44 +02:00
BodgeMaster 5574cdb4bf tools/hexnet: Don’t confuse users 2022-10-22 01:02:00 +02:00
BodgeMaster 89cfb9d850 tools/hexnet: Reduce redundant code 2022-10-21 23:01:03 +02:00
BodgeMaster f681c54c82 tools/hexnet: Reimplement TCP server, implement TCP client
Both can only receive at the moment as we still don’t handle input.
2022-10-21 23:01:03 +02:00
BodgeMaster ee0ebb273c tools/hexnet: Add instructions for setting up TTY 2022-10-21 23:01:03 +02:00
Shwoomple 8dea1f2d31 lib/file: Implement modify functions 2022-10-20 15:12:15 +05:30
Shwoomple c825c73afd lib/file: Implement insertByte function 2022-10-20 13:02:15 +05:30
Shwoomple e7711a3d59 commit merge 2022-10-20 10:27:48 +05:30
Shwoomple e7ce6f5cd4 lib/file: Fix write function bugs 2022-10-20 10:27:00 +05:30
BodgeMaster ee9b5d4f67 tools/hexnet: Add command line parser back
Reimplementation has started.
2022-10-19 15:26:38 +02:00
BodgeMaster b84130344d tools/hexnet: Starting over 2022-10-19 15:26:38 +02:00
BodgeMaster 25d7806f6d lib/nbt: Fix memory leak in case of parsing error 2022-10-19 15:26:38 +02:00
Shwoomple 6e57a86338 lib/file: Implement write functions 2022-10-19 10:43:23 +05:30
Shwoomple c54eb48887 lib/file: Implement writeByte function 2022-10-18 20:57:02 +05:30
BodgeMaster c14504ce0b tools/dumpnbt: Display empty lists correctly 2022-10-17 07:02:36 +02:00
BodgeMaster 92cf81c1b4 tools/dumpnbt: Implement basic functionality.
FINALLY!!! :^)
2022-10-17 06:40:56 +02:00
BodgeMaster df35243ee9 lib/nbt: Add NBT::Tag::List::getContainedType()
Supid oversight, easily work-aroundable,
but it doesn’t hurt and was easy to implement
2022-10-17 06:39:42 +02:00
BodgeMaster 36dcf0a0f5 resources/dumpnbt_output_format: Give end tags header and payload information 2022-10-17 06:37:16 +02:00
BodgeMaster 93fdcb7b65 resources: Add a file with examples for possible dumpnbt output
This may be useful for future unit tests.
I did this because I had no fucking idea how to implement drawing
the tree. I still have no fucking idea.

I hope there are no errors in there. If there are any, we’ll
find out when writing the relevant unit tests.
2022-10-16 07:40:55 +02:00
BodgeMaster 8d2f3f2fa5 tools/dumpnbt: Switch from using the validator to using the parser, update some strings 2022-10-16 07:40:00 +02:00
BodgeMaster cdc23e7468 lib/nbt: Implement parser 2022-10-15 23:05:26 +02:00
BodgeMaster e9bfb6eeee resources/NBT_data: Add two files that are valid NBT by our extended spec
Since we allow loose tags to be valid NBT data, this is valid NBT by our spec.
This isn’t valid by Mojang’s spec.
2022-10-15 21:41:32 +02:00
BodgeMaster 8b62ec9c88 lib/nbt: Get rid of that ugly #define return hack
Instead of doing #define return, the boolean returnValue is set and
a goto statement is used to get to the code that does what the macro
used to do.
2022-10-15 18:55:58 +02:00
BodgeMaster ca0af3306f test/nbt_tags: Implement Int64Array test
My arm hurts.
My back huts.
Man, that was exhausting.
Finally done.
2022-10-14 22:58:59 +02:00
BodgeMaster 9b21dfaee5 test/nbt_tags: Implement Int32Array test 2022-10-14 22:33:57 +02:00
BodgeMaster 996154fbbc lib/nbt: Return the correct error code from soem functions for i32 arrays. 2022-10-14 22:33:32 +02:00
BodgeMaster 77dd79398f test/nbt_tags: Test compound serialization 2022-10-14 21:46:55 +02:00
BodgeMaster fd5fe3967f test/nbt_tags: Partially implement compound test 2022-10-14 20:33:43 +02:00
BodgeMaster 374466f26c lib/nbt: Fix not actually returning from Compound::setElementPointerAt in the do nothing case 2022-10-14 20:01:47 +02:00
BodgeMaster 3b56a52085 lib/nbt: Fix a memory leak 2022-10-14 19:22:05 +02:00
BodgeMaster 7be73f86d4 lib/nbt: Prevent addition of additional end tags to compounds 2022-10-14 18:33:05 +02:00
BodgeMaster 71834e1018 lib/nbt: Build an end tag object into compound tag objects to prevent creation of endless compounds 2022-10-14 18:08:49 +02:00
BodgeMaster ccce564219 lib/error: Add NOT_ALLOWED 2022-10-14 17:39:00 +02:00
BodgeMaster 53279c6905 lib/error: Clean up comments 2022-10-14 17:30:35 +02:00
BodgeMaster f8dd10d301 test/nbt_tags: Finish test implementation for NBT::Tag::List 2022-10-13 11:34:57 +02:00
BodgeMaster 6672a4f149 test/nbt_tags: Implement string test and partial list test 2022-10-09 10:29:28 +02:00
BodgeMaster d5ce50a4a9 lib/nbt: Fix a potential memory leak 2022-10-09 10:18:20 +02:00
BodgeMaster f0092b78d3 lib/nbt: Make list serielizer return an error when serializing a list with no contained type set 2022-10-08 23:35:21 +02:00
BodgeMaster b1ba33b39f test/nbt_tags: Implement unit tests for Float, Double, and Int8Array 2022-10-08 12:56:13 +02:00
BodgeMaster 833c09e2da lib/nbt: Fix a bug caused by having two return statements swapped 2022-10-08 12:54:05 +02:00
BodgeMaster b53999a548 tools/arraydump: Remove a debugging message that I accidentally left in 2022-10-08 08:32:02 +02:00
BodgeMaster 379903d751 tools/baseconvert: New tool
This tool allows for easy conversion between relevant bases
for unsigned numbers.

You may be able to coerce it into converting signed numbers into
unsigned numbers if you know how, though that is not an officially
supported use case (aka that’s using UB in the STL).
2022-10-08 08:29:01 +02:00
BodgeMaster 17792ec5bf lib/cli: Implement the "Additional Info" feature 2022-10-08 08:27:48 +02:00
BodgeMaster 9ce35b5c6b test/nbt_tags: Implement more tests 2022-10-08 08:25:57 +02:00
BodgeMaster be08a97275 tesst/nbt_tags: Choose a more sensible value for int8_0 2022-10-06 20:46:50 +02:00
BodgeMaster 6bdf99c897 test/nbt_tags: Implement tests for the first three tag types 2022-10-06 11:10:51 +02:00
BodgeMaster 936def1a65 lib/nbt: move a comment (yes, very important commit) 2022-10-06 11:09:58 +02:00
BodgeMaster 3cc1222de9 lib/nbt: Fix a bug in NBT::Helper::writeString()
Writing a string to a byte vector would result in it being replaced
with one holding only the string.
2022-10-06 10:21:59 +02:00
BodgeMaster ecf3b14b5a test/nbt_tags: I have no clue what’s going on here.
Committing so I can debug at my PC.
2022-10-06 09:59:48 +02:00
BodgeMaster 8da758becd scripts/test/arraydump: Add test 2022-10-05 06:26:41 +02:00
BodgeMaster 1e5051b503 setupenv: Remove check for xxd
`xxd` is no longer needed as arraydump has been rewritten in C++
2022-10-05 05:31:37 +02:00
BodgeMaster 3b4c125ca2 tools/arraydump: Reimplement in C++ 2022-10-05 05:26:04 +02:00
BodgeMaster 4cb1206839 lib/file: Fix isOpen not being set properly when closing and reopening; add destructor 2022-10-05 05:02:58 +02:00
BodgeMaster 39c5940200 lib/file: Fix a potential memory leak created by yours truly 2022-10-05 04:50:41 +02:00
BodgeMaster f3e03710f6 lib/file: Ensure a file is actually open before attempting to read 2022-10-05 04:07:46 +02:00
BodgeMaster 72fc923839 lib/nbt: remove now useless comment 2022-10-05 04:02:32 +02:00
BodgeMaster 5059bd0193 lib/file: change File::eof to a function
This is just way easier to implement and less messy.
2022-10-05 04:01:18 +02:00
BodgeMaster 8bb633f118 lib/error and lib/file: reassign code 9 to FILE_NOT_OPEN
These are not exceptions so we don’t need multiple variants of
generic error in case someone wants to catch it somewhere
up the call stack.

It’s okay to use UNKNOWN for generic errors where we don’t know
exactly what happened.
2022-10-05 03:48:05 +02:00
BodgeMaster ec44ac9531 lib/file: Check if there are enough bytes left to read 2022-10-05 03:46:42 +02:00
BodgeMaster 341b4c187e lib/file: Fix (potential) memory leaks 2022-10-05 03:30:01 +02:00
BodgeMaster 79650e390e lib/nbt: Implement the rest of the functions outline in the header
This concludes the implementation of the in-memory NBT representation.
This is still all untested code so it might just blow up in your face.
The next step will be writing tests (and probably a lot of cursing and
debugging)...
2022-10-05 01:12:38 +02:00
BodgeMaster 2d2b67373c lib/nbt: Yesterday’s progress of implementing more of the functions in the header 2022-10-04 02:39:58 +02:00
BodgeMaster 4c4366f7e6 lib/nbt: Partially update documentation to current terminology.
Would probably be better if it were moved to doc/ and completely rewritten.
2022-10-02 07:48:03 +02:00
BodgeMaster 4ef1d2c44f lib/nbt: Split serializer into two components
One for serializing full tags and one for serializing tags without
their header.

The former is what used to be `toRawData` - though a bunch of
duplicated code has been removed by just moving it to the Generic
type class instead of having an implementation on the sub-classes.

The latter is useful for serializing lists.

The previous warning that all of this is untested still applies.
2022-10-02 07:47:12 +02:00
Shwoomple 5920d1d004 commit merge 2022-10-02 08:42:33 +05:30
Shwoomple 056c1e6b11 lib/file.cpp: fix read fubctions. 2022-10-02 08:38:39 +05:30
BodgeMaster 8b1491c311 lib/nbt: Add missing class prefix to function names 2022-10-01 04:57:45 +02:00
BodgeMaster fc2caf3bc0 lib/nbt: Implement more member functions for tag types 2022-10-01 04:51:53 +02:00
BodgeMaster 89baeebc65 lib/file: Switch to using pointers for passing File objects around 2022-09-30 20:10:28 +02:00
Shwoomple c204aa7d76 lib/file.cpp:Fix exception warnings 2022-09-30 22:41:55 +05:30
BodgeMaster 09503d3dc7 lib/file: Remove accidentally included C header 2022-09-30 18:31:49 +02:00
BodgeMaster 9fb06c998e scripts/build: Temporarily comment out hexnet 2022-09-30 18:31:20 +02:00
BodgeMaster aa97154474 lib/error: Remove misplaced code fragment
brea
2022-09-30 18:09:23 +02:00
BodgeMaster 9abfd9e5a9 setupenv: Move sourcing the local environment before FOSS-VG environment adjustments 2022-09-30 17:55:17 +02:00
Shwoomple 150ce826ba complete merge 2022-09-30 11:03:05 +05:30
Shwoomple 3449e3b9c4 lib/file: Implement broken file functions 2022-09-30 11:02:46 +05:30
Charlie Root 213d28a9b8 setupenv: Fix syntax errors 2022-09-30 06:13:38 +02:00
Shwoomple c1d7801436 complete merge 2022-09-28 08:22:09 +05:30
Shwoomple 9610f4a4a2 lib/file: implement open function 2022-09-28 08:21:39 +05:30
BodgeMaster 434c976cc5 lib/nbt: Continue implementing in-memory NBT tag data types
This is still all unvalidated work.
For all I know it might cause your computer to burst into flames,
grow an arm and a leg and an eye, and attack you with a sword.
But probably, it will just not work and I will realize that sleepy
past me was a fucking idiot (as always).
2022-09-28 03:43:54 +02:00
BodgeMaster 47fd1f8970 test/nbt_read_write_helpers: Change unnecessarily long string to be just one above the allowed size 2022-09-26 23:53:53 +02:00
BodgeMaster 10b1d9fa0c lib/nbt: Start implementing NBT data types, make NBT::Helper::writeString() return an error instead of aborting the program
The former is finally some progress on getting NBT going,
though at this point it is all unvalidated work.
For all I know it might cause your computer to burst into flames,
grow an arm and a leg and an eye, and attack you with a sword.
But probably, it won’t do that and instead start working after I
have debugged it so much that I wish I could attack my PC with a sword.

The latter is the implementation of a change I prepared in a previous commit
when i added ErrorOrVoid.
2022-09-26 03:11:44 +02:00
Shwoomple e8d41efeef lib/error.hpp: fix merge conflicts 2022-09-19 16:21:25 +05:30
Shwoomple 508b5e67e7 lib/error.hpp: add file errors 2022-09-19 16:19:52 +05:30
BodgeMaster 398321e415 test/nbt*: Replace NBT::helper with NBT::Helper
Yes, I forgot to run the test suite before committing.
This is why we need CI.
2022-09-19 11:47:29 +02:00
BodgeMaster 53878c3e2b lib/nbt: Start implementing the in-memory data structure for NBT 2022-09-15 06:06:47 +02:00
BodgeMaster ad291ee77d lib/nbt: Capitalize NBT::Helper because I feel like it 2022-09-15 02:00:07 +02:00
BodgeMaster 6149418f52 Add a level.dat to test the NBT parser on. 2022-09-15 01:51:51 +02:00
BodgeMaster ac12bcf865 Makefile: Finally get rid of this as we don't need it.
A makefile may be added back when we are preparing distribution but
it’s a long road until then.
2022-09-15 01:06:59 +02:00
BodgeMaster 8b92d24ab9 setupenv: Add dependency check 2022-09-11 10:11:17 +02:00
BodgeMaster aab91a2523 lib/nbt: Fix NBT::validateRawNBTData() and NBT::validateRawListContents closing #52 and #53 2022-09-11 09:14:32 +02:00
BodgeMaster 58b1199e38 lib/javacompat: Fixed JavaCompat::importJavaString() hanging when trying to import long strings
This was caused by an integer overflow due to using a too small data type in a counter variable.
2022-09-11 09:08:08 +02:00
Shwoomple 48f8a7dcf2 tools/hexnet: start implementing console input 2022-08-30 00:52:06 +05:30
BodgeMaster 209d0828b4 tools/hexnet: un-comment problematic sections 2022-08-29 21:06:54 +02:00
BodgeMaster b4d4ce77b2 tools/dumpnbt: better variable name and exit when data is invalid 2022-08-29 21:02:36 +02:00
BodgeMaster ee5048331c tools/dumpnbt: start implementing a preliminary version
for use until the full NBT library is in place...
2022-08-28 13:59:31 +02:00
BodgeMaster 629c999336 lib/nbt: Return correct error code from read functions (fixes #17) 2022-08-27 22:35:10 +02:00
BodgeMaster cdd17045d1 test/nbt_read_write_helpers: add more tests according to issue #43 2022-08-27 20:10:29 +02:00
BodgeMaster bb40f6553e tools/hexnet: comment sections out temporarily to get rid of compile errors
Doesn’t compile on Void x86_32 glibc. This will need to be investigated.
I just wanted to get rid of the errors while working on other issues.

Compile command:
ccache g++ -std=c++20 -Wall -Wextra src/tools/hexnet.cpp -I./include -Lbin/lib -l:cli.so -l:libsockpp.so -o bin/tools/hexnet
2022-08-27 11:50:36 +02:00
BodgeMaster a9759e3bc2 lib/file: Clarify what the functions do, take cursor position into account for cut functions and add missing cut function 2022-08-24 01:38:44 +02:00
BodgeMaster ab1164557d lib/file: Write header
I hope I didn’t forget anything. Not exactly capable of thinking rn.
2022-08-24 01:27:34 +02:00
BodgeMaster 1b8819ffe5 lib/error: Add ErrorOrVoid
This allows for error propagation on functions that would otherwise
not return anything.
2022-08-24 01:21:38 +02:00
BodgeMaster 4934a78aaa lib/error: Move definitions of constructors of ErrorOr<> inside class definition. 2022-08-24 01:19:59 +02:00
BodgeMaster 327ad9a9b5 Copyright Notice: Add authors section 2022-08-23 23:24:29 +02:00
BodgeMaster bddab2e9f8 setupenv: Add tool directories to PATH, be more verbose
No more `bin/tools/hexnet -4t 9000`. Just `hexnet -4t 9000` now.
2022-08-16 13:55:42 +02:00
BodgeMaster 017c8a61f8 scripts/tools/arraydump: New tool added
Never again will you need to manually convert binary data into an array!
This tool does it for you! Get it now for only $99! Limited availability!

To process data from stdin / a pipe, just pass `-` as the file name.
2022-08-16 13:40:50 +02:00
BodgeMaster 5272636cb8 test/nbt_read_write_helpers: fix unit tests for readString() 2022-08-15 13:30:53 +02:00
BodgeMaster 91d16ea451 test/javacompat: add test for mismatched size 2022-08-15 12:24:03 +02:00
BodgeMaster a1fc0ce4b4 lib/nbt: Fix a possible buffer overflow in readString() 2022-08-15 12:02:58 +02:00
Shwoomple ca7b121c4d tools/hexnet: Implement udp partially. 2022-08-15 15:07:33 +05:30
BodgeMaster 25bec4c587 lib/nbt: Validator: Fix bytes not being added up correctly in multiple places 2022-08-15 10:51:50 +02:00
BodgeMaster 589cf1ddaf lib/nbt: NBT validator: Fix wrong function declaration in the header, fix not using the currentPosition variable when accessing data 2022-08-15 09:53:06 +02:00
BodgeMaster 884a5239c6 lib/nbt: fix a bug in NBT::helper::readString() which caused it to asuume that dataSize is the size of the string 2022-08-15 09:51:46 +02:00
BodgeMaster 9190cad80d lib/nbt: finish implementation of validateRawNBTData() and fix a critical macro-induced bug
I did a `#define return` and then tried to `if () return;` everywhere...
2022-08-15 08:50:07 +02:00
BodgeMaster a862590370 lib/nbt: Start implementing the NBT validator
In theory, this is it. It’s just missing the portion that deals with lists
and unit tests. Both will each likely require similar effort to this.
2022-08-15 05:20:05 +02:00
BodgeMaster 3995e97f03 lib/javacompat: Make the endianness error message refer to the correct function 2022-08-15 02:07:00 +02:00
BodgeMaster c9ec524db1 test/nbt_size_helpers: Implement tests for valid input 2022-08-13 17:32:47 +02:00
BodgeMaster 73ae58e522 test/assert: Add line number to assertion failed message 2022-08-13 17:32:47 +02:00
BodgeMaster acc19ae100 lib/nbt: Change behavior of totalTagSize to treat encounters of compounds and lists as errors
I stumbled over this when writing the unit test. Previously, it would return
an error code but explicitly mark it as not being an error. This was intended
behavior but I decided to change it because I didn’t anticipate it when writing
the test.

Technically `ErrorOr<T>` can be used to pass any message alongside `T`,
but practically, it is reasonable to assume that the error code is
`ErrorCodes::SUCCESS` when `isError` is false. Therefore, this feature
should be really only used in the weirdest edge cases - if at all.
Even then, it is most likely still preferable to flag it as an error and
just hand the resulting `T` back using the long constructor
`ErrorOr<T>(bool, uint8_t, T)`.
2022-08-13 17:32:47 +02:00
BodgeMaster 149285c357 lib/nbt: Finish implementing containedDataLength, rename nextTagTotalSize->totalTagSize and nextTagDataLength->containedDataLength 2022-08-13 17:32:47 +02:00
BodgeMaster 0c92bdf8fd test/nbt_size_helpers: begin adding unit tests for lib/nbt's new nextTag size helpers 2022-08-13 17:32:47 +02:00
BodgeMaster 86f1ef596f lib/nbt: Begin implementing nextTagDataLength 2022-08-13 17:32:47 +02:00
BodgeMaster 027f324f03 lib/nbt: Fix a bug in nextTagTotalSize and significantly improve readability by removing redundant code 2022-08-13 17:32:47 +02:00
BodgeMaster 7a2c1d7d57 scripts/test: Clean old unit tests before building new ones to avoid confusion
if a unit test fails to build and old unit tests remain in place, this can
lead to confusion when the old test is run anyway.

"Why are some test cases missing?"
"Huh, it failed to build but passed?"
2022-08-13 17:32:47 +02:00
BodgeMaster 6fecb2cdb7 scripts/test/hexnet: Add license information
Yeah, I forgot it again -_-
2022-08-13 17:32:47 +02:00
BodgeMaster e882a09099 Build system: Improve output readability and prepare for script based unit tests 2022-08-13 17:32:47 +02:00
BodgeMaster 748c91c375 scripts/test: change unit test file names 2022-08-13 17:32:47 +02:00
BodgeMaster f5d85da98c lib/nbt: Move the functions for getting tag sizes into the helper namespace, give up on handling lists the same as all other tags
I tried dealing with lists in the same way as with other more basic tags
but came to the conclusion that this is most likely not feasible in the same
way that it is not feasible for compounds. It would require a mini-parser
that can deal with all sorts of tags (including nested lists and compounds).

Instead, an approach more similar to the recursion for compound tags will
be used (using its own function to deal with the missing tag headers ofc).
2022-08-13 17:32:47 +02:00
BodgeMaster 396b9673fd lib/nbt: Various minor fixes to get the program to compile properly 2022-08-13 17:32:47 +02:00
BodgeMaster 68fbf3ae20 lib/nbt: remove a function used to get the next tag type which introduced unnecessary complexity 2022-08-13 17:32:47 +02:00
BodgeMaster 4363432025 Environment: don't unset PROJECT_BASE_DIR which is needed for the aliases to work properly 2022-08-13 17:32:47 +02:00
BodgeMaster 5400790e78 test/nbt*: rename files, move byte tag object test from helper test file into its own file 2022-08-13 17:32:47 +02:00
BodgeMaster c7dd5471dd lib/nbt: Start implementing NBT validator 2022-08-13 17:32:47 +02:00
BodgeMaster 8048dc8891 tools/hexnet: Put the new usage generator to use and remove prefixes for IPv4 and IPv6
The prefixes were part of a planned feature but since a connection is
either IPv4 or IPv6 but never both, it would have been completely useless
to specify which to use. Instead, only TCP and UDP will need to be specified.
2022-08-12 12:30:55 +02:00
BodgeMaster a1f16e6f6b lib/cli: Fix the usage text generator not dealing well with absent sections 2022-08-12 11:59:42 +02:00
Shwoomple cb7b5ddba7 lib/cli: Add usage generator. 2022-08-12 13:35:56 +05:30
Shwoomple e0648720bb tools/hexnet: Implement ipv6 support. 2022-08-11 22:55:12 +05:30
Shwoomple ebcf436a18 lib/cli: delete duplicate header file. 2022-08-11 18:57:22 +05:30
BodgeMaster c59a1ac723 Resources: Add two NBT files that can be used for testing. 2022-08-05 09:12:27 +02:00
BodgeMaster aef91fe7cd test/nbt_helpers: Fix wrong test pass message 2022-08-04 07:50:20 +02:00
BodgeMaster 4af9003761 Code style: I just decided to accept that float and double exist and that we can just assume they are 32 and 64 bits repectively.
This isn't going to run on an Arduino or anything like that anyway.
2022-08-04 07:47:24 +02:00
Milan Suman 704b440d5a lib/nbt: Add tag classes 2022-08-04 00:01:12 +05:30
BodgeMaster 608767f5c2 tools/hexnet: Add more command line flags and options
This only adds the options to the parser. They aren't used anywhere in the code yet.
2022-08-02 03:42:37 +02:00
BodgeMaster e31bff0802 test/nbt_writestring_failure_mode: Add license information 2022-08-02 03:37:10 +02:00
BodgeMaster 5c73308934 Rename all headers from .h++ to .hpp
Idk why I did that in the first place. Probably bc hpp looks stupid.
But having a + in a file name bugs me just as much. And other ppl as well.
So I changed it.
2022-08-02 03:35:08 +02:00
BodgeMaster b59fe1857e lib/cli: minor refactoring to make things less confusing and nicer to use
I renamed "unpositional arguments" to "options" and "positional arguments" to "arguments".
This is intended to make the code more readable and easier to type out.
2022-08-02 03:16:54 +02:00
BodgeMaster 69f15e928a lib/cli: Add fields for a short description and additional usage information to the arguments parser
This is in preparation for building the help text generator.
2022-08-02 02:03:50 +02:00
BodgeMaster d0d02fc8d2 tools/hexnet: Move the TCP reading portion to a thread
This was too straight-forward to not just do it when I previously worked
on hexnet. Why didn't I just do it? Idk.
2022-08-02 01:07:20 +02:00
BodgeMaster 1308327fae tests: OCD fixes lol
sorry
2022-08-02 01:04:45 +02:00
BodgeMaster 4582c3e595 test/nbt_helpers: move the test that aborts to its own program 2022-08-02 00:41:11 +02:00
BodgeMaster 28719072bb fix a compiler warning 2022-08-01 16:39:18 +02:00
BodgeMaster 800fd66044 Build system: fix typo 2022-07-30 21:14:23 +02:00
BodgeMaster d90e7f16bd Build system: better cross-platform compatibility
I hope we don't run into more versions of sha256(sum)...
2022-07-30 21:10:07 +02:00
BodgeMaster 845b3fb922 Documentation: update build system documentation and prerequisites in preparation for next commit 2022-07-30 21:08:03 +02:00
Milan Suman 4f1ad714bd lib/nbt.cpp: Implement writeString function 2022-07-28 17:15:04 +05:30
BodgeMaster adc9a7f36b tools/hexnet: prepare for multithreading
Multithreading will be needed to simultaneously receive and send data.

The preparations include:
- move all the settings of the program into global scope
- add mutexes
- move the code that reads from the TCP socket into a dedicated function
2022-07-25 15:55:40 +02:00
BodgeMaster b044503951 test/cli_argument_parser: get rid of useless parameters
(pointed out by compiler warning)
2022-07-23 10:55:03 +02:00
BodgeMaster d97e1a8336 Build system: Use -Wextra 2022-07-23 10:54:05 +02:00
BodgeMaster 6baff11ebd test/cli_argument_parser: implement tests for generated error messages 2022-07-21 09:41:04 +02:00
BodgeMaster c9d6cf0b5e lib/cli: minor consistency changes 2022-07-21 09:39:45 +02:00
BodgeMaster 7d7ce2ba6b Documentation: Update build system documentation 2022-07-20 19:45:40 +02:00
BodgeMaster a51c65c9f2 test/javacompat: get rid of a compiler warning 2022-07-20 19:22:06 +02:00
BodgeMaster 8bb0732cc6 Build system: add single-threaded compile mode to test script 2022-07-20 19:20:27 +02:00
BodgeMaster 0dcc579bb5 test/cli_arguments_parser: extend some tests, implement all remaining parser tests except checking for error messages 2022-07-20 19:16:02 +02:00
BodgeMaster db3b133f88 lib/cli: fix trailing incomplete unpositional arguments
The parser used to rely on the next iteration of the loop to detect
if an unpositional argument was missing its value, this has now been
fixed by adding an additional check on unpositional arguments waiting
for a value that detects if the end of the loop has been reached
2022-07-20 18:30:25 +02:00
BodgeMaster 44a20c875a Build system: cache downloaded files 2022-07-20 15:52:04 +02:00
BodgeMaster 1804433a9f Build system: add a central directory to collect all the include files 2022-07-20 12:15:31 +02:00
BodgeMaster 2cc2543b2a lib/error: fix compiler error with g++11 or newer
This wasprobably a syntax error on my part.
2022-07-20 09:58:58 +02:00
BodgeMaster a0d2974f0a test/cli_argument_parser: fix programmer-generated segfault 2022-07-20 08:46:06 +02:00
Milan Suman 4659946a2f src/test: free pointers 2022-07-20 12:13:47 +05:30
Milan Suman b5312aeb58 lib/nbt: implement readString NBT helper function 2022-07-20 12:08:04 +05:30
BodgeMaster 9562ae7be9 Merge branch 'master' of https://lostcave.ddnss.de/git/BodgeMaster/FOSS-VG 2022-07-20 08:07:03 +02:00
BodgeMaster b1733bc007 tools/hexnet: initial implementation of TCP receive only server 2022-07-20 08:06:45 +02:00
Milan Suman d315c6fcfc Merge branch 'broken' 2022-07-20 10:39:16 +05:30
Milan Suman 2f38636a27 lib/javacompat: implement exportJavaString 2022-07-20 10:38:55 +05:30
BodgeMaster 21310fecc7 environment: allow for local customizations using a hidden file
To use this, create a file `.localenv.bashrc` in the project's
root directory and put your customizations in there.
2022-07-19 01:19:17 +02:00
BodgeMaster 09e2030a55 test/cli_argument_parser: More thorough unknwon flag test, add unknown unpositional argument test 2022-07-18 21:26:37 +02:00
51 changed files with 7430 additions and 934 deletions

9
.gitignore vendored
View File

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

View File

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

View File

@ -22,13 +22,19 @@ Immediate goals:
### 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
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
- if using bash:
- `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`
- if not using bash or not using the provided environment:
- `cd` to the project's base directory

View File

@ -48,11 +48,6 @@ universally used.
Use explicitly sized data types where possible.
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
Use the hash bang `#!/usr/bin/env bash`.

View File

@ -1,5 +1,8 @@
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
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, version 3.

View File

@ -24,11 +24,15 @@ Provided aliases:
## setup_project
Cleans the project and its dependencies first, ensuring the project is in
a known state, then downloads and extracts dependencies.
Cleans the project and its dependencies first (see below), ensuring
the project is in a known state, then sets up dependencies.
This will probably do more in the future (for example compile dependencies
that need to be compiled).
This includes downloading (and caching) of files that are not yet
locally cached and compiling dependencies that need to be compiled.
This scripts intended purpose is setting up a development environment but
it can also be used to update the locally set-up dependencies to the ones
that the project is using.
# build
@ -55,14 +59,37 @@ Accepted environment variables:
- `CXX`: override the default compiler
- `CXXFLAGS`: override the default compiler flags, must at least specify
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
Removes and re-creates `bin/`. Also removes the endianness header and
the endianness check binary.
Removes and re-creates `bin/` and `include/`, then re-creates symlinks to
shared object and header files as needed for the build process.
Also removes the endianness header and the endianness check binary.
# 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

0
include/.placeholder Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

BIN
resources/all_bytes Normal file

Binary file not shown.

View File

@ -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. Lets find out.
|
|[92: Compound] compound:
| |Header: 11 bytes
| |Payload: 45 bytes
| |Total: 56 bytes
| |Length: 3
| |
| |[103: 32 Bit Integer] some_number:
| | |Header: 14 bytes
| | |Payload: 4 bytes
| | |Total: 18 bytes
| | 'Value: -754506943
| |
| |[121: String] some_text:
| | |Header: 12 bytes
| | |Payload: 14 bytes
| | |Total: 26 bytes
| | 'Value: eat a cookie
| |
| '[147: End]:
| |Header: 1 byte
| |Payload: 0 bytes
| 'Total: 1 byte
|
|[148: Double] double:
| |Header: 9 bytes
| |Payload: 8 bytes
| |Total: 17 bytes
| 'Value: 623593.6542742235
|
|[165: Float] float:
| |Header: 8 bytes
| |Payload: 4 bytes
| |Total: 12 bytes
| 'Value: 35.2678337097168
|
|[177: 16 Bit Integer] int16:
| |Header: 8 bytes
| |Payload: 2 bytes
| |Total: 10 bytes
| 'Value: 2000
|
|[187: 32 Bit Integer] int32:
| |Header: 8 bytes
| |Payload: 4 bytes
| |Total: 12 bytes
| 'Value: 10101010
|
|[199: Array of 32 Bit Integers] int32_array:
| |Header: 14 bytes
| |Payload: 20 bytes
| |Total: 34 bytes
| |Length: 4
| 'Values:
| |398452796
| |43259
| |2147483647
| '1634890337
|
|[233: 64 Bit Integer] int64:
| |Header: 8 bytes
| |Payload: 8 bytes
| |Total: 16 bytes
| 'Value: 810001800766
|
|[249: Array of 64 Bit Integers] int64_array:
| |Header: 14 bytes
| |Payload: 44 bytes
| |Total: 58 bytes
| |Length: 5
| 'Values:
| |239865
| |23586749
| |9223372036854775807
| |188944201329624
| '3116157694992754
|
|[307: 8 Bit Integer] int8:
| |Header: 7 bytes
| |Payload: 1 byte
| |Total: 8 bytes
| 'Value: 100
|
|[315: Array of 8 Bit Integers] int8_array:
| |Header: 13 bytes
| |Payload: 12 bytes
| |Total: 25 bytes
| |Length: 8
| 'Values:
| |113
| |53
| |119
| |98
| |84
| |100
| |245
| '50
|
|[340: List] list_int8:
| |Header: 12 bytes
| |Payload: 10 bytes
| |Total: 22 bytes
| |Contained Type: 8 Bit Integer
| |Length: 5
| |
| |[357: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 65
| |
| |[358: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 96
| |
| |[359: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 78
| |
| |[360: 8 Bit Integer]:
| | |Payload: 1 byte
| | |Total: 1 byte
| | 'Value: 127
| |
| '[361: 8 Bit Integer]:
| |Payload: 1 byte
| |Total: 1 byte
| 'Value: -6
|
|[362: List] list_strings:
| |Header: 15 bytes
| |Payload: 77 bytes
| |Total: 92 bytes
| |Contained Type: String
| |Length: 12
| |
| |[382: String]:
| | |Payload: 8 bytes
| | |Total: 8 bytes
| | 'Value: Pacman
| |
| |[390: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: ate
| |
| |[395: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: all
| |
| |[400: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: the
| |
| |[405: String]:
| | |Payload: 6 bytes
| | |Total: 6 bytes
| | 'Value: dots
| |
| |[411: String]:
| | |Payload: 4 bytes
| | |Total: 4 bytes
| | 'Value: so
| |
| |[415: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: now
| |
| |[420: String]:
| | |Payload: 8 bytes
| | |Total: 8 bytes
| | 'Value: hes
| |
| |[428: String]:
| | |Payload: 8 bytes
| | |Total: 8 bytes
| | 'Value: coming
| |
| |[436: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: for
| |
| |[441: String]:
| | |Payload: 5 bytes
| | |Total: 5 bytes
| | 'Value: the
| |
| '[446: String]:
| |Payload: 8 bytes
| |Total: 8 bytes
| 'Value: words.
|
|[454: String] string:
| |Header: 9 bytes
| |Payload: 14 bytes
| |Total: 23 bytes
| 'Value: Hello World!
|
'[477: End]:
|Header: 1 byte
|Payload: 0 bytes
'Total: 1 byte

View File

@ -15,20 +15,7 @@
# 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 "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"
source scripts/lib.sh
# if unknown, figure out endianness
if [ -f .endianness ]; then
@ -46,10 +33,10 @@ fi
# `.cpp` files in src/lib will be automatically picked up and compiled into
# dynamically linked libraries.
echo "Building libs..."
mkdir -pv bin/lib
echo ">>> Building libs..."
create_directory bin/lib
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
$COMPILE_COMMAND &
$WAIT_ANYWAY
@ -68,12 +55,14 @@ wait
# How to run a tool: specify the library path to use for the dynamic linker
# when running a program
# Example: LD_LIBRARY_PATH=bin/lib bin/tools/dumpnbt
echo "Building tools..."
mkdir -pv bin/tools
echo ">>> Building tools..."
create_directory bin/tools
# add compile commands to this array
COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/tools/dumpnbt.cpp -Lbin/lib -l:nbt.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/dumpnbt.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -l:cli.so -o bin/tools/dumpnbt"
"$CXX_WITH_FLAGS src/tools/arraydump.cpp -I./include -Lbin/lib -l:file.so -l:cli.so -o bin/tools/arraydump"
"$CXX_WITH_FLAGS src/tools/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
echo "${COMPILE_COMMANDS[command]}"

View File

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

View File

@ -15,7 +15,26 @@
# version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
rm -rv ./dependencies
mkdir -v ./dependencies
set -v
echo -n "" > ./dependencies/.placeholder
source scripts/lib.sh
echo ">>> Checking download cache for unneeded or corrupted files..."
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

78
scripts/lib.sh Normal file
View File

@ -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 dont support `mkdir -v`
function create_directory {
echo "Creating directory: $1"
mkdir -p "$1"
}
# `mkfile -v` if you will
function create_file {
echo "Creating file: $1"
touch "$1"
}

View File

@ -15,6 +15,8 @@
# version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
source scripts/lib.sh
echo -n "Wget or curl? "
if command -v wget >/dev/null 2>&1; then
USE_WGET=yes
@ -32,53 +34,67 @@ fi
function download {
URL="$1"
DESTINATION="$2"
SHA256SUM="$3 *$2"
SHA256SUM="$3"
if [ ! -d .download_cache ]; then
echo "Cache directory missing."
create_directory .download_cache
fi
if [ -f ".download_cache/$SHA256SUM" ]; then
if check_sha256 ".download_cache/$SHA256SUM" "$SHA256SUM"; then
echo "Using locally cached file for $DESTINATION"
cp ".download_cache/$SHA256SUM" "$DESTINATION"
return 0
else
echo "Locally cached file for $DESTINATION is corrupted."
rm ".download_cache/$SHA256SUM"
download "$URL" "$DESTINATION" "$SHA256SUM"
return $?
fi
else
echo -n "Downloading $URL to $DESTINATION... "
if [ $USE_WGET = yes ]; then
wget -O "$DESTINATION" "$URL" >/dev/null 2>&1
wget -O ".download_cache/$SHA256SUM" "$URL" >/dev/null 2>&1
else
curl -L "$URL" --output "$DESTINATION" >/dev/null 2>&1
curl -L "$URL" --output ".download_cache/$SHA256SUM" >/dev/null 2>&1
fi
if sha256sum --check <<< $SHA256SUM >/dev/null 2>&1; then
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."
rm -v "$DESTINATION"
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
}
echo "Cleaning build files..."
scripts/clean.sh
echo "Cleaning dependencies..."
scripts/clean_dependencies.sh
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/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
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
tar -xf dependencies/tmp/sockpp.tar -C dependencies
echo "done."
echo -n "Building sockpp... "
echo "done"
echo ">>> Building sockpp... "
pushd dependencies/sockpp-0.7.1/ >/dev/null 2>&1
cmake -Bbuild .
cmake --build build
popd >/dev/null 2>&1
echo "done."
rm -rv dependencies/tmp
echo ">>> Cleaning up..."
remove dependencies/tmp

View File

@ -15,13 +15,7 @@
# version 3 along with this program.
# If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
if [ -z "$CXX" ]; then
CXX="c++"
fi
if [ -z "$CXXFLAGS" ]; then
CXXFLAGS="-std=c++20 -Wall"
fi
CXX_WITH_FLAGS="$CXX $CXXFLAGS"
source scripts/lib.sh
echo -n "Using LD_LIBRARY_PATH "
if [ -z "$LD_LIBRARY_PATH" ]; then
@ -31,25 +25,38 @@ else
fi
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
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/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
echo "${COMPILE_COMMANDS[command]}"
${COMPILE_COMMANDS[command]} &
$WAIT_ANYWAY
done
wait
echo "Running tests..."
echo ">>> Running tests..."
# explicitly allow commands to fail at this stage
set +e
for test in $(ls bin/test); do
bin/test/$test
done
for test in $(ls scripts/test); do
scripts/test/$test
done

11
scripts/test/arraydump.sh Executable file
View File

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

22
scripts/test/baseconvert.sh Executable file
View File

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

23
scripts/test/hexnet.sh Normal file
View File

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

View File

View File

@ -13,9 +13,15 @@
# version 3 along with this program.
# 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 )"
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_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
}
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
export LD_LIBRARY_PATH="$PROJECT_BASE_DIR"/bin/lib
else
export LD_LIBRARY_PATH="$PROJECT_BASE_DIR"/bin/lib:"$LD_LIBRARY_PATH"
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

View File

@ -1,5 +1,8 @@
// 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.
@ -13,13 +16,12 @@
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include "cli.h++"
#include <string>
#include <vector>
#include <map>
#include "error.h++"
#include "cli.hpp"
#include "error.hpp"
namespace CLI {
Flag::Flag() {
@ -32,10 +34,10 @@ namespace CLI {
this->present = false;
}
UnpositionalArgument::UnpositionalArgument() {
Option::Option() {
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->longName = longName;
this->description = description;
@ -43,23 +45,22 @@ namespace CLI {
this->present = false;
}
PositionalArgument::PositionalArgument() {
Argument::Argument() {
this->present = false;
}
PositionalArgument::PositionalArgument(std::string placeholder, std::string description) {
Argument::Argument(std::string placeholder, std::string description) {
this->description = description;
this->placeholder = placeholder;
this->present = false;
}
// 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->wrongUsageMessages = std::vector<std::string>();
this->programName = std::string(argv[0]);
this->positionalArguments = positionalArguments;
// create lookup tables for all flags and unpositional arguments
// by their names
this->arguments = arguments;
// create lookup tables for all flags and options by their names
this->flagsByShortName = std::map<char, Flag*>();
this->flagsByLongName = std::map<std::string, Flag*>();
for (Flag flag: flags) {
@ -68,25 +69,25 @@ namespace CLI {
this->flagsByShortName[flag.shortName] = flagPointer;
this->flagsByLongName[flag.longName] = flagPointer;
}
this->argumentsByShortName = std::map<char, UnpositionalArgument*>();
this->argumentsByLongName = std::map<std::string, UnpositionalArgument*>();
for (UnpositionalArgument unpositionalArgument: unpositionalArguments) {
UnpositionalArgument* argumentPointer = new UnpositionalArgument();
*argumentPointer = unpositionalArgument;
this->argumentsByShortName[unpositionalArgument.shortName] = argumentPointer;
this->argumentsByLongName[unpositionalArgument.longName] = argumentPointer;
this->optionsByShortName = std::map<char, Option*>();
this->optionsByLongName = std::map<std::string, Option*>();
for (Option option: options) {
Option* optionPointer = new Option();
*optionPointer = option;
this->optionsByShortName[option.shortName] = optionPointer;
this->optionsByLongName[option.longName] = optionPointer;
}
UnpositionalArgument* argumentWaitingForValue = nullptr;
std::vector<CLI::PositionalArgument>::size_type positionalArgumentCounter = 0;
Option* optionWaitingForValue = nullptr;
std::vector<CLI::Argument>::size_type argumentCounter = 0;
for (int i=1; i<argc; i++) {
std::string argument(argv[i]);
if (argument[0]=='-') {
// do we have unfinished business?
if (argumentWaitingForValue!=nullptr) {
if (optionWaitingForValue!=nullptr) {
this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+argumentWaitingForValue->longName);
argumentWaitingForValue = nullptr;
this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+optionWaitingForValue->longName);
optionWaitingForValue = nullptr;
}
// long name or short name?
if (argument[1]=='-') {
@ -98,15 +99,19 @@ namespace CLI {
auto position = argument.find("=");
if (position==std::string::npos) {
// no =value
//is argument or flag?
//is option or flag?
std::string argumentName = argument.substr(2,argument.length()-2);
if (flagsByLongName.contains(argumentName)) {
// flag
flagsByLongName[argumentName]->present = true;
} else if (argumentsByLongName.contains(argumentName)) {
// unpositional argument
argumentsByLongName[argumentName]->present = true;
argumentWaitingForValue = argumentsByLongName[argumentName];
} else if (optionsByLongName.contains(argumentName)) {
// option
optionsByLongName[argumentName]->present = true;
optionWaitingForValue = optionsByLongName[argumentName];
if (i+1 == argc) {
this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Argument expects value but has none: ")+argumentName);
}
} else {
this->wrongUsage = true;
this->wrongUsageMessages.push_back(std::string("Unknown argument or flag: ")+argument);
@ -115,9 +120,9 @@ namespace CLI {
// has =value
std::string value = argument.substr(position+1, argument.length()-position-1);
std::string argumentName = argument.substr(2, position-2);
if (argumentsByLongName.contains(argumentName)) {
argumentsByLongName[argumentName]->present = true;
argumentsByLongName[argumentName]->value = value;
if (optionsByLongName.contains(argumentName)) {
optionsByLongName[argumentName]->present = true;
optionsByLongName[argumentName]->value = value;
} else {
this->wrongUsage = true;
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
// (std::__cxx11::basic_string<char>::size_type ?)
// starting at 1 because 0 is '-'
for (int i=1; i<(int) argument.length(); i++) {
//is argument or flag?
if (flagsByShortName.contains(argument[i])) {
flagsByShortName[argument[i]]->present = true;
} else if (argumentsByShortName.contains(argument[i])) {
argumentsByShortName[argument[i]]->present = true;
for (int j=1; j<(int) argument.length(); j++) {
// is option or flag?
if (flagsByShortName.contains(argument[j])) {
// flag
flagsByShortName[argument[j]]->present = true;
} else if (optionsByShortName.contains(argument[j])) {
// option
optionsByShortName[argument[j]]->present = true;
//FIXME: see above
if (i+1==(int) argument.length()) {
argumentWaitingForValue = argumentsByShortName[argument[i]];
if (j+1==(int) argument.length()) {
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 {
//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;
}
} else {
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
// no unwanted options get activated on programs
// that deal gracefully with unrecognized command
@ -155,39 +166,48 @@ namespace CLI {
}
}
} else {
// positional argument or value for unpositional arg?
if (argumentWaitingForValue==nullptr) {
// positional argument
if (positionalArgumentCounter < this->positionalArguments.size()) {
this->positionalArguments.at(positionalArgumentCounter).present = true;
this->positionalArguments.at(positionalArgumentCounter).value = argument;
// argument or value for option?
if (optionWaitingForValue==nullptr) {
// argument
if (argumentCounter < this->arguments.size()) {
this->arguments.at(argumentCounter).present = true;
this->arguments.at(argumentCounter).value = argument;
} else {
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 {
// value for unpositional argument
argumentWaitingForValue->value = argument;
argumentWaitingForValue = nullptr;
// value for option
optionWaitingForValue->value = argument;
optionWaitingForValue = nullptr;
}
}
}
for (PositionalArgument const& positionalArgument: this->positionalArguments) {
if (!positionalArgument.present) {
for (Argument const& argument: this->arguments) {
if (!argument.present) {
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() {
//TODO: check that this actually runs
for (auto const& [shortName, flag]: this->flagsByShortName) {
delete flag;
}
for (auto const& [shortName, unpositionalArgument]: this->argumentsByShortName) {
delete unpositionalArgument;
for (auto const& [shortName, option]: this->optionsByShortName) {
delete option;
}
}
@ -211,35 +231,100 @@ namespace CLI {
else return ErrorOr<bool> (false, ErrorCodes::NOT_PRESENT, false);
}
ErrorOr<std::string> ArgumentsParser::getPositionalArgument(std::vector<CLI::PositionalArgument>::size_type position){
if (position >= this->positionalArguments.size()) return ErrorOr<std::string>(true, ErrorCodes::OUT_OF_RANGE, std::string(""));
ErrorOr<std::string> ArgumentsParser::getArgument(std::vector<CLI::Argument>::size_type position){
if (position >= this->arguments.size()) return ErrorOr<std::string>(true, ErrorCodes::OUT_OF_RANGE, std::string(""));
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(""));
}
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) {
if (!this->argumentsByShortName.contains(shortName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string(""));
ErrorOr<std::string> ArgumentsParser::getOption(char shortName) {
if (!this->optionsByShortName.contains(shortName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string(""));
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(""));
}
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, ""
else return ErrorOr<std::string>(false, ErrorCodes::NOT_PRESENT, std::string(""));
}
ErrorOr<std::string> ArgumentsParser::getUnpositionalArgument(std::string longName) {
if (!this->argumentsByLongName.contains(longName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string(""));
ErrorOr<std::string> ArgumentsParser::getOption(std::string longName) {
if (!this->optionsByLongName.contains(longName)) return ErrorOr<std::string>(true, ErrorCodes::UNKNOWN_KEY, std::string(""));
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(""));
}
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, ""
else return ErrorOr<std::string>(false, ErrorCodes::NOT_PRESENT, std::string(""));
}
//std::string ArgumentsParser::getUsage();
std::string ArgumentsParser::getUsage(){
std::string usageString = "";
if (this->description != "") {
usageString += "Help: " + this->programName + "\n\n\t" + this->description + "\n\n";
}
usageString += "Usage: " + this->programName + " ";
if(!this->flagsByShortName.empty()){
usageString += "[-";
}
for(const auto& [key, value]: this->flagsByShortName){
usageString.push_back(key);
}
if(!this->flagsByShortName.empty()){
usageString += "] ";
}
for(const auto& [key, value]: this->optionsByShortName){
usageString += "[-";
usageString.push_back(key);
usageString += " " + value->placeholder + "] ";
}
for(const auto& argument: this->arguments){
usageString += argument.placeholder + " ";
}
usageString.push_back('\n');
if(!this->flagsByShortName.empty()){
usageString += "\nFlags:\n";
for(const auto& [key, value]: this->flagsByShortName){
usageString += "\t-";
usageString.push_back(key);
usageString += ", --" + value->longName + "\n\t\t" + value->description + "\n";
}
}
if(!this->optionsByShortName.empty()){
usageString += "\nOptions:\n";
for(const auto& [key, value]: this->optionsByShortName){
usageString += "\t-";
usageString.push_back(key);
usageString += " " + value->placeholder + ", --" + value->longName + "=" + value->placeholder + "\n\t\t" + value->description + "\n";
}
}
if(!this->arguments.empty()){
usageString += "\nArguments:\n";
for(const auto& argument: this->arguments){
usageString += "\t" + argument.placeholder + "\n\t\t" + argument.description + "\n";
}
}
if (this->additionalInfo != "") {
usageString += "\nAdditional Info:\n\n\t" + this->additionalInfo + "\n";
}
return usageString;
}
}

View File

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

View File

@ -23,37 +23,43 @@ struct ErrorOr {
uint8_t errorCode;
T value;
ErrorOr<T>();
ErrorOr<T>(T);
ErrorOr<T>(bool, uint8_t);
ErrorOr<T>(bool, uint8_t, T);
ErrorOr() {
this->isError = false;
this->errorCode = 0;
}
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>
ErrorOr<T>::ErrorOr() {
struct ErrorOrVoid {
bool isError;
uint8_t errorCode;
ErrorOrVoid() {
this->isError = false;
this->errorCode = 0;
}
}
template <typename T>
ErrorOr<T>::ErrorOr(T value) {
this->isError = false;
this->errorCode = 0;
this->value = value;
}
template <typename T>
ErrorOr<T>::ErrorOr(bool isError, uint8_t errorCode) {
ErrorOrVoid(bool isError, uint8_t errorCode) {
this->isError = isError;
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 {
// These are all arbitrary values used to assign error codes to different
@ -64,13 +70,10 @@ namespace ErrorCodes {
// Ahh yes, very useful.
const uint8_t SUCCESS = 0;
// IndexOutOfRangeException equivalent
const uint8_t OUT_OF_RANGE = 1;
// when going out of bounds in a non-predetermined way
// like OUT_OF_RANGE but when going out of bounds in a non-predetermined way
const uint8_t OVERRUN = 2;
// when checking for presence of something, for example CLI arguments
const uint8_t NOT_PRESENT = 3;
const uint8_t WRONG_USAGE = 4;
@ -78,6 +81,20 @@ namespace ErrorCodes {
// when dealing with maps
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 UNKNOWN = 255;

274
src/lib/file.cpp Normal file
View File

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

92
src/lib/file.hpp Normal file
View File

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

View File

@ -15,18 +15,30 @@
#include <tinyutf8/tinyutf8.h>
#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 {
//FIXME: contrary to what I said, we need to explicitly pass the data
// size because files could have been tampered with or corrupted
tiny_utf8::string importJavaString(uint8_t data[]) {
ErrorOr<tiny_utf8::string> importJavaString(uint8_t data[], uint16_t size) {
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(data[i] == 0x80 && data[i-1] == 0xc0){
stdString[stdString.length() - 1] = '\0';
@ -35,11 +47,48 @@ namespace JavaCompat {
}
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) {
return ErrorOr(nullptr);
ErrorOr<std::vector<uint8_t>> exportJavaString(tiny_utf8::string data) {
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);
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

307
src/lib/nbt.hpp Normal file
View File

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

View File

@ -15,4 +15,4 @@
#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; }

View File

@ -16,14 +16,14 @@
#include <string>
#include <iostream>
#include <vector>
#include "assert.h++"
#include "../lib/error.h++"
#include "assert.hpp"
#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 << "CLI argument parser tests" << std::endl;
std::cout << "CLI argument parsing tests" << std::endl;
std::cout << "################################################################################" << std::endl;
// Valid parameter test ############################################
@ -31,7 +31,7 @@ int main(int argc, char* argv[]) {
// with many variations of valid input on a single 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;
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('6', "66666", "unused flag"));
std::vector<CLI::UnpositionalArgument> validTestUnpositionalArguments;
validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('c', "ccccc", "VALUE", "short argument concatenated with its value"));
validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('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"));
validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('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 ="));
validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value"));
validTestUnpositionalArguments.push_back(CLI::UnpositionalArgument('z', "zzzzz", "NOPE", "unused argument"));
std::vector<CLI::Option> validTestOptions;
validTestOptions.push_back(CLI::Option('c', "ccccc", "VALUE", "short argument concatenated with its value"));
validTestOptions.push_back(CLI::Option('d', "ddddd", " VALUE", "short argument with separate value"));
validTestOptions.push_back(CLI::Option('f', "fffff", "VALUE", "short argument concatenated with a flag and its value"));
validTestOptions.push_back(CLI::Option('h', "hhhhh", " VALUE", "short argument concatenated with a flag with separate value"));
validTestOptions.push_back(CLI::Option('x', "long-argument-with-value-included", "VALUE", "long argument with its value included using ="));
validTestOptions.push_back(CLI::Option('y', "long-argument-with-value-separated", " VALUE", "long argument with separate value"));
validTestOptions.push_back(CLI::Option('z', "zzzzz", "NOPE", "unused argument"));
std::vector<CLI::PositionalArgument> validTestPositionalArguments;
validTestPositionalArguments.push_back(CLI::PositionalArgument("argument0", "positional argument between optional parameters"));
validTestPositionalArguments.push_back(CLI::PositionalArgument("argument1", "positional argument"));
validTestPositionalArguments.push_back(CLI::PositionalArgument("argument2", "positional argument"));
std::vector<CLI::Argument> validTestArguments;
validTestArguments.push_back(CLI::Argument("argument0", "argument between optional parameters"));
validTestArguments.push_back(CLI::Argument("argument1", "argument"));
validTestArguments.push_back(CLI::Argument("argument2", "argument"));
const char** validTestParameterList = new const char*[17];
validTestParameterList[ 0] = "test";
@ -71,17 +71,17 @@ int main(int argc, char* argv[]) {
validTestParameterList[ 6] = "-d";
validTestParameterList[ 7] = "separate-value";
validTestParameterList[ 8] = "-efdouble-concatenated";
validTestParameterList[ 9] = "positional argument 0";
validTestParameterList[ 9] = "argument 0";
validTestParameterList[10] = "-gh";
validTestParameterList[11] = "concatenated-separate-value";
validTestParameterList[12] = "--long-argument-with-value-included=included value";
validTestParameterList[13] = "--long-argument-with-value-separated";
validTestParameterList[14] = "separate value";
validTestParameterList[15] = "positional argument 1";
validTestParameterList[16] = "positional argument 2";
validTestParameterList[15] = "argument 1";
validTestParameterList[16] = "argument 2";
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.programName == std::string("test"));
@ -115,49 +115,50 @@ int main(int argc, char* argv[]) {
ASSERT(!validTestParameterParser.getFlag('6').isError);
ASSERT(!validTestParameterParser.getFlag('6').value);
ASSERT(!validTestParameterParser.getFlag(std::string("66666")).value);
ASSERT(!validTestParameterParser.getUnpositionalArgument('c').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('c').value==std::string("concatenated"));
ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("ccccc")).value==std::string("concatenated"));
ASSERT(!validTestParameterParser.getUnpositionalArgument('d').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('d').value==std::string("separate-value"));
ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("ddddd")).value==std::string("separate-value"));
ASSERT(!validTestParameterParser.getOption('c').isError);
ASSERT(validTestParameterParser.getOption('c').value==std::string("concatenated"));
ASSERT(validTestParameterParser.getOption(std::string("ccccc")).value==std::string("concatenated"));
ASSERT(!validTestParameterParser.getOption('d').isError);
ASSERT(validTestParameterParser.getOption('d').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').value);
ASSERT(validTestParameterParser.getFlag(std::string("eeeee")).value);
ASSERT(!validTestParameterParser.getUnpositionalArgument('f').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('f').value==std::string("double-concatenated"));
ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("fffff")).value==std::string("double-concatenated"));
ASSERT(!validTestParameterParser.getPositionalArgument(0).isError);
ASSERT(validTestParameterParser.getPositionalArgument(0).value=="positional argument 0");
ASSERT(!validTestParameterParser.getOption('f').isError);
ASSERT(validTestParameterParser.getOption('f').value==std::string("double-concatenated"));
ASSERT(validTestParameterParser.getOption(std::string("fffff")).value==std::string("double-concatenated"));
ASSERT(!validTestParameterParser.getArgument(0).isError);
ASSERT(validTestParameterParser.getArgument(0).value=="argument 0");
ASSERT(!validTestParameterParser.getFlag('g').isError);
ASSERT(validTestParameterParser.getFlag('g').value);
ASSERT(validTestParameterParser.getFlag(std::string("ggggg")).value);
ASSERT(!validTestParameterParser.getUnpositionalArgument('h').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('h').value==std::string("concatenated-separate-value"));
ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("hhhhh")).value==std::string("concatenated-separate-value"));
ASSERT(!validTestParameterParser.getUnpositionalArgument('x').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('x').value==std::string("included value"));
ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("long-argument-with-value-included")).value==std::string("included value"));
ASSERT(!validTestParameterParser.getUnpositionalArgument('y').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('y').value==std::string("separate value"));
ASSERT(validTestParameterParser.getUnpositionalArgument(std::string("long-argument-with-value-separated")).value==std::string("separate value"));
ASSERT(!validTestParameterParser.getPositionalArgument(1).isError);
ASSERT(validTestParameterParser.getPositionalArgument(1).value=="positional argument 1");
ASSERT(!validTestParameterParser.getPositionalArgument(2).isError);
ASSERT(validTestParameterParser.getPositionalArgument(2).value=="positional argument 2");
ASSERT(!validTestParameterParser.getUnpositionalArgument('z').isError);
ASSERT(validTestParameterParser.getUnpositionalArgument('z').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(validTestParameterParser.getUnpositionalArgument('z').value == std::string(""));
ASSERT(!validTestParameterParser.getOption('h').isError);
ASSERT(validTestParameterParser.getOption('h').value==std::string("concatenated-separate-value"));
ASSERT(validTestParameterParser.getOption(std::string("hhhhh")).value==std::string("concatenated-separate-value"));
ASSERT(!validTestParameterParser.getOption('x').isError);
ASSERT(validTestParameterParser.getOption('x').value==std::string("included value"));
ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-included")).value==std::string("included value"));
ASSERT(!validTestParameterParser.getOption('y').isError);
ASSERT(validTestParameterParser.getOption('y').value==std::string("separate value"));
ASSERT(validTestParameterParser.getOption(std::string("long-argument-with-value-separated")).value==std::string("separate value"));
ASSERT(!validTestParameterParser.getArgument(1).isError);
ASSERT(validTestParameterParser.getArgument(1).value=="argument 1");
ASSERT(!validTestParameterParser.getArgument(2).isError);
ASSERT(validTestParameterParser.getArgument(2).value=="argument 2");
ASSERT(!validTestParameterParser.getOption('z').isError);
ASSERT(validTestParameterParser.getOption('z').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(validTestParameterParser.getOption('z').value == std::string(""));
ASSERT(validTestParameterParser.wrongUsageMessages.size() == 0);
delete[] validTestParameterList;
std::cout << "Passed valid input test." << std::endl;
std::cout << "Passed valid command line test." << std::endl;
// empty input (valid and invalid) #################################
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];
// 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;
// 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.programName == std::string("test"));
ASSERT(validEmptyTestParameterParser.wrongUsageMessages.size() == 0);
//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.programName == std::string("test"));
ASSERT(invalidEmptyTestParameterParser.getPositionalArgument(0).isError);
ASSERT(invalidEmptyTestParameterParser.getPositionalArgument(0).errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(invalidEmptyTestParameterParser.getArgument(0).isError);
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;
std::cout << "Passed empty input test." << std::endl;
std::cout << "Passed empty command line test." << std::endl;
//TODO add tests for invalid input
// test cases for invalid input:
// - unknown flags, arguments (mixed with other input and standalone)
// - too few, too many positional arguments (mixed with other input and standalone)
// - check the produced error messages
// unknown flag ####################################################
std::vector<CLI::Flag> unknownFlagTestFlags;
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('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[1] = "-a";
unknownFlagTestParameterList[2] = "-1";
@ -211,20 +211,554 @@ int main(int argc, char* argv[]) {
unknownFlagTestParameterList[6] = "-5";
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').errorCode == ErrorCodes::NOT_PRESENT);
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;
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').errorCode == ErrorCodes::WRONG_USAGE);
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;
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;
// 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;
}

183
src/test/file.cpp Normal file
View File

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

View File

@ -13,12 +13,12 @@
// version 3 along with this program.
// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include "assert.h++"
#include "../lib/javacompat.h++"
#include <iostream>
#include <tinyutf8/tinyutf8.h>
#include <fstream>
#include <vector>
#include "assert.hpp"
#include "../lib/javacompat.hpp"
int main(){
@ -28,7 +28,7 @@ int main(){
// java-style_unicode: 119 bytes, 85 characters ??
std::cout << "################################################################################" << std::endl;
std::cout << "Java String Tests" << std::endl;
std::cout << "Javacompat tests" << std::endl;
std::cout << "################################################################################" << std::endl;
char* nextChar = new char;
@ -52,7 +52,7 @@ int main(){
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::string normalStdString;
@ -75,9 +75,30 @@ int main(){
tiny_utf8::string normalString = tiny_utf8::string(normalStdString);
// check for normal operation
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;
//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;
}

View File

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

View File

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

View File

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

1523
src/test/nbt_tags.cpp Normal file

File diff suppressed because it is too large Load Diff

116
src/tools/arraydump.cpp Normal file
View File

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

130
src/tools/baseconvert.cpp Normal file
View File

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

View File

@ -13,8 +13,360 @@
// version 3 along with this program.
// 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[]) {
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;
}

View File

@ -1,5 +1,8 @@
//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.
@ -13,68 +16,479 @@
//version 3 along with this program.
//If not, see https://www.gnu.org/licenses/agpl-3.0.en.html
#include <chrono>
#include <csignal>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <cstdint>
#include <cctype>
#include <sockpp/tcp_acceptor.h>
#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.h++"
#include "../lib/cli.hpp"
#include "../lib/error.hpp"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
#define EXIT_USAGE 2
#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 ipv4 = true;
bool ipv6 = true;
bool tcp = true;
bool udp = true;
bool listen = false;
std::string host;
uint16_t port;
bool exitProgram = false;
bool ipv4 = false;
bool ipv6 = false;
bool tcp = false;
bool udp = false;
bool outgoing = false;
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;
flags.push_back(CLI::Flag('4', "ipv4", "use IPv4, defaults to both when -4 and -6 are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('6', "ipv6", "use IPv6, defaults to both when -4 and -6 are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('t', "tcp", "use TCP, defaults to both when -t and -u are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('u', "udp", "use UDP, defaults to both when -t and -u 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, either this or IPv4 has to be 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, 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;
unpositionalArguments.push_back(CLI::UnpositionalArgument('c', "connect", "HOST", "connect to HOST, listen for incoming connections if omitted"));
std::vector<CLI::Option> options;
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;
positionalArguments.push_back(CLI::PositionalArgument("PORT", "the port to use"));
std::vector<CLI::Argument> arguments;
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) {
//TODO: spit out usage information generated by the parser
std::cout << cliParser.getUsage() << std::endl;
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;
std::cerr << (listen? "Listening" : "Host: ") << host << std::endl;
std::cerr << "IPv4: " << (ipv4? "yes" : "no") << std::endl;
std::cerr << "IPv6: " << (ipv6? "yes" : "no") << std::endl;
std::cerr << "TCP: " << ( tcp? "yes" : "no") << std::endl;
std::cerr << "UDP: " << ( udp? "yes" : "no") << std::endl;
return EXIT_UNIMPLEMENTED;
ipv4 = cliParser.getFlag("ipv4").value;
ipv6 = cliParser.getFlag("ipv6").value;
tcp = cliParser.getFlag("tcp").value;
udp = cliParser.getFlag("udp").value;
if (cliParser.getOption("connect").errorCode != ErrorCodes::NOT_PRESENT) {
outgoing = true;
host = cliParser.getOption("connect").value;
}
if (!(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;
}
}
}
}