chore: Bunnyfication
.replaceAll("Pyoncord", "Bunny") 🧌
This commit is contained in:
parent
60be3dd5d1
commit
01804727b0
15 changed files with 66 additions and 67 deletions
12
Makefile
12
Makefile
|
|
@ -7,13 +7,13 @@ endif
|
|||
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
TWEAK_NAME = PyoncordTweak
|
||||
TWEAK_NAME = BunnyTweak
|
||||
|
||||
PyoncordTweak_FILES = $(shell find Sources/PyoncordTweak -name '*.swift') $(shell find Sources/PyoncordTweakC -name '*.m' -o -name '*.c' -o -name '*.mm' -o -name '*.cpp')
|
||||
PyoncordTweak_SWIFTFLAGS = -ISources/PyoncordTweakC/include
|
||||
PyoncordTweak_CFLAGS = -fobjc-arc -ISources/PyoncordTweakC/include
|
||||
BunnyTweak_FILES = $(shell find Sources/BunnyTweak -name '*.swift') $(shell find Sources/BunnyTweakC -name '*.m' -o -name '*.c' -o -name '*.mm' -o -name '*.cpp')
|
||||
BunnyTweak_SWIFTFLAGS = -ISources/BunnyTweakC/include
|
||||
BunnyTweak_CFLAGS = -fobjc-arc -ISources/BunnyTweakC/include
|
||||
|
||||
PyoncordTweak_BUNDLE_NAME = PyoncordPatches
|
||||
PyoncordTweak_BUNDLE_RESOURCE_DIRS = Resources
|
||||
BunnyTweak_BUNDLE_NAME = BunnyPatches
|
||||
BunnyTweak_BUNDLE_RESOURCE_DIRS = Resources
|
||||
|
||||
include $(THEOS_MAKE_PATH)/tweak.mk
|
||||
|
|
|
|||
|
|
@ -65,23 +65,23 @@ let swiftFlags: [String] = libFlags + [
|
|||
]
|
||||
|
||||
let package = Package(
|
||||
name: "PyoncordTweak",
|
||||
name: "BunnyTweak",
|
||||
platforms: [.iOS(deploymentTarget)],
|
||||
products: [
|
||||
.library(
|
||||
name: "PyoncordTweak",
|
||||
targets: ["PyoncordTweak"]
|
||||
name: "BunnyTweak",
|
||||
targets: ["BunnyTweak"]
|
||||
),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "PyoncordTweakC",
|
||||
name: "BunnyTweakC",
|
||||
cSettings: [.unsafeFlags(cFlags)],
|
||||
cxxSettings: [.unsafeFlags(cxxFlags)]
|
||||
),
|
||||
.target(
|
||||
name: "PyoncordTweak",
|
||||
dependencies: ["PyoncordTweakC"],
|
||||
name: "BunnyTweak",
|
||||
dependencies: ["BunnyTweakC"],
|
||||
swiftSettings: [.unsafeFlags(swiftFlags)]
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ Builds can be found in the [Releases](https://github.com/pyoncord/BunnyTweak/rel
|
|||
### Jailbroken (Rootful/Rootless)
|
||||
|
||||
1. Install the Orion runtime via your preferred package manager, by adding `https://repo.chariz.com/` to your sources, then finding `Orion Runtime`.
|
||||
1. Install Bunny by downloading the appropriate `.deb` file (or by building your own, see [Building BunnyTweak locally](#building-pyoncordtweak-locally)). Use the file ending in `arm.deb` for rootful jailbreaks, and the file ending in `arm64.deb` for rootless jailbreaks.
|
||||
1. Install Bunny by downloading the appropriate `.deb` file (or by building your own, see [Building BunnyTweak locally](#building-bunnytweak-locally)). Use the file ending in `arm.deb` for rootful jailbreaks, and the file ending in `arm64.deb` for rootless jailbreaks.
|
||||
|
||||
### Non-Jailbroken
|
||||
<a href="https://tinyurl.com/yh455zk6"><img src="https://i.imgur.com/46qhEAv.png" width="230"></a>
|
||||
1. Download and install `Pyoncord.ipa` using your preferred sideloading method.
|
||||
1. Download and install `Bunny.ipa` using your preferred sideloading method.
|
||||
|
||||
## Building BunnyTweak locally
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
globalThis.__PYON_LOADER__ = {
|
||||
loaderName: "BunnyTweak",
|
||||
loaderVersion: "0.2.0",
|
||||
loaderVersion: "0.3.0",
|
||||
hasThemeSupport: true,
|
||||
storedTheme: null,
|
||||
fontPatch: 2
|
||||
storedTheme: null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import PyoncordTweakC
|
||||
import BunnyTweakC
|
||||
import Orion
|
||||
import UIKit
|
||||
import os
|
||||
|
|
@ -35,7 +35,7 @@ class FontsHook: ClassHook<UIFont> {
|
|||
|
||||
func patchFonts(_ main: [String: String], fontDefName: String) {
|
||||
for (fontName, url) in main {
|
||||
os_log("Replacing font %{public}@ with URL: %{public}@", log: pyoncordLog, type: .info, fontName, url)
|
||||
os_log("Replacing font %{public}@ with URL: %{public}@", log: bunnyLog, type: .info, fontName, url)
|
||||
|
||||
let fontExtension = URL(string: url)!.pathExtension
|
||||
let fontCachePath = pyoncordDirectory
|
||||
|
|
@ -45,26 +45,26 @@ func patchFonts(_ main: [String: String], fontDefName: String) {
|
|||
.appendingPathComponent("\(fontName).\(fontExtension)")
|
||||
|
||||
do {
|
||||
os_log("Attempting to register font %{public}@ from %{public}@", log: pyoncordLog, type: .info, fontName, url)
|
||||
os_log("Attempting to register font %{public}@ from %{public}@", log: bunnyLog, type: .info, fontName, url)
|
||||
|
||||
let parent = fontCachePath.deletingLastPathComponent()
|
||||
if !FileManager.default.fileExists(atPath: parent.path) {
|
||||
os_log("Creating parent directory: %{public}@", log: pyoncordLog, type: .debug, parent.path)
|
||||
os_log("Creating parent directory: %{public}@", log: bunnyLog, type: .debug, parent.path)
|
||||
try FileManager.default.createDirectory(
|
||||
at: parent, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
|
||||
// JS side should download these already, but just in case...
|
||||
if !FileManager.default.fileExists(atPath: fontCachePath.path) {
|
||||
os_log("Downloading font %{public}@ from %{public}@", log: pyoncordLog, type: .debug, fontName, url)
|
||||
os_log("Downloading font %{public}@ from %{public}@", log: bunnyLog, type: .debug, fontName, url)
|
||||
if let data = try? Data(contentsOf: URL(string: url)!) {
|
||||
os_log("Writing font data to: %{public}@", log: pyoncordLog, type: .debug, fontCachePath.path)
|
||||
os_log("Writing font data to: %{public}@", log: bunnyLog, type: .debug, fontCachePath.path)
|
||||
try? data.write(to: fontCachePath)
|
||||
}
|
||||
}
|
||||
|
||||
if let data = try? Data(contentsOf: fontCachePath) {
|
||||
os_log("Registering font %{public}@ with provider", log: pyoncordLog, type: .debug, fontName)
|
||||
os_log("Registering font %{public}@ with provider", log: bunnyLog, type: .debug, fontName)
|
||||
let provider = CGDataProvider(data: data as CFData)
|
||||
let font = CGFont(provider!)
|
||||
var error: Unmanaged<CFError>?
|
||||
|
|
@ -73,7 +73,7 @@ func patchFonts(_ main: [String: String], fontDefName: String) {
|
|||
if let existingFont = CGFont(font!.postScriptName!) {
|
||||
var unregisterError: Unmanaged<CFError>?
|
||||
if !CTFontManagerUnregisterGraphicsFont(existingFont, &unregisterError) {
|
||||
os_log("Failed to deregister font %{public}@: %{public}@", log: pyoncordLog, type: .error,
|
||||
os_log("Failed to deregister font %{public}@: %{public}@", log: bunnyLog, type: .error,
|
||||
font!.postScriptName! as String,
|
||||
String(describing: unregisterError!.takeUnretainedValue()))
|
||||
}
|
||||
|
|
@ -81,15 +81,15 @@ func patchFonts(_ main: [String: String], fontDefName: String) {
|
|||
|
||||
if CTFontManagerRegisterGraphicsFont(font!, &error) {
|
||||
fontMap[fontName] = font!.postScriptName! as String
|
||||
os_log("Successfully registered font %{public}@ to %{public}@", log: pyoncordLog, type: .info, fontName, font!.postScriptName! as String)
|
||||
os_log("Successfully registered font %{public}@ to %{public}@", log: bunnyLog, type: .info, fontName, font!.postScriptName! as String)
|
||||
} else {
|
||||
os_log("Failed to register font %{public}@: %{public}@", log: pyoncordLog, type: .error, fontName, String(describing: error!.takeUnretainedValue()))
|
||||
os_log("Failed to register font %{public}@: %{public}@", log: bunnyLog, type: .error, fontName, String(describing: error!.takeUnretainedValue()))
|
||||
}
|
||||
} else {
|
||||
os_log("Failed to read font data from: %{public}@", log: pyoncordLog, type: .error, fontCachePath.path)
|
||||
os_log("Failed to read font data from: %{public}@", log: bunnyLog, type: .error, fontCachePath.path)
|
||||
}
|
||||
} catch {
|
||||
os_log("Failed to register font %{public}@: %{public}@", log: pyoncordLog, type: .error, fontName, error.localizedDescription)
|
||||
os_log("Failed to register font %{public}@: %{public}@", log: bunnyLog, type: .error, fontName, error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ let pyoncordDirectory = getPyoncordDirectory()
|
|||
let loaderConfigUrl = pyoncordDirectory.appendingPathComponent("loader.json")
|
||||
|
||||
func getLoaderConfig() -> LoaderConfig {
|
||||
os_log("Getting loader config", log: pyoncordLog, type: .debug)
|
||||
os_log("Getting loader config", log: bunnyLog, type: .debug)
|
||||
let fileManager = FileManager.default
|
||||
|
||||
do {
|
||||
|
|
@ -33,14 +33,14 @@ func getLoaderConfig() -> LoaderConfig {
|
|||
let data = try Data(contentsOf: loaderConfigUrl)
|
||||
let loaderConfig = try JSONDecoder().decode(LoaderConfig.self, from: data)
|
||||
|
||||
os_log("Got loader config", log: pyoncordLog, type: .debug)
|
||||
os_log("Got loader config", log: bunnyLog, type: .debug)
|
||||
|
||||
return loaderConfig
|
||||
} else {
|
||||
throw LoaderConfigError.doesNotExist
|
||||
}
|
||||
} catch {
|
||||
os_log("Couldn't get loader config", log: pyoncordLog, type: .error)
|
||||
os_log("Couldn't get loader config", log: bunnyLog, type: .error)
|
||||
|
||||
return defaultLoaderConfig
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import Orion
|
||||
import UIKit
|
||||
import PyoncordTweakC
|
||||
import BunnyTweakC
|
||||
import os
|
||||
|
||||
struct Author: Codable {
|
||||
|
|
@ -56,7 +56,7 @@ func hexToUIColor(_ hex: String) -> UIColor? {
|
|||
}
|
||||
|
||||
func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) {
|
||||
os_log("Swizzling DCDThemeColor", log: pyoncordLog, type: .info)
|
||||
os_log("Swizzling DCDThemeColor", log: bunnyLog, type: .info)
|
||||
|
||||
let DCDTheme: AnyClass = NSClassFromString("DCDTheme")!
|
||||
let dcdThemeTarget: AnyClass = object_getClass(DCDTheme)!
|
||||
|
|
@ -68,25 +68,25 @@ func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) {
|
|||
let themeIndex: ThemeIndexType = unsafeBitCast(themeIndexImpl, to: ThemeIndexType.self)
|
||||
|
||||
let DCDThemeColor: AnyClass = NSClassFromString("DCDThemeColor")!
|
||||
os_log("Found instance of DCDThemeColor", log: pyoncordLog, type: .debug)
|
||||
os_log("Found instance of DCDThemeColor", log: bunnyLog, type: .debug)
|
||||
let target: AnyClass = object_getClass(DCDThemeColor)!
|
||||
|
||||
os_log("Found DCDThemeColor", log: pyoncordLog, type: .debug)
|
||||
os_log("Found DCDThemeColor", log: bunnyLog, type: .debug)
|
||||
|
||||
var methodCount: UInt32 = 0
|
||||
let methods = class_copyMethodList(target, &methodCount)!
|
||||
|
||||
os_log("DCDThemeColor has %{public}d methods", log: pyoncordLog, type: .debug, methodCount)
|
||||
os_log("DCDThemeColor has %{public}d methods", log: bunnyLog, type: .debug, methodCount)
|
||||
|
||||
for i in 0..<Int(methodCount) {
|
||||
let method = methods[i]
|
||||
let selector = method_getName(method)
|
||||
let methodName = NSStringFromSelector(selector)
|
||||
os_log(
|
||||
"Found method %{public}@", log: pyoncordLog, type: .debug, methodName)
|
||||
"Found method %{public}@", log: bunnyLog, type: .debug, methodName)
|
||||
if let semanticColor = semanticColors[methodName] {
|
||||
os_log(
|
||||
"Swizzling %{public}@", log: pyoncordLog, type: .debug, methodName)
|
||||
"Swizzling %{public}@", log: bunnyLog, type: .debug, methodName)
|
||||
|
||||
let originalImpl = method_getImplementation(method)
|
||||
typealias OriginalType = @convention(c) (AnyObject, Selector) -> UIColor
|
||||
|
|
@ -115,28 +115,28 @@ func swizzleDCDThemeColor(_ semanticColors: [String: [String]]) {
|
|||
}
|
||||
|
||||
func swizzleUIColor(_ rawColors: [String: String]) {
|
||||
os_log("Swizzling UIColor", log: pyoncordLog, type: .info)
|
||||
os_log("Swizzling UIColor", log: bunnyLog, type: .info)
|
||||
|
||||
let UIColor: AnyClass = NSClassFromString("UIColor")!
|
||||
os_log("Found instance of UIColor", log: pyoncordLog, type: .debug)
|
||||
os_log("Found instance of UIColor", log: bunnyLog, type: .debug)
|
||||
let target: AnyClass = object_getClass(UIColor)!
|
||||
|
||||
os_log("Found UIColor", log: pyoncordLog, type: .debug)
|
||||
os_log("Found UIColor", log: bunnyLog, type: .debug)
|
||||
|
||||
var methodCount: UInt32 = 0
|
||||
let methods = class_copyMethodList(target, &methodCount)!
|
||||
|
||||
os_log("UIColor has %{public}d methods", log: pyoncordLog, type: .debug, methodCount)
|
||||
os_log("UIColor has %{public}d methods", log: bunnyLog, type: .debug, methodCount)
|
||||
|
||||
for i in 0..<Int(methodCount) {
|
||||
let method = methods[i]
|
||||
let selector = method_getName(method)
|
||||
let methodName = NSStringFromSelector(selector)
|
||||
os_log(
|
||||
"Found method %{public}@", log: pyoncordLog, type: .debug, methodName)
|
||||
"Found method %{public}@", log: bunnyLog, type: .debug, methodName)
|
||||
if let rawColor = rawColors[methodName] {
|
||||
os_log(
|
||||
"Swizzling %{public}@", log: pyoncordLog, type: .debug, methodName)
|
||||
"Swizzling %{public}@", log: bunnyLog, type: .debug, methodName)
|
||||
let rawColorBlock: @convention(block) (AnyObject) -> UIColor = {
|
||||
(self: AnyObject) -> UIColor in
|
||||
return hexToUIColor(rawColor)!
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
import Orion
|
||||
import PyoncordTweakC
|
||||
import BunnyTweakC
|
||||
import os
|
||||
|
||||
let pyoncordLog = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "pyoncord")
|
||||
let bunnyLog = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "bunny")
|
||||
let source = URL(string: "bunny")!
|
||||
|
||||
let install_prefix = String(cString: get_install_prefix())
|
||||
let isJailbroken = FileManager.default.fileExists(atPath: "\(install_prefix)/Library/Application Support/PyoncordTweak/PyoncordPatches.bundle")
|
||||
let isJailbroken = FileManager.default.fileExists(atPath: "\(install_prefix)/Library/Application Support/BunnyTweak/BunnyPatches.bundle")
|
||||
|
||||
let pyoncordPatchesBundlePath = isJailbroken ? "\(install_prefix)/Library/Application Support/PyoncordTweak/PyoncordPatches.bundle" : "\(Bundle.main.bundleURL.path)/PyoncordPatches.bundle"
|
||||
let bunnyPatchesBundlePath = isJailbroken ? "\(install_prefix)/Library/Application Support/BunnyTweak/BunnyPatches.bundle" : "\(Bundle.main.bundleURL.path)/BunnyPatches.bundle"
|
||||
|
||||
class FileManagerLoadHook: ClassHook<FileManager> {
|
||||
func containerURLForSecurityApplicationGroupIdentifier(_ groupIdentifier: NSString?) -> URL? {
|
||||
os_log("containerURLForSecurityApplicationGroupIdentifier called! %{public}@ groupIdentifier", log: pyoncordLog, type: .debug, groupIdentifier ?? "nil")
|
||||
os_log("containerURLForSecurityApplicationGroupIdentifier called! %{public}@ groupIdentifier", log: bunnyLog, type: .debug, groupIdentifier ?? "nil")
|
||||
|
||||
if (isJailbroken) {
|
||||
return orig.containerURLForSecurityApplicationGroupIdentifier(groupIdentifier)
|
||||
|
|
@ -26,15 +26,15 @@ class FileManagerLoadHook: ClassHook<FileManager> {
|
|||
|
||||
class LoadHook: ClassHook<RCTCxxBridge> {
|
||||
func executeApplicationScript(_ script: Data, url: URL, async: Bool) {
|
||||
os_log("executeApplicationScript called!", log: pyoncordLog, type: .debug)
|
||||
os_log("executeApplicationScript called!", log: bunnyLog, type: .debug)
|
||||
|
||||
let loaderConfig = getLoaderConfig()
|
||||
|
||||
let pyoncordPatchesBundle = Bundle(path: pyoncordPatchesBundlePath)!
|
||||
let bunnyPatchesBundle = Bundle(path: bunnyPatchesBundlePath)!
|
||||
|
||||
if let patchPath = pyoncordPatchesBundle.url(forResource: "payload-base", withExtension: "js") {
|
||||
if let patchPath = bunnyPatchesBundle.url(forResource: "payload-base", withExtension: "js") {
|
||||
let patchData = try! Data(contentsOf: patchPath)
|
||||
os_log("Executing payload base", log: pyoncordLog, type: .debug)
|
||||
os_log("Executing payload base", log: bunnyLog, type: .debug)
|
||||
orig.executeApplicationScript(patchData, url: source, async: true)
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
var bundleUrl: URL
|
||||
if loaderConfig.customLoadUrl.enabled {
|
||||
os_log(
|
||||
"Custom load URL enabled, with URL %{public}@ ", log: pyoncordLog, type: .info,
|
||||
"Custom load URL enabled, with URL %{public}@ ", log: bunnyLog, type: .info,
|
||||
loaderConfig.customLoadUrl.url.absoluteString)
|
||||
bundleUrl = loaderConfig.customLoadUrl.url
|
||||
} else {
|
||||
|
|
@ -56,7 +56,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
string: "https://raw.githubusercontent.com/pyoncord/bunny-builds/main/bunny.js")!
|
||||
}
|
||||
|
||||
os_log("Fetching JS bundle", log: pyoncordLog, type: .info)
|
||||
os_log("Fetching JS bundle", log: bunnyLog, type: .info)
|
||||
var bundleRequest = URLRequest(
|
||||
url: bundleUrl, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 3.0)
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
|
||||
let fetchTask = URLSession.shared.dataTask(with: bundleRequest) { data, response, error in
|
||||
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
|
||||
os_log("Successfully fetched JS Bundle", log: pyoncordLog, type: .debug)
|
||||
os_log("Successfully fetched JS Bundle", log: bunnyLog, type: .debug)
|
||||
bundle = data
|
||||
try? bundle?.write(to: pyoncordDirectory.appendingPathComponent("bundle.js"))
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
for fileURL in contents {
|
||||
if fileURL.pathExtension == "js" {
|
||||
os_log(
|
||||
"Executing preload JS file %{public}@ ", log: pyoncordLog, type: .info, fileURL.absoluteString)
|
||||
"Executing preload JS file %{public}@ ", log: bunnyLog, type: .info, fileURL.absoluteString)
|
||||
|
||||
if let data = try? Data(contentsOf: fileURL) {
|
||||
orig.executeApplicationScript(data, url: source, async: async)
|
||||
|
|
@ -109,23 +109,23 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
}
|
||||
}
|
||||
} catch {
|
||||
os_log("Error reading contents of preloads directory", log: pyoncordLog, type: .error)
|
||||
os_log("Error reading contents of preloads directory", log: bunnyLog, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
if bundle != nil {
|
||||
os_log("Executing JS bundle", log: pyoncordLog, type: .info)
|
||||
os_log("Executing JS bundle", log: bunnyLog, type: .info)
|
||||
orig.executeApplicationScript(bundle!, url: source, async: async)
|
||||
} else {
|
||||
os_log("Unable to fetch JS bundle", log: pyoncordLog, type: .error)
|
||||
os_log("Unable to fetch JS bundle", log: bunnyLog, type: .error)
|
||||
}
|
||||
|
||||
os_log("Executing original script", log: pyoncordLog, type: .info)
|
||||
os_log("Executing original script", log: bunnyLog, type: .info)
|
||||
orig.executeApplicationScript(script, url: url, async: async)
|
||||
}
|
||||
}
|
||||
|
||||
struct PyoncordTweak: Tweak {
|
||||
struct BunnyTweak: Tweak {
|
||||
func tweakDidActivate() {
|
||||
if let themeData = try? Data(
|
||||
contentsOf: pyoncordDirectory.appendingPathComponent("current-theme.json")) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
module PyoncordTweakC {
|
||||
module BunnyTweakC {
|
||||
umbrella "."
|
||||
export *
|
||||
}
|
||||
6
control
6
control
|
|
@ -1,11 +1,11 @@
|
|||
Package: io.github.pyoncord.app
|
||||
Name: Bunny
|
||||
Version: 0.2.0
|
||||
Version: 0.3.0
|
||||
Architecture: iphoneos-arm
|
||||
Description: A mod for Discord's mobile apps.
|
||||
Maintainer: Pylix
|
||||
Author: FieryFlames
|
||||
Icon: https://avatars.githubusercontent.com/u/112445065
|
||||
Author: Pylix, FieryFlames
|
||||
Icon: https://avatars.githubusercontent.com/u/132727175
|
||||
Section: Tweaks
|
||||
Depends: ${ORION}, firmware (>= 13.0)
|
||||
Conflicts: app.enmity, mod.vendetta
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue