commit c3e1cc76011f7dc0d62c52b4072b04d1e2a72aa5 Author: acidicoala <67734819+acidicoala@users.noreply.github.com> Date: Mon May 9 02:28:43 2022 +0300 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a8db3a7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,522 @@ +[*] +charset = utf-8 +end_of_line = crlf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_visual_guides = none +ij_wrap_on_typing = false + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +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 + +[{*.ant,*.fxml,*.icls,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +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 + +[{*.apinotes,*.yaml,*.yml,.clang-format,.clang-tidy,_clang-format}] +indent_size = 2 +ij_yaml_align_values_properties = do_not_align +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 + +[{*.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 = false +ij_shell_use_unix_line_separator = true + +[{*.c,*.c++,*.cc,*.cp,*.cpp,*.cu,*.cuh,*.cxx,*.h,*.h++,*.hh,*.hp,*.hpp,*.hxx,*.i,*.icc,*.ii,*.inl,*.ino,*.ipp,*.m,*.mm,*.pch,*.tcc,*.tpp,version.gen.rc}] +ij_visual_guides = 100 +ij_c_add_brief_tag = false +ij_c_add_getter_prefix = true +ij_c_add_setter_prefix = true +ij_c_align_dictionary_pair_values = false +ij_c_align_group_field_declarations = false +ij_c_align_init_list_in_columns = true +ij_c_align_multiline_array_initializer_expression = false +ij_c_align_multiline_assignment = true +ij_c_align_multiline_binary_operation = true +ij_c_align_multiline_chained_methods = false +ij_c_align_multiline_for = true +ij_c_align_multiline_ternary_operation = true +ij_c_array_initializer_comma_on_next_line = false +ij_c_array_initializer_new_line_after_left_brace = false +ij_c_array_initializer_right_brace_on_new_line = false +ij_c_array_initializer_wrap = normal +ij_c_assignment_wrap = off +ij_c_binary_operation_sign_on_next_line = false +ij_c_binary_operation_wrap = normal +ij_c_blank_lines_after_class_header = 0 +ij_c_blank_lines_after_imports = 0 +ij_c_blank_lines_around_class = 1 +ij_c_blank_lines_around_field = 0 +ij_c_blank_lines_around_field_in_interface = 0 +ij_c_blank_lines_around_method = 1 +ij_c_blank_lines_around_method_in_interface = 1 +ij_c_blank_lines_around_namespace = 0 +ij_c_blank_lines_around_properties_in_declaration = 0 +ij_c_blank_lines_around_properties_in_interface = 0 +ij_c_blank_lines_before_imports = 0 +ij_c_blank_lines_before_method_body = 0 +ij_c_block_brace_placement = end_of_line +ij_c_block_brace_style = end_of_line +ij_c_block_comment_at_first_column = true +ij_c_catch_on_new_line = false +ij_c_class_brace_style = end_of_line +ij_c_class_constructor_init_list_align_multiline = true +ij_c_class_constructor_init_list_comma_on_next_line = false +ij_c_class_constructor_init_list_new_line_after_colon = never +ij_c_class_constructor_init_list_new_line_before_colon = if_long +ij_c_class_constructor_init_list_wrap = normal +ij_c_copy_is_deep = false +ij_c_create_interface_for_categories = true +ij_c_declare_generated_methods = true +ij_c_description_include_member_names = true +ij_c_discharged_short_ternary_operator = false +ij_c_do_not_add_breaks = false +ij_c_do_while_brace_force = never +ij_c_else_on_new_line = false +ij_c_enum_constants_comma_on_next_line = false +ij_c_enum_constants_wrap = on_every_item +ij_c_for_brace_force = never +ij_c_for_statement_new_line_after_left_paren = false +ij_c_for_statement_right_paren_on_new_line = false +ij_c_for_statement_wrap = off +ij_c_function_brace_placement = end_of_line +ij_c_function_call_arguments_align_multiline = true +ij_c_function_call_arguments_align_multiline_pars = false +ij_c_function_call_arguments_comma_on_next_line = false +ij_c_function_call_arguments_new_line_after_lpar = false +ij_c_function_call_arguments_new_line_before_rpar = false +ij_c_function_call_arguments_wrap = normal +ij_c_function_non_top_after_return_type_wrap = normal +ij_c_function_parameters_align_multiline = true +ij_c_function_parameters_align_multiline_pars = false +ij_c_function_parameters_comma_on_next_line = false +ij_c_function_parameters_new_line_after_lpar = false +ij_c_function_parameters_new_line_before_rpar = false +ij_c_function_parameters_wrap = normal +ij_c_function_top_after_return_type_wrap = normal +ij_c_generate_additional_eq_operators = true +ij_c_generate_additional_rel_operators = true +ij_c_generate_class_constructor = true +ij_c_generate_comparison_operators_use_std_tie = false +ij_c_generate_instance_variables_for_properties = ask +ij_c_generate_operators_as_members = true +ij_c_header_guard_style_pattern = ${PROJECT_NAME}_${FILE_NAME}_${EXT} +ij_c_if_brace_force = never +ij_c_in_line_short_ternary_operator = true +ij_c_indent_block_comment = true +ij_c_indent_c_struct_members = 4 +ij_c_indent_case_from_switch = true +ij_c_indent_class_members = 4 +ij_c_indent_directive_as_code = false +ij_c_indent_implementation_members = 0 +ij_c_indent_inside_code_block = 4 +ij_c_indent_interface_members = 0 +ij_c_indent_interface_members_except_ivars_block = false +ij_c_indent_namespace_members = 4 +ij_c_indent_preprocessor_directive = 0 +ij_c_indent_visibility_keywords = 0 +ij_c_insert_override = true +ij_c_insert_virtual_with_override = false +ij_c_introduce_auto_vars = false +ij_c_introduce_const_params = false +ij_c_introduce_const_vars = false +ij_c_introduce_generate_property = false +ij_c_introduce_generate_synthesize = true +ij_c_introduce_globals_to_header = true +ij_c_introduce_prop_to_private_category = false +ij_c_introduce_static_consts = true +ij_c_introduce_use_ns_types = false +ij_c_ivars_prefix = _ +ij_c_keep_blank_lines_before_end = 2 +ij_c_keep_blank_lines_before_right_brace = 2 +ij_c_keep_blank_lines_in_code = 2 +ij_c_keep_blank_lines_in_declarations = 2 +ij_c_keep_case_expressions_in_one_line = false +ij_c_keep_control_statement_in_one_line = true +ij_c_keep_directive_at_first_column = true +ij_c_keep_first_column_comment = true +ij_c_keep_line_breaks = true +ij_c_keep_nested_namespaces_in_one_line = false +ij_c_keep_simple_blocks_in_one_line = true +ij_c_keep_simple_methods_in_one_line = true +ij_c_keep_structures_in_one_line = false +ij_c_lambda_capture_list_align_multiline = false +ij_c_lambda_capture_list_align_multiline_bracket = false +ij_c_lambda_capture_list_comma_on_next_line = false +ij_c_lambda_capture_list_new_line_after_lbracket = false +ij_c_lambda_capture_list_new_line_before_rbracket = false +ij_c_lambda_capture_list_wrap = off +ij_c_line_comment_add_space = false +ij_c_line_comment_at_first_column = true +ij_c_method_brace_placement = end_of_line +ij_c_method_call_arguments_align_by_colons = true +ij_c_method_call_arguments_align_multiline = false +ij_c_method_call_arguments_special_dictionary_pairs_treatment = true +ij_c_method_call_arguments_wrap = off +ij_c_method_call_chain_wrap = off +ij_c_method_parameters_align_by_colons = true +ij_c_method_parameters_align_multiline = false +ij_c_method_parameters_wrap = off +ij_c_namespace_brace_placement = end_of_line +ij_c_parentheses_expression_new_line_after_left_paren = false +ij_c_parentheses_expression_right_paren_on_new_line = false +ij_c_place_assignment_sign_on_next_line = false +ij_c_property_nonatomic = true +ij_c_put_ivars_to_implementation = true +ij_c_refactor_compatibility_aliases_and_classes = true +ij_c_refactor_properties_and_ivars = true +ij_c_release_style = ivar +ij_c_retain_object_parameters_in_constructor = true +ij_c_semicolon_after_method_signature = false +ij_c_shift_operation_align_multiline = true +ij_c_shift_operation_wrap = normal +ij_c_show_non_virtual_functions = false +ij_c_space_after_colon = true +ij_c_space_after_colon_in_foreach = true +ij_c_space_after_colon_in_selector = false +ij_c_space_after_comma = true +ij_c_space_after_cup_in_blocks = false +ij_c_space_after_dictionary_literal_colon = true +ij_c_space_after_for_semicolon = true +ij_c_space_after_init_list_colon = true +ij_c_space_after_method_parameter_type_parentheses = false +ij_c_space_after_method_return_type_parentheses = false +ij_c_space_after_pointer_in_declaration = true +ij_c_space_after_quest = true +ij_c_space_after_reference_in_declaration = false +ij_c_space_after_reference_in_rvalue = false +ij_c_space_after_structures_rbrace = true +ij_c_space_after_superclass_colon = true +ij_c_space_after_type_cast = true +ij_c_space_after_visibility_sign_in_method_declaration = true +ij_c_space_before_autorelease_pool_lbrace = true +ij_c_space_before_catch_keyword = true +ij_c_space_before_catch_left_brace = true +ij_c_space_before_catch_parentheses = true +ij_c_space_before_category_parentheses = true +ij_c_space_before_chained_send_message = true +ij_c_space_before_class_left_brace = true +ij_c_space_before_colon = true +ij_c_space_before_colon_in_foreach = false +ij_c_space_before_comma = false +ij_c_space_before_dictionary_literal_colon = false +ij_c_space_before_do_left_brace = true +ij_c_space_before_else_keyword = true +ij_c_space_before_else_left_brace = true +ij_c_space_before_for_left_brace = true +ij_c_space_before_for_parentheses = true +ij_c_space_before_for_semicolon = false +ij_c_space_before_if_left_brace = true +ij_c_space_before_if_parentheses = true +ij_c_space_before_init_list = false +ij_c_space_before_init_list_colon = true +ij_c_space_before_method_call_parentheses = false +ij_c_space_before_method_left_brace = true +ij_c_space_before_method_parentheses = false +ij_c_space_before_namespace_lbrace = true +ij_c_space_before_pointer_in_declaration = false +ij_c_space_before_property_attributes_parentheses = false +ij_c_space_before_protocols_brackets = true +ij_c_space_before_quest = true +ij_c_space_before_reference_in_declaration = true +ij_c_space_before_superclass_colon = true +ij_c_space_before_switch_left_brace = true +ij_c_space_before_switch_parentheses = true +ij_c_space_before_template_call_lt = false +ij_c_space_before_template_declaration_lt = false +ij_c_space_before_try_left_brace = true +ij_c_space_before_while_keyword = true +ij_c_space_before_while_left_brace = true +ij_c_space_before_while_parentheses = true +ij_c_space_between_adjacent_brackets = false +ij_c_space_between_operator_and_punctuator = false +ij_c_space_within_empty_array_initializer_braces = false +ij_c_spaces_around_additive_operators = true +ij_c_spaces_around_assignment_operators = true +ij_c_spaces_around_bitwise_operators = true +ij_c_spaces_around_equality_operators = true +ij_c_spaces_around_lambda_arrow = true +ij_c_spaces_around_logical_operators = true +ij_c_spaces_around_multiplicative_operators = true +ij_c_spaces_around_pm_operators = false +ij_c_spaces_around_relational_operators = true +ij_c_spaces_around_shift_operators = true +ij_c_spaces_around_unary_operator = false +ij_c_spaces_within_array_initializer_braces = false +ij_c_spaces_within_braces = true +ij_c_spaces_within_brackets = false +ij_c_spaces_within_cast_parentheses = false +ij_c_spaces_within_catch_parentheses = false +ij_c_spaces_within_category_parentheses = false +ij_c_spaces_within_empty_braces = false +ij_c_spaces_within_empty_function_call_parentheses = false +ij_c_spaces_within_empty_function_declaration_parentheses = false +ij_c_spaces_within_empty_lambda_capture_list_bracket = false +ij_c_spaces_within_empty_template_call_ltgt = false +ij_c_spaces_within_empty_template_declaration_ltgt = false +ij_c_spaces_within_for_parentheses = false +ij_c_spaces_within_function_call_parentheses = false +ij_c_spaces_within_function_declaration_parentheses = false +ij_c_spaces_within_if_parentheses = false +ij_c_spaces_within_lambda_capture_list_bracket = false +ij_c_spaces_within_method_parameter_type_parentheses = false +ij_c_spaces_within_method_return_type_parentheses = false +ij_c_spaces_within_parentheses = false +ij_c_spaces_within_property_attributes_parentheses = false +ij_c_spaces_within_protocols_brackets = false +ij_c_spaces_within_send_message_brackets = false +ij_c_spaces_within_structured_binding_list_bracket = false +ij_c_spaces_within_switch_parentheses = false +ij_c_spaces_within_template_call_ltgt = false +ij_c_spaces_within_template_declaration_ltgt = false +ij_c_spaces_within_template_double_gt = true +ij_c_spaces_within_while_parentheses = false +ij_c_special_else_if_treatment = true +ij_c_structured_binding_list_align_multiline = false +ij_c_structured_binding_list_align_multiline_bracket = false +ij_c_structured_binding_list_comma_on_next_line = false +ij_c_structured_binding_list_new_line_after_lbracket = false +ij_c_structured_binding_list_new_line_before_rbracket = false +ij_c_structured_binding_list_wrap = off +ij_c_superclass_list_after_colon = never +ij_c_superclass_list_align_multiline = true +ij_c_superclass_list_before_colon = if_long +ij_c_superclass_list_comma_on_next_line = false +ij_c_superclass_list_wrap = on_every_item +ij_c_tag_prefix_of_block_comment = at +ij_c_tag_prefix_of_line_comment = back_slash +ij_c_template_call_arguments_align_multiline = false +ij_c_template_call_arguments_align_multiline_pars = false +ij_c_template_call_arguments_comma_on_next_line = false +ij_c_template_call_arguments_new_line_after_lt = false +ij_c_template_call_arguments_new_line_before_gt = false +ij_c_template_call_arguments_wrap = off +ij_c_template_declaration_function_body_indent = false +ij_c_template_declaration_function_wrap = split_into_lines +ij_c_template_declaration_struct_body_indent = false +ij_c_template_declaration_struct_wrap = split_into_lines +ij_c_template_parameters_align_multiline = false +ij_c_template_parameters_align_multiline_pars = false +ij_c_template_parameters_comma_on_next_line = false +ij_c_template_parameters_new_line_after_lt = false +ij_c_template_parameters_new_line_before_gt = false +ij_c_template_parameters_wrap = off +ij_c_ternary_operation_signs_on_next_line = true +ij_c_ternary_operation_wrap = normal +ij_c_type_qualifiers_placement = before +ij_c_use_modern_casts = true +ij_c_use_setters_in_constructor = true +ij_c_while_brace_force = never +ij_c_while_on_new_line = false +ij_c_wrap_property_declaration = off + +[{*.cmake,CMakeLists.txt}] +ij_cmake_align_multiline_parameters_in_calls = false +ij_cmake_force_commands_case = 2 +ij_cmake_keep_blank_lines_in_code = 2 +ij_cmake_space_before_for_parentheses = true +ij_cmake_space_before_if_parentheses = true +ij_cmake_space_before_method_call_parentheses = false +ij_cmake_space_before_method_parentheses = false +ij_cmake_space_before_while_parentheses = true +ij_cmake_spaces_within_for_parentheses = false +ij_cmake_spaces_within_if_parentheses = false +ij_cmake_spaces_within_method_call_parentheses = false +ij_cmake_spaces_within_method_parentheses = false +ij_cmake_spaces_within_while_parentheses = false + +[{*.har,*.json,*.jsonc}] +indent_size = 2 +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm,*.html,*.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 + +[{*.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_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,build_debug_32.ps1}] +max_line_length = 115 +ij_powershell_align_multiline_binary_operation = false +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 = off +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 = off +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 = off +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 = false +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 = false +ij_powershell_method_annotation_wrap = split_into_lines +ij_powershell_method_brace_style = next_line +ij_powershell_method_call_chain_wrap = off +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 = off +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 = true +ij_powershell_space_before_catch_keyword = true +ij_powershell_space_before_catch_left_brace = true +ij_powershell_space_before_class_left_brace = true +ij_powershell_space_before_colon = true +ij_powershell_space_before_comma = false +ij_powershell_space_before_do_left_brace = true +ij_powershell_space_before_else_keyword = true +ij_powershell_space_before_else_left_brace = true +ij_powershell_space_before_finally_keyword = true +ij_powershell_space_before_finally_left_brace = true +ij_powershell_space_before_for_left_brace = true +ij_powershell_space_before_for_parentheses = true +ij_powershell_space_before_for_semicolon = false +ij_powershell_space_before_if_left_brace = true +ij_powershell_space_before_if_parentheses = true +ij_powershell_space_before_method_call_parentheses = false +ij_powershell_space_before_method_left_brace = true +ij_powershell_space_before_method_parentheses = false +ij_powershell_space_before_switch_left_brace = true +ij_powershell_space_before_switch_parentheses = true +ij_powershell_space_before_try_left_brace = true +ij_powershell_space_before_while_keyword = true +ij_powershell_space_before_while_left_brace = true +ij_powershell_space_before_while_parentheses = true +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 + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] +ij_toml_keep_indents_on_empty_lines = false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1c25c7f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,18 @@ +name: Continuous Integration +on: push + +jobs: + ci: + name: CI + uses: acidicoala/KoalaBox/.github/workflows/build-and-package.yml@a9a657f17a353cd273e3127bdd2c864610008d83 + permissions: + contents: write + with: + modules: >- + ["SmokeAPI"] + + zip_command: > + zip -j $ZIP_NAME + artifacts/*/*.dll + + config: Debug diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5886685 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "KoalaBox"] + path = KoalaBox + url = ../../acidicoala/KoalaBox.git diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/SmokeAPI.iml b/.idea/SmokeAPI.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/SmokeAPI.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/cmake.xml b/.idea/cmake.xml new file mode 100644 index 0000000..dfd4919 --- /dev/null +++ b/.idea/cmake.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..4c2251c --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..62e2e30 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..73a5d51 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3d1fc74 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9124f09 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..b8793f3 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f24f2d9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.22) + +project(SmokeAPI VERSION 1.0.0) + +include(KoalaBox/cmake/KoalaBox.cmake) + +add_subdirectory(KoalaBox EXCLUDE_FROM_ALL) + +set_32_and_64(ORIGINAL_DLL steam_api) +set_32_and_64(STEAMCLIENT_DLL steamclient) +set_32_and_64(VSTDLIB_DLL vstdlib_s) + +configure_version_resource("Free DLC for everyone ʕ ᵔᴥᵔʔ") + +# Setup linker exports + +set_32_and_64(DLL_SUFFIX steam_api win64/steam_api64) +file(GLOB DLL_INPUT "res/dll/*/sdk/redistributable_bin/${DLL_SUFFIX}.dll") + +set( + STEAM_API_EXPORTS + "${CMAKE_CURRENT_SOURCE_DIR}/src/steam_api_exports/steam_api_flat.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/steam_api_exports/steam_api_internal.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/steam_api_exports/steam_api_unversioned.cpp" +) + +configure_linker_exports( + UNDECORATE false + FORWARDED_DLL "${ORIGINAL_DLL}_o" + INPUT_SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/steam_api_exports" + INPUT_DLLS "${DLL_INPUT}" + DEP_SOURCES "${STEAM_API_EXPORTS}" +) + +configure_build_config(extra_build_config) + +set(SMOKE_API_SOURCES + src/smoke_api/smoke_api.cpp + src/smoke_api/smoke_api.hpp + src/steam_api_exports/steam_api_flat.cpp + src/steam_api_exports/steam_api_internal.cpp + src/steam_api_exports/steam_api_unversioned.cpp + src/steam_api_virtuals/isteamapps.cpp + src/steam_api_virtuals/isteamclient.cpp + src/steam_api_virtuals/isteaminventory.cpp + src/steam_api_virtuals/isteamuser.cpp + src/steam_impl/steam_apps.cpp + src/steam_impl/steam_client.cpp + src/steam_impl/steam_impl.hpp + src/steam_impl/steam_inventory.cpp + src/steam_impl/steam_user.cpp + src/steam_functions/steam_functions.cpp + src/steam_functions/steam_functions.hpp + src/steam_types/steam_types.hpp + src/steamclient_exports/steamclient.cpp + src/vstdlib/vstdlib.cpp + src/main.cpp + ${GENERATED_LINKER_EXPORTS} +) + +add_library(SmokeAPI SHARED ${SMOKE_API_SOURCES} ${VERSION_RESOURCE}) + +configure_output_name(${ORIGINAL_DLL}) + +configure_include_directories() + +target_link_libraries(SmokeAPI PRIVATE KoalaBox) diff --git a/KoalaBox b/KoalaBox new file mode 160000 index 0000000..a9a657f --- /dev/null +++ b/KoalaBox @@ -0,0 +1 @@ +Subproject commit a9a657f17a353cd273e3127bdd2c864610008d83 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..e15023f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,12 @@ +Copyright (c) 2022 by acidicoala + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..128663e --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +# 🐨 SmokeAPI ♨ + +**Legit DLC Unlocker for Steamworks** +___ + +**Features** + +- 🔓 Legit DLC Unlocking +- 🪝 Hook mode and Proxy mode installation +- 📝 Configless operation +- 🛅 Inventory emulation + +📥 [Download the latest release](https://github.com/acidicoala/SmokeAPI/releases/latest) + +💬 [Official forum topic]() + +## ℹ Introduction + +
What is SmokeAPI? + +SmokeAPI is a DLC unlocker for the games that are legitimately owned in your Steam account. It attempts to fool games that use Steamworks SDK into thinking that you own the desired DLCs. However, SmokeAPI does not modify the rest of the Steamworks SDK, hence features like multiplayer, achievements, etc. remain fully functional. + +
+ + +
Supported versions + +SmokeAPI aims to support all released SteamAPI versions. When it encountered a new, unsupported interface version, it will fall back on the latest supported version. Below is a list of supported interface versions: + +- ISteamClient v6—v20. (Versions before 6 did not contain any DLC related interfaces) +- ISteamApps v2—v8. (Version 1 did not contain any DLC related functions) +- ISteamUser v12—v21. (Versions before 12 did not contain any DLC related functions) +- ISteamInventory v1—v3. + +Steam inventory does not work in all games with steam inventory because of custom implementation, and online checks. +The list of games where inventory emulation has been shown to work is as follows: +- Hero Siege +- Project Winter +- Euro Truck Simulator 2 + +
+ +## 🛠 Installation Instructions + +This unlocker supports 2 modes of installation: *Hook* mode and *Proxy* mode. +Try installing the unlocker in hook mode first. If it doesn't work, try installing it in proxy mode. + +#### 🪝 Hook mode + +1. Download the latest Koaloader release zip from [Koaloader Releases]. +2. From Koaloader archive unpack `version.dll` from version-32/64, depending on the game bitness, and place it next to the game exe file. +3. Download the latest SmokeAPI release zip from [SmokeAPI Releases]. +4. From SmokeAPI archive unpack `steam_api.dll`/`steam_api64.dll`, depending on the game bitness, rename it to `SmokeAPI.dll`, and place it next to the game exe file. + +#### 🔀 Proxy mode + +1. Find `steam_api.dll`/`steam_api64.dll` file in game directory, and add `_o` to it's name, e.g. `steam_api64_o.dll`. +2. Download the latest SmokeAPI release zip from [SmokeAPI Releases]. +3. From SmokeAPI archive unpack `steam_api.dll`/`steam_api64.dll`, depending on the game bitness, and place it next the original steam_api DLL file. + +If the unlocker is not working as expected, then please fully read the [Generic Unlocker Installation Instructions] before seeking help in the support forum. + +[Koaloader Releases]: https://github.com/acidicoala/Koaloader/releases/latest + +[SmokeAPI Releases]: https://github.com/acidicoala/SmokeAPI/releases/latest + +[Generic Unlocker Installation Instructions]: https://gist.github.com/acidicoala/2c131cb90e251f97c0c1dbeaf2c174dc + +## ⚙ Configuration + +SmokeAPI does not require any manual configuration. By default, it uses the most reasonable options and tries to unlock all DLCs that it can. However, there might be circumstances in which you need more custom-tailored behaviour. In this case you can use a configuration file SmokeAPI.json that you can find here in this repository. To use it, just drop it alongside the SmokeAPI DLL. It will be read upon each launch of a game. In the absence of the config file, default value specified below will be used. + +| Option | Description | Type | Default | +|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|:-------:| +| `$version` | A technical field reserved for future use by tools like GUI config editors | Integer | `1` | +| `logging` | Toggles generation of `*.log` file | Boolean | `false` | +| `hook_steamclient` | When installed in hook mode, this option toggles between hooking steamclient(64).dll and steam_api(64).dll | Boolean | `true` | +| `unlock_all` | Toggles whether all DLCs should be unlocked by default | Boolean | `true` | +| `override` | When `unlock_all` is `true`, this option serves as a blacklist of DLC IDs, which should remain locked. When `unlock_all` is `false`, this option serves as a whitelist of DLC IDs, which should become unlocked | List of Integers | `[]` | +| `dlc_ids` | When game requests list of all DLCs from Steam and the number of registered DLCs is greater than 64, Steam may not return all of them. In this case, SmokeAPI will fetch all released DLCs from Web API. In some games, however (like Monster Hunter: World), web api also doesn't return all possible DLCs. To address this issue, you can specify the missing DLC IDs¹ in this option. For some games (including MH:W), however, it is not necessary because SmokeAPI will also automatically fetch a [manually maintained list of DLC IDs] that are missing from web api | List of Integers | `[]` | +| `auto_inject_inventory` | Toggles whether SmokeAPI should automatically inject a list of all registered inventory items, when a game queries user inventory | Boolean | `true` | +| `inventory_items` | A list of inventory items IDs¹ that will be added in addition to the automatically injected items | List of Integers | `[]` | + +¹ DLC/Item IDs can be obtained from https://steamdb.info. You need to be logged in with your steam account in order to see accurate inventory item IDs. + +[manually maintained list of DLC IDs]: https://github.com/acidicoala/public-entitlements/blob/main/steam/v1/dlc.json + +## 👋 Acknowledgements + +SmokeAPI makes use of the following open source projects: + +- [C++ Requests](https://github.com/libcpr/cpr) +- [JSON for Modern C++](https://github.com/nlohmann/json) +- [PolyHook 2](https://github.com/stevemk14ebr/PolyHook_2_0) +- [spdlog](https://github.com/gabime/spdlog) + +## 📄 License + +This software is licensed under [BSD Zero Clause License], terms of which are available in [LICENSE.txt] + +[BSD Zero Clause License]: https://choosealicense.com/licenses/0bsd/ + +[LICENSE.txt]: LICENSE.txt diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..4a3805f --- /dev/null +++ b/build.ps1 @@ -0,0 +1,5 @@ +Set-Location (Get-Item $PSScriptRoot) + +. ./KoalaBox/build.ps1 @args + +Build-Project diff --git a/res/SmokeAPI.json b/res/SmokeAPI.json new file mode 100644 index 0000000..cd9000e --- /dev/null +++ b/res/SmokeAPI.json @@ -0,0 +1,10 @@ +{ + "$version": 1, + "logging": true, + "hook_steamclient": true, + "unlock_all": true, + "override": [], + "dlc_ids": [], + "auto_inject_inventory": true, + "inventory_items": [] +} diff --git a/res/dll/steamworks_sdk_100/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_100/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..64bcfe8 Binary files /dev/null and b/res/dll/steamworks_sdk_100/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_101/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_101/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..4b384cd Binary files /dev/null and b/res/dll/steamworks_sdk_101/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_102/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_102/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..5ebf334 Binary files /dev/null and b/res/dll/steamworks_sdk_102/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_103/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_103/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..4ae07ef Binary files /dev/null and b/res/dll/steamworks_sdk_103/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_104/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_104/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..1eaae9f Binary files /dev/null and b/res/dll/steamworks_sdk_104/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_105/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_105/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..fb0610e Binary files /dev/null and b/res/dll/steamworks_sdk_105/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_106/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_106/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..597c8dd Binary files /dev/null and b/res/dll/steamworks_sdk_106/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_107/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_107/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..f1e8889 Binary files /dev/null and b/res/dll/steamworks_sdk_107/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_108/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_108/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..7e3a6ab Binary files /dev/null and b/res/dll/steamworks_sdk_108/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_109/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_109/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..79a5000 Binary files /dev/null and b/res/dll/steamworks_sdk_109/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_110/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_110/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..f10948d Binary files /dev/null and b/res/dll/steamworks_sdk_110/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_111/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_111/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..cd7bfe8 Binary files /dev/null and b/res/dll/steamworks_sdk_111/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_111/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_111/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..3217466 Binary files /dev/null and b/res/dll/steamworks_sdk_111/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_112/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_112/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..d82068f Binary files /dev/null and b/res/dll/steamworks_sdk_112/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_112/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_112/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..508d821 Binary files /dev/null and b/res/dll/steamworks_sdk_112/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_113/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_113/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..3296eae Binary files /dev/null and b/res/dll/steamworks_sdk_113/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_113/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_113/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..1b0f817 Binary files /dev/null and b/res/dll/steamworks_sdk_113/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_114/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_114/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..4102e1a Binary files /dev/null and b/res/dll/steamworks_sdk_114/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_114/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_114/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..8f167dc Binary files /dev/null and b/res/dll/steamworks_sdk_114/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_115/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_115/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..a35f7dd Binary files /dev/null and b/res/dll/steamworks_sdk_115/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_115/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_115/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..c54d799 Binary files /dev/null and b/res/dll/steamworks_sdk_115/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_116/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_116/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..c4c4525 Binary files /dev/null and b/res/dll/steamworks_sdk_116/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_116/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_116/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..a77e7b1 Binary files /dev/null and b/res/dll/steamworks_sdk_116/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_117/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_117/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..66aaca3 Binary files /dev/null and b/res/dll/steamworks_sdk_117/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_117/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_117/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..37fd087 Binary files /dev/null and b/res/dll/steamworks_sdk_117/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_118/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_118/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..fa1ee11 Binary files /dev/null and b/res/dll/steamworks_sdk_118/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_118/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_118/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..1122cb9 Binary files /dev/null and b/res/dll/steamworks_sdk_118/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_119/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_119/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..19912c8 Binary files /dev/null and b/res/dll/steamworks_sdk_119/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_119/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_119/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..68a8301 Binary files /dev/null and b/res/dll/steamworks_sdk_119/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_120/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_120/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..1c05ecd Binary files /dev/null and b/res/dll/steamworks_sdk_120/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_120/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_120/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..1de7203 Binary files /dev/null and b/res/dll/steamworks_sdk_120/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_121/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_121/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..2ed16e0 Binary files /dev/null and b/res/dll/steamworks_sdk_121/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_121/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_121/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..f35b306 Binary files /dev/null and b/res/dll/steamworks_sdk_121/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_122/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_122/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..536aae5 Binary files /dev/null and b/res/dll/steamworks_sdk_122/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_122/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_122/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..e05bc77 Binary files /dev/null and b/res/dll/steamworks_sdk_122/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_123a/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_123a/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..2150263 Binary files /dev/null and b/res/dll/steamworks_sdk_123a/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_123a/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_123a/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..358471e Binary files /dev/null and b/res/dll/steamworks_sdk_123a/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_125/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_125/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..21def0e Binary files /dev/null and b/res/dll/steamworks_sdk_125/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_125/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_125/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..50572f3 Binary files /dev/null and b/res/dll/steamworks_sdk_125/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_126a/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_126a/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..e21ea26 Binary files /dev/null and b/res/dll/steamworks_sdk_126a/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_126a/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_126a/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..0f18ec3 Binary files /dev/null and b/res/dll/steamworks_sdk_126a/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_127/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_127/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..804072a Binary files /dev/null and b/res/dll/steamworks_sdk_127/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_127/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_127/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..3696653 Binary files /dev/null and b/res/dll/steamworks_sdk_127/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_128/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_128/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..5271cae Binary files /dev/null and b/res/dll/steamworks_sdk_128/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_128/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_128/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..816152d Binary files /dev/null and b/res/dll/steamworks_sdk_128/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_129a/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_129a/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..b6158e3 Binary files /dev/null and b/res/dll/steamworks_sdk_129a/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_129a/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_129a/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..3698b04 Binary files /dev/null and b/res/dll/steamworks_sdk_129a/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_130/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_130/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..4f9b5de Binary files /dev/null and b/res/dll/steamworks_sdk_130/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_130/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_130/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..8888b2b Binary files /dev/null and b/res/dll/steamworks_sdk_130/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_131/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_131/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..f31d08b Binary files /dev/null and b/res/dll/steamworks_sdk_131/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_131/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_131/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..14aeae6 Binary files /dev/null and b/res/dll/steamworks_sdk_131/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_132/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_132/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..8527d11 Binary files /dev/null and b/res/dll/steamworks_sdk_132/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_132/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_132/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..89ced94 Binary files /dev/null and b/res/dll/steamworks_sdk_132/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_133b/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_133b/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..33eeff7 Binary files /dev/null and b/res/dll/steamworks_sdk_133b/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_133b/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_133b/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..a8d5e50 Binary files /dev/null and b/res/dll/steamworks_sdk_133b/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_134/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_134/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..8ac7a97 Binary files /dev/null and b/res/dll/steamworks_sdk_134/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_134/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_134/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..e67d32e Binary files /dev/null and b/res/dll/steamworks_sdk_134/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_135/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_135/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..f2a0450 Binary files /dev/null and b/res/dll/steamworks_sdk_135/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_135/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_135/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..d3c9268 Binary files /dev/null and b/res/dll/steamworks_sdk_135/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_136/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_136/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..1b06c10 Binary files /dev/null and b/res/dll/steamworks_sdk_136/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_136/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_136/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..6d61394 Binary files /dev/null and b/res/dll/steamworks_sdk_136/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_136_unpublished/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_136_unpublished/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..ea5e422 Binary files /dev/null and b/res/dll/steamworks_sdk_136_unpublished/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_137/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_137/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..535ba15 Binary files /dev/null and b/res/dll/steamworks_sdk_137/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_137/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_137/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..7b4da36 Binary files /dev/null and b/res/dll/steamworks_sdk_137/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_138a/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_138a/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..8e5f502 Binary files /dev/null and b/res/dll/steamworks_sdk_138a/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_138a/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_138a/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..bf392b1 Binary files /dev/null and b/res/dll/steamworks_sdk_138a/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_139/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_139/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..52171b1 Binary files /dev/null and b/res/dll/steamworks_sdk_139/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_139/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_139/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..39e0b99 Binary files /dev/null and b/res/dll/steamworks_sdk_139/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_140/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_140/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..e62639a Binary files /dev/null and b/res/dll/steamworks_sdk_140/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_140/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_140/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..3398d35 Binary files /dev/null and b/res/dll/steamworks_sdk_140/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_141/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_141/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..407934b Binary files /dev/null and b/res/dll/steamworks_sdk_141/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_141/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_141/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..4839b84 Binary files /dev/null and b/res/dll/steamworks_sdk_141/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_142/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_142/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..97158ab Binary files /dev/null and b/res/dll/steamworks_sdk_142/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_142/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_142/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..0013288 Binary files /dev/null and b/res/dll/steamworks_sdk_142/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_143/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_143/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..060b6b9 Binary files /dev/null and b/res/dll/steamworks_sdk_143/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_143/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_143/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..328dade Binary files /dev/null and b/res/dll/steamworks_sdk_143/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_144/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_144/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..060b6b9 Binary files /dev/null and b/res/dll/steamworks_sdk_144/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_144/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_144/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..328dade Binary files /dev/null and b/res/dll/steamworks_sdk_144/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_145/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_145/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..4a06775 Binary files /dev/null and b/res/dll/steamworks_sdk_145/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_145/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_145/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..30d3c91 Binary files /dev/null and b/res/dll/steamworks_sdk_145/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_145/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_145/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..0dcc965 Binary files /dev/null and b/res/dll/steamworks_sdk_145/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_146/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_146/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..2243190 Binary files /dev/null and b/res/dll/steamworks_sdk_146/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_146/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_146/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..ed895bd Binary files /dev/null and b/res/dll/steamworks_sdk_146/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_146/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_146/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..3b25386 Binary files /dev/null and b/res/dll/steamworks_sdk_146/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_147/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_147/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..42ff283 Binary files /dev/null and b/res/dll/steamworks_sdk_147/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_147/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_147/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..79e1725 Binary files /dev/null and b/res/dll/steamworks_sdk_147/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_147/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_147/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..0198a44 Binary files /dev/null and b/res/dll/steamworks_sdk_147/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..97b6446 Binary files /dev/null and b/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..6e0f440 Binary files /dev/null and b/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..ad13f2b Binary files /dev/null and b/res/dll/steamworks_sdk_148a/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_149/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_149/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..8b4a95a Binary files /dev/null and b/res/dll/steamworks_sdk_149/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_149/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_149/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..8a9939b Binary files /dev/null and b/res/dll/steamworks_sdk_149/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_149/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_149/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..a0ded9f Binary files /dev/null and b/res/dll/steamworks_sdk_149/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_150/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_150/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..5cba9b0 Binary files /dev/null and b/res/dll/steamworks_sdk_150/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_150/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_150/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..a3a661a Binary files /dev/null and b/res/dll/steamworks_sdk_150/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_150/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_150/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..2fc976a Binary files /dev/null and b/res/dll/steamworks_sdk_150/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_151/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_151/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..d89750e Binary files /dev/null and b/res/dll/steamworks_sdk_151/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_151/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_151/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..8fb32b4 Binary files /dev/null and b/res/dll/steamworks_sdk_151/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_151/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_151/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..c6e55cf Binary files /dev/null and b/res/dll/steamworks_sdk_151/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_152/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_152/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..638d8eb Binary files /dev/null and b/res/dll/steamworks_sdk_152/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_152/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_152/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..e2b2db0 Binary files /dev/null and b/res/dll/steamworks_sdk_152/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_152/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_152/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..7190117 Binary files /dev/null and b/res/dll/steamworks_sdk_152/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/osx/libsteam_api.dylib b/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/osx/libsteam_api.dylib new file mode 100644 index 0000000..8d3c5ee Binary files /dev/null and b/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/osx/libsteam_api.dylib differ diff --git a/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/steam_api.dll b/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/steam_api.dll new file mode 100644 index 0000000..319bb83 Binary files /dev/null and b/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/steam_api.dll differ diff --git a/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/win64/steam_api64.dll b/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/win64/steam_api64.dll new file mode 100644 index 0000000..e1ca692 Binary files /dev/null and b/res/dll/steamworks_sdk_153a/sdk/redistributable_bin/win64/steam_api64.dll differ diff --git a/res/extra_build_config.gen.h b/res/extra_build_config.gen.h new file mode 100644 index 0000000..f20724c --- /dev/null +++ b/res/extra_build_config.gen.h @@ -0,0 +1,5 @@ +#pragma once + +#define ORIGINAL_DLL "${ORIGINAL_DLL}" +#define STEAMCLIENT_DLL "${STEAMCLIENT_DLL}" +#define VSTDLIB_DLL "${VSTDLIB_DLL}" diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..76e85cc --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,14 @@ +#include + +// This header will be populated at build time +#include + +EXTERN_C [[maybe_unused]] BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID) { + if (reason == DLL_PROCESS_ATTACH) { + smoke_api::init(module); + } else if (reason == DLL_PROCESS_DETACH) { + smoke_api::shutdown(); + } + + return TRUE; +} diff --git a/src/smoke_api/smoke_api.cpp b/src/smoke_api/smoke_api.cpp new file mode 100644 index 0000000..c165cd6 --- /dev/null +++ b/src/smoke_api/smoke_api.cpp @@ -0,0 +1,123 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DETOUR(FUNC) hook::detour_or_warn(original_library, #FUNC, reinterpret_cast(FUNC)); + +namespace smoke_api { + Config config = {}; // NOLINT(cert-err58-cpp) + + HMODULE original_library = nullptr; + + bool is_hook_mode = false; + + Path self_directory; + + void init(HMODULE self_module) { + try { + DisableThreadLibraryCalls(self_module); + + koalabox::project_name = PROJECT_NAME; + + self_directory = loader::get_module_dir(self_module); + + config = config_parser::parse(self_directory / PROJECT_NAME".json"); + + const auto exe_path = Path(win_util::get_module_file_name_or_throw(nullptr)); + const auto exe_name = exe_path.filename().string(); + const auto exe_bitness = util::is_x64() ? 64 : 32; + + if (config.logging) { + logger = file_logger::create(self_directory / fmt::format("{}.log", PROJECT_NAME)); + } + + logger->info("🐨 {} v{}", PROJECT_NAME, PROJECT_VERSION); + + logger->debug(R"(Process name: "{}" [{}-bit])", exe_name, exe_bitness); + + is_hook_mode = hook::is_hook_mode(self_module, ORIGINAL_DLL); + + if (is_hook_mode) { + hook::init(true); + + if (util::strings_are_equal(exe_name, "steam.exe")) { // target vstdlib_s.dll + logger->info("🐨 Detected Koalageddon mode 💥"); + + dll_monitor::init(VSTDLIB_DLL, [](const HMODULE& library) { + original_library = library; + + DETOUR(Coroutine_Create) + }); + } else if (config.hook_steamclient) { // target steamclient(64).dll + logger->info("🪝 Detected hook mode for SteamClient"); + + dll_monitor::init(STEAMCLIENT_DLL, [](const HMODULE& library) { + original_library = library; + + DETOUR(CreateInterface) + }); + } else { // target steam_api.dll + logger->info("🪝 Detected hook mode for Steam_API"); + + dll_monitor::init(ORIGINAL_DLL, [](const HMODULE& library) { + original_library = library; + + DETOUR(SteamInternal_FindOrCreateUserInterface) + DETOUR(SteamInternal_CreateInterface) + DETOUR(SteamApps) + DETOUR(SteamClient) + DETOUR(SteamUser) + + DETOUR(SteamAPI_ISteamApps_BIsSubscribedApp) + DETOUR(SteamAPI_ISteamApps_BIsDlcInstalled) + DETOUR(SteamAPI_ISteamApps_GetDLCCount) + DETOUR(SteamAPI_ISteamApps_BGetDLCDataByIndex) + DETOUR(SteamAPI_ISteamClient_GetISteamGenericInterface) + + DETOUR(SteamAPI_ISteamInventory_GetResultStatus) + DETOUR(SteamAPI_ISteamInventory_GetResultItems) + DETOUR(SteamAPI_ISteamInventory_GetResultItemProperty) + DETOUR(SteamAPI_ISteamInventory_CheckResultSteamID) + DETOUR(SteamAPI_ISteamInventory_GetAllItems) + DETOUR(SteamAPI_ISteamInventory_GetItemsByID) + DETOUR(SteamAPI_ISteamInventory_GetItemDefinitionIDs) + DETOUR(SteamAPI_ISteamInventory_GetItemDefinitionProperty) + }); + } + } else { + logger->info("🔀 Detected proxy mode for Steam_API"); + + original_library = loader::load_original_library(self_directory, ORIGINAL_DLL); + } + logger->info("🚀 Initialization complete"); + } catch (const Exception& ex) { + util::panic(fmt::format("Initialization error: {}", ex.what())); + } + } + + void shutdown() { + try { + if (is_hook_mode) { + dll_monitor::shutdown(); + } else { + win_util::free_library(original_library); + } + + logger->info("💀 Shutdown complete"); + } catch (const Exception& ex) { + logger->error("Shutdown error: {}", ex.what()); + } + } + + bool should_unlock(uint32_t appId) { + return config.unlock_all != config.override.contains(appId); + } + +} diff --git a/src/smoke_api/smoke_api.hpp b/src/smoke_api/smoke_api.hpp new file mode 100644 index 0000000..4f8af98 --- /dev/null +++ b/src/smoke_api/smoke_api.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include // For macros + +#include + +#define GET_ORIGINAL_FUNCTION(FUNC) \ + static const auto FUNC##_o = hook::get_original_function( \ + smoke_api::is_hook_mode, \ + smoke_api::original_library, \ + #FUNC, \ + FUNC \ + ); + +#define GET_ORIGINAL_VIRTUAL_FUNCTION(FUNC) \ + static const auto FUNC##_o = hook::get_original_function( \ + true, \ + smoke_api::original_library, \ + #FUNC, \ + FUNC \ + ); + +namespace smoke_api { + using namespace koalabox; + + struct Config { + uint32_t $version = 1; + bool logging = false; + bool hook_steamclient = true; + bool unlock_all = true; + Set override; + Vector dlc_ids; + bool auto_inject_inventory = true; + Vector inventory_items; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT( + Config, $version, + logging, + unlock_all, + hook_steamclient, + override, + dlc_ids, + auto_inject_inventory, + inventory_items + ) + }; + + extern Config config; + + extern HMODULE original_library; + + extern bool is_hook_mode; + + extern Path self_directory; + + void init(HMODULE self_module); + + void shutdown(); + + bool should_unlock(uint32_t appId); + +} diff --git a/src/steam_api_exports/steam_api_flat.cpp b/src/steam_api_exports/steam_api_flat.cpp new file mode 100644 index 0000000..1b64531 --- /dev/null +++ b/src/steam_api_exports/steam_api_flat.cpp @@ -0,0 +1,200 @@ +#include +#include + +using namespace smoke_api; + +// ISteamApps + +DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t appID) { + return steam_apps::IsSubscribedApp(__func__, appID); +} + +DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t appID) { + return steam_apps::IsDlcInstalled(__func__, appID); +} + +DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps* self) { + return steam_apps::GetDLCCount(__func__, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_GetDLCCount) + + return SteamAPI_ISteamApps_GetDLCCount_o(self); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamApps_BGetDLCDataByIndex( + ISteamApps* self, + int iDLC, + AppId_t* pAppID, + bool* pbAvailable, + char* pchName, + int cchNameBufferSize +) { + return steam_apps::GetDLCDataByIndex(__func__, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamApps_BGetDLCDataByIndex) + + return SteamAPI_ISteamApps_BGetDLCDataByIndex_o( + self, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize + ); + }); +} + +// ISteamUser + +DLL_EXPORT(EUserHasLicenseForAppResult) SteamAPI_ISteamUser_UserHasLicenseForApp( + ISteamUser* self, + CSteamID steamID, + AppId_t appID +) { + return steam_user::UserHasLicenseForApp(__func__, appID, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamUser_UserHasLicenseForApp) + + return SteamAPI_ISteamUser_UserHasLicenseForApp_o(self, steamID, appID); + }); +} + +// ISteamClient + +DLL_EXPORT(void*) SteamAPI_ISteamClient_GetISteamGenericInterface( + ISteamClient* self, + HSteamUser hSteamUser, + HSteamPipe hSteamPipe, + const char* pchVersion +) { + return steam_client::GetGenericInterface(__func__, pchVersion, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamClient_GetISteamGenericInterface) + + return SteamAPI_ISteamClient_GetISteamGenericInterface_o(self, hSteamUser, hSteamPipe, pchVersion); + }); +} + +// ISteamInventory + +DLL_EXPORT(EResult) SteamAPI_ISteamInventory_GetResultStatus( + ISteamInventory* self, + SteamInventoryResult_t resultHandle +) { + return steam_inventory::GetResultStatus(__func__, resultHandle, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetResultStatus) + + return SteamAPI_ISteamInventory_GetResultStatus_o(self, resultHandle); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItems( + ISteamInventory* self, + SteamInventoryResult_t resultHandle, + SteamItemDetails_t* pOutItemsArray, + uint32_t* punOutItemsArraySize +) { + return steam_inventory::GetResultItems( + __func__, resultHandle, pOutItemsArray, punOutItemsArraySize, + [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetResultItems) + + return SteamAPI_ISteamInventory_GetResultItems_o(self, resultHandle, pOutItemsArray, punOutItemsArraySize); + }, + [&](SteamItemDef_t* pItemDefIDs, uint32_t* punItemDefIDsArraySize) { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemDefinitionIDs) + + return SteamAPI_ISteamInventory_GetItemDefinitionIDs_o(self, pItemDefIDs, punItemDefIDsArraySize); + } + ); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItemProperty( + ISteamInventory* self, + SteamInventoryResult_t resultHandle, + uint32_t unItemIndex, + const char* pchPropertyName, + char* pchValueBuffer, + const uint32_t* punValueBufferSizeOut +) { + return steam_inventory::GetResultItemProperty( + __func__, resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(SteamAPI_ISteamInventory_GetResultItemProperty) + + return SteamAPI_ISteamInventory_GetResultItemProperty_o( + self, resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut + ); + } + ); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_CheckResultSteamID( + ISteamInventory* self, + SteamInventoryResult_t resultHandle, + CSteamID steamIDExpected +) { + return steam_inventory::CheckResultSteamID(__func__, resultHandle, steamIDExpected, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(SteamAPI_ISteamInventory_CheckResultSteamID) + + return SteamAPI_ISteamInventory_CheckResultSteamID_o(self, resultHandle, steamIDExpected); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetAllItems( + ISteamInventory* self, + SteamInventoryResult_t* pResultHandle +) { + return steam_inventory::GetAllItems(__func__, pResultHandle, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetAllItems) + + return SteamAPI_ISteamInventory_GetAllItems_o(self, pResultHandle); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemsByID( + ISteamInventory* self, + SteamInventoryResult_t* pResultHandle, + const SteamItemInstanceID_t* pInstanceIDs, + uint32_t unCountInstanceIDs +) { + return steam_inventory::GetItemsByID(__func__, pResultHandle, pInstanceIDs, unCountInstanceIDs, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemsByID) + + return SteamAPI_ISteamInventory_GetItemsByID_o(self, pResultHandle, pInstanceIDs, unCountInstanceIDs); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_SerializeResult( + ISteamInventory* self, + SteamInventoryResult_t resultHandle, + void* pOutBuffer, + uint32_t* punOutBufferSize +) { + return steam_inventory::SerializeResult(__func__, resultHandle, pOutBuffer, punOutBufferSize, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_SerializeResult) + + return SteamAPI_ISteamInventory_SerializeResult_o(self, resultHandle, pOutBuffer, punOutBufferSize); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionIDs( + ISteamInventory* self, + SteamItemDef_t* pItemDefIDs, + uint32_t* punItemDefIDsArraySize +) { + return steam_inventory::GetItemDefinitionIDs(__func__, pItemDefIDs, punItemDefIDsArraySize, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemDefinitionIDs) + + return SteamAPI_ISteamInventory_GetItemDefinitionIDs_o(self, pItemDefIDs, punItemDefIDsArraySize); + }); +} + +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionProperty( + ISteamInventory* self, + SteamItemDef_t iDefinition, + const char* pchPropertyName, + char* pchValueBuffer, + uint32_t* punValueBufferSizeOut +) { + return steam_inventory::GetItemDefinitionProperty( + __func__, iDefinition, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() { + GET_ORIGINAL_FUNCTION(SteamAPI_ISteamInventory_GetItemDefinitionProperty) + + return SteamAPI_ISteamInventory_GetItemDefinitionProperty_o( + self, iDefinition, pchPropertyName, pchValueBuffer, punValueBufferSizeOut + ); + } + ); +} diff --git a/src/steam_api_exports/steam_api_internal.cpp b/src/steam_api_exports/steam_api_internal.cpp new file mode 100644 index 0000000..63b3f62 --- /dev/null +++ b/src/steam_api_exports/steam_api_internal.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +using namespace smoke_api; + +DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser hSteamUser, const char* version) { + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_FUNCTION(SteamInternal_FindOrCreateUserInterface) + + return SteamInternal_FindOrCreateUserInterface_o(hSteamUser, version); + }); +} + +DLL_EXPORT(void*) SteamInternal_CreateInterface(const char* version) { + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_FUNCTION(SteamInternal_CreateInterface) + + return SteamInternal_CreateInterface_o(version); + }); +} diff --git a/src/steam_api_exports/steam_api_unversioned.cpp b/src/steam_api_exports/steam_api_unversioned.cpp new file mode 100644 index 0000000..80290ae --- /dev/null +++ b/src/steam_api_exports/steam_api_unversioned.cpp @@ -0,0 +1,83 @@ +#include +#include +#include + +#include + +#include + +using namespace smoke_api; +using namespace steam_functions; + +/** + * Searches the `.rdata` section of the original dll for the full interface version string + * Results are cached for performance. + */ +String get_versioned_interface(const String& version_prefix, const String& fallback) { + static Map version_map; + + if (not version_map.contains(version_prefix)) { + try { + String rdata = win_util::get_pe_section_data_or_throw(original_library, ".rdata"); + + std::regex regex(version_prefix + "\\d{3}"); + std::smatch match; + if (std::regex_search(rdata, match, regex)) { + version_map[version_prefix] = match[0]; + return version_map[version_prefix]; + } + + throw util::exception("No match found for '{}'", version_prefix); + } catch (const Exception& ex) { + logger->error( + "Failed to get versioned interface: {}." + "Falling back to version {}", ex.what(), fallback + ); + + version_map[version_prefix] = version_prefix + fallback; + } + } + + return version_map[version_prefix]; +} + +DLL_EXPORT(void*) SteamClient() { + static auto version = get_versioned_interface(STEAM_CLIENT, "006"); + + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_FUNCTION(SteamClient) + + return SteamClient_o(); + }); +} + +DLL_EXPORT(void*) SteamApps() { + static auto version = get_versioned_interface(STEAM_APPS, "002"); + + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_FUNCTION(SteamApps) + + return SteamApps_o(); + }); +} + +DLL_EXPORT(void*) SteamUser() { + static auto version = get_versioned_interface(STEAM_USER, "012"); + + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_FUNCTION(SteamUser) + + return SteamUser_o(); + }); +} + + +DLL_EXPORT(void*) SteamInventory() { + static auto version = get_versioned_interface(STEAM_INVENTORY, "001"); + + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_FUNCTION(SteamInventory) + + return SteamInventory_o(); + }); +} diff --git a/src/steam_api_virtuals/isteamapps.cpp b/src/steam_api_virtuals/isteamapps.cpp new file mode 100644 index 0000000..f80e261 --- /dev/null +++ b/src/steam_api_virtuals/isteamapps.cpp @@ -0,0 +1,38 @@ +#include +#include + +using namespace smoke_api; + +VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters) + return steam_apps::IsSubscribedApp(__func__, appID); +} + +VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t appID)) { // NOLINT(misc-unused-parameters) + return steam_apps::IsDlcInstalled(__func__, appID); +} + +VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS()) { + return steam_apps::GetDLCCount(__func__, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamApps_GetDLCCount) + + return ISteamApps_GetDLCCount_o(ARGS()); + }); +} + +VIRTUAL(bool) ISteamApps_BGetDLCDataByIndex( + PARAMS( + int iDLC, + AppId_t* pAppID, + bool* pbAvailable, + char* pchName, + int cchNameBufferSize + ) +) { + return steam_apps::GetDLCDataByIndex(__func__, iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamApps_BGetDLCDataByIndex) + + return ISteamApps_BGetDLCDataByIndex_o( + ARGS(iDLC, pAppID, pbAvailable, pchName, cchNameBufferSize) + ); + }); +} diff --git a/src/steam_api_virtuals/isteamclient.cpp b/src/steam_api_virtuals/isteamclient.cpp new file mode 100644 index 0000000..349793a --- /dev/null +++ b/src/steam_api_virtuals/isteamclient.cpp @@ -0,0 +1,61 @@ +#include +#include +#include + +using namespace smoke_api; + +VIRTUAL(void*) ISteamClient_GetISteamApps( + PARAMS( + HSteamUser hSteamUser, + HSteamPipe hSteamPipe, + const char* version + ) +) { + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamApps) + + return ISteamClient_GetISteamApps_o(ARGS(hSteamUser, hSteamPipe, version)); + }); +} + +VIRTUAL(void*) ISteamClient_GetISteamUser( + PARAMS( + HSteamUser hSteamUser, + HSteamPipe hSteamPipe, + const char* version + ) +) { + return steam_client::GetGenericInterface(__func__, version, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamUser) + + return ISteamClient_GetISteamUser_o(ARGS(hSteamUser, hSteamPipe, version)); + }); +} + +VIRTUAL(void*) ISteamClient_GetISteamGenericInterface( + PARAMS( + HSteamUser hSteamUser, + HSteamPipe hSteamPipe, + const char* pchVersion + ) +) { + return steam_client::GetGenericInterface(__func__, pchVersion, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamGenericInterface) + + return ISteamClient_GetISteamGenericInterface_o(ARGS(hSteamUser, hSteamPipe, pchVersion)); + }); +} + +VIRTUAL(void*) ISteamClient_GetISteamInventory( + PARAMS( + HSteamUser hSteamUser, + HSteamPipe hSteamPipe, + const char* pchVersion + ) +) { + return steam_client::GetGenericInterface(__func__, pchVersion, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamClient_GetISteamInventory) + + return ISteamClient_GetISteamInventory_o(ARGS(hSteamUser, hSteamPipe, pchVersion)); + }); +} diff --git a/src/steam_api_virtuals/isteaminventory.cpp b/src/steam_api_virtuals/isteaminventory.cpp new file mode 100644 index 0000000..b348549 --- /dev/null +++ b/src/steam_api_virtuals/isteaminventory.cpp @@ -0,0 +1,141 @@ +#include +#include + +using namespace smoke_api; + +VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(SteamInventoryResult_t resultHandle)) { + return steam_inventory::GetResultStatus(__func__, resultHandle, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetResultStatus) + + return ISteamInventory_GetResultStatus_o(ARGS(resultHandle)); + }); +} + +VIRTUAL(bool) ISteamInventory_GetResultItems( + PARAMS( + SteamInventoryResult_t resultHandle, + SteamItemDetails_t* pOutItemsArray, + uint32_t * punOutItemsArraySize + ) +) { + + return steam_inventory::GetResultItems( + __func__, resultHandle, pOutItemsArray, punOutItemsArraySize, + [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetResultItems) + + return ISteamInventory_GetResultItems_o(ARGS(resultHandle, pOutItemsArray, punOutItemsArraySize)); + }, + [&](SteamItemDef_t* pItemDefIDs, uint32_t* punItemDefIDsArraySize) { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemDefinitionIDs) + + return ISteamInventory_GetItemDefinitionIDs_o(ARGS(pItemDefIDs, punItemDefIDsArraySize)); + } + ); +} + +VIRTUAL(bool) ISteamInventory_GetResultItemProperty( + PARAMS( + SteamInventoryResult_t resultHandle, + uint32_t unItemIndex, + const char* pchPropertyName, + char* pchValueBuffer, + const uint32_t* punValueBufferSizeOut + ) +) { + return steam_inventory::GetResultItemProperty( + __func__, resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetResultItemProperty) + + return ISteamInventory_GetResultItemProperty_o( + ARGS(resultHandle, unItemIndex, pchPropertyName, pchValueBuffer, punValueBufferSizeOut) + ); + } + ); +} + +VIRTUAL(bool) ISteamInventory_CheckResultSteamID( + PARAMS( + SteamInventoryResult_t resultHandle, + CSteamID steamIDExpected + ) +) { + return steam_inventory::CheckResultSteamID(__func__, resultHandle, steamIDExpected, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_CheckResultSteamID) + + return ISteamInventory_CheckResultSteamID_o(ARGS(resultHandle, steamIDExpected)); + }); +} + +VIRTUAL(bool) ISteamInventory_GetAllItems(PARAMS(SteamInventoryResult_t* pResultHandle)) { + return steam_inventory::GetAllItems(__func__, pResultHandle, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetAllItems) + + return ISteamInventory_GetAllItems_o(ARGS(pResultHandle)); + }); +} + +VIRTUAL(bool) ISteamInventory_GetItemsByID( + PARAMS( + SteamInventoryResult_t* pResultHandle, + const SteamItemInstanceID_t* pInstanceIDs, + uint32_t unCountInstanceIDs + ) +) { + return steam_inventory::GetItemsByID(__func__, pResultHandle, pInstanceIDs, unCountInstanceIDs, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemsByID) + + return ISteamInventory_GetItemsByID_o(ARGS(pResultHandle, pInstanceIDs, unCountInstanceIDs)); + }); +} + +VIRTUAL(bool) ISteamInventory_SerializeResult( + PARAMS( + SteamInventoryResult_t resultHandle, + void* pOutBuffer, + uint32_t * punOutBufferSize + ) +) { + return steam_inventory::SerializeResult(__func__, resultHandle, pOutBuffer, punOutBufferSize, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_SerializeResult) + + return ISteamInventory_SerializeResult_o(ARGS(resultHandle, pOutBuffer, punOutBufferSize)); + }); +} + +VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs( + PARAMS( + SteamItemDef_t* pItemDefIDs, + uint32_t * punItemDefIDsArraySize + ) +) { + return steam_inventory::GetItemDefinitionIDs(__func__, pItemDefIDs, punItemDefIDsArraySize, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemDefinitionIDs) + + return ISteamInventory_GetItemDefinitionIDs_o(ARGS(pItemDefIDs, punItemDefIDsArraySize)); + }); +} + +VIRTUAL(bool) ISteamInventory_GetItemDefinitionProperty( + PARAMS( + SteamItemDef_t iDefinition, + const char* pchPropertyName, + char* pchValueBuffer, + uint32_t * punValueBufferSizeOut + ) +) { + return steam_inventory::GetItemDefinitionProperty( + __func__, iDefinition, pchPropertyName, pchValueBuffer, punValueBufferSizeOut, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamInventory_GetItemDefinitionProperty) + + return ISteamInventory_GetItemDefinitionProperty_o( + ARGS( + iDefinition, + pchPropertyName, + pchValueBuffer, + punValueBufferSizeOut + ) + ); + } + ); +} diff --git a/src/steam_api_virtuals/isteamuser.cpp b/src/steam_api_virtuals/isteamuser.cpp new file mode 100644 index 0000000..a7617a9 --- /dev/null +++ b/src/steam_api_virtuals/isteamuser.cpp @@ -0,0 +1,12 @@ +#include +#include + +using namespace smoke_api; + +VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID steamID, AppId_t appID)) { + return steam_user::UserHasLicenseForApp(__func__, appID, [&]() { + GET_ORIGINAL_VIRTUAL_FUNCTION(ISteamUser_UserHasLicenseForApp) + + return ISteamUser_UserHasLicenseForApp_o(ARGS(steamID, appID)); + }); +} diff --git a/src/steam_functions/steam_functions.cpp b/src/steam_functions/steam_functions.cpp new file mode 100644 index 0000000..347d55f --- /dev/null +++ b/src/steam_functions/steam_functions.cpp @@ -0,0 +1,270 @@ +#include +#include + +#include +#include + +#include + +namespace steam_functions { + + typedef Map> FunctionOrdinalMap; + + FunctionOrdinalMap steam_client_ordinal_map = { // NOLINT(cert-err58-cpp) + {"ISteamClient_GetISteamApps", + { + {6, 16}, + {7, 18}, + {8, 15}, + {9, 16}, + {12, 15}, + } + }, + {"ISteamClient_GetISteamUser", + { + {6, 6}, + {7, 5}, + } + }, + {"ISteamClient_GetISteamUtils", + { + {6, 12}, + {7, 9}, + } + }, + {"ISteamClient_GetISteamGenericInterface", + { + {7, 14}, + {8, 13}, + {12, 12}, + } + }, + {"ISteamClient_GetISteamInventory", + { + {17, 34}, + {18, 35}, + } + } + }; + + FunctionOrdinalMap steam_apps_ordinal_map = { // NOLINT(cert-err58-cpp) + {"ISteamApps_BIsSubscribedApp", {{2, 6}}}, + {"ISteamApps_BIsDlcInstalled", {{2, 7}}}, + {"ISteamApps_GetDLCCount", {{2, 10}}}, + {"ISteamApps_BGetDLCDataByIndex", {{2, 11}}}, + }; + + FunctionOrdinalMap steam_user_ordinal_map = { // NOLINT(cert-err58-cpp) + {"ISteamUser_UserHasLicenseForApp", { + {12, 15}, + {13, 16}, + {15, 17}, + }} + }; + + FunctionOrdinalMap steam_utils_ordinal_map = { // NOLINT(cert-err58-cpp) + {"ISteamUtils_GetAppID", {{2, 9}}} + }; + + FunctionOrdinalMap steam_inventory_ordinal_map = { // NOLINT(cert-err58-cpp) + {"ISteamInventory_GetResultStatus", {{1, 0}}}, + {"ISteamInventory_GetResultItems", {{1, 1}}}, + {"ISteamInventory_GetResultItemProperty", {{2, 2}}}, + {"ISteamInventory_CheckResultSteamID", {{1, 3}, {2, 4}}}, + {"ISteamInventory_GetAllItems", {{1, 5}, {2, 6}}}, + {"ISteamInventory_GetItemsByID", {{1, 6}, {2, 7}}}, + {"ISteamInventory_SerializeResult", {{1, 7}, {2, 8}}}, + {"ISteamInventory_GetItemDefinitionIDs", {{1, 20}, {2, 21}}}, + {"ISteamInventory_GetItemDefinitionProperty", {{1, 21}, {2, 22}}}, + }; + + int extract_version_number( + const String& version_string, + const String& prefix, + int min_version, + int max_version + ) { + logger->debug("Hooking interface '{}'", version_string); + + try { + const auto version_number = stoi(version_string.substr(prefix.length())); + + if (version_number < min_version) { + util::panic("Unsupported old version of {}: {}", version_string, version_number); + } + + if (version_number > max_version) { + logger->warn( + "Unsupported new version of {}: {}. Fallback version {} will be used", + version_string, version_number, max_version + ); + } + + return version_number; + } catch (const std::exception& ex) { + util::panic("Failed to extract version number from: '{}'", version_string); + } + } + + int get_ordinal(const FunctionOrdinalMap& ordinal_map, const String& function_name, int interface_version) { + const auto& map = ordinal_map.at(function_name); + + for (auto it = map.rbegin(); it != map.rend(); it++) { + const auto [version, ordinal] = *it; + + if (interface_version >= version) { + return ordinal; + } + } + + util::panic("Invalid interface version ({}) for function {}", interface_version, function_name); + } + +#define HOOK(MAP, FUNC) \ + hook::swap_virtual_func( \ + interface, \ + #FUNC, \ + get_ordinal(MAP, #FUNC, version_number), \ + (FunctionAddress) (FUNC) \ + ); + +#define HOOK_STEAM_CLIENT(FUNC) HOOK(steam_client_ordinal_map, FUNC) +#define HOOK_STEAM_APPS(FUNC) HOOK(steam_apps_ordinal_map, FUNC) +#define HOOK_STEAM_USER(FUNC) HOOK(steam_user_ordinal_map, FUNC) +#define HOOK_STEAM_INVENTORY(FUNC) HOOK(steam_inventory_ordinal_map, FUNC) + + void hook_virtuals(void* interface, const String& version_string) { + if (interface == nullptr) { + // Game has tried to use an interface before initializing steam api + return; + } + + static Set hooked_interfaces; + + if (hooked_interfaces.contains(interface)) { + // This interface is already hooked. Skipping it. + return; + } + + static std::mutex section; + std::lock_guard guard(section); + + if (version_string.starts_with(STEAM_CLIENT)) { + const auto version_number = extract_version_number(version_string, STEAM_CLIENT, 6, 20); + + HOOK_STEAM_CLIENT(ISteamClient_GetISteamApps) + HOOK_STEAM_CLIENT(ISteamClient_GetISteamUser) + if (version_number >= 7) { + HOOK_STEAM_CLIENT(ISteamClient_GetISteamGenericInterface) + } + if (version_number >= 17) { + HOOK_STEAM_CLIENT(ISteamClient_GetISteamInventory) + } + } else if (version_string.starts_with(STEAM_APPS)) { + const auto version_number = extract_version_number(version_string, STEAM_APPS, 2, 8); + + HOOK_STEAM_APPS(ISteamApps_BIsSubscribedApp) + + if (version_number >= 3) { + HOOK_STEAM_APPS(ISteamApps_BIsDlcInstalled) + } + + if (version_number >= 4) { + HOOK_STEAM_APPS(ISteamApps_GetDLCCount) + HOOK_STEAM_APPS(ISteamApps_BGetDLCDataByIndex) + } + } else if (version_string.starts_with(STEAM_USER)) { + const auto version_number = extract_version_number(version_string, STEAM_USER, 9, 21); + + if (version_number >= 12) { + HOOK_STEAM_USER(ISteamUser_UserHasLicenseForApp) + } + } else if (version_string.starts_with(STEAM_INVENTORY)) { + const auto version_number = extract_version_number(version_string, STEAM_INVENTORY, 1, 3); + + HOOK_STEAM_INVENTORY(ISteamInventory_GetResultStatus) + HOOK_STEAM_INVENTORY(ISteamInventory_GetResultItems) + HOOK_STEAM_INVENTORY(ISteamInventory_CheckResultSteamID) + HOOK_STEAM_INVENTORY(ISteamInventory_GetAllItems) + HOOK_STEAM_INVENTORY(ISteamInventory_GetItemsByID) + HOOK_STEAM_INVENTORY(ISteamInventory_SerializeResult) + HOOK_STEAM_INVENTORY(ISteamInventory_GetItemDefinitionIDs) + HOOK_STEAM_INVENTORY(ISteamInventory_GetItemDefinitionProperty) + + if (version_number >= 2) { + HOOK_STEAM_INVENTORY(ISteamInventory_GetResultItemProperty) + } + } else { + return; + } + + hooked_interfaces.insert(interface); + } + + HSteamPipe get_steam_pipe_or_throw() { + const auto& steam_api_module = win_util::get_module_handle_or_throw(ORIGINAL_DLL); + void* GetHSteamPipe_address; + try { + GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw( + steam_api_module, "SteamAPI_GetHSteamPipe" + ); + } catch (const Exception& ex) { + GetHSteamPipe_address = (void*) win_util::get_proc_address_or_throw( + steam_api_module, "GetHSteamPipe" + ); + } + typedef HSteamPipe (__cdecl* GetHSteamPipe_t)(); + const auto GetHSteamPipe_o = (GetHSteamPipe_t) GetHSteamPipe_address; + return GetHSteamPipe_o(); + } + + template + F get_virtual_function(void* interface, int ordinal) { + auto* v_table = (void***) interface; + return (F) (*v_table)[ordinal]; + } + + template + auto call_virtual_function(void* interface, F function, Args... args) { +#ifdef _WIN64 + void* RCX = interface; +#else + void* ECX = interface; + void* EDX = nullptr; +#endif + + return function(ARGS(args...)); + } + + uint32_t get_app_id_or_throw() { + // Get CreateInterface + const auto& steam_client_module = win_util::get_module_handle_or_throw(STEAMCLIENT_DLL); + auto* CreateInterface_address = (void*) win_util::get_proc_address_or_throw( + steam_client_module, "CreateInterface" + ); + auto* CreateInterface_o = PLH::FnCast(CreateInterface_address, CreateInterface); + + // Get ISteamClient + int result; + auto* i_steam_client = CreateInterface_o("SteamClient006", &result); + if (i_steam_client == nullptr) { + throw util::exception("Failed to obtain SteamClient006 interface. Result: {}", result); + } + + // Get GetISteamUtils + typedef void*** (__fastcall* GetISteamUtils_t)(PARAMS(HSteamPipe hSteamPipe, const char* version)); + const auto steam_utils_ordinal = steam_client_ordinal_map["ISteamClient_GetISteamUtils"][6]; + const auto GetISteamUtils_o = get_virtual_function(i_steam_client, steam_utils_ordinal); + + // Get ISteamUtils + const auto steam_pipe = get_steam_pipe_or_throw(); + auto* i_steam_utils = call_virtual_function(i_steam_client, GetISteamUtils_o, steam_pipe, "SteamUtils002"); + + // Get GetAppID + typedef uint32_t (__fastcall* GetAppID_t)(PARAMS()); + const auto get_app_id_ordinal = steam_utils_ordinal_map["ISteamUtils_GetAppID"][2]; + const auto GetAppID_o = get_virtual_function(i_steam_utils, get_app_id_ordinal); + + return call_virtual_function(i_steam_utils, GetAppID_o); + } +} diff --git a/src/steam_functions/steam_functions.hpp b/src/steam_functions/steam_functions.hpp new file mode 100644 index 0000000..5bd5fb4 --- /dev/null +++ b/src/steam_functions/steam_functions.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include + +#include + +/** + * By default, virtual functions are declared with __thiscall + * convention, which is normal since they are class members. + * But it presents an issue for us, since we cannot pass *this + * pointer as a function argument. This is because *this + * pointer is passed via register ECX in __thiscall + * convention. Hence, to resolve this issue we declare our + * hooked functions with __fastcall convention, to trick + * the compiler into reading ECX & EDX registers as 1st + * and 2nd function arguments respectively. Similarly, __fastcall + * makes the compiler push the first argument into the ECX register, + * which mimics the __thiscall calling convention. Register EDX + * is not used anywhere in this case, but we still pass it along + * to conform to the __fastcall convention. This all applies + * to the x86 architecture. + * + * In x86-64 however, there is only one calling convention, + * so __fastcall is simply ignored. However, RDX in this case + * will store the 1st actual argument to the function, so we + * have to omit it from the function signature. + * + * The macros below implement the above-mentioned considerations. + */ +#ifdef _WIN64 +#define PARAMS(...) void* RCX, ##__VA_ARGS__ +#define ARGS(...) RCX, ##__VA_ARGS__ +#define THIS RCX +#else +#define PARAMS(...) void* ECX, void* EDX, ##__VA_ARGS__ +#define ARGS(...) ECX, EDX, ##__VA_ARGS__ +#define THIS ECX +#endif + +#define DLL_EXPORT(TYPE) extern "C" __declspec( dllexport ) TYPE __cdecl +#define VIRTUAL(TYPE) __declspec(noinline) TYPE __fastcall + +class ISteamClient; + +class ISteamApps; + +class ISteamUser; + +class ISteamInventory; + +typedef __int32 HSteamPipe; +typedef __int32 HSteamUser; +typedef uint32_t AppId_t; +typedef uint64_t CSteamID; + +typedef uint32_t HCoroutine; + +// ISteamClient +VIRTUAL(void*) ISteamClient_GetISteamApps(PARAMS(HSteamUser, HSteamPipe, const char*)); +VIRTUAL(void*) ISteamClient_GetISteamUser(PARAMS(HSteamUser, HSteamPipe, const char*)); +VIRTUAL(void*) ISteamClient_GetISteamGenericInterface(PARAMS(HSteamUser, HSteamPipe, const char*)); +VIRTUAL(void*) ISteamClient_GetISteamInventory(PARAMS(HSteamUser, HSteamPipe, const char*)); + +// ISteamApps +VIRTUAL(bool) ISteamApps_BIsSubscribedApp(PARAMS(AppId_t)); +VIRTUAL(bool) ISteamApps_BIsDlcInstalled(PARAMS(AppId_t)); +VIRTUAL(int) ISteamApps_GetDLCCount(PARAMS()); +VIRTUAL(bool) ISteamApps_BGetDLCDataByIndex(PARAMS(int, AppId_t*, bool*, char*, int)); + +// ISteamUser +VIRTUAL(EUserHasLicenseForAppResult) ISteamUser_UserHasLicenseForApp(PARAMS(CSteamID, AppId_t)); + +// ISteamInventory +VIRTUAL(EResult) ISteamInventory_GetResultStatus(PARAMS(uint32_t)); +VIRTUAL(bool) ISteamInventory_GetResultItems(PARAMS(SteamInventoryResult_t, SteamItemDetails_t*, uint32_t*)); +VIRTUAL(bool) ISteamInventory_GetResultItemProperty( + PARAMS(SteamInventoryResult_t, uint32_t, const char*, char*, const uint32_t*) +); +VIRTUAL(bool) ISteamInventory_GetAllItems(PARAMS(SteamInventoryResult_t*)); +VIRTUAL(bool) ISteamInventory_GetItemsByID(PARAMS(SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t)); +VIRTUAL(bool) ISteamInventory_SerializeResult(PARAMS(SteamInventoryResult_t, void*, uint32_t*)); +VIRTUAL(bool) ISteamInventory_GetItemDefinitionIDs(PARAMS(SteamItemDef_t*, uint32_t*)); +VIRTUAL(bool) ISteamInventory_GetItemDefinitionProperty(PARAMS(SteamItemDef_t, const char*, char*, uint32_t*)); +VIRTUAL(bool) ISteamInventory_CheckResultSteamID(PARAMS(SteamInventoryResult_t, CSteamID)); + +// API + +DLL_EXPORT(void*) CreateInterface(const char*, int*); +DLL_EXPORT(void*) SteamInternal_FindOrCreateUserInterface(HSteamUser, const char*); +DLL_EXPORT(void*) SteamInternal_CreateInterface(const char*); +DLL_EXPORT(void*) SteamApps(); +DLL_EXPORT(void*) SteamClient(); +DLL_EXPORT(void*) SteamUser(); + +// Flat interfaces + +DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsSubscribedApp(ISteamApps*, AppId_t); +DLL_EXPORT(bool) SteamAPI_ISteamApps_BIsDlcInstalled(ISteamApps*, AppId_t); +DLL_EXPORT(int) SteamAPI_ISteamApps_GetDLCCount(ISteamApps*); +DLL_EXPORT(bool) SteamAPI_ISteamApps_BGetDLCDataByIndex(ISteamApps*, int, AppId_t*, bool*, char*, int); +DLL_EXPORT(EUserHasLicenseForAppResult) SteamAPI_ISteamUser_UserHasLicenseForApp(ISteamUser*, CSteamID, AppId_t); +DLL_EXPORT(void*) SteamAPI_ISteamClient_GetISteamGenericInterface(ISteamClient*, HSteamUser, HSteamPipe, const char*); +DLL_EXPORT(EResult) SteamAPI_ISteamInventory_GetResultStatus(ISteamInventory*, SteamInventoryResult_t); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItems( + ISteamInventory*, SteamInventoryResult_t, SteamItemDetails_t*, uint32_t* +); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetResultItemProperty( + ISteamInventory*, SteamInventoryResult_t, uint32_t, const char*, char*, const uint32_t* +); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_CheckResultSteamID(ISteamInventory*, SteamInventoryResult_t, CSteamID); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetAllItems(ISteamInventory*, SteamInventoryResult_t*); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemsByID( + ISteamInventory*, SteamInventoryResult_t*, const SteamItemInstanceID_t*, uint32_t +); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_SerializeResult(ISteamInventory*, SteamInventoryResult_t, void*, uint32_t*); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionIDs(ISteamInventory*, SteamItemDef_t*, uint32_t*); +DLL_EXPORT(bool) SteamAPI_ISteamInventory_GetItemDefinitionProperty( + ISteamInventory*, SteamItemDef_t, const char*, char*, uint32_t* +); + +// vstdlib + +DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, struct CoroutineData* data); + +namespace steam_functions { + using namespace koalabox; + + const String STEAM_APPS = "STEAMAPPS_INTERFACE_VERSION"; // NOLINT(cert-err58-cpp) + const String STEAM_CLIENT = "SteamClient"; // NOLINT(cert-err58-cpp) + const String STEAM_USER = "SteamUser"; // NOLINT(cert-err58-cpp) + const String STEAM_INVENTORY = "STEAMINVENTORY_INTERFACE_V"; // NOLINT(cert-err58-cpp) + + void hook_virtuals(void* interface, const String& version_string); + uint32_t get_app_id_or_throw(); +} diff --git a/src/steam_impl/steam_apps.cpp b/src/steam_impl/steam_apps.cpp new file mode 100644 index 0000000..805f927 --- /dev/null +++ b/src/steam_impl/steam_apps.cpp @@ -0,0 +1,268 @@ +#include +#include + +#include + +#include + +using namespace smoke_api; + +constexpr auto max_dlc = 64; + +Vector cached_dlcs; +int original_dlc_count = 0; + +Path get_cache_path() { + static const auto path = self_directory / "SmokeAPI.cache.json"; + return path; +} + +void read_from_cache(const String& app_id_str) { + try { + const auto text = io::read_file(get_cache_path()); + if (text.empty()) { + return; + } + + auto json = nlohmann::json::parse(text); + + cached_dlcs = json[app_id_str]["dlc"].get(); + + logger->debug("Read {} DLCs from cache", cached_dlcs.size()); + } catch (const Exception& ex) { + logger->error("Error reading DLCs from cache: {}", ex.what()); + } + +} + +void save_to_cache(const String& app_id_str) { + try { + logger->debug("Saving {} DLCs to cache", cached_dlcs.size()); + + const nlohmann::json json = { + {app_id_str, { + {"dlc", cached_dlcs} + }} + }; + + io::write_file(get_cache_path(), json.dump(2)); + } catch (const Exception& ex) { + logger->error("Error saving DLCs to cache: {}", ex.what()); + } +} + +void fetch_and_cache_dlcs() { + uint32_t app_id; + try { + app_id = steam_functions::get_app_id_or_throw(); + logger->info("Detected App ID: {}", app_id); + } catch (const Exception& ex) { + logger->error("Failed to get app ID: {}", ex.what()); + return; + } + + const auto app_id_str = std::to_string(app_id); + + const auto fetch_from_steam = [&]() { + const auto url = fmt::format("https://store.steampowered.com/dlc/{}/ajaxgetdlclist", app_id_str); + const auto res = cpr::Get(cpr::Url{url}); + + if (res.status_code != cpr::status::HTTP_OK) { + throw util::exception( + "Steam Web API didn't responded with HTTP_OK result. Code: {}, Error: {},\n" + "Headers:\n{}\nBody:\n{}", + res.status_code, (int) res.error.code, res.raw_header, res.text + ); + } + + const auto json = nlohmann::json::parse(res.text); + + + if (json["success"] != 1) { + throw util::exception("Web API responded with 'success': 1."); + } + + Vector dlcs; + + for (const auto& dlc: json["dlcs"]) { + const auto app_id = dlc["appid"].get(); + dlcs.emplace_back(std::stoi(app_id)); + } + + return dlcs; + }; + + const auto fetch_from_github = [&]() { + const String url = "https://raw.githubusercontent.com/acidicoala/public-entitlements/main/steam/v1/dlc.json"; + const auto res = cpr::Get(cpr::Url{url}); + + if (res.status_code != cpr::status::HTTP_OK) { + throw util::exception( + "Github Web API didn't responded with HTTP_OK result. Code: {}, Error: {},\n" + "Headers:\n{}\nBody:\n{}", + res.status_code, (int) res.error.code, res.raw_header, res.text + ); + } + + const auto json = nlohmann::json::parse(res.text); + + if (json.contains(app_id_str)) { + return json[app_id_str].get(); + } + + return Vector{}; + }; + + try { + read_from_cache(app_id_str); + + auto list1 = fetch_from_steam(); + auto list2 = fetch_from_github(); + list1.insert(list1.end(), list2.begin(), list2.end()); + Set fetched_dlcs(list1.begin(), list1.end()); + + if (fetched_dlcs.size() > cached_dlcs.size()) { + cached_dlcs = Vector(fetched_dlcs.begin(), fetched_dlcs.end()); + } + + save_to_cache(app_id_str); + } catch (const Exception& ex) { + logger->error("Failed to fetch DLC: {}", ex.what()); + } +} + + +namespace steam_apps{ + + bool IsSubscribedApp(const String& function_name, AppId_t appID) { + const auto subscribed = should_unlock(appID); + + logger->info("{} -> App ID: {}, Subscribed: {}", function_name, appID, subscribed); + + return subscribed; + } + + bool IsDlcInstalled(const String& function_name, AppId_t appID) { + const auto installed = should_unlock(appID); + + logger->info("{} -> App ID: {}, Installed: {}", function_name, appID, installed); + + return installed; + } + + int GetDLCCount(const String& function_name, const std::function& original_function) { + static std::mutex section; + std::lock_guard guard(section); + + // Compute count only once + static int total_count = [&]() { + original_dlc_count = original_function(); + logger->debug("{} -> Original DLC count: {}", function_name, original_dlc_count); + + const auto injected_count = static_cast(config.dlc_ids.size()); + logger->debug("{} -> Injected DLC count: {}", function_name, injected_count); + + if (original_dlc_count < max_dlc) { + // Steamworks may max out this value at 64, depending on how much unowned DLCs the user has. + // Despite this limit, some games with more than 64 DLCs still keep using this method. + // This means we have to fetch full list of IDs from web api. + + return original_dlc_count + injected_count; + } + + logger->debug("Game has {} or more DLCs. Fetching DLCs from a web API.", max_dlc); + fetch_and_cache_dlcs(); + + const auto fetched_count = static_cast(cached_dlcs.size()); + logger->debug("{} -> Fetched/cached DLC count: {}", function_name, fetched_count); + + return fetched_count + injected_count; + }(); + + logger->info("{} -> Responding with DLC count: {}", function_name, total_count); + + return total_count; + } + + bool GetDLCDataByIndex( + const String& function_name, + int iDLC, + AppId_t* pAppID, + bool* pbAvailable, + char* pchName, + int cchNameBufferSize, + const std::function& original_function + ) { + const auto print_dlc_info = [&](const String& tag) { + logger->info( + "{} -> [{}] index: {}, App ID: {}, available: {}, name: '{}'", + function_name, tag, iDLC, *pAppID, *pbAvailable, pchName + ); + }; + + const auto fill_dlc_info = [&](const AppId_t id) { + *pAppID = id; + *pbAvailable = should_unlock(id); + + auto name = fmt::format("DLC #{} with ID: {} ", iDLC, id); + name = name.substr(0, cchNameBufferSize); + *name.rbegin() = '\0'; + memcpy_s(pchName, cchNameBufferSize, name.c_str(), name.size()); + }; + + const auto inject_dlc = [&](const int index) { + if (index >= config.dlc_ids.size()) { + logger->error("{} -> Out of bounds injected index: {}", function_name, index); + return false; + } + + const auto app_id = config.dlc_ids[index]; + fill_dlc_info(app_id); + print_dlc_info("injected"); + return true; + }; + + // Original response + if (cached_dlcs.empty()) { + // Original DLC index + if (iDLC < original_dlc_count) { + const auto success = original_function(); + + if (success) { + *pbAvailable = should_unlock(*pAppID); + print_dlc_info("original"); + } else { + logger->warn("{} -> original function failed for index: {}", function_name, iDLC); + } + return success; + } + + // Injected DLC index (after original) + const auto index = iDLC - original_dlc_count; + return inject_dlc(index); + } + + // Cached response + const auto total_size = cached_dlcs.size() + config.dlc_ids.size(); + if (iDLC < 0 or iDLC >= total_size) { + logger->error( + "{} -> Game accessed out of bounds DLC index: {}. Total size: {}", + function_name, iDLC, total_size + ); + return false; + } + + // Cached index + if (iDLC < cached_dlcs.size()) { + const auto app_id = cached_dlcs[iDLC]; + fill_dlc_info(app_id); + print_dlc_info("cached"); + return true; + } + + // Injected DLC index (after cached) + + const auto index = iDLC - static_cast(cached_dlcs.size()); + return inject_dlc(index); + } +} diff --git a/src/steam_impl/steam_client.cpp b/src/steam_impl/steam_client.cpp new file mode 100644 index 0000000..07a8a05 --- /dev/null +++ b/src/steam_impl/steam_client.cpp @@ -0,0 +1,21 @@ +#include + +namespace steam_client{ + + void* GetGenericInterface( + const String& function_name, + const String& interface_version, + const std::function& original_function + ) { + logger->debug("{} -> Version: '{}'", function_name, interface_version); + + auto* const interface = original_function(); + + logger->debug("{} -> Result: {}", function_name, fmt::ptr(interface)); + + steam_functions::hook_virtuals(interface, interface_version); + + return interface; + } + +} diff --git a/src/steam_impl/steam_impl.hpp b/src/steam_impl/steam_impl.hpp new file mode 100644 index 0000000..884516c --- /dev/null +++ b/src/steam_impl/steam_impl.hpp @@ -0,0 +1,116 @@ +#include + +using namespace koalabox; + +namespace steam_apps { + + bool IsSubscribedApp(const String& function_name, AppId_t appID); + + bool IsDlcInstalled(const String& function_name, AppId_t appID); + + int GetDLCCount(const String& function_name, const std::function& original_function); + + bool GetDLCDataByIndex( + const String& function_name, + int iDLC, + AppId_t* pAppID, + bool* pbAvailable, + char* pchName, + int cchNameBufferSize, + const std::function& original_function + ); + +} + +namespace steam_user { + + EUserHasLicenseForAppResult UserHasLicenseForApp( + const String& function_name, + AppId_t appID, + const std::function& original_function + ); + +} + +namespace steam_client { + + void* GetGenericInterface( + const String& function_name, + const String& interface_version, + const std::function& original_function + ); + +} + +namespace steam_inventory { + + EResult GetResultStatus( + const String& function_name, + SteamInventoryResult_t resultHandle, + const std::function& original_function + ); + + bool GetResultItems( + const String& function_name, + SteamInventoryResult_t resultHandle, + SteamItemDetails_t* pOutItemsArray, + uint32_t* punOutItemsArraySize, + const std::function& original_function, + const std::function& get_item_definition_ids + ); + + bool GetResultItemProperty( + const String& function_name, + SteamInventoryResult_t resultHandle, + uint32_t unItemIndex, + const char* pchPropertyName, + char* pchValueBuffer, + const uint32_t* punValueBufferSizeOut, + const std::function& original_function + ); + + bool GetAllItems( + const String& function_name, + const SteamInventoryResult_t* pResultHandle, + const std::function& original_function + ); + + bool GetItemsByID( + const String& function_name, + SteamInventoryResult_t* pResultHandle, + const SteamItemInstanceID_t* pInstanceIDs, + uint32_t unCountInstanceIDs, + const std::function& original_function + ); + + bool SerializeResult( + const String& function_name, + SteamInventoryResult_t resultHandle, + void* pOutBuffer, + uint32_t* punOutBufferSize, + const std::function& original_function + ); + + bool GetItemDefinitionIDs( + const String& function_name, + const SteamItemDef_t* pItemDefIDs, + uint32_t* punItemDefIDsArraySize, + const std::function& original_function + ); + + bool GetItemDefinitionProperty( + const String& function_name, + SteamItemDef_t iDefinition, + const char* pchPropertyName, + char* pchValueBuffer, + const uint32_t* punValueBufferSizeOut, + const std::function& original_function + ); + + bool CheckResultSteamID( + const String& function_name, + SteamInventoryResult_t resultHandle, + CSteamID steamIDExpected, + const std::function& original_function + ); +} diff --git a/src/steam_impl/steam_inventory.cpp b/src/steam_impl/steam_inventory.cpp new file mode 100644 index 0000000..5dd6f53 --- /dev/null +++ b/src/steam_impl/steam_inventory.cpp @@ -0,0 +1,258 @@ +#include +#include + +namespace steam_inventory { + + EResult GetResultStatus( + const String& function_name, + const SteamInventoryResult_t resultHandle, + const std::function& original_function + ) { + const auto status = original_function(); + + logger->debug("{} -> handle: {}, status: {}", function_name, resultHandle, (int) status); + + return status; + } + + bool GetResultItems( + const String& function_name, + const SteamInventoryResult_t resultHandle, + SteamItemDetails_t* pOutItemsArray, + uint32_t* punOutItemsArraySize, + const std::function& original_function, + const std::function& get_item_definition_ids + ) { + static std::mutex section; + std::lock_guard guard(section); + + const auto success = original_function(); + + auto print_item = [](const String& tag, const SteamItemDetails_t& item) { + logger->debug( + " [{}] definitionId: {}, itemId: {}, quantity: {}, flags: {}", + tag, item.m_iDefinition, item.m_itemId, item.m_unQuantity, item.m_unFlags + ); + }; + + if (not success) { + logger->debug("{} -> original result is false", function_name); + return success; + } + + if (punOutItemsArraySize == nullptr) { + logger->error("{} -> arraySize pointer is null", function_name); + return success; + } + + logger->debug( + "{} -> handle: {}, pOutItemsArray: {}, arraySize: {}", + function_name, resultHandle, fmt::ptr(pOutItemsArray), *punOutItemsArraySize + ); + + static uint32_t original_count = 0; + const auto injected_count = smoke_api::config.inventory_items.size(); + + // Automatically get inventory items from steam + static Vector auto_inventory_items; + if (smoke_api::config.auto_inject_inventory) { + static std::once_flag flag; + std::call_once(flag, [&]() { + uint32_t count = 0; + if (get_item_definition_ids(nullptr, &count)) { + auto_inventory_items.resize(count); + get_item_definition_ids(auto_inventory_items.data(), &count); + } + }); + } + const auto auto_injected_count = auto_inventory_items.size(); + + + if (not pOutItemsArray) { + // If pOutItemsArray is NULL then we must set the array size. + original_count = *punOutItemsArraySize; + *punOutItemsArraySize += auto_injected_count + injected_count; + logger->debug( + "{} -> Original count: {}, Total count: {}", + function_name, original_count, *punOutItemsArraySize + ); + } else { + // Otherwise, we modify the array + for (int i = 0; i < original_count; i++) { + print_item("original", pOutItemsArray[i]); + } + + static auto new_item = [](SteamItemDef_t id) { + return SteamItemDetails_t{ + .m_itemId=id, + .m_iDefinition=id, + .m_unQuantity=1, + .m_unFlags=0, + }; + }; + + for (int i = 0; i < auto_injected_count; i++) { + auto& item = pOutItemsArray[original_count + i]; + const auto item_def_id = auto_inventory_items[i]; + + item = new_item(item_def_id); + + print_item("auto-injected", item); + } + + for (int i = 0; i < injected_count; i++) { + auto& item = pOutItemsArray[original_count + auto_injected_count + i]; + const auto item_def_id = smoke_api::config.inventory_items[i]; + + item = new_item(item_def_id); + + print_item("injected", item); + } + } + + return success; + } + + bool GetResultItemProperty( + const String& function_name, + SteamInventoryResult_t resultHandle, + uint32_t unItemIndex, + const char* pchPropertyName, + char* pchValueBuffer, + const uint32_t* punValueBufferSizeOut, + const std::function& original_function + ) { + const auto success = original_function(); + + if (!success) { + logger->warn("{} -> result is false", function_name); + return false; + } + + logger->debug( + "{} -> handle: {}, index: {}, propertyName: '{}', buffer: {}", + function_name, resultHandle, unItemIndex, pchPropertyName, + String(pchValueBuffer, *punValueBufferSizeOut - 1) + ); + + return success; + } + + bool GetAllItems( + const String& function_name, + const SteamInventoryResult_t* pResultHandle, + const std::function& original_function + ) { + const auto success = original_function(); + + logger->debug("{} -> handle: {}", function_name, fmt::ptr(pResultHandle)); + + return success; + } + + + bool GetItemsByID( + const String& function_name, + SteamInventoryResult_t* pResultHandle, + const SteamItemInstanceID_t* pInstanceIDs, + const uint32_t unCountInstanceIDs, + const std::function& original_function + ) { + const auto success = original_function(); + + logger->trace("{} -> handle: {}", function_name, fmt::ptr(pResultHandle)); + + if (success && pInstanceIDs != nullptr) { + for (int i = 0; i < unCountInstanceIDs; i++) { + logger->trace(" index: {}, itemId: {}", i, pInstanceIDs[i]); + } + } + + return success; + } + + bool SerializeResult( + const String& function_name, + SteamInventoryResult_t resultHandle, + void* pOutBuffer, + uint32_t* punOutBufferSize, + const std::function& original_function + ) { + const auto success = original_function(); + + if (pOutBuffer != nullptr) { + String buffer((char*) pOutBuffer, *punOutBufferSize); + logger->debug("{} -> handle: {}, buffer: '{}'", function_name, resultHandle, buffer); + } else { + logger->debug("{} -> handle: {}, size: '{}'", function_name, resultHandle, *punOutBufferSize); + } + + return success; + } + + bool GetItemDefinitionIDs( + const String& function_name, + const SteamItemDef_t* pItemDefIDs, + uint32_t* punItemDefIDsArraySize, + const std::function& original_function + ) { + const auto success = original_function(); + + if (!success) { + logger->warn("{} -> result is false", function_name); + return false; + } + + if (punItemDefIDsArraySize) { + logger->debug("{} -> size: '{}'", function_name, *punItemDefIDsArraySize); + } + + if (pItemDefIDs) { // Definitions were copied + for (int i = 0; i < *punItemDefIDsArraySize; i++) { + const auto& def = pItemDefIDs[i]; + logger->debug(" Definition index: '{}', ID: {}", i, def); + } + } + + return success; + } + + bool GetItemDefinitionProperty( + const String& function_name, + SteamItemDef_t iDefinition, + const char* pchPropertyName, + char* pchValueBuffer, + const uint32_t* punValueBufferSizeOut, + const std::function& original_function + ) { + const auto success = original_function(); + + if (!success) { + logger->warn("{} -> result is false", function_name); + return false; + } + + logger->debug( + "{} -> Definition ID: {}, name: '{}', buffer: '{}'", + function_name, iDefinition, pchPropertyName, String(pchValueBuffer, *punValueBufferSizeOut - 1) + ); + + return success; + } + + bool CheckResultSteamID( + const String& function_name, + SteamInventoryResult_t resultHandle, + CSteamID steamIDExpected, + const std::function& original_function + ) { + const auto result = original_function(); + + logger->debug( + "{} -> handle: {}, steamID: {}, original result: {}", + function_name, resultHandle, steamIDExpected, result + ); + + return true; + } +} diff --git a/src/steam_impl/steam_user.cpp b/src/steam_impl/steam_user.cpp new file mode 100644 index 0000000..36a81c1 --- /dev/null +++ b/src/steam_impl/steam_user.cpp @@ -0,0 +1,27 @@ +#include +#include + +namespace steam_user { + + EUserHasLicenseForAppResult UserHasLicenseForApp( + const String& function_name, + AppId_t appID, + const std::function& original_function + ) { + const auto result = original_function(); + + if (result == k_EUserHasLicenseResultNoAuth) { + logger->warn("{} -> App ID: {}, Result: NoAuth", function_name, appID); + return result; + } + + const auto has_license = smoke_api::should_unlock(appID); + + logger->info("{} -> App ID: {}, HasLicense: {}", function_name, appID, has_license); + + return has_license + ? k_EUserHasLicenseResultHasLicense + : k_EUserHasLicenseResultDoesNotHaveLicense; + } + +} diff --git a/src/steam_types/steam_types.hpp b/src/steam_types/steam_types.hpp new file mode 100644 index 0000000..94cf3da --- /dev/null +++ b/src/steam_types/steam_types.hpp @@ -0,0 +1,146 @@ +#pragma once + +// results from UserHasLicenseForApp +enum EUserHasLicenseForAppResult { + k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app + k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app + k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated +}; + +typedef uint32_t SteamInventoryResult_t; +typedef uint64_t SteamItemInstanceID_t; +typedef uint32_t SteamItemDef_t; + +struct SteamItemDetails_t { + SteamItemInstanceID_t m_itemId; + uint32_t m_iDefinition; + uint16_t m_unQuantity; + uint16_t m_unFlags; // see ESteamItemFlags +}; + +enum EResult { + k_EResultNone = 0, // no result + k_EResultOK = 1, // success + k_EResultFail = 2, // generic failure + k_EResultNoConnection = 3, // no/failed network connection +// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed + k_EResultInvalidPassword = 5, // password/ticket is invalid + k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere + k_EResultInvalidProtocolVer = 7, // protocol version is incorrect + k_EResultInvalidParam = 8, // a parameter is incorrect + k_EResultFileNotFound = 9, // file was not found + k_EResultBusy = 10, // called method busy - action not taken + k_EResultInvalidState = 11, // called object was in an invalid state + k_EResultInvalidName = 12, // name is invalid + k_EResultInvalidEmail = 13, // email is invalid + k_EResultDuplicateName = 14, // name is not unique + k_EResultAccessDenied = 15, // access is denied + k_EResultTimeout = 16, // operation timed out + k_EResultBanned = 17, // VAC2 banned + k_EResultAccountNotFound = 18, // account not found + k_EResultInvalidSteamID = 19, // steamID is invalid + k_EResultServiceUnavailable = 20, // The requested service is currently unavailable + k_EResultNotLoggedOn = 21, // The user is not logged on + k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party) + k_EResultEncryptionFailure = 23, // Encryption or Decryption failed + k_EResultInsufficientPrivilege = 24, // Insufficient privilege + k_EResultLimitExceeded = 25, // Too much of a good thing + k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes) + k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired + k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again + k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time + k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user + k_EResultIPNotFound = 31, // IP address not found + k_EResultPersistFailed = 32, // failed to write change to the data store + k_EResultLockingFailed = 33, // failed to acquire access lock for this operation + k_EResultLogonSessionReplaced = 34, + k_EResultConnectFailed = 35, + k_EResultHandshakeFailed = 36, + k_EResultIOFailure = 37, + k_EResultRemoteDisconnect = 38, + k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested + k_EResultBlocked = 40, // a user didn't allow it + k_EResultIgnored = 41, // target is ignoring sender + k_EResultNoMatch = 42, // nothing matching the request found + k_EResultAccountDisabled = 43, + k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now + k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available + k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin + k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol. + k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another. + k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed. + k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait + k_EResultSuspended = 51, // Long running operation (content download) suspended/paused + k_EResultCancelled = 52, // Operation canceled (typically by user: content download) + k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable + k_EResultDiskFull = 54, // Operation canceled - not enough disk space. + k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed + k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side + k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account + k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid + k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first + k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files + k_EResultIllegalPassword = 61, // The requested new password is not legal + k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer ) + k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure + k_EResultCannotUseOldPassword = 64, // The requested new password is not legal + k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid + k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent + k_EResultHardwareNotCapableOfIPT = 67, // + k_EResultIPTInitError = 68, // + k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user + k_EResultFacebookQueryError = 70, // Facebook query returned an error + k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired + k_EResultIPLoginRestrictionFailed = 72, + k_EResultAccountLockedDown = 73, + k_EResultAccountLogonDeniedVerifiedEmailRequired = 74, + k_EResultNoMatchingURL = 75, + k_EResultBadResponse = 76, // parse failure, missing field, etc. + k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password + k_EResultValueOutOfRange = 78, // the value entered is outside the acceptable range + k_EResultUnexpectedError = 79, // something happened that we didn't expect to ever happen + k_EResultDisabled = 80, // The requested service has been configured to be unavailable + k_EResultInvalidCEGSubmission = 81, // The set of files submitted to the CEG server are not valid ! + k_EResultRestrictedDevice = 82, // The device being used is not allowed to perform this action + k_EResultRegionLocked = 83, // The action could not be complete because it is region restricted + k_EResultRateLimitExceeded = 84, // Temporary rate limit exceeded, try again later, different from k_EResultLimitExceeded which may be permanent + k_EResultAccountLoginDeniedNeedTwoFactor = 85, // Need two-factor code to login + k_EResultItemDeleted = 86, // The thing we're trying to access has been deleted + k_EResultAccountLoginDeniedThrottle = 87, // login attempt failed, try to throttle response to possible attacker + k_EResultTwoFactorCodeMismatch = 88, // two factor code mismatch + k_EResultTwoFactorActivationCodeMismatch = 89, // activation code for two-factor didn't match + k_EResultAccountAssociatedToMultiplePartners = 90, // account has been associated with multiple partners + k_EResultNotModified = 91, // data not modified + k_EResultNoMobileDevice = 92, // the account does not have a mobile device associated with it + k_EResultTimeNotSynced = 93, // the time presented is out of range or tolerance + k_EResultSmsCodeFailed = 94, // SMS code failure (no match, none pending, etc.) + k_EResultAccountLimitExceeded = 95, // Too many accounts access this resource + k_EResultAccountActivityLimitExceeded = 96, // Too many changes to this account + k_EResultPhoneActivityLimitExceeded = 97, // Too many changes to this phone + k_EResultRefundToWallet = 98, // Cannot refund to payment method, must use wallet + k_EResultEmailSendFailure = 99, // Cannot send an email + k_EResultNotSettled = 100, // Can't perform operation till payment has settled + k_EResultNeedCaptcha = 101, // Needs to provide a valid captcha + k_EResultGSLTDenied = 102, // a game server login token owned by this token's owner has been banned + k_EResultGSOwnerDenied = 103, // game server owner is denied for other reason (account lock, community ban, vac ban, missing phone) + k_EResultInvalidItemType = 104, // the type of thing we were requested to act on is invalid + k_EResultIPBanned = 105, // the ip address has been banned from taking this action + k_EResultGSLTExpired = 106, // this token has expired from disuse; can be reset for use + k_EResultInsufficientFunds = 107, // user doesn't have enough wallet funds to complete the action + k_EResultTooManyPending = 108, // There are too many of this thing pending already + k_EResultNoSiteLicensesFound = 109, // No site licenses found + k_EResultWGNetworkSendExceeded = 110, // the WG couldn't send a response because we exceeded max network send size + k_EResultAccountNotFriends = 111, // the user is not mutually friends + k_EResultLimitedUserAccount = 112, // the user is limited + k_EResultCantRemoveItem = 113, // item can't be removed + k_EResultAccountDeleted = 114, // account has been deleted + k_EResultExistingUserCancelledLicense = 115, // A license for this already exists, but cancelled + k_EResultCommunityCooldown = 116, // access is denied because of a community cooldown (probably from support profile data resets) + k_EResultNoLauncherSpecified = 117, // No launcher was specified, but a launcher was needed to choose correct realm for operation. + k_EResultMustAgreeToSSA = 118, // User must agree to china SSA or global SSA before login + k_EResultLauncherMigrated = 119, // The specified launcher type is no longer supported; the user should be directed elsewhere + k_EResultSteamRealmMismatch = 120, // The user's realm does not match the realm of the requested resource + k_EResultInvalidSignature = 121, // signature check did not match + k_EResultParseFailure = 122, // Failed to parse input + k_EResultNoVerifiedPhone = 123, // account does not have a verified phone number +}; diff --git a/src/steamclient_exports/steamclient.cpp b/src/steamclient_exports/steamclient.cpp new file mode 100644 index 0000000..ebcc710 --- /dev/null +++ b/src/steamclient_exports/steamclient.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +using namespace smoke_api; + +DLL_EXPORT(void*) CreateInterface(const char* interface_string, int* out_result) { + return steam_client::GetGenericInterface(__func__, interface_string, [&]() { + GET_ORIGINAL_FUNCTION(CreateInterface) + + return CreateInterface_o(interface_string, out_result); + }); +} diff --git a/src/vstdlib/vstdlib.cpp b/src/vstdlib/vstdlib.cpp new file mode 100644 index 0000000..ee3abfa --- /dev/null +++ b/src/vstdlib/vstdlib.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include + +using namespace smoke_api; + +#define DETOUR(FUNC, address) hook::detour((FunctionAddress) (address), #FUNC, (FunctionAddress) (FUNC)); + +VIRTUAL(bool) SharedLicensesLockStatus(PARAMS(void* arg)) { // NOLINT(misc-unused-parameters) + logger->debug("{} -> instance: {}, arg: {}", __func__, fmt::ptr(THIS), fmt::ptr(arg)); + return true; +} + +VIRTUAL(bool) SharedLibraryStopPlaying(PARAMS(void* arg)) { // NOLINT(misc-unused-parameters) + logger->debug("{} -> instance: {}, arg: {}", __func__, fmt::ptr(THIS), fmt::ptr(arg)); + return true; +} + +struct CallbackData { + [[maybe_unused]] void* pad1[1]; + void* set_callback_name_address; + [[maybe_unused]] void* pad15[15]; + void* callback_address; +}; + +struct CoroutineData { + CallbackData* callback_data; + [[maybe_unused]] uint32_t pad3[3]; + const char* callback_name; +}; + +VIRTUAL(void) set_callback_name(PARAMS(const char** p_name)) { + GET_ORIGINAL_FUNCTION(set_callback_name) + + set_callback_name_o(ARGS(p_name)); + + static auto hooked_functions = 0; + + if (hooked_functions == 2) { + return; + } + + auto* const data = (CoroutineData*) THIS; + + if (data && data->callback_name) { + const auto name = String(data->callback_name); + // logger->trace("{} -> instance: {}, name: '{}'", __func__, fmt::ptr(THIS), name); + + if (name == "SharedLicensesLockStatus") { + static std::once_flag flag; + std::call_once(flag, [&]() { + DETOUR(SharedLicensesLockStatus, data->callback_data->callback_address) + hooked_functions++; + }); + } else if (name == "SharedLibraryStopPlaying") { + static std::once_flag flag; + std::call_once(flag, [&]() { + DETOUR(SharedLibraryStopPlaying, data->callback_data->callback_address) + hooked_functions++; + }); + } + } +} + +/** + * Initially, callback data passed into this function is not complete, + * hence we must hook an interface method that sets the callback name. + */ +DLL_EXPORT(HCoroutine) Coroutine_Create(void* callback_address, CoroutineData* data) { + GET_ORIGINAL_FUNCTION(Coroutine_Create) + + const auto result = Coroutine_Create_o(callback_address, data); + + // Coroutine callback appears to be always the same + static std::once_flag flag; + std::call_once(flag, [&]() { + logger->debug("Coroutine_Create -> callback: {}, data: {}", callback_address, fmt::ptr(data)); + DETOUR(set_callback_name, data->callback_data->set_callback_name_address) + }); + + return result; +}