Repository: acidicoala/UplayR2Unlocker
Branch: master
Commit: 375d505083e7
Files: 25
Total size: 38.7 KB
Directory structure:
gitextract_u6dmngb8/
├── .editorconfig
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .gitmodules
├── .idea/
│ ├── .gitignore
│ ├── UplayR2Unlocker2.iml
│ ├── cmake.xml
│ ├── codeStyles/
│ │ ├── Project.xml
│ │ └── codeStyleConfig.xml
│ ├── inspectionProfiles/
│ │ └── Project_Default.xml
│ ├── modules.xml
│ └── vcs.xml
├── CMakeLists.txt
├── LICENSE.txt
├── README.md
├── build.ps1
├── res/
│ ├── UplayR2Unlocker.jsonc
│ └── extra_build_config.gen.h
├── src/
│ ├── main.cpp
│ ├── pch.hpp
│ ├── unlocker/
│ │ ├── unlocker.cpp
│ │ └── unlocker.hpp
│ └── upc/
│ ├── upc.cpp
│ └── upc.hpp
└── vcpkg.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
[*]
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
[{*.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_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 = true
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 = false
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 = 1
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 = 1
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 = false
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 = true
ij_c_function_parameters_new_line_before_rpar = true
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 = 1
ij_c_keep_blank_lines_before_right_brace = 1
ij_c_keep_blank_lines_in_code = 1
ij_c_keep_blank_lines_in_declarations = 1
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 = true
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 = true
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 = false
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 = true
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_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_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_continuation_indent_size = 4
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 = 1
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
[{*.apinotes,*.yaml,*.yml,.clang-format,.clang-tidy,_clang-format}]
indent_size = 2
================================================
FILE: .github/workflows/ci.yml
================================================
name: Continuous Integration
on: push
jobs:
ci:
name: CI
uses: acidicoala/KoalaBox/.github/workflows/build-and-package.yml@db2ee8fa2074b05f8d876f54e2a36486d0e69258
permissions:
contents: write
with:
modules: >-
["UplayR2Unlocker"]
zip_command: >
zip -j $ZIP_NAME
artifacts/*/*.dll
config: Debug
================================================
FILE: .gitignore
================================================
/build
================================================
FILE: .gitmodules
================================================
[submodule "vcpkg"]
path = vcpkg
url = https://github.com/microsoft/vcpkg.git
[submodule "KoalaBox"]
path = KoalaBox
url = https://acidicoala@github.com/acidicoala/KoalaBox.git
================================================
FILE: .idea/.gitignore
================================================
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
/misc.xml
================================================
FILE: .idea/UplayR2Unlocker2.iml
================================================
================================================
FILE: .idea/cmake.xml
================================================
================================================
FILE: .idea/codeStyles/Project.xml
================================================
================================================
FILE: .idea/codeStyles/codeStyleConfig.xml
================================================
================================================
FILE: .idea/inspectionProfiles/Project_Default.xml
================================================
================================================
FILE: .idea/modules.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.21)
include(KoalaBox/KoalaBox.cmake)
configure_globals(KoalaBox)
project(UplayR2Unlocker VERSION 3.0.1)
set_32_and_64(ORIGINAL_DLL upc_r2_loader upc_r2_loader64)
set_32_and_64(LEGACY_ORIGINAL_DLL uplay_r2_loader uplay_r2_loader64)
set_32_and_64(STORE_DLL uplay_r2 uplay_r264)
configure_version_resource("https://github.com/acidicoala/UplayR2Unlocker")
set_32_and_64(DLL_INPUT "${RES_DIR}/uplay_r2.dll" "${RES_DIR}/upc_r2_loader64.dll")
configure_exports_generator()
configure_linker_exports(
false
"${ORIGINAL_DLL}_o"
${DLL_INPUT}
"${SRC_DIR}/upc"
"${SRC_DIR}/upc/upc.cpp"
)
configure_build_config(extra_build_config)
configure_library(
SHARED
${SRC_DIR}/unlocker/unlocker.cpp
${SRC_DIR}/upc/upc.cpp
${SRC_DIR}/main.cpp
${KOALABOX_SRC_DIR}/koalabox/dll_monitor/dll_monitor.cpp
${KOALABOX_SRC_DIR}/koalabox/hook/hook.cpp
${KOALABOX_SRC_DIR}/koalabox/loader/loader.cpp
${KOALABOX_SRC_DIR}/koalabox/file_logger/file_logger.cpp
${KOALABOX_SRC_DIR}/koalabox/util/util.cpp
${KOALABOX_SRC_DIR}/koalabox/win_util/win_util.cpp
${LINKER_EXPORTS}
)
configure_precompile_headers(${CMAKE_PROJECT_NAME} ${SRC_DIR}/pch.hpp)
configure_output_name(${ORIGINAL_DLL})
configure_include_directories()
configure_dependencies(${CMAKE_PROJECT_NAME} cpr PolyHook_2 nlohmann_json spdlog)
================================================
FILE: LICENSE.txt
================================================
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.
================================================
FILE: README.md
================================================
# 🐨 Uplay R2 Unlocker 🔓
Legit DLC Unlocker for Ubisoft Connect (R2)
### [Official forum topic]
For user-friendly description, consult the official forum thread. This document is meant for developers.
[Official forum topic]: https://cs.rin.ru/forum/viewtopic.php?f=10&t=111874
### 👋 Acknowledgements
Credit to Mr. Goldberg for the open source release of his Uplay R2 emulator.
This project 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.0](https://github.com/stevemk14ebr/PolyHook_2_0)
- [spdlog](https://github.com/gabime/spdlog)
### 📄 License
This software is licensed under [Zero Clause BSD] license, terms of which are available in [LICENSE.txt]
[Zero Clause BSD]: https://choosealicense.com/licenses/0bsd/
[LICENSE.txt]: ./LICENSE.txt
================================================
FILE: build.ps1
================================================
Set-Location (Get-Item $PSScriptRoot)
. ./KoalaBox/build.ps1 @args
Build-Project
================================================
FILE: res/UplayR2Unlocker.jsonc
================================================
{
"logging": false,
"lang": "default",
"auto_fetch": true,
"dlcs": [],
"items": [],
"blacklist": []
}
================================================
FILE: res/extra_build_config.gen.h
================================================
#pragma once
#define ORIGINAL_DLL "${ORIGINAL_DLL}"
#define LEGACY_ORIGINAL_DLL "${LEGACY_ORIGINAL_DLL}"
#define STORE_DLL "${STORE_DLL}"
================================================
FILE: src/main.cpp
================================================
#include "unlocker/unlocker.hpp"
// This header will be populated at build time
#include
EXTERN_C BOOL WINAPI DllMain(HMODULE module, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) {
unlocker::init(module);
} else if (reason == DLL_PROCESS_DETACH) {
unlocker::shutdown();
}
return TRUE;
}
================================================
FILE: src/pch.hpp
================================================
#pragma once
#include "koalabox/pch.hpp"
#include "3rd_party/cpr.hpp"
#include "3rd_party/json.hpp"
#include "3rd_party/polyhook2.hpp"
#include "koalabox/config_parser/config_parser.hpp"
#include "koalabox/dll_monitor/dll_monitor.hpp"
#include "koalabox/hook/hook.hpp"
#include "koalabox/loader/loader.hpp"
#include "koalabox/file_logger/file_logger.hpp"
#include "koalabox/util/util.hpp"
#include "koalabox/win_util/win_util.hpp"
================================================
FILE: src/unlocker/unlocker.cpp
================================================
#include "unlocker.hpp"
#include "upc/upc.hpp"
#include "koalabox/config_parser/config_parser.hpp"
#include "koalabox/loader/loader.hpp"
#include "koalabox/util/util.hpp"
#include "koalabox/win_util/win_util.hpp"
#include
#define DETOUR(FUNC) \
hook::detour(original_library, #FUNC, reinterpret_cast(FUNC));
namespace unlocker {
Config config = {}; // NOLINT(cert-err58-cpp)
ProductID global_app_id = 0;
HMODULE original_library = nullptr;
bool is_hook_mode = false;
void init(const HMODULE& self_module) {
try {
DisableThreadLibraryCalls(self_module);
const auto self_directory = loader::get_module_dir(self_module);
config = config_parser::parse(self_directory / PROJECT_NAME".jsonc", true);
if (config.logging) {
logger = file_logger::create(self_directory / PROJECT_NAME".log");
}
logger->info("🐨 {} 🔓 v{}", PROJECT_NAME, PROJECT_VERSION);
const auto module_path = win_util::get_module_file_name(self_module);
const auto is_not_original_dll = hook::is_hook_mode(self_module, ORIGINAL_DLL);
const auto is_not_legacy_dll = hook::is_hook_mode(self_module, LEGACY_ORIGINAL_DLL);
is_hook_mode = is_not_original_dll and is_not_legacy_dll;
if (is_hook_mode) {
logger->info("🪝 Detected hook mode");
dll_monitor::init(STORE_DLL, [](const HMODULE& store_library) {
hook::init();
original_library = store_library;
DETOUR(UPC_Init)
DETOUR(UPC_InstallLanguageGet)
DETOUR(UPC_ProductListFree)
DETOUR(UPC_ProductListGet)
logger->info("Hooking complete");
dll_monitor::shutdown();
});
} else {
logger->info("🔀 Detected proxy mode");
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 (not is_hook_mode) {
win_util::free_library(original_library);
}
logger->info("💀 Shutdown complete");
} catch (const Exception& ex) {
logger->error("Shutdown error: {}", ex.what());
}
}
}
================================================
FILE: src/unlocker/unlocker.hpp
================================================
#pragma once
#include "koalabox/koalabox.hpp"
namespace unlocker {
using namespace koalabox;
typedef uint32_t ProductID;
struct Config {
bool logging = false;
String lang = "default";
bool auto_fetch = true;
Set dlcs;
Set items;
Set blacklist;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Config, logging, lang, auto_fetch, dlcs, items, blacklist)
};
extern Config config;
extern HMODULE original_library;
extern ProductID global_app_id;
extern bool is_hook_mode;
void init(const HMODULE& loader_library);
void post_init();
void shutdown();
}
================================================
FILE: src/upc/upc.cpp
================================================
#include "upc.hpp"
#define GET_ORIGINAL_FUNCTION(FUNC) \
static const auto FUNC##_o = hook::get_original_function( \
unlocker::is_hook_mode, unlocker::original_library, #FUNC, FUNC \
);
String Product::get_type_string() const {
switch (type) {
case ProductType::App:
return "App";
case ProductType::DLC:
return "DLC";
case ProductType::Item:
return "Item";
default:
return "Unexpected Type";
}
}
void add_config_products(Map& products) {
products.insert({ global_app_id, Product(global_app_id, ProductType::App) });
for (const auto& dlc_id: config.dlcs) {
products.insert({ dlc_id, Product(dlc_id, ProductType::DLC) });
}
for (const auto& item_id: config.items) {
products.insert({ item_id, Product(item_id, ProductType::Item) });
}
}
void add_fetched_products(Map& products) {
if (not config.auto_fetch) {
return;
}
const auto res = cpr::Get(cpr::Url{
"https://raw.githubusercontent.com/acidicoala/public-entitlements/main/ubisoft/v1/products.jsonc"
});
if (res.status_code == cpr::status::HTTP_OK) {
try {
const auto json = nlohmann::json::parse(res.text, nullptr, true, true);
// iterate over keys to find the one that matches the game
for (const auto&[key, game]: json.items()) {
// Regex matching enables us to specify multiple IDs for the same game
if (std::regex_match(std::to_string(global_app_id), std::regex(key))) {
const String name(game["name"]);
logger->info("Fetched products for game: '{}'", name);
for (const ProductID dlc: game["dlcs"]) {
logger->debug(" ID: {}, Type: DLC", dlc);
products.insert({ dlc, Product(dlc, ProductType::DLC) });
}
for (const ProductID item: game["items"]) {
logger->debug(" ID: {}, Type: Item", item);
products.insert({ item, Product(item, ProductType::Item) });
}
break;
}
}
} catch (const std::exception& ex) {
logger->error("Failed to parse fetched products: {}", ex.what());
}
} else {
logger->error(
"Failed to fetch product IDs. "
"Status code: {}. Text: {}", res.status_code, res.text
);
}
}
void add_legit_products(Map& products, const ProductList* legit_product_list) {
logger->info("Original product list contains {} elements:", legit_product_list->length);
Vector missing_products;
// Iterate over original product list to find missing items
for (uint32_t i = 0; i < legit_product_list->length; i++) {
const auto product = legit_product_list->data[i];
logger->debug(
" ID: {}, Type: {}, Mystery1: {}, Mystery2: {}, Always0: {}, Always3: {}",
product->app_id, (int) product->type, product->mystery1,
product->mystery2, product->always_0, product->always_3
);
if (product->type == ProductType::App) {
continue;
}
// Insert missing products into the global product list
if (not products.contains(product->app_id)) {
products.insert({ product->app_id, Product(*product) });
missing_products.push_back(product);
}
}
// Let the user know which legitimately owned products are missing
if (not missing_products.empty()) {
logger->warn("Some of the legitimately owned products are missing from the config:");
for (const auto missing_product: missing_products) {
logger->warn(
" ID: {}, Type: {}",
missing_product->app_id,
missing_product->get_type_string()
);
}
}
}
Vector get_filtered_products(Map& products) {
Vector filtered_products;
// Construct a filtered list for game that excludes blacklisted ids
for (const auto&[id, product]: products) {
const auto included = not config.blacklist.contains(id);
if (included) {
filtered_products.push_back(product);
}
logger->debug(
" {} ID: {}, Type: {}",
included ? "✅" : "❌", id, product.get_type_string()
);
}
return filtered_products;
}
ProductList* create_new_product_list(const Vector& filtered_products) {
const auto product_list = new ProductList{
.length = static_cast(filtered_products.size()),
.data = new Product* [filtered_products.size()]
};
for (size_t i = 0; i < filtered_products.size(); i++) {
product_list->data[i] = new Product(filtered_products.at(i));
}
return product_list;
}
DLL_EXPORT(int) UPC_Init(unsigned int version, ProductID app_id) {
logger->info("⚡ {} -> version: {}, app_id: {}", __func__, version, app_id);
global_app_id = app_id;
GET_ORIGINAL_FUNCTION(UPC_Init)
const auto result = UPC_Init_o(version, app_id);
logger->debug("{} result: {}", __func__, result);
return result;
}
DLL_EXPORT(int) UPC_ProductListFree(void*, ProductList* inProductList) {
logger->debug(__func__);
if (inProductList) {
for (uint32_t i = 0; i < inProductList->length; i++) {
delete inProductList->data[i];
}
delete[] inProductList->data;
}
delete inProductList;
return 0;
}
DLL_EXPORT(int) UPC_ProductListGet(
void* context,
const char* inOptUserIdUtf8,
unsigned int inFilter,
ProductList** outProductList,
UplayCallback inCallback,
void* inCallbackData
) {
logger->debug(__func__);
const auto callbackContainer = new CallbackContainer{
.context = context,
.original_callback = inCallback,
.in_callback_data = inCallbackData,
.out_product_list = outProductList,
};
static const auto callback = [](unsigned long arg1, void* data) {
logger->debug("UPC_ProductListGet callback -> arg1: {}", arg1);
const auto container = static_cast(data);
Map products;
add_config_products(products);
add_fetched_products(products);
add_legit_products(products, container->legit_product_list);
logger->info("🍀 Unlocker prepared {} products:", products.size());
const auto filtered_products = get_filtered_products(products);
const auto product_list = create_new_product_list(filtered_products);
// Save the product list address in the pointer provided by game earlier
*(container->out_product_list) = product_list;
// Let the game know that the product list is ready
container->original_callback(arg1, container->in_callback_data);
// Free the legit product list
GET_ORIGINAL_FUNCTION(UPC_ProductListFree)
UPC_ProductListFree_o(container->context, container->legit_product_list);
delete container;
};
GET_ORIGINAL_FUNCTION(UPC_ProductListGet)
return UPC_ProductListGet_o(
context,
inOptUserIdUtf8,
inFilter,
&callbackContainer->legit_product_list,
callback,
callbackContainer
);
}
DLL_EXPORT(const char*) UPC_InstallLanguageGet(void* context) {
logger->debug(__func__);
if (config.lang == "default") {
GET_ORIGINAL_FUNCTION(UPC_InstallLanguageGet)
const auto result = UPC_InstallLanguageGet_o(context);
logger->info("🔤 Responding with original language -> '{}'", result);
return result;
} else {
logger->info("🔤 Responding with configured language -> '{}'", config.lang);
return config.lang.c_str();
}
}
================================================
FILE: src/upc/upc.hpp
================================================
#pragma once
#include "unlocker/unlocker.hpp"
#define DLL_EXPORT(TYPE) extern "C" _declspec(dllexport) TYPE
using namespace unlocker;
enum class ProductType {
App = 1,
DLC = 2,
Item = 4,
};
struct Product {
uint32_t app_id;
ProductType type;
[[maybe_unused]] uint32_t mystery1;
[[maybe_unused]] uint32_t always_3 = 3;
[[maybe_unused]] uint32_t always_0 = 0;
[[maybe_unused]] uint32_t mystery2;
Product(uint32_t app_id, ProductType type) {
this->app_id = app_id;
this->type = type;
this->mystery1 = type == ProductType::Item ? 4 : 1;
this->mystery2 = type == ProductType::Item ? 1 : 3;
}
[[nodiscard]]
String get_type_string() const;
};
struct ProductList {
uint32_t length = 0;
[[maybe_unused]] uint32_t padding = 0; // What is this? offset?
Product** data = nullptr; // Array of pointers
};
typedef void (* UplayCallback)(unsigned long, void*);
struct CallbackContainer {
void* context = nullptr;
UplayCallback original_callback = nullptr;
void* in_callback_data = nullptr;
ProductList* legit_product_list = nullptr;
ProductList** out_product_list = nullptr;
};
DLL_EXPORT(int) UPC_Init(unsigned int version, ProductID app_id);
DLL_EXPORT(const char*) UPC_InstallLanguageGet(void* context);
DLL_EXPORT(int) UPC_ProductListFree(void* context, ProductList* inProductList);
DLL_EXPORT(int) UPC_ProductListGet(
void* context,
const char* inOptUserIdUtf8,
unsigned int inFilter,
ProductList** outProductList,
UplayCallback inCallback,
void* inCallbackData
);
================================================
FILE: vcpkg.json
================================================
{
"name": "uplay-r2-unlocker",
"version": "0.0.0",
"license": "0BSD",
"dependencies": [
"cpr",
"nlohmann-json",
"polyhook2",
"spdlog"
]
}