7 Commits
10.0 ... 10.0.4

Author SHA1 Message Date
Alessandro Autiero
0cfa4af236 10.0.4 2024-12-10 14:45:56 +01:00
Alessandro Autiero
d42946c44b 10.0.3 2024-12-09 22:28:24 +01:00
Alessandro Autiero
0a59a32c1b 10.0.2 2024-12-09 14:36:43 +01:00
Alessandro Autiero
2046cb14f6 10.0 2024-12-09 12:59:14 +01:00
Alessandro Autiero
e3f7a1d2cc 10.0 2024-12-09 12:49:21 +01:00
Alessandro Autiero
cd6752ed3f Merge pull request #169 from Auties00/_onLoggedIn
10.0
2024-12-09 12:44:14 +01:00
Alessandro Autiero
e1df46efd9 10.0 2024-12-09 12:42:49 +01:00
31 changed files with 402 additions and 1968 deletions

8
archive/README.md Normal file
View File

@@ -0,0 +1,8 @@
# Builds Archive
Builds are stored on a Cloudflare R2 instance at `https://builds.rebootfn.org/versions.json`.
If you want to move them to another AWS-compatible object storage, run:
```
move.ps1
```
and provide the required parameters.

98
archive/move.ps1 Normal file
View File

@@ -0,0 +1,98 @@
param(
[Parameter(Mandatory=$true)]
[string]$UrlListPath, # Path to a text file with one URL per line
[Parameter(Mandatory=$true)]
[string]$BucketName, # Name of the R2 bucket
[Parameter(Mandatory=$true)]
[string]$AccessKey, # Your R2 access key
[Parameter(Mandatory=$true)]
[string]$SecretKey, # Your R2 secret key
[Parameter(Mandatory=$true)]
[string]$EndPointURL, # Your R2 endpoint URL, e.g. https://<account_id>.r2.cloudflarestorage.com
[Parameter(Mandatory=$false)]
[int]$MaxConcurrentConnections = 16, # Number of concurrent connections for each file download
[Parameter(Mandatory=$false)]
[int]$SplitCount = 16, # Number of segments to split the download into
[Parameter(Mandatory=$false)]
[string]$AwsRegion = "auto" # Region; often "auto" works for R2, but can be set if needed
)
# Set AWS environment variables for this session
$Env:AWS_ACCESS_KEY_ID = $AccessKey
$Env:AWS_SECRET_ACCESS_KEY = $SecretKey
$Env:AWS_REGION = $AwsRegion # If required, or leave as "auto"
# Read all URLs from file
$Urls = Get-Content $UrlListPath | Where-Object { $_ -and $_. Trim() -ne "" }
# Ensure aria2 is available
if (-not (Get-Command aria2c -ErrorAction SilentlyContinue)) {
Write-Error "aria2c not found in PATH. Please install aria2."
exit 1
}
# Ensure aws CLI is available
if (-not (Get-Command aws -ErrorAction SilentlyContinue)) {
Write-Error "aws CLI not found in PATH. Please install AWS CLI."
exit 1
}
function Process-Url {
param(
[string]$Url,
[string]$BucketName,
[string]$EndPointURL,
[int]$MaxConcurrentConnections,
[int]$SplitCount
)
# Extract the filename from the URL
$FileName = Split-Path -Leaf $Url
try {
Write-Host "Downloading: $Url"
# Use aria2c to download with multiple connections
& aria2c `
--max-connection-per-server=$MaxConcurrentConnections `
--split=$SplitCount `
--out=$FileName `
--check-certificate=false `
--header="Cookie: _c_t_c=1" `
$Url
if (!(Test-Path $FileName)) {
Write-Host "Failed to download $Url"
return
}
Write-Host "Uploading $FileName to R2 bucket: $BucketName"
& aws s3 cp $FileName "s3://$BucketName/$FileName" --endpoint-url $EndPointURL
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to upload $FileName to R2"
return
}
Write-Host "Upload successful. Deleting local file: $FileName"
Remove-Item $FileName -Force
Write-Host "Completed processing of $FileName."
} catch {
Write-Host "Error processing $Url"
Write-Host $_
}
}
# Process each URL sequentially here. If you'd like to run multiple URLs in parallel,
# you could replace the foreach loop with a ForEach-Object -Parallel block.
foreach ($Url in $Urls) {
Process-Url -Url $Url -BucketName $BucketName -EndPointURL $EndPointURL -MaxConcurrentConnections $MaxConcurrentConnections -SplitCount $SplitCount
}

