├── .gitignore ├── CMakeLists.txt ├── CMakeSettings.json ├── LICENSE ├── Logger.cpp ├── Logger.h ├── MountedBuild.cpp ├── MountedBuild.h ├── README.md ├── Stats.cpp ├── Stats.h ├── checks ├── oodle_handler.cpp ├── oodle_handler.h ├── symlink_workaround.cpp ├── symlink_workaround.h ├── winfspcheck.cpp └── winfspcheck.h ├── containers ├── cancel_flag.h └── file_sha.h ├── docs ├── step1.gif ├── step10.gif ├── step11.gif ├── step12.gif ├── step2.gif ├── step4.gif ├── step5.gif ├── step7.gif └── step8.gif ├── filesystem ├── dirtree.h ├── egfs.cpp └── egfs.h ├── gui ├── GameUpdateChecker.cpp ├── GameUpdateChecker.h ├── Localization.cpp ├── Localization.h ├── UpdateChecker.cpp ├── UpdateChecker.h ├── cApp.cpp ├── cApp.h ├── cMain.cpp ├── cMain.h ├── cProgress.cpp ├── cProgress.h ├── cSetup.cpp ├── cSetup.h ├── cSplash.cpp ├── cSplash.h ├── cStorage.cpp ├── cStorage.h ├── guimain.cpp ├── localedefs.h ├── settings.cpp ├── settings.h ├── taskbar.h ├── versioninfo.h ├── wxHelpButton.cpp ├── wxHelpButton.h ├── wxLabelSlider.cpp ├── wxLabelSlider.h ├── wxModalWindow.cpp └── wxModalWindow.h ├── help ├── MAIN_BTN_PLAY.htm ├── MAIN_BTN_SETTINGS.htm ├── MAIN_BTN_STORAGE.htm ├── MAIN_BTN_VERIFY.htm ├── SETUP_ADVANCED_BUFCT.htm ├── SETUP_ADVANCED_CMDARGS.htm ├── SETUP_ADVANCED_THDCT.htm ├── SETUP_GENERAL_COMPLEVEL.htm ├── SETUP_GENERAL_COMPMETHOD.htm ├── SETUP_GENERAL_INSTFOLDER.htm ├── SETUP_GENERAL_UPDATEINT.htm ├── egl2.hhp ├── icon.png ├── main.css └── map.htm ├── icon.ico ├── icon.svg ├── libraries ├── curlion │ ├── connection.cpp │ ├── connection.h │ ├── connection_manager.cpp │ ├── connection_manager.h │ ├── curlion.h │ ├── error.h │ ├── http_connection.cpp │ ├── http_connection.h │ ├── http_form.cpp │ ├── http_form.h │ ├── log.h │ ├── socket_factory.h │ ├── socket_watcher.h │ └── timer.h ├── libdeflate │ ├── libdeflate.h │ └── libdeflatestatic.lib └── oodle │ ├── oo2core_8_win64.lib │ └── oodle2.h ├── locales ├── AR.json ├── DE.json ├── EN.json ├── ES.json ├── FI.json ├── FR.json ├── IT.json ├── JA.json ├── PL.json ├── PT_BR.json ├── RU.json └── TR.json ├── localetool └── main.cpp ├── resources.rc ├── splash.ico ├── storage ├── EGSProvider.cpp ├── EGSProvider.h ├── compression.cpp ├── compression.h ├── sha.h ├── storage.cpp └── storage.h └── web ├── http.h ├── http ├── Client.cpp └── Client.h ├── manifest ├── auth.cpp ├── auth.h ├── chunk.cpp ├── chunk.h ├── chunk_part.h ├── feature_level.h ├── file.cpp ├── file.h ├── manifest.cpp └── manifest.h └── personal ├── DeviceCodeAuth.cpp ├── DeviceCodeAuth.h ├── PersonalAuth.cpp └── PersonalAuth.h /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | .vs/ 3 | 4 | cert.pfx 5 | cert.pwd 6 | 7 | # You can get locales.dat by just running LocaleTool.exe anyway 8 | locales.dat 9 | 10 | # You can get help.chm by just running hhw.exe or some hhp compiler 11 | help.chm 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.9.2) 2 | 3 | project (EGL2) 4 | 5 | set(WX_DIR "J:\\Code\\wxWidgets" CACHE PATH "wxWidgets directory" FORCE) 6 | option(ENABLE_CONSOLE "Use console subsystem" OFF) 7 | option(ENABLE_SIGNING "Sign the executable" OFF) 8 | 9 | aux_source_directory(. FILE_SOURCES) 10 | aux_source_directory(filesystem FILESYSTEM_FILE_SOURCES) 11 | aux_source_directory(storage STORAGE_FILE_SOURCES) 12 | aux_source_directory(web WEB_FILE_SOURCES) 13 | aux_source_directory(web/personal PERSONAL_FILE_SOURCES) 14 | aux_source_directory(web/manifest MANIFEST_FILE_SOURCES) 15 | aux_source_directory(web/http HTTP_FILE_SOURCES) 16 | aux_source_directory(checks CHECKS_FILE_SOURCES) 17 | aux_source_directory(gui INTERFACE_FILE_SOURCES) 18 | aux_source_directory(libraries/curlion CURLION_FILE_SOURCES) 19 | 20 | aux_source_directory(localetool LOCALETOOL_FILE_SOURCES) 21 | 22 | if (ENABLE_CONSOLE) 23 | set(SUBSYSTEM "") 24 | add_compile_definitions(USE_CONSOLE) 25 | message(STATUS "Using console subsystem") 26 | else() 27 | set (SUBSYSTEM "WIN32") 28 | message(STATUS "Using GUI subsystem") 29 | endif() 30 | 31 | add_executable(EGL2 ${SUBSYSTEM} 32 | ${INTERFACE_FILE_SOURCES} 33 | "resources.rc" 34 | ${CURLION_FILE_SOURCES} 35 | ${FILESYSTEM_FILE_SOURCES} 36 | ${STORAGE_FILE_SOURCES} 37 | ${WEB_FILE_SOURCES} 38 | ${MANIFEST_FILE_SOURCES} 39 | ${PERSONAL_FILE_SOURCES} 40 | ${HTTP_FILE_SOURCES} 41 | ${CHECKS_FILE_SOURCES} 42 | ${FILE_SOURCES}) 43 | 44 | add_executable(LocaleTool 45 | ${LOCALETOOL_FILE_SOURCES}) 46 | 47 | set(wxWidgets_ROOT_DIR "${WX_DIR}") 48 | set(wxWidgets_LIB_DIR "${WX_DIR}/lib/vc_x64_lib") 49 | set(wxWidgets_EXCLUDE_COMMON_LIBRARIES TRUE) 50 | set(wxWidgets_USE_STATIC ON) 51 | set(wxWidgets_USE_UNICODE ON) 52 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 53 | set(wxWidgets_USE_DEBUG ON) 54 | else() 55 | set(wxWidgets_USE_DEBUG OFF) 56 | endif() 57 | 58 | set(Boost_USE_STATIC_LIBS ON) 59 | add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS) 60 | 61 | set_property(TARGET EGL2 PROPERTY CXX_STANDARD 20) 62 | set_property(TARGET LocaleTool PROPERTY CXX_STANDARD 20) 63 | 64 | find_package(OpenSSL REQUIRED) 65 | find_package(RapidJSON CONFIG REQUIRED) 66 | find_package(lz4 REQUIRED) 67 | find_package(zstd CONFIG REQUIRED) 68 | find_package(CURL CONFIG REQUIRED) 69 | find_package(LibLZMA CONFIG REQUIRED) 70 | find_package(BOOST REQUIRED COMPONENTS asio) 71 | find_package(wxWidgets REQUIRED COMPONENTS core base) 72 | include(${wxWidgets_USE_FILE}) 73 | 74 | if (ENABLE_SIGNING) 75 | message(STATUS "Signing files") 76 | file(READ "cert.pwd" SIGN_PASS) 77 | add_custom_command(TARGET EGL2 POST_BUILD 78 | COMMAND 79 | signtool sign /f "${CMAKE_SOURCE_DIR}/cert.pfx" /p "${SIGN_PASS}" /fd sha1 /t http://timestamp.digicert.com /v $ && 80 | signtool sign /f "${CMAKE_SOURCE_DIR}/cert.pfx" /p "${SIGN_PASS}" /fd sha256 /tr http://timestamp.digicert.com?td=sha256 /td sha256 /as /v $ 81 | ) 82 | endif() 83 | 84 | set(CompilerFlags 85 | CMAKE_CXX_FLAGS 86 | CMAKE_CXX_FLAGS_DEBUG 87 | CMAKE_CXX_FLAGS_RELWITHDEBINFO 88 | CMAKE_CXX_FLAGS_MINSIZEREL 89 | CMAKE_CXX_FLAGS_RELEASE 90 | CMAKE_C_FLAGS 91 | CMAKE_C_FLAGS_DEBUG 92 | CMAKE_C_FLAGS_RELWITHDEBINFO 93 | CMAKE_C_FLAGS_MINSIZEREL 94 | CMAKE_C_FLAGS_RELEASE 95 | ) 96 | foreach(CompilerFlag ${CompilerFlags}) 97 | string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") 98 | endforeach() 99 | 100 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC" AND CMAKE_BUILD_TYPE MATCHES "Release") 101 | target_compile_options(EGL2 PRIVATE /Zi) 102 | set_target_properties(EGL2 PROPERTIES 103 | LINK_FLAGS "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF" 104 | COMPILE_PDB_NAME EGL2 105 | COMPILE_PDB_OUTPUT_DIR ${CMAKE_BINARY_DIR} 106 | ) 107 | endif() 108 | 109 | target_link_options(EGL2 PRIVATE "/DELAYLOAD:winfsp-x64.dll" "/DELAYLOAD:oo2core_8_win64.dll") 110 | target_include_directories(EGL2 PRIVATE 111 | "$ENV{ProgramFiles\(x86\)}\\WinFsp\\inc" 112 | ${Boost_LIBRARIES} 113 | ${RAPIDJSON_INCLUDE_DIRS} 114 | "libraries\\libdeflate" 115 | "libraries\\curlion" 116 | "libraries\\oodle") 117 | target_link_libraries(EGL2 PRIVATE 118 | "$ENV{ProgramFiles\(x86\)}\\WinFsp\\lib\\winfsp-x64.lib" 119 | ${wxWidgets_LIBRARIES} 120 | CURL::libcurl 121 | OpenSSL::Crypto 122 | Crypt32 123 | libzstd 124 | lz4::lz4 125 | LibLZMA::LibLZMA 126 | Htmlhelp 127 | delayimp 128 | "${CMAKE_CURRENT_SOURCE_DIR}\\libraries\\libdeflate\\libdeflatestatic.lib" 129 | "${CMAKE_CURRENT_SOURCE_DIR}\\libraries\\oodle\\oo2core_8_win64.lib") 130 | 131 | target_link_libraries(LocaleTool PRIVATE libzstd) -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug-Con", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\out\\build\\${name}", 9 | "installRoot": "${projectDir}\\out\\install\\${name}", 10 | "cmakeCommandArgs": "-DVCPKG_TARGET_TRIPLET=x64-windows-static -DENABLE_CONSOLE:BOOL=ON", 11 | "buildCommandArgs": "-v", 12 | "ctestCommandArgs": "", 13 | "variables": [] 14 | }, 15 | { 16 | "name": "x64-Debug", 17 | "generator": "Ninja", 18 | "configurationType": "Debug", 19 | "inheritEnvironments": [ "msvc_x64_x64" ], 20 | "buildRoot": "${projectDir}\\out\\build\\${name}", 21 | "installRoot": "${projectDir}\\out\\install\\${name}", 22 | "cmakeCommandArgs": "-DVCPKG_TARGET_TRIPLET=x64-windows-static", 23 | "buildCommandArgs": "-v", 24 | "ctestCommandArgs": "", 25 | "variables": [] 26 | }, 27 | { 28 | "name": "x64-RelDbg", 29 | "generator": "Ninja", 30 | "configurationType": "RelWithDebInfo", 31 | "buildRoot": "${projectDir}\\out\\build\\${name}", 32 | "installRoot": "${projectDir}\\out\\install\\${name}", 33 | "cmakeCommandArgs": "-DVCPKG_TARGET_TRIPLET=x64-windows-static", 34 | "buildCommandArgs": "-v", 35 | "ctestCommandArgs": "", 36 | "inheritEnvironments": [ "msvc_x64_x64" ], 37 | "variables": [] 38 | }, 39 | { 40 | "name": "x64-Release", 41 | "generator": "Ninja", 42 | "configurationType": "Release", 43 | "buildRoot": "${projectDir}\\out\\build\\${name}", 44 | "installRoot": "${projectDir}\\out\\install\\${name}", 45 | "cmakeCommandArgs": "-DVCPKG_TARGET_TRIPLET=x64-windows-static -DENABLE_SIGNING:BOOL=ON", 46 | "buildCommandArgs": "-v", 47 | "ctestCommandArgs": "", 48 | "inheritEnvironments": [ "msvc_x64_x64" ], 49 | "variables": [] 50 | }, 51 | { 52 | "name": "x64-Release-Con", 53 | "generator": "Ninja", 54 | "configurationType": "Release", 55 | "buildRoot": "${projectDir}\\out\\build\\${name}", 56 | "installRoot": "${projectDir}\\out\\install\\${name}", 57 | "cmakeCommandArgs": "-DVCPKG_TARGET_TRIPLET=x64-windows-static -DENABLE_CONSOLE:BOOL=ON", 58 | "buildCommandArgs": "-v", 59 | "ctestCommandArgs": "", 60 | "inheritEnvironments": [ "msvc_x64_x64" ], 61 | "variables": [] 62 | }, 63 | { 64 | "name": "x64-MinRel", 65 | "generator": "Ninja", 66 | "configurationType": "MinSizeRel", 67 | "buildRoot": "${projectDir}\\out\\build\\${name}", 68 | "installRoot": "${projectDir}\\out\\install\\${name}", 69 | "cmakeCommandArgs": "-DVCPKG_TARGET_TRIPLET=x64-windows-static", 70 | "buildCommandArgs": "-v", 71 | "ctestCommandArgs": "", 72 | "inheritEnvironments": [ "msvc_x64_x64" ], 73 | "variables": [] 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | bool Logger::Setup() { 7 | auto stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); 8 | if (stdoutHandle != INVALID_HANDLE_VALUE) { 9 | DWORD outMode; 10 | if (GetConsoleMode(stdoutHandle, &outMode)) { 11 | return SetConsoleMode(stdoutHandle, outMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); 12 | } 13 | } 14 | return false; 15 | } -------------------------------------------------------------------------------- /Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LOG_DEBUG(str, ...) Logger::Log(Logger::LogLevel::DEBUG, LOG_SECTION, str, __VA_ARGS__) 4 | #define LOG_INFO(str, ...) Logger::Log(Logger::LogLevel::INFO, LOG_SECTION, str, __VA_ARGS__) 5 | #define LOG_WARN(str, ...) Logger::Log(Logger::LogLevel::WARN, LOG_SECTION, str, __VA_ARGS__) 6 | #define LOG_ERROR(str, ...) Logger::Log(Logger::LogLevel::ERROR_, LOG_SECTION, str, __VA_ARGS__) 7 | #define LOG_FATAL(str, ...) Logger::Log(Logger::LogLevel::FATAL, LOG_SECTION, str, __VA_ARGS__) 8 | 9 | #define LOG_VA_DEBUG(str, va) Logger::Log(Logger::LogLevel::DEBUG, LOG_SECTION, str, va) 10 | #define LOG_VA_INFO(str, va) Logger::Log(Logger::LogLevel::INFO, LOG_SECTION, str, va) 11 | #define LOG_VA_WARN(str, va) Logger::Log(Logger::LogLevel::WARN, LOG_SECTION, str, va) 12 | #define LOG_VA_ERROR(str, va) Logger::Log(Logger::LogLevel::ERROR_, LOG_SECTION, str, va) 13 | #define LOG_VA_FATAL(str, va) Logger::Log(Logger::LogLevel::FATAL, LOG_SECTION, str, va) 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | class Logger { 20 | public: 21 | enum class LogLevel : uint8_t { 22 | UNKNOWN, 23 | DEBUG, 24 | INFO, 25 | WARN, 26 | ERROR_, 27 | FATAL 28 | }; 29 | 30 | static bool Setup(); 31 | 32 | inline static uint32_t GetThreadId() { 33 | auto id = std::this_thread::get_id(); 34 | return *(uint32_t*)&id; 35 | } 36 | 37 | template 38 | static void Log(LogLevel level, const char* section, const char* str, Args... args) { 39 | auto size = snprintf(nullptr, 0, str, args...) + 1; 40 | auto buf = std::make_unique(size); 41 | snprintf(buf.get(), size, str, args...); 42 | printf("%s%s - %d %s: %s\n%s", Logger::LevelAsColor(level), Logger::LevelAsString(level), GetThreadId(), section, buf.get(), Logger::ResetColor); 43 | if (Callback) { 44 | Callback(level, section, buf.get()); 45 | } 46 | } 47 | 48 | static void Log(LogLevel level, const char* section, const char* str, va_list args) { 49 | auto size = vsnprintf(nullptr, 0, str, args) + 1; 50 | auto buf = std::make_unique(size); 51 | vsnprintf(buf.get(), size, str, args); 52 | printf("%s%s - %d %s: %s\n%s", Logger::LevelAsColor(level), Logger::LevelAsString(level), GetThreadId(), section, buf.get(), Logger::ResetColor); 53 | if (Callback) { 54 | Callback(level, section, buf.get()); 55 | } 56 | } 57 | 58 | static constexpr const char* LevelAsString(LogLevel level) { 59 | switch (level) 60 | { 61 | case LogLevel::DEBUG: 62 | return "Debug"; 63 | case LogLevel::INFO: 64 | return "Info "; 65 | case LogLevel::WARN: 66 | return "Warn "; 67 | case LogLevel::ERROR_: 68 | return "Error"; 69 | case LogLevel::FATAL: 70 | return "Fatal"; 71 | default: 72 | return "Unkwn"; 73 | } 74 | } 75 | 76 | static constexpr const char* LevelAsColor(LogLevel level) { 77 | switch (level) 78 | { 79 | case LogLevel::DEBUG: 80 | return "\33[0;37m"; 81 | case LogLevel::INFO: 82 | return "\33[0;92m"; 83 | case LogLevel::WARN: 84 | return "\33[0;93m"; 85 | case LogLevel::ERROR_: 86 | return "\33[0;91m"; 87 | case LogLevel::FATAL: 88 | return "\33[0;31m"; 89 | default: 90 | return "\33[0;95m"; 91 | } 92 | } 93 | 94 | static constexpr const char* ResetColor = "\33[0m"; 95 | 96 | using callback = std::function; 97 | static inline callback Callback = nullptr; 98 | }; -------------------------------------------------------------------------------- /MountedBuild.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "containers/cancel_flag.h" 4 | #include "filesystem/egfs.h" 5 | #include "web/manifest/manifest.h" 6 | #include "storage/storage.h" 7 | 8 | #include 9 | 10 | namespace fs = std::filesystem; 11 | 12 | typedef std::function ProgressSetMaxHandler; 13 | typedef std::function ProgressIncrHandler; 14 | typedef std::function ProgressFinishHandler; 15 | 16 | struct ChunkMetadata { 17 | std::shared_ptr Chunk; 18 | bool Downloaded; 19 | size_t FileSize; 20 | uint16_t Flags; 21 | }; 22 | 23 | typedef std::function QueryChunkCallback; 24 | 25 | class MountedBuild { 26 | public: 27 | MountedBuild(Manifest manifest, fs::path mountDir, fs::path cachePath, uint32_t storageFlags, uint32_t memoryPoolCapacity); 28 | ~MountedBuild(); 29 | 30 | static bool SetupCacheDirectory(fs::path CacheDir); 31 | void SetupGameDirectory(ProgressSetMaxHandler setMax, ProgressIncrHandler onProg, ProgressFinishHandler onFinish, cancel_flag& flag, uint32_t threadCount); 32 | void PreloadAllChunks(ProgressSetMaxHandler setMax, ProgressIncrHandler onProg, ProgressFinishHandler onFinish, cancel_flag& flag, uint32_t threadCount); 33 | void VerifyAllChunks(ProgressSetMaxHandler setMax, ProgressIncrHandler onProg, ProgressFinishHandler onFinish, cancel_flag& flag, uint32_t threadCount); 34 | void PurgeUnusedChunks(cancel_flag& flag); 35 | void QueryChunks(QueryChunkCallback onQuery, cancel_flag& flag, uint32_t threadCount); 36 | uint32_t GetMissingChunkCount(); 37 | void LaunchGame(const char* additionalArgs); 38 | 39 | const fs::path& GetCachePath() const { 40 | return CacheDir; 41 | } 42 | 43 | const fs::path& GetMountPath() const { 44 | return MountDir; 45 | } 46 | 47 | private: 48 | void PreloadFile(File& File, uint32_t ThreadCount, cancel_flag& cancelFlag); 49 | 50 | void FileRead(PVOID Handle, PVOID Buffer, UINT64 offset, ULONG length, ULONG* bytesRead); 51 | 52 | fs::path MountDir; 53 | fs::path CacheDir; 54 | Manifest Build; 55 | Storage StorageData; 56 | std::unique_ptr Egfs; 57 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EGL2 2 | ## An alternative to the Epic Games Launcher 3 | 4 | ## Features 5 | - Extremely quick updating 6 | - Or, download the update it in the background while playing! (Not recommended, but possible!) 7 | - Implements directly with your file system 8 | - Low memory footprint 9 | - Use your favorite datamining tools or extractors seamlessly 10 | - Trade performance for disk space, keep your game install compressed 11 | 12 | ## Installation / Getting Started 13 | ### This setup is currently outdated. An updated version will be put up soon. Right now, all you need to know is that: **Your install folder needs to be empty! This is separate from what the official launcher has!** 14 | The process can be a little awkward, so here's a simple step by step guide: 15 | |Description|Guide (Click one to view it)| 16 | |--|--| 17 | |Install [WinFsp](http://www.secfs.net/winfsp/), which can be downloaded [here](https://github.com/billziss-gh/winfsp/releases/download/v1.7B1/winfsp-1.7.20038.msi). (Only the "Core" feature is necessary)|![WinFsp Installation](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step1.gif)| 18 | |Download the latest version of EGL2 [here](https://github.com/WorkingRobot/EGL2/releases/latest/download/EGL2.exe).|![Running EGL2](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step2.gif)| 19 | |When ran, you should be greeted with a window with several buttons. If instead you see an error, follow the instructions it shows.| | 20 | |Click "Setup", where a new window will prompt you to setup where you want your data to be stored.|![Clicking Setup](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step4.gif)| 21 | |Create an **empty** folder where you want Fortnite to be installed. Set the "Install Folder" location to the folder you've just made.|![Setting the install folder](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step5.gif)| 22 | |By default, it is configured to download and install the game in a decompressed state. This will simply download and decompress your game to it's full size.| | 23 | |Feel free to use the other compression methods at the expense of download/update time (due to compression). I recommend using "LZ4" and the slowest compression level setting you feel comfortable with. LZ4 has decompression speeds that are extremely fast (~4 GB/s), and the slowest compression level can compress your install by over 2x. The slowest setting with LZ4 can compress ~40MB/s on a good computer, so do be conservative if your computer isn't great.|![Setting compression options](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step7.gif)| 24 | |Close the setup window when you're done configuring.|![Closing Setup window](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step8.gif)|If you set your install folder correctly, the other buttons should be enabled.| | 25 | |Click "Update". This will take about an hour or two, depending on the settings you chose and the computer you have. (You are downloading an entire game and probably compressing it, too, y'know.)|![Click Update](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step10.gif)| 26 | |When your game finishes updating/installing, click "Start".|![Click Start](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step11.gif)| 27 | |If all goes well, you should be able to click "Play". Once you enter in your exchange code, Fortnite should start!|![Click Play](https://raw.githubusercontent.com/WorkingRobot/EGL2/master/docs/step12.gif)| 28 | |When relaunching, it's recommended to click "Update" again, in case a new update released.| | 29 | 30 | ## Building 31 | I use [CMake for Visual Studio](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio), so your results may vary. 32 | 33 | ### Prerequisites 34 | - CMake 35 | - MSVC (with C++20 support) 36 | - vcpkg 37 | - (Install these packages with `x64-windows-static`) 38 | - OpenSSL 39 | - RapidJSON 40 | - lz4 41 | - zstd 42 | - curl 43 | - boost 44 | - wxWidgets 45 | - Make sure to set the subsystem from /MD to /MT when compiling 46 | - WinFsp with the "Developer" feature installed 47 | 48 | ### CMake Build Options 49 | - `WX_DIR` - Set this path to your wxWidgets directory. -------------------------------------------------------------------------------- /Stats.cpp: -------------------------------------------------------------------------------- 1 | #include "Stats.h" 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | #include 7 | 8 | constexpr inline ULONGLONG ConvertTime(FILETIME& ft) { 9 | return ULARGE_INTEGER{ ft.dwLowDateTime, ft.dwHighDateTime }.QuadPart; 10 | } 11 | 12 | void Stats::UpdateData() 13 | { 14 | memset(&Data, 0, sizeof StatsUpdateData); 15 | 16 | static auto refreshScale = 1000.f / RefreshRate.count(); 17 | 18 | static uint64_t prevSysIdle = 0; 19 | static uint64_t prevSysUse = 0; 20 | static uint64_t prevProcUse = 0; 21 | static FILETIME sysIdleFt, sysKernelFt, sysUserFt; 22 | static FILETIME procCreateFt, procExitFt, procKernelFt, procUserFt; 23 | 24 | static PROCESS_MEMORY_COUNTERS_EX pmc; 25 | 26 | static uint64_t prevRead = 0; 27 | static uint64_t prevWrite = 0; 28 | static uint64_t prevProvide = 0; 29 | static uint64_t prevDownload = 0; 30 | static uint64_t prevLatOp = 0; 31 | static uint64_t prevLatNs = 0; 32 | 33 | // cpu calculation 34 | if (GetSystemTimes(&sysIdleFt, &sysKernelFt, &sysUserFt) && GetProcessTimes(GetCurrentProcess(), &procCreateFt, &procExitFt, &procKernelFt, &procUserFt)) { 35 | uint64_t sysIdle = ConvertTime(sysIdleFt); 36 | uint64_t sysUse = ConvertTime(sysKernelFt) + ConvertTime(sysUserFt); 37 | uint64_t procUse = ConvertTime(procKernelFt) + ConvertTime(procUserFt); 38 | 39 | if (prevSysIdle) 40 | { 41 | uint64_t sysTotal = sysUse - prevSysUse; 42 | uint64_t procTotal = procUse - prevProcUse; 43 | 44 | if (sysTotal > 0) { 45 | Data.cpu = float((double)procTotal / sysTotal * 100); 46 | } 47 | } 48 | 49 | prevSysIdle = sysIdle; 50 | prevSysUse = sysUse; 51 | prevProcUse = procUse; 52 | } 53 | 54 | // ram calculation 55 | if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) { 56 | Data.ram = pmc.PrivateUsage; 57 | } 58 | 59 | // thread count calculation 60 | { 61 | auto procId = GetCurrentProcessId(); 62 | HANDLE hnd = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, procId); 63 | if (hnd != INVALID_HANDLE_VALUE) { 64 | THREADENTRY32 threadEntry; 65 | threadEntry.dwSize = sizeof THREADENTRY32; 66 | 67 | if (Thread32First(hnd, &threadEntry)) { 68 | int threadCount = 0; 69 | do { 70 | if (threadEntry.th32OwnerProcessID == procId) { 71 | threadCount++; 72 | } 73 | } while (Thread32Next(hnd, &threadEntry)); 74 | Data.threads = threadCount; 75 | } 76 | 77 | CloseHandle(hnd); 78 | } 79 | } 80 | 81 | // other atomic stuff to pass 82 | uint64_t read = FileReadCount.load(std::memory_order_relaxed); 83 | uint64_t write = FileWriteCount.load(std::memory_order_relaxed); 84 | uint64_t provide = ProvideCount.load(std::memory_order_relaxed); 85 | uint64_t download = DownloadCount.load(std::memory_order_relaxed); 86 | uint64_t latOp = LatOpCount.load(std::memory_order_relaxed); 87 | uint64_t latNs = LatNsCount.load(std::memory_order_relaxed); 88 | 89 | Data.read = (read - prevRead) * refreshScale; 90 | Data.write = (write - prevWrite) * refreshScale; 91 | Data.provide = (provide - prevProvide) * refreshScale; 92 | Data.download = (download - prevDownload) * refreshScale; 93 | Data.latency = ((double)(latNs - prevLatNs) / (latOp - prevLatOp)) / 1000000 * refreshScale; 94 | 95 | prevRead = read; 96 | prevWrite = write; 97 | prevProvide = provide; 98 | prevDownload = download; 99 | prevLatOp = latOp; 100 | prevLatNs = latNs; 101 | } 102 | -------------------------------------------------------------------------------- /Stats.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "containers/cancel_flag.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace ch = std::chrono; 11 | 12 | struct StatsUpdateData { 13 | #define DEFINE_STAT(name, type) type name; 14 | 15 | DEFINE_STAT(cpu, float) 16 | DEFINE_STAT(ram, size_t) 17 | DEFINE_STAT(read, size_t) 18 | DEFINE_STAT(write, size_t) 19 | DEFINE_STAT(provide, size_t) 20 | DEFINE_STAT(download, size_t) 21 | DEFINE_STAT(latency, float) 22 | DEFINE_STAT(threads, int) 23 | 24 | #undef DEFINE_STAT 25 | }; 26 | 27 | class Stats { 28 | public: 29 | Stats() = delete; 30 | Stats(const Stats&) = delete; 31 | Stats& operator=(const Stats&) = delete; 32 | 33 | static inline const wxString GetReadableSize(size_t size) { 34 | if (!size) { 35 | return "0 B"; 36 | } 37 | 38 | static constexpr const char* suffix[] = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; 39 | int i = 0; 40 | auto sizeD = (double)size; 41 | while (sizeD >= 1024) { 42 | sizeD /= 1024; 43 | i++; 44 | } 45 | return wxString::Format("%.*f %s", 2 - (int)floor(log10(sizeD)), sizeD, suffix[i]); 46 | } 47 | 48 | template 49 | static inline void StartUpdateThread(ch::milliseconds refreshRate, Callback&& updateCallback, Args&&... args) { 50 | if (ThreadRunning) { 51 | return; 52 | } 53 | RefreshRate = refreshRate; 54 | 55 | std::thread([=]() { 56 | ThreadRunning = true; 57 | while (!Flag.cancelled()) { 58 | UpdateData(); 59 | if (Flag.cancelled()) { 60 | break; 61 | } 62 | if (!updateCallback(Data, args...)) { 63 | break; 64 | } 65 | std::this_thread::sleep_for(RefreshRate); 66 | } 67 | ThreadRunning = false; 68 | }).detach(); 69 | } 70 | 71 | static inline void StopUpdateThread() { 72 | Flag.cancel(); 73 | } 74 | 75 | static inline std::atomic_uint64_t ProvideCount = 0; // std::atomic_uint_fast64_t is the same in msvc 76 | static inline std::atomic_uint64_t FileReadCount = 0; 77 | static inline std::atomic_uint64_t FileWriteCount = 0; 78 | static inline std::atomic_uint64_t DownloadCount = 0; 79 | static inline std::atomic_uint64_t LatOpCount = 0; 80 | static inline std::atomic_uint64_t LatNsCount = 0; 81 | 82 | private: 83 | static inline StatsUpdateData Data; 84 | static inline ch::milliseconds RefreshRate; 85 | static inline bool ThreadRunning = false; 86 | static inline cancel_flag Flag; 87 | static void UpdateData(); 88 | }; -------------------------------------------------------------------------------- /checks/oodle_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "oodle_handler.h" 2 | 3 | #define WARFRAME_CDN_HOST "https://origin.warframe.com" // content.warframe.com is also viable 4 | #define WARFRAME_INDEX_PATH "/origin/E926E926/index.txt.lzma" // id is any random 8 char hex 5 | #define WARFRAME_INDEX_URL WARFRAME_CDN_HOST WARFRAME_INDEX_PATH 6 | #define OODLE_DLL_NAME "oo2core_8_win64.dll" 7 | 8 | #include "../web/http.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | int LoadDll(const fs::path& dllPath) { 15 | if (fs::status(dllPath).type() != fs::file_type::regular) { 16 | return 1; 17 | } 18 | std::error_code ec; 19 | if (fs::file_size(dllPath, ec) < 512 * 1024) { // size too small to be oodle 20 | return 2; 21 | } 22 | if (LoadLibraryA(dllPath.string().c_str()) == NULL) { 23 | return 2; 24 | } 25 | return 0; 26 | } 27 | 28 | OodleHandlerResult ParseData(const char* url, std::stringstream& outStrm) { 29 | std::string indexData; 30 | int indexDataOffset = 0; 31 | { 32 | auto indexConn = Client::CreateConnection(); 33 | indexConn->SetUrl(url 34 | ); 35 | if (!Client::Execute(indexConn, cancel_flag())) { 36 | return OodleHandlerResult::NET_ERROR; 37 | } 38 | indexData = indexConn->GetResponseBody(); 39 | } 40 | 41 | lzma_stream strm = LZMA_STREAM_INIT; 42 | 43 | if (lzma_alone_decoder(&strm, UINT64_MAX) != LZMA_OK) { 44 | return OodleHandlerResult::LZMA_ERROR; 45 | } 46 | 47 | auto outBuf = std::make_unique(1024 * 1024); 48 | strm.next_out = (uint8_t*)outBuf.get(); 49 | 50 | while (indexDataOffset < indexData.size()) { 51 | strm.next_in = (uint8_t*)indexData.data() + indexDataOffset; 52 | strm.avail_in = std::min(1024 * 1024, (int)indexData.size() - indexDataOffset); 53 | strm.avail_out = 1024 * 1024; 54 | auto ret = lzma_code(&strm, LZMA_RUN); 55 | if (ret == LZMA_OK) { 56 | outStrm.write(outBuf.get(), 1024 * 1024 - strm.avail_out); 57 | indexDataOffset += 1024 * 1024 - strm.avail_in; 58 | } 59 | else if (ret == LZMA_STREAM_END) { 60 | outStrm.write(outBuf.get(), 1024 * 1024 - strm.avail_out); 61 | break; 62 | } 63 | else { 64 | lzma_end(&strm); 65 | return OodleHandlerResult::LZMA_ERROR; 66 | } 67 | } 68 | 69 | lzma_end(&strm); 70 | return OodleHandlerResult::LOADED; 71 | } 72 | 73 | OodleHandlerResult LoadOodle(const fs::path& cachePath) { 74 | static bool alreadyLoaded = false; 75 | if (alreadyLoaded) { 76 | return OodleHandlerResult::LOADED; 77 | } 78 | 79 | auto loadRet = LoadDll(cachePath / OODLE_DLL_NAME); 80 | if (!loadRet) { 81 | alreadyLoaded = true; 82 | return OodleHandlerResult::LOADED; 83 | } 84 | { 85 | std::error_code ec; 86 | fs::remove(cachePath / OODLE_DLL_NAME, ec); 87 | } 88 | 89 | std::stringstream outStrm; 90 | auto ret = ParseData(WARFRAME_INDEX_URL, outStrm); 91 | if (ret != OodleHandlerResult::LOADED) { 92 | return ret; 93 | } 94 | 95 | std::string line, dllUrl; 96 | while (std::getline(outStrm, line)) { 97 | if (line.find(OODLE_DLL_NAME) != std::string::npos) { 98 | dllUrl = WARFRAME_CDN_HOST + line.substr(0, line.find(',')); 99 | break; 100 | } 101 | } 102 | if (dllUrl.empty()) { 103 | return OodleHandlerResult::INDEX_ERROR; 104 | } 105 | 106 | outStrm = std::stringstream(); 107 | ret = ParseData(dllUrl.c_str(), outStrm); 108 | if (ret != OodleHandlerResult::LOADED) { 109 | return ret; 110 | } 111 | 112 | std::ofstream dllFp((cachePath / OODLE_DLL_NAME).string().c_str(), std::ios::binary | std::ios::trunc); 113 | if (dllFp.bad()) { 114 | return OodleHandlerResult::CANNOT_WRITE; 115 | } 116 | dllFp << outStrm.rdbuf(); 117 | dllFp.close(); 118 | 119 | loadRet = LoadDll(cachePath / OODLE_DLL_NAME); 120 | if (!loadRet) { 121 | alreadyLoaded = true; 122 | return OodleHandlerResult::LOADED; 123 | } 124 | return OodleHandlerResult::CANNOT_LOAD; 125 | } 126 | -------------------------------------------------------------------------------- /checks/oodle_handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // https://encode.su/threads/2577-Open-source-Kraken-Mermaid-Selkie-LZNA-BitKnit-decompression/page2#post_49929 4 | // Quite an interesting thread, too. Thanks for all the work you do RAD :) 5 | 6 | // Legally, I can't distribute the dll, so I'll grab it from a legal source. 7 | // https://origin.warframe.com/origin/00000000/index.txt.lzma 8 | 9 | #include 10 | 11 | namespace fs = std::filesystem; 12 | 13 | enum class OodleHandlerResult { 14 | LOADED, // successfully loaded 15 | NET_ERROR, // failed to download 16 | LZMA_ERROR, // some lzma error 17 | INDEX_ERROR, // cannot parse warframe's index 18 | CANNOT_LOAD, // cannot load dll 19 | CANNOT_WRITE // cannot write dll 20 | }; 21 | 22 | OodleHandlerResult LoadOodle(const fs::path& cachePath); -------------------------------------------------------------------------------- /checks/symlink_workaround.cpp: -------------------------------------------------------------------------------- 1 | #include "symlink_workaround.h" 2 | 3 | #define DEVELOPER_MODE_REGKEY L"Software\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock" 4 | #define DEVELOPER_MODE_REGVALUE L"AllowDevelopmentWithoutDevLicense" 5 | 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #include 9 | 10 | // Taken from https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-checktokenmembership 11 | bool IsUserAdmin() 12 | { 13 | SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; 14 | PSID AdministratorsGroup; 15 | BOOL b = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup); 16 | if (b) 17 | { 18 | if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) 19 | { 20 | b = false; 21 | } 22 | FreeSid(AdministratorsGroup); 23 | } 24 | 25 | return b; 26 | } 27 | 28 | bool IsDeveloperModeEnabled() { 29 | HKEY key; 30 | auto Result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVELOPER_MODE_REGKEY, NULL, KEY_READ, &key); 31 | 32 | if (Result != ERROR_SUCCESS) { // should be ERROR_NO_MATCH or ERROR_FILE_NOT_FOUND 33 | return false; // In reality, AppModelUnlock should always exist, so this shouldn't run 34 | } 35 | else { 36 | DWORD data; 37 | DWORD type = REG_DWORD; 38 | DWORD size = sizeof(data); 39 | Result = RegQueryValueEx(key, DEVELOPER_MODE_REGVALUE, NULL, &type, (LPBYTE)&data, &size); 40 | RegCloseKey(key); 41 | return Result == ERROR_SUCCESS ? data : false; 42 | } 43 | } 44 | 45 | bool EnableDeveloperMode() { 46 | DWORD data = 1; 47 | return RegSetKeyValue(HKEY_LOCAL_MACHINE, DEVELOPER_MODE_REGKEY, DEVELOPER_MODE_REGVALUE, REG_DWORD, &data, sizeof(data)) == ERROR_SUCCESS; 48 | } -------------------------------------------------------------------------------- /checks/symlink_workaround.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Symlinks can only be made if: 4 | // - The program is in administrator mode 5 | // - Developer mode is active (Win10 only) 6 | // - Local Security Policy (secpol.msc): Local Policies -> User Rights Assignment -> Create symbolic links: must include the user's group/SID here (ties into first bullet point) 7 | // 8 | // A terrible security workaround, but I'm unsure if there is any other better way around it. 9 | 10 | // Implementation of workaround: 11 | // if (!IsDeveloperModeEnabled()) { 12 | // if (!IsUserAdmin()) { 13 | // Message("Please restart the program in administator mode!"); 14 | // } 15 | // else { 16 | // EnableDeveloperMode(); 17 | // } 18 | // } 19 | 20 | bool IsUserAdmin(); 21 | 22 | bool IsDeveloperModeEnabled(); 23 | 24 | bool EnableDeveloperMode(); -------------------------------------------------------------------------------- /checks/winfspcheck.cpp: -------------------------------------------------------------------------------- 1 | #include "winfspcheck.h" 2 | 3 | #define WINFSP_INSTALL_REGKEY L"Software\\WOW6432Node\\WinFsp" 4 | #define WINFSP_INSTALL_REGVALUE L"InstallDir" 5 | 6 | #include 7 | #define WIN32_LEAN_AND_MEAN 8 | #include 9 | #include 10 | 11 | namespace fs = std::filesystem; 12 | 13 | fs::path GetRegInstallDir() { 14 | HKEY key; 15 | auto Result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WINFSP_INSTALL_REGKEY, NULL, KEY_READ, &key); 16 | 17 | if (Result != ERROR_SUCCESS) { // should be ERROR_NO_MATCH or ERROR_FILE_NOT_FOUND 18 | return nullptr; // In reality, it should always exist, so this shouldn't run 19 | } 20 | else { 21 | WCHAR data[MAX_PATH]; 22 | DWORD type = REG_SZ; 23 | DWORD size = sizeof(data); 24 | Result = RegQueryValueEx(key, WINFSP_INSTALL_REGVALUE, NULL, &type, (LPBYTE)&data, &size); 25 | RegCloseKey(key); 26 | return Result == ERROR_SUCCESS ? data : nullptr; 27 | } 28 | } 29 | 30 | WinFspCheckResult LoadWinFsp() { 31 | static bool alreadyLoaded = false; 32 | if (alreadyLoaded) { 33 | return WinFspCheckResult::LOADED; 34 | } 35 | 36 | fs::path InstallDir = GetRegInstallDir(); 37 | if (InstallDir.empty()) { 38 | { 39 | PWSTR appDataFolder; 40 | if (SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, NULL, &appDataFolder) != S_OK) { 41 | return WinFspCheckResult::NO_PATH; 42 | } 43 | InstallDir = fs::path(appDataFolder) / "WinFsp"; 44 | CoTaskMemFree(appDataFolder); 45 | } 46 | } 47 | 48 | InstallDir = InstallDir / "bin" / "winfsp-x64.dll"; 49 | 50 | if (fs::status(InstallDir).type() != fs::file_type::regular) { 51 | return WinFspCheckResult::NO_DLL; 52 | } 53 | if (LoadLibraryA(InstallDir.string().c_str()) == NULL) { 54 | return WinFspCheckResult::CANNOT_LOAD; 55 | } 56 | alreadyLoaded = true; 57 | return WinFspCheckResult::LOADED; 58 | } -------------------------------------------------------------------------------- /checks/winfspcheck.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class WinFspCheckResult { 4 | LOADED, // successfully loaded 5 | NO_PATH, // cannot get program files x86 folder 6 | NO_DLL, // no dll in winfsp folder 7 | CANNOT_LOAD // cannot load dll 8 | }; 9 | 10 | WinFspCheckResult LoadWinFsp(); -------------------------------------------------------------------------------- /containers/cancel_flag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define SAFE_FLAG_RETURN(value) if (flag.cancelled()) return value; 6 | 7 | class cancel_flag { 8 | public: 9 | bool cancelled() { 10 | return value.load(); 11 | } 12 | 13 | bool cancelled() const { 14 | return value.load(); 15 | } 16 | 17 | bool cancelled() volatile { 18 | return value.load(); 19 | } 20 | 21 | bool cancelled() const volatile { 22 | return value.load(); 23 | } 24 | 25 | void cancel() { 26 | value.store(true); 27 | } 28 | 29 | void cancel() volatile { 30 | value.store(true); 31 | } 32 | 33 | protected: 34 | std::atomic_bool value = false; 35 | }; -------------------------------------------------------------------------------- /containers/file_sha.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace fs = std::filesystem; 8 | 9 | inline const bool SHAFile(fs::path path, char OutHash[SHA_DIGEST_LENGTH]) { 10 | static constexpr int buffer_size = 1 << 14; // 16384 11 | char buffer[buffer_size]; 12 | 13 | SHA_CTX ctx; 14 | SHA1_Init(&ctx); 15 | 16 | std::ifstream fp(path.c_str(), std::ios::in | std::ios::binary); 17 | 18 | if (!fp.good()) { 19 | return false; 20 | } 21 | 22 | while (fp.good()) { 23 | fp.read(buffer, buffer_size); 24 | SHA1_Update(&ctx, buffer, fp.gcount()); 25 | } 26 | fp.close(); 27 | 28 | SHA1_Final((unsigned char*)OutHash, &ctx); 29 | return true; 30 | } -------------------------------------------------------------------------------- /docs/step1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step1.gif -------------------------------------------------------------------------------- /docs/step10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step10.gif -------------------------------------------------------------------------------- /docs/step11.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step11.gif -------------------------------------------------------------------------------- /docs/step12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step12.gif -------------------------------------------------------------------------------- /docs/step2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step2.gif -------------------------------------------------------------------------------- /docs/step4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step4.gif -------------------------------------------------------------------------------- /docs/step5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step5.gif -------------------------------------------------------------------------------- /docs/step7.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step7.gif -------------------------------------------------------------------------------- /docs/step8.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/docs/step8.gif -------------------------------------------------------------------------------- /filesystem/dirtree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "egfs.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | class DirTree { 13 | public: 14 | static constexpr auto Separators = L"\\/"; 15 | 16 | using node_type = typename std::variant, T>; 17 | using container_type = typename std::unordered_map; 18 | using iterator = typename container_type::iterator; 19 | using const_iterator = typename container_type::const_iterator; 20 | 21 | DirTree() : Parent(nullptr) { } 22 | 23 | DirTree(DirTree* parent) 24 | { 25 | Parent = parent; 26 | } 27 | 28 | ~DirTree() { 29 | 30 | } 31 | 32 | void AddFile(std::wstring_view& path, T&& data) { 33 | auto separator = path.find_first_of(Separators); 34 | if (separator != std::wstring_view::npos) { 35 | DirTree* child; 36 | { 37 | // emplace creates a new value if it doesn't exist and otherwise grabs by the key 38 | auto& emplaced = Children.emplace(std::wstring(path.substr(0, separator)), DirTree(this)); 39 | if (emplaced.second) { 40 | child = &std::get>(emplaced.first->second); 41 | child->Name = emplaced.first->first; 42 | } 43 | else { 44 | if (auto folderPtr = std::get_if>(&emplaced.first->second)) { 45 | child = folderPtr; 46 | } 47 | else { 48 | return; // child existed as a file, not a folder 49 | } 50 | } 51 | } 52 | child->AddFile(path.substr(separator + 1), std::forward(data)); 53 | } 54 | else { 55 | Children[std::wstring(path)] = data; 56 | } 57 | } 58 | 59 | void AddFile(const wchar_t* path, T&& data) { 60 | AddFile(std::wstring_view(path), std::forward(data)); 61 | } 62 | 63 | node_type* GetFile(std::wstring_view& path) { 64 | auto separator = path.find_first_of(Separators); 65 | if (separator != std::wstring_view::npos) { 66 | auto n = Children.find(std::wstring(path.substr(0, separator))); 67 | if (n == Children.end()) { 68 | return nullptr; 69 | } 70 | auto& child = n->second; 71 | if (auto folder = std::get_if>(&child)) { 72 | return folder->GetFile(path.substr(separator + 1)); 73 | } 74 | return nullptr; 75 | } 76 | else { 77 | auto n = Children.find(std::wstring(path)); 78 | if (n == Children.end()) { 79 | return nullptr; 80 | } 81 | return &n->second; 82 | } 83 | } 84 | 85 | node_type* GetFile(const wchar_t* path) { 86 | return GetFile(std::wstring_view(path)); 87 | } 88 | 89 | inline iterator begin() noexcept { return Children.begin(); } 90 | inline const_iterator cbegin() const noexcept { return Children.cbegin(); } 91 | inline iterator end() noexcept { return Children.end(); } 92 | inline const_iterator cend() const noexcept { return Children.cend(); } 93 | 94 | const DirTree* Parent; 95 | std::wstring Name; 96 | container_type Children; 97 | }; 98 | 99 | template 100 | class RootTree { 101 | public: 102 | using node_type = typename DirTree::node_type; 103 | using container_type = typename DirTree::container_type; 104 | using iterator = typename container_type::iterator; 105 | using const_iterator = typename container_type::const_iterator; 106 | 107 | RootTree() : 108 | _RootNode(DirTree()), 109 | RootNode(std::get_if>(&_RootNode)) 110 | { } 111 | 112 | ~RootTree() { 113 | 114 | } 115 | 116 | node_type* GetFile(const wchar_t* path) { 117 | if (path[1] == L'\0') { 118 | return &_RootNode; 119 | } 120 | return RootNode->GetFile(path + 1); 121 | } 122 | 123 | void AddFile(const wchar_t* path, T&& data) { 124 | RootNode->AddFile(std::wstring_view(path), std::forward(data)); 125 | } 126 | 127 | inline iterator begin() noexcept { return RootNode->begin(); } 128 | inline const_iterator cbegin() const noexcept { return RootNode->cbegin(); } 129 | inline iterator end() noexcept { return RootNode->end(); } 130 | inline const_iterator cend() const noexcept { return RootNode->cend(); } 131 | 132 | node_type _RootNode; 133 | DirTree* RootNode; 134 | }; -------------------------------------------------------------------------------- /filesystem/egfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "dirtree.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace fs = std::filesystem; 11 | 12 | typedef std::function EGFS_READ_CALLBACK; 13 | 14 | struct EGFS_PARAMS { 15 | WCHAR FileSystemName[16]; 16 | WCHAR VolumePrefix[32]; // can be 192 length, but why would you 17 | WCHAR VolumeLabel[32]; 18 | UINT64 VolumeTotal; 19 | UINT64 VolumeFree; 20 | PVOID Security; 21 | SIZE_T SecuritySize; 22 | EGFS_READ_CALLBACK OnRead; 23 | 24 | UINT16 SectorSize; 25 | UINT16 SectorsPerAllocationUnit; 26 | UINT64 VolumeCreationTime; 27 | UINT32 VolumeSerialNumber; 28 | UINT32 FileInfoTimeout; 29 | bool CaseSensitiveSearch; 30 | bool CasePreservedNames; 31 | bool UnicodeOnDisk; 32 | bool PersistentAcls; 33 | bool ReparsePoints; 34 | bool ReparsePointsAccessCheck; 35 | bool PostCleanupWhenModifiedOnly; 36 | bool FlushAndPurgeOnCleanup; 37 | bool AllowOpenInKernelMode; 38 | 39 | UINT32 LogFlags; 40 | }; 41 | 42 | struct EGFS_FILE { 43 | EGFS_FILE(UINT64 FileSize, PVOID Context, UINT64 CreationTime = 0, UINT64 AccessTime = 0, UINT64 WriteTime = 0, UINT64 ChangeTime = 0) : 44 | Context(Context), 45 | FileSize(FileSize), 46 | CreationTime(CreationTime), 47 | AccessTime(AccessTime), 48 | WriteTime(WriteTime), 49 | ChangeTime(ChangeTime) 50 | { } 51 | 52 | PVOID Context; 53 | 54 | UINT64 FileSize; 55 | UINT64 CreationTime; // File creation timestamp 56 | UINT64 AccessTime; // File data read timestamp 57 | UINT64 WriteTime; // File data change timestamp 58 | UINT64 ChangeTime; // Metadata change timestamp 59 | }; 60 | 61 | class EGFS { 62 | public: 63 | using container_type = typename DirTree; 64 | using node_type = typename container_type::node_type; 65 | 66 | EGFS(EGFS_PARAMS* Params, NTSTATUS& ErrorCode); 67 | ~EGFS(); 68 | 69 | bool SetMountPoint(PCWSTR MountDir, PVOID Security); 70 | void AddFile(fs::path&& Path, PVOID Context, UINT64 FileSize); 71 | 72 | bool Start(); 73 | bool Stop(); 74 | bool Started(); 75 | 76 | private: 77 | RootTree Files; 78 | 79 | FSP_FILE_SYSTEM* FileSystem; 80 | EGFS_READ_CALLBACK OnRead; 81 | 82 | std::unique_ptr Security; 83 | SIZE_T SecuritySize; 84 | 85 | WCHAR VolumeLabel[32]; 86 | UINT64 VolumeTotal; 87 | UINT64 VolumeFree; 88 | 89 | bool IsStarted; 90 | 91 | static UINT32 GetFileAttributes(node_type* file); 92 | static void GetFileInfo(container_type* file, FSP_FSCTL_FILE_INFO* info); 93 | static void GetFileInfo(node_type* file, FSP_FSCTL_FILE_INFO* info); 94 | static UINT64 GetFileSize(node_type* file); 95 | 96 | // Interface 97 | static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM* FileSystem, FSP_FSCTL_VOLUME_INFO* VolumeInfo); 98 | static NTSTATUS SetVolumeLabel(FSP_FILE_SYSTEM* FileSystem, PWSTR VolumeLabel, FSP_FSCTL_VOLUME_INFO* VolumeInfo); 99 | static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM* FileSystem, PWSTR FileName, PUINT32 PFileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T* PSecurityDescriptorSize); 100 | static NTSTATUS Create(FSP_FILE_SYSTEM* FileSystem, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, PVOID* PFileNode, FSP_FSCTL_FILE_INFO* FileInfo); 101 | static NTSTATUS Open(FSP_FILE_SYSTEM* FileSystem, PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, PVOID* PFileNode, FSP_FSCTL_FILE_INFO* FileInfo); 102 | static NTSTATUS Overwrite(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, FSP_FSCTL_FILE_INFO* FileInfo); 103 | static VOID Cleanup(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PWSTR FileName, ULONG Flags); 104 | static VOID Close(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0); 105 | static NTSTATUS Read(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length, PULONG PBytesTransferred); 106 | static NTSTATUS Write(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length, BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO* FileInfo); 107 | static NTSTATUS Flush(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, FSP_FSCTL_FILE_INFO* FileInfo); 108 | static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, FSP_FSCTL_FILE_INFO* FileInfo); 109 | static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, UINT32 FileAttributes, UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, FSP_FSCTL_FILE_INFO* FileInfo); 110 | static NTSTATUS SetFileSize(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO* FileInfo); 111 | static NTSTATUS CanDelete(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PWSTR FileName); 112 | static NTSTATUS Rename(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists); 113 | static NTSTATUS GetSecurity(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T* PSecurityDescriptorSize); 114 | static NTSTATUS SetSecurity(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor); 115 | static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM* FileSystem, PVOID FileNode0, PWSTR Pattern, PWSTR Marker, PVOID Buffer, ULONG Length, PULONG PBytesTransferred); 116 | 117 | static FSP_FILE_SYSTEM_INTERFACE FspInterface; 118 | }; -------------------------------------------------------------------------------- /gui/GameUpdateChecker.cpp: -------------------------------------------------------------------------------- 1 | #include "GameUpdateChecker.h" 2 | 3 | #ifndef LOG_SECTION 4 | #define LOG_SECTION "GameUpdateChecker" 5 | #endif 6 | 7 | #include "../Logger.h" 8 | 9 | GameUpdateChecker::GameUpdateChecker(fs::path cachePath, GameUpdateCallback callback, std::chrono::milliseconds checkInterval) : 10 | Auth(cachePath), 11 | Callback(callback), 12 | CheckInterval(checkInterval) 13 | { 14 | LOG_DEBUG("Initializing force update"); 15 | ForceUpdate(); 16 | LOG_DEBUG("Creating game update thread"); 17 | UpdateThread = std::thread(&GameUpdateChecker::Thread, this); 18 | UpdateThread.detach(); // causes some exception when the deconstructer is trying to join it otherwise 19 | } 20 | 21 | GameUpdateChecker::~GameUpdateChecker() { 22 | UpdateFlag.cancel(); 23 | } 24 | 25 | void GameUpdateChecker::SetInterval(std::chrono::milliseconds newInterval) 26 | { 27 | CheckInterval.store(newInterval); 28 | UpdateWakeup = std::chrono::steady_clock::time_point::min(); // force update 29 | } 30 | 31 | void GameUpdateChecker::StopUpdateThread() 32 | { 33 | UpdateFlag.cancel(); 34 | } 35 | 36 | bool GameUpdateChecker::ForceUpdate() { 37 | auto info = Auth.GetLatestManifest(); 38 | auto id = Auth.GetManifestId(info.first); 39 | if (LatestId == id) { 40 | return false; 41 | } 42 | LatestUrl = info.first; 43 | LatestId = id; 44 | LatestVersion = info.second; 45 | return true; 46 | } 47 | 48 | std::string& GameUpdateChecker::GetLatestId() 49 | { 50 | return LatestId; 51 | } 52 | 53 | std::string& GameUpdateChecker::GetLatestUrl() 54 | { 55 | return LatestUrl; 56 | } 57 | 58 | std::string& GameUpdateChecker::GetLatestVersion() 59 | { 60 | return LatestVersion; 61 | } 62 | 63 | Manifest GameUpdateChecker::GetManifest(const std::string& Url) 64 | { 65 | return Auth.GetManifest(Url); 66 | } 67 | 68 | void GameUpdateChecker::Thread() { 69 | while (!UpdateFlag.cancelled()) { 70 | do { 71 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 72 | } while (std::chrono::steady_clock::now() < UpdateWakeup); 73 | if (ForceUpdate()) { 74 | Callback(LatestUrl, LatestVersion); 75 | UpdateWakeup = std::chrono::steady_clock::now() + std::chrono::seconds(60); // fixes load balancing issues 76 | } 77 | else { 78 | UpdateWakeup = std::chrono::steady_clock::now() + CheckInterval.load(); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /gui/GameUpdateChecker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../containers/cancel_flag.h" 3 | #include "../web/manifest/auth.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace fs = std::filesystem; 11 | 12 | typedef std::function GameUpdateCallback; 13 | 14 | class GameUpdateChecker { 15 | public: 16 | GameUpdateChecker(fs::path cachePath, GameUpdateCallback callback, std::chrono::milliseconds checkInterval); 17 | ~GameUpdateChecker(); 18 | 19 | void SetInterval(std::chrono::milliseconds newInterval); 20 | 21 | void StopUpdateThread(); 22 | 23 | bool ForceUpdate(); 24 | 25 | std::string& GetLatestId(); 26 | std::string& GetLatestUrl(); 27 | std::string& GetLatestVersion(); 28 | 29 | static std::string GetReadableVersion(const std::string_view& version) { 30 | auto start = version.find('-') + 1; 31 | auto start2 = version.find('-', start); 32 | auto end2 = version.find('-', start2 + 1) + 1; 33 | auto end = version.find('-', end2); 34 | 35 | char buf[64]; 36 | sprintf_s(buf, "%.*s (CL: %.*s)", start2 - start, version.data() + start, end - end2, version.data() + end2); 37 | return buf; 38 | } 39 | 40 | Manifest GetManifest(const std::string& Url); 41 | 42 | private: 43 | void Thread(); 44 | 45 | std::atomic CheckInterval; 46 | 47 | ManifestAuth Auth; 48 | std::string LatestId; 49 | std::string LatestUrl; 50 | std::string LatestVersion; 51 | 52 | GameUpdateCallback Callback; 53 | std::thread UpdateThread; 54 | std::chrono::steady_clock::time_point UpdateWakeup; 55 | cancel_flag UpdateFlag; 56 | }; -------------------------------------------------------------------------------- /gui/Localization.cpp: -------------------------------------------------------------------------------- 1 | #include "Localization.h" 2 | 3 | #ifndef LOG_SECTION 4 | #define LOG_SECTION "Locale" 5 | #endif 6 | 7 | #include "../Logger.h" 8 | 9 | #include 10 | #define WIN32_LEAN_AND_MEAN 11 | #include 12 | #include 13 | 14 | inline wchar_t* ReadString(char** bufPos) { 15 | uint16_t size = *(uint16_t*)(*bufPos); 16 | auto buf = new wchar_t[size + 1]; 17 | memcpy(buf, *bufPos + sizeof(uint16_t), size * 2); 18 | buf[size] = L'\0'; 19 | *bufPos += sizeof(uint16_t) + size * 2; 20 | return buf; 21 | } 22 | 23 | inline void SplitLCID(LCID lcid, uint8_t& sortId, uint16_t& languageId) { 24 | sortId = (lcid >> 16) & 0xF; 25 | languageId = lcid & 0xFFFF; 26 | } 27 | 28 | Locale Localization::GetSystemLocale() 29 | { 30 | uint8_t sortId; 31 | // language ids contain 2 bytes 32 | // the first is the language byte (the actual language: en, fr, es, ar, etc.) 33 | // the second is the region byte (only unique to the language, one doesn't always correspond to the same region) 34 | // a region of 0x40 is not specifically US (only in en-US) 35 | uint16_t langId; 36 | SplitLCID(GetUserDefaultLCID(), sortId, langId); 37 | switch (langId & 0xFF) 38 | { 39 | case 0x01: 40 | return Locale::AR; 41 | case 0x07: 42 | return Locale::DE; 43 | case 0x09: 44 | return Locale::EN; 45 | case 0x0A: 46 | return Locale::ES; 47 | case 0x0B: 48 | return Locale::FI; 49 | case 0x0C: 50 | return Locale::FR; 51 | case 0x10: 52 | return Locale::IT; 53 | case 0x11: 54 | return Locale::JA; 55 | case 0x15: 56 | return Locale::PL; 57 | case 0x16: 58 | return Locale::PT_BR; // just treat it as normal portuguese (should be similar enough I hope) 59 | case 0x19: 60 | return Locale::RU; 61 | case 0x1F: 62 | return Locale::TR; 63 | default: 64 | LOG_WARN("Using default locale for %02X\n", langId & 0xFF); 65 | return Locale::EN; 66 | } 67 | } 68 | 69 | inline LPCWSTR GetResourceName(Locale locale) { 70 | switch (locale) { 71 | #define LS(name) case Locale::##name: return L"LOCALE_" #name; 72 | LOCALETYPES 73 | #undef LS 74 | default: return L"LOCALE_EN"; 75 | } 76 | } 77 | 78 | bool Localization::UseLocale(Locale locale) { 79 | if (locale == SelectedLocale) { 80 | return true; 81 | } 82 | auto resInfo = FindResource(NULL, GetResourceName(locale), RT_RCDATA); 83 | if (!resInfo) { 84 | LOG_FATAL("Could not find locale resource!"); 85 | return false; 86 | } 87 | auto resData = LoadResource(NULL, resInfo); 88 | if (!resData) { 89 | LOG_FATAL("Could not load locale resource!"); 90 | return false; 91 | } 92 | auto resPtr = LockResource(resData); 93 | if (!resPtr) { 94 | LOG_FATAL("Could not lock locale resource!"); 95 | FreeResource(resData); 96 | return false; 97 | } 98 | auto resSize = SizeofResource(NULL, resInfo); 99 | if (!resSize) { 100 | LOG_FATAL("Could not get size of locale resource!"); 101 | FreeResource(resData); 102 | return false; 103 | } 104 | 105 | auto locData = std::unique_ptr(new char[*(uint32_t*)resPtr]); 106 | auto locSize = ZSTD_decompress(locData.get(), *(uint32_t*)resPtr, (char*)resPtr + sizeof(uint32_t), resSize - sizeof(uint32_t)); 107 | if (ZSTD_isError(locSize)) { 108 | LOG_FATAL("Could not decompress locale data: %s", ZSTD_getErrorName(locSize)); 109 | FreeResource(resData); 110 | return false; 111 | } 112 | 113 | char* locPos = locData.get(); 114 | for (int i = 0; i < (int)LocaleString::Count; ++i) { 115 | if (locPos - locSize == locData.get()) { 116 | LOG_ERROR("Could not parse locale data at %d", i); 117 | FreeResource(resData); 118 | SelectedLocale = locale; 119 | return true; 120 | } 121 | LocaleStrings[i].reset(ReadString(&locPos)); 122 | } 123 | 124 | FreeResource(resData); 125 | SelectedLocale = locale; 126 | return true; 127 | } -------------------------------------------------------------------------------- /gui/Localization.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LSTR(name) Localization::GetString(LocaleString::name) 4 | #define LTITLE(str) wxString::Format("%s - EGL2", str) 5 | 6 | #include "localedefs.h" 7 | 8 | #include 9 | 10 | enum class Locale : uint8_t { 11 | #define LS(name) name, 12 | LOCALETYPES 13 | #undef LS 14 | 15 | // If you want to help with translating EGL2 into other languages, dm me @AsrielD#6969 16 | Count 17 | }; 18 | 19 | enum class LocaleString : uint16_t { 20 | #define LS(name) name, 21 | LOCALESTRINGS 22 | #undef LS 23 | 24 | Count 25 | }; 26 | 27 | class Localization { 28 | public: 29 | Localization() = delete; 30 | Localization(const Localization&) = delete; 31 | Localization& operator=(const Localization&) = delete; 32 | 33 | static Locale GetSystemLocale(); 34 | 35 | static bool UseLocale(Locale locale); 36 | 37 | static inline const wchar_t* GetString(LocaleString string) { 38 | return LocaleStrings[(int)string].get(); 39 | } 40 | 41 | private: 42 | static inline Locale SelectedLocale = (Locale)-1; 43 | 44 | static inline std::unique_ptr LocaleStrings[(int)LocaleString::Count]; 45 | }; -------------------------------------------------------------------------------- /gui/UpdateChecker.cpp: -------------------------------------------------------------------------------- 1 | #include "UpdateChecker.h" 2 | 3 | #define GH_REPO "WorkingRobot/EGL2" 4 | #define GH_RELEASE_URL "https://api.github.com/repos/" GH_REPO "/releases/latest" 5 | 6 | #ifndef LOG_SECTION 7 | #define LOG_SECTION "UpdateChecker" 8 | #endif 9 | 10 | #include "../Logger.h" 11 | #include "../web/http.h" 12 | #include "versioninfo.h" 13 | 14 | #include 15 | 16 | UpdateChecker::UpdateChecker(UpdateCallback callback, std::chrono::milliseconds checkInterval) : 17 | Callback(callback), 18 | CheckInterval(checkInterval), 19 | LatestVersion(VERSION_STRING) 20 | { 21 | LOG_DEBUG("Creating update thread"); 22 | UpdateThread = std::thread(&UpdateChecker::Thread, this); 23 | UpdateThread.detach(); // causes some exception when the deconstructer is trying to join it otherwise 24 | } 25 | 26 | UpdateChecker::~UpdateChecker() { 27 | UpdateFlag.cancel(); 28 | } 29 | 30 | void UpdateChecker::SetInterval(std::chrono::milliseconds newInterval) 31 | { 32 | CheckInterval.store(newInterval); 33 | UpdateWakeup = std::chrono::steady_clock::time_point::min(); // force update 34 | } 35 | 36 | void UpdateChecker::StopUpdateThread() 37 | { 38 | UpdateFlag.cancel(); 39 | } 40 | 41 | bool UpdateChecker::ForceUpdate() { 42 | auto conn = Client::CreateConnection(); 43 | conn->SetUrl(GH_RELEASE_URL); 44 | conn->SetUserAgent(GH_REPO); 45 | 46 | if (!Client::Execute(conn, cancel_flag(), true)) { 47 | LOG_WARN("Could not check for EGL2 update"); 48 | return false; 49 | } 50 | 51 | if (conn->GetResponseCode() != 200) { 52 | return false; 53 | } 54 | 55 | rapidjson::Document releaseInfo; 56 | releaseInfo.Parse(conn->GetResponseBody().c_str()); 57 | 58 | if (releaseInfo.HasParseError()) { 59 | LOG_ERROR("Getting release info: JSON Parse Error %d @ %zu", releaseInfo.GetParseError(), releaseInfo.GetErrorOffset()); 60 | return false; 61 | } 62 | 63 | auto version = releaseInfo["tag_name"].GetString(); 64 | if (LatestVersion == version) { 65 | LOG_DEBUG("NO EGL2 UPDATE"); 66 | return false; 67 | } 68 | 69 | LatestInfo.Version = version; 70 | LatestInfo.Url = releaseInfo["html_url"].GetString(); 71 | 72 | auto& exeAsset = releaseInfo["assets"][0]; 73 | LatestInfo.DownloadUrl = exeAsset["browser_download_url"].GetString(); 74 | LatestInfo.DownloadCount = exeAsset["download_count"].GetInt(); 75 | LatestInfo.DownloadSize = exeAsset["size"].GetInt(); 76 | return true; 77 | } 78 | 79 | UpdateInfo& UpdateChecker::GetLatestInfo() 80 | { 81 | return LatestInfo; 82 | } 83 | 84 | void UpdateChecker::Thread() { 85 | while (!UpdateFlag.cancelled()) { 86 | do { 87 | std::this_thread::sleep_for(std::chrono::milliseconds(5000)); 88 | } while (std::chrono::steady_clock::now() < UpdateWakeup); 89 | if (ForceUpdate()) { 90 | Callback(LatestInfo); 91 | } 92 | UpdateWakeup = std::chrono::steady_clock::now() + CheckInterval.load(); 93 | } 94 | } -------------------------------------------------------------------------------- /gui/UpdateChecker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../containers/cancel_flag.h" 3 | 4 | #include 5 | #include 6 | 7 | struct UpdateInfo { 8 | std::string Version; 9 | std::string Url; 10 | 11 | std::string DownloadUrl; 12 | int DownloadCount; 13 | int DownloadSize; 14 | }; 15 | 16 | typedef std::function UpdateCallback; 17 | 18 | class UpdateChecker { 19 | public: 20 | UpdateChecker(UpdateCallback callback, std::chrono::milliseconds checkInterval); 21 | ~UpdateChecker(); 22 | 23 | void SetInterval(std::chrono::milliseconds newInterval); 24 | 25 | void StopUpdateThread(); 26 | 27 | bool ForceUpdate(); 28 | 29 | UpdateInfo& GetLatestInfo(); 30 | 31 | private: 32 | void Thread(); 33 | 34 | std::atomic CheckInterval; 35 | 36 | std::string LatestVersion; 37 | UpdateInfo LatestInfo; 38 | 39 | UpdateCallback Callback; 40 | std::thread UpdateThread; 41 | std::chrono::steady_clock::time_point UpdateWakeup; 42 | cancel_flag UpdateFlag; 43 | }; -------------------------------------------------------------------------------- /gui/cApp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../web/personal/PersonalAuth.h" 4 | #include "cMain.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class cApp : public wxApp 11 | { 12 | public: 13 | cApp(); 14 | ~cApp(); 15 | 16 | virtual bool OnInit(); 17 | bool InitThread(); 18 | 19 | wxWindow* SplashWindow = nullptr; 20 | 21 | fs::path DataFolder; 22 | FILE* LogFile = nullptr; 23 | 24 | wxSingleInstanceChecker* InstanceChecker = nullptr; 25 | std::shared_ptr AuthDetails; 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /gui/cMain.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | #include "../MountedBuild.h" 5 | #include "../web/manifest/auth.h" 6 | #include "../web/personal/PersonalAuth.h" 7 | #include "cProgress.h" 8 | #include "cSetup.h" 9 | #include "cStorage.h" 10 | #include "GameUpdateChecker.h" 11 | #include "settings.h" 12 | #include "UpdateChecker.h" 13 | #include "wxHelpButton.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | class cMain : public wxFrame 23 | { 24 | public: 25 | cMain(wxApp* app, const fs::path& settingsPath, const fs::path& manifestPath, const std::shared_ptr& personalAuth); 26 | ~cMain(); 27 | 28 | protected: 29 | wxPanel* panel = nullptr; 30 | 31 | wxButton* settingsBtn = nullptr; 32 | wxHelpButton* settingsHelpBtn = nullptr; 33 | wxButton* storageBtn = nullptr; 34 | wxHelpButton* storageHelpBtn = nullptr; 35 | wxButton* verifyBtn = nullptr; 36 | wxHelpButton* verifyHelpBtn = nullptr; 37 | wxButton* playBtn = nullptr; 38 | wxHelpButton* playHelpBtn = nullptr; 39 | 40 | wxStaticBoxSizer* statsBox = nullptr; 41 | 42 | #define DEFINE_STAT(name) \ 43 | wxStaticText* stat##name##Label; \ 44 | wxGauge* stat##name##Value; \ 45 | wxStaticText* stat##name##Text; 46 | 47 | DEFINE_STAT(cpu) 48 | DEFINE_STAT(ram) 49 | DEFINE_STAT(read) 50 | DEFINE_STAT(write) 51 | DEFINE_STAT(provide) 52 | DEFINE_STAT(download) 53 | DEFINE_STAT(latency) 54 | DEFINE_STAT(threads) 55 | 56 | #undef DEFINE_STAT 57 | 58 | wxStaticText* statusBar = nullptr; 59 | wxStaticText* selloutBar = nullptr; 60 | 61 | void OnSettingsClicked(bool onStartup); 62 | void OnStorageClicked(); 63 | void OnVerifyClicked(); 64 | void OnPlayClicked(); 65 | 66 | bool OnClose(); 67 | 68 | void SetStatus(const wxString& string); 69 | 70 | private: 71 | void Mount(const std::string& Url); 72 | 73 | wxWeakRef App; 74 | wxSharedPtr Systray; 75 | wxWindowPtr VerifyWnd; 76 | wxWindowPtr UpdateWnd; 77 | wxWindowPtr SetupWnd; 78 | wxWindowPtr StorageWnd; 79 | 80 | bool FirstAuthLaunched = false; 81 | std::shared_ptr Auth; 82 | 83 | std::unique_ptr GameUpdater; 84 | bool GameUpdateAvailable; 85 | std::optional GameUpdateUrl; 86 | 87 | void OnGameUpdate(const std::string& Version, const std::optional& Url = std::nullopt); 88 | void BeginGameUpdate(); 89 | 90 | std::unique_ptr Updater; 91 | std::string UpdateUrl; 92 | 93 | void OnUpdate(const UpdateInfo& Info); 94 | void BeginUpdate(); 95 | 96 | fs::path SettingsPath; 97 | SETTINGS Settings; 98 | 99 | std::unique_ptr Build; 100 | 101 | friend class SystrayIcon; 102 | }; 103 | 104 | -------------------------------------------------------------------------------- /gui/cProgress.cpp: -------------------------------------------------------------------------------- 1 | #include "cProgress.h" 2 | 3 | #include "Localization.h" 4 | 5 | #include 6 | #include 7 | 8 | cProgress::cProgress(wxWindow* main, wxString taskName, cancel_flag& cancelFlag, std::function onCancel, float updateFreq, uint32_t maximum) : wxFrame(main, wxID_ANY, LTITLE(taskName), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE ^ (wxMAXIMIZE_BOX | wxRESIZE_BORDER)) { 9 | OnCancel = onCancel; 10 | value = 0; 11 | frequency = updateFreq; 12 | maxValue = maximum; 13 | startTime = Clock::now(); 14 | etaTimePoints.push(std::make_pair(0, startTime)); 15 | 16 | this->SetIcon(wxICON(APP_ICON)); 17 | this->SetMinSize(wxSize(400, -1)); 18 | this->SetMaxSize(wxSize(400, -1)); 19 | 20 | panel = new wxPanel(this, wxID_ANY, 21 | wxDefaultPosition, 22 | wxDefaultSize, 23 | wxTAB_TRAVERSAL); 24 | 25 | progressBar = new wxGauge(panel, wxID_ANY, maxValue, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH); 26 | 27 | progressPercent = new wxStaticText(panel, wxID_ANY, "0.00%", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); 28 | progressTotal = new wxStaticText(panel, wxID_ANY, "0 / 0", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); 29 | progressTimeElapsed = new wxStaticText(panel, wxID_ANY, wxString::Format("%s: 00:00:00", LSTR(PROG_LABEL_ELAPSED)), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); 30 | progressTimeETA = new wxStaticText(panel, wxID_ANY, wxString::Format("%s: 00:00:00", LSTR(PROG_LABEL_ETA)), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); 31 | 32 | progressCancelBtn = new wxButton(panel, wxID_ANY, LSTR(PROG_BTN_CANCEL)); 33 | progressTaskbar = new wxAppProgressIndicator(this, maxValue); 34 | 35 | progressTextSizer = new wxGridSizer(2, 2, 5, 5); 36 | progressTextSizer->Add(progressPercent, 1, wxEXPAND); 37 | progressTextSizer->Add(progressTimeElapsed, 1, wxEXPAND); 38 | progressTextSizer->Add(progressTotal, 1, wxEXPAND); 39 | progressTextSizer->Add(progressTimeETA, 1, wxEXPAND); 40 | 41 | auto sizer = new wxBoxSizer(wxVERTICAL); 42 | 43 | sizer->Add(progressBar, 0, wxEXPAND); 44 | sizer->Add(progressTextSizer, 0, wxEXPAND | wxUP | wxDOWN, 5); 45 | sizer->Add(progressCancelBtn, 0, wxEXPAND); 46 | 47 | auto topSizer = new wxBoxSizer(wxVERTICAL); 48 | topSizer->Add(sizer, wxSizerFlags(1).Expand().Border(wxALL, 5)); 49 | panel->SetSizerAndFit(topSizer); 50 | this->Fit(); 51 | 52 | progressCancelBtn->Bind(wxEVT_BUTTON, [this, &cancelFlag](wxCommandEvent& evt) { Cancel(cancelFlag); }); 53 | Bind(wxEVT_CLOSE_WINDOW, [this, &cancelFlag](wxCloseEvent& evt) { Cancel(cancelFlag); evt.Veto(); }); 54 | } 55 | 56 | cProgress::~cProgress() { 57 | delete progressTaskbar; 58 | } 59 | 60 | inline void cProgress::Cancel(cancel_flag& cancelFlag) { 61 | cancelFlag.cancel(); 62 | progressCancelBtn->Disable(); 63 | progressCancelBtn->SetLabel(LSTR(PROG_BTN_CANCELLING)); 64 | Finish(); 65 | Hide(); 66 | OnCancel(); 67 | } 68 | 69 | inline wxString FormatTime(ch::seconds secs) { 70 | auto mins = ch::duration_cast(secs); 71 | secs -= ch::duration_cast(mins); 72 | auto hour = ch::duration_cast(mins); 73 | mins -= ch::duration_cast(hour); 74 | 75 | return wxString::Format("%02d:%02d:%02d", hour.count(), mins.count(), int(secs.count())); 76 | } 77 | 78 | inline ch::seconds GetETA(ch::nanoseconds duration, uint32_t amt, uint32_t targetAmt) { 79 | return amt ? ch::duration_cast(duration * targetAmt / amt) : ch::seconds::zero(); 80 | } 81 | 82 | inline void cProgress::Update(bool force) { 83 | auto now = Clock::now(); 84 | ch::duration duration = now - lastUpdate; 85 | if (duration.count() < frequency || force) { 86 | return; 87 | } 88 | lastUpdate = now; 89 | auto val = value.load(); 90 | SetMaximum((std::max)(val, maxValue)); 91 | progressBar->SetValue(val + 1); 92 | progressBar->SetValue(val); 93 | progressTaskbar->SetValue(val); 94 | 95 | { 96 | progressPercent->SetLabel(wxString::Format("%.2f%%", float(val) * 100 / maxValue)); 97 | progressTotal->SetLabel(wxString::Format("%u / %u", val, maxValue)); 98 | 99 | auto elapsed = ch::duration_cast(now - startTime); 100 | progressTimeElapsed->SetLabel(wxString::Format("%s: %s", LSTR(PROG_LABEL_ELAPSED), FormatTime(elapsed))); 101 | 102 | auto& timePoint = etaTimePoints.front(); 103 | auto eta = GetETA(now - timePoint.second, val - timePoint.first, maxValue - val); 104 | progressTimeETA->SetLabel(wxString::Format("%s: %s", LSTR(PROG_LABEL_ETA), FormatTime(eta))); 105 | 106 | etaTimePoints.push(std::make_pair(val, now)); 107 | if (etaTimePoints.size() > queueSize) { 108 | etaTimePoints.pop(); 109 | } 110 | } 111 | } 112 | 113 | void cProgress::SetFrequency(float updateFreq) { 114 | frequency = updateFreq; 115 | } 116 | 117 | void cProgress::SetMaximum(uint32_t maximum) { 118 | if (maximum != maxValue) { 119 | maxValue = maximum; 120 | progressBar->SetRange(maxValue); 121 | progressTaskbar->SetRange(maxValue); 122 | Update(); 123 | } 124 | } 125 | 126 | void cProgress::Increment() { 127 | if (finished) { 128 | return; 129 | } 130 | value++; 131 | Update(value == maxValue); 132 | } 133 | 134 | void cProgress::Finish() { 135 | value = maxValue; 136 | finished = true; 137 | Update(true); 138 | } -------------------------------------------------------------------------------- /gui/cProgress.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../containers/cancel_flag.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ch = std::chrono; 10 | 11 | class cProgress : public wxFrame 12 | { 13 | typedef std::chrono::steady_clock Clock; 14 | static constexpr int queueSize = 200; 15 | 16 | public: 17 | cProgress(wxWindow* main, wxString taskName, cancel_flag& cancelFlag, std::function onCancel, float updateFreq = .05f, uint32_t maximum = 1); 18 | ~cProgress(); 19 | 20 | void SetFrequency(float updateFreq); 21 | void SetMaximum(uint32_t maximum); 22 | void Increment(); 23 | void Finish(); 24 | 25 | protected: 26 | wxPanel* panel = nullptr; 27 | 28 | wxGauge* progressBar = nullptr; 29 | wxSizer* progressTextSizer = nullptr; 30 | wxStaticText* progressPercent = nullptr; 31 | wxStaticText* progressTotal = nullptr; 32 | wxStaticText* progressTimeElapsed = nullptr; 33 | wxStaticText* progressTimeETA = nullptr; 34 | wxButton* progressCancelBtn = nullptr; 35 | 36 | wxAppProgressIndicator* progressTaskbar = nullptr; 37 | 38 | private: 39 | std::function OnCancel; 40 | void Cancel(cancel_flag& cancelFlag); 41 | 42 | void Update(bool force = false); 43 | 44 | bool finished = false; 45 | std::queue> etaTimePoints; 46 | Clock::time_point startTime; 47 | Clock::time_point lastUpdate; 48 | float frequency; 49 | std::atomic_uint32_t value; 50 | uint32_t maxValue; 51 | }; 52 | 53 | -------------------------------------------------------------------------------- /gui/cSetup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "settings.h" 4 | #include "wxModalWindow.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class cSetup : public wxModalWindow 11 | { 12 | public: 13 | using flush_callback = std::function; 14 | using validate_callback = std::function; 15 | using exit_callback = std::function; 16 | 17 | cSetup(wxWindow* main, SETTINGS* settings, bool startupInvalid, flush_callback callback, validate_callback validator, exit_callback onExit); 18 | ~cSetup(); 19 | 20 | private: 21 | std::vector> ReadBinds; 22 | std::vector> WriteBinds; 23 | 24 | SETTINGS* Settings; 25 | bool InvalidStartup; 26 | SETTINGS OldSettings; 27 | flush_callback Callback; 28 | validate_callback Validator; 29 | exit_callback OnExit; 30 | 31 | void ReadConfig(); 32 | void WriteConfig(); 33 | 34 | void OkClicked(); 35 | void CancelClicked(); 36 | void ApplyClicked(); 37 | void CloseClicked(wxCloseEvent& evt); 38 | }; 39 | 40 | -------------------------------------------------------------------------------- /gui/cSplash.cpp: -------------------------------------------------------------------------------- 1 | #include "cSplash.h" 2 | 3 | #include 4 | 5 | cSplash::cSplash() : wxFrame(nullptr, wxID_ANY, "EGL2 Splash", wxDefaultPosition, wxDefaultSize, wxFRAME_NO_TASKBAR | wxBORDER_NONE), 6 | background(L"SPLASH_ICON", wxBITMAP_TYPE_ICO_RESOURCE, 256, 256) { 7 | SetClientSize(background.GetSize()); 8 | 9 | Bind(wxEVT_PAINT, &cSplash::OnPaint, this); 10 | 11 | SetBackgroundStyle(wxBG_STYLE_CUSTOM); 12 | SetWindowLong(GetHandle(), GWL_EXSTYLE, GetWindowLong(GetHandle(), GWL_EXSTYLE) | WS_EX_LAYERED); 13 | SetLayeredWindowAttributes(GetHandle(), RGB(255, 0, 255), 255, LWA_COLORKEY); 14 | 15 | CenterOnScreen(); 16 | } 17 | 18 | cSplash::~cSplash() { 19 | 20 | } 21 | 22 | void cSplash::OnPaint(wxPaintEvent& event) 23 | { 24 | wxAutoBufferedPaintDC dc(this); 25 | dc.Clear(); 26 | 27 | dc.SetBrush(wxBrush(wxColor(255, 0, 255))); 28 | dc.SetPen(*wxTRANSPARENT_PEN); 29 | dc.DrawRectangle(0, 0, 256, 256); 30 | 31 | dc.DrawIcon(background, 0, 0); 32 | } 33 | -------------------------------------------------------------------------------- /gui/cSplash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class cSplash : public wxFrame 6 | { 7 | public: 8 | cSplash(); 9 | ~cSplash(); 10 | 11 | private: 12 | wxIcon background; 13 | 14 | void OnPaint(wxPaintEvent& event); 15 | }; -------------------------------------------------------------------------------- /gui/cStorage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../containers/cancel_flag.h" 4 | #include "../MountedBuild.h" 5 | #include "wxModalWindow.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | class cStorage : public wxModalWindow 12 | { 13 | public: 14 | cStorage(wxWindow* main, std::unique_ptr& build, uint32_t threadCount, std::function onClose); 15 | ~cStorage(); 16 | 17 | private: 18 | wxGauge* storageBar; 19 | wxStaticText* storageTxt; 20 | 21 | wxGauge* downloadBar; 22 | wxStaticText* downloadTxt; 23 | 24 | wxGauge* diskBar; 25 | wxStaticText* diskTxt; 26 | 27 | // [decomp, decomp%, comp, comp%, compRatio] 28 | wxStaticText* compTexts[ChunkFlagCompCount][5]; 29 | 30 | // [decompressed, compressed] 31 | std::atomic_size_t compStats[ChunkFlagCompCount][2]; 32 | std::atomic_size_t compMainStats[2]; 33 | 34 | cancel_flag flag; 35 | std::atomic_uint32_t chunkCount = 0; 36 | std::atomic_uint32_t chunkDlCount = 0; 37 | std::atomic lastUpdate; 38 | void AddChunkData(const ChunkMetadata& meta, bool isLast); 39 | 40 | std::function onClose; 41 | void OnClose(wxCloseEvent& evt); 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /gui/guimain.cpp: -------------------------------------------------------------------------------- 1 | #include "cApp.h" 2 | 3 | #ifdef USE_CONSOLE 4 | #define IMPLEMENT_APP wxIMPLEMENT_APP_CONSOLE 5 | #else 6 | #define IMPLEMENT_APP wxIMPLEMENT_APP 7 | #endif 8 | 9 | IMPLEMENT_APP(cApp); -------------------------------------------------------------------------------- /gui/settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FILE_CONFIG_MAGIC 0xE6219B27 4 | #define FILE_CONFIG_VERSION (uint16_t)SettingsVersion::Version13 5 | 6 | #include "../storage/storage.h" 7 | 8 | enum class SettingsVersion : uint16_t { 9 | // Initial Version 10 | Initial, 11 | 12 | // Removes GameDir and MountDrive 13 | // Adds CommandArgs 14 | SimplifyPathsAndCmdLine, 15 | 16 | // Adds ThreadCount, BufferCount, and UpdateInterval 17 | // Removes VerifyCache and EnableGaming 18 | Version13, 19 | 20 | Oodle, 21 | 22 | LatestPlusOne, 23 | Latest = LatestPlusOne - 1 24 | }; 25 | 26 | enum class SettingsCompressionMethod : uint8_t { 27 | Decompressed, 28 | Zstandard, 29 | LZ4, 30 | OodleSelkie 31 | }; 32 | 33 | enum class SettingsCompressionLevel : uint8_t { 34 | Fastest, 35 | Fast, 36 | Normal, 37 | Slow, 38 | Slowest 39 | }; 40 | 41 | enum class SettingsUpdateInterval : uint8_t { 42 | Second1, 43 | Second5, 44 | Second10, 45 | Second30, 46 | Minute1, 47 | Minute5, 48 | Minute10, 49 | Minute30, 50 | Hour1 51 | }; 52 | 53 | struct SETTINGS { 54 | char CacheDir[_MAX_PATH + 1]; 55 | SettingsCompressionMethod CompressionMethod; 56 | SettingsCompressionLevel CompressionLevel; 57 | SettingsUpdateInterval UpdateInterval; 58 | 59 | // Advanced 60 | uint16_t BufferCount; 61 | uint16_t ThreadCount; 62 | char CommandArgs[1024 + 1]; 63 | }; 64 | 65 | bool SettingsRead(SETTINGS* Settings, FILE* File); 66 | void SettingsWrite(SETTINGS* Settings, FILE* File); 67 | 68 | SETTINGS SettingsDefault(); 69 | bool SettingsValidate(SETTINGS* Settings); 70 | std::chrono::milliseconds SettingsGetUpdateInterval(SETTINGS* Settings); 71 | uint32_t SettingsGetStorageFlags(SETTINGS* Settings); -------------------------------------------------------------------------------- /gui/taskbar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cMain.h" 4 | 5 | #include 6 | #include 7 | 8 | class cMain; 9 | 10 | class SystrayIcon : public wxTaskBarIcon { 11 | public: 12 | SystrayIcon(cMain* main) : wxTaskBarIcon(), 13 | Main(main) 14 | { 15 | Bind(wxEVT_TASKBAR_LEFT_UP, [this](wxTaskBarIconEvent& evt) { 16 | Main->Show(); 17 | Main->Restore(); 18 | Main->Raise(); 19 | Main->SetFocus(); 20 | }); 21 | } 22 | 23 | protected: 24 | wxMenu* CreatePopupMenu() override { 25 | auto menu = new wxMenu(); 26 | 27 | menu->Bind(wxEVT_MENU, [this](wxCommandEvent& evt) { 28 | switch (evt.GetId()) 29 | { 30 | case SETTINGS_ID: 31 | Main->OnSettingsClicked(false); 32 | break; 33 | case VERIFY_ID: 34 | Main->OnVerifyClicked(); 35 | break; 36 | case PLAY_ID: 37 | Main->OnPlayClicked(); 38 | break; 39 | case EXIT_ID: 40 | if (Main->OnClose()) { 41 | Stats::StopUpdateThread(); 42 | Main->GameUpdater->StopUpdateThread(); 43 | Main->Destroy(); 44 | Main->App->Exit(); 45 | } 46 | break; 47 | } 48 | }); 49 | 50 | auto titleItem = menu->Append(TITLE_ID, "EGL2"); 51 | titleItem->Enable(false); 52 | titleItem->SetBitmap(wxIcon(L"APP_ICON", wxBITMAP_TYPE_ICO_RESOURCE, 16, 16)); 53 | 54 | menu->AppendSeparator(); 55 | 56 | menu->Append(SETTINGS_ID, LSTR(MAIN_BTN_SETTINGS)); 57 | menu->Append(VERIFY_ID, LSTR(MAIN_BTN_VERIFY)); 58 | menu->Append(PLAY_ID, Main->playBtn->GetLabel()); 59 | 60 | menu->AppendSeparator(); 61 | 62 | menu->Append(EXIT_ID, "Exit"); 63 | return menu; 64 | } 65 | 66 | private: 67 | cMain* Main; 68 | 69 | static constexpr int TITLE_ID = 42; 70 | static constexpr int SETTINGS_ID = 43; 71 | static constexpr int VERIFY_ID = 44; 72 | static constexpr int PLAY_ID = 45; 73 | static constexpr int EXIT_ID = 46; 74 | }; -------------------------------------------------------------------------------- /gui/versioninfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define VERSION_MAJOR 1 4 | #define VERSION_MINOR 4 5 | #define VERSION_PATCH 0 6 | 7 | #define STR_INDIR(x) #x 8 | #define STR(x) STR_INDIR(x) 9 | #define VERSION_STRING STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_PATCH) 10 | -------------------------------------------------------------------------------- /gui/wxHelpButton.cpp: -------------------------------------------------------------------------------- 1 | #include "wxHelpButton.h" 2 | 3 | #ifndef LOG_SECTION 4 | #define LOG_SECTION "HelpBtn" 5 | #endif 6 | 7 | #include "../containers/file_sha.h" 8 | #include "../Logger.h" 9 | 10 | #include 11 | 12 | // taken from https://github.com/wxWidgets/wxWidgets/blob/cc931612eec2e3ea49200ebff45042135f3c3f9c/src/common/cshelp.cpp#L282 13 | constexpr const char* csquery_xpm[] = { // this is cleaner than wxBITMAP(csquery) 14 | "12 11 2 1", 15 | " c None", 16 | ". c #000000", 17 | " ", 18 | " .... ", 19 | " .. .. ", 20 | " .. .. ", 21 | " .. ", 22 | " .. ", 23 | " .. ", 24 | " ", 25 | " .. ", 26 | " .. ", 27 | " " }; 28 | static const wxBitmap csquery_bmp(csquery_xpm); 29 | 30 | wxHelpButton::wxHelpButton() : wxBitmapButton() { } 31 | 32 | wxHelpButton::wxHelpButton(wxWindow* parent, wxWindowID id, const wchar_t* helpFile) : 33 | wxBitmapButton(parent, id, csquery_bmp, wxDefaultPosition, wxSize(24, 24)), 34 | Topic(helpFile) 35 | { 36 | Bind(wxEVT_BUTTON, &wxHelpButton::OnClick, this); 37 | Bind(wxEVT_SIZE, [this] (wxSizeEvent& evt) { // keeps it 1:1 aspect ratio 38 | auto size = std::min(evt.GetSize().GetWidth(), evt.GetSize().GetHeight()); 39 | evt.SetSize(wxSize(size, size)); 40 | SetSize(wxSize(size, size)); 41 | }); 42 | } 43 | 44 | constexpr char ChmSha[20] = { 0x72, 0xB9, 0x40, 0xC7, 0x2B, 0x9E, 0x59, 0xF5, 0x5A, 0x98, 0x90, 0x97, 0xC5, 0x1A, 0x18, 0x18, 0x3B, 0x44, 0x73, 0xDF }; 45 | 46 | bool VerifyChmFile(const fs::path& cacheFile) { 47 | std::error_code ec; 48 | if (fs::is_regular_file(cacheFile, ec)) { 49 | if (fs::file_size(cacheFile, ec) > 8 * 1024) { 50 | char fileSha[20]; 51 | if (SHAFile(cacheFile, fileSha)) { 52 | if (!memcmp(fileSha, ChmSha, 20)) { 53 | return true; 54 | } 55 | } 56 | } 57 | } 58 | LOG_INFO("Grabbing chm file from resources"); 59 | auto resInfo = FindResource(NULL, L"CHM_HELP", RT_RCDATA); 60 | if (!resInfo) { 61 | LOG_FATAL("Could not find locale resource!"); 62 | return false; 63 | } 64 | auto resData = LoadResource(NULL, resInfo); 65 | if (!resData) { 66 | LOG_FATAL("Could not load locale resource!"); 67 | return false; 68 | } 69 | auto resPtr = LockResource(resData); 70 | if (!resPtr) { 71 | LOG_FATAL("Could not lock locale resource!"); 72 | FreeResource(resData); 73 | return false; 74 | } 75 | auto resSize = SizeofResource(NULL, resInfo); 76 | if (!resSize) { 77 | LOG_FATAL("Could not get size of locale resource!"); 78 | FreeResource(resData); 79 | return false; 80 | } 81 | 82 | auto filePtr = fopen(cacheFile.string().c_str(), "wb"); 83 | if (!filePtr) { 84 | LOG_FATAL("Could not open %s for writing!", cacheFile.string().c_str()); 85 | return false; 86 | } 87 | fwrite(resPtr, resSize, 1, filePtr); 88 | fclose(filePtr); 89 | 90 | FreeResource(resData); 91 | return true; 92 | } 93 | 94 | bool wxHelpButton::LoadHelp(const fs::path& cachePath) 95 | { 96 | File = cachePath / "help.chm"; 97 | if (!VerifyChmFile(File)) { 98 | return false; 99 | } 100 | return true; 101 | } 102 | 103 | void wxHelpButton::OnClick(wxCommandEvent& evt) 104 | { 105 | auto helpHwnd = HtmlHelp(GetHWND(), File.c_str(), HH_DISPLAY_TOPIC, (DWORD_PTR)Topic); 106 | } 107 | -------------------------------------------------------------------------------- /gui/wxHelpButton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LSTR_LOC(str) L"" #str ".htm" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace fs = std::filesystem; 10 | 11 | class wxHelpButton : public wxBitmapButton { 12 | public: 13 | wxHelpButton(); 14 | 15 | wxHelpButton(wxWindow* parent, wxWindowID id, const wchar_t* topic); 16 | 17 | static bool LoadHelp(const fs::path& cachePath); 18 | 19 | private: 20 | static inline fs::path File; 21 | 22 | const wchar_t* Topic = nullptr; 23 | 24 | void OnClick(wxCommandEvent& evt); 25 | }; -------------------------------------------------------------------------------- /gui/wxLabelSlider.cpp: -------------------------------------------------------------------------------- 1 | #include "wxLabelSlider.h" 2 | 3 | wxLabelSlider::wxLabelSlider() : wxPanel() { } 4 | 5 | wxLabelSlider::wxLabelSlider(wxWindow* parent, wxWindowID id, int selectedIndex, const wxArrayString& values, long style, const wxString& name) : 6 | wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, 45)), 7 | values(values) 8 | { 9 | if (style & wxSL_SELRANGE) { 10 | style ^= wxSL_SELRANGE; 11 | } 12 | 13 | mLabel = style & wxSL_MIN_MAX_LABELS; 14 | if (style & wxSL_MIN_MAX_LABELS) { 15 | style ^= wxSL_MIN_MAX_LABELS; 16 | } 17 | 18 | vLabel = style ^ wxSL_VALUE_LABEL; 19 | if (style & wxSL_VALUE_LABEL) { 20 | style ^= wxSL_VALUE_LABEL; 21 | } 22 | 23 | slider = new wxSlider(this, id, selectedIndex, 0, values.size() - 1, wxDefaultPosition, wxSize(-1, 32), style | wxSL_HORIZONTAL, wxDefaultValidator, name); 24 | sliderText = new wxStaticText(this, wxID_ANY, values[0], wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); 25 | 26 | sliderSizer = new wxBoxSizer(wxVERTICAL); 27 | sliderSizer->Add(slider, 1, wxEXPAND); 28 | sliderSizer->Add(sliderText, 0, wxEXPAND); 29 | 30 | panelSizer = new wxBoxSizer(wxHORIZONTAL); 31 | 32 | int labelSize; 33 | if (mLabel) { 34 | labelSize = std::max(GetTextExtent(values[0]).x, GetTextExtent(values[values.size() - 1]).x); 35 | panelSizer->Add(new wxStaticText(this, wxID_ANY, values[0], wxDefaultPosition, wxSize(labelSize, -1), wxALIGN_CENTRE_HORIZONTAL), 0, wxALIGN_CENTRE); 36 | } 37 | panelSizer->Add(sliderSizer, 1, wxEXPAND); 38 | if (mLabel) { 39 | panelSizer->Add(new wxStaticText(this, wxID_ANY, values[values.size() - 1], wxDefaultPosition, wxSize(labelSize, -1), wxALIGN_CENTRE_HORIZONTAL), 0, wxALIGN_CENTRE); 40 | } 41 | 42 | slider->Bind(wxEVT_SCROLL_TOP, &wxLabelSlider::_OnTop, this); 43 | slider->Bind(wxEVT_SCROLL_BOTTOM, &wxLabelSlider::_OnBottom, this); 44 | slider->Bind(wxEVT_SCROLL_LINEUP, &wxLabelSlider::_OnLineUp, this); 45 | slider->Bind(wxEVT_SCROLL_LINEDOWN, &wxLabelSlider::_OnLineDown, this); 46 | slider->Bind(wxEVT_SCROLL_PAGEUP, &wxLabelSlider::_OnPageUp, this); 47 | slider->Bind(wxEVT_SCROLL_PAGEDOWN, &wxLabelSlider::_OnPageDown, this); 48 | slider->Bind(wxEVT_SCROLL_THUMBTRACK, &wxLabelSlider::_OnThumbTrack, this); 49 | slider->Bind(wxEVT_SCROLL_THUMBRELEASE, &wxLabelSlider::_OnThumbRelease, this); 50 | slider->Bind(wxEVT_SCROLL_CHANGED, &wxLabelSlider::_OnChanged, this); 51 | 52 | SetSizer(panelSizer); 53 | Refresh(); 54 | } 55 | 56 | void wxLabelSlider::_OnTop(wxScrollEvent& evt) 57 | { 58 | _CreateEvent(wxEVT_SCROLL_TOP); 59 | } 60 | 61 | void wxLabelSlider::_OnBottom(wxScrollEvent& evt) 62 | { 63 | _CreateEvent(wxEVT_SCROLL_BOTTOM); 64 | } 65 | 66 | void wxLabelSlider::_OnLineUp(wxScrollEvent& evt) 67 | { 68 | _CreateEvent(wxEVT_SCROLL_LINEUP); 69 | } 70 | 71 | void wxLabelSlider::_OnLineDown(wxScrollEvent& evt) 72 | { 73 | _CreateEvent(wxEVT_SCROLL_LINEDOWN); 74 | } 75 | 76 | void wxLabelSlider::_OnPageUp(wxScrollEvent& evt) 77 | { 78 | _CreateEvent(wxEVT_SCROLL_PAGEUP); 79 | } 80 | 81 | void wxLabelSlider::_OnPageDown(wxScrollEvent& evt) 82 | { 83 | _CreateEvent(wxEVT_SCROLL_PAGEDOWN); 84 | } 85 | 86 | void wxLabelSlider::_OnThumbTrack(wxScrollEvent& evt) 87 | { 88 | _CreateEvent(wxEVT_SCROLL_THUMBTRACK); 89 | } 90 | 91 | void wxLabelSlider::_OnThumbRelease(wxScrollEvent& evt) 92 | { 93 | _CreateEvent(wxEVT_SCROLL_THUMBRELEASE); 94 | } 95 | 96 | void wxLabelSlider::_OnChanged(wxScrollEvent& evt) 97 | { 98 | _CreateEvent(wxEVT_SCROLL_CHANGED); 99 | } 100 | 101 | template 102 | void wxLabelSlider::_CreateEvent(const EventTag& eventType) 103 | { 104 | sliderText->SetLabel(values[slider->GetValue()]); 105 | 106 | auto evt = wxScrollEvent(eventType, GetId()); 107 | evt.SetEventObject(this); 108 | GetEventHandler()->ProcessEvent(evt); 109 | slider->Refresh(); 110 | Refresh(); 111 | } 112 | -------------------------------------------------------------------------------- /gui/wxLabelSlider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class wxLabelSlider : public wxPanel { 7 | public: 8 | wxLabelSlider(); 9 | 10 | // only wxSL_AUTOTICKS, wxSL_MIN_MAX_LABELS, and wxSL_VALUE_LABEL flags are respected 11 | wxLabelSlider(wxWindow* parent, 12 | wxWindowID id, 13 | int selectedIndex, 14 | const wxArrayString& values, 15 | long style = 0, 16 | const wxString& name = wxSliderNameStr); 17 | 18 | int GetValue() { 19 | return slider->GetValue(); 20 | } 21 | 22 | void SetValue(int value) { 23 | slider->SetValue(value); 24 | _CreateEvent(wxEVT_SCROLL_CHANGED); 25 | } 26 | 27 | private: 28 | void _OnTop(wxScrollEvent& evt); 29 | void _OnBottom(wxScrollEvent& evt); 30 | void _OnLineUp(wxScrollEvent& evt); 31 | void _OnLineDown(wxScrollEvent& evt); 32 | void _OnPageUp(wxScrollEvent& evt); 33 | void _OnPageDown(wxScrollEvent& evt); 34 | void _OnThumbTrack(wxScrollEvent& evt); 35 | void _OnThumbRelease(wxScrollEvent& evt); 36 | void _OnChanged(wxScrollEvent& evt); 37 | 38 | template 39 | void _CreateEvent(const EventTag& eventType); 40 | 41 | wxArrayString values; 42 | bool mLabel, vLabel; 43 | wxSlider* slider; 44 | wxStaticText* sliderText; 45 | wxBoxSizer* sliderSizer; 46 | wxBoxSizer* panelSizer; 47 | }; -------------------------------------------------------------------------------- /gui/wxModalWindow.cpp: -------------------------------------------------------------------------------- 1 | // Taken from https://forums.wxwidgets.org/viewtopic.php?t=20752#p89334 2 | 3 | /********************************************************************************************** 4 | * 5 | * Filename : modalwindow.cpp 6 | * Purpose : Allow a modalwindow like wxDialog but allowing menus and such. 7 | * Author : John A. Mason 8 | * Created : 8/27/2008 07:54:12 AM 9 | * Copyright : Released under wxWidgets original license. 10 | * 11 | **********************************************************************************************/ 12 | 13 | #include "wxModalWindow.h" 14 | 15 | wxModalWindow::wxModalWindow() 16 | : wxFrame() { 17 | Init(); 18 | } 19 | 20 | wxModalWindow::wxModalWindow(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style, const wxString& name) 21 | : wxFrame(parent, id, title, pos, size, style, name) { 22 | Init(); 23 | } 24 | 25 | 26 | wxModalWindow::~wxModalWindow() { 27 | delete m_eventLoop; 28 | } 29 | 30 | 31 | void wxModalWindow::Init() 32 | { 33 | m_returnCode = 0; 34 | m_windowDisabler = NULL; 35 | m_eventLoop = NULL; 36 | m_isShowingModal = false; 37 | } 38 | 39 | bool wxModalWindow::Show(bool show) 40 | { 41 | if (!show) 42 | { 43 | // if we had disabled other app windows, reenable them back now because 44 | // if they stay disabled Windows will activate another window (one 45 | // which is enabled, anyhow) and we will lose activation 46 | if (m_windowDisabler) 47 | { 48 | delete m_windowDisabler; 49 | m_windowDisabler = NULL; 50 | } 51 | 52 | if (IsModal()) 53 | EndModal(wxID_CANCEL); 54 | } 55 | 56 | bool ret = wxFrame::Show(show); 57 | 58 | //I don't think we need this. Since it is a wxFrame that we are extending, 59 | // we don't need wxEVT_INIT_DIALOG firing off - that's what InitDialog does... 60 | // and this would only make sense if we have a wxDialog and validators 61 | // if ( show ) 62 | //InitDialog(); 63 | 64 | return ret; 65 | } 66 | 67 | bool wxModalWindow::IsModal() const { 68 | return m_isShowingModal; 69 | } 70 | 71 | int wxModalWindow::ShowModal() { 72 | if (IsModal()) 73 | { 74 | wxFAIL_MSG(wxT("wxModalWindow:ShowModal called twice")); 75 | return GetReturnCode(); 76 | } 77 | 78 | // use the apps top level window as parent if none given unless explicitly 79 | // forbidden 80 | if (!GetParent()) 81 | { 82 | wxWindow* parent = wxTheApp->GetTopWindow(); 83 | if (parent && parent != this) 84 | { 85 | m_parent = parent; 86 | } 87 | } 88 | 89 | Show(true); 90 | 91 | m_isShowingModal = true; 92 | 93 | wxASSERT_MSG(!m_windowDisabler, _T("disabling windows twice?")); 94 | 95 | #if defined(__WXGTK__) || defined(__WXMGL__) 96 | wxBusyCursorSuspender suspender; 97 | // FIXME (FIXME_MGL) - make sure busy cursor disappears under MSW too 98 | #endif 99 | 100 | m_windowDisabler = new wxWindowDisabler(this); 101 | if (!m_eventLoop) 102 | m_eventLoop = new wxEventLoop; 103 | 104 | m_eventLoop->Run(); 105 | 106 | return GetReturnCode(); 107 | } 108 | 109 | 110 | 111 | void wxModalWindow::EndModal(int retCode) { 112 | wxASSERT_MSG(m_eventLoop, _T("wxModalWindow is not modal")); 113 | 114 | SetReturnCode(retCode); 115 | 116 | if (!IsModal()) 117 | { 118 | wxFAIL_MSG(wxT("wxModalWindow:EndModal called twice")); 119 | return; 120 | } 121 | 122 | m_isShowingModal = false; 123 | 124 | m_eventLoop->Exit(); 125 | 126 | Show(false); 127 | } 128 | 129 | 130 | void wxModalWindow::SetReturnCode(int retCode) { 131 | m_returnCode = retCode; 132 | } 133 | 134 | 135 | int wxModalWindow::GetReturnCode() const { 136 | return m_returnCode; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /gui/wxModalWindow.h: -------------------------------------------------------------------------------- 1 | // Taken from https://forums.wxwidgets.org/viewtopic.php?t=20752#p89334 2 | 3 | /********************************************************************************************** 4 | * 5 | * Filename : modalwindow.h 6 | * Purpose : Allow a modalwindow like wxDialog but allowing menus and such. 7 | * Author : John A. Mason 8 | * Created : 8/27/2008 07:54:12 AM 9 | * Copyright : Released under wxWidgets original license. 10 | * 11 | **********************************************************************************************/ 12 | 13 | #ifndef __wx_ModalWindow_h__ 14 | #define __wx_ModalWindow_h__ 15 | 16 | #include 17 | 18 | #ifdef __BORLANDC__ 19 | #pragma hdrstop 20 | #endif 21 | 22 | #include 23 | 24 | #ifndef WX_PRECOMP 25 | #include 26 | #include 27 | #endif 28 | 29 | #include 30 | 31 | class wxModalWindow : public wxFrame { 32 | private: 33 | // while we are showing a modal window we disable the other windows using 34 | // this object 35 | wxWindowDisabler* m_windowDisabler; 36 | 37 | // modal window runs its own event loop 38 | wxEventLoop* m_eventLoop; 39 | 40 | // is modal right now? 41 | bool m_isShowingModal; 42 | 43 | //The return code of a modal window 44 | int m_returnCode; 45 | public: 46 | wxModalWindow(); 47 | wxModalWindow(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_FRAME_STYLE, const wxString& name = "modalwindow"); 48 | virtual ~wxModalWindow(); 49 | 50 | 51 | void Init(); 52 | bool Show(bool show); 53 | bool IsModal() const; 54 | int ShowModal(); 55 | 56 | void EndModal(int retCode); 57 | void SetReturnCode(int retCode); 58 | int GetReturnCode() const; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /help/MAIN_BTN_PLAY.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Play/Update 10 | 11 |

