From b4caf9dd8ed5ac23cc462a4d68b021b75f730a5e Mon Sep 17 00:00:00 2001 From: Stephen <158498287+StephenDev0@users.noreply.github.com> Date: Sun, 31 May 2026 21:02:17 -0400 Subject: [PATCH] Clean up app --- StikDebug/App/AppFeature.swift | 11 +- StikDebug/Scripts/attachDetach.js | 18 - StikDebug/Scripts/manic.js | 176 -------- StikDebug/Support/AutoScriptAssignments.swift | 7 +- StikDebug/Support/ScriptStore.swift | 2 - StikDebug/Support/UserDefaults+Keys.swift | 2 +- StikDebug/Views/HomeView.swift | 4 - StikDebug/Views/NewsView.swift | 379 ------------------ news.json | 13 - 9 files changed, 8 insertions(+), 604 deletions(-) delete mode 100644 StikDebug/Scripts/attachDetach.js delete mode 100644 StikDebug/Scripts/manic.js delete mode 100644 StikDebug/Views/NewsView.swift delete mode 100644 news.json diff --git a/StikDebug/App/AppFeature.swift b/StikDebug/App/AppFeature.swift index 2183805..23234ca 100644 --- a/StikDebug/App/AppFeature.swift +++ b/StikDebug/App/AppFeature.swift @@ -9,7 +9,6 @@ enum AppFeature: String, CaseIterable, Identifiable { case home case scripts case tools - case news case console case deviceInfo = "deviceinfo" case profiles @@ -29,8 +28,6 @@ enum AppFeature: String, CaseIterable, Identifiable { return "Scripts" case .tools: return "Tools" - case .news: - return "News" case .console: return "Console" case .deviceInfo: @@ -54,8 +51,6 @@ enum AppFeature: String, CaseIterable, Identifiable { return "Manage and run JS scripts" case .tools: return "Access additional tools" - case .news: - return "Latest StikDebug updates" case .console: return "Live device logs" case .deviceInfo: @@ -88,8 +83,6 @@ enum AppFeature: String, CaseIterable, Identifiable { return "scroll" case .tools: return "wrench.and.screwdriver" - case .news: - return "newspaper" case .console: return "terminal" case .deviceInfo: @@ -114,8 +107,6 @@ enum AppFeature: String, CaseIterable, Identifiable { ScriptListView() case .tools: ToolsView() - case .news: - NewsView() case .console: ConsoleLogsView() case .deviceInfo: @@ -133,6 +124,6 @@ enum AppFeature: String, CaseIterable, Identifiable { } extension AppFeature { - static let mainTabs: [AppFeature] = [.home, .tools, .news, .settings] + static let mainTabs: [AppFeature] = [.home, .tools, .settings] static let toolList: [AppFeature] = [.scripts, .console, .deviceInfo, .profiles, .processes, .location] } diff --git a/StikDebug/Scripts/attachDetach.js b/StikDebug/Scripts/attachDetach.js deleted file mode 100644 index 35dfc17..0000000 --- a/StikDebug/Scripts/attachDetach.js +++ /dev/null @@ -1,18 +0,0 @@ -function attach() { - log("Welcome to use StikDebug's scripting feature! Here you can use JavaScript to customize your debug experience!") - log("This script is a demo script that attaches to the connected app and detaches from it.") - - // get pid of the connected app - let pid = get_pid(); - log(`pid = ${pid}`) - - // attach - let attachResponse = send_command(`vAttach;${pid.toString(16)}`) - log(`attach_response = ${attachResponse}`) - - // detach - let detachResponse = send_command(`D`) - log(`detachResponse = ${detachResponse}`) -} - -attach() diff --git a/StikDebug/Scripts/manic.js b/StikDebug/Scripts/manic.js deleted file mode 100644 index 9cf9857..0000000 --- a/StikDebug/Scripts/manic.js +++ /dev/null @@ -1,176 +0,0 @@ -// manic.js - iOS 26 TXM JIT Support Script for Manic EMU Emulator -// Optimized for Manic EMU using Geode.js's infinite loop mode -// Features: -// 1. Only handles brk #0x69 (JIT memory mapping requests) -// 2. Keeps StikDebug connection alive indefinitely -// 3. Supports JIT memory requests for multiple code blocks - -function littleEndianHexStringToNumber(hexStr) { - const bytes = []; - for (let i = 0; i < hexStr.length; i += 2) { - bytes.push(parseInt(hexStr.substr(i, 2), 16)); - } - let num = 0n; - for (let i = 4; i >= 0; i--) { - num = (num << 8n) | BigInt(bytes[i]); - } - return num; -} - -function numberToLittleEndianHexString(num) { - const bytes = []; - for (let i = 0; i < 5; i++) { - bytes.push(Number(num & 0xFFn)); - num >>= 8n; - } - while (bytes.length < 8) { - bytes.push(0); - } - return bytes.map(b => b.toString(16).padStart(2, '0')).join(''); -} - -function littleEndianHexToU32(hexStr) { - return parseInt(hexStr.match(/../g).reverse().join(''), 16); -} - -function extractBrkImmediate(u32) { - return (u32 >> 5) & 0xFFFF; -} - -// Check if the instruction is a BRK instruction -// BRK instruction format: 0xD4200000 | (imm16 << 5) -// The upper 16 bits should be 0xD420 -function isBrkInstruction(u32) { - return (u32 >>> 16) === 0xD420; -} - -// Format size into a human-readable format -function formatSize(size) { - if (size >= 1024 * 1024) { - return `${(size / (1024 * 1024)).toFixed(2)} MB`; - } else if (size >= 1024) { - return `${(size / 1024).toFixed(2)} KB`; - } - return `${size} bytes`; -} - -log(`[Manic EMU] ========================================`); -log(`[Manic EMU] Manic EMU iOS 26 TXM JIT Support Script`); -log(`[Manic EMU] ========================================`); - -let pid = get_pid(); -log(`[Manic EMU] pid = ${pid}`); -let attachResponse = send_command(`vAttach;${pid.toString(16)}`); -log(`[Manic EMU] attach_response = ${attachResponse}`); - -let validBreakpoints = 0; -let totalBreakpoints = 0; -let totalMemoryMapped = 0n; - -// Track processed memory areas for debugging. -let mappedRegions = []; - -// Infinite Loop - StikDebug Must Stay Connected in TXM Mode -// The Manic EMU emulator dynamically creates multiple code blocks, each of which needs to be marked as executable by StikDebug. -log(`[Manic EMU] Starting infinite loop - StikDebug will stay connected`); -log(`[Manic EMU] Waiting for JIT memory requests (brk #0x69)...`); - -while (true) { - totalBreakpoints++; - - let brkResponse = send_command(`c`); - - // Check Exception Types (metype) - // metype:6 = EXC_BREAKPOINT - let metypeMatch = /metype:(\d+)/.exec(brkResponse); - let metype = metypeMatch ? parseInt(metypeMatch[1]) : -1; - - // If it's not a breakpoint exception, just continue (let the program handle it). - if (metype !== 6 && metype !== -1) { - log(`[Manic EMU] Non-breakpoint exception (metype=${metype}), continuing...`); - continue; - } - - let tidMatch = /T[0-9a-f]+thread:(?[0-9a-f]+);/.exec(brkResponse); - let tid = tidMatch ? tidMatch.groups['tid'] : null; - let pcMatch = /20:(?[0-9a-f]{16});/.exec(brkResponse); - let pc = pcMatch ? pcMatch.groups['reg'] : null; - let x0Match = /00:(?[0-9a-f]{16});/.exec(brkResponse); - let x0 = x0Match ? x0Match.groups['reg'] : null; - let x1Match = /01:(?[0-9a-f]{16});/.exec(brkResponse); - let x1 = x1Match ? x1Match.groups['reg'] : null; - - if (!tid || !pc || !x0) { - log(`[Manic EMU] Failed to extract registers, continuing...`); - continue; - } - - const pcNum = littleEndianHexStringToNumber(pc); - const x0Num = littleEndianHexStringToNumber(x0); - const x1Num = x1 ? littleEndianHexStringToNumber(x1) : 0n; - - let instructionResponse = send_command(`m${pcNum.toString(16)},4`); - let instrU32 = littleEndianHexToU32(instructionResponse); - - // Check if it's a BRK instruction. - if (!isBrkInstruction(instrU32)) { - // Not a BRK instruction, skip it. - log(`[Manic EMU] Not a BRK instruction at PC=0x${pcNum.toString(16)}, skipping...`); - continue; - } - - let brkImmediate = extractBrkImmediate(instrU32); - - // Only handle brk #0x69 (JIT memory mapping request). - if (brkImmediate === 0x69) { - validBreakpoints++; - - let jitPageAddress = x0Num; - // If x1 is 0, use the default size of 64KB (0x10000). - // Manic EMU's CodeBlock typically requests large memory chunks (tens of MB for CPU JIT) - // but also asks for smaller ones (like 4KB/16KB for SpinLock and Shader JIT). - let size = x1Num > 0n ? x1Num : 0x10000n; - - log(`[Manic EMU] ----------------------------------------`); - log(`[Manic EMU] JIT Request #${validBreakpoints}`); - log(`[Manic EMU] Address: 0x${jitPageAddress.toString(16)}`); - log(`[Manic EMU] Size: 0x${size.toString(16)} (${formatSize(Number(size))})`); - - // Call prepare_memory_region to mark the memory as executable. - let prepareJITPageResponse = prepare_memory_region(Number(jitPageAddress), Number(size)); - log(`[Manic EMU] prepare_memory_region result: ${prepareJITPageResponse}`); - - // Log statistics - totalMemoryMapped += size; - mappedRegions.push({ - address: `0x${jitPageAddress.toString(16)}`, - size: Number(size), - index: validBreakpoints - }); - - log(`[Manic EMU] Total JIT memory mapped: ${formatSize(Number(totalMemoryMapped))}`); - - // Set PC+4 to continue program execution - let pcPlus4 = numberToLittleEndianHexString(pcNum + 4n); - send_command(`P20=${pcPlus4};thread:${tid};`); - - log(`[Manic EMU] Resumed execution at PC=0x${(pcNum + 4n).toString(16)}`); - log(`[Manic EMU] ----------------------------------------`); - - } else if (brkImmediate === 0x70 || brkImmediate === 0x71) { - // brk #0x70 and brk #0x71 are breakpoints used by some debuggers. - // Skip directly to PC+4 and continue execution. - log(`[Manic EMU] Debug breakpoint brk #0x${brkImmediate.toString(16)} at PC=0x${pcNum.toString(16)}, skipping...`); - let pcPlus4 = numberToLittleEndianHexString(pcNum + 4n); - send_command(`P20=${pcPlus4};thread:${tid};`); - - } else { - // Other BRK instructions, skip PC+4 and continue execution. - log(`[Manic EMU] Unknown brk #0x${brkImmediate.toString(16)} at PC=0x${pcNum.toString(16)}, skipping...`); - let pcPlus4 = numberToLittleEndianHexString(pcNum + 4n); - send_command(`P20=${pcPlus4};thread:${tid};`); - } -} - -// This line of code will never run because it's an infinite loop -// log(`[Manic EMU] Script ended`); diff --git a/StikDebug/Support/AutoScriptAssignments.swift b/StikDebug/Support/AutoScriptAssignments.swift index 27fd983..6457e41 100644 --- a/StikDebug/Support/AutoScriptAssignments.swift +++ b/StikDebug/Support/AutoScriptAssignments.swift @@ -19,11 +19,16 @@ extension ScriptStore { "MeloCafe", "Manic EMU", "Manic", - "Geode", "DukeX", ], resource: ScriptResource(resourceName: "universal", fileName: "universal.js") ), + AutoScriptAssignment( + appNames: [ + "Geode" + ], + resource: ScriptResource(resourceName: "Geode", fileName: "Geode.js") + ), AutoScriptAssignment( appNames: [ "UTM", diff --git a/StikDebug/Support/ScriptStore.swift b/StikDebug/Support/ScriptStore.swift index bee09e8..0e609a3 100644 --- a/StikDebug/Support/ScriptStore.swift +++ b/StikDebug/Support/ScriptStore.swift @@ -17,11 +17,9 @@ enum ScriptStore { static let favoriteAppNamesKey = "favoriteAppNames" static let defaultScriptName = UserDefaults.Keys.defaultScriptNameValue static let bundledResources: [ScriptResource] = [ - ScriptResource(resourceName: "attachDetach", fileName: "attachDetach.js"), ScriptResource(resourceName: "maciOS", fileName: "maciOS.js"), ScriptResource(resourceName: "universal", fileName: "universal.js"), ScriptResource(resourceName: "Geode", fileName: "Geode.js"), - ScriptResource(resourceName: "manic", fileName: "manic.js"), ScriptResource(resourceName: "UTM-Dolphin", fileName: "UTM-Dolphin.js") ] diff --git a/StikDebug/Support/UserDefaults+Keys.swift b/StikDebug/Support/UserDefaults+Keys.swift index f6ce75c..014ef21 100644 --- a/StikDebug/Support/UserDefaults+Keys.swift +++ b/StikDebug/Support/UserDefaults+Keys.swift @@ -11,6 +11,6 @@ extension UserDefaults { static let txmOverride = "overrideTXMForScripts" static let bundleScriptMap = "BundleScriptMap" static let defaultScriptName = "DefaultScriptName" - static let defaultScriptNameValue = "attachDetach.js" + static let defaultScriptNameValue = "" } } diff --git a/StikDebug/Views/HomeView.swift b/StikDebug/Views/HomeView.swift index caf8ba0..1bea75b 100644 --- a/StikDebug/Views/HomeView.swift +++ b/StikDebug/Views/HomeView.swift @@ -10,7 +10,6 @@ import SwiftUI struct HomeView: View { @AppStorage("autoQuitAfterEnablingJIT") private var doAutoQuitAfterEnablingJIT = false @AppStorage("bundleID") private var bundleID: String = "" - @AppStorage(UserDefaults.Keys.defaultScriptName) private var selectedScript = UserDefaults.Keys.defaultScriptNameValue @ObservedObject private var mounting = MountingProgress.shared @@ -114,9 +113,6 @@ struct HomeView: View { return } - if let name = notification.userInfo?["scriptName"] as? String { - selectedScript = name - } scriptRunModel = model } diff --git a/StikDebug/Views/NewsView.swift b/StikDebug/Views/NewsView.swift deleted file mode 100644 index 3599a19..0000000 --- a/StikDebug/Views/NewsView.swift +++ /dev/null @@ -1,379 +0,0 @@ -// -// NewsView.swift -// StikDebug -// - -import Foundation -import SwiftUI -import UIKit - -struct NewsView: View { - @State private var items: [NewsItem] = [] - @State private var isLoading = false - @State private var errorMessage: String? - - private let feedURL = NewsFeedEndpoint.url - - var body: some View { - NavigationStack { - ScrollView { - LazyVStack(spacing: 12) { - if items.isEmpty { - statusContent - } else { - if let errorMessage { - NewsBanner(message: errorMessage) - } - - ForEach(items) { item in - NewsCard(item: item) - } - } - } - .padding(.horizontal, 16) - .padding(.vertical, 14) - } - .background(Color(uiColor: .systemGroupedBackground)) - .navigationTitle("News") - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button { - Task { await loadNews() } - } label: { - Label("Refresh", systemImage: "arrow.clockwise") - } - .disabled(isLoading) - } - } - .refreshable { - await loadNews() - } - } - .task { - guard items.isEmpty, errorMessage == nil else { return } - await loadNews() - } - } - - @ViewBuilder - private var statusContent: some View { - if isLoading { - VStack(spacing: 12) { - ProgressView() - Text("Loading News") - .font(.callout.weight(.medium)) - .foregroundStyle(.secondary) - } - .frame(maxWidth: .infinity, minHeight: 220) - } else if let errorMessage { - ContentUnavailableView { - Label("Could Not Load News", systemImage: "exclamationmark.triangle") - } description: { - Text(errorMessage) - } actions: { - Button("Try Again") { - Task { await loadNews() } - } - } - .frame(minHeight: 280) - } else { - ContentUnavailableView("No News", systemImage: "newspaper") - .frame(minHeight: 280) - } - } - - @MainActor - private func loadNews() async { - guard !isLoading else { return } - - isLoading = true - errorMessage = nil - - do { - let feed = try await NewsFeedClient.fetch(from: feedURL) - items = feed.items - } catch { - errorMessage = error.localizedDescription - } - - isLoading = false - } -} - -private enum NewsFeedEndpoint { - static let url = URL(string: "https://raw.githubusercontent.com/StephenDev0/StikDebug/main/news.json")! -} - -private enum NewsFeedClient { - static func fetch(from url: URL) async throws -> NewsFeed { - var request = URLRequest(url: url) - request.timeoutInterval = 15 - request.cachePolicy = .reloadRevalidatingCacheData - - let (data, response) = try await URLSession.shared.data(for: request) - - guard let httpResponse = response as? HTTPURLResponse else { - throw NewsFeedError.invalidResponse - } - - guard (200..<300).contains(httpResponse.statusCode) else { - throw NewsFeedError.badStatus(httpResponse.statusCode) - } - - return try JSONDecoder().decode(NewsFeed.self, from: data) - } -} - -private enum NewsFeedError: LocalizedError { - case invalidResponse - case badStatus(Int) - - var errorDescription: String? { - switch self { - case .invalidResponse: - return "GitHub returned an invalid news response." - case .badStatus(let statusCode): - return "GitHub returned status \(statusCode)." - } - } -} - -private struct NewsFeed: Decodable { - let items: [NewsItem] - - enum CodingKeys: String, CodingKey { - case items - } - - init(from decoder: Decoder) throws { - if let container = try? decoder.singleValueContainer(), - let items = try? container.decode([NewsItem].self) { - self.items = items - return - } - - let container = try decoder.container(keyedBy: CodingKeys.self) - items = try container.decode([NewsItem].self, forKey: .items) - } -} - -private struct NewsItem: Decodable, Identifiable { - let id: String - let title: String - let body: String - let date: String - let category: String - let tint: String - let symbol: String - let imageURL: URL? - let url: URL? - - enum CodingKeys: String, CodingKey { - case id - case title - case body - case summary - case description - case date - case publishedAt - case category - case tint - case color - case symbol - case image - case imageURL - case imageUrl - case image_url - case link - case url - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - title = try container.decode(String.self, forKey: .title) - body = try container.decodeFirstString(for: [.body, .summary, .description]) - date = (try? container.decodeFirstString(for: [.date, .publishedAt])) ?? "" - category = (try? container.decode(String.self, forKey: .category)) ?? "Update" - tint = (try? container.decodeFirstString(for: [.tint, .color])) ?? "blue" - symbol = (try? container.decode(String.self, forKey: .symbol)) ?? "sparkles" - imageURL = container.decodeFirstURL(for: [.imageURL, .imageUrl, .image_url, .image]) - url = container.decodeFirstURL(for: [.url, .link]) - id = (try? container.decode(String.self, forKey: .id)) ?? Self.generatedID(title: title, date: date) - } - - private static func generatedID(title: String, date: String) -> String { - "\(date)-\(title)" - .lowercased() - .replacingOccurrences(of: #"[^a-z0-9]+"#, with: "-", options: .regularExpression) - .trimmingCharacters(in: CharacterSet(charactersIn: "-")) - } -} - -private extension KeyedDecodingContainer where Key == NewsItem.CodingKeys { - func decodeFirstString(for keys: [Key]) throws -> String { - for key in keys { - if let value = try decodeIfPresent(String.self, forKey: key), !value.isEmpty { - return value - } - } - - throw DecodingError.keyNotFound( - keys.first ?? NewsItem.CodingKeys.body, - DecodingError.Context(codingPath: codingPath, debugDescription: "Expected one of \(keys.map { $0.stringValue }.joined(separator: ", ")).") - ) - } - - func decodeFirstURL(for keys: [Key]) -> URL? { - for key in keys { - if let value = try? decodeIfPresent(String.self, forKey: key), - !value.isEmpty, - let url = URL(string: value) { - return url - } - } - - return nil - } -} - -private struct NewsCard: View { - let item: NewsItem - - private var accentColor: Color { - NewsTint.color(for: item.tint) - } - - var body: some View { - VStack(alignment: .leading, spacing: 12) { - NewsMedia(item: item, accentColor: accentColor) - - HStack(alignment: .firstTextBaseline) { - Text(item.category.uppercased()) - .font(.caption.weight(.bold)) - .foregroundStyle(accentColor) - Spacer(minLength: 12) - if !item.date.isEmpty { - Text(item.date) - .font(.caption) - .foregroundStyle(.secondary) - } - } - - Text(item.title) - .font(.title3.weight(.bold)) - .foregroundStyle(.primary) - .fixedSize(horizontal: false, vertical: true) - - Text(item.body) - .font(.body) - .foregroundStyle(.secondary) - .fixedSize(horizontal: false, vertical: true) - - if let url = item.url { - Link(destination: url) { - Label("Read More", systemImage: "arrow.up.right") - .font(.callout.weight(.semibold)) - } - .buttonStyle(.bordered) - .padding(.top, 2) - } - } - .padding(14) - .background(Color(uiColor: .secondarySystemGroupedBackground), in: RoundedRectangle(cornerRadius: 8, style: .continuous)) - .overlay { - RoundedRectangle(cornerRadius: 8, style: .continuous) - .stroke(Color(uiColor: .separator).opacity(0.18), lineWidth: 1) - } - } -} - -private struct NewsMedia: View { - let item: NewsItem - let accentColor: Color - - var body: some View { - Group { - if let imageURL = item.imageURL { - AsyncImage(url: imageURL, transaction: Transaction(animation: .easeInOut)) { phase in - switch phase { - case .empty: - ZStack { - mediaBackground - ProgressView() - } - case .success(let image): - image - .resizable() - .scaledToFill() - case .failure: - symbolHeader - @unknown default: - symbolHeader - } - } - } else { - symbolHeader - } - } - .frame(maxWidth: .infinity) - .aspectRatio(16 / 9, contentMode: .fit) - .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) - } - - private var symbolHeader: some View { - ZStack { - mediaBackground - Image(systemName: item.symbol) - .font(.system(size: 42, weight: .semibold)) - .foregroundStyle(accentColor) - } - } - - private var mediaBackground: some View { - Rectangle() - .fill(accentColor.opacity(0.14)) - .overlay(alignment: .bottom) { - Rectangle() - .fill(accentColor) - .frame(height: 4) - } - } -} - -private struct NewsBanner: View { - let message: String - - var body: some View { - Label(message, systemImage: "exclamationmark.triangle.fill") - .font(.footnote) - .foregroundStyle(.orange) - .frame(maxWidth: .infinity, alignment: .leading) - .padding(12) - .background(Color.orange.opacity(0.12), in: RoundedRectangle(cornerRadius: 8, style: .continuous)) - } -} - -private enum NewsTint { - static func color(for value: String) -> Color { - switch value.lowercased() { - case "green": - return .green - case "mint": - return .mint - case "orange": - return .orange - case "pink": - return .pink - case "purple": - return .purple - case "red": - return .red - case "teal": - return .teal - case "yellow": - return .yellow - default: - return .blue - } - } -} diff --git a/news.json b/news.json deleted file mode 100644 index 083c7db..0000000 --- a/news.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "items": [ - { - "id": "stikdebug-3-1-3", - "title": "StikDebug 3.1.3 is out", - "body": "Includes performance improvements, bug fixes, quality of life improvements, a new news tab, and more.", - "date": "2026-05-18", - "category": "Update", - "symbol": "newspaper.fill", - "tint": "blue" - } - ] -}