2022-06-27 11:46:13 +02:00
// 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
2022-06-24 12:15:34 +02:00
# include <bit>
2022-06-27 04:50:32 +02:00
# include <cstdint>
# include <vector>
2022-07-20 08:38:04 +02:00
# include <tinyutf8/tinyutf8.h>
2022-06-24 12:15:34 +02:00
2022-08-02 03:35:08 +02:00
# include "nbt.hpp"
# include "error.hpp"
# include "javacompat.hpp"
2022-07-20 08:38:04 +02:00
2022-06-27 04:50:32 +02:00
2022-07-01 21:15:18 +02:00
# include "../../.endianness"
# 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
2022-06-24 12:15:34 +02:00
2022-06-27 04:50:32 +02:00
namespace NBT {
2022-09-15 02:00:07 +02:00
namespace Helper {
2022-06-28 16:51:52 +02:00
ErrorOr < int8_t > readInt8 ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
if ( currentPosition > = dataSize ) return ErrorOr < int8_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
2022-06-28 16:58:31 +02:00
return ErrorOr < int8_t > ( ( int8_t ) data [ currentPosition ] ) ;
2022-06-27 04:50:32 +02:00
}
2022-06-28 16:51:52 +02:00
ErrorOr < int16_t > readInt16 ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
if ( currentPosition > = dataSize ) return ErrorOr < int16_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
if ( dataSize < currentPosition + 2 ) return ErrorOr < int16_t > ( true , ErrorCodes : : OVERRUN ) ;
2022-06-28 22:04:04 +02:00
return ErrorOr < int16_t > ( ( int16_t ) ( ( static_cast < int16_t > ( data [ currentPosition ] ) < < 8 ) | static_cast < int16_t > ( data [ currentPosition + 1 ] ) ) ) ;
2022-06-27 04:50:32 +02:00
}
2022-06-28 16:51:52 +02:00
ErrorOr < int32_t > readInt32 ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
if ( currentPosition > = dataSize ) return ErrorOr < int32_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
if ( dataSize < currentPosition + 4 ) return ErrorOr < int32_t > ( true , ErrorCodes : : OVERRUN ) ;
2022-06-29 00:05:02 +02:00
return ErrorOr < int32_t > ( ( int32_t ) (
( static_cast < int32_t > ( data [ currentPosition ] ) < < 24 ) |
( static_cast < int32_t > ( data [ currentPosition + 1 ] ) < < 16 ) |
( static_cast < int32_t > ( data [ currentPosition + 2 ] ) < < 8 ) |
static_cast < int32_t > ( data [ currentPosition + 3 ] )
) ) ;
2022-06-27 04:50:32 +02:00
}
2022-06-28 16:51:52 +02:00
ErrorOr < int64_t > readInt64 ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
if ( currentPosition > = dataSize ) return ErrorOr < int64_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
if ( dataSize < currentPosition + 8 ) return ErrorOr < int64_t > ( true , ErrorCodes : : OVERRUN ) ;
2022-06-29 00:05:02 +02:00
return ErrorOr < int64_t > ( ( int64_t ) (
( static_cast < int64_t > ( data [ currentPosition ] ) < < 56 ) |
( static_cast < int64_t > ( data [ currentPosition + 1 ] ) < < 48 ) |
( static_cast < int64_t > ( data [ currentPosition + 2 ] ) < < 40 ) |
( static_cast < int64_t > ( data [ currentPosition + 3 ] ) < < 32 ) |
( static_cast < int64_t > ( data [ currentPosition + 4 ] ) < < 24 ) |
( static_cast < int64_t > ( data [ currentPosition + 5 ] ) < < 16 ) |
( static_cast < int64_t > ( data [ currentPosition + 6 ] ) < < 8 ) |
static_cast < int64_t > ( data [ currentPosition + 7 ] )
) ) ;
2022-06-27 04:50:32 +02:00
}
2022-08-04 07:42:40 +02:00
//FIXME: endian-dependent implementations
ErrorOr < float > readFloat ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-07-01 21:15:18 +02:00
float * value = new float ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
2022-07-13 21:00:42 +02:00
if ( dataSize < = currentPosition ) return ErrorOr < float > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
if ( dataSize < currentPosition + 4 ) return ErrorOr < float > ( true , ErrorCodes : : OVERRUN ) ;
2022-07-01 21:15:18 +02:00
# ifdef FOSSVG_BIG_ENDIAN
* valueAsBytes = data [ currentPosition ] ;
* ( valueAsBytes + 1 ) = data [ currentPosition + 1 ] ;
* ( valueAsBytes + 2 ) = data [ currentPosition + 2 ] ;
* ( valueAsBytes + 3 ) = data [ currentPosition + 3 ] ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
* valueAsBytes = data [ currentPosition + 3 ] ;
* ( valueAsBytes + 1 ) = data [ currentPosition + 2 ] ;
* ( valueAsBytes + 2 ) = data [ currentPosition + 1 ] ;
* ( valueAsBytes + 3 ) = data [ currentPosition ] ;
2022-07-02 16:41:54 +02:00
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::readFloat: An implementation for your endianness is unavailable."
2022-07-01 21:15:18 +02:00
# endif
# endif
float dereferencedValue = * value ;
delete value ;
return ErrorOr < float > ( dereferencedValue ) ;
2022-06-27 04:50:32 +02:00
}
2022-08-04 07:42:40 +02:00
//FIXME: endian-dependent implementations
ErrorOr < double > readDouble ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-07-01 21:15:18 +02:00
double * value = new double ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
2022-07-13 21:00:42 +02:00
if ( dataSize < = currentPosition ) return ErrorOr < double > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
if ( dataSize < currentPosition + 8 ) return ErrorOr < double > ( true , ErrorCodes : : OVERRUN ) ;
2022-07-01 21:15:18 +02:00
# ifdef FOSSVG_BIG_ENDIAN
* valueAsBytes = data [ currentPosition ] ;
* ( valueAsBytes + 1 ) = data [ currentPosition + 1 ] ;
* ( valueAsBytes + 2 ) = data [ currentPosition + 2 ] ;
* ( valueAsBytes + 3 ) = data [ currentPosition + 3 ] ;
* ( valueAsBytes + 4 ) = data [ currentPosition + 4 ] ;
* ( valueAsBytes + 5 ) = data [ currentPosition + 5 ] ;
* ( valueAsBytes + 6 ) = data [ currentPosition + 6 ] ;
* ( valueAsBytes + 7 ) = data [ currentPosition + 7 ] ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
* valueAsBytes = data [ currentPosition + 7 ] ;
* ( valueAsBytes + 1 ) = data [ currentPosition + 6 ] ;
* ( valueAsBytes + 2 ) = data [ currentPosition + 5 ] ;
* ( valueAsBytes + 3 ) = data [ currentPosition + 4 ] ;
* ( valueAsBytes + 4 ) = data [ currentPosition + 3 ] ;
* ( valueAsBytes + 5 ) = data [ currentPosition + 2 ] ;
* ( valueAsBytes + 6 ) = data [ currentPosition + 1 ] ;
* ( valueAsBytes + 7 ) = data [ currentPosition ] ;
2022-07-02 16:41:54 +02:00
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::readDouble: An implementation for your endianness is unavailable."
2022-07-01 21:15:18 +02:00
# endif
# endif
double dereferencedValue = * value ;
delete value ;
return ErrorOr < double > ( dereferencedValue ) ;
2022-06-27 04:50:32 +02:00
}
2022-06-28 16:51:52 +02:00
ErrorOr < std : : vector < int8_t > > readInt8Array ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
2022-06-29 12:22:54 +02:00
ErrorOr < int32_t > size = readInt32 ( data , dataSize , currentPosition ) ;
2022-08-27 22:35:10 +02:00
if ( size . isError ) {
// It is okay to pass up the error code here without further
// processing because both OVERRUN and OUT_OF_RANGE errors will
// still apply.
return ErrorOr < std : : vector < int8_t > > ( true , size . errorCode ) ;
}
2022-06-29 12:22:54 +02:00
// get content
2022-07-13 21:00:42 +02:00
if ( currentPosition + 4 + size . value > dataSize ) return ErrorOr < std : : vector < int8_t > > ( true , ErrorCodes : : OVERRUN ) ;
2022-06-29 12:22:54 +02:00
std : : vector < int8_t > result = std : : vector < int8_t > ( ) ;
for ( int i = 0 ; i < size . value ; i + + ) {
result . push_back ( data [ currentPosition + 4 + i ] ) ;
}
return ErrorOr < std : : vector < int8_t > > ( result ) ;
2022-06-27 04:50:32 +02:00
}
2022-07-20 08:38:04 +02:00
ErrorOr < tiny_utf8 : : string > readString ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-02 03:35:08 +02:00
2022-08-15 09:51:46 +02:00
ErrorOr < int16_t > stringSize = readInt16 ( data , dataSize , currentPosition ) ;
if ( stringSize . isError ) {
2022-08-27 22:35:10 +02:00
// It is okay to pass up the error code here without further
// processing because both OVERRUN and OUT_OF_RANGE errors will
// still apply.
2022-08-15 09:51:46 +02:00
return ErrorOr < tiny_utf8 : : string > ( true , stringSize . errorCode ) ;
}
2022-08-15 12:02:58 +02:00
if ( currentPosition + ( uint64_t ) stringSize . value + 2 > dataSize ) {
return ErrorOr < tiny_utf8 : : string > ( true , ErrorCodes : : OVERRUN ) ;
}
2022-08-15 09:51:46 +02:00
ErrorOr < tiny_utf8 : : string > output = JavaCompat : : importJavaString ( data + currentPosition , stringSize . value ) ;
2022-07-20 08:38:04 +02:00
if ( output . isError ) {
return ErrorOr < tiny_utf8 : : string > ( true , output . errorCode ) ;
}
return output ;
}
2022-06-27 04:50:32 +02:00
2022-06-28 16:51:52 +02:00
ErrorOr < std : : vector < int32_t > > readInt32Array ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
2022-06-30 10:45:12 +02:00
ErrorOr < int32_t > size = readInt32 ( data , dataSize , currentPosition ) ;
2022-08-27 22:35:10 +02:00
if ( size . isError ) {
// It is okay to pass up the error code here without further
// processing because both OVERRUN and OUT_OF_RANGE errors will
// still apply.
return ErrorOr < std : : vector < int32_t > > ( true , size . errorCode ) ;
}
2022-06-30 10:45:12 +02:00
// get content
2022-07-13 21:00:42 +02:00
if ( currentPosition + 4 + ( size . value * 4 ) > dataSize ) return ErrorOr < std : : vector < int32_t > > ( true , ErrorCodes : : OVERRUN ) ;
2022-06-30 10:45:12 +02:00
std : : vector < int32_t > result = std : : vector < int32_t > ( ) ;
for ( int i = 0 ; i < size . value ; i + + ) {
ErrorOr < int32_t > nextInt32 = readInt32 ( data , dataSize , currentPosition + 4 + ( i * 4 ) ) ;
if ( nextInt32 . isError ) return ErrorOr < std : : vector < int32_t > > ( true , nextInt32 . errorCode ) ;
result . push_back ( nextInt32 . value ) ;
}
return ErrorOr < std : : vector < int32_t > > ( result ) ;
2022-06-27 04:50:32 +02:00
}
2022-06-28 16:51:52 +02:00
ErrorOr < std : : vector < int64_t > > readInt64Array ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-27 22:35:10 +02:00
2022-06-30 11:02:30 +02:00
ErrorOr < int32_t > size = readInt32 ( data , dataSize , currentPosition ) ;
2022-08-27 22:35:10 +02:00
if ( size . isError ) {
// It is okay to pass up the error code here without further
// processing because both OVERRUN and OUT_OF_RANGE errors will
// still apply.
return ErrorOr < std : : vector < int64_t > > ( true , size . errorCode ) ;
}
2022-06-30 11:02:30 +02:00
// get content
2022-07-13 21:00:42 +02:00
if ( currentPosition + 4 + ( size . value * 8 ) > dataSize ) return ErrorOr < std : : vector < int64_t > > ( true , ErrorCodes : : OVERRUN ) ;
2022-06-30 11:02:30 +02:00
std : : vector < int64_t > result = std : : vector < int64_t > ( ) ;
for ( int i = 0 ; i < size . value ; i + + ) {
ErrorOr < int64_t > nextInt64 = readInt64 ( data , dataSize , currentPosition + 4 + ( i * 8 ) ) ;
if ( nextInt64 . isError ) return ErrorOr < std : : vector < int64_t > > ( true , nextInt64 . errorCode ) ;
result . push_back ( nextInt64 . value ) ;
}
return ErrorOr < std : : vector < int64_t > > ( result ) ;
2022-06-27 04:50:32 +02:00
}
2022-07-02 02:08:32 +02:00
void writeInt8 ( std : : vector < uint8_t > * destination , int8_t data ) {
2022-07-04 18:03:40 +02:00
destination - > push_back ( ( uint8_t ) data ) ;
2022-07-02 02:08:32 +02:00
}
2022-07-04 18:03:40 +02:00
//FIXME: endian dependent implementation
2022-07-02 02:08:32 +02:00
void writeInt16 ( std : : vector < uint8_t > * destination , int16_t data ) {
2022-07-04 18:03:40 +02:00
int16_t * value = new int16_t ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
* value = data ;
# ifdef FOSSVG_BIG_ENDIAN
destination - > push_back ( * valueAsBytes ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * valueAsBytes ) ;
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
2022-07-04 18:03:40 +02:00
# endif
# endif
delete value ;
2022-07-02 02:08:32 +02:00
}
2022-07-04 18:27:51 +02:00
//FIXME: endian dependent implementation
2022-07-02 02:08:32 +02:00
void writeInt32 ( std : : vector < uint8_t > * destination , int32_t data ) {
2022-07-04 18:27:51 +02:00
int32_t * value = new int32_t ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
* value = data ;
# ifdef FOSSVG_BIG_ENDIAN
destination - > push_back ( * valueAsBytes ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * valueAsBytes ) ;
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
2022-07-04 18:27:51 +02:00
# endif
# endif
delete value ;
2022-07-02 02:08:32 +02:00
}
2022-07-04 18:27:51 +02:00
//FIXME: endian dependent implementation
2022-07-02 02:08:32 +02:00
void writeInt64 ( std : : vector < uint8_t > * destination , int64_t data ) {
2022-07-04 18:27:51 +02:00
int64_t * value = new int64_t ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
* value = data ;
# ifdef FOSSVG_BIG_ENDIAN
destination - > push_back ( * valueAsBytes ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
destination - > push_back ( * ( valueAsBytes + 4 ) ) ;
destination - > push_back ( * ( valueAsBytes + 5 ) ) ;
destination - > push_back ( * ( valueAsBytes + 6 ) ) ;
destination - > push_back ( * ( valueAsBytes + 7 ) ) ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
destination - > push_back ( * ( valueAsBytes + 7 ) ) ;
destination - > push_back ( * ( valueAsBytes + 6 ) ) ;
destination - > push_back ( * ( valueAsBytes + 5 ) ) ;
destination - > push_back ( * ( valueAsBytes + 4 ) ) ;
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * valueAsBytes ) ;
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
2022-07-04 18:27:51 +02:00
# endif
# endif
delete value ;
2022-07-02 02:08:32 +02:00
}
2022-08-04 07:42:40 +02:00
//FIXME: endian-specific implementations
void writeFloat ( std : : vector < uint8_t > * destination , float data ) {
2022-07-04 19:43:31 +02:00
float * value = new float ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
* value = data ;
# ifdef FOSSVG_BIG_ENDIAN
destination - > push_back ( * valueAsBytes ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * valueAsBytes ) ;
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
2022-07-04 19:43:31 +02:00
# endif
# endif
delete value ;
2022-07-02 02:08:32 +02:00
}
2022-08-04 07:42:40 +02:00
//FIXME: endian-specific implementations
void writeDouble ( std : : vector < uint8_t > * destination , double data ) {
2022-07-04 19:43:31 +02:00
double * value = new double ;
uint8_t * valueAsBytes = reinterpret_cast < uint8_t * > ( value ) ;
* value = data ;
# ifdef FOSSVG_BIG_ENDIAN
destination - > push_back ( * valueAsBytes ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
destination - > push_back ( * ( valueAsBytes + 4 ) ) ;
destination - > push_back ( * ( valueAsBytes + 5 ) ) ;
destination - > push_back ( * ( valueAsBytes + 6 ) ) ;
destination - > push_back ( * ( valueAsBytes + 7 ) ) ;
# else
# ifdef FOSSVG_LITTLE_ENDIAN
destination - > push_back ( * ( valueAsBytes + 7 ) ) ;
destination - > push_back ( * ( valueAsBytes + 6 ) ) ;
destination - > push_back ( * ( valueAsBytes + 5 ) ) ;
destination - > push_back ( * ( valueAsBytes + 4 ) ) ;
destination - > push_back ( * ( valueAsBytes + 3 ) ) ;
destination - > push_back ( * ( valueAsBytes + 2 ) ) ;
destination - > push_back ( * ( valueAsBytes + 1 ) ) ;
destination - > push_back ( * valueAsBytes ) ;
# else
2022-09-15 02:00:07 +02:00
# error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
2022-07-04 19:43:31 +02:00
# endif
# endif
delete value ;
2022-07-02 02:08:32 +02:00
}
void writeInt8Array ( std : : vector < uint8_t > * destination , std : : vector < int8_t > data ) {
2022-07-06 13:46:47 +02:00
writeInt32 ( destination , data . size ( ) ) ;
for ( int8_t datum : data ) {
destination - > push_back ( datum ) ;
}
2022-07-02 02:08:32 +02:00
}
2022-07-06 12:57:32 +02:00
void writeInt8Array ( std : : vector < uint8_t > * destination , int8_t data [ ] , uint32_t dataSize ) {
writeInt32 ( destination , dataSize ) ;
for ( uint32_t i = 0 ; i < dataSize ; i + + ) {
destination - > push_back ( data [ i ] ) ;
}
2022-07-02 02:08:32 +02:00
}
2022-09-26 03:11:44 +02:00
ErrorOrVoid writeString ( std : : vector < uint8_t > * destination , tiny_utf8 : : string data ) {
2022-07-28 13:45:04 +02:00
ErrorOr < std : : vector < uint8_t > > exportedString = JavaCompat : : exportJavaString ( data ) ;
if ( exportedString . isError ) {
2022-09-26 03:11:44 +02:00
return ErrorOrVoid ( true , ErrorCodes : : OVERRUN ) ;
2022-07-28 13:45:04 +02:00
}
2022-10-06 10:21:59 +02:00
destination - > insert ( destination - > end ( ) , exportedString . value . begin ( ) , exportedString . value . end ( ) ) ;
2022-09-26 03:11:44 +02:00
return ErrorOrVoid ( ) ;
2022-07-28 13:45:04 +02:00
}
2022-07-02 02:08:32 +02:00
void writeInt32Array ( std : : vector < uint8_t > * destination , std : : vector < int32_t > data ) {
2022-07-06 14:58:02 +02:00
writeInt32 ( destination , data . size ( ) ) ;
for ( int32_t element : data ) {
writeInt32 ( destination , element ) ;
}
2022-07-02 02:08:32 +02:00
}
2022-07-06 14:58:02 +02:00
void writeInt32Array ( std : : vector < uint8_t > * destination , int32_t data [ ] , uint32_t dataSize ) {
writeInt32 ( destination , dataSize ) ;
for ( uint32_t i = 0 ; i < dataSize ; i + + ) {
writeInt32 ( destination , data [ i ] ) ;
}
2022-07-02 02:08:32 +02:00
}
void writeInt64Array ( std : : vector < uint8_t > * destination , std : : vector < int64_t > data ) {
2022-07-06 14:58:02 +02:00
writeInt32 ( destination , data . size ( ) ) ;
for ( int64_t element : data ) {
writeInt64 ( destination , element ) ;
}
2022-07-02 02:08:32 +02:00
}
2022-07-06 14:58:02 +02:00
void writeInt64Array ( std : : vector < uint8_t > * destination , int64_t data [ ] , uint32_t dataSize ) {
writeInt32 ( destination , dataSize ) ;
for ( uint32_t i = 0 ; i < dataSize ; i + + ) {
writeInt64 ( destination , data [ i ] ) ;
}
2022-07-02 02:08:32 +02:00
}
2022-08-11 07:43:54 +02:00
//FIXME: instead of blindly passing the error code upwards, choose
// one that is applicable to the situation (for example replace
// OUT_OF_RANGE with OVERRUN where appropriate)
//
2022-08-13 15:56:59 +02:00
// The total size in bytes
//
2022-08-11 07:43:54 +02:00
// Does not work for compound tags and lists. This is an intended
// feature as compound tags and lists need to be dealt with
2022-08-13 15:42:52 +02:00
// separately to avoid unnecessarily long and complex code.
2022-08-11 07:43:54 +02:00
//
2022-08-27 22:35:10 +02:00
// Regarding lists specifically: The size of some lists can can
2022-08-11 07:43:54 +02:00
// be determined easily by looking at the contained data type and
// size information but cases like string lists or compound lists
// are significantly more difficult to deal with. Parsing their
2022-08-15 05:15:17 +02:00
// contents requires special attention anyway due to the tag headers
// of contained tags being absent so they may as well get treated
// separately for this as well.
2022-08-13 15:56:59 +02:00
ErrorOr < uint64_t > totalTagSize ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-11 07:43:54 +02:00
uint8_t nextTag ;
if ( dataSize < = currentPosition ) {
return ErrorOr < uint64_t > ( true , ErrorCodes : : OVERRUN ) ;
} else {
nextTag = data [ currentPosition ] ;
}
2022-08-13 16:49:01 +02:00
// deal with compound tags and lists separately
if ( nextTag = = TagType : : COMPOUND | | nextTag = = TagType : : LIST ) return ErrorOr < uint64_t > ( true , ErrorCodes : : NOT_YET_KNOWN ) ;
2022-08-11 07:43:54 +02:00
// deal with end tag before trying to access the name
if ( nextTag = = TagType : : END ) return ErrorOr < uint64_t > ( 1 ) ;
2022-08-13 15:42:52 +02:00
2022-09-15 02:00:07 +02:00
ErrorOr < int16_t > nameSize = Helper : : readInt16 ( data , dataSize , currentPosition + 1 ) ;
2022-08-11 07:43:54 +02:00
if ( nameSize . isError ) {
return ErrorOr < uint64_t > ( true , nameSize . errorCode ) ;
}
2022-08-13 15:42:52 +02:00
// add type byte and name size bytes
uint64_t prefixSize = ( uint64_t ) nameSize . value + 3 ;
2022-08-11 07:43:54 +02:00
switch ( nextTag ) {
case TagType : : INT8 :
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( prefixSize + 1 ) ;
2022-08-11 07:43:54 +02:00
case TagType : : INT16 :
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( prefixSize + 2 ) ;
2022-08-11 07:43:54 +02:00
case TagType : : INT32 :
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( prefixSize + 4 ) ;
2022-08-11 07:43:54 +02:00
case TagType : : INT64 :
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( prefixSize + 8 ) ;
2022-08-11 07:43:54 +02:00
case TagType : : FLOAT :
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( prefixSize + 4 ) ;
2022-08-11 07:43:54 +02:00
case TagType : : DOUBLE :
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( prefixSize + 8 ) ;
2022-08-11 07:43:54 +02:00
case TagType : : INT8_ARRAY : {
2022-09-15 02:00:07 +02:00
ErrorOr < int32_t > arrayLength = Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:42:52 +02:00
if ( arrayLength . isError ) {
return ErrorOr < uint64_t > ( true , arrayLength . errorCode ) ;
2022-08-11 07:43:54 +02:00
}
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( ( uint64_t ) arrayLength . value + prefixSize + 4 ) ;
2022-08-11 07:43:54 +02:00
}
case TagType : : STRING : {
2022-09-15 02:00:07 +02:00
ErrorOr < int16_t > stringSize = Helper : : readInt16 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-11 07:43:54 +02:00
if ( stringSize . isError ) {
return ErrorOr < uint64_t > ( true , stringSize . errorCode ) ;
}
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( ( uint64_t ) stringSize . value + prefixSize + 2 ) ;
2022-08-11 07:43:54 +02:00
}
case TagType : : INT32_ARRAY : {
2022-09-15 02:00:07 +02:00
ErrorOr < int32_t > arrayLength = Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:42:52 +02:00
if ( arrayLength . isError ) {
return ErrorOr < uint64_t > ( true , arrayLength . errorCode ) ;
2022-08-11 07:43:54 +02:00
}
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( ( uint64_t ) arrayLength . value * 4 + prefixSize + 4 ) ;
2022-08-11 07:43:54 +02:00
}
case TagType : : INT64_ARRAY : {
2022-09-15 02:00:07 +02:00
ErrorOr < int32_t > arrayLength = Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:42:52 +02:00
if ( arrayLength . isError ) {
return ErrorOr < uint64_t > ( true , arrayLength . errorCode ) ;
2022-08-11 07:43:54 +02:00
}
2022-08-13 15:42:52 +02:00
return ErrorOr < uint64_t > ( ( uint64_t ) arrayLength . value * 8 + prefixSize + 4 ) ;
2022-08-11 07:43:54 +02:00
}
// unknown tag or parsing error
default :
return ErrorOr < uint64_t > ( true , ErrorCodes : : UNKNOWN ) ;
}
}
2022-08-13 15:46:06 +02:00
//FIXME: instead of blindly passing the error code upwards, choose
// one that is applicable to the situation (for example replace
// OUT_OF_RANGE with OVERRUN where appropriate)
2022-08-13 15:56:59 +02:00
//
// Length is the number of stored elements, not to be confused with size
// which is the size in bytes.
ErrorOr < int32_t > containedDataLength ( uint8_t data [ ] , uint64_t dataSize , uint64_t currentPosition ) {
2022-08-11 07:43:54 +02:00
uint8_t nextTag ;
if ( dataSize < = currentPosition ) {
2022-08-13 15:46:06 +02:00
return ErrorOr < int32_t > ( true , ErrorCodes : : OVERRUN ) ;
2022-08-11 07:43:54 +02:00
} else {
nextTag = data [ currentPosition ] ;
}
// deal with compound tags separately
2022-08-13 15:46:06 +02:00
if ( nextTag = = TagType : : COMPOUND ) {
return ErrorOr < int32_t > ( true , ErrorCodes : : NOT_YET_KNOWN ) ;
}
2022-08-11 07:43:54 +02:00
// deal with end tag before trying to access the name
2022-08-13 15:46:06 +02:00
if ( nextTag = = TagType : : END ) {
return ErrorOr < int32_t > ( 0 ) ;
}
// tags that only ever hold one value
if ( nextTag = = TagType : : INT8 | | nextTag = = TagType : : INT16 | | nextTag = = TagType : : INT32 | | nextTag = = TagType : : INT64 | | nextTag = = TagType : : FLOAT | | nextTag = = TagType : : DOUBLE ) {
return ErrorOr < int32_t > ( 1 ) ;
}
2022-09-15 02:00:07 +02:00
ErrorOr < int16_t > nameSize = Helper : : readInt16 ( data , dataSize , currentPosition + 1 ) ;
2022-08-13 15:46:06 +02:00
if ( nameSize . isError ) {
return ErrorOr < int32_t > ( true , nameSize . errorCode ) ;
}
// add type byte and name size bytes
uint64_t prefixSize = ( uint64_t ) nameSize . value + 3 ;
switch ( nextTag ) {
case TagType : : INT8_ARRAY : {
2022-09-15 02:00:07 +02:00
return Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:46:06 +02:00
}
case TagType : : STRING : {
2022-09-15 02:00:07 +02:00
ErrorOr < int16_t > stringSize = Helper : : readInt16 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:46:06 +02:00
if ( stringSize . isError ) {
return ErrorOr < int32_t > ( true , stringSize . errorCode ) ;
}
return ErrorOr < int32_t > ( ( int32_t ) stringSize . value ) ;
}
case TagType : : LIST : {
2022-08-13 15:56:59 +02:00
// add an additional byte for the contained data type
2022-09-15 02:00:07 +02:00
return Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize + 1 ) ;
2022-08-13 15:46:06 +02:00
}
case TagType : : INT32_ARRAY : {
2022-09-15 02:00:07 +02:00
return Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:46:06 +02:00
}
case TagType : : INT64_ARRAY : {
2022-09-15 02:00:07 +02:00
return Helper : : readInt32 ( data , dataSize , currentPosition + prefixSize ) ;
2022-08-13 15:46:06 +02:00
}
default :
// unknown tag or parsing error
return ErrorOr < int32_t > ( true , ErrorCodes : : UNKNOWN ) ;
}
2022-08-11 07:43:54 +02:00
}
2022-06-27 04:50:32 +02:00
}
2022-09-26 03:11:44 +02:00
namespace Tag {
2022-08-03 20:31:12 +02:00
2022-09-26 03:11:44 +02:00
Generic : : Generic ( ) {
this - > type = TagType : : INVALID ;
}
Generic : : ~ Generic ( ) { }
2022-10-02 07:25:50 +02:00
ErrorOrVoid Generic : : serializeWithoutHeader ( [[maybe_unused]] std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
return ErrorOrVoid ( true , ErrorCodes : : INVALID_TYPE ) ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Generic : : serialize ( std : : vector < uint8_t > * rawData ) {
rawData - > push_back ( this - > type ) ;
if ( Helper : : writeString ( rawData , this - > name ) . isError ) {
return ErrorOrVoid ( true , ErrorCodes : : OVERRUN ) ;
}
return this - > serializeWithoutHeader ( rawData ) ;
}
2022-09-26 03:11:44 +02:00
uint8_t Generic : : getTagType ( ) {
return this - > type ;
}
End : : End ( ) {
this - > type = TagType : : END ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid End : : serializeWithoutHeader ( [[maybe_unused]] std : : vector < uint8_t > * rawData ) {
return ErrorOrVoid ( ) ;
}
ErrorOrVoid End : : serialize ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
rawData - > push_back ( this - > type ) ;
return ErrorOrVoid ( ) ;
}
Int8 : : Int8 ( ) {
this - > type = TagType : : INT8 ;
}
Int8 : : Int8 ( tiny_utf8 : : string name , int8_t value ) {
this - > type = TagType : : INT8 ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Int8 : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
Helper : : writeInt8 ( rawData , this - > value ) ;
return ErrorOrVoid ( ) ;
}
int8_t Int8 : : getValue ( ) {
this - > mutex . lock ( ) ;
int8_t value = this - > value ;
this - > mutex . unlock ( ) ;
return value ;
}
void Int8 : : setValue ( int8_t value ) {
this - > mutex . lock ( ) ;
this - > value = value ;
this - > mutex . unlock ( ) ;
}
Int16 : : Int16 ( ) {
this - > type = TagType : : INT16 ;
}
Int16 : : Int16 ( tiny_utf8 : : string name , int16_t value ) {
this - > type = TagType : : INT16 ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Int16 : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
Helper : : writeInt16 ( rawData , this - > value ) ;
return ErrorOrVoid ( ) ;
}
int16_t Int16 : : getValue ( ) {
this - > mutex . lock ( ) ;
int16_t value = this - > value ;
this - > mutex . unlock ( ) ;
return value ;
}
void Int16 : : setValue ( int16_t value ) {
this - > mutex . lock ( ) ;
this - > value = value ;
this - > mutex . unlock ( ) ;
}
Int32 : : Int32 ( ) {
this - > type = TagType : : INT32 ;
}
Int32 : : Int32 ( tiny_utf8 : : string name , int32_t value ) {
this - > type = TagType : : INT32 ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Int32 : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
Helper : : writeInt32 ( rawData , this - > value ) ;
return ErrorOrVoid ( ) ;
}
int32_t Int32 : : getValue ( ) {
this - > mutex . lock ( ) ;
int32_t value = this - > value ;
this - > mutex . unlock ( ) ;
return value ;
}
void Int32 : : setValue ( int32_t value ) {
this - > mutex . lock ( ) ;
this - > value = value ;
this - > mutex . unlock ( ) ;
}
Int64 : : Int64 ( ) {
this - > type = TagType : : INT64 ;
}
Int64 : : Int64 ( tiny_utf8 : : string name , int64_t value ) {
this - > type = TagType : : INT64 ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Int64 : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
Helper : : writeInt64 ( rawData , this - > value ) ;
return ErrorOrVoid ( ) ;
}
int64_t Int64 : : getValue ( ) {
this - > mutex . lock ( ) ;
int64_t value = this - > value ;
this - > mutex . unlock ( ) ;
return value ;
}
void Int64 : : setValue ( int64_t value ) {
this - > mutex . lock ( ) ;
this - > value = value ;
this - > mutex . unlock ( ) ;
}
Float : : Float ( ) {
this - > type = TagType : : FLOAT ;
}
Float : : Float ( tiny_utf8 : : string name , float value ) {
this - > type = TagType : : FLOAT ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Float : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
Helper : : writeFloat ( rawData , this - > value ) ;
return ErrorOrVoid ( ) ;
}
float Float : : getValue ( ) {
this - > mutex . lock ( ) ;
float value = this - > value ;
this - > mutex . unlock ( ) ;
return value ;
}
void Float : : setValue ( float value ) {
this - > mutex . lock ( ) ;
this - > value = value ;
this - > mutex . unlock ( ) ;
}
Double : : Double ( ) {
this - > type = TagType : : DOUBLE ;
}
Double : : Double ( tiny_utf8 : : string name , double value ) {
this - > type = TagType : : DOUBLE ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Double : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-26 03:11:44 +02:00
Helper : : writeDouble ( rawData , this - > value ) ;
return ErrorOrVoid ( ) ;
}
double Double : : getValue ( ) {
this - > mutex . lock ( ) ;
double value = this - > value ;
this - > mutex . unlock ( ) ;
return value ;
}
void Double : : setValue ( double value ) {
this - > mutex . lock ( ) ;
this - > value = value ;
this - > mutex . unlock ( ) ;
}
2022-09-28 03:43:54 +02:00
Int8Array : : Int8Array ( ) {
this - > type = TagType : : INT8_ARRAY ;
}
Int8Array : : Int8Array ( tiny_utf8 : : string name , std : : vector < int8_t > data ) {
this - > type = TagType : : INT8_ARRAY ;
this - > name = name ;
this - > data = data ;
}
Int8Array : : Int8Array ( tiny_utf8 : : string name , uint64_t length , int8_t data [ ] ) {
this - > type = TagType : : INT8_ARRAY ;
this - > name = name ;
this - > data = std : : vector < int8_t > ( data , data + length ) ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid Int8Array : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-09-28 03:43:54 +02:00
Helper : : writeInt8Array ( rawData , this - > data ) ;
return ErrorOrVoid ( ) ;
}
std : : vector < int8_t > Int8Array : : getData ( ) {
return this - > data ;
}
ErrorOr < int8_t > Int8Array : : getValue ( uint64_t position ) {
if ( this - > data . size ( ) < = position ) {
2022-10-08 12:54:05 +02:00
return ErrorOr < int8_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
2022-09-28 03:43:54 +02:00
}
2022-10-08 12:54:05 +02:00
return ErrorOr < int8_t > ( this - > data . at ( position ) ) ;
2022-09-28 03:43:54 +02:00
}
void Int8Array : : setData ( std : : vector < int8_t > newData ) {
this - > data = newData ;
}
ErrorOrVoid Int8Array : : setValue ( uint64_t position , int8_t value ) {
if ( this - > data . size ( ) < = position ) {
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
this - > data [ position ] = value ;
return ErrorOrVoid ( ) ;
}
uint64_t Int8Array : : length ( ) {
return this - > data . size ( ) ;
}
void Int8Array : : addElement ( int8_t element ) {
this - > data . push_back ( element ) ;
}
ErrorOrVoid Int8Array : : removeElement ( uint64_t position ) {
2022-10-05 01:12:38 +02:00
if ( position > = this - > data . size ( ) ) {
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
this - > data . erase ( this - > data . begin ( ) + position ) ;
return ErrorOrVoid ( ) ;
2022-10-01 04:51:53 +02:00
}
String : : String ( ) {
this - > type = TagType : : STRING ;
}
String : : String ( tiny_utf8 : : string name , tiny_utf8 : : string value ) {
this - > type = TagType : : STRING ;
this - > name = name ;
this - > value = value ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid String : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-10-01 04:51:53 +02:00
return Helper : : writeString ( rawData , this - > value ) ;
}
tiny_utf8 : : string String : : getValue ( ) {
return this - > value ;
}
void String : : setValue ( tiny_utf8 : : string value ) {
this - > value = value ;
}
List : : List ( ) {
this - > type = TagType : : LIST ;
this - > containedType = TagType : : INVALID ;
}
List : : List ( tiny_utf8 : : string name , uint8_t type ) {
this - > type = TagType : : LIST ;
this - > name = name ;
this - > containedType = type ;
}
// WARNING: The pointers inside the vector are automatically cleaned
// up upon deletion of the List object. Do not retain a copy of them
// elsewhere and especially do not delete them externally.
List : : List ( tiny_utf8 : : string name , std : : vector < Generic * > data ) {
this - > type = TagType : : LIST ;
this - > name = name ;
this - > tags = data ;
if ( data . size ( ) = = 0 ) {
this - > containedType = TagType : : END ;
} else {
this - > containedType = data . at ( 0 ) - > getTagType ( ) ;
}
}
2022-10-28 04:08:06 +02:00
ErrorOr < List * > List : : constructWithData ( tiny_utf8 : : string name , std : : vector < Generic * > data ) {
if ( data . size ( ) > 0xFFFFFFFF ) {
return ErrorOr < List * > ( true , ErrorCodes : : OVERRUN , nullptr ) ;
}
if ( data . size ( ) > 0 ) {
uint8_t dataType = data [ 0 ] - > getTagType ( ) ;
for ( uint32_t i = 1 ; i < data . size ( ) ; i + + ) {
if ( data [ i ] - > getTagType ( ) ! = dataType ) {
return ErrorOr < List * > ( true , ErrorCodes : : MIXED_TYPES , nullptr ) ;
}
}
}
return ErrorOr < List * > ( new List ( name , data ) ) ;
}
2022-10-01 04:51:53 +02:00
List : : ~ List ( ) {
for ( uint64_t i = 0 ; i < this - > tags . size ( ) ; i + + ) {
delete this - > tags . at ( i ) ;
}
}
2022-10-17 06:39:03 +02:00
uint8_t List : : getContainedType ( ) {
return this - > containedType ;
}
2022-10-02 07:25:50 +02:00
ErrorOrVoid List : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
2022-10-08 23:35:21 +02:00
if ( this - > containedType = = TagType : : INVALID ) {
return ErrorOrVoid ( true , ErrorCodes : : INVALID_TYPE ) ;
}
2022-10-02 07:25:50 +02:00
rawData - > push_back ( this - > containedType ) ;
// 32 bit signed integer max
if ( this - > tags . size ( ) > 0x7FFFFFFF ) {
return ErrorOrVoid ( true , ErrorCodes : : OVERRUN ) ;
}
Helper : : writeInt32 ( rawData , this - > tags . size ( ) ) ;
2022-10-04 02:39:58 +02:00
// unsigned integer bc of compiler warning (shouldn't matter)
for ( uint32_t i = 0 ; i < this - > tags . size ( ) ; i + + ) {
2022-10-02 07:25:50 +02:00
ErrorOrVoid result = this - > tags . at ( i ) - > serializeWithoutHeader ( rawData ) ;
if ( result . isError ) {
return result ;
}
}
return ErrorOrVoid ( ) ;
2022-10-01 04:51:53 +02:00
}
2022-10-01 04:57:45 +02:00
ErrorOr < Generic * > List : : getElementPointer ( uint64_t position ) {
2022-10-04 02:39:58 +02:00
if ( this - > tags . size ( ) < = position ) {
return ErrorOr < Generic * > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
return ErrorOr < Generic * > ( this - > tags . at ( position ) ) ;
2022-10-01 04:51:53 +02:00
}
2022-10-04 02:39:58 +02:00
ErrorOrVoid List : : setElementPointerAt ( uint64_t position , Generic * pointer ) {
if ( this - > tags . size ( ) < = position ) {
2022-10-09 10:18:20 +02:00
delete pointer ;
2022-10-04 02:39:58 +02:00
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
if ( pointer - > getTagType ( ) ! = this - > containedType ) {
2022-10-09 10:18:20 +02:00
delete pointer ;
2022-10-04 02:39:58 +02:00
return ErrorOrVoid ( true , ErrorCodes : : INVALID_TYPE ) ;
}
delete this - > tags [ position ] ;
this - > tags [ position ] = pointer ;
return ErrorOrVoid ( ) ;
2022-10-01 04:51:53 +02:00
}
2022-10-04 02:39:58 +02:00
ErrorOrVoid List : : appendPointer ( Generic * pointer ) {
if ( pointer - > getTagType ( ) ! = this - > containedType ) {
return ErrorOrVoid ( true , ErrorCodes : : INVALID_TYPE ) ;
}
this - > tags . push_back ( pointer ) ;
return ErrorOrVoid ( ) ;
2022-10-01 04:51:53 +02:00
}
2022-10-01 04:57:45 +02:00
ErrorOrVoid List : : deleteElement ( uint64_t position ) {
2022-10-05 01:12:38 +02:00
if ( position > = this - > tags . size ( ) ) {
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
2022-10-04 02:39:58 +02:00
2022-10-05 01:12:38 +02:00
delete this - > tags [ position ] ;
this - > tags . erase ( this - > tags . begin ( ) + position ) ;
2022-10-04 02:39:58 +02:00
2022-10-05 01:12:38 +02:00
return ErrorOrVoid ( ) ;
2022-10-01 04:51:53 +02:00
}
2022-10-01 04:57:45 +02:00
uint64_t List : : length ( ) {
2022-10-04 02:39:58 +02:00
return this - > tags . size ( ) ;
2022-09-28 03:43:54 +02:00
}
2022-10-05 01:12:38 +02:00
Compound : : Compound ( ) {
this - > type = TagType : : COMPOUND ;
2022-10-14 18:08:49 +02:00
this - > endPointer = new End ( ) ;
2022-10-05 01:12:38 +02:00
}
Compound : : Compound ( tiny_utf8 : : string name ) {
this - > type = TagType : : COMPOUND ;
this - > name = name ;
2022-10-14 18:08:49 +02:00
this - > endPointer = new End ( ) ;
2022-10-05 01:12:38 +02:00
}
Compound : : Compound ( tiny_utf8 : : string name , std : : vector < Generic * > data ) {
this - > type = TagType : : COMPOUND ;
this - > name = name ;
this - > tags = data ;
2022-10-14 18:08:49 +02:00
this - > endPointer = new End ( ) ;
2022-10-05 01:12:38 +02:00
}
2022-11-25 15:45:20 +01:00
ErrorOr < Compound * > Compound : : constructWithData ( tiny_utf8 : : string name , std : : vector < Generic * > data ) {
if ( data . size ( ) > 0 ) {
for ( uint64_t i = 0 ; i < data . size ( ) ; i + + ) {
if ( data [ i ] - > getTagType ( ) = = TagType : : END & & i ! = data . size ( ) - 1 ) {
return ErrorOr < Compound * > ( true , ErrorCodes : : NOT_ALLOWED , nullptr ) ;
}
}
if ( data [ data . size ( ) - 1 ] - > getTagType ( ) = = TagType : : END ) {
return ErrorOr < Compound * > ( new Compound ( name , std : : vector ( data . begin ( ) , data . end ( ) - 1 ) ) ) ;
}
}
return ErrorOr < Compound * > ( new Compound ( name , data ) ) ;
}
2022-10-05 01:12:38 +02:00
Compound : : ~ Compound ( ) {
for ( uint64_t i = 0 ; i < this - > tags . size ( ) ; i + + ) {
delete this - > tags . at ( i ) ;
}
2022-10-14 18:08:49 +02:00
delete this - > endPointer ;
2022-10-05 01:12:38 +02:00
}
ErrorOrVoid Compound : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
for ( uint64_t i = 0 ; i < this - > tags . size ( ) ; i + + ) {
ErrorOrVoid result = this - > tags . at ( i ) - > serialize ( rawData ) ;
if ( result . isError ) {
return result ;
}
}
2022-10-14 18:08:49 +02:00
this - > endPointer - > serialize ( rawData ) ;
2022-10-05 01:12:38 +02:00
return ErrorOrVoid ( ) ;
}
ErrorOr < Generic * > Compound : : getElementPointer ( uint64_t position ) {
2022-10-14 18:08:49 +02:00
if ( position > this - > tags . size ( ) ) {
2022-10-05 01:12:38 +02:00
return ErrorOr < Generic * > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
2022-10-14 18:08:49 +02:00
if ( position = = this - > tags . size ( ) ) {
return this - > endPointer ;
}
2022-10-05 01:12:38 +02:00
return ErrorOr < Generic * > ( this - > tags . at ( position ) ) ;
}
ErrorOrVoid Compound : : setElementPointerAt ( uint64_t position , Generic * pointer ) {
2022-10-14 18:30:39 +02:00
if ( position = = this - > tags . size ( ) | | pointer - > getTagType ( ) = = TagType : : END ) {
if ( position = = this - > tags . size ( ) & & pointer - > getTagType ( ) = = TagType : : END ) {
delete pointer ;
// do nothing, already have one of those
2022-10-14 20:01:47 +02:00
return ErrorOrVoid ( ) ;
2022-10-14 18:30:39 +02:00
} else {
delete pointer ;
// End tags may only go at the end and
// the end may only hold an end tag.
return ErrorOrVoid ( true , ErrorCodes : : NOT_ALLOWED ) ;
}
2022-10-14 18:08:49 +02:00
}
if ( position > this - > tags . size ( ) ) {
2022-10-05 01:12:38 +02:00
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
delete this - > tags [ position ] ;
this - > tags [ position ] = pointer ;
return ErrorOrVoid ( ) ;
}
2022-10-14 18:30:39 +02:00
ErrorOrVoid Compound : : appendPointer ( Generic * pointer ) {
if ( pointer - > getTagType ( ) = = TagType : : END ) {
2022-10-14 19:22:05 +02:00
delete pointer ;
2022-10-14 18:30:39 +02:00
return ErrorOrVoid ( true , ErrorCodes : : NOT_ALLOWED ) ;
}
2022-10-05 01:12:38 +02:00
this - > tags . push_back ( pointer ) ;
2022-10-14 18:30:39 +02:00
return ErrorOrVoid ( ) ;
2022-10-05 01:12:38 +02:00
}
ErrorOrVoid Compound : : deleteElement ( uint64_t position ) {
2022-10-14 18:08:49 +02:00
// built-in end tag
if ( position = = this - > tags . size ( ) ) {
return ErrorOrVoid ( true , ErrorCodes : : NOT_ALLOWED ) ;
}
if ( position > this - > tags . size ( ) ) {
2022-10-05 01:12:38 +02:00
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
delete this - > tags [ position ] ;
this - > tags . erase ( this - > tags . begin ( ) + position ) ;
return ErrorOrVoid ( ) ;
}
uint64_t Compound : : length ( ) {
2022-10-14 18:08:49 +02:00
// account for built-in end tag
return this - > tags . size ( ) + 1 ;
2022-10-05 01:12:38 +02:00
}
Int32Array : : Int32Array ( ) {
this - > type = TagType : : INT32_ARRAY ;
}
Int32Array : : Int32Array ( tiny_utf8 : : string name , std : : vector < int32_t > data ) {
this - > type = TagType : : INT32_ARRAY ;
this - > name = name ;
this - > data = data ;
}
Int32Array : : Int32Array ( tiny_utf8 : : string name , uint64_t length , int32_t data [ ] ) {
this - > type = TagType : : INT32_ARRAY ;
this - > name = name ;
this - > data = std : : vector ( data , data + length ) ;
}
ErrorOrVoid Int32Array : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
Helper : : writeInt32Array ( rawData , this - > data ) ;
return ErrorOrVoid ( ) ;
}
std : : vector < int32_t > Int32Array : : getData ( ) {
return this - > data ;
}
ErrorOr < int32_t > Int32Array : : getValue ( uint64_t position ) {
if ( position > = this - > data . size ( ) ) {
2022-10-14 22:31:24 +02:00
return ErrorOr < int32_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
2022-10-05 01:12:38 +02:00
}
return ErrorOr < int32_t > ( this - > data . at ( position ) ) ;
}
void Int32Array : : setData ( std : : vector < int32_t > newData ) {
this - > data = newData ;
}
ErrorOrVoid Int32Array : : setValue ( uint64_t position , int32_t value ) {
if ( position > = this - > data . size ( ) ) {
2022-10-14 22:31:24 +02:00
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
2022-10-05 01:12:38 +02:00
}
this - > data [ position ] = value ;
return ErrorOrVoid ( ) ;
}
uint64_t Int32Array : : length ( ) {
return this - > data . size ( ) ;
}
void Int32Array : : addElement ( int32_t element ) {
this - > data . push_back ( element ) ;
}
ErrorOrVoid Int32Array : : removeElement ( uint64_t position ) {
if ( position > = this - > data . size ( ) ) {
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
this - > data . erase ( this - > data . begin ( ) + position ) ;
return ErrorOrVoid ( ) ;
}
Int64Array : : Int64Array ( ) {
this - > type = TagType : : INT64_ARRAY ;
}
Int64Array : : Int64Array ( tiny_utf8 : : string name , std : : vector < int64_t > data ) {
this - > type = TagType : : INT64_ARRAY ;
this - > name = name ;
this - > data = data ;
}
Int64Array : : Int64Array ( tiny_utf8 : : string name , uint64_t length , int64_t data [ ] ) {
this - > type = TagType : : INT64_ARRAY ;
this - > name = name ;
this - > data = std : : vector ( data , data + length ) ;
}
ErrorOrVoid Int64Array : : serializeWithoutHeader ( std : : vector < uint8_t > * rawData ) {
Helper : : writeInt64Array ( rawData , this - > data ) ;
return ErrorOrVoid ( ) ;
}
std : : vector < int64_t > Int64Array : : getData ( ) {
return this - > data ;
}
ErrorOr < int64_t > Int64Array : : getValue ( uint64_t position ) {
if ( position > = this - > data . size ( ) ) {
return ErrorOr < int64_t > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
return ErrorOr < int64_t > ( this - > data [ position ] ) ;
}
void Int64Array : : setData ( std : : vector < int64_t > newData ) {
this - > data = newData ;
}
ErrorOrVoid Int64Array : : setValue ( uint64_t position , int64_t value ) {
if ( position > = this - > data . size ( ) ) {
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
this - > data [ position ] = value ;
return ErrorOrVoid ( ) ;
}
uint64_t Int64Array : : length ( ) {
return this - > data . size ( ) ;
}
void Int64Array : : addElement ( int64_t element ) {
this - > data . push_back ( element ) ;
}
ErrorOrVoid Int64Array : : removeElement ( uint64_t position ) {
if ( position > = this - > data . size ( ) ) {
return ErrorOrVoid ( true , ErrorCodes : : OUT_OF_RANGE ) ;
}
this - > data . erase ( this - > data . begin ( ) + position ) ;
return ErrorOrVoid ( ) ;
}
2022-08-03 20:31:12 +02:00
}
2022-10-15 23:05:26 +02:00
// the same comment about blindly passing up error codes applies to this function
// FIXME: memory leak when returning errors
ErrorOr < std : : vector < Tag : : Generic * > > deserializeRawListContents ( uint8_t data [ ] , uint64_t dataSize , uint64_t initialPosition , uint64_t * processedDataSize ) {
std : : vector < Tag : : Generic * > contents ;
2022-10-17 16:01:57 +02:00
ErrorOr < std : : vector < Tag : : Generic * > > returnValue ;
2022-10-15 23:05:26 +02:00
// get contained data length by reading it manually because
// the function that does it normally can't deal with
// headerless tags
//
// add one byte to position to skip the type byte
ErrorOr < int32_t > elementCount = Helper : : readInt32 ( data , dataSize , initialPosition + 1 ) ;
if ( elementCount . isError ) {
2022-10-17 16:01:57 +02:00
// this is before the creation of any pointers so we can just return
// without using the returnError label at the end of this function
2022-10-15 23:05:26 +02:00
return ErrorOr < std : : vector < Tag : : Generic * > > ( true , elementCount . errorCode ) ;
}
uint8_t contentType = data [ initialPosition ] ;
// contained type byte + 4 length bytes = 5
* processedDataSize = 5 ;
switch ( contentType ) {
case TagType : : END : {
// everything except content has been touched at this point
// and a list of end tags has no content that could be read
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
contents . push_back ( new Tag : : End ( ) ) ;
}
break ;
}
case TagType : : INT8 : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < int8_t > nextInt = Helper : : readInt8 ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextInt . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextInt . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int8 ( " " , nextInt . value ) ) ;
* processedDataSize + = 1 ;
}
break ;
}
case TagType : : INT16 : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < int16_t > nextInt = Helper : : readInt16 ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextInt . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextInt . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int16 ( " " , nextInt . value ) ) ;
* processedDataSize + = 2 ;
}
break ;
}
case TagType : : INT32 : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < int32_t > nextInt = Helper : : readInt32 ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextInt . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextInt . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int32 ( " " , nextInt . value ) ) ;
* processedDataSize + = 4 ;
}
break ;
}
case TagType : : FLOAT : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < float > nextFloat = Helper : : readFloat ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextFloat . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextFloat . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Float ( " " , nextFloat . value ) ) ;
* processedDataSize + = 4 ;
}
break ;
}
case TagType : : INT64 : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < int64_t > nextInt = Helper : : readInt64 ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextInt . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextInt . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int64 ( " " , nextInt . value ) ) ;
* processedDataSize + = 8 ;
}
break ;
}
case TagType : : DOUBLE : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < double > nextDouble = Helper : : readDouble ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextDouble . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextDouble . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Double ( " " , nextDouble . value ) ) ;
* processedDataSize + = 8 ;
}
break ;
}
case TagType : : INT8_ARRAY : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < std : : vector < int8_t > > nextArray = Helper : : readInt8Array ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextArray . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextArray . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int8Array ( " " , nextArray . value ) ) ;
* processedDataSize + = ( uint64_t ) nextArray . value . size ( ) ;
}
break ;
}
case TagType : : STRING : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < tiny_utf8 : : string > nextString = Helper : : readString ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextString . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextString . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : String ( " " , nextString . value ) ) ;
// this cannot be an error because it just got read
int16_t nextStringSize = Helper : : readInt16 ( data , dataSize , initialPosition + * processedDataSize ) . value ;
* processedDataSize + = ( uint64_t ) nextStringSize + 2 ;
}
break ;
}
case TagType : : LIST : {
uint64_t * containedDataSize = new uint64_t ;
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
* containedDataSize = 0 ;
ErrorOr < std : : vector < Tag : : Generic * > > nextListContents = deserializeRawListContents ( data , dataSize , initialPosition + * processedDataSize , containedDataSize ) ;
if ( nextListContents . isError ) {
delete containedDataSize ;
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextListContents . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
2022-10-28 04:08:06 +02:00
contents . push_back ( Tag : : List : : constructWithData ( " " , nextListContents . value ) . value ) ;
2022-10-15 23:05:26 +02:00
* processedDataSize + = * containedDataSize ;
}
delete containedDataSize ;
break ;
}
case TagType : : COMPOUND : {
uint64_t * containedDataSize = new uint64_t ;
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
* containedDataSize = 0 ;
ErrorOr < std : : vector < Tag : : Generic * > > nextCompoundData = deserialize ( data , dataSize , initialPosition + * processedDataSize , containedDataSize ) ;
if ( nextCompoundData . isError ) {
delete containedDataSize ;
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextCompoundData . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
2022-11-25 15:45:20 +01:00
contents . push_back ( reinterpret_cast < Tag : : Generic * > ( Tag : : Compound : : constructWithData ( " " , nextCompoundData . value ) . value ) ) ;
2022-10-15 23:05:26 +02:00
* processedDataSize + = * containedDataSize ;
}
delete containedDataSize ;
break ;
}
case TagType : : INT32_ARRAY : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < std : : vector < int32_t > > nextArray = Helper : : readInt32Array ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextArray . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextArray . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int32Array ( " " , nextArray . value ) ) ;
* processedDataSize + = ( uint64_t ) nextArray . value . size ( ) * 4 ;
}
break ;
}
case TagType : : INT64_ARRAY : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
ErrorOr < std : : vector < int64_t > > nextArray = Helper : : readInt64Array ( data , dataSize , initialPosition + * processedDataSize ) ;
if ( nextArray . isError ) {
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextArray . errorCode ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
contents . push_back ( new Tag : : Int64Array ( " " , nextArray . value ) ) ;
* processedDataSize + = ( uint64_t ) nextArray . value . size ( ) * 8 ;
}
break ;
}
default :
2022-10-17 16:01:57 +02:00
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , ErrorCodes : : INVALID_TYPE ) ;
goto returnError ;
2022-10-15 23:05:26 +02:00
}
return ErrorOr < std : : vector < Tag : : Generic * > > ( contents ) ;
2022-10-17 16:01:57 +02:00
returnError :
for ( uint64_t i = 0 ; i < contents . size ( ) ; i + + ) {
delete contents . at ( i ) ;
}
return returnValue ;
2022-10-15 23:05:26 +02:00
}
// comment about blindly passing up error codes applies here
//
// The return value of this function is a vector of tags
// instead of a compound tag due to a spec extension that allows
// for any bare tag to be valid NBT data without a containing
// compound tag. This also just makes the implementation easier.
ErrorOr < std : : vector < Tag : : Generic * > > deserialize ( uint8_t data [ ] , uint64_t dataSize , uint64_t initialPosition , uint64_t * processedDataSize ) {
if ( initialPosition > = dataSize ) {
if ( processedDataSize ! = nullptr ) * processedDataSize = 0 ;
return ErrorOr < std : : vector < Tag : : Generic * > > ( true , ErrorCodes : : OUT_OF_RANGE ) ;
// An interesting question at this point is whether we should
// consider empty input valid or invalid NBT data.
//
// The original spec says that the top-most tag is always a
// compound (or in more recent times, the Microsoft-commercialized
// in-game-purchase-enabling version also allows list tags)
// which automatically means that no data is invalid data...
// I don't see a reason why having a different tag as the top-most
// tag shouldn't be valid NBT in which case we have to face the
// question whether no data is invalid or just empty NBT data.
//
// This seems like a reasonable extension to the spec to me and
// it should be backwards compatible AFAIK.
//
// - BodgeMaster
}
std : : vector < Tag : : Generic * > tags = std : : vector < Tag : : Generic * > ( ) ;
ErrorOr < std : : vector < Tag : : Generic * > > returnValue ;
uint64_t currentPosition = initialPosition ;
while ( currentPosition < dataSize ) {
ErrorOr < uint64_t > nextTagSize = Helper : : totalTagSize ( data , dataSize , currentPosition ) ;
if ( nextTagSize . isError ) {
if ( nextTagSize . errorCode = = ErrorCodes : : NOT_YET_KNOWN ) {
ErrorOr < tiny_utf8 : : string > tagName = Helper : : readString ( data , dataSize , currentPosition + 1 ) ;
if ( tagName . isError ) {
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , tagName . errorCode ) ;
goto returnNow ;
}
// used seek to the start of the list's/compound’ s contents
//
// there is no way this is an error bc it gets
// checked while trying to parse the string above
int16_t nameSize = Helper : : readInt16 ( data , dataSize , currentPosition + 1 ) . value ;
uint64_t * processedTagSize = new uint64_t ;
* processedTagSize = 0 ;
if ( data [ currentPosition ] = = TagType : : LIST ) {
// type byte + two name size bytes = 3
ErrorOr < std : : vector < Tag : : Generic * > > listData = deserializeRawListContents ( data , dataSize , currentPosition + ( uint64_t ) nameSize + 3 , processedTagSize ) ;
if ( listData . isError ) {
delete processedTagSize ;
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , listData . errorCode ) ;
goto returnNow ;
}
2022-10-28 04:08:06 +02:00
tags . push_back ( Tag : : List : : constructWithData ( tagName . value , listData . value ) . value ) ;
2022-10-15 23:05:26 +02:00
* processedTagSize + = ( uint64_t ) nameSize + 3 ;
}
if ( data [ currentPosition ] = = TagType : : COMPOUND ) {
// type byte + two name size bytes = 3
ErrorOr < std : : vector < Tag : : Generic * > > compoundData = deserialize ( data , dataSize , currentPosition + ( uint64_t ) nameSize + 3 , processedTagSize ) ;
if ( compoundData . isError ) {
delete processedTagSize ;
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , compoundData . errorCode ) ;
goto returnNow ;
}
2022-11-25 15:45:20 +01:00
tags . push_back ( reinterpret_cast < Tag : : Generic * > ( Tag : : Compound : : constructWithData ( tagName . value , compoundData . value ) . value ) ) ;
2022-10-15 23:05:26 +02:00
* processedTagSize + = ( uint64_t ) nameSize + 3 ;
}
currentPosition + = * processedTagSize ;
delete processedTagSize ;
continue ;
}
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , nextTagSize . errorCode ) ;
goto returnNow ;
}
if ( currentPosition + nextTagSize . value > dataSize ) {
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , ErrorCodes : : OVERRUN ) ;
goto returnNow ;
}
// recursion abort condition
if ( data [ currentPosition ] = = TagType : : END ) {
// not appending an end tag as it is built into
// the compound anyway
currentPosition + + ;
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( tags ) ;
goto returnNow ;
}
// nameSize cannot be an error here bc it got checked in
// nextTagSize() already
int16_t nameSize = Helper : : readInt16 ( data , dataSize , currentPosition + 1 ) . value ;
ErrorOr < tiny_utf8 : : string > name = Helper : : readString ( data , dataSize , currentPosition + 1 ) ;
if ( name . isError ) {
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , name . errorCode ) ;
goto returnNow ;
}
// Overrun / out of range errors have already been ruled out by
// checking the tag size against the total amount of data.
switch ( data [ currentPosition ] ) {
case TagType : : INT8 : {
int8_t content = Helper : : readInt8 ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int8 ( name . value , content ) ) ;
break ;
}
case TagType : : INT16 : {
int16_t content = Helper : : readInt16 ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int16 ( name . value , content ) ) ;
break ;
}
case TagType : : INT32 : {
int32_t content = Helper : : readInt32 ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int32 ( name . value , content ) ) ;
break ;
}
case TagType : : INT64 : {
int64_t content = Helper : : readInt64 ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int64 ( name . value , content ) ) ;
break ;
}
case TagType : : FLOAT : {
float content = Helper : : readFloat ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Float ( name . value , content ) ) ;
break ;
}
case TagType : : DOUBLE : {
double content = Helper : : readDouble ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Double ( name . value , content ) ) ;
break ;
}
case TagType : : INT8_ARRAY : {
std : : vector < int8_t > content = Helper : : readInt8Array ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int8Array ( name . value , content ) ) ;
break ;
}
case TagType : : STRING : {
ErrorOr < tiny_utf8 : : string > content = Helper : : readString ( data , dataSize , currentPosition + nameSize + 3 ) ;
if ( content . isError ) {
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , content . errorCode ) ;
goto returnNow ;
}
tags . push_back ( new Tag : : String ( name . value , content . value ) ) ;
break ;
}
case TagType : : INT32_ARRAY : {
std : : vector < int32_t > content = Helper : : readInt32Array ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int32Array ( name . value , content ) ) ;
break ;
}
case TagType : : INT64_ARRAY : {
std : : vector < int64_t > content = Helper : : readInt64Array ( data , dataSize , currentPosition + nameSize + 3 ) . value ;
tags . push_back ( new Tag : : Int64Array ( name . value , content ) ) ;
break ;
}
default : {
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( true , ErrorCodes : : UNKNOWN ) ;
goto returnNow ;
}
}
currentPosition + = nextTagSize . value ;
}
returnValue = ErrorOr < std : : vector < Tag : : Generic * > > ( tags ) ;
goto returnNow ;
returnNow :
if ( processedDataSize ! = nullptr ) {
* processedDataSize = currentPosition - initialPosition ;
}
if ( returnValue . isError ) {
for ( uint64_t i = 0 ; i < tags . size ( ) ; i + + ) {
delete tags [ i ] ;
}
}
return returnValue ;
}
2022-09-11 09:14:32 +02:00
bool validateRawListContents ( uint8_t data [ ] , uint64_t dataSize , uint64_t initialPosition , uint64_t * processedDataSize ) {
// get contained data length by reading it manually because
// the function that does it normally can't deal with
// headerless tags
//
// add one byte to position to skip the type byte
2022-09-15 02:00:07 +02:00
ErrorOr < int32_t > elementCount = Helper : : readInt32 ( data , dataSize , initialPosition + 1 ) ;
2022-08-15 08:50:07 +02:00
if ( elementCount . isError ) {
return false ;
}
2022-09-11 09:14:32 +02:00
uint8_t contentType = data [ initialPosition ] ;
// contained type byte + 4 length bytes = 5
* processedDataSize = 5 ;
2022-08-15 08:50:07 +02:00
switch ( contentType ) {
case TagType : : END :
// everything except content has been touched at this point
// and a list of end tags has no content
return true ;
case TagType : : INT8 : {
* processedDataSize + = ( uint64_t ) elementCount . value ;
return initialPosition + * processedDataSize < dataSize ;
}
case TagType : : INT16 : {
* processedDataSize + = ( uint64_t ) elementCount . value * 2 ;
return initialPosition + * processedDataSize < dataSize ;
}
case TagType : : INT32 :
case TagType : : FLOAT : {
* processedDataSize + = ( uint64_t ) elementCount . value * 4 ;
return initialPosition + * processedDataSize < dataSize ;
}
case TagType : : INT64 :
case TagType : : DOUBLE : {
* processedDataSize + = ( uint64_t ) elementCount . value * 8 ;
return initialPosition + * processedDataSize < dataSize ;
}
case TagType : : INT8_ARRAY : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
2022-09-15 02:00:07 +02:00
ErrorOr < std : : vector < int8_t > > nextArray = Helper : : readInt8Array ( data , dataSize , initialPosition + * processedDataSize ) ;
2022-08-15 08:50:07 +02:00
if ( nextArray . isError ) {
return false ;
}
* processedDataSize + = ( uint64_t ) nextArray . value . size ( ) ;
}
return true ;
}
case TagType : : STRING : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
2022-09-15 02:00:07 +02:00
ErrorOr < tiny_utf8 : : string > nextString = Helper : : readString ( data , dataSize , initialPosition + * processedDataSize ) ;
2022-08-15 08:50:07 +02:00
if ( nextString . isError ) {
return false ;
}
// this cannot be an error because it just got checked
2022-09-15 02:00:07 +02:00
int16_t nextStringSize = Helper : : readInt16 ( data , dataSize , initialPosition + * processedDataSize ) . value ;
2022-08-15 08:50:07 +02:00
* processedDataSize + = ( uint64_t ) nextStringSize + 2 ;
}
return true ;
}
case TagType : : LIST : {
uint64_t * containedDataSize = new uint64_t ;
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
* containedDataSize = 0 ;
2022-09-11 09:14:32 +02:00
if ( validateRawListContents ( data , dataSize , initialPosition + * processedDataSize , containedDataSize ) ) {
2022-08-15 08:50:07 +02:00
* processedDataSize + = * containedDataSize ;
} else {
delete containedDataSize ;
return false ;
}
}
delete containedDataSize ;
return true ;
}
case TagType : : COMPOUND : {
uint64_t * containedDataSize = new uint64_t ;
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
* containedDataSize = 0 ;
2022-09-11 09:14:32 +02:00
if ( validateRawNBTData ( data , dataSize , initialPosition + * processedDataSize , containedDataSize ) ) {
2022-08-15 08:50:07 +02:00
* processedDataSize + = * containedDataSize ;
} else {
delete containedDataSize ;
return false ;
}
}
delete containedDataSize ;
return true ;
}
case TagType : : INT32_ARRAY : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
2022-09-15 02:00:07 +02:00
ErrorOr < std : : vector < int32_t > > nextArray = Helper : : readInt32Array ( data , dataSize , initialPosition + * processedDataSize ) ;
2022-08-15 08:50:07 +02:00
if ( nextArray . isError ) {
return false ;
}
* processedDataSize + = ( uint64_t ) nextArray . value . size ( ) * 4 ;
}
return true ;
}
case TagType : : INT64_ARRAY : {
for ( int32_t i = 0 ; i < elementCount . value ; i + + ) {
2022-09-15 02:00:07 +02:00
ErrorOr < std : : vector < int64_t > > nextArray = Helper : : readInt64Array ( data , dataSize , initialPosition + * processedDataSize ) ;
2022-08-15 08:50:07 +02:00
if ( nextArray . isError ) {
return false ;
}
* processedDataSize + = ( uint64_t ) nextArray . value . size ( ) * 8 ;
}
return true ;
}
default :
return false ;
}
}
2022-08-15 05:15:17 +02:00
bool validateRawNBTData ( uint8_t data [ ] , uint64_t dataSize , uint64_t initialPosition , uint64_t * processedDataSize ) {
if ( initialPosition > = dataSize ) {
// Yes, this *could* return an instance of ErrorOr with
// ErrorCodes::OVERRUN but we only care to know if what is
// at that position is valid NBT which it clearly isn't according
// to the original spec.
if ( processedDataSize ! = nullptr ) * processedDataSize = 0 ;
return false ;
// An interesting question at this point is whether we should
// consider empty input valid or invalid NBT data.
//
// The original spec says that the top-most tag is always a
// compound (or in more recent times, the Microsoft-commercialized
// in-game-purchase-enabling version also allows list tags)
// which automatically means that no data is invalid data...
// I don't see a reason why having a different tag as the top-most
// tag shouldn't be valid NBT in which case we have to face the
// question whether no data is invalid or just empty NBT data.
//
// This seems like a reasonable extension to the spec to me and
// it should be backwards compatible AFAIK.
//
// - BodgeMaster
}
2022-08-15 08:50:07 +02:00
2022-10-15 18:55:58 +02:00
bool returnValue ;
2022-08-15 05:15:17 +02:00
uint64_t currentPosition = initialPosition ;
while ( currentPosition < dataSize ) {
2022-09-15 02:00:07 +02:00
ErrorOr < uint64_t > nextTagSize = Helper : : totalTagSize ( data , dataSize , currentPosition ) ;
2022-08-15 05:15:17 +02:00
if ( nextTagSize . isError ) {
if ( nextTagSize . errorCode = = ErrorCodes : : NOT_YET_KNOWN ) {
// attempt parsing the name
2022-09-15 02:00:07 +02:00
ErrorOr < tiny_utf8 : : string > tagName = Helper : : readString ( data , dataSize , currentPosition + 1 ) ;
2022-08-15 08:50:07 +02:00
if ( tagName . isError ) {
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-09-11 09:14:32 +02:00
// used seek to the start of the list's/compound’ s contents
//
// there is no way this is an error bc it gets
// checked while trying to parse the string above
2022-09-15 02:00:07 +02:00
int16_t nameSize = Helper : : readInt16 ( data , dataSize , currentPosition + 1 ) . value ;
2022-09-11 09:14:32 +02:00
2022-08-15 08:50:07 +02:00
uint64_t * processedTagSize = new uint64_t ;
* processedTagSize = 0 ;
2022-08-15 05:15:17 +02:00
if ( data [ currentPosition ] = = TagType : : LIST ) {
2022-09-11 09:14:32 +02:00
// type byte + two name size bytes = 3
if ( ! validateRawListContents ( data , dataSize , currentPosition + ( uint64_t ) nameSize + 3 , processedTagSize ) ) {
2022-08-15 08:50:07 +02:00
delete processedTagSize ;
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-09-11 09:14:32 +02:00
* processedTagSize + = ( uint64_t ) nameSize + 3 ;
2022-08-15 05:15:17 +02:00
}
if ( data [ currentPosition ] = = TagType : : COMPOUND ) {
2022-08-15 10:51:50 +02:00
// type byte + two name size bytes = 3
if ( ! validateRawNBTData ( data , dataSize , currentPosition + ( uint64_t ) nameSize + 3 , processedTagSize ) ) {
2022-08-15 08:50:07 +02:00
delete processedTagSize ;
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-08-15 10:51:50 +02:00
* processedTagSize + = ( uint64_t ) nameSize + 3 ;
2022-08-15 05:15:17 +02:00
}
currentPosition + = * processedTagSize ;
2022-08-15 08:50:07 +02:00
delete processedTagSize ;
2022-08-15 05:15:17 +02:00
continue ;
}
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 05:15:17 +02:00
}
2022-08-15 08:50:07 +02:00
if ( currentPosition + nextTagSize . value > dataSize ) {
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-08-15 05:15:17 +02:00
// recursion abort condition
2022-08-15 08:50:07 +02:00
if ( data [ currentPosition ] = = TagType : : END ) {
2022-08-15 10:51:50 +02:00
currentPosition + + ;
2022-10-15 18:55:58 +02:00
returnValue = true ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-08-15 05:15:17 +02:00
// nameSize cannot be an error here bc it got checked in
// nextTagSize() already
2022-09-15 02:00:07 +02:00
int16_t nameSize = Helper : : readInt16 ( data , dataSize , currentPosition + 1 ) . value ;
2022-08-15 05:15:17 +02:00
// attempt parsing the name
//
// This shouldn't matter too much here as the only error condition
// the parser function deals with rn is an overrun which is already
// being guarded against with
2022-10-15 18:55:58 +02:00
// if (currentPosition + nextTagSize.value > dataSize) {
// returnValue = false;
// goto returnNow;
// }
2022-08-15 05:15:17 +02:00
// It might, however, turn out to be a useful check in the future.
2022-09-15 02:00:07 +02:00
ErrorOr < tiny_utf8 : : string > name = Helper : : readString ( data , dataSize , currentPosition + 1 ) ;
2022-08-15 08:50:07 +02:00
if ( name . isError ) {
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-08-15 05:15:17 +02:00
2022-08-15 09:53:06 +02:00
switch ( data [ currentPosition ] ) {
2022-08-15 05:15:17 +02:00
case TagType : : INT8 :
case TagType : : INT16 :
case TagType : : INT32 :
case TagType : : INT64 :
case TagType : : FLOAT :
case TagType : : DOUBLE :
case TagType : : INT8_ARRAY :
break ;
case TagType : : STRING : {
// attempt parsing the content
//
// This shouldn't matter too much here as the only
// error condition the parser function deals with rn is
// an overrun which is already being guarded against with
2022-10-15 18:55:58 +02:00
// if (currentPosition + nextTagSize.value > dataSize) {
// returnValue = false;
// goto returnNow;
// }
2022-08-15 05:15:17 +02:00
// It might, however, turn out to be a useful check
// in the future.
//
// type byte + two name size bytes = 3
2022-09-15 02:00:07 +02:00
ErrorOr < tiny_utf8 : : string > content = Helper : : readString ( data , dataSize , currentPosition + nameSize + 3 ) ;
2022-08-15 08:50:07 +02:00
if ( content . isError ) {
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 08:50:07 +02:00
}
2022-08-15 05:15:17 +02:00
break ;
}
case TagType : : INT32_ARRAY :
case TagType : : INT64_ARRAY :
break ;
default :
2022-10-15 18:55:58 +02:00
returnValue = false ;
goto returnNow ;
2022-08-15 05:15:17 +02:00
}
currentPosition + = nextTagSize . value ;
}
2022-10-15 18:55:58 +02:00
returnValue = true ;
goto returnNow ;
returnNow :
if ( processedDataSize ! = nullptr ) {
* processedDataSize = currentPosition - initialPosition ;
}
return returnValue ;
2022-06-27 04:50:32 +02:00
}
}