85
archive/versions.txt Normal file
View File

@@ -0,0 +1,85 @@
https://builds.rebootfn.org/1.7.2.zip
https://builds.rebootfn.org/1.8.rar
https://builds.rebootfn.org/1.8.1.rar
https://builds.rebootfn.org/1.8.2.rar
https://builds.rebootfn.org/1.9.rar
https://builds.rebootfn.org/1.9.1.rar
https://builds.rebootfn.org/1.10.rar
https://builds.rebootfn.org/1.11.zip
https://builds.rebootfn.org/2.1.0.zip
https://builds.rebootfn.org/2.2.0.rar
https://builds.rebootfn.org/2.3.rar
https://builds.rebootfn.org/2.4.0.zip
https://builds.rebootfn.org/2.4.2.zip
https://builds.rebootfn.org/2.5.0.rar
https://builds.rebootfn.org/3.0.zip
https://builds.rebootfn.org/3.1.rar
https://builds.rebootfn.org/3.1.1.zip
https://builds.rebootfn.org/3.2.zip
https://builds.rebootfn.org/3.3.rar
https://builds.rebootfn.org/3.5.rar
https://builds.rebootfn.org/3.6.zip
https://builds.rebootfn.org/4.0.zip
https://builds.rebootfn.org/4.1.zip
https://builds.rebootfn.org/4.2.zip
https://builds.rebootfn.org/4.4.rar
https://builds.rebootfn.org/4.5.rar
https://builds.rebootfn.org/5.00.rar
https://builds.rebootfn.org/5.0.1.rar
https://builds.rebootfn.org/5.10.rar
https://builds.rebootfn.org/5.21.rar
https://builds.rebootfn.org/5.30.rar
https://builds.rebootfn.org/5.40.rar
https://builds.rebootfn.org/6.00.rar
https://builds.rebootfn.org/6.01.rar
https://builds.rebootfn.org/6.1.1.rar
https://builds.rebootfn.org/6.02.rar
https://builds.rebootfn.org/6.2.1.rar
https://builds.rebootfn.org/6.10.rar
https://builds.rebootfn.org/6.10.1.rar
https://builds.rebootfn.org/6.10.2.rar
https://builds.rebootfn.org/6.21.rar
https://builds.rebootfn.org/6.22.rar
https://builds.rebootfn.org/6.30.rar
https://builds.rebootfn.org/6.31.rar
https://builds.rebootfn.org/7.00.rar
https://builds.rebootfn.org/7.10.rar
https://builds.rebootfn.org/7.20.rar
https://builds.rebootfn.org/7.30.zip
https://builds.rebootfn.org/7.40.rar
https://builds.rebootfn.org/8.00.zip
https://builds.rebootfn.org/8.20.rar
https://builds.rebootfn.org/8.30.rar
https://builds.rebootfn.org/8.40.zip
https://builds.rebootfn.org/8.50.zip
https://builds.rebootfn.org/8.51.rar
https://builds.rebootfn.org/9.00.zip
https://builds.rebootfn.org/9.01.zip
https://builds.rebootfn.org/9.10.rar
https://builds.rebootfn.org/9.21.zip
https://builds.rebootfn.org/9.30.zip
https://builds.rebootfn.org/9.40.zip
https://builds.rebootfn.org/9.41.rar
https://builds.rebootfn.org/10.00.zip
https://builds.rebootfn.org/10.10.zip
https://builds.rebootfn.org/10.20.zip
https://builds.rebootfn.org/10.31.zip
https://builds.rebootfn.org/10.40.rar
https://builds.rebootfn.org/11.00.zip
https://builds.rebootfn.org/11.31.rar
https://builds.rebootfn.org/12.00.rar
https://builds.rebootfn.org/12.21.zip
https://builds.rebootfn.org/12.50.zip
https://builds.rebootfn.org/12.61.zip
https://builds.rebootfn.org/13.00.rar
https://builds.rebootfn.org/13.40.zip
https://builds.rebootfn.org/14.00.rar
https://builds.rebootfn.org/14.40.rar
https://builds.rebootfn.org/14.60.rar
https://builds.rebootfn.org/15.30.rar
https://builds.rebootfn.org/16.40.rar
https://builds.rebootfn.org/17.30.zip
https://builds.rebootfn.org/17.50.zip
https://builds.rebootfn.org/18.40.zip
https://builds.rebootfn.org/19.10.rar
https://builds.rebootfn.org/20.40.zip"