12 |
13 |

14 | Launch Fortnite with just one click! This button gets changed to show "Update" if a new update is released or you don't have all of Fortnite's files downloaded. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/MAIN_BTN_SETTINGS.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Settings 10 | 11 |

12 |
13 |

14 | Configure your EGL2 install and how it operates. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/MAIN_BTN_STORAGE.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Storage 10 | 11 |

12 |
13 |

14 | View the storage space used by EGL2 and how much space you're saving by using compression, among other things. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/MAIN_BTN_VERIFY.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Verify 10 | 11 |

12 |
13 |

14 | Verifies the integrity of your install by removing invalid data and redownloading it (or using your official Epic Games Launcher's version of Fortnite). 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_ADVANCED_BUFCT.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Buffer Count 10 | 11 |

12 |
13 |

14 | This is about how many megabytes of Fortnite's recently accessed data is stored in RAM. Having a sizeable buffer prevents Fortnite from using your storage drive again and possibly decompressing the same data. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_ADVANCED_CMDARGS.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Command Arguments 10 | 11 |

12 |
13 |

14 | These are the extra command arguments that are launched with Fortnite, nothing much here. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_ADVANCED_THDCT.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Thread Count 10 | 11 |

12 |
13 |

14 | This is approximately how many "threads" are run in parallel when updating, verifying, etc. If you are updating and this value is too high, this can seriously impact performance due to too many concurrent downloads. But if this value is too low, you may not be using your internet or computer resources to its fullest extent. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_GENERAL_COMPLEVEL.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Compression Level 10 | 11 |

12 |
13 |

14 | If you have set your compression method to not use compression, this setting will have no effect. This setting shouldn't have much of an effect when playing Fortnite. The general rule of thumb is: the slower your level, the more CPU it will take to update (which can cause updates to be slower), but your install size will be smaller. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_GENERAL_COMPMETHOD.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Compression Method 10 | 11 |

12 |
13 |

14 | If you don't care about reducing your install size for Fortnite, just set this value to "No Compression". If you would like to use compression, it's recommended to use Oodle's Selkie. With Selkie, you can compress your install down by over 50% with barely any comprimises in game performance or update times. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_GENERAL_INSTFOLDER.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Install Folder 10 | 11 |

12 |
13 |

14 | This installation folder is where EGL2 will store all its files for Fortnite. This is not where you set the folder of your previous (official Epic Games Launcher) installation. EGL2 uses a completely separate storage mechanism for Fortnite which is incompatible and way different from your normal way of saving files. Make sure EGL2 is able to read and write in this folder and that this folder is empty (or previously used by EGL2). 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/SETUP_GENERAL_UPDATEINT.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

9 | Update Interval 10 | 11 |

12 |
13 |

14 | This is how often EGL2 will check for a Fortnite update. This shouldn't really have a performance impact, but I have seen some people get rate limited when this value is set to 1 second. 15 |

16 | 17 | -------------------------------------------------------------------------------- /help/egl2.hhp: -------------------------------------------------------------------------------- 1 | [OPTIONS] 2 | Binary Index=No 3 | Compatibility=1.0 4 | Compiled file=help.chm 5 | Default Window=main 6 | Display compile notes=No 7 | Display compile progress=No 8 | Language=0x409 English (United States) 9 | Title=EGL2 10 | 11 | [WINDOWS] 12 | main="EGL2 Help",,,"map.htm",,,,,,0x2100,,0x0,,0x80400000,,,,,,0 13 | 14 | 15 | [INFOTYPES] 16 | 17 | -------------------------------------------------------------------------------- /help/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/help/icon.png -------------------------------------------------------------------------------- /help/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #F8F8F8; 3 | font-family: Arial, Helvetica, sans-serif; 4 | scrollbar-highlight-color: #F8F8F8; 5 | scrollbar-arrow-color: #F8F8F8; 6 | scrollbar-face-color: lightgray; 7 | -ms-overflow-style: -ms-autohiding-scrollbar; 8 | } 9 | h3, p { 10 | margin: 5px 0; 11 | } 12 | img { 13 | width: 1.25em; 14 | float: right; 15 | } -------------------------------------------------------------------------------- /help/map.htm: -------------------------------------------------------------------------------- 1 | . 2 | . 3 | . 4 | . 5 | . 6 | . 7 | . 8 | . 9 | . 10 | . 11 | . -------------------------------------------------------------------------------- /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/icon.ico -------------------------------------------------------------------------------- /icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | EGL2 23 | 25 | 26 | 28 | image/svg+xml 29 | 31 | EGL2 32 | 33 | 34 | 35 | 37 | 40 | 44 | 48 | 49 | 56 | 67 | 68 | 89 | 98 | 103 | 108 | 109 | -------------------------------------------------------------------------------- /libraries/curlion/connection_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace curlion { 9 | 10 | class Connection; 11 | class SocketFactory; 12 | class SocketWatcher; 13 | class Timer; 14 | 15 | /** 16 | ConnectionManager manages connections' executions, including starting or 17 | aborting connection. 18 | 19 | Call SartConnection method to start a connection. Call AbortConnection 20 | method to stop a running connection. 21 | 22 | This class is not thread safe. 23 | 24 | This is a encapsulation against libcurl's multi handle. 25 | */ 26 | class ConnectionManager { 27 | public: 28 | /** 29 | Construct the ConnectionManager instance. 30 | */ 31 | ConnectionManager(const std::shared_ptr& socket_factory, 32 | const std::shared_ptr& socket_watcher, 33 | const std::shared_ptr& timer); 34 | 35 | /** 36 | Destruct the ConnectionManager instance. 37 | 38 | When this method got called, all running connections would be aborted. 39 | */ 40 | ~ConnectionManager(); 41 | 42 | /** 43 | Start a connection. 44 | 45 | @param connection 46 | The connection to start. Must not be nullptr. 47 | 48 | @return 49 | Return an error on failure. 50 | 51 | This method will retain the connection, until it is finished or aborted. 52 | 53 | It is OK to call this method with the same Connection instance multiple times. 54 | Nothing changed if the connection is running; Otherwise it will be restarted. 55 | */ 56 | std::error_condition StartConnection(const std::shared_ptr& connection); 57 | 58 | /** 59 | Abort a running connection. 60 | 61 | @param connection 62 | The connection to abort. Must not be nullptr. 63 | 64 | @return 65 | Return an error on failure. 66 | 67 | Note that the connection's finished callback would not be triggered when it is 68 | aborted. 69 | 70 | Is is OK to call this methods while the connection is not running, nothing 71 | would happend. 72 | 73 | If this method fails, the connection is in an unknown condition, it should be 74 | abondaned and never be reused again. 75 | */ 76 | std::error_condition AbortConnection(const std::shared_ptr& connection); 77 | 78 | /** 79 | Get the underlying multi handle. 80 | */ 81 | CURLM* GetHandle() const { 82 | return multi_handle_; 83 | } 84 | 85 | private: 86 | static curl_socket_t CurlOpenSocketCallback(void* clientp, curlsocktype socket_type, curl_sockaddr* address); 87 | static int CurlCloseSocketCallback(void* clientp, curl_socket_t socket); 88 | static int CurlTimerCallback(CURLM* multi_handle, long timeout_ms, void* user_pointer); 89 | static int CurlSocketCallback(CURL* easy_handle, 90 | curl_socket_t socket, 91 | int action, 92 | void* user_pointer, 93 | void* socket_pointer); 94 | 95 | curl_socket_t OpenSocket(curlsocktype socket_type, curl_sockaddr* address); 96 | bool CloseSocket(curl_socket_t socket); 97 | 98 | void SetTimer(long timeout_ms); 99 | void TimerTriggered(); 100 | 101 | void WatchSocket(curl_socket_t socket, int action, void* socket_pointer); 102 | void SocketEventTriggered(curl_socket_t socket, bool can_write); 103 | 104 | void CheckFinishedConnections(); 105 | 106 | private: 107 | ConnectionManager(const ConnectionManager&) = delete; 108 | ConnectionManager& operator=(const ConnectionManager&) = delete; 109 | 110 | private: 111 | std::shared_ptr socket_factory_; 112 | std::shared_ptr socket_watcher_; 113 | std::shared_ptr timer_; 114 | 115 | CURLM* multi_handle_; 116 | std::map> running_connections_; 117 | }; 118 | 119 | } 120 | -------------------------------------------------------------------------------- /libraries/curlion/curlion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "connection.h" 4 | #include "connection_manager.h" 5 | #include "error.h" 6 | #include "http_connection.h" 7 | #include "http_form.h" 8 | #include "socket_factory.h" 9 | #include "socket_watcher.h" 10 | #include "timer.h" 11 | -------------------------------------------------------------------------------- /libraries/curlion/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace curlion { 7 | 8 | inline const std::error_category& CurlMultiErrorCategory() { 9 | 10 | class CurlMultiErrorCategory : public std::error_category { 11 | public: 12 | const char* name() const noexcept override { 13 | return "CURLMcode"; 14 | } 15 | 16 | std::string message(int condition) const override { 17 | return std::string(); 18 | } 19 | }; 20 | 21 | static CurlMultiErrorCategory category; 22 | return category; 23 | } 24 | 25 | 26 | inline const std::error_category& CurlFormErrorCategory() { 27 | 28 | class CurlFormErrorCategory : public std::error_category { 29 | public: 30 | const char* name() const noexcept override { 31 | return "CURLFORMcode"; 32 | } 33 | 34 | std::string message(int condition) const override { 35 | return std::string(); 36 | } 37 | }; 38 | 39 | static CurlFormErrorCategory category; 40 | return category; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /libraries/curlion/http_connection.cpp: -------------------------------------------------------------------------------- 1 | #include "http_connection.h" 2 | #include 3 | #include "http_form.h" 4 | 5 | namespace curlion { 6 | 7 | static std::string MakeHttpHeaderLine(const std::string& field, const std::string& value); 8 | static std::vector SplitString(const std::string& string, const std::string& delimiter); 9 | 10 | 11 | HttpConnection::HttpConnection() : 12 | request_headers_(nullptr), 13 | has_parsed_response_headers_(false) { 14 | 15 | } 16 | 17 | 18 | HttpConnection::~HttpConnection() { 19 | ReleaseRequestHeaders(); 20 | } 21 | 22 | 23 | void HttpConnection::SetUsePost(bool use_post) { 24 | curl_easy_setopt(GetHandle(), CURLOPT_POST, use_post); 25 | } 26 | 27 | 28 | void HttpConnection::SetRequestHeaders(const std::multimap& headers) { 29 | 30 | ReleaseRequestHeaders(); 31 | 32 | for (auto& each_header : headers) { 33 | 34 | std::string each_header_line = MakeHttpHeaderLine(each_header.first, each_header.second); 35 | request_headers_ = curl_slist_append(request_headers_, each_header_line.c_str()); 36 | } 37 | 38 | curl_easy_setopt(GetHandle(), CURLOPT_HTTPHEADER, request_headers_); 39 | } 40 | 41 | 42 | void HttpConnection::AddRequestHeader(const std::string& field, const std::string& value) { 43 | 44 | std::string header_line = MakeHttpHeaderLine(field, value); 45 | request_headers_ = curl_slist_append(request_headers_, header_line.c_str()); 46 | 47 | curl_easy_setopt(GetHandle(), CURLOPT_HTTPHEADER, request_headers_); 48 | } 49 | 50 | 51 | void HttpConnection::SetRequestForm(const std::shared_ptr& form) { 52 | 53 | form_ = form; 54 | 55 | curl_httppost* handle = nullptr; 56 | if (form_ != nullptr) { 57 | handle = form_->GetHandle(); 58 | } 59 | 60 | curl_easy_setopt(GetHandle(), CURLOPT_HTTPPOST, handle); 61 | } 62 | 63 | 64 | void HttpConnection::SetAutoRedirect(bool auto_redirect) { 65 | curl_easy_setopt(GetHandle(), CURLOPT_FOLLOWLOCATION, auto_redirect); 66 | } 67 | 68 | 69 | void HttpConnection::SetMaxAutoRedirectCount(long count) { 70 | curl_easy_setopt(GetHandle(), CURLOPT_MAXREDIRS, count); 71 | } 72 | 73 | 74 | const std::multimap& HttpConnection::GetResponseHeaders() const { 75 | 76 | if (! has_parsed_response_headers_) { 77 | ParseResponseHeaders(); 78 | has_parsed_response_headers_ = true; 79 | } 80 | 81 | return response_headers_; 82 | } 83 | 84 | 85 | void HttpConnection::ParseResponseHeaders() const { 86 | 87 | std::vector lines = SplitString(GetResponseHeader(), "\r\n"); 88 | 89 | std::vector key_value_pair; 90 | for (auto& each_line : lines) { 91 | 92 | key_value_pair = SplitString(each_line, ": "); 93 | if (key_value_pair.size() < 2) { 94 | continue; 95 | } 96 | 97 | response_headers_.insert(std::make_pair(key_value_pair.at(0), key_value_pair.at(1))); 98 | } 99 | } 100 | 101 | 102 | void HttpConnection::ResetResponseStates() { 103 | 104 | Connection::ResetResponseStates(); 105 | 106 | has_parsed_response_headers_ = false; 107 | response_headers_.clear(); 108 | } 109 | 110 | 111 | void HttpConnection::ResetOptionResources() { 112 | 113 | Connection::ResetOptionResources(); 114 | 115 | ReleaseRequestHeaders(); 116 | form_.reset(); 117 | } 118 | 119 | 120 | void HttpConnection::ReleaseRequestHeaders() { 121 | 122 | if (request_headers_ != nullptr) { 123 | curl_slist_free_all(request_headers_); 124 | request_headers_ = nullptr; 125 | } 126 | } 127 | 128 | 129 | static std::string MakeHttpHeaderLine(const std::string& field, const std::string& value) { 130 | 131 | std::string header_line = field; 132 | header_line.append(": "); 133 | header_line.append(value); 134 | 135 | return header_line; 136 | } 137 | 138 | 139 | static std::vector SplitString(const std::string& string, const std::string& delimiter) { 140 | 141 | std::vector splitted_strings; 142 | 143 | std::size_t begin_index = 0; 144 | std::size_t end_index = 0; 145 | 146 | while (begin_index < string.length()) { 147 | 148 | end_index = string.find(delimiter, begin_index); 149 | 150 | if (end_index == std::string::npos) { 151 | end_index = string.length(); 152 | } 153 | 154 | splitted_strings.push_back(string.substr(begin_index, end_index - begin_index)); 155 | 156 | begin_index = end_index + delimiter.length(); 157 | } 158 | 159 | return splitted_strings; 160 | } 161 | 162 | } -------------------------------------------------------------------------------- /libraries/curlion/http_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "connection.h" 6 | 7 | namespace curlion { 8 | 9 | class HttpForm; 10 | 11 | /** 12 | HttpConnection used to send HTTP request and received HTTP response. 13 | 14 | This class dervies from Connection, adds some setter and getter methods speicfic to HTTP. 15 | */ 16 | class HttpConnection : public Connection { 17 | public: 18 | /** 19 | Construct the HttpConnection instance. 20 | */ 21 | HttpConnection(); 22 | 23 | /** 24 | Destruct the HttpConnection instance. 25 | */ 26 | ~HttpConnection(); 27 | 28 | /** 29 | Set whether to use HTTP POST method. 30 | 31 | The default is false, means using GET method. 32 | */ 33 | void SetUsePost(bool use_post); 34 | 35 | /** 36 | Set HTTP request headers. 37 | 38 | The new headers would replace all headers previously set. 39 | */ 40 | void SetRequestHeaders(const std::multimap& headers); 41 | 42 | /** 43 | Add a single HTTP request header. 44 | */ 45 | void AddRequestHeader(const std::string& field, const std::string& value); 46 | 47 | /** 48 | Set a request form for HTTP POST. 49 | 50 | The default is nullptr. 51 | */ 52 | void SetRequestForm(const std::shared_ptr& form); 53 | 54 | /** 55 | Set whether to auto-redirect when received HTTP 3xx response. 56 | 57 | Use SetMaxAutoRedirectCount to limit the redirction count. 58 | 59 | The default is false. 60 | */ 61 | void SetAutoRedirect(bool auto_redirect); 62 | 63 | /** 64 | Set maximum number of auto-redirects allowed. 65 | 66 | Set to 0 to forbidden any redirect. 67 | 68 | The default is -1, meas no redirect limits. 69 | */ 70 | void SetMaxAutoRedirectCount(long count); 71 | 72 | /** 73 | Get HTTP response headers. 74 | 75 | This is a wrapper method for GetResponseHeader, parses the raw header string to key value pairs. 76 | Note that when auto-redirection is enabled, all headers in multiple responses would be contained. 77 | */ 78 | const std::multimap& GetResponseHeaders() const; 79 | 80 | protected: 81 | void ResetResponseStates() override; 82 | void ResetOptionResources() override; 83 | 84 | private: 85 | void ParseResponseHeaders() const; 86 | void ReleaseRequestHeaders(); 87 | 88 | private: 89 | curl_slist* request_headers_; 90 | std::shared_ptr form_; 91 | mutable bool has_parsed_response_headers_; 92 | mutable std::multimap response_headers_; 93 | }; 94 | 95 | } -------------------------------------------------------------------------------- /libraries/curlion/http_form.cpp: -------------------------------------------------------------------------------- 1 | #include "http_form.h" 2 | 3 | namespace curlion { 4 | 5 | static std::vector CreateOptions(const std::shared_ptr& part); 6 | 7 | HttpForm::~HttpForm() { 8 | 9 | curl_formfree(handle_); 10 | } 11 | 12 | 13 | std::error_condition HttpForm::AddPart(const std::shared_ptr& part) { 14 | 15 | std::error_condition error; 16 | 17 | auto options = CreateOptions(part); 18 | auto result = curl_formadd(&handle_, &handle_last_, CURLFORM_ARRAY, options.data(), CURLFORM_END); 19 | 20 | if (result == CURL_FORMADD_OK) { 21 | parts_.push_back(part); 22 | } 23 | else { 24 | error.assign(result, CurlFormErrorCategory()); 25 | } 26 | 27 | return error; 28 | } 29 | 30 | std::error_condition HttpForm::AddPart(const Part&& part) { 31 | return AddPart(std::make_shared(part)); 32 | } 33 | 34 | std::error_condition HttpForm::AddPart(const std::string& name, const std::string& content) { 35 | return AddPart(Part(name, content)); 36 | } 37 | 38 | 39 | static std::vector CreateOptions(const std::shared_ptr& part) { 40 | 41 | std::vector options; 42 | 43 | curl_forms name_option; 44 | name_option.option = CURLFORM_PTRNAME; 45 | name_option.value = part->name.c_str(); 46 | options.push_back(name_option); 47 | 48 | const auto& content = part->content; 49 | if ((! content.empty()) || (part->files.empty())) { 50 | 51 | curl_forms content_option; 52 | content_option.option = CURLFORM_PTRCONTENTS; 53 | content_option.value = content.c_str(); 54 | options.push_back(content_option); 55 | 56 | curl_forms content_length_option; 57 | content_length_option.option = CURLFORM_CONTENTSLENGTH; 58 | content_length_option.value = reinterpret_cast(content.length()); 59 | options.push_back(content_length_option); 60 | } 61 | else { 62 | 63 | for (const auto& each_file : part->files) { 64 | 65 | curl_forms path_option; 66 | path_option.option = CURLFORM_FILE; 67 | path_option.value = each_file->path.c_str(); 68 | options.push_back(path_option); 69 | 70 | if (! each_file->name.empty()) { 71 | curl_forms name_option; 72 | name_option.option = CURLFORM_FILENAME; 73 | name_option.value = each_file->name.c_str(); 74 | options.push_back((name_option)); 75 | } 76 | 77 | if (! each_file->content_type.empty()) { 78 | curl_forms content_type_option; 79 | content_type_option.option = CURLFORM_CONTENTTYPE; 80 | content_type_option.value = each_file->content_type.c_str(); 81 | options.push_back(content_type_option); 82 | } 83 | } 84 | } 85 | 86 | curl_forms end_option; 87 | end_option.option = CURLFORM_END; 88 | end_option.value = nullptr; 89 | options.push_back(end_option); 90 | 91 | return options; 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /libraries/curlion/http_form.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "error.h" 8 | 9 | namespace curlion { 10 | 11 | /** 12 | HttpForm used to build a multi-part form request content. 13 | 14 | The instance of this class is used in HttpConnection::SetRequestForm method. 15 | */ 16 | class HttpForm { 17 | public: 18 | /** 19 | File represents information of a file in a part. 20 | */ 21 | class File { 22 | public: 23 | /** 24 | Construct a empty file. 25 | */ 26 | File() { } 27 | 28 | /** 29 | Construct a file with specified path. 30 | 31 | @param path 32 | Path of the file. 33 | */ 34 | File(const std::string& path) : path(path) { } 35 | 36 | /** 37 | Path of the file. 38 | */ 39 | std::string path; 40 | 41 | /** 42 | Name of the file. 43 | 44 | If this field is empty, the name in path would be used. 45 | */ 46 | std::string name; 47 | 48 | /** 49 | Content type of the file. 50 | 51 | If this field is empty, the content type is determinated by the content of file. 52 | */ 53 | std::string content_type; 54 | }; 55 | 56 | /** 57 | Part represents information of a single part in a multi-part form. 58 | */ 59 | class Part { 60 | public: 61 | /** 62 | Construct an empty part. 63 | */ 64 | Part() { } 65 | 66 | /** 67 | Construct a part with specified name and content. 68 | 69 | @param name 70 | Name of the part. 71 | 72 | @param content 73 | Content of the part. 74 | */ 75 | Part(const std::string& name, const std::string& content) : name(name), content(content) { } 76 | 77 | /** 78 | Name of the part. 79 | */ 80 | std::string name; 81 | 82 | /** 83 | Content of the part. 84 | */ 85 | std::string content; 86 | 87 | /** 88 | Files in the part. 89 | 90 | Note that files and content can not coexist in a part, and content has a higher priority 91 | than files. That is, if content is not empty, all files are ignored. 92 | */ 93 | std::vector> files; 94 | }; 95 | 96 | public: 97 | /** 98 | Destruct the HttpForm instance. 99 | */ 100 | ~HttpForm(); 101 | 102 | /** 103 | Add a part to the form. 104 | 105 | @param part 106 | The part is added, must not be nullptr. 107 | 108 | @return 109 | Return an error on failure. 110 | */ 111 | std::error_condition AddPart(const std::shared_ptr& part); 112 | std::error_condition AddPart(const Part&& part); 113 | std::error_condition AddPart(const std::string& name, const std::string& content); 114 | 115 | /** 116 | Get the handle of the form. 117 | 118 | @return 119 | The curl_httppost* handle of the form. 120 | */ 121 | curl_httppost* GetHandle() const { 122 | return handle_; 123 | } 124 | 125 | private: 126 | curl_httppost* handle_ = nullptr; 127 | curl_httppost* handle_last_ = nullptr; 128 | 129 | std::vector> parts_; 130 | }; 131 | 132 | } -------------------------------------------------------------------------------- /libraries/curlion/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | CURLION_VERBOSE macro controls whether to print debug information to stdout. 5 | Enable it by changing its value to none zero. 6 | 7 | Be aware of that enabling this macro would produce much output that flood the console easily. 8 | So it should be used for debug purpose only. 9 | 10 | This macro effects only when NDEBUG macro is not defined. 11 | */ 12 | #define CURLION_VERBOSE 0 13 | 14 | #if (! defined(NDEBUG)) && (CURLION_VERBOSE) 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | namespace curlion { 21 | 22 | #if (! defined(NDEBUG)) && (CURLION_VERBOSE) 23 | 24 | class Logger { 25 | public: 26 | void Write(const std::string& log) { 27 | std::cout << "curlion> " << log << std::endl; 28 | } 29 | }; 30 | 31 | 32 | class LoggerProxy { 33 | public: 34 | LoggerProxy(const std::shared_ptr& logger) : logger_(logger), stream_(new std::ostringstream()) { 35 | 36 | } 37 | 38 | LoggerProxy(LoggerProxy&& other) : logger_(std::move(other.logger_)), stream_(std::move(other.stream_)) { 39 | 40 | } 41 | 42 | ~LoggerProxy() { 43 | 44 | if ( (logger_ != nullptr) && (stream_ != nullptr) ) { 45 | logger_->Write(stream_->str()); 46 | } 47 | } 48 | 49 | template 50 | LoggerProxy operator<<(const Type& value) { 51 | *stream_ << value; 52 | return std::move(*this); 53 | } 54 | 55 | private: 56 | std::shared_ptr logger_; 57 | std::unique_ptr stream_; 58 | }; 59 | 60 | 61 | inline LoggerProxy Log() { 62 | return LoggerProxy(std::make_shared()); 63 | } 64 | 65 | #else 66 | 67 | class LoggerProxy { 68 | public: 69 | template 70 | LoggerProxy operator<<(const Type& value) { 71 | return *this; 72 | } 73 | }; 74 | 75 | inline LoggerProxy Log() { 76 | return LoggerProxy(); 77 | } 78 | 79 | #endif 80 | 81 | } -------------------------------------------------------------------------------- /libraries/curlion/socket_factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace curlion { 7 | 8 | /** 9 | SocketFactory is an interface used by Connection to open and close sockets. 10 | */ 11 | class SocketFactory { 12 | public: 13 | SocketFactory() { } 14 | virtual ~SocketFactory() { } 15 | 16 | /** 17 | Open a socket for specific type and address. 18 | 19 | Return a valid socket handle if succeeded; return CURL_SOCKET_BAD otherwise. 20 | */ 21 | virtual curl_socket_t Open(curlsocktype socket_type, const curl_sockaddr* address) = 0; 22 | 23 | /** 24 | Close a socket. 25 | 26 | Return a bool indicates that whether successful. 27 | */ 28 | virtual bool Close(curl_socket_t socket) = 0; 29 | 30 | private: 31 | SocketFactory(const SocketFactory&) = delete; 32 | SocketFactory& operator=(const SocketFactory&) = delete; 33 | }; 34 | 35 | } -------------------------------------------------------------------------------- /libraries/curlion/socket_watcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace curlion { 7 | 8 | /** 9 | SocketWatcher is an interface used by ConnectionManager to trigger a notification when a socket 10 | is ready to read or write data. 11 | */ 12 | class SocketWatcher { 13 | public: 14 | /** 15 | Socket's event type to be watched. 16 | */ 17 | enum class Event { 18 | 19 | /** 20 | Read event. 21 | */ 22 | Read, 23 | 24 | /** 25 | Write event. 26 | */ 27 | Write, 28 | 29 | /** 30 | Both read and write event. 31 | */ 32 | ReadWrite, 33 | }; 34 | 35 | /** 36 | Callback prototype for the socket event. 37 | 38 | @param socket 39 | Socket handle triggers the event. 40 | 41 | @param can_write 42 | Indicates the event type. True for write, false for read. 43 | */ 44 | typedef std::function EventCallback; 45 | 46 | public: 47 | SocketWatcher() { } 48 | virtual ~SocketWatcher() { } 49 | 50 | /** 51 | Start watching a socket for specific event. 52 | 53 | The watching should be continually, until a corresponding StopWatching is called. callback should 54 | be called when the event on socket is triggered every time. 55 | 56 | Is is guaranteed that Watch would not be call for the second time unless StopWatching is called. 57 | 58 | The implementation must ensure that the thread calling callback is the same one calling Watch. 59 | */ 60 | virtual void Watch(curl_socket_t socket, Event event, const EventCallback& callback) = 0; 61 | 62 | /** 63 | Stop watching a socket's event. 64 | 65 | Once StopWatching is called, no callback should be called any more. 66 | Note that the socket may be closed before passed to StopWatching. Check for this case if it matters. 67 | */ 68 | virtual void StopWatching(curl_socket_t socket) = 0; 69 | 70 | private: 71 | SocketWatcher(const SocketWatcher&) = delete; 72 | SocketWatcher& operator=(const SocketWatcher&) = delete; 73 | }; 74 | 75 | } -------------------------------------------------------------------------------- /libraries/curlion/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace curlion { 6 | 7 | /** 8 | Timer is an interface used by ConnectionManager to trigger a notification after a period of time. 9 | */ 10 | class Timer { 11 | public: 12 | Timer() { } 13 | virtual ~Timer() { } 14 | 15 | /** 16 | Start the timer. 17 | 18 | callback should be called after timeout_ms milliseconds. In case of timeout_ms is 0, 19 | call callback as soon as possible. 20 | 21 | Is is guaranteed that Start would not be call for the second time unless Stop is called. 22 | 23 | The implementation must ensure that the thread calling callback is the same one calling Start. 24 | */ 25 | virtual void Start(long timeout_ms, const std::function& callback) = 0; 26 | 27 | /** 28 | Stop the timer. 29 | 30 | Once Stop is called, no callback should be called any more. 31 | */ 32 | virtual void Stop() = 0; 33 | 34 | private: 35 | Timer(const Timer&) = delete; 36 | Timer& operator=(const Timer&) = delete; 37 | }; 38 | 39 | } -------------------------------------------------------------------------------- /libraries/libdeflate/libdeflatestatic.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/libraries/libdeflate/libdeflatestatic.lib -------------------------------------------------------------------------------- /libraries/oodle/oo2core_8_win64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/libraries/oodle/oo2core_8_win64.lib -------------------------------------------------------------------------------- /libraries/oodle/oodle2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // All this stuff was grabbed from scouring the web 4 | // I don't even know where I'd find a copy of the SDK publicly 5 | 6 | // The .lib file was from: https://stackoverflow.com/a/16127548 7 | 8 | #include 9 | 10 | extern "C" 11 | { 12 | 13 | typedef uint32_t U32; 14 | typedef uint64_t U64; 15 | typedef int32_t S32; 16 | typedef S32 SINTa; // i think that's what this is? 17 | 18 | enum OodleLZ_Compressor { 19 | OodleLZ_Compressor_Invalid = -1, 20 | 21 | OodleLZ_Compressor_LZH, 22 | OodleLZ_Compressor_LZHLW, 23 | OodleLZ_Compressor_LZNIB, 24 | OodleLZ_Compressor_None, 25 | OodleLZ_Compressor_LZB16, 26 | OodleLZ_Compressor_LZBLW, 27 | OodleLZ_Compressor_LZA, 28 | OodleLZ_Compressor_LZNA, 29 | OodleLZ_Compressor_Kraken, 30 | OodleLZ_Compressor_Mermaid, 31 | OodleLZ_Compressor_BitKnit, 32 | OodleLZ_Compressor_Selkie, 33 | OodleLZ_Compressor_Hydra, 34 | OodleLZ_Compressor_Leviathan, 35 | }; 36 | 37 | enum OodleLZ_CompressionLevel { 38 | OodleLZ_CompressionLevel_HyperFast4 = -4, 39 | OodleLZ_CompressionLevel_HyperFast3, 40 | OodleLZ_CompressionLevel_HyperFast2, 41 | OodleLZ_CompressionLevel_HyperFast1, 42 | OodleLZ_CompressionLevel_None, 43 | OodleLZ_CompressionLevel_SuperFast, 44 | OodleLZ_CompressionLevel_VeryFast, 45 | OodleLZ_CompressionLevel_Fast, 46 | OodleLZ_CompressionLevel_Normal, 47 | OodleLZ_CompressionLevel_Optimal1, 48 | OodleLZ_CompressionLevel_Optimal2, 49 | OodleLZ_CompressionLevel_Optimal3, 50 | OodleLZ_CompressionLevel_Optimal4, 51 | OodleLZ_CompressionLevel_Optimal5, 52 | // OodleLZ_CompressionLevel_TooHigh, 53 | 54 | OodleLZ_CompressionLevel_Min = OodleLZ_CompressionLevel_HyperFast4, 55 | OodleLZ_CompressionLevel_Max = OodleLZ_CompressionLevel_Optimal5 56 | }; 57 | 58 | enum OodleLZ_FuzzSafe { 59 | OodleLZ_FuzzSafe_No, 60 | OodleLZ_FuzzSafe_Yes 61 | }; 62 | 63 | enum OodleLZ_CheckCRC { 64 | OodleLZ_CheckCRC_No, 65 | OodleLZ_CheckCRC_Yes 66 | }; 67 | 68 | enum OodleLZ_Verbosity { 69 | OodleLZ_Verbosity_None, 70 | OodleLZ_Verbosity_Max = 3 71 | }; 72 | 73 | enum OodleLZ_Decode_ThreadPhase { 74 | OodleLZ_Decode_ThreadPhase1 = 0x1, 75 | OodleLZ_Decode_ThreadPhase2 = 0x2, 76 | 77 | OodleLZ_Decode_Unthreaded = OodleLZ_Decode_ThreadPhase1 | OodleLZ_Decode_ThreadPhase2, 78 | }; 79 | 80 | 81 | #define OodleSDKVersion 0x2E080630 82 | #define OodleVersion "2.8.6" // i guess?? 83 | #define RADCOPYRIGHT "Oodle " OodleVersion " Copyright (C) 1994-2020, RAD Game Tools, Inc." 84 | 85 | #define OODLELZ_BLOCK_LEN 0x40000 86 | #define OODLELZ_LOCALDICTIONARYSIZE_MAX 1<<30 87 | #define OODLELZ_FAILED 0 // i think so? 88 | #define OODLEAPI __stdcall 89 | 90 | // opts[5]: dictionarySize limit 91 | // opts[6]: spaceSpeedTradeoffBytes 92 | // opts[12]: jobify 93 | struct OodleLZ_CompressOptions { 94 | int minMatchLen; 95 | bool makeLongRangeMatcher; 96 | int spaceSpeedTradeoffBytes; // [6] 97 | int seekChunkLen; 98 | bool seekChunkReset; 99 | int dictionarySize; // [5] 100 | int maxLocalDictionarySize; 101 | int matchTableSizeLog2; 102 | bool sendQuantumCRCs; 103 | }; 104 | 105 | struct OodleConfigValues { 106 | int m_OodleLZ_BackwardsCompatible_MajorVersion; 107 | }; 108 | 109 | bool OODLEAPI Oodle_CheckVersion(U32 sdk_version, U32* dll_version); 110 | 111 | void OODLEAPI Oodle_SetUsageWarnings(bool ignore); 112 | 113 | void OODLEAPI Oodle_LogHeader(); 114 | 115 | void OODLEAPI OodleCore_Plugin_Printf_Default(bool debug, const char* filename, uint32_t line_num, const char* format, ...); 116 | 117 | decltype(OodleCore_Plugin_Printf_Default)* OODLEAPI OodleCore_Plugins_SetPrintf(decltype(OodleCore_Plugin_Printf_Default) new_printf); 118 | 119 | void OODLEAPI Oodle_GetConfigValues(OodleConfigValues* config); 120 | 121 | void OODLEAPI Oodle_SetConfigValues(OodleConfigValues* config); 122 | 123 | const OodleLZ_CompressOptions* OODLEAPI OodleLZ_CompressOptions_GetDefault(OodleLZ_Compressor compressor, OodleLZ_CompressionLevel level); 124 | 125 | void OODLEAPI OodleLZ_CompressOptions_Validate(OodleLZ_CompressOptions* options); 126 | 127 | const char* OODLEAPI OodleLZ_Compressor_GetName(OodleLZ_Compressor compressor); 128 | 129 | const char* OODLEAPI OodleLZ_CompressionLevel_GetName(OodleLZ_CompressionLevel level); 130 | 131 | SINTa OODLEAPI OodleLZ_GetCompressedBufferSizeNeeded(OodleLZ_Compressor compressor, SINTa size); 132 | 133 | SINTa OODLEAPI OodleLZDecoder_MemorySizeNeeded(OodleLZ_Compressor compressor, SINTa size); 134 | 135 | SINTa OODLEAPI OodleLZ_Compress(OodleLZ_Compressor compressor, const char* src, SINTa src_size, char* dst, OodleLZ_CompressionLevel level, OodleLZ_CompressOptions* opts, const char* context, void* unused, void* scratch, SINTa scratch_size); 136 | 137 | SINTa OODLEAPI OodleLZ_Decompress(const char* src, SINTa src_size, char* dst, SINTa dst_size, OodleLZ_FuzzSafe fuzz, OodleLZ_CheckCRC crc, OodleLZ_Verbosity verbosity, char* context, U64 e, void* callback, void* callback_ctx, void* scratch, SINTa scratch_size); 138 | 139 | } -------------------------------------------------------------------------------- /locales/AR.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "خطأ", 3 | "APP_ERROR_RUNNING": "EGL2 قيد التشغيل بالفعل!", 4 | "APP_ERROR_APPDATA": "تعذر الحصول على مكان مجلد AppData", 5 | "APP_ERROR_DATA": "تعذر إنشاء مجلد بيانات الخاص بـ EGL2", 6 | "APP_ERROR_LOGS": "تعذر إنشاء مجلد سجلات الخاص بـ EGL2", 7 | "APP_ERROR_MANIFESTS": "تعذر إنشاء مجلد قوائم الخاص بـ EGL2", 8 | "APP_ERROR_LOGFILE": "لا يمكن إنشاء ملف سجل! بدونه ، لا يمكنني مساعدتك في أي من المشاكل.", 9 | "APP_ERROR_PROGFILES86": "تعذر الحصول على مجلد \"(Program Files (x86\" . بصراحة ليس لدي فكرة كيف ستحصل على هذا الخطأ.", 10 | "APP_ERROR_WINFSP_FIND": "تعذر العثور على DLL الخاص ببرنامج WinFsp. حاول إعادة تثبيت برنامج WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "تعذر تحميل DLL الخاص ببرنامج WinFsp. حاول إعادة تثبيت برنامج WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "حدث خطأ غير معروف عند محاولة تحميل DLL الخاص ببرنامج WinFsp: %d", 13 | "MAIN_DESC_DEFAULT": "مرر فوق أي زر لمعرفة ما يفعله", 14 | "MAIN_DESC_SETTINGS": "تخصيص التثبيت الخاص بك", 15 | "MAIN_DESC_VERIFY": "التحقق من ملفات اللعبة (في حالة وجود ملفات تالفة او ناقصة)", 16 | "MAIN_DESC_PLAY": "قم بتشغيل Fortnite!", 17 | "MAIN_STATUS_STARTING": "جار البدء...", 18 | "MAIN_STATUS_PLAYABLE": "تم البدء! اضغط على \"تشغيل\" لبدء اللعبة!", 19 | "MAIN_BTN_SETTINGS": "الإعدادات", 20 | "MAIN_BTN_VERIFY": "تحقق", 21 | "MAIN_BTN_PLAY": "تشغيل", 22 | "MAIN_STATUS_SELLOUT": "لا تنسى تستخدم كود \"furry\" في متجر العناصر! (#ad)", 23 | "MAIN_DESC_TITLE": "الوصف", 24 | "MAIN_STATS_TITLE": "الاحصائيات", 25 | "MAIN_STATS_CPU": "وحدة المعالجة المركزية", 26 | "MAIN_STATS_RAM": "الذاكرة (RAM)", 27 | "MAIN_STATS_READ": "القراءة", 28 | "MAIN_STATS_WRITE": "الكتابة", 29 | "MAIN_STATS_PROVIDE": "يزود", 30 | "MAIN_STATS_DOWNLOAD": "التحميل", 31 | "MAIN_STATS_LATENCY": "التأخير", 32 | "MAIN_STATS_THREADS": "المسارات", 33 | "MAIN_PROG_VERIFY": "جارٍ التحقق", 34 | "MAIN_EXIT_VETOMSG": "Fortnite لا تزال قيد التشغيل! هل ما زلت تريد الخروج؟", 35 | "MAIN_EXIT_VETOTITLE": "قيد التشغيل", 36 | "MAIN_BTN_UPDATE": "تحديث", 37 | "MAIN_NOTIF_TITLE": "التحديث الجديد لـ Fortnite !", 38 | "MAIN_NOTIF_DESC": "%s انه متاح الان!", 39 | "MAIN_NOTIF_ACTION": "اضغط للتحديث", 40 | "MAIN_PROG_UPDATE": "جارٍ التحديث", 41 | "PROG_LABEL_ELAPSED": "انقضى", 42 | "PROG_LABEL_ETA": "الوقت المتبقي", 43 | "PROG_BTN_CANCEL": "إلغاء", 44 | "PROG_BTN_CANCELLING": "جارٍ الإلغاء", 45 | "SETUP_TITLE": "تنصيب", 46 | "SETUP_COMP_LEVEL_FASTEST": "الأسرع", 47 | "SETUP_COMP_LEVEL_FAST": "سريع", 48 | "SETUP_COMP_LEVEL_NORMAL": "عادي", 49 | "SETUP_COMP_LEVEL_SLOW": "بطيء", 50 | "SETUP_COMP_LEVEL_SLOWEST": "الأبطأ", 51 | "SETUP_UPDATE_LEVEL_SEC1": "ثانية واحدة", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 ثواني", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 ثواني", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 ثانية", 55 | "SETUP_UPDATE_LEVEL_MIN1": "دقيقة واحدة", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 دقائق", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 دقائق", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 دقيقة", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "ساعة واحدة", 60 | "SETUP_GENERAL_LABEL": "عام", 61 | "SETUP_GENERAL_INSTFOLDER": "ملف التثبيت", 62 | "SETUP_GENERAL_COMPMETHOD": "طريقة الضغط", 63 | "SETUP_GENERAL_COMPLEVEL": "مستوى الضغط", 64 | "SETUP_GENERAL_UPDATEINT": "الفاصل الزمني للتحديث", 65 | "SETUP_ADVANCED_LABEL": "الإعدادات المتقدمة", 66 | "SETUP_ADVANCED_BUFCT": "عدد المخزن المؤقت", 67 | "SETUP_ADVANCED_THDCT": "عدد المسارات", 68 | "SETUP_ADVANCED_CMDARGS": "أوامر", 69 | "SETUP_BTN_OK": "حسنًا", 70 | "SETUP_BTN_CANCEL": "إلغاء", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "مفكوك عنه الضغط", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/DE.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Fehler", 3 | "APP_ERROR_RUNNING": "EGL2 läuft bereits!", 4 | "APP_ERROR_APPDATA": "Der Speicherort des AppData-Ordners konnte nicht gefunden werden.", 5 | "APP_ERROR_DATA": "Der EGL2-Datenordner konnte nicht erstellt werden", 6 | "APP_ERROR_LOGS": "Konnte keinen Ordner für EGL2-Protokolle erstellen", 7 | "APP_ERROR_MANIFESTS": "Konnte keinen Ordner für EGL2-Manifeste erstellen", 8 | "APP_ERROR_LOGFILE": "Konnte keine Protokolldatei erstellen! Ohne sie kann ich Ihnen nicht bei irgendwelchen Problemen helfen.", 9 | "APP_ERROR_PROGFILES86": "Konnte Ihren Ordner Programme (x86) nicht erhalten. Ich habe ehrlich gesagt keine Ahnung, was Sie getan haben, dass dieser Fehler aufgekommen ist.", 10 | "APP_ERROR_WINFSP_FIND": "Die DLL von WinFsp konnte im Ordner des Treibers nicht gefunden werden. Versuchen Sie, WinFsp neu zu installieren.", 11 | "APP_ERROR_WINFSP_LOAD": "WinFsp's DLL konnte nicht in dem \"driver's folder\" geladen werden. Versuche WinFsp neu zu installieren.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Beim Versuch, die DLL von WinFsp zu laden, ist ein unbekannter Fehler aufgetreten: %d", 13 | "MAIN_DESC_DEFAULT": "Fahren Sie mit der Maus über eine Schaltfläche, um zu sehen, was passiert.", 14 | "MAIN_DESC_SETTINGS": "Konfigurieren/Regulieren Sie Ihre Installation", 15 | "MAIN_DESC_VERIFY": "Verifizieren Sie ihr Spiel, falls beschädigte Dateien bestehen.", 16 | "MAIN_DESC_PLAY": "Starten Sie Fortnite!", 17 | "MAIN_STATUS_STARTING": "Hochfahren...", 18 | "MAIN_STATUS_PLAYABLE": "Gestartet! Drücken Sie \"Spiel starten\" um zu starten!", 19 | "MAIN_BTN_SETTINGS": "Einstellungen", 20 | "MAIN_BTN_VERIFY": "Überprüfen", 21 | "MAIN_BTN_PLAY": "Spiel starten.", 22 | "MAIN_STATUS_SELLOUT": "Benutze den Creator Code \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Beschreibung", 24 | "MAIN_STATS_TITLE": "Stats", 25 | "MAIN_STATS_CPU": "Prozessor", 26 | "MAIN_STATS_RAM": "Speicher", 27 | "MAIN_STATS_READ": "Lesen", 28 | "MAIN_STATS_WRITE": "Schreiben", 29 | "MAIN_STATS_PROVIDE": "zur Verfügung stellen", 30 | "MAIN_STATS_DOWNLOAD": "Herunterladen", 31 | "MAIN_STATS_LATENCY": "Latenz", 32 | "MAIN_STATS_THREADS": "Themen", 33 | "MAIN_PROG_VERIFY": "Verifizierung", 34 | "MAIN_EXIT_VETOMSG": "Fortnite ist immer noch am laufen. Möchtest du es verlassen?", 35 | "MAIN_EXIT_VETOTITLE": "Läuft aktuell", 36 | "MAIN_BTN_UPDATE": "Aktualisierung/Update", 37 | "MAIN_NOTIF_TITLE": "Neues Fortnite Update!", 38 | "MAIN_NOTIF_DESC": "%s ist jetzt verfügbar!", 39 | "MAIN_NOTIF_ACTION": "Zum Aktualisieren klicken", 40 | "MAIN_PROG_UPDATE": "Aktualisiert...", 41 | "PROG_LABEL_ELAPSED": "Verstrichen", 42 | "PROG_LABEL_ETA": "ETA", 43 | "PROG_BTN_CANCEL": "Abbrechen", 44 | "PROG_BTN_CANCELLING": "Abbrechen", 45 | "SETUP_TITLE": "Einrichtung", 46 | "SETUP_COMP_LEVEL_FASTEST": "Schnellste", 47 | "SETUP_COMP_LEVEL_FAST": "Schnell", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normal", 49 | "SETUP_COMP_LEVEL_SLOW": "Langsam", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Langsamste", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 Sekunde", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 Sekunden", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 Sekunden", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 Sekunden", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 Minute", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 Minuten", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 Minuten", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 Minuten", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 Stunde", 60 | "SETUP_GENERAL_LABEL": "Allgemein", 61 | "SETUP_GENERAL_INSTFOLDER": "Installationsordner", 62 | "SETUP_GENERAL_COMPMETHOD": "Kompressionsverfahren", 63 | "SETUP_GENERAL_COMPLEVEL": "Kompressionsstufe", 64 | "SETUP_GENERAL_UPDATEINT": "Aktualisierungsintervall", 65 | "SETUP_ADVANCED_LABEL": "Fortgeschrittene", 66 | "SETUP_ADVANCED_BUFCT": "Puffer-Anzahl", 67 | "SETUP_ADVANCED_THDCT": "Fadenzahl", 68 | "SETUP_ADVANCED_CMDARGS": "Kommando-Argumente", 69 | "SETUP_BTN_OK": "OK", 70 | "SETUP_BTN_CANCEL": "Abbrechen", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Dekomprimiert", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/EN.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Error", 3 | "APP_ERROR_RUNNING": "EGL2 is already running!", 4 | "APP_ERROR_APPDATA": "Could not get the location of your AppData folder", 5 | "APP_ERROR_DATA": "Could not create EGL2 data folder", 6 | "APP_ERROR_LOGS": "Could not create EGL2 logs folder", 7 | "APP_ERROR_MANIFESTS": "Could not create EGL2 manifests folder", 8 | "APP_ERROR_LOGFILE": "Could not create a log file! Without it, I can't assist you with any issues.", 9 | "APP_ERROR_PROGFILES86": "Could not get your Program Files (x86) folder. I honestly have no idea how you'd get this error.", 10 | "APP_ERROR_WINFSP_FIND": "Could not find WinFsp's DLL in the driver's folder. Try reinstalling WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "Could not load WinFsp's DLL in the driver's folder. Try reinstalling WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "An unknown error occurred when trying to load WinFsp's DLL: %d", 13 | "MAIN_STATUS_STARTING": "Starting up...", 14 | "MAIN_STATUS_PLAYABLE": "Started! Press \"Play\" to start playing!", 15 | "MAIN_BTN_SETTINGS": "Settings", 16 | "MAIN_BTN_VERIFY": "Verify", 17 | "MAIN_BTN_PLAY": "Play", 18 | "MAIN_STATUS_SELLOUT": "Use code \"furry\"! (#ad)", 19 | "MAIN_STATS_TITLE": "Stats", 20 | "MAIN_STATS_CPU": "CPU", 21 | "MAIN_STATS_RAM": "RAM", 22 | "MAIN_STATS_READ": "Read", 23 | "MAIN_STATS_WRITE": "Write", 24 | "MAIN_STATS_PROVIDE": "Provide", 25 | "MAIN_STATS_DOWNLOAD": "Download", 26 | "MAIN_STATS_LATENCY": "Latency", 27 | "MAIN_STATS_THREADS": "Threads", 28 | "MAIN_PROG_VERIFY": "Verifying", 29 | "MAIN_EXIT_VETOMSG": "Fortnite is still running! Do you still want to exit?", 30 | "MAIN_EXIT_VETOTITLE": "Currently Running", 31 | "MAIN_BTN_UPDATE": "Update", 32 | "MAIN_NOTIF_TITLE": "New Fortnite Update!", 33 | "MAIN_NOTIF_DESC": "%s is now available!", 34 | "MAIN_NOTIF_ACTION": "Click to Update", 35 | "MAIN_PROG_UPDATE": "Updating", 36 | "PROG_LABEL_ELAPSED": "Elapsed", 37 | "PROG_LABEL_ETA": "ETA", 38 | "PROG_BTN_CANCEL": "Cancel", 39 | "PROG_BTN_CANCELLING": "Cancelling", 40 | "SETUP_TITLE": "Setup", 41 | "SETUP_COMP_LEVEL_FASTEST": "Fastest", 42 | "SETUP_COMP_LEVEL_FAST": "Fast", 43 | "SETUP_COMP_LEVEL_NORMAL": "Normal", 44 | "SETUP_COMP_LEVEL_SLOW": "Slow", 45 | "SETUP_COMP_LEVEL_SLOWEST": "Slowest", 46 | "SETUP_UPDATE_LEVEL_SEC1": "1 second", 47 | "SETUP_UPDATE_LEVEL_SEC5": "5 seconds", 48 | "SETUP_UPDATE_LEVEL_SEC10": "10 seconds", 49 | "SETUP_UPDATE_LEVEL_SEC30": "30 seconds", 50 | "SETUP_UPDATE_LEVEL_MIN1": "1 minute", 51 | "SETUP_UPDATE_LEVEL_MIN5": "5 minutes", 52 | "SETUP_UPDATE_LEVEL_MIN10": "10 minutes", 53 | "SETUP_UPDATE_LEVEL_MIN30": "30 minutes", 54 | "SETUP_UPDATE_LEVEL_HOUR1": "1 hour", 55 | "SETUP_GENERAL_LABEL": "General", 56 | "SETUP_GENERAL_INSTFOLDER": "Install Folder", 57 | "SETUP_GENERAL_COMPMETHOD": "Compression Method", 58 | "SETUP_GENERAL_COMPLEVEL": "Compression Level", 59 | "SETUP_GENERAL_UPDATEINT": "Update Interval", 60 | "SETUP_ADVANCED_LABEL": "Advanced", 61 | "SETUP_ADVANCED_BUFCT": "Buffer Count", 62 | "SETUP_ADVANCED_THDCT": "Thread Count", 63 | "SETUP_ADVANCED_CMDARGS": "Command Arguments", 64 | "SETUP_BTN_OK": "OK", 65 | "SETUP_BTN_CANCEL": "Cancel", 66 | 67 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 68 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 69 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 70 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 71 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 72 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 73 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 74 | "SETUP_COMP_METHOD_DECOMP": "No Compression", 75 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 76 | "SETUP_COMP_METHOD_LZ4": "LZ4", 77 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie", 78 | 79 | "APP_ERROR_CHM": "Could not create EGL2 chm file", 80 | "MAIN_BTN_STORAGE": "Storage" 81 | } -------------------------------------------------------------------------------- /locales/ES.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Error", 3 | "APP_ERROR_RUNNING": "¡EGL2 ya está funcionando!", 4 | "APP_ERROR_APPDATA": "No se logró localizar la carpeta de AppData", 5 | "APP_ERROR_DATA": "No se logró crear la carpeta EGL2 Data", 6 | "APP_ERROR_LOGS": "No se logró crear la carpeta EGL2 Logs", 7 | "APP_ERROR_MANIFESTS": "No se logró crear la carpeta EGL2 Manifests", 8 | "APP_ERROR_LOGFILE": "No se logró crear el archivo log. Sin este archivo, no se podrá observar los problemas ocurridos.", 9 | "APP_ERROR_PROGFILES86": "No se logró localizar tu carpeta \"Program Files (x86)\". Este error es Desconocido.", 10 | "APP_ERROR_WINFSP_FIND": "No se logró localizar el DLL de WinFsp. Intente reinstalar WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "No se pudo cargar el DLL de WinFsp en la carpeta \"Drivers\". Intente reinstalar WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Un error desconocido ocurrió al intentar de cargar el DLL de WinFsp: %d", 13 | "MAIN_DESC_DEFAULT": "Pase el cursor sobre un botón para ver la función", 14 | "MAIN_DESC_SETTINGS": "Configurar la Instalación", 15 | "MAIN_DESC_VERIFY": "Verifica tu juego (en caso de que tenga datos corruptos)", 16 | "MAIN_DESC_PLAY": "¡Lanza Fortnite!", 17 | "MAIN_STATUS_STARTING": "Comenzando...", 18 | "MAIN_STATUS_PLAYABLE": "¡Comenzado! ¡Presiona \"Jugar\" para comenzar el juego!", 19 | "MAIN_BTN_SETTINGS": "Configuración", 20 | "MAIN_BTN_VERIFY": "Verificar", 21 | "MAIN_BTN_PLAY": "Jugar", 22 | "MAIN_STATUS_SELLOUT": "¡Utiliza el código \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Descripción", 24 | "MAIN_STATS_TITLE": "Estadísticas", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "Lectura", 28 | "MAIN_STATS_WRITE": "Escriba", 29 | "MAIN_STATS_PROVIDE": "Proporciona", 30 | "MAIN_STATS_DOWNLOAD": "Descarga", 31 | "MAIN_STATS_LATENCY": "Latencia", 32 | "MAIN_STATS_THREADS": "Subprocesos", 33 | "MAIN_PROG_VERIFY": "Verificando", 34 | "MAIN_EXIT_VETOMSG": "Fortnite aún está ejecutándose. ¿Seguro qué quieres salir?", 35 | "MAIN_EXIT_VETOTITLE": "Actualmente Ejecutándose", 36 | "MAIN_BTN_UPDATE": "Actualizar", 37 | "MAIN_NOTIF_TITLE": "¡Nueva actualización de Fortnite!", 38 | "MAIN_NOTIF_DESC": "¡%s ahora está disponible!", 39 | "MAIN_NOTIF_ACTION": "Presiona para Actualizar", 40 | "MAIN_PROG_UPDATE": "Actualizando", 41 | "PROG_LABEL_ELAPSED": "Transcurrido ", 42 | "PROG_LABEL_ETA": "ETA (Tiempo Estimado)", 43 | "PROG_BTN_CANCEL": "Cancelar", 44 | "PROG_BTN_CANCELLING": "Cancelando", 45 | "SETUP_TITLE": "Configuración", 46 | "SETUP_COMP_LEVEL_FASTEST": "Más Rápido", 47 | "SETUP_COMP_LEVEL_FAST": "Rápido", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normal", 49 | "SETUP_COMP_LEVEL_SLOW": "Lento", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Más Lento", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 segundo", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 segundos", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 segundos", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 segundos", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 minuto", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 minutos", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 minutos", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 minutos", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 hora", 60 | "SETUP_GENERAL_LABEL": "General", 61 | "SETUP_GENERAL_INSTFOLDER": "Carpeta de Instalación", 62 | "SETUP_GENERAL_COMPMETHOD": "Método de Compresión", 63 | "SETUP_GENERAL_COMPLEVEL": "Nivel de Compresión", 64 | "SETUP_GENERAL_UPDATEINT": "Intervalo de Actualización", 65 | "SETUP_ADVANCED_LABEL": "Avanzado", 66 | "SETUP_ADVANCED_BUFCT": "Contador de Búfer", 67 | "SETUP_ADVANCED_THDCT": "Contador de Subprocesos", 68 | "SETUP_ADVANCED_CMDARGS": "Argumentos de Comando", 69 | "SETUP_BTN_OK": "OK", 70 | "SETUP_BTN_CANCEL": "Cancelar", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Descomprimido", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/FI.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Virhe", 3 | "APP_ERROR_RUNNING": "EGL2 on jo käynnissä!", 4 | "APP_ERROR_APPDATA": "AppData -kansiota ei voitu löytää", 5 | "APP_ERROR_DATA": "EGL2:n datakansiota ei voitu luoda", 6 | "APP_ERROR_LOGS": "EGL2:n logikansiota ei voitu luoda", 7 | "APP_ERROR_MANIFESTS": "EGL2:n manifest kansiota ei voitu luoda", 8 | "APP_ERROR_LOGFILE": "Logitiedostoa ei voitu luoda! Ilman sitä en voi auttaa sinua ongelmien kanssa.", 9 | "APP_ERROR_PROGFILES86": "Program Files (x86) -kansiota ei voitu löytää. Totta puhuen en ymmärrä miten sait tämän virheen.", 10 | "APP_ERROR_WINFSP_FIND": "WinFsp:n DLL -tiedostoja ei voitu löytää ajurit -kansiosta. Yritä asentaa WinFsp uudelleen.", 11 | "APP_ERROR_WINFSP_LOAD": "WinFsp:n DLL -tiedostoja ei voitu ladata ajurit -kansiosta. Yritä asentaa WinFsp uudelleen.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Tuntematon virhe ilmeni ladatessa WinFsp:n DLL -tiedostoja: %d", 13 | "MAIN_DESC_DEFAULT": "Vie hiiri painikkeen päälle nähdäksesi mitä se tekee", 14 | "MAIN_DESC_SETTINGS": "Määritä asennuksesi", 15 | "MAIN_DESC_VERIFY": "Tarkistaa pelisi korruptoituneiden tiedostojen varalta", 16 | "MAIN_DESC_PLAY": "Käynnistä Fortnite!", 17 | "MAIN_STATUS_STARTING": "Käynnistetään...", 18 | "MAIN_STATUS_PLAYABLE": "Käynnistetty! Paina \"Pelaa\" aloittaaksesi pelaamisen!", 19 | "MAIN_BTN_SETTINGS": "Asetukset", 20 | "MAIN_BTN_VERIFY": "Tarkista", 21 | "MAIN_BTN_PLAY": "Pelaa", 22 | "MAIN_STATUS_SELLOUT": "Käytä koodia \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Kuvaus", 24 | "MAIN_STATS_TITLE": "Tilastot", 25 | "MAIN_STATS_CPU": "Prosessori", 26 | "MAIN_STATS_RAM": "Muisti", 27 | "MAIN_STATS_READ": "Lukeminen", 28 | "MAIN_STATS_WRITE": "Kirjoitus", 29 | "MAIN_STATS_PROVIDE": "Toimitus", 30 | "MAIN_STATS_DOWNLOAD": "Lataus", 31 | "MAIN_STATS_LATENCY": "Viive", 32 | "MAIN_STATS_THREADS": "Säikeet", 33 | "MAIN_PROG_VERIFY": "Tarkistetaan", 34 | "MAIN_EXIT_VETOMSG": "Fortnite on vielä päällä! Haluatko silti poistua?", 35 | "MAIN_EXIT_VETOTITLE": "Tällä hetkellä käynnissä", 36 | "MAIN_BTN_UPDATE": "Päivitä", 37 | "MAIN_NOTIF_TITLE": "Uusi Fortnite Päivitys!", 38 | "MAIN_NOTIF_DESC": "%s on nyt saatavilla!", 39 | "MAIN_NOTIF_ACTION": "Paina Päivittääksesi", 40 | "MAIN_PROG_UPDATE": "Päivitetään", 41 | "PROG_LABEL_ELAPSED": "Kulunut aika", 42 | "PROG_LABEL_ETA": "Arvioitu aika", 43 | "PROG_BTN_CANCEL": "Peruuta", 44 | "PROG_BTN_CANCELLING": "Peruutetaan", 45 | "SETUP_TITLE": "Asennus", 46 | "SETUP_COMP_LEVEL_FASTEST": "Nopein", 47 | "SETUP_COMP_LEVEL_FAST": "Nopea", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normaali", 49 | "SETUP_COMP_LEVEL_SLOW": "Hidas", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Hitain", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 sekunti", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 sekuntia", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 sekuntia", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 sekuntia", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 minuutti", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 minuuttia", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 minuuttia", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 minuuttia", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 tunti", 60 | "SETUP_GENERAL_LABEL": "Yleinen", 61 | "SETUP_GENERAL_INSTFOLDER": "Asennuskansio", 62 | "SETUP_GENERAL_COMPMETHOD": "Kompressointi Tapa", 63 | "SETUP_GENERAL_COMPLEVEL": "Kompressointi Taso", 64 | "SETUP_GENERAL_UPDATEINT": "Päivittämisen Aikaväli", 65 | "SETUP_ADVANCED_LABEL": "Kehittynyt", 66 | "SETUP_ADVANCED_BUFCT": "Puskureiden Määrä", 67 | "SETUP_ADVANCED_THDCT": "Säikeiden Määrä", 68 | "SETUP_ADVANCED_CMDARGS": "Komento Argumentit", 69 | "SETUP_BTN_OK": "OK", 70 | "SETUP_BTN_CANCEL": "Peruuta", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Purettu", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/FR.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Erreur", 3 | "APP_ERROR_RUNNING": "EGL2 est déjà en cours d'exécution!", 4 | "APP_ERROR_APPDATA": "Échec dans l'obtention de la localisation du dossier \"AppData\"", 5 | "APP_ERROR_DATA": "Échec dans la création du dossier de données de EGL2", 6 | "APP_ERROR_LOGS": "Échec dans la création du dossier des logs de EGL2", 7 | "APP_ERROR_MANIFESTS": "Échec dans la création du dossier des manifests de EGL2", 8 | "APP_ERROR_LOGFILE": "Échec dans la création d'un fichier log! Sans lui, je ne peux pas vous assister si vous avez des problèmes", 9 | "APP_ERROR_PROGFILES86": "Échec dans l'obtention du dossier \"Program Files (x86)\" . Honêtement, je ne sais pas comment vous auriez cette erreur", 10 | "APP_ERROR_WINFSP_FIND": "Échec dans la localisation du DLL WinFsp dans le dossier du pilote. Essayez de réinstaller WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "Échec du chargement du DLL WinFsp dans le dossier du pilote. Essayez de réinstaller WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Une erreur inconnue est survenu durant le chargement du DLL WinFsp: %d", 13 | "MAIN_DESC_DEFAULT": "Survoler un boutton pour savoir à quoi il sert", 14 | "MAIN_DESC_SETTINGS": "Configurez votre installation", 15 | "MAIN_DESC_VERIFY": "Vérifie votre jeu (dans le cas où vous avez des données corrompues)", 16 | "MAIN_DESC_PLAY": "Lancez Fortnite!", 17 | "MAIN_STATUS_STARTING": "Démarrage en cours...", 18 | "MAIN_STATUS_PLAYABLE": "Démarré! Cliquez \"Jouer\" pour commencer à jouer!", 19 | "MAIN_BTN_SETTINGS": "Réglages", 20 | "MAIN_BTN_VERIFY": "Vérifier", 21 | "MAIN_BTN_PLAY": "Jouer", 22 | "MAIN_STATUS_SELLOUT": "Utilise le code \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Description", 24 | "MAIN_STATS_TITLE": "Stats", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "Lire", 28 | "MAIN_STATS_WRITE": "Écrire", 29 | "MAIN_STATS_PROVIDE": "Fournir", 30 | "MAIN_STATS_DOWNLOAD": "Télécharger", 31 | "MAIN_STATS_LATENCY": "Latence", 32 | "MAIN_STATS_THREADS": "Threads", 33 | "MAIN_PROG_VERIFY": "Vérification en cours", 34 | "MAIN_EXIT_VETOMSG": "Fortnite est toujours en courd d'exécution! Êtes-vous sûr de vouloir quitter?", 35 | "MAIN_EXIT_VETOTITLE": "En cours d'Exécution", 36 | "MAIN_BTN_UPDATE": "Mise à Jour", 37 | "MAIN_NOTIF_TITLE": "Nouvelle Mise à Jour Fortnite!", 38 | "MAIN_NOTIF_DESC": "%s est disponible maintenant!", 39 | "MAIN_NOTIF_ACTION": "Cliquer pour mettre à jour", 40 | "MAIN_PROG_UPDATE": "Mise à Jour en cours", 41 | "PROG_LABEL_ELAPSED": "Écoulé", 42 | "PROG_LABEL_ETA": "HAP", 43 | "PROG_BTN_CANCEL": "Annuler", 44 | "PROG_BTN_CANCELLING": "Annulation", 45 | "SETUP_TITLE": "Setup", 46 | "SETUP_COMP_LEVEL_FASTEST": "Le Plus Rapide", 47 | "SETUP_COMP_LEVEL_FAST": "Rapide", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normal", 49 | "SETUP_COMP_LEVEL_SLOW": "Lent", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Le Plus Lent", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 seconde", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 secondes", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 secondes", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 secondes", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 minute", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 minutes", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 minutes", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 minutes", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 heure", 60 | "SETUP_GENERAL_LABEL": "Général", 61 | "SETUP_GENERAL_INSTFOLDER": "Installer le Dossier", 62 | "SETUP_GENERAL_COMPMETHOD": "Méthode de Compression", 63 | "SETUP_GENERAL_COMPLEVEL": "Niveau de Compression", 64 | "SETUP_GENERAL_UPDATEINT": "Interval de Mise à Jour", 65 | "SETUP_ADVANCED_LABEL": "Avancé", 66 | "SETUP_ADVANCED_BUFCT": "Nombre de Buffer", 67 | "SETUP_ADVANCED_THDCT": "Nombre de Thread", 68 | "SETUP_ADVANCED_CMDARGS": "Command Arguments", 69 | "SETUP_BTN_OK": "D'accord", 70 | "SETUP_BTN_CANCEL": "Annuler", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Décompressé", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/IT.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Errore", 3 | "APP_ERROR_RUNNING": "EGL2 è già in esecuzione!", 4 | "APP_ERROR_APPDATA": "Impossibile ottenere la posizione della cartella AppData", 5 | "APP_ERROR_DATA": "Impossibile creare la cartella EGL2 Data", 6 | "APP_ERROR_LOGS": "Impossibile creare la cartella EGL2 Logs", 7 | "APP_ERROR_MANIFESTS": "Impossibile creare la cartella EGL2 Manifests", 8 | "APP_ERROR_LOGFILE": "Impossibile creare un file di log! Senza di esso non potrò assisterti con nessun problema.", 9 | "APP_ERROR_PROGFILES86": "Impossibile ottenere la cartella Programmi (x86). Onestamente non ho alcune idea di come tu abbia questo errore.", 10 | "APP_ERROR_WINFSP_FIND": "Non riesco a trovare il DLL di WinFsp nella cartella dei driver. Prova installando nuovamente WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "Non riesco a caricare il DLL di WinFsp nella cartella dei driver. Prova installando nuovamente WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Si è verificato un errore sconosciuto nel tentativo di caricare il DLL di WinFsp: %d", 13 | "MAIN_DESC_DEFAULT": "Vai con il cursore sopra un bottone per vedere cosa fa", 14 | "MAIN_DESC_SETTINGS": "Configura la tua installazione", 15 | "MAIN_DESC_VERIFY": "Verifica il gioco (in caso di dat corrotti)", 16 | "MAIN_DESC_PLAY": "Avvia Fortnite!", 17 | "MAIN_STATUS_STARTING": "Inizializzando...", 18 | "MAIN_STATUS_PLAYABLE": "Inizializzato! Premi \"Gioca\" per iniziare a giocare!", 19 | "MAIN_BTN_SETTINGS": "Impostazioni", 20 | "MAIN_BTN_VERIFY": "Verifica", 21 | "MAIN_BTN_PLAY": "Gioca", 22 | "MAIN_STATUS_SELLOUT": "Usa il codice \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Descrizione", 24 | "MAIN_STATS_TITLE": "Statistiche", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "Lettura", 28 | "MAIN_STATS_WRITE": "Scrittura", 29 | "MAIN_STATS_PROVIDE": "Forniti", 30 | "MAIN_STATS_DOWNLOAD": "Download", 31 | "MAIN_STATS_LATENCY": "Latenza", 32 | "MAIN_STATS_THREADS": "Processi", 33 | "MAIN_PROG_VERIFY": "Verificando", 34 | "MAIN_EXIT_VETOMSG": "Fortnite è in esecuzione! Vuoi lo stesso uscire?", 35 | "MAIN_EXIT_VETOTITLE": "Attualmente in corso", 36 | "MAIN_BTN_UPDATE": "Aggiorna", 37 | "MAIN_NOTIF_TITLE": "Nuovo aggiornamento di Fortnite!", 38 | "MAIN_NOTIF_DESC": "%s è ora disponibile!", 39 | "MAIN_NOTIF_ACTION": "Clicca per aggiornare", 40 | "MAIN_PROG_UPDATE": "Aggiornando", 41 | "PROG_LABEL_ELAPSED": "Tempo trascorso", 42 | "PROG_LABEL_ETA": "Tempo rimanente", 43 | "PROG_BTN_CANCEL": "Annulla", 44 | "PROG_BTN_CANCELLING": "Annullando", 45 | "SETUP_TITLE": "Imposta", 46 | "SETUP_COMP_LEVEL_FASTEST": "Molto veloce", 47 | "SETUP_COMP_LEVEL_FAST": "Veloce", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normale", 49 | "SETUP_COMP_LEVEL_SLOW": "Lenta", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Molto lenta", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 secondo", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 secondi", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 secondi", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 secondi", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 minuto", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 minuti", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 minuti", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 minuti", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 ora", 60 | "SETUP_GENERAL_LABEL": "Generale", 61 | "SETUP_GENERAL_INSTFOLDER": "Cartella di installazione", 62 | "SETUP_GENERAL_COMPMETHOD": "Metodo di compressione", 63 | "SETUP_GENERAL_COMPLEVEL": "Livello di compressione", 64 | "SETUP_GENERAL_UPDATEINT": "Intervallo di aggiornamento", 65 | "SETUP_ADVANCED_LABEL": "Avanzato", 66 | "SETUP_ADVANCED_BUFCT": "Numero di buffer", 67 | "SETUP_ADVANCED_THDCT": "Numero di thread", 68 | "SETUP_ADVANCED_CMDARGS": "Argomenti di riga comandi", 69 | "SETUP_BTN_OK": "OK", 70 | "SETUP_BTN_CANCEL": "Annulla", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Decompresso", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/JA.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "エラー", 3 | "APP_ERROR_RUNNING": "EGL2はすでに実行されています!", 4 | "APP_ERROR_APPDATA": "アプリケーションデータフォルダーの場所を取得できませんでした", 5 | "APP_ERROR_DATA": "EGL2のデータフォルダーを作成できませんでした", 6 | "APP_ERROR_LOGS": "EGL2のログフォルダーを作成できませんでした", 7 | "APP_ERROR_MANIFESTS": "EGL2のmanifestフォルダーを作成できませんでした", 8 | "APP_ERROR_LOGFILE": "ログファイルを作成できませんでした!それがないと、あらゆる問題が起こってもあなたを助けることができません。", 9 | "APP_ERROR_PROGFILES86": "Program Files (x86)フォルダーを取得できませんでした。このエラーがどうして発生するかはわかりません。", 10 | "APP_ERROR_WINFSP_FIND": "ドライバーのフォルダーにWinFspのDLLが見つかりませんでした。 WinFspを再インストールしてください。", 11 | "APP_ERROR_WINFSP_LOAD": "ドライバーのフォルダーにWinFspのDLLを読み込めませんでした。 WinFspを再インストールしてください。", 12 | "APP_ERROR_WINFSP_UNKNOWN": "WinFspのDLLをロードする際に不明なエラーが発生しました: %d", 13 | "MAIN_DESC_DEFAULT": "ボタンにカーソルを合わせると、その機能を確認できます", 14 | "MAIN_DESC_SETTINGS": "インストールを構成する", 15 | "MAIN_DESC_VERIFY": "ゲームを検証します (データが破損している場合)", 16 | "MAIN_DESC_PLAY": "フォートナイトを起動します!", 17 | "MAIN_STATUS_STARTING": "起動中...", 18 | "MAIN_STATUS_PLAYABLE": "開始できます! 「開始」を押して始めてください!", 19 | "MAIN_BTN_SETTINGS": "設定", 20 | "MAIN_BTN_VERIFY": "確認", 21 | "MAIN_BTN_PLAY": "プレー", 22 | "MAIN_STATUS_SELLOUT": "クリエイターコード「furry」を使用してください! (#ad)", 23 | "MAIN_DESC_TITLE": "説明", 24 | "MAIN_STATS_TITLE": "開始", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "読む", 28 | "MAIN_STATS_WRITE": "書く", 29 | "MAIN_STATS_PROVIDE": "プロパイド", 30 | "MAIN_STATS_DOWNLOAD": "ダウンロード", 31 | "MAIN_STATS_LATENCY": "待ち時間", 32 | "MAIN_STATS_THREADS": "スレッド", 33 | "MAIN_PROG_VERIFY": "確認中", 34 | "MAIN_EXIT_VETOMSG": "フォートナイトはまだ実行中です!終了しますか?", 35 | "MAIN_EXIT_VETOTITLE": "現在実行中", 36 | "MAIN_BTN_UPDATE": "アップデート", 37 | "MAIN_NOTIF_TITLE": "新しいフォートナイトのアップデート!", 38 | "MAIN_NOTIF_DESC": "%sが利用可能になりました!", 39 | "MAIN_NOTIF_ACTION": "アップデートをチェック", 40 | "MAIN_PROG_UPDATE": "アップデート中", 41 | "PROG_LABEL_ELAPSED": "経過", 42 | "PROG_LABEL_ETA": "ETA", 43 | "PROG_BTN_CANCEL": "キャンセル", 44 | "PROG_BTN_CANCELLING": "キャンセル中", 45 | "SETUP_TITLE": "セットアップ", 46 | "SETUP_COMP_LEVEL_FASTEST": "高速", 47 | "SETUP_COMP_LEVEL_FAST": "速い", 48 | "SETUP_COMP_LEVEL_NORMAL": "普通", 49 | "SETUP_COMP_LEVEL_SLOW": "遅い", 50 | "SETUP_COMP_LEVEL_SLOWEST": "低速", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1秒", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5秒", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10秒", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30秒", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1分", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5分", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10分", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30分", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1時間", 60 | "SETUP_GENERAL_LABEL": "一般", 61 | "SETUP_GENERAL_INSTFOLDER": "インストールフォルダー", 62 | "SETUP_GENERAL_COMPMETHOD": "圧縮方法", 63 | "SETUP_GENERAL_COMPLEVEL": "圧縮レベル", 64 | "SETUP_GENERAL_UPDATEINT": "更新間隔", 65 | "SETUP_ADVANCED_LABEL": "アドバンス", 66 | "SETUP_ADVANCED_BUFCT": "バッファ数", 67 | "SETUP_ADVANCED_THDCT": "スレッド数", 68 | "SETUP_ADVANCED_CMDARGS": "コマンド引数", 69 | "SETUP_BTN_OK": "はい", 70 | "SETUP_BTN_CANCEL": "キャンセル", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "解凍", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } 84 | -------------------------------------------------------------------------------- /locales/PL.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Błąd", 3 | "APP_ERROR_RUNNING": "EGL2 już działa!", 4 | "APP_ERROR_APPDATA": "Nie można uzyskać lokalizacji folderu AppData", 5 | "APP_ERROR_DATA": "Nie można utworzyć folderu danych EGL2", 6 | "APP_ERROR_LOGS": "Nie można utworzyć folderu dziennika EGL2", 7 | "APP_ERROR_MANIFESTS": "Nie można utworzyć folderu manifestów EGL2", 8 | "APP_ERROR_LOGFILE": "Nie można utworzyć pliku dziennika! Bez tego nie mogę pomóc z żadnymi problemami.", 9 | "APP_ERROR_PROGFILES86": "Nie można pobrać folderu Program Files (x86). Naprawdę nie mam pojęcia jak możesz dostać ten błąd.\n", 10 | "APP_ERROR_WINFSP_FIND": "Nie znaleziono bibloteki WinFsp's DLL w folderze sterwowników. Spróbuj ponownie zainstalować WinFsP", 11 | "APP_ERROR_WINFSP_LOAD": "Nie można załadowac bibloteki WinFsp's DLL do folderu sterwoników. Spróbuj ponowanie zainstalować WinFsp", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Wystąpił nieznany błąd podczas próby załadowania biblioteki WinFsp's DLL: %d", 13 | "MAIN_DESC_DEFAULT": "Najedź kursorem na przycisk, aby zobaczyć co robi", 14 | "MAIN_DESC_SETTINGS": "Skonfiguruj swoją instalacje", 15 | "MAIN_DESC_VERIFY": "Weryfikuj swoją grę (w przypadku uszkodzenia danych)", 16 | "MAIN_DESC_PLAY": "Uruchom Fortnite!", 17 | "MAIN_STATUS_STARTING": "Uruchamianie...", 18 | "MAIN_STATUS_PLAYABLE": "Gotowe! Naciśnij \"Graj\" aby rozpocząć grę!", 19 | "MAIN_BTN_SETTINGS": "Ustawienia", 20 | "MAIN_BTN_VERIFY": "Weryfikacja", 21 | "MAIN_BTN_PLAY": "Graj", 22 | "MAIN_STATUS_SELLOUT": "Użyj kodu \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Opis", 24 | "MAIN_STATS_TITLE": "Statystyki", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "Read", 28 | "MAIN_STATS_WRITE": "Write", 29 | "MAIN_STATS_PROVIDE": "Provide", 30 | "MAIN_STATS_DOWNLOAD": "Pobieranie", 31 | "MAIN_STATS_LATENCY": "Czas oczekiwania", 32 | "MAIN_STATS_THREADS": "Wątki", 33 | "MAIN_PROG_VERIFY": "Weryfikacja", 34 | "MAIN_EXIT_VETOMSG": "Fortnite jest wciąż włączony! Czy nadal chcesz wyjść?", 35 | "MAIN_EXIT_VETOTITLE": "Aktualnie uruchomiony", 36 | "MAIN_BTN_UPDATE": "Aktualizuj", 37 | "MAIN_NOTIF_TITLE": "Nowa aktualizacja w Fortnite!", 38 | "MAIN_NOTIF_DESC": "%s jest już dostępny!", 39 | "MAIN_NOTIF_ACTION": "Kliknij aby zaaktualizować", 40 | "MAIN_PROG_UPDATE": "Aktualizowanie", 41 | "PROG_LABEL_ELAPSED": "Upłyneło", 42 | "PROG_LABEL_ETA": "ETA", 43 | "PROG_BTN_CANCEL": "Anuluj", 44 | "PROG_BTN_CANCELLING": "Anulowanie", 45 | "SETUP_TITLE": "Setup", 46 | "SETUP_COMP_LEVEL_FASTEST": "Najszybszy", 47 | "SETUP_COMP_LEVEL_FAST": "Szybki", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normalny", 49 | "SETUP_COMP_LEVEL_SLOW": "Wolny", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Najwolniejszy", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 sekunda", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 sekund", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 sekund", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 sekund", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 minuta", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 minut", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 minut", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 minut", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 godzina", 60 | "SETUP_GENERAL_LABEL": "General", 61 | "SETUP_GENERAL_INSTFOLDER": "Folder instalacji", 62 | "SETUP_GENERAL_COMPMETHOD": "Metoda kompresji", 63 | "SETUP_GENERAL_COMPLEVEL": "Poziom kompresji", 64 | "SETUP_GENERAL_UPDATEINT": "Interwał aktualizacji", 65 | "SETUP_ADVANCED_LABEL": "Zaawansowane", 66 | "SETUP_ADVANCED_BUFCT": "Liczba bufforów", 67 | "SETUP_ADVANCED_THDCT": "Liczba wątków", 68 | "SETUP_ADVANCED_CMDARGS": "Argumenty poleceń", 69 | "SETUP_BTN_OK": "OK", 70 | "SETUP_BTN_CANCEL": "Anuluj", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Zdekompresowane", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/PT_BR.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Erro", 3 | "APP_ERROR_RUNNING": "O EGL2 já está em execução!", 4 | "APP_ERROR_APPDATA": "Não foi possível localizar sua pasta AppData.", 5 | "APP_ERROR_DATA": "Não foi possível criar a pasta de dados do EGL2.", 6 | "APP_ERROR_LOGS": "Não foi possível criar a pasta de logs do EGL2.", 7 | "APP_ERROR_MANIFESTS": "Não foi possível criar a pasta de manifestos do EGL2.", 8 | "APP_ERROR_LOGFILE": "Não foi possível criar o arquivo de Logs! Sem isso, não conseguimos lhe ajudar em algum problema.", 9 | "APP_ERROR_PROGFILES86": "Não foi possível localizar a pasta \"Arquivos de Programas (x86)\". Honestamente, não tenho ideia de como aconteceu esse erro.", 10 | "APP_ERROR_WINFSP_FIND": "Não foi possível localizar as DLL's do WinFsp em sua pasta de drivers. Tente reinstalar o WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "Não foi possível carregar as DLL's do WinFsp em sua pasta de drivers. Tente reinstalar o WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Um erro desconhecido ocorreu na tentativa de carregar as DLL's do WinFsp: %d", 13 | "MAIN_DESC_DEFAULT": "Passe o mouse em cima do botão para ver o que ele faz", 14 | "MAIN_DESC_SETTINGS": "Configure sua instalação", 15 | "MAIN_DESC_VERIFY": "Verifique os arquivos do jogo (caso você tenha alguns arquivos corrompidos)", 16 | "MAIN_DESC_PLAY": "Lançar Fortnite!", 17 | "MAIN_STATUS_STARTING": "Iniciando...", 18 | "MAIN_STATUS_PLAYABLE": "Pronto! Clique em \"Jogar\" para abrir o jogo.", 19 | "MAIN_BTN_SETTINGS": "Configurações", 20 | "MAIN_BTN_VERIFY": "Verifique", 21 | "MAIN_BTN_PLAY": "Jogar", 22 | "MAIN_STATUS_SELLOUT": "Use code \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Descrição", 24 | "MAIN_STATS_TITLE": "Status", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "Ler", 28 | "MAIN_STATS_WRITE": "Escrever", 29 | "MAIN_STATS_PROVIDE": "Fornecer", 30 | "MAIN_STATS_DOWNLOAD": "Baxiar", 31 | "MAIN_STATS_LATENCY": "Latência", 32 | "MAIN_STATS_THREADS": "Threads", 33 | "MAIN_PROG_VERIFY": "Verificando", 34 | "MAIN_EXIT_VETOMSG": "Você já está sendo executado! Você deseja sair do jogo?", 35 | "MAIN_EXIT_VETOTITLE": "Jogo em execução.", 36 | "MAIN_BTN_UPDATE": "Atualizar", 37 | "MAIN_NOTIF_TITLE": "Nova atualização do Fortnite!", 38 | "MAIN_NOTIF_DESC": "%s está disponível!", 39 | "MAIN_NOTIF_ACTION": "Clique para atualizar", 40 | "MAIN_PROG_UPDATE": "Atualizando", 41 | "PROG_LABEL_ELAPSED": "Tempo decorrido", 42 | "PROG_LABEL_ETA": "ETA", 43 | "PROG_BTN_CANCEL": "Cancelar", 44 | "PROG_BTN_CANCELLING": "Cancelando", 45 | "SETUP_TITLE": "Setup", 46 | "SETUP_COMP_LEVEL_FASTEST": "Muito rápido", 47 | "SETUP_COMP_LEVEL_FAST": "Rápido", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normal", 49 | "SETUP_COMP_LEVEL_SLOW": "Lento", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Muito lento", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 segundo", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 segundos", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 segundos", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 segundos", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 minuto", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 minutos", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 minutos", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 minutos", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 hora", 60 | "SETUP_GENERAL_LABEL": "Geral", 61 | "SETUP_GENERAL_INSTFOLDER": "Pasta de instalação", 62 | "SETUP_GENERAL_COMPMETHOD": "Método de Compressão", 63 | "SETUP_GENERAL_COMPLEVEL": "Nível de Compressão", 64 | "SETUP_GENERAL_UPDATEINT": "Intervalo de Atualização", 65 | "SETUP_ADVANCED_LABEL": "Avançado", 66 | "SETUP_ADVANCED_BUFCT": "Número de Buffers", 67 | "SETUP_ADVANCED_THDCT": "Número de Threads", 68 | "SETUP_ADVANCED_CMDARGS": "Argumentos adicionais da linha de comando", 69 | "SETUP_BTN_OK": "OK", 70 | "SETUP_BTN_CANCEL": "Cancelar", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Não-comprimido", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/RU.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Ошибка", 3 | "APP_ERROR_RUNNING": "EGL2 уже запущен!", 4 | "APP_ERROR_APPDATA": "Не удалось получить расположение папки AppData", 5 | "APP_ERROR_DATA": "Не удалось создать папку данных EGL2", 6 | "APP_ERROR_LOGS": "Не удалось создать папку логов EGL2", 7 | "APP_ERROR_MANIFESTS": "Не удалось создать папку манифестов EGL2", 8 | "APP_ERROR_LOGFILE": "Не удалось создать файл логов! Без него, я не смогу помочь вам при возникновении проблемы.", 9 | "APP_ERROR_PROGFILES86": "Не удалось получить вашу папку Program Files (x86). Честно говоря, я без понятия как вы получили эту ошибку.", 10 | "APP_ERROR_WINFSP_FIND": "Не удалось найти WinFsp's DLL в папке драйверов. Попробуйте переустановить WinFsp.", 11 | "APP_ERROR_WINFSP_LOAD": "Не удалось загрузить WinFsp's DLL в папке драйверов. Попробуйте переустановить WinFsp.", 12 | "APP_ERROR_WINFSP_UNKNOWN": "Произошла неизвестная ошибка при попытке загрузки WinFsp's DLL: %d", 13 | "MAIN_DESC_DEFAULT": "Наведите на кнопку, чтобы узнать, что она делает", 14 | "MAIN_DESC_SETTINGS": "Настройте вашу установку", 15 | "MAIN_DESC_VERIFY": "Проверяет вашу игру (в случае наличия у вас повреждённых данных)", 16 | "MAIN_DESC_PLAY": "Запустить Fortnite!", 17 | "MAIN_STATUS_STARTING": "Запуск...", 18 | "MAIN_STATUS_PLAYABLE": "Запущено! Нажмите \"Играть\"!", 19 | "MAIN_BTN_SETTINGS": "Настройки", 20 | "MAIN_BTN_VERIFY": "Проверить", 21 | "MAIN_BTN_PLAY": "Играть", 22 | "MAIN_STATUS_SELLOUT": "Используйте код поддержки \"furry\"! (#ad)", 23 | "MAIN_DESC_TITLE": "Описание", 24 | "MAIN_STATS_TITLE": "Характеристики", 25 | "MAIN_STATS_CPU": "Процессор", 26 | "MAIN_STATS_RAM": "Оперативная память", 27 | "MAIN_STATS_READ": "Чтение", 28 | "MAIN_STATS_WRITE": "Запись", 29 | "MAIN_STATS_PROVIDE": "Предоставление", 30 | "MAIN_STATS_DOWNLOAD": "Скачать", 31 | "MAIN_STATS_LATENCY": "Задержка", 32 | "MAIN_STATS_THREADS": "Потоки", 33 | "MAIN_PROG_VERIFY": "Проверка", 34 | "MAIN_EXIT_VETOMSG": "Fortnite всё ещё запущен! Вы всё ещё хотите выйти?", 35 | "MAIN_EXIT_VETOTITLE": "Сейчас запущен", 36 | "MAIN_BTN_UPDATE": "Обновить", 37 | "MAIN_NOTIF_TITLE": "Новое объявление Fortnite!", 38 | "MAIN_NOTIF_DESC": "%s теперь доступно!", 39 | "MAIN_NOTIF_ACTION": "Нажмите для обновления", 40 | "MAIN_PROG_UPDATE": "Обновление", 41 | "PROG_LABEL_ELAPSED": "Прошло", 42 | "PROG_LABEL_ETA": "Предполагаемое время", 43 | "PROG_BTN_CANCEL": "Отменить", 44 | "PROG_BTN_CANCELLING": "Отмена", 45 | "SETUP_TITLE": "Установить", 46 | "SETUP_COMP_LEVEL_FASTEST": "Самый быстрый", 47 | "SETUP_COMP_LEVEL_FAST": "Быстрый", 48 | "SETUP_COMP_LEVEL_NORMAL": "Обычный", 49 | "SETUP_COMP_LEVEL_SLOW": "медленный", 50 | "SETUP_COMP_LEVEL_SLOWEST": "Самый медленный", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 секунда", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 секунд", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 секунд", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 секунд", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 минута", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 минут", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 минут", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 минут", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 час", 60 | "SETUP_GENERAL_LABEL": "Основное", 61 | "SETUP_GENERAL_INSTFOLDER": "Папка установки", 62 | "SETUP_GENERAL_COMPMETHOD": "Метод сжатия", 63 | "SETUP_GENERAL_COMPLEVEL": "Уровень сжатия", 64 | "SETUP_GENERAL_UPDATEINT": "Интервал обновлений", 65 | "SETUP_ADVANCED_LABEL": "Расширенные", 66 | "SETUP_ADVANCED_BUFCT": "Количество буферов", 67 | "SETUP_ADVANCED_THDCT": "Число потоков", 68 | "SETUP_ADVANCED_CMDARGS": "Командные параметры", 69 | "SETUP_BTN_OK": "ОК", 70 | "SETUP_BTN_CANCEL": "Отменить", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Декомпресован", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /locales/TR.json: -------------------------------------------------------------------------------- 1 | { 2 | "APP_ERROR": "Hata", 3 | "APP_ERROR_RUNNING": "EGL2 zaten çalışıyor!", 4 | "APP_ERROR_APPDATA": "AppData klasörünün konumu bulunamadı", 5 | "APP_ERROR_DATA": "EGL2 veri klasörü oluşturulamadı", 6 | "APP_ERROR_LOGS": "EGL2 kayıt klasörü oluşturulamadı", 7 | "APP_ERROR_MANIFESTS": "EGL2 doğrulama klasörü oluşturulmadı", 8 | "APP_ERROR_LOGFILE": "Kayıt klasörü oluşturulamadı! Kayıt klasörü olmadan senin problemlerine yardımcı olamam.", 9 | "APP_ERROR_PROGFILES86": "Program Dosyaları (x86) klasörünü bulamadık. ", 10 | "APP_ERROR_WINFSP_FIND": "Sürücünün klasöründe WinFsp'in DLL'ini bulamadık. WinFsp'yi tekrar yüklemeyi deneyiniz.", 11 | "APP_ERROR_WINFSP_LOAD": "Sürücünün klasörüne WinFsp'in DLL'ini yükleyemedik. WinFsp'yi tekrar yüklemeyi deneyiniz", 12 | "APP_ERROR_WINFSP_UNKNOWN": "WinFsp'nin DLL: %d'yi yüklerken bilinmeyen bir hata oluştu.", 13 | "MAIN_DESC_DEFAULT": "Düğmenin ne yaptığını bilmek için imleci üzerine getirin", 14 | "MAIN_DESC_SETTINGS": "Yüklemeni yapılandır", 15 | "MAIN_DESC_VERIFY": "Oyununu doğrular (verilerinizin bozulması durumunda)", 16 | "MAIN_DESC_PLAY": "Fortnite'ı başlat!", 17 | "MAIN_STATUS_STARTING": "Başlatılıyor...", 18 | "MAIN_STATUS_PLAYABLE": "Başladı! Oynamaya başlamak için \"Oyna\" tuşuna tıkla", 19 | "MAIN_BTN_SETTINGS": "Ayarlar", 20 | "MAIN_BTN_VERIFY": "Doğrula", 21 | "MAIN_BTN_PLAY": "Oyna", 22 | "MAIN_STATUS_SELLOUT": "\"furry\" kodunu kullan! (#ad)", 23 | "MAIN_DESC_TITLE": "Açıklama", 24 | "MAIN_STATS_TITLE": "İstatistikler", 25 | "MAIN_STATS_CPU": "CPU", 26 | "MAIN_STATS_RAM": "RAM", 27 | "MAIN_STATS_READ": "Oku", 28 | "MAIN_STATS_WRITE": "Yaz", 29 | "MAIN_STATS_PROVIDE": "Sağlamak", 30 | "MAIN_STATS_DOWNLOAD": "İndir", 31 | "MAIN_STATS_LATENCY": "Gecikme", 32 | "MAIN_STATS_THREADS": "İş Parçacığı", 33 | "MAIN_PROG_VERIFY": "Doğrulanıyor", 34 | "MAIN_EXIT_VETOMSG": "Fortnite hala çalışıyor! Yine de oyundan çıkmak istiyormusun?", 35 | "MAIN_EXIT_VETOTITLE": "Şu anda Çalışıyor", 36 | "MAIN_BTN_UPDATE": "Güncelle", 37 | "MAIN_NOTIF_TITLE": "Yeni Fortnite Güncellemesi!", 38 | "MAIN_NOTIF_DESC": "%s artık kullanılabilir!", 39 | "MAIN_NOTIF_ACTION": "Güncellemek için Tıkla", 40 | "MAIN_PROG_UPDATE": "Güncelleniyor", 41 | "PROG_LABEL_ELAPSED": "Geçen", 42 | "PROG_LABEL_ETA": "TVS", 43 | "PROG_BTN_CANCEL": "İptal", 44 | "PROG_BTN_CANCELLING": "İptal ediliyor", 45 | "SETUP_TITLE": "Kurulum", 46 | "SETUP_COMP_LEVEL_FASTEST": "En hızlı", 47 | "SETUP_COMP_LEVEL_FAST": "Hızlı", 48 | "SETUP_COMP_LEVEL_NORMAL": "Normal", 49 | "SETUP_COMP_LEVEL_SLOW": "Yavaş", 50 | "SETUP_COMP_LEVEL_SLOWEST": "En yavaş", 51 | "SETUP_UPDATE_LEVEL_SEC1": "1 saniye", 52 | "SETUP_UPDATE_LEVEL_SEC5": "5 saniye", 53 | "SETUP_UPDATE_LEVEL_SEC10": "10 saniye", 54 | "SETUP_UPDATE_LEVEL_SEC30": "30 saniye", 55 | "SETUP_UPDATE_LEVEL_MIN1": "1 dakika", 56 | "SETUP_UPDATE_LEVEL_MIN5": "5 dakika", 57 | "SETUP_UPDATE_LEVEL_MIN10": "10 dakika", 58 | "SETUP_UPDATE_LEVEL_MIN30": "30 dakika", 59 | "SETUP_UPDATE_LEVEL_HOUR1": "1 saat", 60 | "SETUP_GENERAL_LABEL": "Genel", 61 | "SETUP_GENERAL_INSTFOLDER": "Klasör Yükle", 62 | "SETUP_GENERAL_COMPMETHOD": "Sıkıştırılma Yöntemi", 63 | "SETUP_GENERAL_COMPLEVEL": "Sıkıştırılma Seviyesi", 64 | "SETUP_GENERAL_UPDATEINT": "Güncelleme Aralığı", 65 | "SETUP_ADVANCED_LABEL": "Gelişmiş", 66 | "SETUP_ADVANCED_BUFCT": "Arabellek Sayısı", 67 | "SETUP_ADVANCED_THDCT": "İş Parçacığı Sayısı", 68 | "SETUP_ADVANCED_CMDARGS": "Komut Argümanları", 69 | "SETUP_BTN_OK": "Tamam", 70 | "SETUP_BTN_CANCEL": "İptal", 71 | 72 | "APP_ERROR_NETWORK": "EGL2 was unable to connect to the internet. Maybe you have a VPN or proxy?", 73 | "APP_ERROR_OODLE_LZMA": "An LZMA error occurred when trying to grab Oodle.", 74 | "APP_ERROR_OODLE_INDEX": "EGL2 was unable to grab Warframe's file index.", 75 | "APP_ERROR_OODLE_LOAD": "EGL2 was unable to load Oodle's DLL.", 76 | "APP_ERROR_OODLE_WRITE": "EGL2 was unable to write Oodle's DLL.", 77 | "APP_ERROR_OODLE_UNKNOWN": "An unknown error occurred when trying to load Oodle's DLL: %d", 78 | "APP_ERROR_OODLE_INCOMPAT": "EGL2 is using an incompatible Oodle DLL. SDK: %08X, DLL: %08X", 79 | "SETUP_COMP_METHOD_DECOMP": "Sıkıştırılmamış", 80 | "SETUP_COMP_METHOD_ZSTD": "Zstandard", 81 | "SETUP_COMP_METHOD_LZ4": "LZ4", 82 | "SETUP_COMP_METHOD_SELKIE": "Oodle Selkie" 83 | } -------------------------------------------------------------------------------- /localetool/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Format: 3 | 4 | STRING FORMAT: 5 | uint16_t prefixed length (in bytes, not characters) 6 | string data (utf8?) 7 | 8 | FILE FORMAT: 9 | just a bunch of strings back to back, parser knows the locale of the string and how many are per locale 10 | 11 | */ 12 | 13 | #define LOCTEXT_FOLDER "../../../locales/" 14 | #define LOCDATA_FOLDER "../../../locales/out/" 15 | 16 | #include "../gui/Localization.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | std::wstring strconv(const char* utf8str) 29 | { 30 | std::wstring_convert> wconv; 31 | return wconv.from_bytes(utf8str); 32 | } 33 | 34 | // length in characters, not bytes 35 | inline void WriteString(std::ostringstream& ostr, const wchar_t* data, uint16_t length) { 36 | ostr.write((char*)&length, 2); 37 | ostr.write((char*)data, length * 2); 38 | } 39 | 40 | #define LS(name) #name, 41 | static constexpr const char* jsonKeys[] = { LOCALESTRINGS }; 42 | #undef LS 43 | 44 | rapidjson::Document fallbackLocale; 45 | inline void SetFallbackLocale(const char* infile) { 46 | auto localePtr = fopen(infile, "rb"); 47 | 48 | char readBuffer[8192]; 49 | rapidjson::FileReadStream is(localePtr, readBuffer, sizeof(readBuffer)); 50 | fallbackLocale.ParseStream(is); 51 | 52 | fclose(localePtr); 53 | 54 | if (fallbackLocale.HasParseError()) { 55 | printf("COULD NOT PARSE FALLBACK: JSON Parse Error %d @ %zu\n", fallbackLocale.GetParseError(), fallbackLocale.GetErrorOffset()); 56 | } 57 | } 58 | 59 | inline void WriteLocale(std::ostringstream& ostr, FILE* localePtr, const char* lang) { 60 | char readBuffer[8192]; 61 | rapidjson::FileReadStream is(localePtr, readBuffer, sizeof(readBuffer)); 62 | rapidjson::Document d; 63 | d.ParseStream(is); 64 | if (d.HasParseError()) { 65 | printf("COULD NOT PARSE %s: JSON Parse Error %d @ %zu\n", lang, d.GetParseError(), d.GetErrorOffset()); 66 | return; 67 | } 68 | 69 | for (int i = 0; i < (int)LocaleString::Count; ++i) { 70 | const char* v; 71 | if (!d.HasMember(jsonKeys[i])) { 72 | printf("%s DOES NOT HAVE %s\n", lang, jsonKeys[i]); 73 | v = fallbackLocale[jsonKeys[i]].GetString(); 74 | } 75 | else { 76 | v = d[jsonKeys[i]].GetString(); 77 | } 78 | auto val = strconv(v); 79 | WriteString(ostr, val.c_str(), val.size()); 80 | } 81 | } 82 | 83 | inline void WriteLocale(const char* infile, const char* outfile, const char* lang) { 84 | std::string odata; 85 | { 86 | std::ostringstream ostr; 87 | auto inpF = fopen(infile, "rb"); 88 | if (inpF) { 89 | printf("Parsing %s\n", lang); 90 | WriteLocale(ostr, inpF, lang); 91 | fclose(inpF); 92 | } 93 | else { 94 | printf("COULD NOT OPEN %s\n", lang); 95 | } 96 | odata = ostr.str(); 97 | } 98 | auto obuf = std::unique_ptr(new char[ZSTD_COMPRESSBOUND(odata.size())]); 99 | auto osize = ZSTD_compress(obuf.get(), ZSTD_COMPRESSBOUND(odata.size()), odata.data(), odata.size(), ZSTD_maxCLevel()); 100 | 101 | std::ofstream outF(outfile, std::ios::out | std::ios::binary | std::ios::trunc); 102 | auto dsize = (uint32_t)odata.size(); 103 | outF.write((char*)&dsize, sizeof(uint32_t)); 104 | outF.write(obuf.get(), osize); 105 | outF.close(); 106 | } 107 | 108 | int main(int argc, char* argv[]) { 109 | SetFallbackLocale(LOCTEXT_FOLDER "EN" ".json"); 110 | 111 | #define LS(name) WriteLocale(LOCTEXT_FOLDER #name ".json", LOCDATA_FOLDER #name ".loc", #name); 112 | LOCALETYPES 113 | #undef LS 114 | } -------------------------------------------------------------------------------- /resources.rc: -------------------------------------------------------------------------------- 1 | APP_ICON ICON "icon.ico" 2 | SPLASH_ICON ICON "splash.ico" 3 | 4 | #include "gui/localedefs.h" 5 | #define STR(N) #N 6 | #define LS(name) LOCALE_##name RCDATA STR(locales/out/##name##.loc) 7 | LOCALETYPES 8 | 9 | CHM_HELP RCDATA "help/help.chm" 10 | 11 | #include 12 | 13 | #include "gui/versioninfo.h" 14 | 15 | VS_VERSION_INFO VERSIONINFO 16 | FILEVERSION 1, 0, 0, 0 17 | PRODUCTVERSION VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, 0 18 | { 19 | BLOCK "StringFileInfo" 20 | { 21 | BLOCK "040904b0" 22 | { 23 | VALUE "Comments", "EGL2" 24 | VALUE "CompanyName", "WorkingRobot" 25 | 26 | VALUE "FileDescription", "EGL2" 27 | VALUE "FileVersion", "1.0.0.0" 28 | VALUE "InternalName", "egl2" 29 | VALUE "LegalCopyright", "EGL2 (c) by Aleks Margarian (WorkingRobot)" 30 | 31 | VALUE "OriginalFilename", "EGL2.exe" 32 | VALUE "ProductName", "EGL2" 33 | VALUE "ProductVersion", VERSION_STRING ".0" 34 | } 35 | } 36 | BLOCK "VarFileInfo" 37 | { 38 | VALUE "Translation", 0x409, 1200 39 | } 40 | } -------------------------------------------------------------------------------- /splash.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WorkingRobot/EGL2/35cc6d5bd7e85a037e10c3668fac504ef3a61a82/splash.ico -------------------------------------------------------------------------------- /storage/EGSProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../web/manifest/manifest.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace fs = std::filesystem; 9 | 10 | class EGSProvider { 11 | public: 12 | inline static bool Available() { 13 | return GetInstance().available(); 14 | } 15 | 16 | inline static bool IsChunkAvailable(std::shared_ptr& Chunk) { 17 | return GetInstance().isChunkAvailable(Chunk); 18 | } 19 | 20 | inline static std::shared_ptr GetChunk(std::shared_ptr& Chunk) { 21 | return GetInstance().getChunk(Chunk); 22 | } 23 | 24 | private: 25 | inline static EGSProvider& GetInstance() { 26 | static EGSProvider instance; 27 | return instance; 28 | } 29 | 30 | EGSProvider(EGSProvider const&) = delete; 31 | EGSProvider& operator=(EGSProvider const&) = delete; 32 | 33 | EGSProvider(); 34 | ~EGSProvider(); 35 | 36 | bool available(); 37 | 38 | bool isChunkAvailable(std::shared_ptr& Chunk); 39 | 40 | std::shared_ptr getChunk(std::shared_ptr& Chunk); 41 | 42 | fs::path InstallDir; 43 | std::unique_ptr Build; 44 | 45 | static constexpr auto guidHash = [](const char* n) { return (*((uint64_t*)n)) ^ (*(((uint64_t*)n) + 1)); }; 46 | static constexpr auto guidEqual = [](const char* a, const char* b) {return !memcmp(a, b, 16); }; 47 | typedef std::unordered_map MANIFEST_CHUNK_LOOKUP; 48 | MANIFEST_CHUNK_LOOKUP ChunkLookup; 49 | }; -------------------------------------------------------------------------------- /storage/compression.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template 13 | class CtxManager { 14 | public: 15 | typedef std::function create_ctx; 16 | typedef std::function delete_ctx; 17 | 18 | CtxManager(create_ctx create, delete_ctx delete_) : 19 | CreateCtx(create), 20 | DeleteCtx(delete_) { } 21 | 22 | ~CtxManager() { 23 | for (auto& ctx : Ctx) { 24 | DeleteCtx(ctx); 25 | } 26 | } 27 | 28 | T& GetCtx(std::unique_lock& lock) { 29 | std::unique_lock lck(Mutex); 30 | 31 | auto mtxIt = Mutexes.begin(); 32 | for (auto ctxIt = Ctx.begin(); ctxIt != Ctx.end(); ctxIt++, mtxIt++) { 33 | lock = std::unique_lock(*mtxIt, std::try_to_lock); 34 | if (lock.owns_lock()) { 35 | return *ctxIt; 36 | } 37 | } 38 | 39 | lock = std::unique_lock(Mutexes.emplace_back()); 40 | return Ctx.emplace_back(CreateCtx()); 41 | } 42 | 43 | private: 44 | create_ctx CreateCtx; 45 | delete_ctx DeleteCtx; 46 | 47 | std::mutex Mutex; 48 | std::deque Ctx; 49 | std::deque Mutexes; 50 | }; 51 | 52 | class Compressor { 53 | public: 54 | using buffer_value = std::pair, size_t>; 55 | 56 | Compressor(uint32_t storageFlags); 57 | ~Compressor(); 58 | 59 | buffer_value StorageCompress(std::shared_ptr buffer, size_t buffer_size); 60 | 61 | buffer_value ZlibDecompress(FILE* File, size_t& inBufSize); 62 | buffer_value ZstdDecompress(FILE* File, size_t& inBufSize); 63 | buffer_value LZ4Decompress(FILE* File, size_t& inBufSize); 64 | buffer_value OodleDecompress(FILE* File, size_t& inBufSize); 65 | 66 | private: 67 | std::function, size_t)> CompressFunc; 68 | 69 | int CLevel; 70 | 71 | std::unique_ptr> CCtx; 72 | 73 | std::unique_ptr> ZlibDCtx; 74 | std::unique_ptr> ZstdDCtx; 75 | // lz4 nor oodle decompression use a DCtx 76 | 77 | uint32_t StorageFlags; 78 | }; -------------------------------------------------------------------------------- /storage/sha.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | inline bool VerifyHash(const char* input, uint32_t inputSize, const char Sha[20]) { 7 | char calculatedHash[20]; 8 | SHA1((const uint8_t*)input, inputSize, (uint8_t*)calculatedHash); 9 | 10 | return !memcmp(Sha, calculatedHash, 20); 11 | } -------------------------------------------------------------------------------- /storage/storage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../containers/cancel_flag.h" 4 | #include "../web/http.h" 5 | #include "../web/manifest/manifest.h" 6 | #include "compression.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | namespace fs = std::filesystem; 15 | 16 | enum 17 | { 18 | StorageDecompressed = 0x00000001, // Chunks saved as raw blocks 19 | StorageZstd = 0x00000002, // Chunks are recompressed with Zlib 20 | StorageLZ4 = 0x00000003, // Chunks are recompressed with LZ4 21 | StorageSelkie = 0x00000004, // Chunks are recompressed with Oodle Selkie 22 | StorageCompMethodMask = 0x0000000F, // Compresssion method mask 23 | 24 | StorageCompressFastest = 0x00000010, 25 | StorageCompressFast = 0x00000020, 26 | StorageCompressNormal = 0x00000030, 27 | StorageCompressSlow = 0x00000040, 28 | StorageCompressSlowest = 0x00000050, 29 | StorageCompLevelMask = 0x000000F0, // Compression level mask 30 | 31 | StorageVerifyHashes = 0x00001000, // Verify SHA hashes of downloaded chunks when reading and redownload if invalid 32 | }; 33 | 34 | enum { 35 | ChunkFlagDecompressed = 0x01, 36 | ChunkFlagZstd = 0x02, 37 | ChunkFlagZlib = 0x04, 38 | ChunkFlagLZ4 = 0x08, 39 | ChunkFlagOodle = 0x09, 40 | 41 | ChunkFlagCompCount = 5, 42 | ChunkFlagCompMask = 0x0F, 43 | }; 44 | 45 | enum class CHUNK_STATUS { 46 | Unavailable, // Readable from download 47 | Grabbing, // Downloading 48 | Available, // Readable from local copy 49 | Reading, // Reading from local copy 50 | Readable // Readable from memory 51 | }; 52 | 53 | struct CHUNK_POOL_DATA { 54 | std::pair, size_t> Buffer; 55 | std::condition_variable CV; 56 | std::mutex CV_Mutex; 57 | std::atomic Status; 58 | }; 59 | 60 | auto hash = [](const char* n) { return (*((uint64_t*)n)) ^ (*(((uint64_t*)n) + 1)); }; 61 | auto equal = [](const char* a, const char* b) {return !memcmp(a, b, 16); }; 62 | // this can be an ordered map, but i'm unsure about the memory usage of this, even if all 81k chunks are read 63 | typedef std::deque> STORAGE_CHUNK_POOL_LOOKUP; 64 | 65 | class Storage { 66 | public: 67 | Storage(uint32_t Flags, uint32_t ChunkPoolCapacity, fs::path CacheLocation, std::string CloudDir); 68 | ~Storage(); 69 | 70 | bool IsChunkDownloaded(std::shared_ptr Chunk); 71 | bool IsChunkDownloaded(ChunkPart& ChunkPart); 72 | bool VerifyChunk(std::shared_ptr Chunk, cancel_flag& flag); 73 | void DeleteChunk(std::shared_ptr Chunk); 74 | std::shared_ptr GetChunk(std::shared_ptr Chunk, cancel_flag& flag); 75 | std::shared_ptr GetChunkPart(ChunkPart& ChunkPart, cancel_flag& flag); 76 | Compressor::buffer_value DownloadChunk(std::shared_ptr Chunk, cancel_flag& flag, bool forceDownload = false); 77 | bool GetChunkMetadata(std::shared_ptr Chunk, uint16_t& flags, size_t& fileSize); 78 | 79 | private: 80 | CHUNK_POOL_DATA& GetPoolData(std::shared_ptr Chunk); 81 | CHUNK_STATUS GetUnpooledChunkStatus(std::shared_ptr Chunk); 82 | bool ReadChunk(fs::path Path, Compressor::buffer_value& ReadBuffer, cancel_flag& flag); 83 | void WriteChunk(fs::path Path, uint32_t DecompressedSize, Compressor::buffer_value& Buffer); 84 | 85 | fs::path CachePath; 86 | uint32_t Flags; 87 | std::string CloudDir; // CloudDir also includes the /ChunksV3/ part, though 88 | Compressor Compressor; 89 | std::mutex ChunkPoolMutex; 90 | STORAGE_CHUNK_POOL_LOOKUP ChunkPool; 91 | uint32_t ChunkPoolCapacity; 92 | }; -------------------------------------------------------------------------------- /web/http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // fortniteIOSGameClient 4 | // the only reason this is IOS and not PC is that PC can't create device auths :( 5 | #define BASIC_FN_AUTH "basic MzQ0NmNkNzI2OTRjNGE0NDg1ZDgxYjc3YWRiYjIxNDE6OTIwOWQ0YTVlMjVhNDU3ZmI5YjA3NDg5ZDMxM2I0MWE=" 6 | 7 | #define NOMINMAX 8 | 9 | #include "http/Client.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | inline void UrlEncode(const std::string& s, std::ostringstream& e) 16 | { 17 | static constexpr const char lookup[] = "0123456789abcdef"; 18 | for (int i = 0, ix = s.size(); i < ix; i++) 19 | { 20 | const char& c = s.data()[i]; 21 | if ((48 <= c && c <= 57) ||//0-9 22 | (65 <= c && c <= 90) ||//abc...xyz 23 | (97 <= c && c <= 122) || //ABC...XYZ 24 | (c == '-' || c == '_' || c == '.' || c == '~') 25 | ) 26 | { 27 | e << c; 28 | } 29 | else 30 | { 31 | e << '%'; 32 | e << lookup[(c & 0xF0) >> 4]; 33 | e << lookup[(c & 0x0F)]; 34 | } 35 | } 36 | } 37 | 38 | typedef std::vector> UrlForm; 39 | inline std::string EncodeUrlForm(const UrlForm& formData) { 40 | std::ostringstream oss; 41 | for (auto& itr : formData) { 42 | UrlEncode(itr.first, oss); 43 | oss << "="; 44 | UrlEncode(itr.second, oss); 45 | oss << "&"; 46 | } 47 | oss.seekp(-1, std::ios_base::end); 48 | return oss.str().erase(oss.tellp()); 49 | } -------------------------------------------------------------------------------- /web/http/Client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../containers/cancel_flag.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class Client { 10 | public: 11 | Client(); 12 | ~Client(); 13 | 14 | std::error_condition StartConnection(const std::shared_ptr& connection); 15 | std::error_condition AbortConnection(const std::shared_ptr& connection); 16 | 17 | // -1: default 18 | static inline void SetPoolSize(long poolSize) { 19 | PoolSize = poolSize == -1 ? DefaultPoolSize : poolSize; 20 | } 21 | 22 | static inline std::shared_ptr CreateConnection() { 23 | auto conn = std::make_shared(); 24 | curl_easy_setopt(conn->GetHandle(), CURLOPT_MAXCONNECTS, PoolSize.load()); 25 | //conn->SetProxy("127.0.0.1:8888"); 26 | //conn->SetVerifyCertificate(false); 27 | return conn; 28 | } 29 | 30 | static bool Execute(const std::shared_ptr& connection, cancel_flag& flag, bool allowNon200 = false); 31 | 32 | private: 33 | static constexpr long DefaultPoolSize = 5; // curl default 34 | static inline std::atomic_long PoolSize = DefaultPoolSize; 35 | 36 | static FILE* CreateTempFile(); 37 | 38 | void* io_service; 39 | std::unique_ptr connection_manager; 40 | }; -------------------------------------------------------------------------------- /web/manifest/auth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "manifest.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace fs = std::filesystem; 9 | 10 | class ManifestAuth { 11 | public: 12 | ManifestAuth(fs::path& cachePath); 13 | ~ManifestAuth(); 14 | 15 | std::pair ManifestAuth::GetLatestManifest(); 16 | std::string GetManifestId(const std::string& Url); 17 | bool IsManifestCached(const std::string& Url); 18 | Manifest GetManifest(const std::string& Url); 19 | 20 | private: 21 | void UpdateIfExpired(bool force = false); 22 | 23 | fs::path CachePath; 24 | 25 | std::string AccessToken; 26 | time_t ExpiresAt; 27 | }; -------------------------------------------------------------------------------- /web/manifest/chunk.cpp: -------------------------------------------------------------------------------- 1 | #include "chunk.h" 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | std::string Chunk::GetGuid() { 7 | char GuidBuffer[33]; 8 | sprintf(GuidBuffer, "%016llX%016llX", ntohll(*(uint64_t*)Guid), ntohll(*(uint64_t*)(Guid + 8))); 9 | return GuidBuffer; 10 | } 11 | 12 | std::string Chunk::GetFilePath() { 13 | char PathBuffer[53]; 14 | sprintf(PathBuffer, "FF/%016llX%016llX", ntohll(*(uint64_t*)Guid), ntohll(*(uint64_t*)(Guid + 8))); 15 | memcpy(PathBuffer, PathBuffer + 3, 2); 16 | return PathBuffer; 17 | } 18 | 19 | std::string Chunk::GetUrl() { 20 | char UrlBuffer[59]; 21 | sprintf(UrlBuffer, "%02d/%016llX_%016llX%016llX.chunk", Group, Hash, ntohll(*(uint64_t*)Guid), ntohll(*(uint64_t*)(Guid + 8))); 22 | return UrlBuffer; 23 | } -------------------------------------------------------------------------------- /web/manifest/chunk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Chunk { 6 | char Guid[16]; 7 | uint64_t Hash; 8 | char ShaHash[20]; 9 | uint8_t Group; 10 | uint32_t WindowSize; // amount of data the chunk provides 11 | uint64_t FileSize; // total chunk file size 12 | 13 | std::string GetGuid(); 14 | std::string GetFilePath(); 15 | std::string GetUrl(); 16 | }; -------------------------------------------------------------------------------- /web/manifest/chunk_part.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "chunk.h" 4 | 5 | #include 6 | 7 | struct ChunkPart { 8 | std::shared_ptr Chunk; 9 | uint32_t Offset; 10 | uint32_t Size; 11 | }; -------------------------------------------------------------------------------- /web/manifest/feature_level.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class EFeatureLevel : int32_t 6 | { 7 | // The original version. 8 | Original = 0, 9 | // Support for custom fields. 10 | CustomFields, 11 | // Started storing the version number. 12 | StartStoringVersion, 13 | // Made after data files where renamed to include the hash value, these chunks now go to ChunksV2. 14 | DataFileRenames, 15 | // Manifest stores whether build was constructed with chunk or file data. 16 | StoresIfChunkOrFileData, 17 | // Manifest stores group number for each chunk/file data for reference so that external readers don't need to know how to calculate them. 18 | StoresDataGroupNumbers, 19 | // Added support for chunk compression, these chunks now go to ChunksV3. NB: Not File Data Compression yet. 20 | ChunkCompressionSupport, 21 | // Manifest stores product prerequisites info. 22 | StoresPrerequisitesInfo, 23 | // Manifest stores chunk download sizes. 24 | StoresChunkFileSizes, 25 | // Manifest can optionally be stored using UObject serialization and compressed. 26 | StoredAsCompressedUClass, 27 | // These two features were removed and never used. 28 | UNUSED_0, 29 | UNUSED_1, 30 | // Manifest stores chunk data SHA1 hash to use in place of data compare, for faster generation. 31 | StoresChunkDataShaHashes, 32 | // Manifest stores Prerequisite Ids. 33 | StoresPrerequisiteIds, 34 | // The first minimal binary format was added. UObject classes will no longer be saved out when binary selected. 35 | StoredAsBinaryData, 36 | // Temporary level where manifest can reference chunks with dynamic window size, but did not serialize them. Chunks from here onwards are stored in ChunksV4. 37 | VariableSizeChunksWithoutWindowSizeChunkInfo, 38 | // Manifest can reference chunks with dynamic window size, and also serializes them. 39 | VariableSizeChunks, 40 | // Manifest stores a unique build id for exact matching of build data. 41 | StoresUniqueBuildId, 42 | 43 | // !! Always after the latest version entry, signifies the latest version plus 1 to allow the following Latest alias. 44 | LatestPlusOne, 45 | // An alias for the actual latest version value. 46 | Latest = (LatestPlusOne - 1), 47 | // An alias to provide the latest version of a manifest supported by file data (nochunks). 48 | LatestNoChunks = StoresChunkFileSizes, 49 | // An alias to provide the latest version of a manifest supported by a json serialized format. 50 | LatestJson = StoresPrerequisiteIds, 51 | // An alias to provide the first available version of optimised delta manifest saving. 52 | FirstOptimisedDelta = StoresUniqueBuildId, 53 | 54 | // JSON manifests were stored with a version of 255 during a certain CL range due to a bug. 55 | // We will treat this as being StoresChunkFileSizes in code. 56 | BrokenJsonVersion = 255, 57 | // This is for UObject default, so that we always serialize it. 58 | Invalid = -1 59 | }; -------------------------------------------------------------------------------- /web/manifest/file.cpp: -------------------------------------------------------------------------------- 1 | #include "file.h" 2 | 3 | #include 4 | 5 | uint64_t File::GetFileSize() { 6 | return std::accumulate(ChunkParts.begin(), ChunkParts.end(), 0ull, 7 | [](uint64_t sum, const ChunkPart& curr) { 8 | return sum + curr.Size; 9 | }); 10 | } 11 | 12 | bool File::GetChunkIndex(uint64_t Offset, uint32_t& ChunkIndex, uint32_t& ChunkOffset) 13 | { 14 | for (ChunkIndex = 0; ChunkIndex < ChunkParts.size(); ++ChunkIndex) { 15 | if (Offset < ChunkParts[ChunkIndex].Size) { 16 | ChunkOffset = Offset; 17 | return true; 18 | } 19 | Offset -= ChunkParts[ChunkIndex].Size; 20 | } 21 | return false; 22 | } 23 | -------------------------------------------------------------------------------- /web/manifest/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "chunk_part.h" 4 | 5 | #include 6 | #include 7 | 8 | struct File { 9 | std::string FileName; 10 | char ShaHash[20]; 11 | std::vector ChunkParts; 12 | 13 | uint64_t GetFileSize(); 14 | 15 | bool GetChunkIndex(uint64_t Offset, uint32_t& ChunkIndex, uint32_t& ChunkOffset); 16 | }; -------------------------------------------------------------------------------- /web/manifest/manifest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "feature_level.h" 4 | #include "file.h" 5 | 6 | #include 7 | #include 8 | 9 | struct Manifest { 10 | public: 11 | Manifest(const rapidjson::Document& jsonData, const std::string& url); 12 | Manifest(FILE* binaryData); 13 | ~Manifest(); 14 | 15 | uint64_t GetDownloadSize(); 16 | uint64_t GetInstallSize(); 17 | 18 | EFeatureLevel FeatureLevel; 19 | bool bIsFileData; 20 | uint32_t AppID; 21 | std::string AppName; 22 | std::string BuildVersion; 23 | std::string LaunchExe; 24 | std::string LaunchCommand; 25 | //std::set PrereqIds; 26 | //std::string PrereqName; 27 | //std::string PrereqPath; 28 | //std::string PrereqArgs; 29 | std::vector FileManifestList; 30 | std::vector> ChunkManifestList; 31 | 32 | std::string CloudDir; 33 | }; -------------------------------------------------------------------------------- /web/personal/DeviceCodeAuth.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceCodeAuth.h" 2 | 3 | #ifndef LOG_SECTION 4 | #define LOG_SECTION "DevCodeAuth" 5 | #endif 6 | 7 | #include "../../Logger.h" 8 | 9 | #include 10 | #include 11 | 12 | DeviceCodeAuth::DeviceCodeAuth(DevCodeCallback setupCb) 13 | { 14 | std::string accessToken; 15 | { 16 | auto tokenConn = Client::CreateConnection(); 17 | 18 | tokenConn->SetUrl("https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token"); 19 | tokenConn->SetUsePost(true); 20 | tokenConn->AddRequestHeader("Authorization", BASIC_FN_AUTH); 21 | tokenConn->AddRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 22 | 23 | tokenConn->SetRequestBody("grant_type=client_credentials"); 24 | 25 | if (!Client::Execute(tokenConn, cancel_flag())) { 26 | LOG_ERROR("Getting access token: failed"); 27 | return; 28 | } 29 | 30 | rapidjson::Document d; 31 | d.Parse(tokenConn->GetResponseBody().c_str()); 32 | if (d.HasParseError()) { 33 | LOG_ERROR("Getting access token: JSON Parse Error %d @ %zu", d.GetParseError(), d.GetErrorOffset()); 34 | return; 35 | } 36 | 37 | accessToken = d["access_token"].GetString(); 38 | } 39 | 40 | std::string deviceCode; 41 | { 42 | auto deviceConn = Client::CreateConnection(); 43 | 44 | deviceConn->SetUrl("https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/deviceAuthorization"); 45 | deviceConn->SetUsePost(true); 46 | deviceConn->AddRequestHeader("Authorization", "bearer " + accessToken); 47 | 48 | if (!Client::Execute(deviceConn, cancel_flag())) { 49 | LOG_ERROR("Init device code token: failed"); 50 | return; 51 | } 52 | 53 | rapidjson::Document d; 54 | d.Parse(deviceConn->GetResponseBody().c_str()); 55 | if (d.HasParseError()) { 56 | LOG_ERROR("Init device code: JSON Parse Error %d @ %zu", d.GetParseError(), d.GetErrorOffset()); 57 | return; 58 | } 59 | 60 | setupCb(d["verification_uri_complete"].GetString(), d["user_code"].GetString()); 61 | deviceCode = d["device_code"].GetString(); 62 | } 63 | 64 | while (1) { 65 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 66 | 67 | auto deviceConn = Client::CreateConnection(); 68 | 69 | deviceConn->SetUrl("https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token"); 70 | deviceConn->SetUsePost(true); 71 | deviceConn->AddRequestHeader("Authorization", BASIC_FN_AUTH); 72 | deviceConn->AddRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 73 | 74 | UrlForm form; 75 | form.emplace_back("grant_type", "device_code"); 76 | form.emplace_back("device_code", deviceCode); 77 | deviceConn->SetRequestBody(EncodeUrlForm(form)); 78 | 79 | if (!Client::Execute(deviceConn, cancel_flag(), true)) { 80 | LOG_ERROR("Attempt device code: failed"); 81 | continue; 82 | } 83 | 84 | switch (deviceConn->GetResponseCode()) { 85 | case 200: 86 | OAuthResult = deviceConn->GetResponseBody(); 87 | return; 88 | case 400: 89 | break; 90 | default: 91 | LOG_ERROR("Attempt device code: Response code %d", deviceConn->GetResponseCode()); 92 | break; 93 | } 94 | } 95 | } 96 | 97 | DeviceCodeAuth::~DeviceCodeAuth() 98 | { 99 | 100 | } -------------------------------------------------------------------------------- /web/personal/DeviceCodeAuth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../http.h" 4 | 5 | #include 6 | 7 | typedef std::function DevCodeCallback; 8 | 9 | class DeviceCodeAuth { 10 | public: 11 | DeviceCodeAuth(DevCodeCallback setupCb); 12 | ~DeviceCodeAuth(); 13 | 14 | inline const std::string& GetResult() { 15 | return OAuthResult; 16 | } 17 | 18 | private: 19 | std::string OAuthResult; 20 | }; -------------------------------------------------------------------------------- /web/personal/PersonalAuth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../http.h" 4 | #include "DeviceCodeAuth.h" 5 | 6 | #include 7 | 8 | namespace fs = std::filesystem; 9 | 10 | struct DeviceAuthData { 11 | std::string AccountId; 12 | std::string DeviceId; 13 | std::string Secret; 14 | }; 15 | 16 | class PersonalAuth { 17 | public: 18 | PersonalAuth(const fs::path& filePath, DevCodeCallback devSetupCb); 19 | ~PersonalAuth(); 20 | 21 | bool GetExchangeCode(std::string& code); 22 | void Recreate(); 23 | 24 | private: 25 | void UpdateIfExpired(bool force = false); 26 | 27 | bool UseOAuthResp(const std::string& data); 28 | bool UseRefreshToken(const std::string& token); 29 | bool UseDeviceAuth(const DeviceAuthData& auth); 30 | 31 | bool CreateDeviceAuth(DeviceAuthData& auth); 32 | 33 | static bool SaveDeviceAuth(const fs::path& filePath, const DeviceAuthData& auth); 34 | static bool ReadDeviceAuth(const fs::path& filePath, DeviceAuthData& auth); 35 | 36 | std::string AccountId; 37 | std::string DisplayName; 38 | std::string AccessToken; 39 | std::string RefreshToken; 40 | time_t ExpiresAt; 41 | 42 | fs::path FilePath; 43 | DevCodeCallback DevSetupCb; 44 | }; --------------------------------------------------------------------------------