diff --git a/Aaru.Helpers/.editorconfig b/Aaru.Helpers/.editorconfig
new file mode 100644
index 000000000..103a5d927
--- /dev/null
+++ b/Aaru.Helpers/.editorconfig
@@ -0,0 +1,1342 @@
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = false
+max_line_length = 120
+tab_width = 2
+trim_trailing_whitespace = false
+ij_continuation_indent_size = 4
+ij_formatter_off_tag = @formatter:off
+ij_formatter_on_tag = @formatter:on
+ij_formatter_tags_enabled = true
+ij_smart_tabs = false
+ij_visual_guides =
+ij_wrap_on_typing = false
+
+# Microsoft .NET properties
+csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
+csharp_space_after_keywords_in_control_flow_statements = false
+csharp_style_namespace_declarations = file_scoped:warning
+csharp_style_prefer_utf8_string_literals = true:warning
+csharp_style_var_elsewhere = false:suggestion
+csharp_style_var_for_built_in_types = false:suggestion
+csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_using_directive_placement = outside_namespace:silent
+dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined
+dotnet_naming_rule.private_constants_rule.severity = warning
+dotnet_naming_rule.private_constants_rule.style = all_upper_style
+dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols
+dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
+dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
+dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
+dotnet_naming_rule.unity_serialized_field_rule.severity = warning
+dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
+dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
+dotnet_naming_style.all_upper_style.capitalization = all_upper
+dotnet_naming_style.all_upper_style.word_separator = _
+dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
+dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private
+dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field
+dotnet_naming_symbols.private_constants_symbols.required_modifiers = const
+dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
+dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
+dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
+dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:warning
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:warning
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_require_accessibility_modifiers = never:suggestion
+
+# ReSharper properties
+resharper_align_first_arg_by_paren = false
+resharper_align_linq_query = true
+resharper_align_multiline_argument = true
+resharper_align_multiline_array_and_object_initializer = false
+resharper_align_multiline_binary_expressions_chain = true
+resharper_align_multiline_binary_patterns = true
+resharper_align_multiline_calls_chain = true
+resharper_align_multiline_expression = true
+resharper_align_multiline_expression_braces = true
+resharper_align_multiline_extends_list = true
+resharper_align_multiline_for_stmt = true
+resharper_align_multiline_list_pattern = true
+resharper_align_multiline_parameter = true
+resharper_align_multiline_property_pattern = true
+resharper_align_multiline_switch_expression = true
+resharper_align_multiple_declaration = true
+resharper_align_multline_type_parameter_constrains = true
+resharper_align_multline_type_parameter_list = true
+resharper_align_ternary = align_all
+resharper_align_tuple_components = true
+resharper_autodetect_indent_settings = true
+resharper_braces_for_for = required_for_multiline
+resharper_braces_for_foreach = required_for_multiline
+resharper_braces_for_ifelse = required_for_multiline
+resharper_braces_for_while = required_for_multiline
+resharper_builtin_type_apply_to_native_integer = false
+resharper_constructor_or_destructor_body = expression_body
+resharper_csharp_align_first_arg_by_paren = true
+resharper_csharp_empty_block_style = together_same_line
+resharper_csharp_place_comments_at_first_column = true
+resharper_csharp_prefer_qualified_reference = false
+resharper_default_value_when_type_not_evident = default_expression
+resharper_enforce_line_ending_style = true
+resharper_formatter_off_tag = @formatter:off
+resharper_formatter_on_tag = @formatter:on
+resharper_formatter_tags_enabled = true
+resharper_for_built_in_types = use_var_when_evident
+resharper_function_declaration_return_type_style = on_single_line
+resharper_function_definition_return_type_style = on_single_line
+resharper_html_pi_attribute_style = first_attribute_on_single_line
+resharper_indent_anonymous_method_block = false
+resharper_indent_preprocessor_if = outdent
+resharper_indent_preprocessor_other = outdent
+resharper_indent_preprocessor_region = outdent
+resharper_int_align = true
+resharper_int_align_bitfield_sizes = true
+resharper_int_align_comments = true
+resharper_int_align_declaration_names = true
+resharper_int_align_enum_initializers = true
+resharper_int_align_eq = true
+resharper_keep_existing_embedded_arrangement = false
+resharper_keep_existing_initializer_arrangement = false
+resharper_keep_existing_linebreaks = false
+resharper_keep_existing_list_patterns_arrangement = false
+resharper_keep_existing_property_patterns_arrangement = false
+resharper_keep_existing_switch_expression_arrangement = false
+resharper_line_break_after_colon_in_member_initializer_lists = on_single_line
+resharper_line_break_before_requires_clause = on_single_line
+resharper_linkage_specification_indentation = all
+resharper_local_function_body = expression_body
+resharper_member_initializer_list_style = on_single_line
+resharper_method_or_operator_body = expression_body
+resharper_outdent_binary_ops = true
+resharper_outdent_binary_pattern_ops = true
+resharper_outdent_commas = true
+resharper_outdent_dots = true
+resharper_outdent_statement_labels = true
+resharper_parentheses_redundancy_style = remove
+resharper_place_attribute_on_same_line = false
+resharper_place_simple_embedded_statement_on_same_line = false
+resharper_place_simple_initializer_on_single_line = false
+resharper_qualified_using_at_nested_scope = true
+resharper_show_autodetect_configure_formatting_tip = false
+resharper_simple_block_style = on_single_line
+resharper_simple_case_statement_style = line_break
+resharper_simple_embedded_statement_style = on_single_line
+resharper_space_after_ptr_in_data_member = false
+resharper_space_after_ptr_in_method = false
+resharper_space_after_ref_in_data_member = false
+resharper_space_after_ref_in_method = false
+resharper_space_before_ptr_in_data_member = true
+resharper_space_before_ptr_in_method = true
+resharper_space_before_ref_in_data_member = true
+resharper_space_before_ref_in_method = true
+resharper_space_before_template_params = false
+resharper_space_within_empty_braces = false
+resharper_toplevel_function_declaration_return_type_style = on_single_line
+resharper_toplevel_function_definition_return_type_style = on_single_line
+resharper_use_indent_from_vs = false
+resharper_wrap_after_dot_in_method_calls = true
+resharper_wrap_base_clause_style = chop_if_long
+resharper_wrap_braced_init_list_style = chop_if_long
+resharper_wrap_chained_binary_expressions = chop_if_long
+resharper_wrap_chained_method_calls = chop_if_long
+resharper_wrap_ctor_initializer_style = chop_if_long
+resharper_wrap_lines = true
+resharper_xmldoc_attribute_indent = align_by_first_attribute
+resharper_xmldoc_attribute_style = first_attribute_on_single_line
+resharper_xmldoc_pi_attribute_style = first_attribute_on_single_line
+
+# ReSharper inspection severities
+resharper_annotate_can_be_null_parameter_highlighting = warning
+resharper_annotate_can_be_null_type_member_highlighting = warning
+resharper_annotate_not_null_parameter_highlighting = warning
+resharper_annotate_not_null_type_member_highlighting = warning
+resharper_arguments_style_anonymous_function_highlighting = warning
+resharper_arguments_style_literal_highlighting = warning
+resharper_arguments_style_named_expression_highlighting = warning
+resharper_arguments_style_other_highlighting = warning
+resharper_arguments_style_string_literal_highlighting = warning
+resharper_arrange_accessor_owner_body_highlighting = warning
+resharper_arrange_constructor_or_destructor_body_highlighting = warning
+resharper_arrange_local_function_body_highlighting = warning
+resharper_arrange_method_or_operator_body_highlighting = warning
+resharper_arrange_redundant_parentheses_highlighting = hint
+resharper_arrange_static_member_qualifier_highlighting = warning
+resharper_arrange_this_qualifier_highlighting = hint
+resharper_arrange_trailing_comma_in_multiline_lists_highlighting = warning
+resharper_arrange_trailing_comma_in_singleline_lists_highlighting = warning
+resharper_arrange_type_member_modifiers_highlighting = hint
+resharper_arrange_type_modifiers_highlighting = hint
+resharper_arrange_var_keywords_in_deconstructing_declaration_highlighting = warning
+resharper_async_void_method_highlighting = warning
+resharper_auto_property_can_be_made_get_only_global_highlighting = warning
+resharper_auto_property_can_be_made_get_only_local_highlighting = warning
+resharper_bad_attribute_brackets_spaces_highlighting = warning
+resharper_bad_braces_spaces_highlighting = warning
+resharper_bad_colon_spaces_highlighting = warning
+resharper_bad_comma_spaces_highlighting = warning
+resharper_bad_control_braces_indent_highlighting = warning
+resharper_bad_control_braces_line_breaks_highlighting = warning
+resharper_bad_declaration_braces_indent_highlighting = warning
+resharper_bad_declaration_braces_line_breaks_highlighting = warning
+resharper_bad_empty_braces_line_breaks_highlighting = warning
+resharper_bad_expression_braces_indent_highlighting = warning
+resharper_bad_expression_braces_line_breaks_highlighting = warning
+resharper_bad_generic_brackets_spaces_highlighting = warning
+resharper_bad_indent_highlighting = warning
+resharper_bad_linq_line_breaks_highlighting = warning
+resharper_bad_member_access_spaces_highlighting = warning
+resharper_bad_namespace_braces_indent_highlighting = warning
+resharper_bad_parens_line_breaks_highlighting = warning
+resharper_bad_parens_spaces_highlighting = warning
+resharper_bad_preprocessor_indent_highlighting = warning
+resharper_bad_semicolon_spaces_highlighting = warning
+resharper_bad_spaces_after_keyword_highlighting = warning
+resharper_bad_square_brackets_spaces_highlighting = warning
+resharper_bad_switch_braces_indent_highlighting = warning
+resharper_bad_symbol_spaces_highlighting = warning
+resharper_built_in_type_reference_style_for_member_access_highlighting = hint
+resharper_built_in_type_reference_style_highlighting = hint
+resharper_check_for_reference_equality_instead_1_highlighting = warning
+resharper_check_for_reference_equality_instead_2_highlighting = warning
+resharper_check_for_reference_equality_instead_3_highlighting = warning
+resharper_check_for_reference_equality_instead_4_highlighting = warning
+resharper_class_can_be_sealed_global_highlighting = warning
+resharper_class_can_be_sealed_local_highlighting = warning
+resharper_class_never_instantiated_global_highlighting = warning
+resharper_class_never_instantiated_local_highlighting = warning
+resharper_class_with_virtual_members_never_inherited_global_highlighting = warning
+resharper_class_with_virtual_members_never_inherited_local_highlighting = warning
+resharper_comment_typo_highlighting = none
+resharper_compare_non_constrained_generic_with_null_highlighting = warning
+resharper_convert_closure_to_method_group_highlighting = warning
+resharper_convert_conditional_ternary_expression_to_switch_expression_highlighting = warning
+resharper_convert_if_do_to_while_highlighting = warning
+resharper_convert_if_statement_to_conditional_ternary_expression_highlighting = warning
+resharper_convert_if_statement_to_null_coalescing_assignment_highlighting = warning
+resharper_convert_if_statement_to_null_coalescing_expression_highlighting = warning
+resharper_convert_if_statement_to_return_statement_highlighting = warning
+resharper_convert_if_statement_to_switch_statement_highlighting = warning
+resharper_convert_if_to_or_expression_highlighting = warning
+resharper_convert_nullable_to_short_form_highlighting = warning
+resharper_convert_switch_statement_to_switch_expression_highlighting = warning
+resharper_convert_to_auto_property_highlighting = warning
+resharper_convert_to_auto_property_when_possible_highlighting = warning
+resharper_convert_to_auto_property_with_private_setter_highlighting = warning
+resharper_convert_to_compound_assignment_highlighting = warning
+resharper_convert_to_constant_global_highlighting = warning
+resharper_convert_to_constant_local_highlighting = warning
+resharper_convert_to_lambda_expression_highlighting = warning
+resharper_convert_to_local_function_highlighting = warning
+resharper_convert_to_null_coalescing_compound_assignment_highlighting = warning
+resharper_convert_to_primary_constructor_highlighting = warning
+resharper_convert_to_static_class_highlighting = warning
+resharper_convert_to_using_declaration_highlighting = warning
+resharper_cpp_enforce_cv_qualifiers_order_highlighting = hint
+resharper_cpp_enforce_cv_qualifiers_placement_highlighting = hint
+resharper_cpp_enforce_do_statement_braces_highlighting = hint
+resharper_cpp_enforce_for_statement_braces_highlighting = hint
+resharper_cpp_enforce_function_declaration_style_highlighting = hint
+resharper_cpp_enforce_if_statement_braces_highlighting = hint
+resharper_cpp_enforce_type_alias_code_style_highlighting = hint
+resharper_cpp_enforce_while_statement_braces_highlighting = hint
+resharper_cpp_remove_redundant_braces_highlighting = hint
+resharper_double_negation_in_pattern_highlighting = warning
+resharper_double_negation_operator_highlighting = warning
+resharper_event_never_invoked_global_highlighting = warning
+resharper_event_never_subscribed_to_global_highlighting = warning
+resharper_event_never_subscribed_to_local_highlighting = warning
+resharper_field_can_be_made_read_only_global_highlighting = warning
+resharper_field_can_be_made_read_only_local_highlighting = warning
+resharper_foreach_can_be_converted_to_query_using_another_get_enumerator_highlighting = warning
+resharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting = none
+resharper_for_can_be_converted_to_foreach_highlighting = warning
+resharper_heap_view_boxing_allocation_highlighting = none
+resharper_heap_view_closure_allocation_highlighting = none
+resharper_heap_view_delegate_allocation_highlighting = none
+resharper_heap_view_object_allocation_evident_highlighting = none
+resharper_heap_view_object_allocation_highlighting = none
+resharper_identifier_typo_highlighting = none
+resharper_incorrect_blank_lines_near_braces_highlighting = warning
+resharper_inheritdoc_consider_usage_highlighting = warning
+resharper_inline_out_variable_declaration_highlighting = warning
+resharper_inline_temporary_variable_highlighting = warning
+resharper_introduce_optional_parameters_global_highlighting = warning
+resharper_introduce_optional_parameters_local_highlighting = warning
+resharper_invert_condition_1_highlighting = warning
+resharper_invert_if_highlighting = warning
+resharper_invocation_is_skipped_highlighting = warning
+resharper_invoke_as_extension_method_highlighting = warning
+resharper_join_declaration_and_initializer_highlighting = warning
+resharper_join_null_check_with_usage_highlighting = warning
+resharper_lambda_expression_must_be_static_highlighting = warning
+resharper_local_function_can_be_made_static_highlighting = warning
+resharper_loop_can_be_converted_to_query_highlighting = warning
+resharper_loop_can_be_partly_converted_to_query_highlighting = warning
+resharper_member_can_be_file_local_highlighting = warning
+resharper_member_can_be_internal_highlighting = warning
+resharper_member_can_be_made_static_global_highlighting = warning
+resharper_member_can_be_made_static_local_highlighting = warning
+resharper_member_can_be_private_global_highlighting = warning
+resharper_member_can_be_private_local_highlighting = warning
+resharper_member_can_be_protected_global_highlighting = warning
+resharper_member_can_be_protected_local_highlighting = warning
+resharper_merge_and_pattern_highlighting = warning
+resharper_merge_cast_with_type_check_highlighting = warning
+resharper_merge_conditional_expression_highlighting = warning
+resharper_merge_into_logical_pattern_highlighting = warning
+resharper_merge_into_negated_pattern_highlighting = warning
+resharper_merge_into_pattern_highlighting = warning
+resharper_merge_nested_property_patterns_highlighting = warning
+resharper_merge_sequential_checks_highlighting = warning
+resharper_method_has_async_overload_highlighting = warning
+resharper_method_has_async_overload_with_cancellation_highlighting = warning
+resharper_method_supports_cancellation_highlighting = warning
+resharper_missing_blank_lines_highlighting = warning
+resharper_missing_linebreak_highlighting = warning
+resharper_missing_space_highlighting = warning
+resharper_more_specific_foreach_variable_type_available_highlighting = warning
+resharper_move_to_existing_positional_deconstruction_pattern_highlighting = warning
+resharper_move_variable_declaration_inside_loop_condition_highlighting = warning
+resharper_multiple_spaces_highlighting = warning
+resharper_multiple_statements_on_one_line_highlighting = warning
+resharper_multiple_type_members_on_one_line_highlighting = warning
+resharper_negation_of_relational_pattern_highlighting = warning
+resharper_negative_equality_expression_highlighting = warning
+resharper_nested_string_interpolation_highlighting = warning
+resharper_not_accessed_field_global_highlighting = warning
+resharper_nullable_warning_suppression_is_used_highlighting = warning
+resharper_outdent_is_off_prev_level_highlighting = warning
+resharper_out_parameter_value_is_always_discarded_global_highlighting = warning
+resharper_parameter_only_used_for_precondition_check_global_highlighting = warning
+resharper_parameter_type_can_be_enumerable_global_highlighting = warning
+resharper_parameter_type_can_be_enumerable_local_highlighting = warning
+resharper_pass_string_interpolation_highlighting = warning
+resharper_possible_unintended_queryable_as_enumerable_highlighting = warning
+resharper_property_can_be_made_init_only_global_highlighting = warning
+resharper_property_can_be_made_init_only_local_highlighting = warning
+resharper_public_constructor_in_abstract_class_highlighting = warning
+resharper_raw_string_can_be_simplified_highlighting = warning
+resharper_redundant_accessor_body_highlighting = warning
+resharper_redundant_always_match_subpattern_highlighting = warning
+resharper_redundant_array_creation_expression_highlighting = warning
+resharper_redundant_attribute_parentheses_highlighting = warning
+resharper_redundant_attribute_usage_property_highlighting = warning
+resharper_redundant_base_qualifier_highlighting = warning
+resharper_redundant_blank_lines_highlighting = warning
+resharper_redundant_collection_initializer_element_braces_highlighting = warning
+resharper_redundant_configure_await_highlighting = warning
+resharper_redundant_declaration_semicolon_highlighting = warning
+resharper_redundant_discard_designation_highlighting = warning
+resharper_redundant_empty_object_creation_argument_list_highlighting = warning
+resharper_redundant_enum_case_label_for_default_section_highlighting = warning
+resharper_redundant_explicit_params_array_creation_highlighting = warning
+resharper_redundant_fixed_pointer_declaration_highlighting = warning
+resharper_redundant_if_else_block_highlighting = warning
+resharper_redundant_immediate_delegate_invocation_highlighting = warning
+resharper_redundant_is_before_relational_pattern_highlighting = warning
+resharper_redundant_lambda_signature_parentheses_highlighting = warning
+resharper_redundant_overload_global_highlighting = warning
+resharper_redundant_overload_local_highlighting = warning
+resharper_redundant_pattern_parentheses_highlighting = warning
+resharper_redundant_property_pattern_clause_highlighting = warning
+resharper_redundant_query_order_by_ascending_keyword_highlighting = warning
+resharper_redundant_range_bound_highlighting = warning
+resharper_redundant_readonly_modifier_highlighting = warning
+resharper_redundant_space_highlighting = warning
+resharper_redundant_string_interpolation_highlighting = warning
+resharper_redundant_to_string_call_for_value_type_highlighting = warning
+resharper_redundant_verbatim_prefix_highlighting = warning
+resharper_redundant_verbatim_string_prefix_highlighting = warning
+resharper_redundant_with_expression_highlighting = warning
+resharper_remove_constructor_invocation_highlighting = warning
+resharper_remove_redundant_braces_highlighting = warning
+resharper_remove_redundant_or_statement_false_highlighting = warning
+resharper_remove_redundant_or_statement_true_highlighting = warning
+resharper_remove_to_list_1_highlighting = warning
+resharper_remove_to_list_2_highlighting = warning
+resharper_replace_auto_property_with_computed_property_highlighting = warning
+resharper_replace_conditional_expression_with_null_coalescing_highlighting = warning
+resharper_replace_object_pattern_with_var_pattern_highlighting = warning
+resharper_replace_slice_with_range_indexer_highlighting = warning
+resharper_replace_substring_with_range_indexer_highlighting = warning
+resharper_replace_with_field_keyword_highlighting = warning
+resharper_replace_with_first_or_default_1_highlighting = warning
+resharper_replace_with_first_or_default_2_highlighting = warning
+resharper_replace_with_first_or_default_3_highlighting = warning
+resharper_replace_with_first_or_default_4_highlighting = warning
+resharper_replace_with_last_or_default_1_highlighting = warning
+resharper_replace_with_last_or_default_2_highlighting = warning
+resharper_replace_with_last_or_default_3_highlighting = warning
+resharper_replace_with_last_or_default_4_highlighting = warning
+resharper_replace_with_of_type_1_highlighting = warning
+resharper_replace_with_of_type_2_highlighting = warning
+resharper_replace_with_of_type_3_highlighting = warning
+resharper_replace_with_of_type_any_1_highlighting = warning
+resharper_replace_with_of_type_any_2_highlighting = warning
+resharper_replace_with_of_type_count_1_highlighting = warning
+resharper_replace_with_of_type_count_2_highlighting = warning
+resharper_replace_with_of_type_first_1_highlighting = warning
+resharper_replace_with_of_type_first_2_highlighting = warning
+resharper_replace_with_of_type_first_or_default_1_highlighting = warning
+resharper_replace_with_of_type_first_or_default_2_highlighting = warning
+resharper_replace_with_of_type_last_1_highlighting = warning
+resharper_replace_with_of_type_last_2_highlighting = warning
+resharper_replace_with_of_type_last_or_default_1_highlighting = warning
+resharper_replace_with_of_type_last_or_default_2_highlighting = warning
+resharper_replace_with_of_type_long_count_highlighting = warning
+resharper_replace_with_of_type_single_1_highlighting = warning
+resharper_replace_with_of_type_single_2_highlighting = warning
+resharper_replace_with_of_type_single_or_default_1_highlighting = warning
+resharper_replace_with_of_type_single_or_default_2_highlighting = warning
+resharper_replace_with_of_type_where_highlighting = warning
+resharper_replace_with_simple_assignment_false_highlighting = warning
+resharper_replace_with_simple_assignment_true_highlighting = warning
+resharper_replace_with_single_assignment_false_highlighting = warning
+resharper_replace_with_single_assignment_true_highlighting = warning
+resharper_replace_with_single_call_to_any_highlighting = warning
+resharper_replace_with_single_call_to_count_highlighting = warning
+resharper_replace_with_single_call_to_first_highlighting = warning
+resharper_replace_with_single_call_to_first_or_default_highlighting = warning
+resharper_replace_with_single_call_to_last_highlighting = warning
+resharper_replace_with_single_call_to_last_or_default_highlighting = warning
+resharper_replace_with_single_call_to_single_highlighting = warning
+resharper_replace_with_single_call_to_single_or_default_highlighting = warning
+resharper_replace_with_single_or_default_1_highlighting = warning
+resharper_replace_with_single_or_default_2_highlighting = warning
+resharper_replace_with_single_or_default_3_highlighting = warning
+resharper_replace_with_single_or_default_4_highlighting = warning
+resharper_replace_with_string_is_null_or_empty_highlighting = warning
+resharper_return_type_can_be_enumerable_global_highlighting = warning
+resharper_return_type_can_be_enumerable_local_highlighting = warning
+resharper_safe_cast_is_used_as_type_check_highlighting = warning
+resharper_separate_control_transfer_statement_highlighting = warning
+resharper_similar_anonymous_type_nearby_highlighting = warning
+resharper_simplify_conditional_ternary_expression_highlighting = warning
+resharper_simplify_linq_expression_use_all_highlighting = warning
+resharper_simplify_linq_expression_use_any_highlighting = warning
+resharper_simplify_linq_expression_use_min_by_and_max_by_highlighting = warning
+resharper_simplify_string_interpolation_highlighting = warning
+resharper_specify_string_comparison_highlighting = warning
+resharper_string_ends_with_is_culture_specific_highlighting = warning
+resharper_string_literal_as_interpolation_argument_highlighting = warning
+resharper_string_literal_typo_highlighting = warning
+resharper_string_starts_with_is_culture_specific_highlighting = warning
+resharper_struct_can_be_made_read_only_highlighting = warning
+resharper_struct_member_can_be_made_read_only_highlighting = warning
+resharper_suggest_base_type_for_parameter_highlighting = none
+resharper_suggest_base_type_for_parameter_in_constructor_highlighting = warning
+resharper_suggest_var_or_type_built_in_types_highlighting = hint
+resharper_suggest_var_or_type_deconstruction_declarations_highlighting = warning
+resharper_suggest_var_or_type_elsewhere_highlighting = hint
+resharper_suggest_var_or_type_simple_types_highlighting = hint
+resharper_swap_via_deconstruction_highlighting = warning
+resharper_switch_expression_handles_some_known_enum_values_with_exception_in_default_highlighting = warning
+resharper_switch_statement_handles_some_known_enum_values_with_default_highlighting = none
+resharper_switch_statement_missing_some_enum_cases_no_default_highlighting = none
+resharper_tabs_and_spaces_mismatch_highlighting = warning
+resharper_tabs_are_disallowed_highlighting = warning
+resharper_tabs_outside_indent_highlighting = warning
+resharper_tail_recursive_call_highlighting = warning
+resharper_too_wide_local_variable_scope_highlighting = warning
+resharper_try_cast_always_succeeds_highlighting = warning
+resharper_try_statements_can_be_merged_highlighting = warning
+resharper_type_parameter_can_be_variant_highlighting = warning
+resharper_unnecessary_whitespace_highlighting = warning
+resharper_unused_member_global_highlighting = warning
+resharper_unused_member_hierarchy_global_highlighting = warning
+resharper_unused_member_in_super_global_highlighting = warning
+resharper_unused_method_return_value_global_highlighting = warning
+resharper_unused_parameter_global_highlighting = warning
+resharper_unused_type_global_highlighting = warning
+resharper_use_array_creation_expression_1_highlighting = warning
+resharper_use_array_creation_expression_2_highlighting = warning
+resharper_use_array_empty_method_highlighting = warning
+resharper_use_await_using_highlighting = warning
+resharper_use_cancellation_token_for_i_async_enumerable_highlighting = warning
+resharper_use_collection_count_property_highlighting = warning
+resharper_use_configure_await_false_highlighting = warning
+resharper_use_deconstruction_highlighting = warning
+resharper_use_empty_types_field_highlighting = warning
+resharper_use_event_args_empty_field_highlighting = warning
+resharper_use_format_specifier_in_format_string_highlighting = warning
+resharper_use_indexed_property_highlighting = warning
+resharper_use_index_from_end_expression_highlighting = warning
+resharper_use_is_operator_1_highlighting = warning
+resharper_use_is_operator_2_highlighting = warning
+resharper_use_method_any_0_highlighting = warning
+resharper_use_method_any_1_highlighting = warning
+resharper_use_method_any_2_highlighting = warning
+resharper_use_method_any_3_highlighting = warning
+resharper_use_method_any_4_highlighting = warning
+resharper_use_method_is_instance_of_type_highlighting = warning
+resharper_use_nameof_expression_for_part_of_the_string_highlighting = warning
+resharper_use_nameof_expression_highlighting = warning
+resharper_use_nameof_for_dependency_property_highlighting = warning
+resharper_use_name_of_instead_of_type_of_highlighting = warning
+resharper_use_negated_pattern_in_is_expression_highlighting = warning
+resharper_use_negated_pattern_matching_highlighting = warning
+resharper_use_nullable_annotation_instead_of_attribute_highlighting = warning
+resharper_use_nullable_attributes_supported_by_compiler_highlighting = warning
+resharper_use_null_propagation_highlighting = warning
+resharper_use_object_or_collection_initializer_highlighting = warning
+resharper_use_pattern_matching_highlighting = warning
+resharper_use_positional_deconstruction_pattern_highlighting = warning
+resharper_use_raw_string_highlighting = warning
+resharper_use_string_interpolation_highlighting = warning
+resharper_use_switch_case_pattern_variable_highlighting = warning
+resharper_use_throw_if_null_method_highlighting = warning
+resharper_use_unsigned_right_shift_operator_highlighting = warning
+resharper_use_verbatim_string_highlighting = warning
+resharper_use_with_expression_to_copy_anonymous_object_highlighting = warning
+resharper_use_with_expression_to_copy_record_highlighting = warning
+resharper_use_with_expression_to_copy_struct_highlighting = warning
+resharper_use_with_expression_to_copy_tuple_highlighting = warning
+resharper_virtual_member_never_overridden_global_highlighting = warning
+resharper_virtual_member_never_overridden_local_highlighting = warning
+resharper_web_config_module_not_resolved_highlighting = warning
+resharper_web_config_type_not_resolved_highlighting = warning
+resharper_web_config_wrong_module_highlighting = warning
+resharper_with_expression_instead_of_initializer_highlighting = warning
+resharper_wrong_indent_size_highlighting = warning
+
+[*.css]
+ij_css_align_closing_brace_with_properties = false
+ij_css_blank_lines_around_nested_selector = 1
+ij_css_blank_lines_between_blocks = 1
+ij_css_block_comment_add_space = false
+ij_css_brace_placement = end_of_line
+ij_css_enforce_quotes_on_format = false
+ij_css_hex_color_long_format = false
+ij_css_hex_color_lower_case = false
+ij_css_hex_color_short_format = false
+ij_css_hex_color_upper_case = false
+ij_css_keep_blank_lines_in_code = 2
+ij_css_keep_indents_on_empty_lines = false
+ij_css_keep_single_line_blocks = false
+ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_css_space_after_colon = true
+ij_css_space_before_opening_brace = true
+ij_css_use_double_quotes = true
+ij_css_value_alignment = do_not_align
+
+[*.csv]
+max_line_length = 2147483647
+ij_csv_wrap_long_lines = false
+indent_style = tab
+tab_width = 1
+
+[*.dart]
+max_line_length = 80
+
+[*.less]
+indent_size = 2
+ij_less_align_closing_brace_with_properties = false
+ij_less_blank_lines_around_nested_selector = 1
+ij_less_blank_lines_between_blocks = 1
+ij_less_block_comment_add_space = false
+ij_less_brace_placement = 0
+ij_less_enforce_quotes_on_format = false
+ij_less_hex_color_long_format = false
+ij_less_hex_color_lower_case = false
+ij_less_hex_color_short_format = false
+ij_less_hex_color_upper_case = false
+ij_less_keep_blank_lines_in_code = 2
+ij_less_keep_indents_on_empty_lines = false
+ij_less_keep_single_line_blocks = false
+ij_less_line_comment_add_space = false
+ij_less_line_comment_at_first_column = false
+ij_less_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_less_space_after_colon = true
+ij_less_space_before_opening_brace = true
+ij_less_use_double_quotes = true
+ij_less_value_alignment = 0
+
+[*.pp]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 2
+ij_puppet_keep_indents_on_empty_lines = false
+
+[*.properties]
+ij_properties_align_group_field_declarations = true
+ij_properties_keep_blank_lines = false
+ij_properties_key_value_delimiter = equals
+ij_properties_spaces_around_key_value_delimiter = true
+
+[*.sass]
+indent_size = 2
+ij_sass_align_closing_brace_with_properties = false
+ij_sass_blank_lines_around_nested_selector = 1
+ij_sass_blank_lines_between_blocks = 1
+ij_sass_brace_placement = 0
+ij_sass_enforce_quotes_on_format = false
+ij_sass_hex_color_long_format = false
+ij_sass_hex_color_lower_case = false
+ij_sass_hex_color_short_format = false
+ij_sass_hex_color_upper_case = false
+ij_sass_keep_blank_lines_in_code = 2
+ij_sass_keep_indents_on_empty_lines = false
+ij_sass_keep_single_line_blocks = false
+ij_sass_line_comment_add_space = false
+ij_sass_line_comment_at_first_column = false
+ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_sass_space_after_colon = true
+ij_sass_space_before_opening_brace = true
+ij_sass_use_double_quotes = true
+ij_sass_value_alignment = 0
+
+[*.scss]
+indent_size = 2
+ij_scss_align_closing_brace_with_properties = false
+ij_scss_blank_lines_around_nested_selector = 1
+ij_scss_blank_lines_between_blocks = 1
+ij_scss_block_comment_add_space = false
+ij_scss_brace_placement = 0
+ij_scss_enforce_quotes_on_format = false
+ij_scss_hex_color_long_format = false
+ij_scss_hex_color_lower_case = false
+ij_scss_hex_color_short_format = false
+ij_scss_hex_color_upper_case = false
+ij_scss_keep_blank_lines_in_code = 2
+ij_scss_keep_indents_on_empty_lines = false
+ij_scss_keep_single_line_blocks = false
+ij_scss_line_comment_add_space = false
+ij_scss_line_comment_at_first_column = false
+ij_scss_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow
+ij_scss_space_after_colon = true
+ij_scss_space_before_opening_brace = true
+ij_scss_use_double_quotes = true
+ij_scss_value_alignment = 0
+
+[*.slim]
+indent_size = 2
+ij_slim_keep_indents_on_empty_lines = false
+
+[*.twig]
+ij_twig_keep_indents_on_empty_lines = false
+ij_twig_spaces_inside_comments_delimiters = true
+ij_twig_spaces_inside_delimiters = true
+ij_twig_spaces_inside_variable_delimiters = true
+
+[*.vue]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_vue_indent_children_of_top_level = template
+ij_vue_interpolation_new_line_after_start_delimiter = true
+ij_vue_interpolation_new_line_before_end_delimiter = true
+ij_vue_interpolation_wrap = off
+ij_vue_keep_indents_on_empty_lines = false
+ij_vue_spaces_within_interpolation_expressions = true
+
+[.editorconfig]
+ij_editorconfig_align_group_field_declarations = true
+ij_editorconfig_space_after_colon = false
+ij_editorconfig_space_after_comma = true
+ij_editorconfig_space_before_colon = false
+ij_editorconfig_space_before_comma = false
+ij_editorconfig_spaces_around_assignment_operators = true
+
+[{*.ad,*.adoc,*.asciidoc,.asciidoctorconfig}]
+ij_asciidoc_blank_lines_after_header = 1
+ij_asciidoc_blank_lines_keep_after_header = 1
+ij_asciidoc_formatting_enabled = true
+ij_asciidoc_one_sentence_per_line = true
+
+[{*.ant,*.appxmanifest,*.axml,*.cscfg,*.csdef,*.disco,*.dotsettings,*.filelayout,*.fxml,*.jhm,*.jnlp,*.jrxml,*.manifest,*.myapp,*.nuspec,*.rng,*.sdef,*.stylecop,*.svcmap,*.tld,*.wadcfgx,*.webref,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,StyleCop.Cache}]
+ij_xml_align_attributes = true
+ij_xml_align_text = false
+ij_xml_attribute_wrap = on_every_item
+ij_xml_block_comment_add_space = false
+ij_xml_block_comment_at_first_column = true
+ij_xml_keep_blank_lines = 2
+ij_xml_keep_indents_on_empty_lines = false
+ij_xml_keep_line_breaks = true
+ij_xml_keep_line_breaks_in_text = true
+ij_xml_keep_whitespaces = false
+ij_xml_keep_whitespaces_around_cdata = preserve
+ij_xml_keep_whitespaces_inside_cdata = false
+ij_xml_line_comment_at_first_column = true
+ij_xml_space_after_tag_name = false
+ij_xml_space_around_equals_in_attribute = false
+ij_xml_space_inside_empty_tag = false
+ij_xml_text_wrap = normal
+ij_xml_use_custom_settings = false
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+
+[{*.applescript,*.scpt}]
+indent_size = 2
+tab_width = 2
+ij_continuation_indent_size = 4
+ij_applescript_align_multiline_binary_operation = true
+ij_applescript_align_multiline_parameters = true
+ij_applescript_align_multiline_parameters_in_calls = true
+ij_applescript_binary_operation_sign_on_next_line = false
+ij_applescript_binary_operation_wrap = off
+ij_applescript_block_brace_style = next_line
+ij_applescript_call_parameters_new_line_after_left_paren = false
+ij_applescript_call_parameters_right_paren_on_new_line = false
+ij_applescript_call_parameters_wrap = off
+ij_applescript_else_on_new_line = true
+ij_applescript_keep_blank_lines_in_code = 2
+ij_applescript_keep_first_column_comment = true
+ij_applescript_keep_indents_on_empty_lines = false
+ij_applescript_keep_line_breaks = true
+ij_applescript_method_brace_style = next_line
+ij_applescript_method_parameters_new_line_after_left_paren = false
+ij_applescript_method_parameters_right_paren_on_new_line = false
+ij_applescript_method_parameters_wrap = off
+ij_applescript_parentheses_expression_new_line_after_left_paren = false
+ij_applescript_parentheses_expression_right_paren_on_new_line = false
+ij_applescript_space_after_colon = true
+ij_applescript_space_after_comma = true
+ij_applescript_space_after_comma_in_type_arguments = true
+ij_applescript_space_before_colon = true
+ij_applescript_space_before_comma = false
+ij_applescript_space_before_else_keyword = true
+ij_applescript_space_before_else_left_brace = true
+ij_applescript_space_before_if_parentheses = false
+ij_applescript_space_before_method_call_parentheses = false
+ij_applescript_space_before_method_left_brace = true
+ij_applescript_space_before_method_parentheses = false
+ij_applescript_space_before_while_keyword = true
+ij_applescript_spaces_around_additive_operators = true
+ij_applescript_spaces_around_assignment_operators = true
+ij_applescript_spaces_around_equality_operators = true
+ij_applescript_spaces_around_logical_operators = true
+ij_applescript_spaces_around_multiplicative_operators = true
+ij_applescript_spaces_around_relational_operators = true
+ij_applescript_spaces_around_shift_operators = true
+ij_applescript_spaces_around_unary_operator = false
+ij_applescript_spaces_within_if_parentheses = false
+ij_applescript_spaces_within_method_call_parentheses = false
+ij_applescript_spaces_within_method_parentheses = false
+ij_applescript_special_else_if_treatment = true
+
+[{*.ats,*.cts,*.mts,*.ts}]
+ij_continuation_indent_size = 4
+ij_typescript_align_imports = true
+ij_typescript_align_multiline_array_initializer_expression = true
+ij_typescript_align_multiline_binary_operation = true
+ij_typescript_align_multiline_chained_methods = true
+ij_typescript_align_multiline_extends_list = false
+ij_typescript_align_multiline_for = true
+ij_typescript_align_multiline_parameters = true
+ij_typescript_align_multiline_parameters_in_calls = true
+ij_typescript_align_multiline_ternary_operation = true
+ij_typescript_align_object_properties = 1
+ij_typescript_align_union_types = true
+ij_typescript_align_var_statements = 2
+ij_typescript_array_initializer_new_line_after_left_brace = false
+ij_typescript_array_initializer_right_brace_on_new_line = false
+ij_typescript_array_initializer_wrap = on_every_item
+ij_typescript_assignment_wrap = on_every_item
+ij_typescript_binary_operation_sign_on_next_line = false
+ij_typescript_binary_operation_wrap = on_every_item
+ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
+ij_typescript_blank_lines_after_imports = 1
+ij_typescript_blank_lines_around_class = 1
+ij_typescript_blank_lines_around_field = 0
+ij_typescript_blank_lines_around_field_in_interface = 0
+ij_typescript_blank_lines_around_function = 1
+ij_typescript_blank_lines_around_method = 1
+ij_typescript_blank_lines_around_method_in_interface = 1
+ij_typescript_block_brace_style = next_line
+ij_typescript_block_comment_add_space = false
+ij_typescript_block_comment_at_first_column = true
+ij_typescript_call_parameters_new_line_after_left_paren = false
+ij_typescript_call_parameters_right_paren_on_new_line = false
+ij_typescript_call_parameters_wrap = on_every_item
+ij_typescript_catch_on_new_line = true
+ij_typescript_chained_call_dot_on_new_line = true
+ij_typescript_class_brace_style = next_line
+ij_typescript_comma_on_new_line = false
+ij_typescript_do_while_brace_force = always
+ij_typescript_else_on_new_line = false
+ij_typescript_enforce_trailing_comma = keep
+ij_typescript_enum_constants_wrap = on_every_item
+ij_typescript_extends_keyword_wrap = normal
+ij_typescript_extends_list_wrap = on_every_item
+ij_typescript_field_prefix = _
+ij_typescript_file_name_style = relaxed
+ij_typescript_finally_on_new_line = true
+ij_typescript_for_brace_force = if_multiline
+ij_typescript_for_statement_new_line_after_left_paren = false
+ij_typescript_for_statement_right_paren_on_new_line = false
+ij_typescript_for_statement_wrap = on_every_item
+ij_typescript_force_quote_style = true
+ij_typescript_force_semicolon_style = true
+ij_typescript_function_expression_brace_style = next_line
+ij_typescript_if_brace_force = never
+ij_typescript_import_merge_members = global
+ij_typescript_import_prefer_absolute_path = true
+ij_typescript_import_sort_members = true
+ij_typescript_import_sort_module_name = true
+ij_typescript_import_use_node_resolution = true
+ij_typescript_imports_wrap = on_every_item
+ij_typescript_indent_case_from_switch = true
+ij_typescript_indent_chained_calls = true
+ij_typescript_indent_package_children = 0
+ij_typescript_jsdoc_include_types = false
+ij_typescript_jsx_attribute_value = braces
+ij_typescript_keep_blank_lines_in_code = 2
+ij_typescript_keep_first_column_comment = true
+ij_typescript_keep_indents_on_empty_lines = false
+ij_typescript_keep_line_breaks = true
+ij_typescript_keep_simple_blocks_in_one_line = false
+ij_typescript_keep_simple_methods_in_one_line = false
+ij_typescript_line_comment_add_space = true
+ij_typescript_line_comment_at_first_column = false
+ij_typescript_method_brace_style = next_line
+ij_typescript_method_call_chain_wrap = on_every_item
+ij_typescript_method_parameters_new_line_after_left_paren = false
+ij_typescript_method_parameters_right_paren_on_new_line = false
+ij_typescript_method_parameters_wrap = on_every_item
+ij_typescript_object_literal_wrap = on_every_item
+ij_typescript_object_types_wrap = on_every_item
+ij_typescript_parentheses_expression_new_line_after_left_paren = false
+ij_typescript_parentheses_expression_right_paren_on_new_line = false
+ij_typescript_place_assignment_sign_on_next_line = false
+ij_typescript_prefer_as_type_cast = false
+ij_typescript_prefer_explicit_types_function_expression_returns = false
+ij_typescript_prefer_explicit_types_function_returns = false
+ij_typescript_prefer_explicit_types_vars_fields = false
+ij_typescript_prefer_parameters_wrap = false
+ij_typescript_property_prefix =
+ij_typescript_reformat_c_style_comments = false
+ij_typescript_space_after_colon = true
+ij_typescript_space_after_comma = true
+ij_typescript_space_after_dots_in_rest_parameter = false
+ij_typescript_space_after_generator_mult = true
+ij_typescript_space_after_property_colon = true
+ij_typescript_space_after_quest = true
+ij_typescript_space_after_type_colon = true
+ij_typescript_space_after_unary_not = false
+ij_typescript_space_before_async_arrow_lparen = false
+ij_typescript_space_before_catch_keyword = true
+ij_typescript_space_before_catch_left_brace = false
+ij_typescript_space_before_catch_parentheses = false
+ij_typescript_space_before_class_lbrace = false
+ij_typescript_space_before_class_left_brace = true
+ij_typescript_space_before_colon = true
+ij_typescript_space_before_comma = false
+ij_typescript_space_before_do_left_brace = false
+ij_typescript_space_before_else_keyword = true
+ij_typescript_space_before_else_left_brace = false
+ij_typescript_space_before_finally_keyword = true
+ij_typescript_space_before_finally_left_brace = false
+ij_typescript_space_before_for_left_brace = false
+ij_typescript_space_before_for_parentheses = false
+ij_typescript_space_before_for_semicolon = false
+ij_typescript_space_before_function_left_parenth = false
+ij_typescript_space_before_generator_mult = false
+ij_typescript_space_before_if_left_brace = false
+ij_typescript_space_before_if_parentheses = false
+ij_typescript_space_before_method_call_parentheses = false
+ij_typescript_space_before_method_left_brace = false
+ij_typescript_space_before_method_parentheses = false
+ij_typescript_space_before_property_colon = false
+ij_typescript_space_before_quest = true
+ij_typescript_space_before_switch_left_brace = false
+ij_typescript_space_before_switch_parentheses = false
+ij_typescript_space_before_try_left_brace = false
+ij_typescript_space_before_type_colon = false
+ij_typescript_space_before_unary_not = false
+ij_typescript_space_before_while_keyword = true
+ij_typescript_space_before_while_left_brace = false
+ij_typescript_space_before_while_parentheses = false
+ij_typescript_spaces_around_additive_operators = true
+ij_typescript_spaces_around_arrow_function_operator = true
+ij_typescript_spaces_around_assignment_operators = true
+ij_typescript_spaces_around_bitwise_operators = true
+ij_typescript_spaces_around_equality_operators = true
+ij_typescript_spaces_around_logical_operators = true
+ij_typescript_spaces_around_multiplicative_operators = true
+ij_typescript_spaces_around_relational_operators = true
+ij_typescript_spaces_around_shift_operators = true
+ij_typescript_spaces_around_unary_operator = false
+ij_typescript_spaces_within_array_initializer_brackets = false
+ij_typescript_spaces_within_brackets = false
+ij_typescript_spaces_within_catch_parentheses = false
+ij_typescript_spaces_within_for_parentheses = false
+ij_typescript_spaces_within_if_parentheses = false
+ij_typescript_spaces_within_imports = false
+ij_typescript_spaces_within_interpolation_expressions = false
+ij_typescript_spaces_within_method_call_parentheses = false
+ij_typescript_spaces_within_method_parentheses = false
+ij_typescript_spaces_within_object_literal_braces = false
+ij_typescript_spaces_within_object_type_braces = true
+ij_typescript_spaces_within_parentheses = false
+ij_typescript_spaces_within_switch_parentheses = false
+ij_typescript_spaces_within_type_assertion = false
+ij_typescript_spaces_within_union_types = true
+ij_typescript_spaces_within_while_parentheses = false
+ij_typescript_special_else_if_treatment = true
+ij_typescript_ternary_operation_signs_on_next_line = false
+ij_typescript_ternary_operation_wrap = on_every_item
+ij_typescript_union_types_wrap = on_every_item
+ij_typescript_use_chained_calls_group_indents = false
+ij_typescript_use_double_quotes = true
+ij_typescript_use_explicit_js_extension = auto
+ij_typescript_use_path_mapping = always
+ij_typescript_use_public_modifier = false
+ij_typescript_use_semicolon_after_statement = true
+ij_typescript_var_declaration_wrap = normal
+ij_typescript_while_brace_force = always
+ij_typescript_while_on_new_line = false
+ij_typescript_wrap_comments = false
+
+[{*.bash,*.sh,*.zsh}]
+indent_size = 2
+tab_width = 2
+ij_shell_binary_ops_start_line = false
+ij_shell_keep_column_alignment_padding = false
+ij_shell_minify_program = false
+ij_shell_redirect_followed_by_space = false
+ij_shell_switch_cases_indented = true
+ij_shell_use_unix_line_separator = true
+indent_style = space
+
+[{*.cjs,*.js}]
+ij_continuation_indent_size = 4
+ij_javascript_align_imports = true
+ij_javascript_align_multiline_array_initializer_expression = true
+ij_javascript_align_multiline_binary_operation = true
+ij_javascript_align_multiline_chained_methods = false
+ij_javascript_align_multiline_extends_list = true
+ij_javascript_align_multiline_for = true
+ij_javascript_align_multiline_parameters = true
+ij_javascript_align_multiline_parameters_in_calls = true
+ij_javascript_align_multiline_ternary_operation = true
+ij_javascript_align_object_properties = 1
+ij_javascript_align_union_types = false
+ij_javascript_align_var_statements = 2
+ij_javascript_array_initializer_new_line_after_left_brace = false
+ij_javascript_array_initializer_right_brace_on_new_line = false
+ij_javascript_array_initializer_wrap = on_every_item
+ij_javascript_assignment_wrap = on_every_item
+ij_javascript_binary_operation_sign_on_next_line = false
+ij_javascript_binary_operation_wrap = on_every_item
+ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/**
+ij_javascript_blank_lines_after_imports = 1
+ij_javascript_blank_lines_around_class = 1
+ij_javascript_blank_lines_around_field = 0
+ij_javascript_blank_lines_around_function = 1
+ij_javascript_blank_lines_around_method = 1
+ij_javascript_block_brace_style = next_line
+ij_javascript_block_comment_add_space = false
+ij_javascript_block_comment_at_first_column = true
+ij_javascript_call_parameters_new_line_after_left_paren = false
+ij_javascript_call_parameters_right_paren_on_new_line = false
+ij_javascript_call_parameters_wrap = on_every_item
+ij_javascript_catch_on_new_line = true
+ij_javascript_chained_call_dot_on_new_line = true
+ij_javascript_class_brace_style = next_line
+ij_javascript_comma_on_new_line = false
+ij_javascript_do_while_brace_force = always
+ij_javascript_else_on_new_line = true
+ij_javascript_enforce_trailing_comma = remove
+ij_javascript_extends_keyword_wrap = normal
+ij_javascript_extends_list_wrap = on_every_item
+ij_javascript_field_prefix = _
+ij_javascript_file_name_style = relaxed
+ij_javascript_finally_on_new_line = true
+ij_javascript_for_brace_force = if_multiline
+ij_javascript_for_statement_new_line_after_left_paren = false
+ij_javascript_for_statement_right_paren_on_new_line = false
+ij_javascript_for_statement_wrap = on_every_item
+ij_javascript_force_quote_style = true
+ij_javascript_force_semicolon_style = true
+ij_javascript_function_expression_brace_style = next_line
+ij_javascript_if_brace_force = if_multiline
+ij_javascript_import_merge_members = global
+ij_javascript_import_prefer_absolute_path = true
+ij_javascript_import_sort_members = true
+ij_javascript_import_sort_module_name = true
+ij_javascript_import_use_node_resolution = true
+ij_javascript_imports_wrap = on_every_item
+ij_javascript_indent_case_from_switch = true
+ij_javascript_indent_chained_calls = true
+ij_javascript_indent_package_children = 0
+ij_javascript_jsx_attribute_value = braces
+ij_javascript_keep_blank_lines_in_code = 2
+ij_javascript_keep_first_column_comment = true
+ij_javascript_keep_indents_on_empty_lines = false
+ij_javascript_keep_line_breaks = true
+ij_javascript_keep_simple_blocks_in_one_line = false
+ij_javascript_keep_simple_methods_in_one_line = false
+ij_javascript_line_comment_add_space = true
+ij_javascript_line_comment_at_first_column = false
+ij_javascript_method_brace_style = next_line
+ij_javascript_method_call_chain_wrap = on_every_item
+ij_javascript_method_parameters_new_line_after_left_paren = false
+ij_javascript_method_parameters_right_paren_on_new_line = false
+ij_javascript_method_parameters_wrap = on_every_item
+ij_javascript_object_literal_wrap = on_every_item
+ij_javascript_object_types_wrap = on_every_item
+ij_javascript_parentheses_expression_new_line_after_left_paren = false
+ij_javascript_parentheses_expression_right_paren_on_new_line = false
+ij_javascript_place_assignment_sign_on_next_line = true
+ij_javascript_prefer_as_type_cast = false
+ij_javascript_prefer_explicit_types_function_expression_returns = false
+ij_javascript_prefer_explicit_types_function_returns = false
+ij_javascript_prefer_explicit_types_vars_fields = false
+ij_javascript_prefer_parameters_wrap = false
+ij_javascript_property_prefix =
+ij_javascript_reformat_c_style_comments = true
+ij_javascript_space_after_colon = true
+ij_javascript_space_after_comma = true
+ij_javascript_space_after_dots_in_rest_parameter = false
+ij_javascript_space_after_generator_mult = true
+ij_javascript_space_after_property_colon = true
+ij_javascript_space_after_quest = true
+ij_javascript_space_after_type_colon = true
+ij_javascript_space_after_unary_not = false
+ij_javascript_space_before_async_arrow_lparen = false
+ij_javascript_space_before_catch_keyword = true
+ij_javascript_space_before_catch_left_brace = false
+ij_javascript_space_before_catch_parentheses = false
+ij_javascript_space_before_class_lbrace = false
+ij_javascript_space_before_class_left_brace = true
+ij_javascript_space_before_colon = true
+ij_javascript_space_before_comma = false
+ij_javascript_space_before_do_left_brace = false
+ij_javascript_space_before_else_keyword = true
+ij_javascript_space_before_else_left_brace = false
+ij_javascript_space_before_finally_keyword = true
+ij_javascript_space_before_finally_left_brace = false
+ij_javascript_space_before_for_left_brace = false
+ij_javascript_space_before_for_parentheses = false
+ij_javascript_space_before_for_semicolon = false
+ij_javascript_space_before_function_left_parenth = false
+ij_javascript_space_before_generator_mult = false
+ij_javascript_space_before_if_left_brace = false
+ij_javascript_space_before_if_parentheses = false
+ij_javascript_space_before_method_call_parentheses = false
+ij_javascript_space_before_method_left_brace = false
+ij_javascript_space_before_method_parentheses = false
+ij_javascript_space_before_property_colon = false
+ij_javascript_space_before_quest = true
+ij_javascript_space_before_switch_left_brace = false
+ij_javascript_space_before_switch_parentheses = false
+ij_javascript_space_before_try_left_brace = false
+ij_javascript_space_before_type_colon = false
+ij_javascript_space_before_unary_not = false
+ij_javascript_space_before_while_keyword = true
+ij_javascript_space_before_while_left_brace = false
+ij_javascript_space_before_while_parentheses = false
+ij_javascript_spaces_around_additive_operators = true
+ij_javascript_spaces_around_arrow_function_operator = true
+ij_javascript_spaces_around_assignment_operators = true
+ij_javascript_spaces_around_bitwise_operators = true
+ij_javascript_spaces_around_equality_operators = true
+ij_javascript_spaces_around_logical_operators = true
+ij_javascript_spaces_around_multiplicative_operators = true
+ij_javascript_spaces_around_relational_operators = true
+ij_javascript_spaces_around_shift_operators = true
+ij_javascript_spaces_around_unary_operator = false
+ij_javascript_spaces_within_array_initializer_brackets = false
+ij_javascript_spaces_within_brackets = false
+ij_javascript_spaces_within_catch_parentheses = false
+ij_javascript_spaces_within_for_parentheses = false
+ij_javascript_spaces_within_if_parentheses = false
+ij_javascript_spaces_within_imports = false
+ij_javascript_spaces_within_interpolation_expressions = false
+ij_javascript_spaces_within_method_call_parentheses = false
+ij_javascript_spaces_within_method_parentheses = false
+ij_javascript_spaces_within_object_literal_braces = false
+ij_javascript_spaces_within_object_type_braces = true
+ij_javascript_spaces_within_parentheses = false
+ij_javascript_spaces_within_switch_parentheses = false
+ij_javascript_spaces_within_type_assertion = false
+ij_javascript_spaces_within_union_types = true
+ij_javascript_spaces_within_while_parentheses = false
+ij_javascript_special_else_if_treatment = true
+ij_javascript_ternary_operation_signs_on_next_line = false
+ij_javascript_ternary_operation_wrap = on_every_item
+ij_javascript_union_types_wrap = on_every_item
+ij_javascript_use_chained_calls_group_indents = true
+ij_javascript_use_double_quotes = true
+ij_javascript_use_explicit_js_extension = auto
+ij_javascript_use_path_mapping = always
+ij_javascript_use_public_modifier = false
+ij_javascript_use_semicolon_after_statement = true
+ij_javascript_var_declaration_wrap = normal
+ij_javascript_while_brace_force = always
+ij_javascript_while_on_new_line = false
+ij_javascript_wrap_comments = false
+
+[{*.comp,*.frag,*.fsh,*.geom,*.glsl,*.tesc,*.tese,*.vert,*.vsh}]
+ij_glsl_keep_indents_on_empty_lines = false
+
+[{*.har,*.jsb2,*.jsb3,*.json,*.jsonc,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,jest.config}]
+indent_size = 2
+ij_json_array_wrapping = normal
+ij_json_keep_blank_lines_in_code = 0
+ij_json_keep_indents_on_empty_lines = false
+ij_json_keep_line_breaks = true
+ij_json_keep_trailing_comma = false
+ij_json_object_wrapping = normal
+ij_json_property_alignment = align_on_value
+ij_json_space_after_colon = true
+ij_json_space_after_comma = true
+ij_json_space_before_colon = false
+ij_json_space_before_comma = false
+ij_json_spaces_within_braces = false
+ij_json_spaces_within_brackets = false
+ij_json_wrap_long_lines = false
+indent_style = space
+
+[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
+ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
+ij_html_align_attributes = true
+ij_html_align_text = false
+ij_html_attribute_wrap = normal
+ij_html_block_comment_add_space = false
+ij_html_block_comment_at_first_column = true
+ij_html_do_not_align_children_of_min_lines = 0
+ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p
+ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot
+ij_html_enforce_quotes = false
+ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var
+ij_html_keep_blank_lines = 2
+ij_html_keep_indents_on_empty_lines = false
+ij_html_keep_line_breaks = true
+ij_html_keep_line_breaks_in_text = true
+ij_html_keep_whitespaces = false
+ij_html_keep_whitespaces_inside = span, pre, textarea
+ij_html_line_comment_at_first_column = true
+ij_html_new_line_after_last_attribute = never
+ij_html_new_line_before_first_attribute = never
+ij_html_quote_style = double
+ij_html_remove_new_line_before_tags = br
+ij_html_space_after_tag_name = false
+ij_html_space_around_equality_in_attribute = false
+ij_html_space_inside_empty_tag = false
+ij_html_text_wrap = normal
+
+[{*.http,*.rest}]
+indent_size = 0
+ij_continuation_indent_size = 4
+ij_http-request_call_parameters_wrap = normal
+ij_http-request_method_parameters_wrap = split_into_lines
+ij_http-request_space_before_comma = true
+ij_http-request_spaces_around_assignment_operators = true
+
+[{*.markdown,*.md}]
+ij_markdown_force_one_space_after_blockquote_symbol = true
+ij_markdown_force_one_space_after_header_symbol = true
+ij_markdown_force_one_space_after_list_bullet = true
+ij_markdown_force_one_space_between_words = true
+ij_markdown_format_tables = true
+ij_markdown_insert_quote_arrows_on_wrap = true
+ij_markdown_keep_indents_on_empty_lines = false
+ij_markdown_keep_line_breaks_inside_text_blocks = true
+ij_markdown_max_lines_around_block_elements = 1
+ij_markdown_max_lines_around_header = 1
+ij_markdown_max_lines_between_paragraphs = 1
+ij_markdown_min_lines_around_block_elements = 1
+ij_markdown_min_lines_around_header = 1
+ij_markdown_min_lines_between_paragraphs = 1
+ij_markdown_wrap_text_if_long = true
+ij_markdown_wrap_text_inside_blockquotes = true
+
+[{*.ps1,*.psd1,*.psm1}]
+max_line_length = 115
+ij_powershell_align_multiline_binary_operation = true
+ij_powershell_align_multiline_chained_methods = false
+ij_powershell_align_multiline_for = true
+ij_powershell_align_multiline_parameters = true
+ij_powershell_align_multiline_parameters_in_calls = false
+ij_powershell_binary_operation_wrap = on_every_item
+ij_powershell_block_brace_style = next_line
+ij_powershell_call_parameters_new_line_after_left_paren = false
+ij_powershell_call_parameters_right_paren_on_new_line = false
+ij_powershell_call_parameters_wrap = on_every_item
+ij_powershell_catch_on_new_line = true
+ij_powershell_class_annotation_wrap = split_into_lines
+ij_powershell_class_brace_style = next_line
+ij_powershell_else_on_new_line = true
+ij_powershell_field_annotation_wrap = off
+ij_powershell_finally_on_new_line = true
+ij_powershell_for_statement_new_line_after_left_paren = false
+ij_powershell_for_statement_right_paren_on_new_line = false
+ij_powershell_for_statement_wrap = on_every_item
+ij_powershell_keep_blank_lines_in_code = 2
+ij_powershell_keep_first_column_comment = true
+ij_powershell_keep_line_breaks = true
+ij_powershell_keep_simple_blocks_in_one_line = true
+ij_powershell_keep_simple_classes_in_one_line = false
+ij_powershell_keep_simple_lambdas_in_one_line = true
+ij_powershell_keep_simple_methods_in_one_line = true
+ij_powershell_method_annotation_wrap = split_into_lines
+ij_powershell_method_brace_style = next_line
+ij_powershell_method_call_chain_wrap = on_every_item
+ij_powershell_method_parameters_new_line_after_left_paren = false
+ij_powershell_method_parameters_right_paren_on_new_line = false
+ij_powershell_method_parameters_wrap = on_every_item
+ij_powershell_parameter_annotation_wrap = off
+ij_powershell_parentheses_expression_new_line_after_left_paren = false
+ij_powershell_parentheses_expression_right_paren_on_new_line = false
+ij_powershell_space_after_colon = true
+ij_powershell_space_after_comma = true
+ij_powershell_space_after_for_semicolon = true
+ij_powershell_space_after_type_cast = false
+ij_powershell_space_before_annotation_parameter_list = false
+ij_powershell_space_before_array_initializer_left_brace = false
+ij_powershell_space_before_catch_keyword = true
+ij_powershell_space_before_catch_left_brace = false
+ij_powershell_space_before_class_left_brace = false
+ij_powershell_space_before_colon = true
+ij_powershell_space_before_comma = false
+ij_powershell_space_before_do_left_brace = false
+ij_powershell_space_before_else_keyword = true
+ij_powershell_space_before_else_left_brace = false
+ij_powershell_space_before_finally_keyword = true
+ij_powershell_space_before_finally_left_brace = false
+ij_powershell_space_before_for_left_brace = false
+ij_powershell_space_before_for_parentheses = false
+ij_powershell_space_before_for_semicolon = false
+ij_powershell_space_before_if_left_brace = false
+ij_powershell_space_before_if_parentheses = false
+ij_powershell_space_before_method_call_parentheses = false
+ij_powershell_space_before_method_left_brace = false
+ij_powershell_space_before_method_parentheses = false
+ij_powershell_space_before_switch_left_brace = false
+ij_powershell_space_before_switch_parentheses = false
+ij_powershell_space_before_try_left_brace = false
+ij_powershell_space_before_while_keyword = true
+ij_powershell_space_before_while_left_brace = false
+ij_powershell_space_before_while_parentheses = false
+ij_powershell_space_within_empty_method_call_parentheses = false
+ij_powershell_space_within_empty_method_parentheses = false
+ij_powershell_spaces_around_additive_operators = true
+ij_powershell_spaces_around_assignment_operators = true
+ij_powershell_spaces_around_bitwise_operators = true
+ij_powershell_spaces_around_logical_operators = true
+ij_powershell_spaces_around_method_ref_dbl_colon = false
+ij_powershell_spaces_around_multiplicative_operators = true
+ij_powershell_spaces_around_relational_operators = true
+ij_powershell_spaces_around_unary_operator = false
+ij_powershell_spaces_within_annotation_parentheses = false
+ij_powershell_spaces_within_braces = true
+ij_powershell_spaces_within_brackets = false
+ij_powershell_spaces_within_cast_parentheses = false
+ij_powershell_spaces_within_for_parentheses = false
+ij_powershell_spaces_within_if_parentheses = false
+ij_powershell_spaces_within_method_call_parentheses = false
+ij_powershell_spaces_within_method_parentheses = false
+ij_powershell_spaces_within_parentheses = false
+ij_powershell_spaces_within_switch_parentheses = false
+ij_powershell_spaces_within_while_parentheses = false
+ij_powershell_special_else_if_treatment = true
+ij_powershell_while_on_new_line = false
+ij_powershell_wrap_first_method_in_call_chain = false
+ij_powershell_wrap_long_lines = false
+
+[{*.py,*.pyw}]
+ij_python_align_collections_and_comprehensions = true
+ij_python_align_multiline_imports = true
+ij_python_align_multiline_parameters = true
+ij_python_align_multiline_parameters_in_calls = true
+ij_python_blank_line_at_file_end = false
+ij_python_blank_lines_after_imports = 1
+ij_python_blank_lines_after_local_imports = 0
+ij_python_blank_lines_around_class = 1
+ij_python_blank_lines_around_method = 1
+ij_python_blank_lines_around_top_level_classes_functions = 2
+ij_python_blank_lines_before_first_method = 0
+ij_python_call_parameters_new_line_after_left_paren = false
+ij_python_call_parameters_right_paren_on_new_line = false
+ij_python_call_parameters_wrap = on_every_item
+ij_python_dict_alignment = 0
+ij_python_dict_new_line_after_left_brace = false
+ij_python_dict_new_line_before_right_brace = false
+ij_python_dict_wrapping = 5
+ij_python_from_import_new_line_after_left_parenthesis = false
+ij_python_from_import_new_line_before_right_parenthesis = false
+ij_python_from_import_parentheses_force_if_multiline = false
+ij_python_from_import_trailing_comma_if_multiline = false
+ij_python_from_import_wrapping = 5
+ij_python_hang_closing_brackets = true
+ij_python_keep_blank_lines_in_code = 1
+ij_python_keep_blank_lines_in_declarations = 1
+ij_python_keep_indents_on_empty_lines = false
+ij_python_keep_line_breaks = true
+ij_python_method_parameters_new_line_after_left_paren = false
+ij_python_method_parameters_right_paren_on_new_line = false
+ij_python_method_parameters_wrap = on_every_item
+ij_python_new_line_after_colon = false
+ij_python_new_line_after_colon_multi_clause = true
+ij_python_optimize_imports_always_split_from_imports = false
+ij_python_optimize_imports_case_insensitive_order = true
+ij_python_optimize_imports_join_from_imports_with_same_source = false
+ij_python_optimize_imports_sort_by_type_first = true
+ij_python_optimize_imports_sort_imports = true
+ij_python_optimize_imports_sort_names_in_from_imports = true
+ij_python_space_after_comma = true
+ij_python_space_after_number_sign = true
+ij_python_space_after_py_colon = true
+ij_python_space_before_backslash = true
+ij_python_space_before_comma = false
+ij_python_space_before_for_semicolon = false
+ij_python_space_before_lbracket = false
+ij_python_space_before_method_call_parentheses = false
+ij_python_space_before_method_parentheses = false
+ij_python_space_before_number_sign = true
+ij_python_space_before_py_colon = false
+ij_python_space_within_empty_method_call_parentheses = false
+ij_python_space_within_empty_method_parentheses = false
+ij_python_spaces_around_additive_operators = true
+ij_python_spaces_around_assignment_operators = true
+ij_python_spaces_around_bitwise_operators = true
+ij_python_spaces_around_eq_in_keyword_argument = false
+ij_python_spaces_around_eq_in_named_parameter = false
+ij_python_spaces_around_equality_operators = true
+ij_python_spaces_around_multiplicative_operators = true
+ij_python_spaces_around_power_operator = true
+ij_python_spaces_around_relational_operators = true
+ij_python_spaces_around_shift_operators = true
+ij_python_spaces_within_braces = false
+ij_python_spaces_within_brackets = false
+ij_python_spaces_within_method_call_parentheses = false
+ij_python_spaces_within_method_parentheses = false
+ij_python_use_continuation_indent_for_arguments = false
+ij_python_use_continuation_indent_for_collection_and_comprehensions = false
+ij_python_use_continuation_indent_for_parameters = true
+ij_python_wrap_long_lines = false
+
+[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}]
+ij_toml_keep_indents_on_empty_lines = false
+
+[{*.yaml,*.yml,pubspec.lock}]
+indent_size = 2
+ij_yaml_align_values_properties = on_value
+ij_yaml_autoinsert_sequence_marker = true
+ij_yaml_block_mapping_on_new_line = false
+ij_yaml_indent_sequence_value = true
+ij_yaml_keep_indents_on_empty_lines = false
+ij_yaml_keep_line_breaks = true
+ij_yaml_sequence_on_new_line = false
+ij_yaml_space_before_colon = false
+ij_yaml_spaces_within_braces = true
+ij_yaml_spaces_within_brackets = true
+indent_style = space
+
+[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}]
+indent_style = space
+indent_size = 4
+tab_width = 4
diff --git a/Aaru.Helpers/.gitignore b/Aaru.Helpers/.gitignore
new file mode 100644
index 000000000..05c540d38
--- /dev/null
+++ b/Aaru.Helpers/.gitignore
@@ -0,0 +1,595 @@
+### VisualStudio template
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+### Linux template
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+### Xcode template
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## User settings
+xcuserdata/
+
+## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
+*.xcscmblueprint
+*.xccheckout
+
+## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
+build/
+DerivedData/
+*.moved-aside
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+### VisualStudioCode template
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+### C++ template
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+# Precompiled Headers
+*.gch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+### MonoDevelop template
+#User Specific
+*.usertasks
+
+#Mono Project Files
+*.resources
+test-results/
+### GPG template
+secring.*
+
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+### CMake template
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Testing
+Makefile
+cmake_install.cmake
+install_manifest.txt
+compile_commands.json
+CTestTestfile.cmake
+### C template
+# Object files
+*.ko
+*.elf
+
+# Linker output
+*.map
+*.exp
+
+*.so.*
+
+# Executables
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+### Windows template
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# NuGet Packages Directory
+packages/
+## TODO: If the tool you use requires repositories.config uncomment the next line
+#!packages/repositories.config
+
+# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
+# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
+!packages/build/
+
+
+# Others
+sql/
+*.Cache
+
+# Visual Studio 2017
+.vs
+
+workspace.xml
+cmake-build-debug
+### macOS template
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+pkg/**/pkg
+pkg/**/src
+pkg/**/*.asc
+pkg/**/*.sig
+pkg/**/*.tar.xz
+pkg/**/*.zip
+pkg/**/aaru
+
+.sonarqube
\ No newline at end of file
diff --git a/Aaru.Helpers/Aaru.Helpers.csproj b/Aaru.Helpers/Aaru.Helpers.csproj
new file mode 100644
index 000000000..02a2e0461
--- /dev/null
+++ b/Aaru.Helpers/Aaru.Helpers.csproj
@@ -0,0 +1,60 @@
+
+
+
+ 2.0
+ {F8BDF57B-1571-4CD0-84B3-B422088D359A}
+ Library
+ Aaru.Helpers
+ Aaru.Helpers
+ $(Version)
+ true
+ 6.0.0-alpha9
+ Claunia.com
+ Copyright © 2011-2023 Natalia Portillo
+ Aaru Data Preservation Suite
+ Aaru.Helpers
+ $(Version)
+ net8.0
+ 12
+ Contains helpers used by the Aaru Data Preservation Suite.
+ https://github.com/aaru-dps/
+ LGPL-2.1-only
+ https://github.com/aaru-dps/Aaru.Helpers
+ true
+ en-US
+ true
+ true
+ snupkg
+ Natalia Portillo <claunia@claunia.com>
+ true
+ true
+
+
+ CS1591;CS1574
+
+
+
+
+
+
+ $(Version)+{chash:8}
+ true
+ true
+
+
+
+
+
+
+ LICENSE.LGPL
+
+
+ ResXFileCodeGenerator
+ Localization.Designer.cs
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Aaru.Helpers/Aaru.Helpers.csproj.DotSettings b/Aaru.Helpers/Aaru.Helpers.csproj.DotSettings
new file mode 100644
index 000000000..67ed7f8db
--- /dev/null
+++ b/Aaru.Helpers/Aaru.Helpers.csproj.DotSettings
@@ -0,0 +1,5 @@
+
+ True
\ No newline at end of file
diff --git a/Aaru.Helpers/ArrayFill.cs b/Aaru.Helpers/ArrayFill.cs
new file mode 100644
index 000000000..cbac721e8
--- /dev/null
+++ b/Aaru.Helpers/ArrayFill.cs
@@ -0,0 +1,79 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : ArrayFill.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Fills an array with a specified value.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// No license specified by creator.
+//
+// Published on https://github.com/mykohsu/Extensions/blob/master/ArrayExtensions.cs
+//
+// Assuming open source.
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// Copyright(C) 2014 mykohsu
+// ****************************************************************************/
+
+using System;
+using System.Text;
+
+namespace Aaru.Helpers;
+
+public static partial class ArrayHelpers
+{
+ /// Fills an array with the specified value
+ /// Array
+ /// Value
+ /// Array type
+ public static void ArrayFill(T[] destinationArray, T value) => ArrayFill(destinationArray, new[]
+ {
+ value
+ });
+
+ /// Fills an array with the contents of the specified array
+ /// Array
+ /// Value
+ /// Array type
+ public static void ArrayFill(T[] destinationArray, T[] value)
+ {
+ ArgumentNullException.ThrowIfNull(destinationArray);
+
+ if(value.Length > destinationArray.Length)
+ throw new ArgumentException(Localization.Length_of_value_array_must_not_be_more_than_length_of_destination);
+
+ // set the initial array value
+ Array.Copy(value, destinationArray, value.Length);
+
+ int arrayToFillHalfLength = destinationArray.Length / 2;
+ int copyLength;
+
+ for(copyLength = value.Length; copyLength < arrayToFillHalfLength; copyLength <<= 1)
+ Array.Copy(destinationArray, 0, destinationArray, copyLength, copyLength);
+
+ Array.Copy(destinationArray, 0, destinationArray, copyLength, destinationArray.Length - copyLength);
+ }
+
+ /// Converts a byte array to its hexadecimal representation
+ /// Byte array
+ /// true to use uppercase
+ ///
+ public static string ByteArrayToHex(byte[] array, bool upper = false)
+ {
+ var sb = new StringBuilder();
+
+ for(long i = 0; i < array.LongLength; i++)
+ sb.Append($"{array[i]:x2}");
+
+ return upper ? sb.ToString().ToUpper() : sb.ToString();
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/ArrayIsEmpty.cs b/Aaru.Helpers/ArrayIsEmpty.cs
new file mode 100644
index 000000000..aa8302bfe
--- /dev/null
+++ b/Aaru.Helpers/ArrayIsEmpty.cs
@@ -0,0 +1,49 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : ArrayIsEmpty.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Methods for detecting an empty array.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System.Linq;
+
+namespace Aaru.Helpers;
+
+/// Helper operations to work with arrays
+public static partial class ArrayHelpers
+{
+ /// Checks if an array is null, filled with the NULL byte (0x00) or ASCII whitespace (0x20)
+ /// Array
+ /// True if null or whitespace
+ public static bool ArrayIsNullOrWhiteSpace(byte[] array) => array?.All(b => b is 0x00 or 0x20) != false;
+
+ /// Checks if an array is null or filled with the NULL byte (0x00)
+ /// Array
+ /// True if null
+ public static bool ArrayIsNullOrEmpty(byte[] array) => array?.All(b => b == 0x00) != false;
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/BigEndianBitConverter.cs b/Aaru.Helpers/BigEndianBitConverter.cs
new file mode 100644
index 000000000..cb1225bea
--- /dev/null
+++ b/Aaru.Helpers/BigEndianBitConverter.cs
@@ -0,0 +1,323 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : BigEndianBitConverter.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Override of System.BitConverter that knows how to handle big-endian.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Aaru.Helpers;
+
+///
+/// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from
+/// the meta data of System.BitConverter. This implementation allows for Endianness consideration.
+///
+[SuppressMessage("ReSharper", "UnusedParameter.Global")]
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public static class BigEndianBitConverter
+{
+ /// Converts the specified double-precision floating point number to a 64-bit signed integer.
+ /// The number to convert.
+ /// A 64-bit signed integer whose value is equivalent to value.
+ /// It is not currently implemented
+ public static long DoubleToInt64Bits(double value) => throw new NotImplementedException();
+
+ /// Returns the specified Boolean value as an array of bytes.
+ /// A Boolean value.
+ /// An array of bytes with length 1.
+ public static byte[] GetBytes(bool value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified Unicode character value as an array of bytes.
+ /// A character to convert.
+ /// An array of bytes with length 2.
+ public static byte[] GetBytes(char value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified double-precision floating point value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 8.
+ public static byte[] GetBytes(double value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified single-precision floating point value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 4.
+ public static byte[] GetBytes(float value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified 32-bit signed integer value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 4.
+ public static byte[] GetBytes(int value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified 64-bit signed integer value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 8.
+ public static byte[] GetBytes(long value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified 16-bit signed integer value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 2.
+ public static byte[] GetBytes(short value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified 32-bit unsigned integer value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 4.
+ public static byte[] GetBytes(uint value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified 64-bit unsigned integer value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 8.
+ public static byte[] GetBytes(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Returns the specified 16-bit unsigned integer value as an array of bytes.
+ /// The number to convert.
+ /// An array of bytes with length 2.
+ public static byte[] GetBytes(ushort value) => BitConverter.GetBytes(value).Reverse().ToArray();
+
+ /// Converts the specified 64-bit signed integer to a double-precision floating point number.
+ /// The number to convert.
+ /// A double-precision floating point number whose value is equivalent to value.
+ public static double Int64BitsToDouble(long value) => throw new NotImplementedException();
+
+ /// Returns a Boolean value converted from one byte at a specified position in a byte array.
+ /// An array of bytes.
+ /// The starting position within value.
+ /// true if the byte at in value is nonzero; otherwise, false.
+ /// value is null.
+ ///
+ /// is less than zero or greater than the
+ /// length of value minus 1.
+ ///
+ public static bool ToBoolean(byte[] value, int startIndex) => throw new NotImplementedException();
+
+ /// Returns a Unicode character converted from two bytes at a specified position in a byte array.
+ /// An array.
+ /// The starting position within value.
+ /// A character formed by two bytes beginning at .
+ /// equals the length of value minus 1.
+ /// value is null.
+ ///
+ /// is less than zero or greater than the
+ /// length of value minus 1.
+ ///
+ public static char ToChar(byte[] value, int startIndex) => throw new NotImplementedException();
+
+ ///
+ /// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte
+ /// array.
+ ///
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A double precision floating point number formed by eight bytes beginning at .
+ ///
+ /// is greater than or equal to the length of value
+ /// minus 7, and is less than or equal to the length of value minus 1.
+ ///
+ /// value is null.
+ ///
+ /// is less than zero or greater than the
+ /// length of value minus 1.
+ ///
+ public static double ToDouble(byte[] value, int startIndex) => throw new NotImplementedException();
+
+ /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A 16-bit signed integer formed by two bytes beginning at .
+ /// equals the length of value minus 1.
+ /// value is null.
+ ///
+ /// startIndex is less than zero or greater than the length of value
+ /// minus 1.
+ ///
+ public static short ToInt16(byte[] value, int startIndex) =>
+ BitConverter.ToInt16(value.Reverse().ToArray(), value.Length - sizeof(short) - startIndex);
+
+ /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A 32-bit signed integer formed by four bytes beginning at .
+ ///
+ /// is greater than or equal to the length of value
+ /// minus 3, and is less than or equal to the length of value minus 1.
+ ///
+ /// value is null.
+ ///
+ /// startIndex is less than zero or greater than the length of value
+ /// minus 1.
+ ///
+ public static int ToInt32(byte[] value, int startIndex) =>
+ BitConverter.ToInt32(value.Reverse().ToArray(), value.Length - sizeof(int) - startIndex);
+
+ /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A 64-bit signed integer formed by eight bytes beginning at .
+ ///
+ /// is greater than or equal to the length of value
+ /// minus 7, and is less than or equal to the length of value minus 1.
+ ///
+ /// value is null.
+ ///
+ /// is less than zero or greater than the
+ /// length of value minus 1.
+ ///
+ public static long ToInt64(byte[] value, int startIndex) =>
+ BitConverter.ToInt64(value.Reverse().ToArray(), value.Length - sizeof(long) - startIndex);
+
+ ///
+ /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte
+ /// array.
+ ///
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A single-precision floating point number formed by four bytes beginning at .
+ ///
+ /// is greater than or equal to the length of value
+ /// minus 3, and is less than or equal to the length of value minus 1.
+ ///
+ /// value is null.
+ ///
+ /// is less than zero or greater than the
+ /// length of value minus 1.
+ ///
+ public static float ToSingle(byte[] value, int startIndex) =>
+ BitConverter.ToSingle(value.Reverse().ToArray(), value.Length - sizeof(float) - startIndex);
+
+ ///
+ /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string
+ /// representation.
+ ///
+ /// An array of bytes.
+ ///
+ /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding
+ /// element in value; for example, "7F-2C-4A".
+ ///
+ /// value is null.
+ public static string ToString(byte[] value) => BitConverter.ToString(value.Reverse().ToArray());
+
+ ///
+ /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal
+ /// string representation.
+ ///
+ /// An array of bytes.
+ /// The starting position within value.
+ ///
+ /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding
+ /// element in a subarray of value; for example, "7F-2C-4A".
+ ///
+ /// value is null.
+ ///
+ /// startIndex is less than zero or greater than the length of value
+ /// minus 1.
+ ///
+ public static string ToString(byte[] value, int startIndex) =>
+ BitConverter.ToString(value.Reverse().ToArray(), startIndex);
+
+ ///
+ /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal
+ /// string representation.
+ ///
+ /// An array of bytes.
+ /// The starting position within value.
+ /// The number of array elements in value to convert.
+ ///
+ /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding
+ /// element in a subarray of value; for example, "7F-2C-4A".
+ ///
+ /// value is null.
+ ///
+ /// startIndex or length is less than zero. -or- startIndex is greater
+ /// than zero and is greater than or equal to the length of value.
+ ///
+ ///
+ /// The combination of startIndex and length does not specify a position within
+ /// value; that is, the startIndex parameter is greater than the length of value minus the length parameter.
+ ///
+ public static string ToString(byte[] value, int startIndex, int length) =>
+ BitConverter.ToString(value.Reverse().ToArray(), startIndex, length);
+
+ /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
+ /// The array of bytes.
+ /// The starting position within value.
+ /// A 16-bit unsigned integer formed by two bytes beginning at startIndex.
+ /// startIndex equals the length of value minus 1.
+ /// value is null.
+ ///
+ /// startIndex is less than zero or greater than the length of value
+ /// minus 1.
+ ///
+ public static ushort ToUInt16(byte[] value, int startIndex) =>
+ BitConverter.ToUInt16(value.Reverse().ToArray(), value.Length - sizeof(ushort) - startIndex);
+
+ /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A 32-bit unsigned integer formed by four bytes beginning at startIndex.
+ ///
+ /// startIndex is greater than or equal to the length of value minus 3, and is
+ /// less than or equal to the length of value minus 1.
+ ///
+ /// value is null.
+ ///
+ /// startIndex is less than zero or greater than the length of value
+ /// minus 1.
+ ///
+ public static uint ToUInt32(byte[] value, int startIndex) =>
+ BitConverter.ToUInt32(value.Reverse().ToArray(), value.Length - sizeof(uint) - startIndex);
+
+ /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.
+ /// An array of bytes.
+ /// The starting position within value.
+ /// A 64-bit unsigned integer formed by the eight bytes beginning at startIndex.
+ ///
+ /// startIndex is greater than or equal to the length of value minus 7, and is
+ /// less than or equal to the length of value minus 1.
+ ///
+ /// value is null.
+ ///
+ /// startIndex is less than zero or greater than the length of value
+ /// minus 1.
+ ///
+ public static ulong ToUInt64(byte[] value, int startIndex) =>
+ BitConverter.ToUInt64(value.Reverse().ToArray(), value.Length - sizeof(ulong) - startIndex);
+
+ /// Converts a big endian byte array representation of a GUID into the .NET Guid structure
+ /// Byte array containing a GUID in big endian
+ /// Start of the byte array to process
+ /// Processed Guid
+ public static Guid ToGuid(byte[] value, int startIndex) => new(ToUInt32(value, 0 + startIndex),
+ ToUInt16(value, 4 + startIndex),
+ ToUInt16(value, 6 + startIndex),
+ value[8 + startIndex + 0], value[8 + startIndex + 1],
+ value[8 + startIndex + 2], value[8 + startIndex + 3],
+ value[8 + startIndex + 5], value[8 + startIndex + 5],
+ value[8 + startIndex + 6],
+ value[8 + startIndex + 7]);
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/BitEndian.cs b/Aaru.Helpers/BitEndian.cs
new file mode 100644
index 000000000..72ae6fdf4
--- /dev/null
+++ b/Aaru.Helpers/BitEndian.cs
@@ -0,0 +1,50 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : BitEndian.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Common types.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Defines enumerations of bit endianness.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+namespace Aaru.Helpers;
+
+/// Describes the endianness of bits on a data structure
+public enum BitEndian
+{
+ /// Little-endian, or least significant bit
+ Little,
+ /// Big-endian, or most significant bit
+ Big,
+ /// PDP-11 endian, little endian except for 32-bit integers where the 16 halves are swapped between them
+ Pdp
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/CHS.cs b/Aaru.Helpers/CHS.cs
new file mode 100644
index 000000000..d13c80b22
--- /dev/null
+++ b/Aaru.Helpers/CHS.cs
@@ -0,0 +1,49 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : CHS.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Helpers for CHS<->LBA conversions
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+namespace Aaru.Helpers;
+
+/// Helper operations to work with CHS values
+public static class CHS
+{
+ /// Converts a CHS position to a LBA one
+ /// Cylinder
+ /// Head
+ /// Sector
+ /// Number of heads
+ /// Number of sectors per track
+ ///
+ public static uint ToLBA(uint cyl, uint head, uint sector, uint maxHead, uint maxSector) =>
+ maxHead == 0 || maxSector == 0
+ ? (cyl * 16 + head) * 63 + sector - 1
+ : (cyl * maxHead + head) * maxSector + sector - 1;
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/CompareBytes.cs b/Aaru.Helpers/CompareBytes.cs
new file mode 100644
index 000000000..a06aaed9f
--- /dev/null
+++ b/Aaru.Helpers/CompareBytes.cs
@@ -0,0 +1,72 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : CompareBytes.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Compares two byte arrays.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+namespace Aaru.Helpers;
+
+public static partial class ArrayHelpers
+{
+ /// Compares two byte arrays
+ /// true if they are different in any way
+ /// true if they have the same size
+ /// Left array
+ /// Right array
+ public static void CompareBytes(out bool different, out bool sameSize, byte[] compareArray1, byte[] compareArray2)
+ {
+ different = false;
+ sameSize = true;
+
+ long leastBytes;
+
+ if(compareArray1.LongLength < compareArray2.LongLength)
+ {
+ sameSize = false;
+ leastBytes = compareArray1.LongLength;
+ }
+ else if(compareArray1.LongLength > compareArray2.LongLength)
+ {
+ sameSize = false;
+ leastBytes = compareArray2.LongLength;
+ }
+ else
+ leastBytes = compareArray1.LongLength;
+
+ for(long i = 0; i < leastBytes; i++)
+ {
+ if(compareArray1[i] == compareArray2[i])
+ continue;
+
+ different = true;
+
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/CountBits.cs b/Aaru.Helpers/CountBits.cs
new file mode 100644
index 000000000..44b81526c
--- /dev/null
+++ b/Aaru.Helpers/CountBits.cs
@@ -0,0 +1,48 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : CountBits.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Counts bits in a number.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+namespace Aaru.Helpers;
+
+/// Helper operations to count bits
+public static class CountBits
+{
+ /// Counts the number of bits set to true in a number
+ /// Number
+ /// Bits set to true
+ public static int Count(uint number)
+ {
+ number -= number >> 1 & 0x55555555;
+ number = (number & 0x33333333) + (number >> 2 & 0x33333333);
+
+ return (int)((number + (number >> 4) & 0x0F0F0F0F) * 0x01010101 >> 24);
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/DateHandlers.cs b/Aaru.Helpers/DateHandlers.cs
new file mode 100644
index 000000000..ba80926df
--- /dev/null
+++ b/Aaru.Helpers/DateHandlers.cs
@@ -0,0 +1,386 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : DateHandlers.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Convert several timestamp formats to C# DateTime.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.Text;
+using Aaru.Console;
+
+namespace Aaru.Helpers;
+
+/// Helper operations for timestamp management (date and time)
+public static class DateHandlers
+{
+ const string ISO9660_MODULE_NAME = "ISO9600ToDateTime handler";
+ const string PASCAL_MODULE_NAME = "UCSDPascalToDateTime handler";
+ const string DOS_MODULE_NAME = "DOSToDateTime handler";
+ static readonly DateTime _lisaEpoch = new(1901, 1, 1, 0, 0, 0);
+ static readonly DateTime _macEpoch = new(1904, 1, 1, 0, 0, 0);
+ static readonly DateTime _unixEpoch = new(1970, 1, 1, 0, 0, 0);
+ /// Day 0 of Julian Date system
+ static readonly DateTime _julianEpoch = new(1858, 11, 17, 0, 0, 0);
+ static readonly DateTime _amigaEpoch = new(1978, 1, 1, 0, 0, 0);
+
+ /// Converts a Macintosh timestamp to a .NET DateTime
+ /// Macintosh timestamp (seconds since 1st Jan. 1904)
+ /// .NET DateTime
+ public static DateTime MacToDateTime(ulong macTimeStamp) => _macEpoch.AddTicks((long)(macTimeStamp * 10000000));
+
+ /// Converts a Lisa timestamp to a .NET DateTime
+ /// Lisa timestamp (seconds since 1st Jan. 1901)
+ /// .NET DateTime
+ public static DateTime LisaToDateTime(uint lisaTimeStamp) => _lisaEpoch.AddSeconds(lisaTimeStamp);
+
+ /// Converts a UNIX timestamp to a .NET DateTime
+ /// UNIX timestamp (seconds since 1st Jan. 1970)
+ /// .NET DateTime
+ public static DateTime UnixToDateTime(int unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
+
+ /// Converts a UNIX timestamp to a .NET DateTime
+ /// UNIX timestamp (seconds since 1st Jan. 1970)
+ /// .NET DateTime
+ public static DateTime UnixToDateTime(long unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
+
+ /// Converts a UNIX timestamp to a .NET DateTime
+ /// UNIX timestamp (seconds since 1st Jan. 1970)
+ /// .NET DateTime
+ public static DateTime UnixUnsignedToDateTime(uint unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
+
+ /// Converts a UNIX timestamp to a .NET DateTime
+ /// Seconds since 1st Jan. 1970)
+ /// Nanoseconds
+ /// .NET DateTime
+ public static DateTime UnixUnsignedToDateTime(uint seconds, uint nanoseconds) =>
+ _unixEpoch.AddSeconds(seconds).AddTicks((long)nanoseconds / 100);
+
+ /// Converts a UNIX timestamp to a .NET DateTime
+ /// UNIX timestamp (seconds since 1st Jan. 1970)
+ /// .NET DateTime
+ public static DateTime UnixUnsignedToDateTime(ulong unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp);
+
+ /// Converts a High Sierra Format timestamp to a .NET DateTime
+ /// High Sierra Format timestamp
+ /// .NET DateTime
+ public static DateTime HighSierraToDateTime(byte[] vdDateTime)
+ {
+ var isoTime = new byte[17];
+ Array.Copy(vdDateTime, 0, isoTime, 0, 16);
+
+ return Iso9660ToDateTime(isoTime);
+ }
+
+ // TODO: Timezone
+ /// Converts an ISO9660 timestamp to a .NET DateTime
+ /// ISO9660 timestamp
+ /// .NET DateTime
+ public static DateTime Iso9660ToDateTime(byte[] vdDateTime)
+ {
+ var twoCharValue = new byte[2];
+ var fourCharValue = new byte[4];
+
+ fourCharValue[0] = vdDateTime[0];
+ fourCharValue[1] = vdDateTime[1];
+ fourCharValue[2] = vdDateTime[2];
+ fourCharValue[3] = vdDateTime[3];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "year = \"{0}\"",
+ StringHandlers.CToString(fourCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(fourCharValue, Encoding.ASCII), out int year))
+ year = 0;
+
+ twoCharValue[0] = vdDateTime[4];
+ twoCharValue[1] = vdDateTime[5];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "month = \"{0}\"",
+ StringHandlers.CToString(twoCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int month))
+ month = 0;
+
+ twoCharValue[0] = vdDateTime[6];
+ twoCharValue[1] = vdDateTime[7];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "day = \"{0}\"",
+ StringHandlers.CToString(twoCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int day))
+ day = 0;
+
+ twoCharValue[0] = vdDateTime[8];
+ twoCharValue[1] = vdDateTime[9];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "hour = \"{0}\"",
+ StringHandlers.CToString(twoCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hour))
+ hour = 0;
+
+ twoCharValue[0] = vdDateTime[10];
+ twoCharValue[1] = vdDateTime[11];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "minute = \"{0}\"",
+ StringHandlers.CToString(twoCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int minute))
+ minute = 0;
+
+ twoCharValue[0] = vdDateTime[12];
+ twoCharValue[1] = vdDateTime[13];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "second = \"{0}\"",
+ StringHandlers.CToString(twoCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int second))
+ second = 0;
+
+ twoCharValue[0] = vdDateTime[14];
+ twoCharValue[1] = vdDateTime[15];
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME, "hundredths = \"{0}\"",
+ StringHandlers.CToString(twoCharValue, Encoding.ASCII));
+
+ if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hundredths))
+ hundredths = 0;
+
+ AaruConsole.DebugWriteLine(ISO9660_MODULE_NAME,
+ "decodedDT = new DateTime({0}, {1}, {2}, {3}, {4}, {5}, {6}, DateTimeKind.Unspecified);",
+ year, month, day, hour, minute, second, hundredths * 10);
+
+ var difference = (sbyte)vdDateTime[16];
+
+ var decodedDt = new DateTime(year, month, day, hour, minute, second, hundredths * 10, DateTimeKind.Utc);
+
+ return decodedDt.AddMinutes(difference * -15);
+ }
+
+ /// Converts a VMS timestamp to a .NET DateTime
+ /// VMS timestamp (tenths of microseconds since day 0 of the Julian Date)
+ /// .NET DateTime
+ /// C# works in UTC, VMS on Julian Date, some displacement may occur on disks created outside UTC
+ public static DateTime VmsToDateTime(ulong vmsDate)
+ {
+ double delta = vmsDate * 0.0001; // Tenths of microseconds to milliseconds, will lose some detail
+
+ return _julianEpoch.AddMilliseconds(delta);
+ }
+
+ /// Converts an Amiga timestamp to a .NET DateTime
+ /// Days since the 1st Jan. 1978
+ /// Minutes since o'clock
+ /// Ticks
+ /// .NET DateTime
+ public static DateTime AmigaToDateTime(uint days, uint minutes, uint ticks)
+ {
+ DateTime temp = _amigaEpoch.AddDays(days);
+ temp = temp.AddMinutes(minutes);
+
+ return temp.AddMilliseconds(ticks * 20);
+ }
+
+ /// Converts an UCSD Pascal timestamp to a .NET DateTime
+ /// UCSD Pascal timestamp
+ /// .NET DateTime
+ public static DateTime UcsdPascalToDateTime(short dateRecord)
+ {
+ int year = ((dateRecord & 0xFE00) >> 9) + 1900;
+ int day = (dateRecord & 0x01F0) >> 4;
+ int month = dateRecord & 0x000F;
+
+ AaruConsole.DebugWriteLine(PASCAL_MODULE_NAME, "dateRecord = 0x{0:X4}, year = {1}, month = {2}, day = {3}",
+ dateRecord, year, month, day);
+
+ return new DateTime(year, month, day);
+ }
+
+ /// Converts a DOS timestamp to a .NET DateTime
+ /// Date
+ /// Time
+ /// .NET DateTime
+ public static DateTime DosToDateTime(ushort date, ushort time)
+ {
+ int year = ((date & 0xFE00) >> 9) + 1980;
+ int month = (date & 0x1E0) >> 5;
+ int day = date & 0x1F;
+ int hour = (time & 0xF800) >> 11;
+ int minute = (time & 0x7E0) >> 5;
+ int second = (time & 0x1F) * 2;
+
+ AaruConsole.DebugWriteLine(DOS_MODULE_NAME, "date = 0x{0:X4}, year = {1}, month = {2}, day = {3}", date, year,
+ month, day);
+
+ AaruConsole.DebugWriteLine(DOS_MODULE_NAME, "time = 0x{0:X4}, hour = {1}, minute = {2}, second = {3}", time,
+ hour, minute, second);
+
+ DateTime dosDate;
+
+ try
+ {
+ dosDate = new DateTime(year, month, day, hour, minute, second);
+ }
+ catch(ArgumentOutOfRangeException)
+ {
+ dosDate = new DateTime(1980, 1, 1, 0, 0, 0);
+ }
+
+ return dosDate;
+ }
+
+ /// Converts a CP/M timestamp to .NET DateTime
+ /// CP/M timestamp
+ /// .NET DateTime
+ public static DateTime CpmToDateTime(byte[] timestamp)
+ {
+ var days = BitConverter.ToUInt16(timestamp, 0);
+ int hours = timestamp[2];
+ int minutes = timestamp[3];
+
+ DateTime temp = _amigaEpoch.AddDays(days);
+ temp = temp.AddHours(hours);
+ temp = temp.AddMinutes(minutes);
+
+ return temp;
+ }
+
+ /// Converts an ECMA timestamp to a .NET DateTime
+ /// Timezone
+ /// Year
+ /// Month
+ /// Day
+ /// Hour
+ /// Minute
+ /// Second
+ /// Centiseconds
+ /// Hundreds of microseconds
+ /// Microseconds
+ ///
+ public static DateTime EcmaToDateTime(ushort typeAndTimeZone, short year, byte month, byte day, byte hour,
+ byte minute, byte second, byte centiseconds, byte hundredsOfMicroseconds,
+ byte microseconds)
+ {
+ var specification = (byte)((typeAndTimeZone & 0xF000) >> 12);
+
+ long ticks = (long)centiseconds * 100000 + (long)hundredsOfMicroseconds * 1000 + (long)microseconds * 10;
+
+ if(specification == 0)
+ return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc).AddTicks(ticks);
+
+ var preOffset = (ushort)(typeAndTimeZone & 0xFFF);
+ short offset;
+
+ if((preOffset & 0x800) == 0x800)
+ offset = (short)(preOffset | 0xF000);
+ else
+ offset = (short)(preOffset & 0x7FF);
+
+ switch(offset)
+ {
+ case -2047:
+ return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified).AddTicks(ticks);
+ case < -1440 or > 1440:
+ offset = 0;
+
+ break;
+ }
+
+ return new DateTimeOffset(year, month, day, hour, minute, second, new TimeSpan(0, offset, 0)).AddTicks(ticks).
+ DateTime;
+ }
+
+ /// Converts a Solaris high resolution timestamp to .NET DateTime
+ /// Solaris high resolution timestamp
+ /// .NET DateTime
+ public static DateTime UnixHrTimeToDateTime(ulong hrTimeStamp) => _unixEpoch.AddTicks((long)(hrTimeStamp / 100));
+
+ /// Converts an OS-9 timestamp to .NET DateTime
+ /// OS-9 timestamp
+ /// .NET DateTime
+ public static DateTime Os9ToDateTime(byte[] date)
+ {
+ if(date == null || date.Length != 3 && date.Length != 5)
+ return DateTime.MinValue;
+
+ DateTime os9Date;
+
+ try
+ {
+ os9Date = date.Length == 5
+ ? new DateTime(1900 + date[0], date[1], date[2], date[3], date[4], 0)
+ : new DateTime(1900 + date[0], date[1], date[2], 0, 0, 0);
+ }
+ catch(ArgumentOutOfRangeException)
+ {
+ os9Date = new DateTime(1900, 0, 0, 0, 0, 0);
+ }
+
+ return os9Date;
+ }
+
+ /// Converts a LIF timestamp to .NET DateTime
+ /// LIF timestamp
+ /// .NET DateTime
+ public static DateTime LifToDateTime(byte[] date) => date is not { Length: 6 }
+ ? new DateTime(1970, 1, 1, 0, 0, 0)
+ : LifToDateTime(date[0], date[1], date[2], date[3],
+ date[4], date[5]);
+
+ /// Converts a LIF timestamp to .NET DateTime
+ /// Yer
+ /// Month
+ /// Day
+ /// Hour
+ /// Minute
+ /// Second
+ /// .NET DateTime
+ public static DateTime LifToDateTime(byte year, byte month, byte day, byte hour, byte minute, byte second)
+ {
+ try
+ {
+ int iyear = (year >> 4) * 10 + (year & 0xF);
+ int imonth = (month >> 4) * 10 + (month & 0xF);
+ int iday = (day >> 4) * 10 + (day & 0xF);
+ int iminute = (minute >> 4) * 10 + (minute & 0xF);
+ int ihour = (hour >> 4) * 10 + (hour & 0xF);
+ int isecond = (second >> 4) * 10 + (second & 0xF);
+
+ if(iyear >= 70)
+ iyear += 1900;
+ else
+ iyear += 2000;
+
+ return new DateTime(iyear, imonth, iday, ihour, iminute, isecond);
+ }
+ catch(ArgumentOutOfRangeException)
+ {
+ return new DateTime(1970, 1, 1, 0, 0, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/Extensions.cs b/Aaru.Helpers/Extensions.cs
new file mode 100644
index 000000000..c48b97066
--- /dev/null
+++ b/Aaru.Helpers/Extensions.cs
@@ -0,0 +1,72 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : Extensions.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Provides class extensions.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System.IO;
+
+namespace Aaru.Helpers;
+
+public static class Extensions
+{
+ ///
+ /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the
+ /// position within the stream by the number of bytes read.
Guarantees the whole count of bytes is read or EOF is
+ /// found
+ ///
+ /// Stream to extend
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the
+ /// values between and ( + - 1) replaced by the bytes
+ /// read from the current source.
+ ///
+ ///
+ /// The zero-based byte offset in at which to begin storing the data read from
+ /// the current stream.
+ ///
+ /// The maximum number of bytes to be read from the current stream.
+ ///
+ /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if the end
+ /// of the stream has been reached.
+ ///
+ public static int EnsureRead(this Stream s, byte[] buffer, int offset, int count)
+ {
+ var pos = 0;
+ int read;
+
+ do
+ {
+ read = s.Read(buffer, pos + offset, count - pos);
+ pos += read;
+ } while(read > 0);
+
+ return pos;
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/IO/ForcedSeekStream.cs b/Aaru.Helpers/IO/ForcedSeekStream.cs
new file mode 100644
index 000000000..fd48c2f49
--- /dev/null
+++ b/Aaru.Helpers/IO/ForcedSeekStream.cs
@@ -0,0 +1,257 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : ForcedSeekStream.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Filters.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Provides a seekable stream from a forward-readable stream.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.IO;
+
+namespace Aaru.Helpers.IO;
+
+///
+/// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the
+/// slow and known trick of rewinding and forward reading until arriving the desired position.
+///
+///
+public sealed class ForcedSeekStream : Stream where T : Stream
+{
+ const int BUFFER_LEN = 1048576;
+ readonly string _backFile;
+ readonly FileStream _backStream;
+ readonly T _baseStream;
+ long _streamLength;
+
+ /// Initializes a new instance of the class.
+ /// The real (uncompressed) length of the stream.
+ /// Parameters that are used to create the base stream.
+ ///
+ public ForcedSeekStream(long length, params object[] args)
+ {
+ _streamLength = length;
+ _baseStream = (T)Activator.CreateInstance(typeof(T), args);
+ _backFile = Path.GetTempFileName();
+ _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+
+ if(length == 0)
+ CalculateLength();
+ }
+
+ /// Initializes a new instance of the class.
+ /// Parameters that are used to create the base stream.
+ ///
+ public ForcedSeekStream(params object[] args)
+ {
+ _baseStream = (T)Activator.CreateInstance(typeof(T), args);
+ _backFile = Path.GetTempFileName();
+ _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
+ CalculateLength();
+ }
+
+ ///
+ public override bool CanRead => _baseStream.CanRead;
+
+ ///
+ public override bool CanSeek => true;
+
+ ///
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => _streamLength;
+
+ ///
+ public override long Position
+ {
+ get => _backStream.Position;
+
+ set => SetPosition(value);
+ }
+
+ ///
+ /// Calculates the real (uncompressed) length of the stream. It basically reads (uncompresses) the whole stream to
+ /// memory discarding its contents, so it should be used as a last resort.
+ ///
+ /// The length.
+ public void CalculateLength()
+ {
+ int read;
+
+ do
+ {
+ var buffer = new byte[BUFFER_LEN];
+ read = _baseStream.EnsureRead(buffer, 0, BUFFER_LEN);
+ _backStream.Write(buffer, 0, read);
+ } while(read == BUFFER_LEN);
+
+ _streamLength = _backStream.Length;
+ _backStream.Position = 0;
+ }
+
+ void SetPosition(long position)
+ {
+ if(position == _backStream.Position)
+ return;
+
+ if(position < _backStream.Length)
+ {
+ _backStream.Position = position;
+
+ return;
+ }
+
+ if(position > _streamLength)
+ position = _streamLength;
+
+ _backStream.Position = _backStream.Length;
+ long toPosition = position - _backStream.Position;
+ var fullBufferReads = (int)(toPosition / BUFFER_LEN);
+ var restToRead = (int)(toPosition % BUFFER_LEN);
+ byte[] buffer;
+ int bufPos;
+ int left;
+
+ for(var i = 0; i < fullBufferReads; i++)
+ {
+ buffer = new byte[BUFFER_LEN];
+ bufPos = 0;
+ left = BUFFER_LEN;
+
+ while(left > 0)
+ {
+ int done = _baseStream.EnsureRead(buffer, bufPos, left);
+ left -= done;
+ bufPos += done;
+ }
+
+ _backStream.Write(buffer, 0, BUFFER_LEN);
+ }
+
+ buffer = new byte[restToRead];
+ bufPos = 0;
+ left = restToRead;
+
+ while(left > 0)
+ {
+ int done = _baseStream.EnsureRead(buffer, bufPos, left);
+ left -= done;
+ bufPos += done;
+ }
+
+ _backStream.Write(buffer, 0, restToRead);
+ }
+
+ ///
+ public override void Flush()
+ {
+ _baseStream.Flush();
+ _backStream.Flush();
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if(_backStream.Position + count > _streamLength)
+ count = (int)(_streamLength - _backStream.Position);
+
+ if(_backStream.Position + count <= _backStream.Length)
+ return _backStream.EnsureRead(buffer, offset, count);
+
+ long oldPosition = _backStream.Position;
+ SetPosition(_backStream.Position + count);
+ SetPosition(oldPosition);
+
+ return _backStream.EnsureRead(buffer, offset, count);
+ }
+
+ ///
+ public override int ReadByte()
+ {
+ if(_backStream.Position + 1 > _streamLength)
+ return -1;
+
+ if(_backStream.Position + 1 <= _backStream.Length)
+ return _backStream.ReadByte();
+
+ SetPosition(_backStream.Position + 1);
+ SetPosition(_backStream.Position - 1);
+
+ return _backStream.ReadByte();
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ switch(origin)
+ {
+ case SeekOrigin.Begin:
+ if(offset < 0)
+ throw new IOException(Localization.Cannot_seek_before_stream_start);
+
+ SetPosition(offset);
+
+ break;
+ case SeekOrigin.End:
+ if(offset > 0)
+ throw new IOException(Localization.Cannot_seek_after_stream_end);
+
+ if(_streamLength == 0)
+ CalculateLength();
+
+ SetPosition(_streamLength + offset);
+
+ break;
+ default:
+ SetPosition(_backStream.Position + offset);
+
+ break;
+ }
+
+ return _backStream.Position;
+ }
+
+ ///
+ public override void SetLength(long value) => throw new NotSupportedException();
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
+
+ ///
+ public override void Close()
+ {
+ _backStream?.Close();
+ File.Delete(_backFile);
+ }
+
+ ~ForcedSeekStream()
+ {
+ _backStream?.Close();
+ File.Delete(_backFile);
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/IO/NonClosableStream.cs b/Aaru.Helpers/IO/NonClosableStream.cs
new file mode 100644
index 000000000..72c100df8
--- /dev/null
+++ b/Aaru.Helpers/IO/NonClosableStream.cs
@@ -0,0 +1,78 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : NonClosableStream.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Compression.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Overrides MemoryStream to ignore standard close requests.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+
+namespace Aaru.Helpers.IO;
+
+///
+/// Creates a MemoryStream that ignores close commands
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public sealed class NonClosableStream : Stream
+{
+ readonly Stream _baseStream;
+
+ public NonClosableStream(byte[] buffer) => _baseStream = new MemoryStream(buffer);
+
+ public NonClosableStream() => _baseStream = new MemoryStream();
+
+ public NonClosableStream(Stream stream) => _baseStream = stream;
+
+ public override bool CanRead => _baseStream.CanRead;
+ public override bool CanSeek => _baseStream.CanSeek;
+ public override bool CanWrite => _baseStream.CanWrite;
+ public override long Length => _baseStream.Length;
+
+ public override long Position
+ {
+ get => _baseStream.Position;
+ set => _baseStream.Position = value;
+ }
+
+ public override void Flush() => _baseStream.Flush();
+
+ public override int Read(byte[] buffer, int offset, int count) => _baseStream.EnsureRead(buffer, offset, count);
+
+ public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);
+
+ public override void SetLength(long value) => _baseStream.SetLength(value);
+
+ public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count);
+
+ public override void Close()
+ {
+ // Do nothing
+ }
+
+ public void ReallyClose() => _baseStream.Close();
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/IO/OffsetStream.cs b/Aaru.Helpers/IO/OffsetStream.cs
new file mode 100644
index 000000000..07fa54a2e
--- /dev/null
+++ b/Aaru.Helpers/IO/OffsetStream.cs
@@ -0,0 +1,680 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : OffsetStream.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Filters.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Provides a stream that's a subset of another stream.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using Microsoft.Win32.SafeHandles;
+#if !NETSTANDARD2_0
+
+#endif
+
+namespace Aaru.Helpers.IO;
+
+/// Creates a stream that is a subset of another stream.
+///
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public sealed class OffsetStream : Stream
+{
+ readonly Stream _baseStream;
+ readonly long _streamEnd;
+ readonly long _streamStart;
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified stream, both inclusive.
+ ///
+ /// Base stream
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(Stream stream, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = stream;
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// A bitwise combination of the enumeration values that specifies additional file options.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
+ FileOptions options, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(path, mode, access, share, bufferSize, options);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A file handle for the file that the stream will encapsulate.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(SafeFileHandle handle, FileAccess access, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(handle, access);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A file handle for the file that the stream will encapsulate.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(SafeFileHandle handle, FileAccess access, int bufferSize, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(handle, access, bufferSize);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A file handle for the file that the stream will encapsulate.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// Specifies whether to use asynchronous I/O or synchronous I/O.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(handle, access, bufferSize, isAsync);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// Specifies whether to use asynchronous I/O or synchronous I/O.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync,
+ long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(path, mode, access, share, bufferSize, useAsync);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, long start,
+ long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(path, mode, access, share, bufferSize);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(path, mode, access, share);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(string path, FileMode mode, FileAccess access, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(path, mode, access);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified file, both inclusive.
+ ///
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(string path, FileMode mode, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new FileStream(path, mode);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified byte array, both inclusive.
+ ///
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The index into at which the stream begins.
+ /// The length in bytes to add to the end of the current stream.
+ /// The setting of the CanWrite property, currently ignored.
+ /// Currently ignored.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new MemoryStream(buffer, index, count, writable, publiclyVisible);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified byte array, both inclusive.
+ ///
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The index into at which the stream begins.
+ /// The length in bytes to add to the end of the current stream.
+ /// The setting of the CanWrite property, currently ignored.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(byte[] buffer, int index, int count, bool writable, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new MemoryStream(buffer, index, count, writable);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified byte array, both inclusive.
+ ///
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The index into at which the stream begins.
+ /// The length in bytes to add to the end of the current stream.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(byte[] buffer, int index, int count, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new MemoryStream(buffer, index, count);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified byte array, both inclusive.
+ ///
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The setting of the CanWrite property, currently ignored.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(byte[] buffer, bool writable, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new MemoryStream(buffer, writable);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ ///
+ /// Initializes a stream that only allows reading from to of the
+ /// specified byte array, both inclusive.
+ ///
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// Start position
+ /// Last readable position
+ /// Invalid range
+ public OffsetStream(byte[] buffer, long start, long end)
+ {
+ if(start < 0)
+ throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
+
+ if(end < 0)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
+
+ _streamStart = start;
+ _streamEnd = end;
+
+ _baseStream = new MemoryStream(buffer);
+
+ if(end > _baseStream.Length)
+ throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
+
+ _baseStream.Position = start;
+ }
+
+ ///
+ public override bool CanRead => _baseStream.CanRead;
+
+ ///
+ public override bool CanSeek => _baseStream.CanSeek;
+
+ ///
+ public override bool CanWrite => _baseStream.CanWrite;
+
+ ///
+ public override long Length => _streamEnd - _streamStart + 1;
+
+ ///
+ public override long Position
+ {
+ get => _baseStream.Position - _streamStart;
+
+ set
+ {
+ if(value + _streamStart > _streamEnd)
+ throw new IOException(Localization.Cannot_set_position_past_stream_end);
+
+ _baseStream.Position = value + _streamStart;
+ }
+ }
+
+ ~OffsetStream()
+ {
+ _baseStream.Close();
+ _baseStream.Dispose();
+ }
+
+ ///
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ if(_baseStream.Position + count > _streamEnd)
+ count = (int)(_streamEnd - _baseStream.Position);
+
+ return _baseStream.BeginRead(buffer, offset, count, callback, state);
+ }
+
+ ///
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
+ {
+ if(_baseStream.Position + count > _streamEnd)
+ throw new IOException(Localization.Cannot_write_past_stream_end);
+
+ return _baseStream.BeginWrite(buffer, offset, count, callback, state);
+ }
+
+ ///
+ public override void Close() => _baseStream.Close();
+
+ ///
+ public override int EndRead(IAsyncResult asyncResult) => _baseStream.EndRead(asyncResult);
+
+ ///
+ public override void EndWrite(IAsyncResult asyncResult) => _baseStream.EndWrite(asyncResult);
+
+ ///
+ public override int ReadByte() => _baseStream.Position == _streamEnd + 1 ? -1 : _baseStream.ReadByte();
+
+ ///
+ public override void WriteByte(byte value)
+ {
+ if(_baseStream.Position + 1 > _streamEnd)
+ throw new IOException(Localization.Cannot_write_past_stream_end);
+
+ _baseStream.WriteByte(value);
+ }
+
+ ///
+ public override void Flush() => _baseStream.Flush();
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if(_baseStream.Position + count > _streamEnd + 1)
+ count = (int)(_streamEnd - _baseStream.Position);
+
+ return _baseStream.EnsureRead(buffer, offset, count);
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ switch(origin)
+ {
+ case SeekOrigin.Begin:
+ if(offset + _streamStart > _streamEnd)
+ throw new IOException(Localization.Cannot_seek_after_stream_end);
+
+ return _baseStream.Seek(offset + _streamStart, SeekOrigin.Begin) - _streamStart;
+ case SeekOrigin.End:
+ if(offset - (_baseStream.Length - _streamEnd) < _streamStart)
+ throw new IOException(Localization.Cannot_seek_before_stream_start);
+
+ return _baseStream.Seek(offset - (_baseStream.Length - _streamEnd), SeekOrigin.End) - _streamStart;
+ default:
+ if(offset + _baseStream.Position > _streamEnd)
+ throw new IOException(Localization.Cannot_seek_after_stream_end);
+
+ return _baseStream.Seek(offset, SeekOrigin.Current) - _streamStart;
+ }
+ }
+
+ ///
+ public override void SetLength(long value) =>
+ throw new NotSupportedException(Localization.Growing_OffsetStream_is_not_supported);
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if(_baseStream.Position + count > _streamEnd)
+ throw new IOException(Localization.Cannot_write_past_stream_end);
+
+ _baseStream.Write(buffer, offset, count);
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/IO/SplitJoinStream.cs b/Aaru.Helpers/IO/SplitJoinStream.cs
new file mode 100644
index 000000000..34981ec06
--- /dev/null
+++ b/Aaru.Helpers/IO/SplitJoinStream.cs
@@ -0,0 +1,372 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using Microsoft.Win32.SafeHandles;
+
+namespace Aaru.Helpers.IO;
+
+///
+/// Implements a stream that joins two or more files (sequentially) as a single stream
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public class SplitJoinStream : Stream
+{
+ readonly Dictionary _baseStreams;
+ long _position;
+ long _streamLength;
+
+ ///
+ public SplitJoinStream()
+ {
+ _baseStreams = new Dictionary();
+ _streamLength = 0;
+ _position = 0;
+ }
+
+ ///
+ public override bool CanRead => true;
+
+ ///
+ public override bool CanSeek => true;
+
+ ///
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => _streamLength;
+
+ ///
+ public override long Position
+ {
+ get => _position;
+
+ set
+ {
+ if(value >= _streamLength)
+ throw new IOException(Localization.Cannot_set_position_past_stream_end);
+
+ _position = value;
+ }
+ }
+
+ /// Adds a stream at the end of the current stream
+ /// Stream to add
+ /// The specified stream is non-readable or non-seekable
+ public void Add(Stream stream)
+ {
+ if(!stream.CanSeek)
+ throw new ArgumentException(Localization.Non_seekable_streams_are_not_supported);
+
+ if(!stream.CanRead)
+ throw new ArgumentException(Localization.Non_readable_streams_are_not_supported);
+
+ _baseStreams[_streamLength] = stream;
+ _streamLength += stream.Length;
+ }
+
+ /// Adds the specified file to the end of the current stream
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// A bitwise combination of the enumeration values that specifies additional file options.
+ public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
+ FileOptions options) => Add(new FileStream(path, mode, access, share, bufferSize, options));
+
+ /// Adds the specified file to the end of the current stream
+ /// A file handle for the file that the stream will encapsulate.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ public void Add(SafeFileHandle handle, FileAccess access) => Add(new FileStream(handle, access));
+
+ /// Adds the specified file to the end of the current stream
+ /// A file handle for the file that the stream will encapsulate.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ public void Add(SafeFileHandle handle, FileAccess access, int bufferSize) =>
+ Add(new FileStream(handle, access, bufferSize));
+
+ /// Adds the specified file to the end of the current stream
+ /// A file handle for the file that the stream will encapsulate.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// Specifies whether to use asynchronous I/O or synchronous I/O.
+ public void Add(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) =>
+ Add(new FileStream(handle, access, bufferSize, isAsync));
+
+ /// Adds the specified file to the end of the current stream
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ /// Specifies whether to use asynchronous I/O or synchronous I/O.
+ public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) =>
+ Add(new FileStream(path, mode, access, share, bufferSize, useAsync));
+
+ /// Adds the specified file to the end of the current stream
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ ///
+ /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
+ /// 4096.
+ ///
+ public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) =>
+ Add(new FileStream(path, mode, access, share, bufferSize));
+
+ /// Adds the specified file to the end of the current stream
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file will be shared by
+ /// processes.
+ ///
+ public void Add(string path, FileMode mode, FileAccess access, FileShare share) =>
+ Add(new FileStream(path, mode, access, share));
+
+ /// Adds the specified file to the end of the current stream
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ public void Add(string path, FileMode mode, FileAccess access) => Add(new FileStream(path, mode, access));
+
+ /// Adds the specified file to the end of the current stream
+ /// A relative or absolute path for the file that the stream will encapsulate.
+ /// One of the enumeration values that determines how to open or create the file.
+ public void Add(string path, FileMode mode) => Add(new FileStream(path, mode));
+
+ /// Adds the specified byte array to the end of the current stream
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The index into at which the stream begins.
+ /// The length in bytes to add to the end of the current stream.
+ /// The setting of the CanWrite property, currently ignored.
+ /// Currently ignored.
+ public void Add(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) =>
+ Add(new MemoryStream(buffer, index, count, writable, publiclyVisible));
+
+ /// Adds the specified byte array to the end of the current stream
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The index into at which the stream begins.
+ /// The length in bytes to add to the end of the current stream.
+ /// The setting of the CanWrite property, currently ignored.
+ public void Add(byte[] buffer, int index, int count, bool writable) =>
+ Add(new MemoryStream(buffer, index, count, writable));
+
+ /// Adds the specified byte array to the end of the current stream
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The index into at which the stream begins.
+ /// The length in bytes to add to the end of the current stream.
+ public void Add(byte[] buffer, int index, int count) => Add(new MemoryStream(buffer, index, count));
+
+ /// Adds the specified byte array to the end of the current stream
+ /// The array of unsigned bytes to add at the end of this stream.
+ /// The setting of the CanWrite property, currently ignored.
+ public void Add(byte[] buffer, bool writable) => Add(new MemoryStream(buffer, writable));
+
+ /// Adds the specified byte array to the end of the current stream
+ /// The array of unsigned bytes to add at the end of this stream.
+ public void Add(byte[] buffer) => Add(new MemoryStream(buffer));
+
+ /// Adds a range of files to the end of the current stream, alphabetically sorted
+ /// Base file path, directory path only
+ /// Counter format, includes filename and a formatting string
+ /// Counter start, defaults to 0
+ ///
+ /// A bitwise combination of the enumeration values that determines how the file can be accessed by a
+ /// object.
+ ///
+ public void AddRange(string basePath, string counterFormat = "{0:D3}", int counterStart = 0,
+ FileAccess access = FileAccess.Read)
+ {
+ while(true)
+ {
+ string filePath = Path.Combine(basePath, string.Format(counterFormat, counterStart));
+
+ if(!File.Exists(filePath))
+ break;
+
+ Add(filePath, FileMode.Open, access);
+
+ counterStart++;
+ }
+ }
+
+ ~SplitJoinStream()
+ {
+ foreach(Stream stream in _baseStreams.Values)
+ {
+ stream.Close();
+ stream.Dispose();
+ }
+
+ _baseStreams.Clear();
+ _position = 0;
+ }
+
+ ///
+ public override IAsyncResult
+ BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
+ throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
+
+ ///
+ public override IAsyncResult
+ BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
+ throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
+
+ ///
+ public override void Close()
+ {
+ foreach(Stream stream in _baseStreams.Values)
+ stream.Close();
+
+ _baseStreams.Clear();
+ _position = 0;
+ }
+
+ ///
+ public override int EndRead(IAsyncResult asyncResult) =>
+ throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
+
+ ///
+ public override void EndWrite(IAsyncResult asyncResult) =>
+ throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
+
+ ///
+ public override int ReadByte()
+ {
+ if(_position >= _streamLength)
+ return -1;
+
+ KeyValuePair baseStream = _baseStreams.FirstOrDefault(s => s.Key >= _position);
+
+ if(baseStream.Value == null)
+ return -1;
+
+ baseStream.Value.Position = _position - baseStream.Key;
+ _position++;
+
+ return baseStream.Value.ReadByte();
+ }
+
+ ///
+ public override void WriteByte(byte value) => throw new ReadOnlyException(Localization.This_stream_is_read_only);
+
+ ///
+ public override void Flush() {}
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ var read = 0;
+
+ while(count > 0)
+ {
+ KeyValuePair baseStream = _baseStreams.LastOrDefault(s => s.Key <= _position);
+
+ if(baseStream.Value == null)
+ break;
+
+ baseStream.Value.Position = _position - baseStream.Key;
+
+ int currentCount = count;
+
+ if(baseStream.Value.Position + currentCount > baseStream.Value.Length)
+ currentCount = (int)(baseStream.Value.Length - baseStream.Value.Position);
+
+ read += baseStream.Value.Read(buffer, offset, currentCount);
+
+ count -= currentCount;
+ offset += currentCount;
+ }
+
+ return read;
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ switch(origin)
+ {
+ case SeekOrigin.Begin:
+ if(offset >= _streamLength)
+ throw new IOException(Localization.Cannot_seek_after_stream_end);
+
+ _position = offset;
+
+ break;
+ case SeekOrigin.End:
+ if(_position - offset < 0)
+ throw new IOException(Localization.Cannot_seek_before_stream_start);
+
+ _position -= offset;
+
+ break;
+ default:
+ if(_position + offset >= _streamLength)
+ throw new IOException(Localization.Cannot_seek_after_stream_end);
+
+ _position += offset;
+
+ break;
+ }
+
+ return _position;
+ }
+
+ ///
+ public override void SetLength(long value) => throw new ReadOnlyException(Localization.This_stream_is_read_only);
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count) =>
+ throw new ReadOnlyException(Localization.This_stream_is_read_only);
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/Localization/Localization.Designer.cs b/Aaru.Helpers/Localization/Localization.Designer.cs
new file mode 100644
index 000000000..36a0e6131
--- /dev/null
+++ b/Aaru.Helpers/Localization/Localization.Designer.cs
@@ -0,0 +1,197 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Aaru.Helpers {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Localization {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Localization() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Aaru.Helpers.Localization.Localization", typeof(Localization).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Asynchronous I/O is not supported..
+ ///
+ internal static string Asynchronous_IO_is_not_supported {
+ get {
+ return ResourceManager.GetString("Asynchronous_IO_is_not_supported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot read past stream end..
+ ///
+ internal static string Cannot_read_past_stream_end {
+ get {
+ return ResourceManager.GetString("Cannot_read_past_stream_end", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot seek after stream end..
+ ///
+ internal static string Cannot_seek_after_stream_end {
+ get {
+ return ResourceManager.GetString("Cannot_seek_after_stream_end", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot seek before stream start..
+ ///
+ internal static string Cannot_seek_before_stream_start {
+ get {
+ return ResourceManager.GetString("Cannot_seek_before_stream_start", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot set position past stream end..
+ ///
+ internal static string Cannot_set_position_past_stream_end {
+ get {
+ return ResourceManager.GetString("Cannot_set_position_past_stream_end", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot write past stream end..
+ ///
+ internal static string Cannot_write_past_stream_end {
+ get {
+ return ResourceManager.GetString("Cannot_write_past_stream_end", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to End can't be a negative number..
+ ///
+ internal static string End_cant_be_a_negative_number {
+ get {
+ return ResourceManager.GetString("End_cant_be_a_negative_number", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to End is after stream end..
+ ///
+ internal static string End_is_after_stream_end {
+ get {
+ return ResourceManager.GetString("End_is_after_stream_end", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Growing OffsetStream is not supported..
+ ///
+ internal static string Growing_OffsetStream_is_not_supported {
+ get {
+ return ResourceManager.GetString("Growing_OffsetStream_is_not_supported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Length of value array must not be more than length of destination.
+ ///
+ internal static string Length_of_value_array_must_not_be_more_than_length_of_destination {
+ get {
+ return ResourceManager.GetString("Length_of_value_array_must_not_be_more_than_length_of_destination", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Non-readable streams are not supported.
+ ///
+ internal static string Non_readable_streams_are_not_supported {
+ get {
+ return ResourceManager.GetString("Non_readable_streams_are_not_supported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Non-seekable streams are not supported.
+ ///
+ internal static string Non_seekable_streams_are_not_supported {
+ get {
+ return ResourceManager.GetString("Non_seekable_streams_are_not_supported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Offset.
+ ///
+ internal static string Offset {
+ get {
+ return ResourceManager.GetString("Offset", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start can't be a negative number..
+ ///
+ internal static string Start_cant_be_a_negative_number {
+ get {
+ return ResourceManager.GetString("Start_cant_be_a_negative_number", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to This stream is read-only.
+ ///
+ internal static string This_stream_is_read_only {
+ get {
+ return ResourceManager.GetString("This_stream_is_read_only", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/Aaru.Helpers/Localization/Localization.es.resx b/Aaru.Helpers/Localization/Localization.es.resx
new file mode 100644
index 000000000..9ed14ff70
--- /dev/null
+++ b/Aaru.Helpers/Localization/Localization.es.resx
@@ -0,0 +1,64 @@
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+ PublicKeyToken=b77a5c561934e089
+
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+ PublicKeyToken=b77a5c561934e089
+
+
+
+ Compensación
+
+
+ La longitud de una colección no puede ser mayor que la longitud del destino
+
+
+ Las secuencias no legíbles no están soportadas.
+
+
+ Las secuencias no posicionables no están soportadas.
+
+
+ No se puede leer más allá del final de la secuencia.
+
+
+ No se puede posicionar después del final de la secuencia.
+
+
+ No se puede posicionar antes del comienzo de la secuencia.
+
+
+ No se puede establecer la posición más allá del final de la secuencia.
+
+
+ No se puede escribir después del final de la secuencia.
+
+
+ El final no puede ser un número negativo.
+
+
+ El final está después del final de la secuencia.
+
+
+ No se puede agrandar un OffsetStream.
+
+
+ E/S asíncrona no soportada.
+
+
+ El comienzo no puede ser un número negativo.
+
+
+ Esta secuencia es de sólo lectura.
+
+
\ No newline at end of file
diff --git a/Aaru.Helpers/Localization/Localization.resx b/Aaru.Helpers/Localization/Localization.resx
new file mode 100644
index 000000000..6f9a16a67
--- /dev/null
+++ b/Aaru.Helpers/Localization/Localization.resx
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+ PublicKeyToken=b77a5c561934e089
+
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
+ PublicKeyToken=b77a5c561934e089
+
+
+
+ Length of value array must not be more than length of destination
+
+
+ Offset
+
+
+ Start can't be a negative number.
+
+
+ End can't be a negative number.
+
+
+ End is after stream end.
+
+
+ Cannot set position past stream end.
+
+
+ Cannot read past stream end.
+
+
+ Cannot write past stream end.
+
+
+ Growing OffsetStream is not supported.
+
+
+ Cannot seek before stream start.
+
+
+ Cannot seek after stream end.
+
+
+ Non-seekable streams are not supported
+
+
+ Non-readable streams are not supported
+
+
+ Asynchronous I/O is not supported.
+
+
+ This stream is read-only
+
+
\ No newline at end of file
diff --git a/Aaru.Helpers/Marshal.cs b/Aaru.Helpers/Marshal.cs
new file mode 100644
index 000000000..738c75f7c
--- /dev/null
+++ b/Aaru.Helpers/Marshal.cs
@@ -0,0 +1,492 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : Marshal.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Provides marshalling for binary data.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Aaru.Helpers;
+
+/// Provides methods to marshal binary data into C# structs
+[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public static class Marshal
+{
+ /// Returns the size of an unmanaged type in bytes.
+ /// The type whose size is to be returned.
+ /// The size, in bytes, of the type that is specified by the generic type parameter.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SizeOf() => System.Runtime.InteropServices.Marshal.SizeOf();
+
+ /// Marshal little-endian binary data to a structure
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ByteArrayToStructureLittleEndian(byte[] bytes) where T : struct
+ {
+ var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+
+ var str = (T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ??
+ default(T));
+
+ ptr.Free();
+
+ return str;
+ }
+
+ /// Marshal little-endian binary data to a structure
+ /// Byte array containing the binary data
+ /// Start on the array where the structure begins
+ /// Length of the structure in bytes
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ByteArrayToStructureLittleEndian(byte[] bytes, int start, int length) where T : struct
+ {
+ Span span = bytes;
+
+ return ByteArrayToStructureLittleEndian(span.Slice(start, length).ToArray());
+ }
+
+ /// Marshal big-endian binary data to a structure
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ByteArrayToStructureBigEndian(byte[] bytes) where T : struct
+ {
+ var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+
+ object str = (T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ??
+ default(T));
+
+ ptr.Free();
+
+ return (T)SwapStructureMembersEndian(str);
+ }
+
+ /// Marshal big-endian binary data to a structure
+ /// Byte array containing the binary data
+ /// Start on the array where the structure begins
+ /// Length of the structure in bytes
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ByteArrayToStructureBigEndian(byte[] bytes, int start, int length) where T : struct
+ {
+ Span span = bytes;
+
+ return ByteArrayToStructureBigEndian(span.Slice(start, length).ToArray());
+ }
+
+ /// Marshal PDP-11 binary data to a structure
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ByteArrayToStructurePdpEndian(byte[] bytes) where T : struct
+ {
+ {
+ var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+
+ object str =
+ (T)(System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)) ??
+ default(T));
+
+ ptr.Free();
+
+ return (T)SwapStructureMembersEndianPdp(str);
+ }
+ }
+
+ /// Marshal PDP-11 binary data to a structure
+ /// Byte array containing the binary data
+ /// Start on the array where the structure begins
+ /// Length of the structure in bytes
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T ByteArrayToStructurePdpEndian(byte[] bytes, int start, int length) where T : struct
+ {
+ Span span = bytes;
+
+ return ByteArrayToStructurePdpEndian(span.Slice(start, length).ToArray());
+ }
+
+ ///
+ /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this
+ /// method will crash.
+ ///
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T SpanToStructureLittleEndian(ReadOnlySpan bytes) where T : struct =>
+ MemoryMarshal.Read(bytes);
+
+ ///
+ /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this
+ /// method will crash.
+ ///
+ /// Byte span containing the binary data
+ /// Start on the span where the structure begins
+ /// Length of the structure in bytes
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T SpanToStructureLittleEndian(ReadOnlySpan bytes, int start, int length) where T : struct =>
+ MemoryMarshal.Read(bytes.Slice(start, length));
+
+ ///
+ /// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method
+ /// will crash.
+ ///
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T SpanToStructureBigEndian(ReadOnlySpan bytes) where T : struct
+ {
+ T str = SpanToStructureLittleEndian(bytes);
+
+ return (T)SwapStructureMembersEndian(str);
+ }
+
+ ///
+ /// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method
+ /// will crash.
+ ///
+ /// Byte span containing the binary data
+ /// Start on the span where the structure begins
+ /// Length of the structure in bytes
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T SpanToStructureBigEndian(ReadOnlySpan bytes, int start, int length) where T : struct
+ {
+ T str = SpanToStructureLittleEndian(bytes.Slice(start, length));
+
+ return (T)SwapStructureMembersEndian(str);
+ }
+
+ ///
+ /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will
+ /// crash.
+ ///
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T SpanToStructurePdpEndian(ReadOnlySpan bytes) where T : struct
+ {
+ object str = SpanToStructureLittleEndian(bytes);
+
+ return (T)SwapStructureMembersEndianPdp(str);
+ }
+
+ ///
+ /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will
+ /// crash.
+ ///
+ /// Byte array containing the binary data
+ /// Start on the span where the structure begins
+ /// Length of the structure in bytes
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T SpanToStructurePdpEndian(ReadOnlySpan bytes, int start, int length) where T : struct
+ {
+ object str = SpanToStructureLittleEndian(bytes.Slice(start, length));
+
+ return (T)SwapStructureMembersEndianPdp(str);
+ }
+
+ ///
+ /// Marshal a structure depending on the decoration of . If the
+ /// decoration is not present it will marshal as a reference type containing little endian structure.
+ ///
+ /// Byte array containing the binary data
+ /// Type of the structure to marshal
+ /// The binary data marshalled in a structure with the specified type
+ ///
+ /// The contains an unsupported
+ /// endian
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T MarshalStructure(byte[] bytes) where T : struct
+ {
+ if(typeof(T).GetCustomAttribute(typeof(MarshallingPropertiesAttribute)) is not MarshallingPropertiesAttribute
+ properties)
+ return ByteArrayToStructureLittleEndian(bytes);
+
+ return properties.Endian switch
+ {
+ BitEndian.Little => properties.HasReferences
+ ? ByteArrayToStructureLittleEndian(bytes)
+ : SpanToStructureLittleEndian(bytes),
+ BitEndian.Big => properties.HasReferences
+ ? ByteArrayToStructureBigEndian(bytes)
+ : SpanToStructureBigEndian(bytes),
+ BitEndian.Pdp => properties.HasReferences
+ ? ByteArrayToStructurePdpEndian(bytes)
+ : SpanToStructurePdpEndian(bytes),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ }
+
+ /// Swaps all members of a structure
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public static object SwapStructureMembersEndian(object str)
+ {
+ Type t = str.GetType();
+ FieldInfo[] fieldInfo = t.GetFields();
+
+ foreach(FieldInfo fi in fieldInfo)
+ {
+ if(fi.FieldType == typeof(short) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(short))
+ {
+ var x = (short)(fi.GetValue(str) ?? default(short));
+ fi.SetValue(str, (short)(x << 8 | x >> 8 & 0xFF));
+ }
+ else if(fi.FieldType == typeof(int) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))
+ {
+ var x = (int)(fi.GetValue(str) ?? default(int));
+ x = (int)(x << 8 & 0xFF00FF00 | (uint)x >> 8 & 0xFF00FF);
+ fi.SetValue(str, (int)((uint)x << 16 | (uint)x >> 16 & 0xFFFF));
+ }
+ else if(fi.FieldType == typeof(long) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(long))
+ {
+ var x = (long)(fi.GetValue(str) ?? default(long));
+ x = (x & 0x00000000FFFFFFFF) << 32 | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32);
+ x = (x & 0x0000FFFF0000FFFF) << 16 | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16);
+ x = (x & 0x00FF00FF00FF00FF) << 8 | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8);
+
+ fi.SetValue(str, x);
+ }
+ else if(fi.FieldType == typeof(ushort) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ushort))
+ {
+ var x = (ushort)(fi.GetValue(str) ?? default(ushort));
+ fi.SetValue(str, (ushort)(x << 8 | x >> 8));
+ }
+ else if(fi.FieldType == typeof(uint) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))
+ {
+ var x = (uint)(fi.GetValue(str) ?? default(uint));
+ x = x << 8 & 0xFF00FF00 | x >> 8 & 0xFF00FF;
+ fi.SetValue(str, x << 16 | x >> 16);
+ }
+ else if(fi.FieldType == typeof(ulong) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ulong))
+ {
+ var x = (ulong)(fi.GetValue(str) ?? default(ulong));
+ x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
+ x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
+ x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
+ fi.SetValue(str, x);
+ }
+ else if(fi.FieldType == typeof(float))
+ {
+ var flt = (float)(fi.GetValue(str) ?? default(float));
+ byte[] flt_b = BitConverter.GetBytes(flt);
+
+ fi.SetValue(str, BitConverter.ToSingle(new[]
+ {
+ flt_b[3], flt_b[2], flt_b[1], flt_b[0]
+ }, 0));
+ }
+ else if(fi.FieldType == typeof(double))
+ {
+ var dbl = (double)(fi.GetValue(str) ?? default(double));
+ byte[] dbl_b = BitConverter.GetBytes(dbl);
+
+ fi.SetValue(str, BitConverter.ToDouble(new[]
+ {
+ dbl_b[7], dbl_b[6], dbl_b[5], dbl_b[4], dbl_b[3], dbl_b[2], dbl_b[1], dbl_b[0]
+ }, 0));
+ }
+ else if(fi.FieldType == typeof(byte) || fi.FieldType == typeof(sbyte))
+ {
+ // Do nothing, can't byteswap them!
+ }
+ else if(fi.FieldType == typeof(Guid))
+ {
+ // TODO: Swap GUID
+ }
+
+ // TODO: Swap arrays
+ else if(fi.FieldType.IsValueType && fi.FieldType is { IsEnum: false, IsArray: false })
+ {
+ object obj = fi.GetValue(str);
+ object strc = SwapStructureMembersEndian(obj);
+ fi.SetValue(str, strc);
+ }
+ }
+
+ return str;
+ }
+
+ /// Swaps all fields in an structure considering them to follow PDP endian conventions
+ /// Source structure
+ /// Resulting structure
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static object SwapStructureMembersEndianPdp(object str)
+ {
+ Type t = str.GetType();
+ FieldInfo[] fieldInfo = t.GetFields();
+
+ foreach(FieldInfo fi in fieldInfo)
+ {
+ if(fi.FieldType == typeof(short) ||
+ fi.FieldType == typeof(long) ||
+ fi.FieldType == typeof(ushort) ||
+ fi.FieldType == typeof(ulong) ||
+ fi.FieldType == typeof(float) ||
+ fi.FieldType == typeof(double) ||
+ fi.FieldType == typeof(byte) ||
+ fi.FieldType == typeof(sbyte) ||
+ fi.FieldType == typeof(Guid))
+ {
+ // Do nothing
+ }
+ else if(fi.FieldType == typeof(int) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))
+ {
+ var x = (int)(fi.GetValue(str) ?? default(int));
+ fi.SetValue(str, (x & 0xffffu) << 16 | (x & 0xffff0000u) >> 16);
+ }
+ else if(fi.FieldType == typeof(uint) ||
+ fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))
+ {
+ var x = (uint)(fi.GetValue(str) ?? default(uint));
+ fi.SetValue(str, (x & 0xffffu) << 16 | (x & 0xffff0000u) >> 16);
+ }
+
+ // TODO: Swap arrays
+ else if(fi.FieldType.IsValueType && fi.FieldType is { IsEnum: false, IsArray: false })
+ {
+ object obj = fi.GetValue(str);
+ object strc = SwapStructureMembersEndianPdp(obj);
+ fi.SetValue(str, strc);
+ }
+ }
+
+ return str;
+ }
+
+ /// Marshal a structure to little-endian binary data
+ /// The structure you want to marshal to binary
+ /// Type of the structure to marshal
+ /// The byte array representing the given structure
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte[] StructureToByteArrayLittleEndian(T str) where T : struct
+ {
+ var buf = new byte[SizeOf()];
+ var ptr = GCHandle.Alloc(buf, GCHandleType.Pinned);
+ System.Runtime.InteropServices.Marshal.StructureToPtr(str, ptr.AddrOfPinnedObject(), false);
+ ptr.Free();
+
+ return buf;
+ }
+
+ /// Marshal a structure to little-endian binary data
+ /// The structure you want to marshal to binary
+ /// Type of the structure to marshal
+ /// The byte array representing the given structure
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte[] StructureToByteArrayBigEndian(T str) where T : struct =>
+ StructureToByteArrayLittleEndian((T)SwapStructureMembersEndian(str));
+
+ /// Converts a hexadecimal string into a byte array
+ /// Hexadecimal string
+ /// Resulting byte array
+ /// Number of output bytes processed
+ public static int ConvertFromHexAscii(string hex, out byte[] outBuf)
+ {
+ outBuf = null;
+
+ if(hex is null or "")
+ return -1;
+
+ var off = 0;
+
+ if(hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X'))
+ off = 2;
+
+ outBuf = new byte[(hex.Length - off) / 2];
+ var count = 0;
+
+ for(int i = off; i < hex.Length; i += 2)
+ {
+ char c = hex[i];
+
+ if(c is < '0' or > '9' and < 'A' or > 'F' and < 'a' or > 'f')
+ break;
+
+ c -= c switch
+ {
+ >= 'a' and <= 'f' => '\u0057',
+ >= 'A' and <= 'F' => '\u0037',
+ _ => '\u0030'
+ };
+
+ outBuf[(i - off) / 2] = (byte)(c << 4);
+
+ c = hex[i + 1];
+
+ if(c is < '0' or > '9' and < 'A' or > 'F' and < 'a' or > 'f')
+ break;
+
+ c -= c switch
+ {
+ >= 'a' and <= 'f' => '\u0057',
+ >= 'A' and <= 'F' => '\u0037',
+ _ => '\u0030'
+ };
+
+ outBuf[(i - off) / 2] += (byte)c;
+
+ count++;
+ }
+
+ return count;
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/MarshallingPropertiesAttribute.cs b/Aaru.Helpers/MarshallingPropertiesAttribute.cs
new file mode 100644
index 000000000..40dc5d63f
--- /dev/null
+++ b/Aaru.Helpers/MarshallingPropertiesAttribute.cs
@@ -0,0 +1,62 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : MarshallingPropertiesAttribute.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Common types.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Declares properties of structs for marshalling.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+
+namespace Aaru.Helpers;
+
+///
+/// Defines properties to help marshalling structs from binary data
+[AttributeUsage(AttributeTargets.Struct)]
+public sealed class MarshallingPropertiesAttribute : Attribute
+{
+ ///
+ /// Defines properties to help marshalling structs from binary data
+ /// Defines properties to help marshalling structs from binary data
+ public MarshallingPropertiesAttribute(BitEndian endian)
+ {
+ Endian = endian;
+ HasReferences = true;
+ }
+
+ /// c
+ public BitEndian Endian { get; }
+
+ /// Tells if the structure, or any nested structure, has any non-value type (e.g. arrays, strings, etc).
+ public bool HasReferences { get; set; }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/PrintHex.cs b/Aaru.Helpers/PrintHex.cs
new file mode 100644
index 000000000..0f3c92b87
--- /dev/null
+++ b/Aaru.Helpers/PrintHex.cs
@@ -0,0 +1,139 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : PrintHex.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Prints a byte array as hexadecimal.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System.Text;
+using Aaru.Console;
+
+namespace Aaru.Helpers;
+
+/// Helper operations to get hexadecimal representations of byte arrays
+public static class PrintHex
+{
+ /// Prints a byte array as hexadecimal values to the console
+ /// Array
+ /// Width of line
+ public static void PrintHexArray(byte[] array, int width = 16) =>
+ AaruConsole.WriteLine(ByteArrayToHexArrayString(array, width));
+
+ /// Prints a byte array as hexadecimal values to a string
+ /// Array
+ /// Width of line
+ /// Use ANSI escape colors for sections
+ /// String containing hexadecimal values
+ public static string ByteArrayToHexArrayString(byte[] array, int width = 16, bool color = false)
+ {
+ if(array is null)
+ return null;
+
+ // TODO: Color list
+ // TODO: Allow to change width
+ string str = Localization.Offset;
+ int rows = array.Length / width;
+ int last = array.Length % width;
+ int offsetLength = $"{array.Length:X}".Length;
+ var sb = new StringBuilder();
+
+ switch(last)
+ {
+ case > 0:
+ rows++;
+
+ break;
+ case 0:
+ last = width;
+
+ break;
+ }
+
+ if(offsetLength < str.Length)
+ offsetLength = str.Length;
+
+ while(str.Length < offsetLength)
+ str += ' ';
+
+ if(color)
+ sb.Append("\u001b[36m");
+
+ sb.Append(str);
+ sb.Append(" ");
+
+ for(var i = 0; i < width; i++)
+ sb.Append($" {i:X2}");
+
+ if(color)
+ sb.Append("\u001b[0m");
+
+ sb.AppendLine();
+
+ var b = 0;
+
+ var format = $"{{0:X{offsetLength}}}";
+
+ for(var i = 0; i < rows; i++)
+ {
+ if(color)
+ sb.Append("\u001b[36m");
+
+ sb.AppendFormat(format, b);
+
+ if(color)
+ sb.Append("\u001b[0m");
+
+ sb.Append(" ");
+ int lastBytes = i == rows - 1 ? last : width;
+ int lastSpaces = width - lastBytes;
+
+ for(var j = 0; j < lastBytes; j++)
+ {
+ sb.Append($" {array[b]:X2}");
+ b++;
+ }
+
+ for(var j = 0; j < lastSpaces; j++)
+ sb.Append(" ");
+
+ b -= lastBytes;
+ sb.Append(" ");
+
+ for(var j = 0; j < lastBytes; j++)
+ {
+ int v = array[b];
+ sb.Append(v is > 31 and < 127 or > 159 ? (char)v : '.');
+ b++;
+ }
+
+ sb.AppendLine();
+ }
+
+ return sb.ToString();
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/StringHandlers.cs b/Aaru.Helpers/StringHandlers.cs
new file mode 100644
index 000000000..07348633b
--- /dev/null
+++ b/Aaru.Helpers/StringHandlers.cs
@@ -0,0 +1,187 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : StringHandlers.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Convert byte arrays to C# strings.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+
+namespace Aaru.Helpers;
+
+/// Helper operations to work with strings
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public static class StringHandlers
+{
+ /// Converts a null-terminated (aka C string) ASCII byte array to a C# string
+ /// The corresponding C# string
+ /// A null-terminated (aka C string) ASCII byte array
+ public static string CToString(byte[] cString) => CToString(cString, Encoding.ASCII);
+
+ /// Converts a null-terminated (aka C string) byte array with the specified encoding to a C# string
+ /// The corresponding C# string
+ /// A null-terminated (aka C string) byte array in the specified encoding
+ /// Encoding.
+ /// Set if encoding uses 16-bit characters.
+ /// Start decoding at this position
+ public static string CToString(byte[] cString, Encoding encoding, bool twoBytes = false, int start = 0)
+ {
+ if(cString == null)
+ return null;
+
+ var len = 0;
+
+ for(int i = start; i < cString.Length; i++)
+ {
+ if(cString[i] == 0)
+ {
+ if(twoBytes)
+ {
+ if(i + 1 < cString.Length && cString[i + 1] == 0)
+ {
+ len++;
+
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ len++;
+ }
+
+ if(twoBytes && len % 2 > 0)
+ len--;
+
+ var dest = new byte[len];
+ Array.Copy(cString, start, dest, 0, len);
+
+ return len == 0 ? "" : encoding.GetString(dest);
+ }
+
+ /// Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string
+ /// The corresponding C# string
+ /// A length-prefixed (aka Pascal string) ASCII byte array
+ public static string PascalToString(byte[] pascalString) => PascalToString(pascalString, Encoding.ASCII);
+
+ /// Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string
+ /// The corresponding C# string
+ /// A length-prefixed (aka Pascal string) ASCII byte array
+ /// Encoding.
+ /// Start decoding at this position
+ public static string PascalToString(byte[] pascalString, Encoding encoding, int start = 0)
+ {
+ if(pascalString == null)
+ return null;
+
+ byte length = pascalString[start];
+ var len = 0;
+
+ for(int i = start + 1; i < length + 1 && i < pascalString.Length; i++)
+ {
+ if(pascalString[i] == 0)
+ break;
+
+ len++;
+ }
+
+ var dest = new byte[len];
+ Array.Copy(pascalString, start + 1, dest, 0, len);
+
+ return len == 0 ? "" : encoding.GetString(dest);
+ }
+
+ /// Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string
+ /// The corresponding C# string
+ /// A space (' ', 0x20, ASCII SPACE) padded ASCII byte array
+ public static string SpacePaddedToString(byte[] spacePaddedString) =>
+ SpacePaddedToString(spacePaddedString, Encoding.ASCII);
+
+ /// Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string
+ /// The corresponding C# string
+ /// A space (' ', 0x20, ASCII SPACE) padded ASCII byte array
+ /// Encoding.
+ /// Start decoding at this position
+ public static string SpacePaddedToString(byte[] spacePaddedString, Encoding encoding, int start = 0)
+ {
+ if(spacePaddedString == null)
+ return null;
+
+ int len = start;
+
+ for(int i = spacePaddedString.Length; i >= start; i--)
+ {
+ if(i == start)
+ return "";
+
+ if(spacePaddedString[i - 1] == 0x20)
+ continue;
+
+ len = i;
+
+ break;
+ }
+
+ return len == 0 ? "" : encoding.GetString(spacePaddedString, start, len);
+ }
+
+ /// Converts an OSTA compressed unicode byte array to a C# string
+ /// The C# string.
+ /// OSTA compressed unicode byte array.
+ public static string DecompressUnicode(byte[] dstring)
+ {
+ byte compId = dstring[0];
+ var temp = "";
+
+ if(compId != 8 && compId != 16)
+ return null;
+
+ for(var byteIndex = 1; byteIndex < dstring.Length;)
+ {
+ ushort unicode;
+
+ if(compId == 16)
+ unicode = (ushort)(dstring[byteIndex++] << 8);
+ else
+ unicode = 0;
+
+ if(byteIndex < dstring.Length)
+ unicode |= dstring[byteIndex++];
+
+ if(unicode == 0)
+ break;
+
+ temp += Encoding.Unicode.GetString(BitConverter.GetBytes(unicode));
+ }
+
+ return temp;
+ }
+}
\ No newline at end of file
diff --git a/Aaru.Helpers/Swapping.cs b/Aaru.Helpers/Swapping.cs
new file mode 100644
index 000000000..e5f3b64f2
--- /dev/null
+++ b/Aaru.Helpers/Swapping.cs
@@ -0,0 +1,113 @@
+// /***************************************************************************
+// Aaru Data Preservation Suite
+// ----------------------------------------------------------------------------
+//
+// Filename : Swapping.cs
+// Author(s) : Natalia Portillo
+//
+// Component : Helpers.
+//
+// --[ Description ] ----------------------------------------------------------
+//
+// Byte-swapping methods.
+//
+// --[ License ] --------------------------------------------------------------
+//
+// This library is free software; you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 2.1 of the
+// License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, see .
+//
+// ----------------------------------------------------------------------------
+// Copyright © 2011-2023 Natalia Portillo
+// ****************************************************************************/
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+namespace Aaru.Helpers;
+
+/// Helper operations to work with swapping endians
+[SuppressMessage("ReSharper", "UnusedMember.Global")]
+public static class Swapping
+{
+ /// Gets the PDP endian equivalent of the given little endian unsigned integer
+ /// Little endian unsigned integer
+ /// PDP unsigned integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint PDPFromLittleEndian(uint x) => (x & 0xffff) << 16 | (x & 0xffff0000) >> 16;
+
+ /// Gets the PDP endian equivalent of the given big endian unsigned integer
+ /// Big endian unsigned integer
+ /// PDP unsigned integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint PDPFromBigEndian(uint x) => (x & 0xff00ff) << 8 | (x & 0xff00ff00) >> 8;
+
+ /// Swaps the endian of the specified unsigned short integer
+ /// Unsigned short integer
+ /// Swapped unsigned short integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort Swap(ushort x) => (ushort)(x << 8 | x >> 8);
+
+ /// Swaps the endian of the specified signed short integer
+ /// Signed short integer
+ /// Swapped signed short integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static short Swap(short x) => (short)(x << 8 | x >> 8 & 0xFF);
+
+ /// Swaps the endian of the specified unsigned integer
+ /// Unsigned integer
+ /// Swapped unsigned integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint Swap(uint x)
+ {
+ x = x << 8 & 0xFF00FF00 | x >> 8 & 0xFF00FF;
+
+ return x << 16 | x >> 16;
+ }
+
+ /// Swaps the endian of the specified signed integer
+ /// Signed integer
+ /// Swapped signed integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Swap(int x)
+ {
+ x = (int)(x << 8 & 0xFF00FF00 | (uint)x >> 8 & 0xFF00FF);
+
+ return (int)((uint)x << 16 | (uint)x >> 16 & 0xFFFF);
+ }
+
+ /// Swaps the endian of the specified unsigned long integer
+ /// Unsigned long integer
+ /// Swapped unsigned long integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong Swap(ulong x)
+ {
+ x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
+ x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
+ x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
+
+ return x;
+ }
+
+ /// Swaps the endian of the specified signed long integer
+ /// Signed long integer
+ /// Swapped signed long integer
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long Swap(long x)
+ {
+ x = (x & 0x00000000FFFFFFFF) << 32 | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32);
+ x = (x & 0x0000FFFF0000FFFF) << 16 | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16);
+ x = (x & 0x00FF00FF00FF00FF) << 8 | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8);
+
+ return x;
+ }
+}
\ No newline at end of file