forked from MeloNX/MeloNX
Add update handling and UI for latest version notifications
This commit is contained in:
parent
5009474e14
commit
bd35b9e2be
@ -24,6 +24,7 @@
|
|||||||
/* End PBXAggregateTarget section */
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
256C91642D8126E300F9736D /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 256C91632D8126E300F9736D /* Alamofire */; };
|
||||||
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */ = {isa = PBXBuildFile; productRef = 4E0DED332D05695D00FEF007 /* SwiftUIJoystick */; };
|
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */ = {isa = PBXBuildFile; productRef = 4E0DED332D05695D00FEF007 /* SwiftUIJoystick */; };
|
||||||
4E12B23C2D797CFA00FB2271 /* MeloNX.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */; };
|
4E12B23C2D797CFA00FB2271 /* MeloNX.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 4E12B23B2D797CFA00FB2271 /* MeloNX.xcconfig */; };
|
||||||
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
|
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E80AA622CD7122800029585 /* GameController.framework */; };
|
||||||
@ -205,6 +206,7 @@
|
|||||||
files = (
|
files = (
|
||||||
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */,
|
4E0DED342D05695D00FEF007 /* SwiftUIJoystick in Frameworks */,
|
||||||
CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */,
|
CA8F9C322D3F5AB200D7E586 /* GameController.framework in Frameworks */,
|
||||||
|
256C91642D8126E300F9736D /* Alamofire in Frameworks */,
|
||||||
4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */,
|
4EA5AE822D16807500AD0B9F /* SwiftSVG in Frameworks */,
|
||||||
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */,
|
4E8A80772D5FDD2D0041B48F /* GameController.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
@ -303,6 +305,7 @@
|
|||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
4E0DED332D05695D00FEF007 /* SwiftUIJoystick */,
|
4E0DED332D05695D00FEF007 /* SwiftUIJoystick */,
|
||||||
4EA5AE812D16807500AD0B9F /* SwiftSVG */,
|
4EA5AE812D16807500AD0B9F /* SwiftSVG */,
|
||||||
|
256C91632D8126E300F9736D /* Alamofire */,
|
||||||
);
|
);
|
||||||
productName = MeloNX;
|
productName = MeloNX;
|
||||||
productReference = 4E80A98D2CD6F54500029585 /* MeloNX.app */;
|
productReference = 4E80A98D2CD6F54500029585 /* MeloNX.app */;
|
||||||
@ -395,6 +398,7 @@
|
|||||||
packageReferences = (
|
packageReferences = (
|
||||||
4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */,
|
4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */,
|
||||||
4EA5AE802D16807500AD0B9F /* XCRemoteSwiftPackageReference "SwiftSVG" */,
|
4EA5AE802D16807500AD0B9F /* XCRemoteSwiftPackageReference "SwiftSVG" */,
|
||||||
|
256C91622D8126E300F9736D /* XCRemoteSwiftPackageReference "Alamofire" */,
|
||||||
);
|
);
|
||||||
preferredProjectObjectVersion = 56;
|
preferredProjectObjectVersion = 56;
|
||||||
productRefGroup = 4E80A98E2CD6F54500029585 /* Products */;
|
productRefGroup = 4E80A98E2CD6F54500029585 /* Products */;
|
||||||
@ -651,7 +655,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 4TD3JXVDW7;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_TESTABILITY = NO;
|
ENABLE_TESTABILITY = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@ -710,6 +714,8 @@
|
|||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
);
|
);
|
||||||
GCC_OPTIMIZATION_LEVEL = s;
|
GCC_OPTIMIZATION_LEVEL = s;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -833,9 +839,13 @@
|
|||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(VERSION)";
|
MARKETING_VERSION = "$(VERSION)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.belladev.MeloNX;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
||||||
@ -854,7 +864,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "";
|
DEVELOPMENT_ASSET_PATHS = "";
|
||||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
DEVELOPMENT_TEAM = 4TD3JXVDW7;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
@ -913,6 +923,8 @@
|
|||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
);
|
);
|
||||||
GCC_OPTIMIZATION_LEVEL = s;
|
GCC_OPTIMIZATION_LEVEL = s;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@ -1036,9 +1048,13 @@
|
|||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "$(VERSION)";
|
MARKETING_VERSION = "$(VERSION)";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = xyz.belladev.MeloNX;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "MeloNX/App/Core/Headers/Ryujinx-Header.h";
|
||||||
@ -1235,6 +1251,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
256C91622D8126E300F9736D /* XCRemoteSwiftPackageReference "Alamofire" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/Alamofire/Alamofire";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 5.10.2;
|
||||||
|
};
|
||||||
|
};
|
||||||
4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */ = {
|
4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/michael94ellis/SwiftUIJoystick";
|
repositoryURL = "https://github.com/michael94ellis/SwiftUIJoystick";
|
||||||
@ -1254,6 +1278,11 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
256C91632D8126E300F9736D /* Alamofire */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 256C91622D8126E300F9736D /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||||
|
productName = Alamofire;
|
||||||
|
};
|
||||||
4E0DED332D05695D00FEF007 /* SwiftUIJoystick */ = {
|
4E0DED332D05695D00FEF007 /* SwiftUIJoystick */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */;
|
package = 4E0DED322D05695D00FEF007 /* XCRemoteSwiftPackageReference "SwiftUIJoystick" */;
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "d611b071fbe94fdc9900a07a218340eab4ce2c3c7168bf6542f2830c0400a72b",
|
"originHash" : "587a0e7c5c7d612a2c16a973e66df9a6a582b963cb51df7c89fd96cb28ef4a63",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "alamofire",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/Alamofire/Alamofire",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5",
|
||||||
|
"version" : "5.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swiftsvg",
|
"identity" : "swiftsvg",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
Binary file not shown.
@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1610"
|
||||||
|
version = "1.7">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES"
|
||||||
|
buildArchitectures = "Automatic">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4E80A98C2CD6F54500029585"
|
||||||
|
BuildableName = "MeloNX.app"
|
||||||
|
BlueprintName = "MeloNX"
|
||||||
|
ReferencedContainer = "container:MeloNX.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
shouldAutocreateTestPlan = "YES">
|
||||||
|
<Testables>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4E80A99C2CD6F54700029585"
|
||||||
|
BuildableName = "MeloNXTests.xctest"
|
||||||
|
BlueprintName = "MeloNXTests"
|
||||||
|
ReferencedContainer = "container:MeloNX.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
<TestableReference
|
||||||
|
skipped = "NO"
|
||||||
|
parallelizable = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4E80A9A62CD6F54700029585"
|
||||||
|
BuildableName = "MeloNXUITests.xctest"
|
||||||
|
BlueprintName = "MeloNXUITests"
|
||||||
|
ReferencedContainer = "container:MeloNX.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</TestableReference>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
|
showGraphicsOverview = "Yes"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4E80A98C2CD6F54500029585"
|
||||||
|
BuildableName = "MeloNX.app"
|
||||||
|
BlueprintName = "MeloNX"
|
||||||
|
ReferencedContainer = "container:MeloNX.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "4E80A98C2CD6F54500029585"
|
||||||
|
BuildableName = "MeloNX.app"
|
||||||
|
BlueprintName = "MeloNX"
|
||||||
|
ReferencedContainer = "container:MeloNX.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
<PreActions>
|
||||||
|
<ExecutionAction
|
||||||
|
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||||
|
<ActionContent
|
||||||
|
title = "Run Script"
|
||||||
|
scriptText = "REPO_DIR="$(cd "${SRCROOT}/../../" && pwd)" SCRIPT_PATH="$REPO_DIR/distribution/ios/set_current_version.sh" sh "${SCRIPT_PATH}" "
|
||||||
|
shellToInvoke = "/bin/bash">
|
||||||
|
</ActionContent>
|
||||||
|
</ExecutionAction>
|
||||||
|
</PreActions>
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
37
src/MeloNX/MeloNX/App/Models/LatestVersionResponse.swift
Normal file
37
src/MeloNX/MeloNX/App/Models/LatestVersionResponse.swift
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// LatestVersionResponse.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Bella on 12/03/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
struct LatestVersionResponse: Codable {
|
||||||
|
let version_number: String
|
||||||
|
let version_number_stripped: String
|
||||||
|
let changelog: String
|
||||||
|
let download_link: String
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
static let example1 = LatestVersionResponse(
|
||||||
|
version_number: "1.0.0",
|
||||||
|
version_number_stripped: "100",
|
||||||
|
changelog: """
|
||||||
|
- Rewrite Display Code (SDL isn't used for display anymore)
|
||||||
|
- Add New Onboarding / Setup
|
||||||
|
- Better Performance
|
||||||
|
- Remove "SDL Window" option in settings
|
||||||
|
- Fix JIT Cache Regions
|
||||||
|
- Fix how JIT is detected in Settings
|
||||||
|
- Fix ABYX being swapped on controller.
|
||||||
|
- Settings are now a config.json file
|
||||||
|
- Fix Performance Overlay not showing when Virtual Controller is hidden
|
||||||
|
- Add displaying logs when Loading or in-game
|
||||||
|
- Fix Launching games from outside of the roms folder
|
||||||
|
- Add Waiting for JIT popup
|
||||||
|
- Fix spesific Games
|
||||||
|
- Added Back Herobrine ("You were supposed to be the hero, Bryan")
|
||||||
|
""",
|
||||||
|
download_link: "https://example.com"
|
||||||
|
)
|
||||||
|
#endif
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// MeloNXUpdateSheet.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Bella on 12/03/2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MeloNXUpdateSheet: View {
|
||||||
|
let updateInfo: LatestVersionResponse
|
||||||
|
@Binding var isPresented: Bool
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
iOSNav {
|
||||||
|
VStack {
|
||||||
|
Text("Version \(updateInfo.version_number) is available. You are currently on Version \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown").")
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
Text("Changelog:")
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
Text(updateInfo.changelog)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.frame(maxHeight: 400)
|
||||||
|
.background(Color(.secondarySystemBackground))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
.padding(.top, 15)
|
||||||
|
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
Button(action: {
|
||||||
|
if let url = URL(string: updateInfo.download_link) {
|
||||||
|
UIApplication.shared.open(url)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Text("Download Now")
|
||||||
|
.font(.title3)
|
||||||
|
.bold()
|
||||||
|
.frame(width: 300, height: 40)
|
||||||
|
}
|
||||||
|
.buttonStyle(.borderedProminent)
|
||||||
|
.frame(alignment: .bottom)
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
.navigationTitle("Version \(updateInfo.version_number) Available!")
|
||||||
|
.toolbar {
|
||||||
|
Button(action: {
|
||||||
|
isPresented = false
|
||||||
|
}) {
|
||||||
|
Text("Close")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
MeloNXUpdateSheet(updateInfo: LatestVersionResponse.example1, isPresented: .constant(true))
|
||||||
|
}
|
@ -8,249 +8,82 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
|
import Alamofire
|
||||||
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct MeloNXApp: App {
|
struct MeloNXApp: App {
|
||||||
|
|
||||||
@State var showed = false
|
|
||||||
@Environment(\.scenePhase) var scenePhase
|
@Environment(\.scenePhase) var scenePhase
|
||||||
@State var alert: UIAlertController? = nil
|
|
||||||
|
|
||||||
@State var finished = false
|
@State var finished = false
|
||||||
|
|
||||||
|
// Variables for the update system :)
|
||||||
|
@State var showOutOfDateSheet = false
|
||||||
|
@State var updateInfo: LatestVersionResponse? = nil
|
||||||
|
|
||||||
@AppStorage("hasbeenfinished") var finishedStorage: Bool = false
|
@AppStorage("hasbeenfinished") var finishedStorage: Bool = false
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ZStack {
|
VStack {
|
||||||
if showed || DRM != 1 {
|
if finishedStorage {
|
||||||
|
ContentView()
|
||||||
if finishedStorage {
|
|
||||||
ContentView()
|
|
||||||
} else {
|
|
||||||
SetupView(finished: $finished)
|
|
||||||
.onChange(of: finished) { newValue in
|
|
||||||
withAnimation {
|
|
||||||
withAnimation {
|
|
||||||
finishedStorage = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Group {
|
SetupView(finished: $finished)
|
||||||
VStack {
|
.onChange(of: finished) { newValue in
|
||||||
Spacer()
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text("Loading...")
|
|
||||||
ProgressView()
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text(UIDevice.current.identifierForVendor?.uuidString ?? "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
initR()
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
||||||
.background(Color.black.opacity(1))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initR() {
|
|
||||||
if DRM == 1 {
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
// drmcheck()
|
|
||||||
InitializeRyujinx() { bool in
|
|
||||||
if bool {
|
|
||||||
print("Ryujinx Files Initialized Successfully")
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
withAnimation {
|
withAnimation {
|
||||||
showed = true
|
withAnimation {
|
||||||
}
|
finishedStorage = newValue
|
||||||
|
|
||||||
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
|
|
||||||
InitializeRyujinx() { bool in
|
|
||||||
if !bool, (scenePhase != .background || scenePhase == .inactive) {
|
|
||||||
withAnimation {
|
|
||||||
showed = false
|
|
||||||
}
|
|
||||||
if !(alert?.isViewLoaded ?? false) {
|
|
||||||
alert = showDMCAAlert()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
alert?.dismiss(animated: true)
|
|
||||||
showed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
showDMCAAlert()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func showAlert() -> UIAlertController? {
|
|
||||||
// Create the alert controller
|
|
||||||
if let mainWindow = UIApplication.shared.windows.last {
|
|
||||||
let alertController = UIAlertController(title: "Enter license", message: "Enter license key:", preferredStyle: .alert)
|
|
||||||
|
|
||||||
// Add a text field to the alert
|
|
||||||
alertController.addTextField { textField in
|
|
||||||
textField.placeholder = "Enter key here"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the "OK" action
|
|
||||||
let okAction = UIAlertAction(title: "OK", style: .default) { _ in
|
|
||||||
// Get the text entered in the text field
|
|
||||||
if let textField = alertController.textFields?.first, let enteredText = textField.text {
|
|
||||||
print("Entered text: \(enteredText)")
|
|
||||||
UserDefaults.standard.set(enteredText, forKey: "MeloDRMID")
|
|
||||||
// drmcheck() { bool in
|
|
||||||
// if bool {
|
|
||||||
// showed = true
|
|
||||||
// } else {
|
|
||||||
// exit(0)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
alertController.addAction(okAction)
|
.onAppear {
|
||||||
|
checkLatestVersion()
|
||||||
// Add a "Cancel" action
|
}
|
||||||
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
|
// this seems like a weird way to show the sheet but, from my history this is the most reliable way for the content to actually show in the sheet, otherwise its blank
|
||||||
alertController.addAction(cancelAction)
|
.sheet(isPresented: Binding(
|
||||||
|
get: { showOutOfDateSheet && updateInfo != nil },
|
||||||
// Present the alert
|
set: { newValue in
|
||||||
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
|
if !newValue {
|
||||||
|
showOutOfDateSheet = false
|
||||||
return alertController
|
updateInfo = nil
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func showDMCAAlert() -> UIAlertController? {
|
|
||||||
if let mainWindow = UIApplication.shared.windows.first {
|
|
||||||
let alertController = UIAlertController(title: "Unauthorized Copy Notice", message: "This app was illegally leaked. Please report the download on the MeloNX Discord. In the meantime, check out Pomelo! \n -Stossy11", preferredStyle: .alert)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
return alertController
|
|
||||||
} else {
|
|
||||||
// uhoh
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func drmcheck(completion: @escaping (Bool) -> Void) {
|
|
||||||
if let deviceid = UIDevice.current.identifierForVendor?.uuidString, let base64device = deviceid.data(using: .utf8)?.base64EncodedString() {
|
|
||||||
if let value = UserDefaults.standard.string(forKey: "MeloDRMID") {
|
|
||||||
if let url = URL(string: "https://mx.stossy11.com/auth/\(value)/\(base64device)") {
|
|
||||||
print(url)
|
|
||||||
// Create a URLSession
|
|
||||||
let session = URLSession.shared
|
|
||||||
|
|
||||||
// Create a data task
|
|
||||||
let task = session.dataTask(with: url) { data, response, error in
|
|
||||||
// Handle errors
|
|
||||||
if let error = error {
|
|
||||||
exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check response and data
|
|
||||||
if let response = response as? HTTPURLResponse, response.statusCode == 200 {
|
|
||||||
print("Successfully Recieved API Data")
|
|
||||||
completion(true)
|
|
||||||
} else if let response = response as? HTTPURLResponse, response.statusCode == 201 {
|
|
||||||
print("Successfully Created Auth UUID")
|
|
||||||
completion(true)
|
|
||||||
} else {
|
|
||||||
completion(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)) {
|
||||||
// Start the task
|
if let updateInfo = updateInfo {
|
||||||
task.resume()
|
MeloNXUpdateSheet(updateInfo: updateInfo, isPresented: $showOutOfDateSheet)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
completion(false)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
completion(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// sends a GET request to the MeloNXSite API and compares the version it returns to the current app version
|
||||||
*/
|
func checkLatestVersion() {
|
||||||
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
|
||||||
|
let strippedAppVersion = appVersion.replacingOccurrences(of: ".", with: "")
|
||||||
|
|
||||||
func InitializeRyujinx(completion: @escaping (Bool) -> Void) {
|
#if DEBUG
|
||||||
let path = "aHR0cHM6Ly9teC5zdG9zc3kxMS5jb20v"
|
// no this isnt a public ip address silly viewers (i know damn well someone thought this was my real ip), this is local :PP
|
||||||
|
let url = "http://192.168.178.116:8000/api/latest_release"
|
||||||
|
#else
|
||||||
|
// dont spam this :pray:
|
||||||
|
let url = "https://melonx.org/api/latest_release"
|
||||||
|
#endif
|
||||||
|
|
||||||
guard let value = Bundle.main.object(forInfoDictionaryKey: "MeloID") as? String, !value.isEmpty else {
|
// actually sends the request
|
||||||
completion(false)
|
AF.request(url).responseDecodable(of: LatestVersionResponse.self) { response in
|
||||||
return
|
switch response.result {
|
||||||
}
|
case .success(let latestVersionResponse):
|
||||||
|
let latestAPIVersionStripped = latestVersionResponse.version_number_stripped
|
||||||
|
if Int(strippedAppVersion) ?? 0 < Int(latestAPIVersionStripped) ?? 0 {
|
||||||
|
updateInfo = latestVersionResponse
|
||||||
if (detectRoms(path: path) != value) {
|
showOutOfDateSheet = true
|
||||||
completion(false)
|
}
|
||||||
}
|
case .failure(let error):
|
||||||
|
print("Error checking for new version: \(error)")
|
||||||
let configuration = URLSessionConfiguration.default
|
}
|
||||||
configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData
|
|
||||||
configuration.urlCache = nil
|
|
||||||
|
|
||||||
let session = URLSession(configuration: configuration)
|
|
||||||
|
|
||||||
guard let url = URL(string: addFolders(path)!) else {
|
|
||||||
completion(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = session.dataTask(with: url) { data, response, error in
|
|
||||||
if error != nil {
|
|
||||||
completion(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
|
||||||
completion(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpResponse.statusCode == 200 {
|
|
||||||
completion(true)
|
|
||||||
} else {
|
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
task.resume()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectRoms(path string: String) -> String {
|
func detectRoms(path string: String) -> String {
|
||||||
@ -259,8 +92,6 @@ func detectRoms(path string: String) -> String {
|
|||||||
return romHash.compactMap { String(format: "%02x", $0) }.joined()
|
return romHash.compactMap { String(format: "%02x", $0) }.joined()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func addFolders(_ folderPath: String) -> String? {
|
func addFolders(_ folderPath: String) -> String? {
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
if let data = Data(base64Encoded: folderPath),
|
if let data = Data(base64Encoded: folderPath),
|
||||||
@ -271,7 +102,6 @@ func addFolders(_ folderPath: String) -> String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
||||||
func print() {
|
func print() {
|
||||||
Swift.print(self)
|
Swift.print(self)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user