mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
@@ -1,6 +1,6 @@
|
||||
enum InjectableDll {
|
||||
console,
|
||||
cobalt,
|
||||
sinum,
|
||||
reboot,
|
||||
memory
|
||||
}
|
||||
|
||||
@@ -6,13 +6,16 @@ import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
bool _watcher = false;
|
||||
final File rebootDllFile = File("${dllsDirectory.path}\\reboot.dll");
|
||||
const String kRebootDownloadUrl =
|
||||
final File rebootBeforeS20DllFile = File("${dllsDirectory.path}\\reboot.dll");
|
||||
final File rebootAboveS20DllFile = File("${dllsDirectory.path}\\rebootS20.dll");
|
||||
const String kRebootBelowS20DownloadUrl =
|
||||
"http://nightly.link/Milxnor/Project-Reboot-3.0/workflows/msbuild/master/Release.zip";
|
||||
const String kRebootAboveS20DownloadUrl =
|
||||
"http://nightly.link/Milxnor/Project-Reboot-3.0/workflows/msbuild/aboveS20/Release.zip";
|
||||
|
||||
Future<bool> hasRebootDllUpdate(int? lastUpdateMs, {int hours = 24, bool force = false}) async {
|
||||
final lastUpdate = await _getLastUpdate(lastUpdateMs);
|
||||
final exists = await rebootDllFile.exists();
|
||||
final exists = await rebootBeforeS20DllFile.exists() && await rebootAboveS20DllFile.exists();
|
||||
final now = DateTime.now();
|
||||
return force || !exists || (hours > 0 && lastUpdate != null && now.difference(lastUpdate).inHours > hours);
|
||||
}
|
||||
@@ -28,9 +31,8 @@ Future<void> downloadCriticalDll(String name, String outputPath) async {
|
||||
await output.writeAsBytes(response.bodyBytes, flush: true);
|
||||
}
|
||||
|
||||
Future<int> downloadRebootDll(String url) async {
|
||||
Future<void> downloadRebootDll(File file, String url) async {
|
||||
Directory? outputDir;
|
||||
final now = DateTime.now();
|
||||
try {
|
||||
final response = await http.get(Uri.parse(url));
|
||||
if(response.statusCode != 200) {
|
||||
@@ -42,8 +44,7 @@ Future<int> downloadRebootDll(String url) async {
|
||||
await tempZip.writeAsBytes(response.bodyBytes, flush: true);
|
||||
await extractFileToDisk(tempZip.path, outputDir.path);
|
||||
final rebootDll = File(outputDir.listSync().firstWhere((element) => path.extension(element.path) == ".dll").path);
|
||||
await rebootDllFile.writeAsBytes(await rebootDll.readAsBytes(), flush: true);
|
||||
return now.millisecondsSinceEpoch;
|
||||
await file.writeAsBytes(await rebootDll.readAsBytes(), flush: true);
|
||||
} finally{
|
||||
if(outputDir != null) {
|
||||
delete(outputDir);
|
||||
@@ -63,7 +64,7 @@ Stream<String> watchDlls() async* {
|
||||
}
|
||||
|
||||
_watcher = true;
|
||||
await for(final event in rebootDllFile.parent.watch(events: FileSystemEvent.delete | FileSystemEvent.move)) {
|
||||
await for(final event in dllsDirectory.watch(events: FileSystemEvent.delete | FileSystemEvent.move)) {
|
||||
if (event.path.endsWith(".dll")) {
|
||||
yield event.path;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ environment:
|
||||
sdk: ">=3.0.0 <=4.0.0"
|
||||
|
||||
dependencies:
|
||||
dio: ^5.3.2
|
||||
win32: 3.0.0
|
||||
ffi: ^2.1.0
|
||||
path: ^1.8.3
|
||||
http: ^1.1.0
|
||||
crypto: ^3.0.2
|
||||
archive: ^3.3.7
|
||||
dio: ^5.7.0
|
||||
win32: ^5.5.4
|
||||
ffi: ^2.1.3
|
||||
path: ^1.9.0
|
||||
http: ^1.2.2
|
||||
crypto: ^3.0.5
|
||||
archive: ^3.6.1
|
||||
ini: ^2.1.0
|
||||
shelf_proxy: ^1.0.2
|
||||
sync: ^0.3.0
|
||||
@@ -22,4 +22,4 @@ dependencies:
|
||||
version: ^3.0.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_lints: ^2.0.1
|
||||
flutter_lints: ^5.0.0
|
||||
BIN
gui/dependencies/dlls/sinum.dll
Normal file
BIN
gui/dependencies/dlls/sinum.dll
Normal file
Binary file not shown.
@@ -98,11 +98,13 @@
|
||||
"settingsServerTypeDescription": "The type of game server to inject",
|
||||
"settingsServerTypeEmbeddedName": "Embedded",
|
||||
"settingsServerTypeCustomName": "Custom",
|
||||
"settingsServerFileName": "Implementation",
|
||||
"settingsOldServerFileName": "Game server (Before Fortnite Season 20)",
|
||||
"settingsNewServerFileName": "Game server (After Fortnite Season 20)",
|
||||
"settingsServerFileDescription": "The file injected to create the game server",
|
||||
"settingsServerPortName": "Port",
|
||||
"settingsServerPortDescription": "The port the launcher expects the game server to be hosted on",
|
||||
"settingsServerMirrorName": "Update mirror",
|
||||
"settingsServerOldMirrorName": "Update mirror (Before Fortnite Season 20)",
|
||||
"settingsServerNewMirrorName": "Update mirror (After Fortnite Season 20)",
|
||||
"settingsServerMirrorDescription": "The URL used to update the game server dll",
|
||||
"settingsServerMirrorPlaceholder": "mirror",
|
||||
"settingsServerTimerName": "Update timer",
|
||||
@@ -368,5 +370,7 @@
|
||||
"automaticGameServerDialogStart": "Start server",
|
||||
"gameResetDefaultsName": "Reset",
|
||||
"gameResetDefaultsDescription": "Resets the game's settings to their default values",
|
||||
"gameResetDefaultsContent": "Reset"
|
||||
"gameResetDefaultsContent": "Reset",
|
||||
"selectFile": "Select a file",
|
||||
"reset": "Reset"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:flutter_gen/gen_l10n/reboot_localizations.dart';
|
||||
@@ -19,9 +20,9 @@ import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/error.dart';
|
||||
import 'package:reboot_launcher/src/page/implementation/home_page.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/url_protocol.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:system_theme/system_theme.dart';
|
||||
import 'package:url_protocol/url_protocol.dart';
|
||||
import 'package:version/version.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
@@ -146,7 +147,7 @@ Future<Object?> _initVersion() async {
|
||||
|
||||
Future<Object?> _initUrlHandler() async {
|
||||
try {
|
||||
registerProtocolHandler(kCustomUrlSchema, arguments: ['%s']);
|
||||
registerUrlProtocol(kCustomUrlSchema, arguments: ['%s']);
|
||||
return null;
|
||||
}catch(error) {
|
||||
return error;
|
||||
|
||||
@@ -4,15 +4,11 @@ import 'dart:io';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:version/version.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class DllController extends GetxController {
|
||||
static const String storageName = "dll_storage";
|
||||
@@ -25,7 +21,8 @@ class DllController extends GetxController {
|
||||
late final TextEditingController memoryLeakDll;
|
||||
late final TextEditingController gameServerPort;
|
||||
late final Rx<UpdateTimer> timer;
|
||||
late final TextEditingController url;
|
||||
late final TextEditingController beforeS20Mirror;
|
||||
late final TextEditingController aboveS20Mirror;
|
||||
late final RxBool customGameServer;
|
||||
late final RxnInt timestamp;
|
||||
late final Rx<UpdateStatus> status;
|
||||
@@ -43,8 +40,10 @@ class DllController extends GetxController {
|
||||
final timerIndex = _storage?.read("timer");
|
||||
timer = Rx(timerIndex == null ? UpdateTimer.hour : UpdateTimer.values.elementAt(timerIndex));
|
||||
timer.listen((value) => _storage?.write("timer", value.index));
|
||||
url = TextEditingController(text: _storage?.read("update_url") ?? kRebootDownloadUrl);
|
||||
url.addListener(() => _storage?.write("update_url", url.text));
|
||||
beforeS20Mirror = TextEditingController(text: _storage?.read("update_url") ?? kRebootBelowS20DownloadUrl);
|
||||
beforeS20Mirror.addListener(() => _storage?.write("update_url", beforeS20Mirror.text));
|
||||
aboveS20Mirror = TextEditingController(text: _storage?.read("old_update_url") ?? kRebootAboveS20DownloadUrl);
|
||||
aboveS20Mirror.addListener(() => _storage?.write("new_update_url", aboveS20Mirror.text));
|
||||
status = Rx(UpdateStatus.waiting);
|
||||
customGameServer = RxBool(_storage?.read("custom_game_server") ?? false);
|
||||
customGameServer.listen((value) => _storage?.write("custom_game_server", value));
|
||||
@@ -68,7 +67,8 @@ class DllController extends GetxController {
|
||||
void resetServer() {
|
||||
gameServerPort.text = kDefaultGameServerPort;
|
||||
timer.value = UpdateTimer.hour;
|
||||
url.text = kRebootDownloadUrl;
|
||||
beforeS20Mirror.text = kRebootBelowS20DownloadUrl;
|
||||
aboveS20Mirror.text = kRebootAboveS20DownloadUrl;
|
||||
status.value = UpdateStatus.waiting;
|
||||
customGameServer.value = false;
|
||||
timestamp.value = null;
|
||||
@@ -109,7 +109,15 @@ class DllController extends GetxController {
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
timestamp.value = await downloadRebootDll(url.text);
|
||||
await Future.wait(
|
||||
[
|
||||
downloadRebootDll(rebootBeforeS20DllFile, beforeS20Mirror.text),
|
||||
downloadRebootDll(rebootAboveS20DllFile, aboveS20Mirror.text),
|
||||
Future.delayed(const Duration(seconds: 1))
|
||||
],
|
||||
eagerError: false
|
||||
);
|
||||
timestamp.value = DateTime.now().millisecondsSinceEpoch;
|
||||
status.value = UpdateStatus.success;
|
||||
infoBarEntry?.close();
|
||||
if(!silent) {
|
||||
@@ -126,15 +134,18 @@ class DllController extends GetxController {
|
||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||
error = error.toLowerCase();
|
||||
status.value = UpdateStatus.error;
|
||||
showRebootInfoBar(
|
||||
translations.downloadDllError("reboot.dll", error.toString()),
|
||||
infoBarEntry = showRebootInfoBar(
|
||||
translations.downloadDllError(error.toString(), "reboot.dll"),
|
||||
duration: infoBarLongDuration,
|
||||
severity: InfoBarSeverity.error,
|
||||
action: Button(
|
||||
onPressed: () => updateGameServerDll(
|
||||
force: true,
|
||||
silent: silent
|
||||
),
|
||||
onPressed: () async {
|
||||
infoBarEntry?.close();
|
||||
updateGameServerDll(
|
||||
force: true,
|
||||
silent: silent
|
||||
);
|
||||
},
|
||||
child: Text(translations.downloadDllRetry),
|
||||
)
|
||||
);
|
||||
@@ -155,7 +166,7 @@ class DllController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
return (rebootDllFile, false);
|
||||
return (rebootBeforeS20DllFile, false);
|
||||
case InjectableDll.console:
|
||||
final ue4ConsoleFile = File(unrealEngineConsoleDll.text);
|
||||
return (ue4ConsoleFile, canonicalize(ue4ConsoleFile.path) != defaultPath);
|
||||
@@ -215,7 +226,7 @@ class DllController extends GetxController {
|
||||
error = error.toLowerCase();
|
||||
final completer = Completer();
|
||||
await showRebootInfoBar(
|
||||
translations.downloadDllError(fileName, error.toString()),
|
||||
translations.downloadDllError(error.toString(), fileName),
|
||||
duration: infoBarLongDuration,
|
||||
severity: InfoBarSeverity.error,
|
||||
onDismissed: () => completer.complete(null),
|
||||
|
||||
@@ -56,20 +56,36 @@ class HostingController extends GetxController {
|
||||
published = RxBool(false);
|
||||
showPassword = RxBool(false);
|
||||
instance = Rxn();
|
||||
final supabase = Supabase.instance.client;
|
||||
servers = Rxn();
|
||||
supabase.from("hosting_v2")
|
||||
.stream(primaryKey: ['id'])
|
||||
.map((event) => event.map((element) => FortniteServer.fromJson(element)).where((element) => element.ip.isNotEmpty).toSet())
|
||||
.listen((event) {
|
||||
servers.value = event;
|
||||
published.value = event.any((element) => element.id == uuid);
|
||||
});
|
||||
_listenServers();
|
||||
customLaunchArgs = TextEditingController(text: _storage?.read("custom_launch_args") ?? "");
|
||||
customLaunchArgs.addListener(() => _storage?.write("custom_launch_args", customLaunchArgs.text));
|
||||
_semaphore = Semaphore();
|
||||
}
|
||||
|
||||
void _listenServers([int attempt = 0]) {
|
||||
log("[SUPABASE] Listening...");
|
||||
final supabase = Supabase.instance.client;
|
||||
supabase.from("hosting_v2")
|
||||
.stream(primaryKey: ['id'])
|
||||
.map((event) => event.map((element) => FortniteServer.fromJson(element)).where((element) => element.ip.isNotEmpty).toSet())
|
||||
.listen(
|
||||
_onNewServer,
|
||||
onError: (error) async {
|
||||
log("[SUPABASE] Error: ${error}");
|
||||
await Future.delayed(Duration(seconds: attempt * 5));
|
||||
_listenServers(attempt + 1);
|
||||
},
|
||||
cancelOnError: true
|
||||
);
|
||||
}
|
||||
|
||||
void _onNewServer(Set<FortniteServer> event) {
|
||||
log("[SUPABASE] New event: ${event}");
|
||||
servers.value = event;
|
||||
published.value = event.any((element) => element.id == uuid);
|
||||
}
|
||||
|
||||
Future<void> publishServer(String author, String version) async {
|
||||
try {
|
||||
_semaphore.acquire();
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/info_bar.dart';
|
||||
@@ -21,7 +19,6 @@ class SettingsController extends GetxController {
|
||||
late final RxString language;
|
||||
late final Rx<ThemeMode> themeMode;
|
||||
late final RxBool firstRun;
|
||||
late final RxBool debug;
|
||||
late double width;
|
||||
late double height;
|
||||
late double? offsetX;
|
||||
@@ -39,7 +36,6 @@ class SettingsController extends GetxController {
|
||||
language.listen((value) => _storage?.write("language", value));
|
||||
firstRun = RxBool(_storage?.read("first_run_tutorial") ?? true);
|
||||
firstRun.listen((value) => _storage?.write("first_run_tutorial", value));
|
||||
debug = RxBool(false);
|
||||
}
|
||||
|
||||
void saveWindowSize(Size size) {
|
||||
|
||||
@@ -300,7 +300,7 @@ class _DialogButtonState extends State<DialogButton> {
|
||||
|
||||
Widget get _primaryButton => Button(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: ButtonState.all(FluentTheme.of(context).accentColor)
|
||||
backgroundColor: WidgetStateProperty.all(FluentTheme.of(context).accentColor)
|
||||
),
|
||||
onPressed: widget.onTap!,
|
||||
child: Text(widget.text!),
|
||||
@@ -308,7 +308,7 @@ class _DialogButtonState extends State<DialogButton> {
|
||||
|
||||
Widget get _secondaryButton => Button(
|
||||
style: widget.color != null ? ButtonStyle(
|
||||
backgroundColor: ButtonState.all(widget.color!)
|
||||
backgroundColor: WidgetStateProperty.all(widget.color!)
|
||||
) : null,
|
||||
onPressed: widget.onTap ?? _onDefaultSecondaryActionTap,
|
||||
child: Text(widget.text ?? translations.defaultDialogSecondaryAction),
|
||||
|
||||
@@ -18,9 +18,12 @@ void onError(Object exception, StackTrace? stackTrace, bool framework) {
|
||||
}
|
||||
|
||||
lastError = exception.toString();
|
||||
final route = ModalRoute.of(pageKey.currentContext!);
|
||||
if(route != null && !route.isCurrent){
|
||||
Navigator.of(pageKey.currentContext!).pop(false);
|
||||
if(inDialog){
|
||||
final context = pageKey.currentContext;
|
||||
if(context != null) {
|
||||
Navigator.of(context).pop(false);
|
||||
inDialog = false;
|
||||
}
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => showRebootDialog(
|
||||
|
||||
@@ -62,7 +62,7 @@ void _promptPlayVersion() {
|
||||
onTap: () async {
|
||||
onClose();
|
||||
if(!hasBuilds) {
|
||||
await VersionSelector.openDownloadDialog(closable: false);
|
||||
await VersionSelector.openDownloadDialog();
|
||||
}
|
||||
_promptServerBrowserPage();
|
||||
}
|
||||
@@ -339,7 +339,7 @@ Widget _buildActionButton({
|
||||
required void Function() onTap,
|
||||
}) => Button(
|
||||
style: themed ? ButtonStyle(
|
||||
backgroundColor: ButtonState.all(FluentTheme.of(context).accentColor)
|
||||
backgroundColor: WidgetStateProperty.all(FluentTheme.of(context).accentColor)
|
||||
) : null,
|
||||
child: Text(label),
|
||||
onPressed: onTap
|
||||
|
||||
@@ -56,8 +56,8 @@ Future<bool> showProfileForm(BuildContext context) async{
|
||||
suffix: Button(
|
||||
onPressed: () => showPassword.value = !showPassword.value,
|
||||
style: ButtonStyle(
|
||||
shape: ButtonState.all(const CircleBorder()),
|
||||
backgroundColor: ButtonState.all(Colors.transparent)
|
||||
shape: WidgetStateProperty.all(const CircleBorder()),
|
||||
backgroundColor: WidgetStateProperty.all(Colors.transparent)
|
||||
),
|
||||
child: Icon(
|
||||
showPassword.value ? Icons.visibility_off : Icons.visibility,
|
||||
|
||||
@@ -257,8 +257,8 @@ extension ServerControllerDialog on BackendController {
|
||||
suffix: !showPasswordTrailing.value ? null : Button(
|
||||
onPressed: () => showPassword.value = !showPassword.value,
|
||||
style: ButtonStyle(
|
||||
shape: ButtonState.all(const CircleBorder()),
|
||||
backgroundColor: ButtonState.all(Colors.transparent)
|
||||
shape: WidgetStateProperty.all(const CircleBorder()),
|
||||
backgroundColor: WidgetStateProperty.all(Colors.transparent)
|
||||
),
|
||||
child: Icon(
|
||||
showPassword.value ? FluentIcons.eye_off_24_regular : FluentIcons.eye_24_regular
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:reboot_launcher/src/util/types.dart';
|
||||
import 'package:reboot_launcher/src/widget/file_selector.dart';
|
||||
import 'package:universal_disk_space/universal_disk_space.dart';
|
||||
import 'package:windows_taskbar/windows_taskbar.dart';
|
||||
@@ -41,6 +42,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
|
||||
SendPort? _downloadPort;
|
||||
Object? _error;
|
||||
StackTrace? _stackTrace;
|
||||
bool _selecting = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -102,7 +104,12 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
|
||||
return ErrorDialog(
|
||||
exception: _error ?? Exception(translations.unknownError),
|
||||
stackTrace: _stackTrace,
|
||||
errorMessageBuilder: (exception) => translations.downloadVersionError(exception.toString())
|
||||
errorMessageBuilder: (exception) {
|
||||
var error = exception.toString();
|
||||
error = error.after("Error: ")?.replaceAll(":", ",") ?? error.after(": ") ?? error;
|
||||
error = error.toLowerCase();
|
||||
return translations.downloadVersionError(error);
|
||||
}
|
||||
);
|
||||
case _DownloadStatus.done:
|
||||
return InfoDialog(
|
||||
@@ -274,7 +281,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFormBody(List<FortniteBuild> builds) => Column(
|
||||
Widget _buildFormBody(List<FortniteBuild> builds) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -292,7 +300,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
|
||||
windowTitle: _source.value == _BuildSource.local ? translations.gameFolderPlaceWindowTitle : translations.buildInstallationDirectoryWindowTitle,
|
||||
controller: _pathController,
|
||||
validator: _source.value == _BuildSource.local ? _checkGameFolder : _checkDownloadDestination,
|
||||
folder: true
|
||||
folder: true,
|
||||
allowNavigator: true
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
@@ -300,6 +309,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String? _checkGameFolder(text) {
|
||||
if (text == null || text.isEmpty) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/onboard.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page_type.dart';
|
||||
@@ -35,7 +34,6 @@ abstract class RebootPageState<T extends RebootPage> extends State<T> with Autom
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildFirstLaunchInfo(),
|
||||
_buildDebugInfo(),
|
||||
Expanded(
|
||||
child: _listView
|
||||
)
|
||||
@@ -47,7 +45,6 @@ abstract class RebootPageState<T extends RebootPage> extends State<T> with Autom
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildFirstLaunchInfo(),
|
||||
_buildDebugInfo(),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
@@ -99,35 +96,6 @@ abstract class RebootPageState<T extends RebootPage> extends State<T> with Autom
|
||||
);
|
||||
});
|
||||
|
||||
Widget _buildDebugInfo() => Obx(() {
|
||||
if(!_settingsController.debug.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 8.0
|
||||
),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: InfoBar(
|
||||
title: Text("Debug mode is enabled"),
|
||||
severity: InfoBarSeverity.warning,
|
||||
isLong: true,
|
||||
content: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text( "• Automatic dll injection is disabled\n"
|
||||
"• The game server cannot start automatically\n"
|
||||
"• The game server runs in a normal window")
|
||||
),
|
||||
onClose: () {
|
||||
_settingsController.debug.value = false;
|
||||
},
|
||||
),
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
ListView get _listView => ListView.builder(
|
||||
itemCount: settings.length,
|
||||
itemBuilder: (context, index) => settings[index],
|
||||
|
||||
@@ -5,7 +5,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/overlay.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/data.dart';
|
||||
@@ -153,9 +152,9 @@ class _BackendPageState extends RebootPageState<BackendPage> {
|
||||
contentWidth: null,
|
||||
content: Row(
|
||||
children: [
|
||||
Text(
|
||||
Obx(() => Text(
|
||||
_backendController.detached.value ? translations.on : translations.off
|
||||
),
|
||||
)),
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
|
||||
@@ -276,8 +276,8 @@ class _BrowsePageState extends RebootPageState<BrowsePage> {
|
||||
_filterControllerStream.add("");
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: ButtonState.all(Colors.transparent),
|
||||
shape: ButtonState.all(Border())
|
||||
backgroundColor: WidgetStateProperty.all(Colors.transparent),
|
||||
shape: WidgetStateProperty.all(Border())
|
||||
),
|
||||
child: _searchBarIconData
|
||||
);
|
||||
|
||||
@@ -298,48 +298,46 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: HomePage.kDefaultPadding,
|
||||
right: HomePage.kDefaultPadding * 2,
|
||||
top: HomePage.kDefaultPadding,
|
||||
bottom: HomePage.kDefaultPadding * 2
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 1000
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
_buildBodyHeader(),
|
||||
const SizedBox(height: 24.0),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
fit: StackFit.loose,
|
||||
children: [
|
||||
_buildBodyContent(),
|
||||
InfoBarArea(
|
||||
key: infoBarAreaKey
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
Widget _buildBody() => Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: HomePage.kDefaultPadding,
|
||||
right: HomePage.kDefaultPadding * 2,
|
||||
top: HomePage.kDefaultPadding,
|
||||
bottom: HomePage.kDefaultPadding * 2
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: 1000
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
_buildBodyHeader(),
|
||||
const SizedBox(height: 24.0),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
fit: StackFit.loose,
|
||||
children: [
|
||||
_buildBodyContent(),
|
||||
InfoBarArea(
|
||||
key: infoBarAreaKey
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildBodyContent() => PageView.builder(
|
||||
controller: _pageController,
|
||||
@@ -500,7 +498,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
decoration: BoxDecoration(
|
||||
color: ButtonThemeData.uncheckedInputColor(
|
||||
FluentTheme.of(context),
|
||||
pageIndex.value == index ? {ButtonStates.hovering} : states,
|
||||
pageIndex.value == index ? {WidgetState.hovered} : states,
|
||||
transparentWhenNone: true,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6.0))
|
||||
@@ -529,12 +527,12 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
stream: pagesController.stream,
|
||||
builder: (context, _) => Button(
|
||||
style: ButtonStyle(
|
||||
padding: ButtonState.all(const EdgeInsets.symmetric(
|
||||
padding: WidgetStateProperty.all(const EdgeInsets.symmetric(
|
||||
vertical: 12.0,
|
||||
horizontal: 16.0
|
||||
)),
|
||||
backgroundColor: ButtonState.all(Colors.transparent),
|
||||
shape: ButtonState.all(Border())
|
||||
backgroundColor: WidgetStateProperty.all(Colors.transparent),
|
||||
shape: WidgetStateProperty.all(Border())
|
||||
),
|
||||
onPressed: appStack.isEmpty && !inDialog ? null : () {
|
||||
if(inDialog) {
|
||||
|
||||
@@ -82,7 +82,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
List<Widget> get settings => [
|
||||
_information,
|
||||
buildVersionSelector(
|
||||
key: hostVersionOverlayTargetKey
|
||||
key: hostVersionOverlayTargetKey
|
||||
),
|
||||
_options,
|
||||
_internalFiles,
|
||||
@@ -155,8 +155,8 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
suffix: Button(
|
||||
onPressed: () => _hostingController.showPassword.value = !_hostingController.showPassword.value,
|
||||
style: ButtonStyle(
|
||||
shape: ButtonState.all(const CircleBorder()),
|
||||
backgroundColor: ButtonState.all(Colors.transparent)
|
||||
shape: WidgetStateProperty.all(const CircleBorder()),
|
||||
backgroundColor: WidgetStateProperty.all(Colors.transparent)
|
||||
),
|
||||
child: Icon(
|
||||
_hostingController.showPassword.value ? FluentIcons.eye_off_24_filled : FluentIcons.eye_24_filled,
|
||||
@@ -175,9 +175,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
contentWidth: null,
|
||||
content: Obx(() => Row(
|
||||
children: [
|
||||
Text(
|
||||
Obx(() => Text(
|
||||
_hostingController.discoverable.value ? translations.on : translations.off
|
||||
),
|
||||
)),
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
@@ -221,12 +221,11 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_settingsController.debug.value ? GameServerType.window.translatedName : _hostingController.type.value.translatedName),
|
||||
leading: Text(_hostingController.type.value.translatedName),
|
||||
items: GameServerType.values.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.translatedName),
|
||||
onPressed: () => _hostingController.type.value = entry
|
||||
)).toList(),
|
||||
disabled: _settingsController.debug.value
|
||||
)).toList()
|
||||
)),
|
||||
),
|
||||
SettingTile(
|
||||
@@ -238,9 +237,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
contentWidth: null,
|
||||
content: Row(
|
||||
children: [
|
||||
Text(
|
||||
Obx(() => Text(
|
||||
_hostingController.autoRestart.value ? translations.on : translations.off
|
||||
),
|
||||
)),
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
@@ -278,151 +277,212 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
title: Text(translations.settingsServerName),
|
||||
subtitle: Text(translations.settingsServerSubtitle),
|
||||
children: [
|
||||
SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.timer_24_regular
|
||||
),
|
||||
title: Text(translations.settingsServerTypeName),
|
||||
subtitle: Text(translations.settingsServerTypeDescription),
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
|
||||
items: {
|
||||
false: translations.settingsServerTypeEmbeddedName,
|
||||
true: translations.settingsServerTypeCustomName
|
||||
}.entries.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.value),
|
||||
onPressed: () {
|
||||
final oldValue = _dllController.customGameServer.value;
|
||||
if(oldValue == entry.key) {
|
||||
return;
|
||||
}
|
||||
_internalFilesServerType,
|
||||
_internalFilesUpdateTimer,
|
||||
_internalFilesOldServerSource,
|
||||
_internalFilesNewServerSource,
|
||||
],
|
||||
);
|
||||
|
||||
_dllController.customGameServer.value = entry.key;
|
||||
_dllController.infoBarEntry?.close();
|
||||
if(!entry.key) {
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
}
|
||||
}
|
||||
)).toList()
|
||||
))
|
||||
Widget get _internalFilesServerType => SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.games_24_regular
|
||||
),
|
||||
Obx(() {
|
||||
if(!_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
title: Text(translations.settingsServerTypeName),
|
||||
subtitle: Text(translations.settingsServerTypeDescription),
|
||||
contentWidth: SettingTile.kDefaultContentWidth + 30,
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
|
||||
items: {
|
||||
false: translations.settingsServerTypeEmbeddedName,
|
||||
true: translations.settingsServerTypeCustomName
|
||||
}.entries.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.value),
|
||||
onPressed: () {
|
||||
final oldValue = _dllController.customGameServer.value;
|
||||
if(oldValue == entry.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
return createFileSetting(
|
||||
title: translations.settingsServerFileName,
|
||||
description: translations.settingsServerFileDescription,
|
||||
controller: _dllController.gameServerDll,
|
||||
onReset: () {
|
||||
final path = _dllController.getDefaultDllPath(InjectableDll.reboot);
|
||||
_dllController.gameServerDll.text = path;
|
||||
_dllController.downloadCriticalDllInteractive(path);
|
||||
}
|
||||
);
|
||||
}),
|
||||
Obx(() {
|
||||
if(_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
_dllController.customGameServer.value = entry.key;
|
||||
_dllController.infoBarEntry?.close();
|
||||
if(!entry.key) {
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
}
|
||||
}
|
||||
)).toList()
|
||||
))
|
||||
);
|
||||
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.globe_24_regular
|
||||
),
|
||||
title: Text(translations.settingsServerMirrorName),
|
||||
subtitle: Text(translations.settingsServerMirrorDescription),
|
||||
content: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormBox(
|
||||
placeholder: translations.settingsServerMirrorPlaceholder,
|
||||
controller: _dllController.url,
|
||||
validator: _checkUpdateUrl
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Button(
|
||||
style: ButtonStyle(
|
||||
padding: ButtonState.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () => _dllController.url.text = kRebootDownloadUrl,
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}),
|
||||
Obx(() {
|
||||
if(_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.timer_24_regular
|
||||
),
|
||||
title: Text(translations.settingsServerTimerName),
|
||||
subtitle: Text(translations.settingsServerTimerSubtitle),
|
||||
content: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_dllController.timer.value.text),
|
||||
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.text),
|
||||
onPressed: () {
|
||||
_dllController.timer.value = entry;
|
||||
_dllController.infoBarEntry?.close();
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
}
|
||||
)).toList()
|
||||
)),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Button(
|
||||
style: ButtonStyle(
|
||||
padding: ButtonState.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () {
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
Widget get _internalFilesOldServerSource => Obx(() {
|
||||
if(!_dllController.customGameServer.value) {
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.globe_24_regular
|
||||
),
|
||||
title: Text(translations.settingsServerOldMirrorName),
|
||||
subtitle: Text(translations.settingsServerMirrorDescription),
|
||||
contentWidth: SettingTile.kDefaultContentWidth + 30,
|
||||
content: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormBox(
|
||||
placeholder: translations.settingsServerMirrorPlaceholder,
|
||||
controller: _dllController.beforeS20Mirror,
|
||||
onChanged: (value) {
|
||||
if(Uri.tryParse(value) != null) {
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Button(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () => _dllController.updateGameServerDll(force: true),
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_download_24_regular
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
})
|
||||
],
|
||||
);
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Button(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () {
|
||||
_dllController.beforeS20Mirror.text = kRebootBelowS20DownloadUrl;
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
},
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}else {
|
||||
return createFileSetting(
|
||||
title: translations.settingsOldServerFileName,
|
||||
description: translations.settingsServerFileDescription,
|
||||
controller: _dllController.gameServerDll,
|
||||
onReset: () {
|
||||
final path = _dllController.getDefaultDllPath(InjectableDll.reboot);
|
||||
_dllController.gameServerDll.text = path;
|
||||
_dllController.downloadCriticalDllInteractive(path);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Widget get _internalFilesNewServerSource => Obx(() {
|
||||
if(!_dllController.customGameServer.value) {
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.globe_24_regular
|
||||
),
|
||||
title: Text(translations.settingsServerNewMirrorName),
|
||||
subtitle: Text(translations.settingsServerMirrorDescription),
|
||||
contentWidth: SettingTile.kDefaultContentWidth + 30,
|
||||
content: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormBox(
|
||||
placeholder: translations.settingsServerMirrorPlaceholder,
|
||||
controller: _dllController.aboveS20Mirror,
|
||||
onChanged: (value) {
|
||||
if(Uri.tryParse(value) != null) {
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Button(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () => _dllController.updateGameServerDll(force: true),
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_download_24_regular
|
||||
),
|
||||
)
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Button(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () {
|
||||
_dllController.aboveS20Mirror.text = kRebootBelowS20DownloadUrl;
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
},
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}else {
|
||||
return createFileSetting(
|
||||
title: translations.settingsNewServerFileName,
|
||||
description: translations.settingsServerFileDescription,
|
||||
controller: _dllController.gameServerDll,
|
||||
onReset: () {
|
||||
final path = _dllController.getDefaultDllPath(InjectableDll.reboot);
|
||||
_dllController.gameServerDll.text = path;
|
||||
_dllController.downloadCriticalDllInteractive(path);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
String? _checkUpdateUrl(String? text) {
|
||||
if (text == null || text.isEmpty) {
|
||||
return translations.emptyURL;
|
||||
Widget get _internalFilesUpdateTimer => Obx(() {
|
||||
if(_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.timer_24_regular
|
||||
),
|
||||
title: Text(translations.settingsServerTimerName),
|
||||
subtitle: Text(translations.settingsServerTimerSubtitle),
|
||||
contentWidth: SettingTile.kDefaultContentWidth + 30,
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_dllController.timer.value.text),
|
||||
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.text),
|
||||
onPressed: () {
|
||||
_dllController.timer.value = entry;
|
||||
_dllController.infoBarEntry?.close();
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
}
|
||||
)).toList()
|
||||
))
|
||||
);
|
||||
});
|
||||
|
||||
SettingTile get _share => SettingTile(
|
||||
icon: Icon(
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/overlay.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/data.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/onboard.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page_type.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
@@ -38,7 +37,6 @@ class PlayPage extends RebootPage {
|
||||
}
|
||||
|
||||
class _PlayPageState extends RebootPageState<PlayPage> {
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
final DllController _dllController = Get.find<DllController>();
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/data.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page_type.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
@@ -42,7 +41,6 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
List<Widget> get settings => [
|
||||
_language,
|
||||
_theme,
|
||||
_debugMode,
|
||||
_installationDirectory,
|
||||
];
|
||||
|
||||
@@ -100,29 +98,6 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
child: Text(translations.settingsUtilsInstallationDirectoryContent),
|
||||
)
|
||||
);
|
||||
|
||||
SettingTile get _debugMode => SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.developer_board_24_regular
|
||||
),
|
||||
title: Text("Debug mode"),
|
||||
subtitle: Text("Whether the launcher should disable automatic features for troubleshooting"),
|
||||
contentWidth: null,
|
||||
content: Row(
|
||||
children: [
|
||||
Text(
|
||||
_settingsController.debug.value ? translations.on : translations.off
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
Obx(() => ToggleSwitch(
|
||||
checked: _settingsController.debug.value,
|
||||
onChanged: (value) => _settingsController.debug.value = value
|
||||
))
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
extension _ThemeModeExtension on ThemeMode {
|
||||
|
||||
@@ -24,10 +24,13 @@ bool get isWin11 {
|
||||
return intBuild != null && intBuild > 22000;
|
||||
}
|
||||
|
||||
Future<String?> openFolderPicker(String title) async =>
|
||||
await FilePicker.platform.getDirectoryPath(dialogTitle: title);
|
||||
Future<String?> openFolderPicker(String title) async {
|
||||
FilePicker.platform = FilePickerWindows();
|
||||
return await FilePicker.platform.getDirectoryPath(dialogTitle: title);
|
||||
}
|
||||
|
||||
Future<String?> openFilePicker(String extension) async {
|
||||
FilePicker.platform = FilePickerWindows();
|
||||
var result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowMultiple: false,
|
||||
@@ -93,7 +96,7 @@ class IVirtualDesktop extends IUnknown {
|
||||
throw WindowsException(code);
|
||||
}
|
||||
|
||||
return convertFromHString(result.value);
|
||||
return _convertFromHString(result.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +283,7 @@ class _IVirtualDesktopManagerInternal extends IUnknown {
|
||||
HRESULT Function(Pointer, COMObject, Int8)>>>()
|
||||
.value
|
||||
.asFunction<int Function(Pointer, COMObject, int)>()(
|
||||
ptr.ref.lpVtbl, desktop.ptr.ref, convertToHString(newName));
|
||||
ptr.ref.lpVtbl, desktop.ptr.ref, _convertToHString(newName));
|
||||
if (code != 0) {
|
||||
throw WindowsException(code);
|
||||
}
|
||||
@@ -369,7 +372,7 @@ List<int> _getHWnds(int pid, String? excludedWindowName) {
|
||||
result.ref.excluded = excludedWindowName.toNativeUtf16();
|
||||
}
|
||||
|
||||
EnumWindows(Pointer.fromFunction<EnumWindowsProc>(_filter, TRUE), result.address);
|
||||
EnumWindows(Pointer.fromFunction<WNDENUMPROC>(_filter, TRUE), result.address);
|
||||
final length = result.ref.HWndLength;
|
||||
final HWndsPointer = result.ref.HWnd;
|
||||
if(HWndsPointer == nullptr) {
|
||||
@@ -397,7 +400,7 @@ class VirtualDesktopManager {
|
||||
}
|
||||
|
||||
final hr = CoInitializeEx(
|
||||
nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
nullptr, COINIT.COINIT_APARTMENTTHREADED | COINIT.COINIT_DISABLE_OLE1DDE);
|
||||
if (FAILED(hr)) {
|
||||
throw WindowsException(hr);
|
||||
}
|
||||
@@ -468,3 +471,19 @@ class VirtualDesktopManager {
|
||||
void setDesktopName(IVirtualDesktop desktop, String newName) =>
|
||||
windowManager.setDesktopName(desktop, newName);
|
||||
}
|
||||
|
||||
String _convertFromHString(int hstring) =>
|
||||
WindowsGetStringRawBuffer(hstring, nullptr).toDartString();
|
||||
|
||||
int _convertToHString(String string) {
|
||||
final hString = calloc<HSTRING>();
|
||||
final stringPtr = string.toNativeUtf16();
|
||||
try {
|
||||
final hr = WindowsCreateString(stringPtr, string.length, hString);
|
||||
if (FAILED(hr)) throw WindowsException(hr);
|
||||
return hString.value;
|
||||
} finally {
|
||||
free(stringPtr);
|
||||
free(hString);
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,14 @@ extension IterableExtension<E> on Iterable<E> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
extension StringExtension on String {
|
||||
String? after(String leading) {
|
||||
final index = indexOf(leading);
|
||||
if(index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return substring(index + leading.length);
|
||||
}
|
||||
}
|
||||
63
gui/lib/src/util/url_protocol.dart
Normal file
63
gui/lib/src/util/url_protocol.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
final _hive = HKEY_CURRENT_USER;
|
||||
|
||||
void registerUrlProtocol(String scheme, {String? executable, List<String>? arguments}) {
|
||||
final prefix = _regPrefix(scheme);
|
||||
final capitalized = scheme[0].toUpperCase() + scheme.substring(1);
|
||||
final args = _getArguments(arguments).map((a) => _sanitize(a));
|
||||
final cmd =
|
||||
'${executable ?? Platform.resolvedExecutable} ${args.join(' ')}';
|
||||
_regCreateStringKey(_hive, prefix, '', 'URL:$capitalized');
|
||||
_regCreateStringKey(_hive, prefix, 'URL Protocol', '');
|
||||
_regCreateStringKey(_hive, prefix + '\\shell\\open\\command', '', cmd);
|
||||
}
|
||||
|
||||
void unregisterUrlProtocol(String scheme) {
|
||||
final txtKey = TEXT(_regPrefix(scheme));
|
||||
try {
|
||||
RegDeleteTree(HKEY_CURRENT_USER, txtKey);
|
||||
} finally {
|
||||
free(txtKey);
|
||||
}
|
||||
}
|
||||
|
||||
String _regPrefix(String scheme) => 'SOFTWARE\\Classes\\$scheme';
|
||||
|
||||
int _regCreateStringKey(int hKey, String key, String valueName, String data) {
|
||||
final txtKey = TEXT(key);
|
||||
final txtValue = TEXT(valueName);
|
||||
final txtData = TEXT(data);
|
||||
try {
|
||||
return RegSetKeyValue(
|
||||
hKey,
|
||||
txtKey,
|
||||
txtValue,
|
||||
REG_VALUE_TYPE.REG_SZ,
|
||||
txtData,
|
||||
txtData.length * 2 + 2,
|
||||
);
|
||||
} finally {
|
||||
free(txtKey);
|
||||
free(txtValue);
|
||||
free(txtData);
|
||||
}
|
||||
}
|
||||
|
||||
String _sanitize(String value) {
|
||||
value = value.replaceAll(r'%s', '%1').replaceAll(r'"', '\\"');
|
||||
return '"$value"';
|
||||
}
|
||||
|
||||
List<String> _getArguments(List<String>? arguments) {
|
||||
if (arguments == null) return ['%s'];
|
||||
|
||||
if (arguments.isEmpty && !arguments.any((e) => e.contains('%s'))) {
|
||||
throw ArgumentError('arguments must contain at least 1 instance of "%s"');
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
@@ -19,10 +19,10 @@ class FileSelector extends StatefulWidget {
|
||||
required this.controller,
|
||||
required this.validator,
|
||||
required this.folder,
|
||||
required this.allowNavigator,
|
||||
this.label,
|
||||
this.extension,
|
||||
this.validatorMode,
|
||||
this.allowNavigator = true,
|
||||
Key? key})
|
||||
: assert(folder || extension != null, "Missing extension for file selector"),
|
||||
super(key: key);
|
||||
|
||||
@@ -1,70 +1,120 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart' as fluentIcons show FluentIcons;
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:reboot_launcher/src/widget/file_selector.dart';
|
||||
import 'package:reboot_launcher/src/widget/setting_tile.dart';
|
||||
|
||||
const double _kButtonDimensions = 30;
|
||||
const double _kButtonSpacing = 8;
|
||||
|
||||
SettingTile createFileSetting({required String title, required String description, required TextEditingController controller, required void Function() onReset}) {
|
||||
final obx = RxString(controller.text);
|
||||
controller.addListener(() => obx.value = controller.text);
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.document_24_regular
|
||||
),
|
||||
title: Text(title),
|
||||
subtitle: Text(description),
|
||||
content: Row(
|
||||
final selecting = RxBool(false);
|
||||
return SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.document_24_regular
|
||||
),
|
||||
title: Text(title),
|
||||
subtitle: Text(description),
|
||||
contentWidth: SettingTile.kDefaultContentWidth + _kButtonDimensions,
|
||||
content: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: FileSelector(
|
||||
placeholder: translations.selectPathPlaceholder,
|
||||
windowTitle: translations.selectPathWindowTitle,
|
||||
controller: controller,
|
||||
validator: _checkDll,
|
||||
extension: "dll",
|
||||
folder: false,
|
||||
validatorMode: AutovalidateMode.always
|
||||
),
|
||||
Expanded(
|
||||
child: FileSelector(
|
||||
placeholder: translations.selectPathPlaceholder,
|
||||
windowTitle: translations.selectPathWindowTitle,
|
||||
controller: controller,
|
||||
validator: _checkDll,
|
||||
extension: "dll",
|
||||
folder: false,
|
||||
validatorMode: AutovalidateMode.always,
|
||||
allowNavigator: false,
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
Obx(() => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
|
||||
),
|
||||
),
|
||||
const SizedBox(width: _kButtonSpacing),
|
||||
Obx(() => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
|
||||
),
|
||||
child: Tooltip(
|
||||
message: translations.selectFile,
|
||||
child: Button(
|
||||
style: ButtonStyle(
|
||||
padding: ButtonState.all(EdgeInsets.zero)
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: () => _onPressed(selecting, controller),
|
||||
child: SizedBox.square(
|
||||
dimension: _kButtonDimensions,
|
||||
child: Icon(
|
||||
fluentIcons.FluentIcons.open_folder_horizontal
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: _kButtonSpacing),
|
||||
Obx(() => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
|
||||
),
|
||||
child: Tooltip(
|
||||
message: translations.reset,
|
||||
child: Button(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
||||
),
|
||||
onPressed: onReset,
|
||||
child: SizedBox.square(
|
||||
dimension: 30,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
dimension: _kButtonDimensions,
|
||||
child: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
)
|
||||
),
|
||||
))
|
||||
),
|
||||
))
|
||||
],
|
||||
)
|
||||
);
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void _onPressed(RxBool selecting, TextEditingController controller) {
|
||||
if(selecting.value){
|
||||
return;
|
||||
}
|
||||
|
||||
selecting.value = true;
|
||||
compute(openFilePicker, "dll")
|
||||
.then((value) => _updateText(controller, value))
|
||||
.then((_) => selecting.value = false);
|
||||
}
|
||||
|
||||
void _updateText(TextEditingController controller, String? value) {
|
||||
final text = value ?? controller.text;
|
||||
controller.text = text;
|
||||
controller.selection = TextSelection.collapsed(offset: text.length);
|
||||
}
|
||||
|
||||
String? _checkDll(String? text) {
|
||||
if (text == null || text.isEmpty) {
|
||||
return translations.invalidDllPath;
|
||||
}
|
||||
if (text == null || text.isEmpty) {
|
||||
return translations.invalidDllPath;
|
||||
}
|
||||
|
||||
final file = File(text);
|
||||
if (!file.existsSync()) {
|
||||
return translations.dllDoesNotExist;
|
||||
}
|
||||
final file = File(text);
|
||||
if (!file.existsSync()) {
|
||||
return translations.dllDoesNotExist;
|
||||
}
|
||||
|
||||
if (!text.endsWith(".dll")) {
|
||||
return translations.invalidDllExtension;
|
||||
}
|
||||
if (!text.endsWith(".dll")) {
|
||||
return translations.invalidDllExtension;
|
||||
}
|
||||
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
return;
|
||||
}
|
||||
log("[${host ? 'HOST' : 'GAME'}] Backend works");
|
||||
final serverType = _settingsController.debug.value ? GameServerType.window : _hostingController.type.value;
|
||||
final serverType = _hostingController.type.value;
|
||||
log("[${host ? 'HOST' : 'GAME'}] Implicit game server metadata: headless($serverType)");
|
||||
final linkedHostingInstance = await _startMatchMakingServer(version, host, serverType, false);
|
||||
log("[${host ? 'HOST' : 'GAME'}] Implicit game server result: $linkedHostingInstance");
|
||||
@@ -159,11 +159,6 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(_settingsController.debug.value) {
|
||||
log("[${host ? 'HOST' : 'GAME'}] The user is on debug mode, not asking for auto server");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!forceLinkedHosting && _backendController.type.value == ServerType.embedded && !isLocalHost(_backendController.gameServerAddress.text)) {
|
||||
log("[${host ? 'HOST' : 'GAME'}] Backend is not set to embedded and/or not pointing to the local game server");
|
||||
return null;
|
||||
@@ -248,7 +243,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}else{
|
||||
_gameController.instance.value = instance;
|
||||
}
|
||||
await _injectOrShowError(InjectableDll.cobalt, host);
|
||||
await _injectOrShowError(InjectableDll.sinum, host);
|
||||
log("[${host ? 'HOST' : 'GAME'}] Finished creating game instance");
|
||||
return instance;
|
||||
}
|
||||
@@ -280,13 +275,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
line: line,
|
||||
host: host,
|
||||
onShutdown: () => _onStop(reason: _StopReason.normal),
|
||||
onTokenError: () {
|
||||
if(_settingsController.debug.value) {
|
||||
log("[PROCESS] Ignoring token error because debug mode is on");
|
||||
}else {
|
||||
_onStop(reason: _StopReason.tokenError);
|
||||
}
|
||||
},
|
||||
onTokenError: () => _onStop(reason: _StopReason.tokenError),
|
||||
onBuildCorrupted: () {
|
||||
if(instance == null) {
|
||||
return;
|
||||
@@ -687,10 +676,6 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
|
||||
log("[${hosting ? 'HOST' : 'GAME'}] Trying to inject ${injectable.name}...");
|
||||
if(_settingsController.debug.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
await injectDll(gameProcess, dllPath);
|
||||
instance.injectedDlls.add(injectable);
|
||||
log("[${hosting ? 'HOST' : 'GAME'}] Injected ${injectable.name}");
|
||||
@@ -742,7 +727,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
loading: true,
|
||||
duration: null,
|
||||
action: Obx(() {
|
||||
if(_settingsController.debug.value || _hostingController.started.value || linkedHosting) {
|
||||
if(_hostingController.started.value || linkedHosting) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,19 +24,22 @@ class InfoBarAreaState extends State<InfoBarArea> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Obx(() => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: hasPageButton ? 72.0 : 16.0
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _children.value.map((child) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 12.0
|
||||
),
|
||||
child: child
|
||||
)).toList(growable: false)
|
||||
),
|
||||
));
|
||||
Widget build(BuildContext context) => StreamBuilder(
|
||||
stream: pagesController.stream,
|
||||
builder: (context, _) => Obx(() => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: hasPageButton ? 72.0 : 16.0
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _children.value.map((child) => Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 12.0
|
||||
),
|
||||
child: child
|
||||
)).toList(growable: false)
|
||||
),
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -15,11 +15,11 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
class VersionSelector extends StatefulWidget {
|
||||
const VersionSelector({Key? key}) : super(key: key);
|
||||
|
||||
static Future<void> openDownloadDialog({bool closable = true}) => showRebootDialog<bool>(
|
||||
static Future<void> openDownloadDialog() => showRebootDialog<bool>(
|
||||
builder: (context) => AddVersionDialog(
|
||||
closable: closable,
|
||||
closable: true,
|
||||
),
|
||||
dismissWithEsc: closable
|
||||
dismissWithEsc: true
|
||||
);
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: reboot_launcher
|
||||
description: Graphical User Interface for Project Reboot
|
||||
version: "9.2.6"
|
||||
version: "9.2.7"
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
@@ -17,53 +17,50 @@ dependencies:
|
||||
path: ./../common
|
||||
|
||||
# Windows UI 3
|
||||
fluent_ui: ^4.8.7
|
||||
fluent_ui: ^4.9.1
|
||||
flutter_acrylic:
|
||||
path: ./dependencies/flutter_acrylic
|
||||
fluentui_system_icons: ^1.1.238
|
||||
system_theme: ^2.0.0
|
||||
fluentui_system_icons: ^1.1.258
|
||||
system_theme: ^3.1.1
|
||||
skeletons:
|
||||
git:
|
||||
url: https://github.com/talok/skeletons
|
||||
ref: main
|
||||
|
||||
# Window management
|
||||
bitsdojo_window: ^0.1.5
|
||||
window_manager: ^0.3.8
|
||||
bitsdojo_window: ^0.1.6
|
||||
window_manager: ^0.4.2
|
||||
|
||||
# Extract zip archives (for example the reboot.zip)
|
||||
archive: ^3.3.1
|
||||
archive: ^3.6.1
|
||||
|
||||
# Cryptographic functions
|
||||
crypto: ^3.0.2
|
||||
bcrypt: ^1.1.3
|
||||
pointycastle: ^3.7.3
|
||||
pointycastle: ^3.9.1
|
||||
|
||||
# Async helpers
|
||||
async: ^2.8.2
|
||||
async: ^2.11.0
|
||||
sync: ^0.3.0
|
||||
|
||||
# State management
|
||||
get: ^4.6.5
|
||||
get: ^4.6.6
|
||||
|
||||
# Native utilities
|
||||
clipboard: ^0.1.3
|
||||
app_links: ^6.0.2
|
||||
url_protocol: ^1.0.0
|
||||
app_links: ^6.3.2
|
||||
windows_taskbar: ^1.1.2
|
||||
file_picker: ^8.0.3
|
||||
url_launcher: ^6.1.5
|
||||
file_picker: ^8.1.2
|
||||
url_launcher: ^6.3.0
|
||||
local_notifier: ^0.1.6
|
||||
|
||||
# Server browser
|
||||
supabase_flutter: ^2.5.2
|
||||
uuid: ^3.0.6
|
||||
supabase_flutter: ^2.7.0
|
||||
dart_ipify: ^1.1.1
|
||||
|
||||
# Storage
|
||||
get_storage: ^2.0.3
|
||||
get_storage: ^2.1.1
|
||||
universal_disk_space: ^0.2.3
|
||||
path: ^1.8.3
|
||||
path: ^1.9.0
|
||||
|
||||
# Translations
|
||||
intl: any
|
||||
@@ -71,23 +68,17 @@ dependencies:
|
||||
|
||||
# Auto updater
|
||||
yaml: ^3.1.2
|
||||
package_info_plus: ^8.0.0
|
||||
package_info_plus: ^8.0.2
|
||||
version: ^3.0.2
|
||||
|
||||
# Validate profile
|
||||
email_validator: ^3.0.0
|
||||
|
||||
dependency_overrides:
|
||||
xml: ^6.3.0
|
||||
http: ^0.13.5
|
||||
win32: ^3.0.0
|
||||
ffi: ^2.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter_lints: ^4.0.0
|
||||
flutter_lints: ^5.0.0
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
13
redirect/Core/Constants.h
Normal file
13
redirect/Core/Constants.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Constants
|
||||
{
|
||||
constexpr auto API_URL = L"http://localhost:3551";
|
||||
|
||||
constexpr auto ProcessRequest = L"Could not set libcurl options for easy handle, processing HTTP request failed. Increase verbosity for additional information.";
|
||||
constexpr auto ProcessRequest_C2 = L"STAT_FCurlHttpRequest_ProcessRequest";
|
||||
constexpr auto URLOffset = L"ProcessRequest failed. URL '%s' is not a valid HTTP request. %p";
|
||||
constexpr auto Realloc = L"AbilitySystem.Debug.NextTarget";
|
||||
}
|
||||
11
redirect/Core/Core.cpp
Normal file
11
redirect/Core/Core.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#include "Core.h"
|
||||
|
||||
void Core::Init()
|
||||
{
|
||||
FMemory::_Realloc = Memcury::Scanner::FindStringRef(Constants::Realloc)
|
||||
.ScanFor({ Memcury::ASM::MNEMONIC::CALL })
|
||||
.RelativeOffset(1)
|
||||
.GetAs<decltype(FMemory::_Realloc)>();
|
||||
}
|
||||
16
redirect/Core/Core.h
Normal file
16
redirect/Core/Core.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "..\Utilities\memcury.h"
|
||||
|
||||
#include "Constants.h"
|
||||
|
||||
#include "Unreal\Memory.h"
|
||||
#include "Unreal\Array.h"
|
||||
#include "Unreal\String.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
void Init();
|
||||
}
|
||||
143
redirect/Core/Unreal/Array.h
Normal file
143
redirect/Core/Unreal/Array.h
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "Memory.h"
|
||||
|
||||
template <typename T>
|
||||
class TArray
|
||||
{
|
||||
friend class FString;
|
||||
|
||||
T* Data;
|
||||
int32_t NumElements;
|
||||
int32_t MaxElements;
|
||||
|
||||
public:
|
||||
|
||||
inline TArray()
|
||||
{
|
||||
Data = nullptr;
|
||||
NumElements = 0;
|
||||
MaxElements = 0;
|
||||
};
|
||||
|
||||
inline void Free()
|
||||
{
|
||||
FMemory::Free(Data);
|
||||
Data = nullptr;
|
||||
NumElements = 0;
|
||||
MaxElements = 0;
|
||||
}
|
||||
|
||||
inline void Reset()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
inline auto GetData()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
inline int GetCount() const
|
||||
{
|
||||
return NumElements;
|
||||
}
|
||||
|
||||
inline int Num() const
|
||||
{
|
||||
return NumElements;
|
||||
}
|
||||
|
||||
inline auto& Get(const int Index)
|
||||
{
|
||||
return Data[Index];
|
||||
}
|
||||
|
||||
inline auto& First()
|
||||
{
|
||||
return Get(0);
|
||||
}
|
||||
|
||||
inline auto GetRef(const int Index, int Size = sizeof(T))
|
||||
{
|
||||
return (T*)((uint8_t*)Data + (Index * Size));
|
||||
}
|
||||
|
||||
inline T& operator[](int i)
|
||||
{
|
||||
return Get(i);
|
||||
};
|
||||
|
||||
inline const T& operator[](int i) const
|
||||
{
|
||||
return Get(i);
|
||||
};
|
||||
|
||||
inline bool Remove(const int Index, int Size = sizeof(T))
|
||||
{
|
||||
if (Index < NumElements)
|
||||
{
|
||||
if (Index != NumElements - 1)
|
||||
Get(Index) = Get(NumElements - 1);
|
||||
|
||||
--NumElements;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
inline bool Any(std::function<bool(T)> Func)
|
||||
{
|
||||
for (int i = 0; i < NumElements; ++i)
|
||||
{
|
||||
if (Func(Get(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline T Select(std::function<bool(T)> Func)
|
||||
{
|
||||
for (int i = 0; i < NumElements; ++i)
|
||||
{
|
||||
if (Func(Get(i)))
|
||||
return Get(i);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline void ForEach(std::function<void(T)> Func)
|
||||
{
|
||||
for (int i = 0; i < NumElements; ++i)
|
||||
{
|
||||
Func(Get(i));
|
||||
}
|
||||
}
|
||||
|
||||
inline int Count(std::function<bool(T)> Func)
|
||||
{
|
||||
int Num = 0;
|
||||
|
||||
for (int i = 0; i < NumElements; ++i)
|
||||
{
|
||||
if (Func(Get(i)))
|
||||
Num++;
|
||||
}
|
||||
return Num;
|
||||
}
|
||||
|
||||
inline int Find(const T& Item)
|
||||
{
|
||||
for (int i = 0; i < NumElements; i++)
|
||||
{
|
||||
if (this->operator[](i) == Item)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
92
redirect/Core/Unreal/Memory.h
Normal file
92
redirect/Core/Unreal/Memory.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
class FMemory
|
||||
{
|
||||
public:
|
||||
static inline void* (*_Realloc)(void*, size_t, int64_t);
|
||||
|
||||
static void Free(void* Data)
|
||||
{
|
||||
_Realloc(Data, 0, 0);
|
||||
}
|
||||
|
||||
static void* Malloc(size_t Size)
|
||||
{
|
||||
return _Realloc(0, Size, 0);
|
||||
}
|
||||
|
||||
static void* Realloc(void* Data, size_t NewSize)
|
||||
{
|
||||
return _Realloc(Data, NewSize, 0);
|
||||
}
|
||||
|
||||
static void* Memmove(void* Dest, const void* Src, size_t Count)
|
||||
{
|
||||
return memmove(Dest, Src, Count);
|
||||
}
|
||||
|
||||
static int Memcmp(const void* Buf1, const void* Buf2, size_t Count)
|
||||
{
|
||||
return memcmp(Buf1, Buf2, Count);
|
||||
}
|
||||
|
||||
static void* Memset(void* Dest, uint8_t Char, size_t Count)
|
||||
{
|
||||
return memset(Dest, Char, Count);
|
||||
}
|
||||
|
||||
template< class T >
|
||||
static void Memset(T& Src, uint8_t ValueToSet)
|
||||
{
|
||||
Memset(&Src, ValueToSet, sizeof(T));
|
||||
}
|
||||
|
||||
static void* Memzero(void* Dest, size_t Count)
|
||||
{
|
||||
return ZeroMemory(Dest, Count);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void Memzero(T& Src)
|
||||
{
|
||||
Memzero(&Src, sizeof(T));
|
||||
}
|
||||
|
||||
static void* Memcpy(void* Dest, const void* Src, size_t Count)
|
||||
{
|
||||
return memcpy(Dest, Src, Count);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void Memcpy(T& Dest, const T& Src)
|
||||
{
|
||||
Memcpy(&Dest, &Src, sizeof(T));
|
||||
}
|
||||
|
||||
static void* Calloc(size_t NumElements, size_t ElementSize)
|
||||
{
|
||||
auto TotalSize = NumElements * ElementSize;
|
||||
auto Data = FMemory::Malloc(TotalSize);
|
||||
|
||||
if (!Data)
|
||||
return NULL;
|
||||
|
||||
FMemory::Memzero(Data, TotalSize);
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
static char* Strdup(const char* Str)
|
||||
{
|
||||
auto StrLen = strlen(Str) + 1;
|
||||
auto StrDup = (char*)FMemory::Malloc(StrLen);
|
||||
|
||||
FMemory::Memcpy(StrDup, Str, StrLen);
|
||||
|
||||
return StrDup;
|
||||
}
|
||||
};
|
||||
54
redirect/Core/Unreal/String.h
Normal file
54
redirect/Core/Unreal/String.h
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
#include "Array.h"
|
||||
#include "Memory.h"
|
||||
|
||||
class FString : private TArray<wchar_t>
|
||||
{
|
||||
public:
|
||||
|
||||
inline FString()
|
||||
{
|
||||
Data = nullptr;
|
||||
NumElements = 0;
|
||||
MaxElements = 0;
|
||||
}
|
||||
|
||||
inline FString(const char* Other)
|
||||
{
|
||||
if (Other)
|
||||
{
|
||||
auto NumCharacters = (int)std::strlen(Other);
|
||||
MaxElements = NumElements = NumCharacters + 1;
|
||||
|
||||
Data = static_cast<wchar_t*>(FMemory::Malloc(NumElements * sizeof(wchar_t)));
|
||||
|
||||
size_t ConvertedChars = 0;
|
||||
mbstowcs_s(&ConvertedChars, Data, NumElements, Other, _TRUNCATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaxElements = NumElements = 0;
|
||||
Data = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
inline FString(const wchar_t* Other)
|
||||
{
|
||||
MaxElements = NumElements = *Other ? (int)std::wcslen(Other) + 1 : 0;
|
||||
|
||||
if (NumElements && Other)
|
||||
{
|
||||
Data = static_cast<wchar_t*>(FMemory::Malloc(NumElements * 2));
|
||||
|
||||
memcpy_s(Data, NumElements * 2, Other, NumElements * 2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline auto c_str()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
};
|
||||
21
redirect/Main.cpp
Normal file
21
redirect/Main.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
static void Main()
|
||||
{
|
||||
Sleep(7500);
|
||||
|
||||
Core::Init();
|
||||
Sinum::Init();
|
||||
}
|
||||
|
||||
bool DllMain(HMODULE hModule, DWORD dwReason, void* lpReserved)
|
||||
{
|
||||
if (dwReason == DLL_PROCESS_ATTACH)
|
||||
{
|
||||
Windows::Thread::Create(Main);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
5
redirect/README.md
Normal file
5
redirect/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Sinum Windows
|
||||
|
||||
https://github.com/projectnovafn/Sinum/tree/main/Windows
|
||||
|
||||
Modified to point to http://localhost:3551
|
||||
22
redirect/Sinum.sln
Normal file
22
redirect/Sinum.sln
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.7.34031.279
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sinum", "Sinum.vcxproj", "{E7291B57-1B5B-497C-9C2E-C78A556A06CF}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E7291B57-1B5B-497C-9C2E-C78A556A06CF}.Release|x64.ActiveCfg = Release|x64
|
||||
{E7291B57-1B5B-497C-9C2E-C78A556A06CF}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {7975A2E1-0078-4AF8-AB10-0A71112857A5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
162
redirect/Sinum.vcxproj
Normal file
162
redirect/Sinum.vcxproj
Normal file
@@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{e7291b57-1b5b-497c-9c2e-c78a556a06cf}</ProjectGuid>
|
||||
<RootNamespace>Sinum</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;SINUM_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Core\Constants.h" />
|
||||
<ClInclude Include="Core\Unreal\Array.h" />
|
||||
<ClInclude Include="Core\Core.h" />
|
||||
<ClInclude Include="Core\Unreal\Memory.h" />
|
||||
<ClInclude Include="Core\Unreal\String.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
<ClInclude Include="Sinum\Sinum.h" />
|
||||
<ClInclude Include="Utilities\memcury.h" />
|
||||
<ClInclude Include="Utilities\Windows.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Core\Core.cpp" />
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="Sinum\Sinum.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
57
redirect/Sinum.vcxproj.filters
Normal file
57
redirect/Sinum.vcxproj.filters
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Utilities\Windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Core\Unreal\Array.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Core\Constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Core\Unreal\String.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Core\Unreal\Memory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Core\Core.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Sinum\Sinum.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Utilities\memcury.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Sinum\Sinum.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Core\Core.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
4
redirect/Sinum.vcxproj.user
Normal file
4
redirect/Sinum.vcxproj.user
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
40
redirect/Sinum/Sinum.cpp
Normal file
40
redirect/Sinum/Sinum.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#include "Sinum.h"
|
||||
|
||||
bool Sinum::ProcessRequestHook(FCurlHttpRequest* Request)
|
||||
{
|
||||
std::wstring URL(Request->GetURL().c_str());
|
||||
size_t PathIndex = URL.find(L"ol.epicgames.com");
|
||||
|
||||
if (PathIndex != std::wstring::npos)
|
||||
{
|
||||
auto Path = URL.substr(PathIndex + 16);
|
||||
auto NewURL = Constants::API_URL + Path;
|
||||
|
||||
Request->SetURL(NewURL.c_str());
|
||||
}
|
||||
|
||||
return _ProcessRequest(Request);
|
||||
}
|
||||
|
||||
void Sinum::Init()
|
||||
{
|
||||
auto StringRef = Memcury::Scanner::FindStringRef(Constants::ProcessRequest);
|
||||
if (StringRef.IsValid())
|
||||
{
|
||||
_ProcessRequest = StringRef
|
||||
.ScanFor({ 0x48, 0x81, 0xEC }, false)
|
||||
.ScanFor({ 0x40 }, false)
|
||||
.GetAs<decltype(_ProcessRequest)>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_ProcessRequest = Memcury::Scanner::FindStringRef(Constants::ProcessRequest_C2)
|
||||
.ScanFor({ 0x4C, 0x8B, 0xDC }, false)
|
||||
.GetAs<decltype(_ProcessRequest)>();
|
||||
}
|
||||
|
||||
*Memcury::Scanner::FindPointerRef(_ProcessRequest)
|
||||
.GetAs<void**>() = ProcessRequestHook;
|
||||
}
|
||||
31
redirect/Sinum/Sinum.h
Normal file
31
redirect/Sinum/Sinum.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
#include "../framework.h"
|
||||
|
||||
class FCurlHttpRequest
|
||||
{
|
||||
private:
|
||||
void** VTable;
|
||||
|
||||
public:
|
||||
|
||||
FString GetURL()
|
||||
{
|
||||
FString Result;
|
||||
return ((FString& (*)(FCurlHttpRequest*, FString&))(*VTable))(this, Result);
|
||||
}
|
||||
|
||||
void SetURL(FString URL)
|
||||
{
|
||||
((void (*)(FCurlHttpRequest*, FString&))(VTable[10]))(this, URL);
|
||||
}
|
||||
};
|
||||
|
||||
namespace Sinum
|
||||
{
|
||||
static bool (*_ProcessRequest)(FCurlHttpRequest*);
|
||||
static bool ProcessRequestHook(FCurlHttpRequest* Request);
|
||||
|
||||
void Init();
|
||||
}
|
||||
15
redirect/Utilities/Windows.h
Normal file
15
redirect/Utilities/Windows.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
#include "../framework.h"
|
||||
|
||||
namespace Windows
|
||||
{
|
||||
namespace Thread
|
||||
{
|
||||
static HANDLE Create(void* Routine, void* Param = NULL)
|
||||
{
|
||||
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Routine, Param, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
1210
redirect/Utilities/memcury.h
Normal file
1210
redirect/Utilities/memcury.h
Normal file
File diff suppressed because it is too large
Load Diff
10
redirect/framework.h
Normal file
10
redirect/framework.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) 2024 Project Nova LLC
|
||||
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
#include "Utilities\memcury.h"
|
||||
#include "Utilities\Windows.h"
|
||||
|
||||
#include "Core\Core.h"
|
||||
#include "Sinum\Sinum.h"
|
||||
Reference in New Issue
Block a user