docs: @SuppressWarnings({"checkstyle:npathcomplexity", "checkstyle:magicnumber"})
  	public List<DBaseFileField> readDBFFields() throws IOException, EOFException {
  		if (this.fields != null) {
  			return this.fields;
  		}
  		if (this.finished) {
  			throw new EOFDBaseFileException();
  		}
  		if (this.fieldCount == -1) {
  			throw new MustCallReadHeaderFunctionException();
  		}
  
  		//
  		//-----------------------------------------------------------
  		// Bytes       Size   Content
  		//-----------------------------------------------------------
  		// 32-m  n*32 bytes   Field descriptors (see bellow)
  		//  m+1     1 byte    terminator character 0x0D
  
  		// A field contains at least the "removal flag" byte
  		int byteSize = 1;
  		final ArrayList<DBaseFileField> array = new ArrayList<>();
  		final Charset charSet = (this.codePage == null) ? null : this.codePage.getChatset();
  		String columnName;
  
  		for (int idxFields = 0; idxFields < this.fieldCount; ++idxFields) {
  
  			// Read the field header
  			//
  			//-----------------------------------------------------------
  			// Bytes       Size   Content
  			//-----------------------------------------------------------
  			//  0-10   11 bytes   Field name, filled with 0x00
  			//    11    1 byte    Field type (see bellow)
  			// 12-15    4 bytes   Field data address, not useful for disk
  			//    16    1 byte    Field length
  			//    17    1 byte    Field decimal count
  			// 18-19    2 bytes   Reserved for dBASE III+ on a Lan
  			//    20    1 byte    Work area ID
  			// 21-22    2 bytes   Reserved for dBASE III+ on a Lan
  			//    23    1 byte    SET FIELDS flag
  			// 24-31    7 bytes   Reserved
  			final byte[] header = new byte[32];
  			this.stream.readFully(header);
  
  			// Update the offset of the first record with the end-of-header character
  			this.firstRecordOffset += header.length;
  
  			// Read the name of the field until 0x00
  			int nbChars = 0;
  			for (int i = 0; i <= 10; ++i) {
  				if (header[i] == 0) {
  					break;
  				}
  				++nbChars;
  			}
  			final byte[] bName = new byte[nbChars];
  			System.arraycopy(header, 0, bName, 0, nbChars);
  			if (charSet != null) {
  				columnName = new String(bName, charSet);
  			} else {
  				columnName = new String(bName);
  			}
  
  			// Read the type
  			final DBaseFieldType dbftype = DBaseFieldType.fromByte(header[11]);
  
  			final DBaseFileField field = new DBaseFileField(
  					columnName,
  					dbftype,
  					// convert unsigned byte into int
  					header[16] & 0xFF,
  					// convert unsigned byte into int
  					header[17] & 0xFF,
  					idxFields);
  			array.add(field);
  			byteSize += field.getLength();
  		}
  
  		// Check if the byte size of the field list corresponds to size specified record size inside the header
  		if (byteSize != this.recordSize) {
  			throw new InvalidRecordSizeException(this.recordSize, byteSize);
  		}
  
  		// Read the terminator character 0x0D
  		final byte bt = this.stream.readByte();
  		if (bt != 0x0D) {
  			throw new InvalidDBaseFieldTerminationException(bt);
  		}
  
  		// Update the offset of the first record with the end-of-header character
  		++this.firstRecordOffset;
  
  		this.fields = array;
  
  		// Save the position inside the input stream for seeking function
  		if (this.stream.markSupported()) {
  			this.stream.mark(this.recordSize * this.recordCount + this.firstRecordOffset + 1);
  		}
  
  		return array;
  	}