Files
Leeksov 4647310322 GLEGram 12.5 — Initial public release
Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
2026-04-06 09:48:12 +03:00

320 lines
12 KiB
Swift

import Foundation
import JSONUtilities
import PathKit
import Yams
public struct Project: BuildSettingsContainer {
public var basePath: Path
public var name: String
public var targets: [Target] {
didSet {
targetsMap = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
}
}
public var aggregateTargets: [AggregateTarget] {
didSet {
aggregateTargetsMap = Dictionary(uniqueKeysWithValues: aggregateTargets.map { ($0.name, $0) })
}
}
public var packages: [String: SwiftPackage]
public var settings: Settings
public var settingGroups: [String: Settings]
public var configs: [Config]
public var schemes: [Scheme]
public var breakpoints: [Breakpoint]
public var options: SpecOptions
public var attributes: [String: Any]
public var fileGroups: [String]
public var configFiles: [String: String]
public var include: [String] = []
public var projectReferences: [ProjectReference] = [] {
didSet {
projectReferencesMap = Dictionary(uniqueKeysWithValues: projectReferences.map { ($0.name, $0) })
}
}
private var targetsMap: [String: Target]
private var aggregateTargetsMap: [String: AggregateTarget]
private var projectReferencesMap: [String: ProjectReference]
public init(
basePath: Path = "",
name: String,
configs: [Config] = Config.defaultConfigs,
targets: [Target] = [],
aggregateTargets: [AggregateTarget] = [],
settings: Settings = .empty,
settingGroups: [String: Settings] = [:],
schemes: [Scheme] = [],
breakpoints: [Breakpoint] = [],
packages: [String: SwiftPackage] = [:],
options: SpecOptions = SpecOptions(),
fileGroups: [String] = [],
configFiles: [String: String] = [:],
attributes: [String: Any] = [:],
projectReferences: [ProjectReference] = []
) {
self.basePath = basePath
self.name = name
self.targets = targets
targetsMap = Dictionary(uniqueKeysWithValues: self.targets.map { ($0.name, $0) })
self.aggregateTargets = aggregateTargets
aggregateTargetsMap = Dictionary(uniqueKeysWithValues: self.aggregateTargets.map { ($0.name, $0) })
self.configs = configs
self.settings = settings
self.settingGroups = settingGroups
self.schemes = schemes
self.breakpoints = breakpoints
self.packages = packages
self.options = options
self.fileGroups = fileGroups
self.configFiles = configFiles
self.attributes = attributes
self.projectReferences = projectReferences
projectReferencesMap = Dictionary(uniqueKeysWithValues: self.projectReferences.map { ($0.name, $0) })
}
public func getProjectReference(_ projectName: String) -> ProjectReference? {
projectReferencesMap[projectName]
}
public func getTarget(_ targetName: String) -> Target? {
targetsMap[targetName]
}
public func getPackage(_ packageName: String) -> SwiftPackage? {
packages[packageName]
}
public func getAggregateTarget(_ targetName: String) -> AggregateTarget? {
aggregateTargetsMap[targetName]
}
public func getProjectTarget(_ targetName: String) -> ProjectTarget? {
targetsMap[targetName] ?? aggregateTargetsMap[targetName]
}
public func getConfig(_ configName: String) -> Config? {
configs.first { $0.name == configName }
}
public var defaultProjectPath: Path {
basePath + "\(name).xcodeproj"
}
}
extension Project: CustomDebugStringConvertible {
public var debugDescription: String {
var string = "Name: \(name)"
let indent = " "
if !include.isEmpty {
string += "\nInclude:\n\(indent)" + include.map { $0.description }.joined(separator: "\n\(indent)")
}
if !settingGroups.isEmpty {
string += "\nSetting Groups:\n\(indent)" + settingGroups.keys
.sorted()
.joined(separator: "\n\(indent)")
}
if !targets.isEmpty {
string += "\nTargets:\n\(indent)" + targets.map { $0.description }.joined(separator: "\n\(indent)")
}
if !aggregateTargets.isEmpty {
string += "\nAggregate Targets:\n\(indent)" + aggregateTargets.map { $0.description }.joined(separator: "\n\(indent)")
}
if !schemes.isEmpty {
let allSchemes = targets.filter { $0.scheme != nil }.map { $0.name } + schemes.map { $0.name }
string += "\nSchemes:\n\(indent)" + allSchemes.joined(separator: "\n\(indent)")
}
return string
}
}
extension Project: Equatable {
public static func == (lhs: Project, rhs: Project) -> Bool {
lhs.name == rhs.name &&
lhs.targets == rhs.targets &&
lhs.aggregateTargets == rhs.aggregateTargets &&
lhs.settings == rhs.settings &&
lhs.settingGroups == rhs.settingGroups &&
lhs.configs == rhs.configs &&
lhs.schemes == rhs.schemes &&
lhs.breakpoints == rhs.breakpoints &&
lhs.fileGroups == rhs.fileGroups &&
lhs.configFiles == rhs.configFiles &&
lhs.options == rhs.options &&
lhs.packages == rhs.packages &&
NSDictionary(dictionary: lhs.attributes).isEqual(to: rhs.attributes)
}
}
extension Project {
public init(path: Path) throws {
let spec = try SpecFile(path: path)
try self.init(spec: spec)
}
public init(spec: SpecFile) throws {
try self.init(basePath: spec.basePath, jsonDictionary: spec.resolvedDictionary())
}
public init(basePath: Path = "", jsonDictionary: JSONDictionary) throws {
self.basePath = basePath
let jsonDictionary = Project.resolveProject(jsonDictionary: jsonDictionary)
let buildSettingsParser = BuildSettingsParser(jsonDictionary: jsonDictionary)
name = try jsonDictionary.json(atKeyPath: "name")
settings = try buildSettingsParser.parse()
settingGroups = try buildSettingsParser.parseSettingGroups()
let configs: [String: String] = jsonDictionary.json(atKeyPath: "configs") ?? [:]
self.configs = configs.isEmpty ? Config.defaultConfigs :
configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }.sorted { $0.name < $1.name }
targets = try jsonDictionary.json(atKeyPath: "targets", parallel: true).sorted { $0.name < $1.name }
aggregateTargets = try jsonDictionary.json(atKeyPath: "aggregateTargets").sorted { $0.name < $1.name }
projectReferences = try jsonDictionary.json(atKeyPath: "projectReferences").sorted { $0.name < $1.name }
schemes = try jsonDictionary.json(atKeyPath: "schemes")
if jsonDictionary["breakpoints"] != nil {
breakpoints = try jsonDictionary.json(atKeyPath: "breakpoints", invalidItemBehaviour: .fail)
} else {
breakpoints = []
}
fileGroups = jsonDictionary.json(atKeyPath: "fileGroups") ?? []
configFiles = jsonDictionary.json(atKeyPath: "configFiles") ?? [:]
attributes = jsonDictionary.json(atKeyPath: "attributes") ?? [:]
include = jsonDictionary.json(atKeyPath: "include") ?? []
if jsonDictionary["packages"] != nil {
packages = try jsonDictionary.json(atKeyPath: "packages", invalidItemBehaviour: .fail)
} else {
packages = [:]
}
// For backward compatibility of old `localPackages:` format
if let localPackages: [String] = jsonDictionary.json(atKeyPath: "localPackages") {
packages.merge(localPackages.reduce(into: [String: SwiftPackage]()) {
// Project name will be obtained by resolved abstractpath's lastComponent for dealing with some path case, like "../"
let packageName = (basePath + Path($1).normalize()).lastComponent
$0[packageName] = .local(path: $1, group: nil, excludeFromProject: false)
}
)
}
if jsonDictionary["options"] != nil {
options = try jsonDictionary.json(atKeyPath: "options")
} else {
options = SpecOptions()
}
targetsMap = Dictionary(uniqueKeysWithValues: targets.map { ($0.name, $0) })
aggregateTargetsMap = Dictionary(uniqueKeysWithValues: aggregateTargets.map { ($0.name, $0) })
projectReferencesMap = Dictionary(uniqueKeysWithValues: projectReferences.map { ($0.name, $0) })
}
static func resolveProject(jsonDictionary: JSONDictionary) -> JSONDictionary {
var jsonDictionary = jsonDictionary
// resolve multiple times so that we support both multi-platform templates,
// as well as platform specific templates in multi-platform targets
jsonDictionary = Target.resolveMultiplatformTargets(jsonDictionary: jsonDictionary)
jsonDictionary = Target.resolveTargetTemplates(jsonDictionary: jsonDictionary)
jsonDictionary = Scheme.resolveSchemeTemplates(jsonDictionary: jsonDictionary)
jsonDictionary = Target.resolveMultiplatformTargets(jsonDictionary: jsonDictionary)
return jsonDictionary
}
}
extension Project: PathContainer {
static var pathProperties: [PathProperty] {
[
.string("configFiles"),
.object("options", SpecOptions.pathProperties),
.object("targets", Target.pathProperties),
.object("targetTemplates", Target.pathProperties),
.object("aggregateTargets", AggregateTarget.pathProperties),
.object("schemes", Scheme.pathProperties),
.object("projectReferences", ProjectReference.pathProperties),
.object("packages", SwiftPackage.pathProperties),
.string("localPackages"),
.string("fileGroups")
]
}
}
extension Project {
public var allFiles: [Path] {
var files: [Path] = []
files.append(contentsOf: configFilePaths)
for fileGroup in fileGroups {
let fileGroupPath = basePath + fileGroup
let fileGroupChildren = (try? fileGroupPath.recursiveChildren()) ?? []
files.append(contentsOf: fileGroupChildren)
files.append(fileGroupPath)
}
for target in aggregateTargets {
files.append(contentsOf: target.configFilePaths)
}
for target in targets {
files.append(contentsOf: target.configFilePaths)
for source in target.sources {
let sourcePath = basePath + source.path
let sourceChildren = (try? sourcePath.recursiveChildren()) ?? []
files.append(contentsOf: sourceChildren)
files.append(sourcePath)
}
}
return files
}
}
extension BuildSettingsContainer {
fileprivate var configFilePaths: [Path] {
configFiles.values.map { Path($0) }
}
}
extension Project: JSONEncodable {
public func toJSONValue() -> Any {
toJSONDictionary()
}
public func toJSONDictionary() -> JSONDictionary {
let targetPairs = targets.map { ($0.name, $0.toJSONValue()) }
let configsPairs = configs.map { ($0.name, $0.type?.rawValue) }
let aggregateTargetsPairs = aggregateTargets.map { ($0.name, $0.toJSONValue()) }
let schemesPairs = schemes.map { ($0.name, $0.toJSONValue()) }
let projectReferencesPairs = projectReferences.map { ($0.name, $0.toJSONValue()) }
var dictionary: JSONDictionary = [:]
dictionary["name"] = name
dictionary["options"] = options.toJSONValue()
dictionary["settings"] = settings.toJSONValue()
dictionary["fileGroups"] = fileGroups
dictionary["configFiles"] = configFiles
dictionary["include"] = include
dictionary["attributes"] = attributes
dictionary["packages"] = packages.mapValues { $0.toJSONValue() }
dictionary["targets"] = Dictionary(uniqueKeysWithValues: targetPairs)
dictionary["configs"] = Dictionary(uniqueKeysWithValues: configsPairs)
dictionary["aggregateTargets"] = Dictionary(uniqueKeysWithValues: aggregateTargetsPairs)
dictionary["schemes"] = Dictionary(uniqueKeysWithValues: schemesPairs)
dictionary["settingGroups"] = settingGroups.mapValues { $0.toJSONValue() }
dictionary["projectReferences"] = Dictionary(uniqueKeysWithValues: projectReferencesPairs)
return dictionary
}
}