View File

@@ -134,17 +134,13 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
}
Future<void> _startAriaServer() async {
final running = await _isAriaRunning();
if(running) {
await killProcessByPort(_ariaPort);
}
await stopDownloadServer();
final aria2c = File("${assetsDirectory.path}\\build\\aria2c.exe");
if(!aria2c.existsSync()) {
throw "Missing aria2c.exe";
}
await startProcess(
final process = await startProcess(
executable: aria2c,
args: [
"--max-connection-per-server=${Platform.numberOfProcessors}",
@@ -153,10 +149,14 @@ Future<void> _startAriaServer() async {
"--rpc-listen-all=true",
"--rpc-allow-origin-all",
"--rpc-secret=$_ariaSecret",
"--rpc-listen-port=$_ariaPort"
"--rpc-listen-port=$_ariaPort",
"--file-allocation=none"
],
window: false
window: false
);
process.stdOutput.listen((message) => log("[ARIA] Message: $message"));
process.stdError.listen((error) => log("[ARIA] Error: $error"));
process.exitCode.then((exitCode) => log("[ARIA] Exit code: $exitCode"));
for(var i = 0; i < _ariaMaxSpawnTime.inSeconds; i++) {
if(await _isAriaRunning()) {
return;
@@ -177,8 +177,8 @@ Future<bool> _isAriaRunning() async {
"token:${_ariaSecret}"
]
};
await http.post(_ariaEndpoint, body: jsonEncode(statusRequest));
return true;
final response = await http.post(_ariaEndpoint, body: jsonEncode(statusRequest));
return response.statusCode == 200;
}catch(_) {
return false;
}
@@ -227,11 +227,16 @@ Future<void> _stopAriaDownload(String downloadId) async {
]
};
await http.post(_ariaEndpoint, body: jsonEncode(addDownloadRequest));
stopDownloadServer();
}catch(error) {
throw "Stop failed (${error})";
}
}
Future<void> stopDownloadServer() async {
await killProcessByPort(_ariaPort);
}
Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File tempFile, FortniteBuildDownloadOptions options) async {
Process? process;

View File

@@ -1,16 +1,39 @@
# reboot_launcher
Launcher for project reboot
# Reboot Launcher
Welcome to the **Reboot Launcher**!
This is a GUI application developed as part of the **Reboot Project**.
## Getting Started
This project is a starting point for a Flutter application.
### Running the Project
To launch the project in development mode, simply run:
```
flutter run
```
A few resources to get you started if this is your first Flutter project:
### Building the Project
To create a production-ready build, use:
```
flutter build
```
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
### Packaging the Project
To package the application for distribution, run:
```
package.bat
```
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
## Requirements
- [Flutter SDK](https://flutter.dev/docs/get-started/install)
- Supported operating systems: Windows
## Other platforms
Native support for these platforms is not currently planned, but Linux support is a priority for the 10.0 release cycle
- [Linux Tutorial using Proton](https://www.reddit.com/r/linux_gaming/comments/1fwa4l8/guide_running_a_fortnite_private_server_to_play/)
- No tutorials are available for MacOS(got lost when the Reboot discord was banned), but it's possible to run Reboot using a compatibility layer
## Contributing
Contributions are welcome! Feel free to open an issue or submit a pull request.

View File

@@ -216,7 +216,6 @@
"downloadedVersion": "The download was completed successfully!",
"download": "Download",
"downloading": "Downloading...",
"allocatingSpace": "Allocating disk space...",
"startingDownload": "Starting download...",
"extracting": "Extracting...",
"buildProgress": "{progress}%",
@@ -237,7 +236,7 @@
"startGame": "Start fortnite",
"stopGame": "Close fortnite",
"waitingForGameServer": "Waiting for the game server to boot up...",
"gameServerStartWarning": "The game server was started successfully, but Reboot didn't load",
"gameServerStartWarning": "Unsupported version: the game server crashed while setting up the server",
"gameServerStartLocalWarning": "The game server was started successfully, but other players can't join",
"gameServerStarted": "The game server was started successfully",
"gameClientStarted": "The game client was started successfully",

View File

@@ -171,7 +171,8 @@ Future<void> _initWindow() async {
}else {
await windowManager.setAlignment(Alignment.center);
}
await windowManager.setPreventClose(true);
await windowManager.setResizable(true);
if(isWin11) {
await Window.setEffect(
effect: WindowEffect.acrylic,

View File

@@ -8,10 +8,10 @@ import 'package:get/get.dart';
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/os.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';
class AddVersionDialog extends StatefulWidget {
@@ -35,9 +35,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
final Rxn<double> _progress = Rxn();
final RxInt _speed = RxInt(0);
late DiskSpace _diskSpace;
late Future<List<FortniteBuild>> _fetchFuture;
late Future _diskFuture;
SendPort? _downloadPort;
Object? _error;
@@ -45,10 +43,10 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
@override
void initState() {
_fetchFuture = compute(fetchBuilds, null);
_diskSpace = DiskSpace();
_diskFuture = _diskSpace.scan()
.then((_) => _updateFormDefaults());
_fetchFuture = compute(fetchBuilds, null).then((value) {
_updateFormDefaults();
return value;
});
super.initState();
}
@@ -71,7 +69,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
switch(_status.value){
case _DownloadStatus.form:
return FutureBuilder(
future: Future.wait([_fetchFuture, _diskFuture]).then((_) async => await _fetchFuture),
future: _fetchFuture,
builder: (context, snapshot) {
if (snapshot.hasError) {
WidgetsBinding.instance.addPostFrameCallback((_) => _onDownloadError(snapshot.error, snapshot.stackTrace));
@@ -244,12 +242,12 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
),
),
if(_progress.value != null && !_isAllocatingDiskSpace)
if(_progress.value != null)
const SizedBox(
height: 8.0,
),
if(_progress.value != null && !_isAllocatingDiskSpace)
if(_progress.value != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -272,7 +270,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
SizedBox(
width: double.infinity,
child: ProgressBar(value: _isAllocatingDiskSpace ? null : _progress.value?.toDouble())
child: ProgressBar(value: _progress.value?.toDouble())
),
const SizedBox(
@@ -291,15 +289,9 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
return translations.startingDownload;
}
if (_speed.value == 0) {
return translations.allocatingSpace;
}
return translations.downloading;
}
bool get _isAllocatingDiskSpace => _status.value == _DownloadStatus.downloading && _speed.value == 0;
Widget _buildFormBody(List<FortniteBuild> builds) {
return Column(
mainAxisSize: MainAxisSize.min,
@@ -450,16 +442,16 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
_build.value = null;
}
if(_source.value != _BuildSource.local && _diskSpace.disks.isNotEmpty) {
await _fetchFuture;
final bestDisk = _diskSpace.disks
.reduce((first, second) => first.availableSpace > second.availableSpace ? first : second);
final disks = WindowsDisk.available();
if(_source.value != _BuildSource.local && disks.isNotEmpty) {
final bestDisk = disks.reduce((first, second) => first.freeBytesAvailable > second.freeBytesAvailable ? first : second);
final build = _build.value;
if(build == null){
return;
}
final pathText = "${bestDisk.devicePath}\\FortniteBuilds\\${build.version}";
print("${bestDisk.path}\\FortniteBuilds\\${build.version}");
final pathText = "${bestDisk.path}FortniteBuilds\\${build.version}";
_pathController.text = pathText;
_pathController.selection = TextSelection.collapsed(offset: pathText.length);
}

View File

@@ -10,6 +10,7 @@ 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/dll_controller.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
import 'package:reboot_launcher/src/controller/settings_controller.dart';
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
@@ -43,6 +44,7 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepAliveClientMixin {
final BackendController _backendController = Get.find<BackendController>();
final GameController _gameController = Get.find<GameController>();
final HostingController _hostingController = Get.find<HostingController>();
final SettingsController _settingsController = Get.find<SettingsController>();
final DllController _dllController = Get.find<DllController>();
@@ -160,11 +162,45 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
@override
void onWindowClose() async {
try {
await windowManager.hide();
}catch(error) {
log("[WINDOW] Cannot hide window: $error");
}
try {
await _hostingController.discardServer();
}catch(error) {
log("[HOSTING] Cannot discard server: $error");
log("[HOSTING] Cannot discard server on exit: $error");
}
try {
if(_backendController.started.value) {
await _backendController.toggleInteractive();
}
}catch(error) {
log("[BACKEND] Cannot stop backend on exit: $error");
}
try {
_gameController.instance.value?.kill();
}catch(error) {
log("[GAME] Cannot stop game on exit: $error");
}
try {
_hostingController.instance.value?.kill();
}catch(error) {
log("[HOST] Cannot stop host on exit: $error");
}
try {
await stopDownloadServer();
}catch(error) {
log("[ARIA] Cannot stop aria server on exit: $error");
}
exit(0);
}
@override

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:reboot_common/common.dart';
@@ -9,22 +10,24 @@ const Duration _timeout = Duration(seconds: 5);
Completer<bool> pingGameServerOrTimeout(String address, Duration timeout) {
final completer = Completer<bool>();
final start = DateTime.now();
(() async {
while (!completer.isCompleted && DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch < timeout.inMilliseconds) {
final result = await pingGameServer(address);
if(result) {
completer.complete(true);
}else {
await Future.delayed(_timeout);
}
}
if(!completer.isCompleted) {
completer.complete(false);
}
})();
_pingGameServerOrTimeout(completer, start, timeout, address);
return completer;
}
Future<void> _pingGameServerOrTimeout(Completer<bool> completer, DateTime start, Duration timeout, String address) async {
while (!completer.isCompleted && max(DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch, 0) < timeout.inMilliseconds) {
final result = await pingGameServer(address);
if(result) {
completer.complete(true);
}else {
await Future.delayed(_timeout);
}
}
if(!completer.isCompleted) {
completer.complete(false);
}
}
Future<bool> pingGameServer(String address) async {
final split = address.split(":");
var hostname = split[0];

View File

@@ -492,3 +492,57 @@ int _convertToHString(String string) {
extension WindowManagerExtension on WindowManager {
Future<void> maximizeOrRestore() async => await windowManager.isMaximized() ? windowManager.restore() : windowManager.maximize();
}
class WindowsDisk {
static final String _nullTerminator = String.fromCharCode(0);
final String path;
final int freeBytesAvailable;
final int totalNumberOfBytes;
const WindowsDisk._internal(this.path, this.freeBytesAvailable, this.totalNumberOfBytes);
static List<WindowsDisk> available() {
final buffer = malloc.allocate<Utf16>(MAX_PATH);
try {
final length = GetLogicalDriveStrings(MAX_PATH, buffer);
if (length == 0) {
return [];
}
return buffer.toDartString(length: length)
.split(_nullTerminator)
.where((drive) => drive.length > 1)
.map((driveName) {
final freeBytesAvailable = calloc<Uint64>();
final totalNumberOfBytes = calloc<Uint64>();
final totalNumberOfFreeBytes = calloc<Uint64>();
try {
GetDiskFreeSpaceEx(
driveName.toNativeUtf16(),
freeBytesAvailable,
totalNumberOfBytes,
totalNumberOfFreeBytes
);
return WindowsDisk._internal(
driveName,
freeBytesAvailable.value,
totalNumberOfBytes.value
);
} finally {
calloc.free(freeBytesAvailable);
calloc.free(totalNumberOfBytes);
calloc.free(totalNumberOfFreeBytes);
}
})
.toList(growable: false);
} finally {
calloc.free(buffer);
}
}
@override
String toString() {
return 'WindowsDisk{path: $path, freeBytesAvailable: $freeBytesAvailable, totalNumberOfBytes: $totalNumberOfBytes}';
}
}

View File

@@ -492,7 +492,7 @@ class _LaunchButtonState extends State<LaunchButton> {
final pingOperation = pingGameServerOrTimeout(
"$publicIp:$gameServerPort",
const Duration(days: 365)
const Duration(days: 1)
);
this._pingOperation = pingOperation;
_gameServerInfoBar = showRebootInfoBar(

View File

@@ -1,6 +1,6 @@
name: reboot_launcher
description: Graphical User Interface for Project Reboot
version: "10.0.0"
version: "10.0.4"
publish_to: 'none'
@@ -58,7 +58,6 @@ dependencies:
# Storage
get_storage: ^2.1.1
universal_disk_space: ^0.2.3
path: ^1.9.0
# Translations

View File

@@ -36,7 +36,6 @@ Source: "{{SOURCE_DIR}}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdir
Source: "..\..\dependencies\redist\VC_redist.x64.exe"; DestDir: {tmp}; Flags: dontcopy
[Run]
Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -Command ""Add-MpPreference -ExclusionPath '{app}'"""; Flags: runhidden
Filename: "{app}\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; Flags: runascurrentuser nowait postinstall skipifsilent
Filename: "{tmp}\VC_redist.x64.exe"; StatusMsg: "{cm:InstallingVC2017redist}"; Parameters: "/quiet"; Check: VC2017RedistNeedsInstall; Flags: waituntilterminated
@@ -46,6 +45,44 @@ Name: "{autodesktop}\{{DISPLAY_NAME}}"; Filename: "{app}\{{EXECUTABLE_NAME}}"; T
Name: "{userstartup}\{{DISPLAY_NAME}}"; Filename: "{app}\{{EXECUTABLE_NAME}}"; WorkingDir: "{app}"; Tasks: launchAtStartup
[Code]
var
Page: TInputOptionWizardPage;
procedure InitializeWizard();
begin
Page := CreateInputOptionPage(
wpWelcome,
' Allow DLL injection',
' The Reboot Launcher needs to inject DLLs into Fortnite to create the game server',
'Selecting the option below will add the Reboot Launcher to the Windows Exclusions list. ' +
'This is necessary because DLL injection is often detected as a virus, but is necessary to modify Fortnite. ' +
'This option was designed for advanced users who want to manually manage the exclusions list on their machine. ' +
'If you do not trust the Reboot Launcher, you can audit the source code at https://github.com/Auties00/reboot_launcher and build it from source.',
False,
False
);
Page.Add('&Add the launcher to the Windows Exclusions list');
Page.Values[0] := True;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
ResultCode: Integer;
InstallationDir: String;
begin
if (CurStep = ssPostInstall) and Page.Values[0] then
begin
InstallationDir := ExpandConstant('{app}');
Exec('powershell.exe', '-ExecutionPolicy Bypass -Command ""Add-MpPreference -ExclusionPath ''' + InstallationDir + '''""' , '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
Log('Powershell exit code: ' + IntToStr(ResultCode));
end;
end;
function CompareVersion(version1, version2: String): Integer;
var
packVersion1, packVersion2: Int64;

View File

@@ -1,13 +0,0 @@
// 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";
}

View File

@@ -1,11 +0,0 @@
// 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)>();
}

View File

@@ -1,16 +0,0 @@
// 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();
}

View File

@@ -1,143 +0,0 @@
// 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;
}
};

View File

@@ -1,92 +0,0 @@
// 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;
}
};

View File

@@ -1,54 +0,0 @@
// 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;
}
};

View File

@@ -1,21 +0,0 @@
// 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;
}

View File

@@ -1,5 +0,0 @@
# Sinum Windows
https://github.com/projectnovafn/Sinum/tree/main/Windows
Modified to point to http://localhost:3551

View File

@@ -1,22 +0,0 @@

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

View File

@@ -1,162 +0,0 @@
<?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>

View File

@@ -1,57 +0,0 @@
<?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>

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View File

@@ -1,40 +0,0 @@
// 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;
}

View File

@@ -1,31 +0,0 @@
// 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();
}

View File

@@ -1,15 +0,0 @@
// 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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
// 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"