16 Commits

Author SHA1 Message Date
ΞTΞRNAL
bc252ea9a3 prepare for v1.3.2 2026-02-24 16:03:05 +05:30
particle-box
4aba9c815f multiple fixes 2026-02-24 12:51:25 +05:30
ΞTΞRNAL
05c71c7ef4 fix: checksum 2026-02-24 03:41:22 +05:30
ΞTΞRNAL
57ebadbc51 fix: checksum 2026-02-24 03:38:00 +05:30
particle-box
bdf3808526 fix: slow builds 2026-02-24 02:21:26 +05:30
particle-box
e8d1f62ff1 add translations back 2026-02-24 02:10:33 +05:30
particle-box
b64faf31ae Merge branch 'dev' of https://github.com/particle-box/PurrfectSnap into dev 2026-02-24 01:58:11 +05:30
particle-box
ed86916ed7 temporary removal 2026-02-24 01:57:47 +05:30
ΞTΞRNAL
5935afb6e1 fix(PR): Media file Picker and added mapper by RSR
fix MediaFilePicker on snap13.79 and added mapper
2026-02-18 11:43:03 +05:30
RSR/
1ac1542384 fix MediaFilePicker on snap13.79 and added mapper 2026-02-16 20:31:42 +04:00
ΞTΞRNAL
8eea50e745 fix: accidental change 2026-02-14 14:27:07 +05:30
ΞTΞRNAL
795bc1f96a address fdroid bot feedback
Added SHA256 checksum for Gradle distribution.
2026-02-14 14:26:06 +05:30
particle-box
0ab60d8b20 add icons and banner 2026-02-14 14:06:26 +05:30
particle-box
627abb3927 add support for frdroid 2026-02-14 14:04:17 +05:30
ΞTΞRNAL
f32a4c4a23 add buiding instructions 2026-02-14 13:39:53 +05:30
RSR/
e3008e0c9c fix 2026-02-07 19:20:39 +04:00
25 changed files with 435 additions and 381 deletions

257
README.md
View File

@@ -16,7 +16,7 @@
[![Downloads](https://img.shields.io/github/downloads/particle-box/PurrfectSnap/total?style=for-the-badge&color=f5bde6&labelColor=1e1e2e)](https://github.com/particle-box/PurrfectSnap/releases)
[![License](https://img.shields.io/badge/License-GPL_3.0-f5a97f?style=for-the-badge&labelColor=1e1e2e)](https://github.com/particle-box/PurrfectSnap/blob/dev/LICENSE)
[Installation](#installation) • [Features](#features) • [Changelog](#whats-new) • [Community](#community)
[Installation](#installation) • [Features](#features) • [Build](#build-from-source) • [Community](#community)
</div>
@@ -40,44 +40,9 @@ No aggressive donation requests. No minimal changes disguised as “major update
<br>
---
<br>
## 🔄 How It Works
```mermaid
graph TD
A[Install PurrfectSnap] --> B{Device Type?}
B -->|Non-Rooted| C[Auto Patcher Runs]
B -->|Rooted| D[Xposed Framework]
C --> E[Snapchat Patched Automatically]
D --> E
E --> F[Launch Snapchat]
F --> G[Access PurrfectSnap Settings]
G --> H[Configure Features]
H --> I{Enable Features}
I --> J[Media Downloader]
I --> K[Privacy Controls]
I --> L[UI Customization]
I --> M[Camera Enhancements]
I --> N[AI Intelligence Layer]
J --> O[Enhanced Snapchat Experience]
K --> O
L --> O
M --> O
N --> O
```
<br>
---
<br>
## Features
PurrfectSnap offers deep control across media, privacy, automation, and UIdesigned for both casual users and power users.
PurrfectSnap offers deep control across media, privacy, automation, and UI:designed for both casual users and power users.
<table>
<tr>
@@ -246,152 +211,6 @@ Extensibility through custom scripts.
<br>
---
<br>
## What's New
Here's what makes PurrfectSnap different from upstream SnapEnhance.
<br>
### Aurora Design System
A complete visual language rebuilt from the ground up. Not just a theme, an entire design philosophy.
The interface now flows with purpose. Subtle animations guide your interactions. Every screen has been reconsidered, every transition refined. The result is an experience that feels premium without being ostentatious.
<br>
### PurrAura
The ban problem? Solved.
No more sacrificing features to stay safe. Add friends, block users, attach music; everything works as it should. No workarounds, no compromises, no disadvantages.
<br>
### Auto Patcher
Installation was complicated. Now it isnt.
We've eliminated 90% of the installation friction. No Shizuku. No LSPatch. Just a straightforward process that respects your time.
<br>
### Intelligence Layer
**Auto Reply**
Let AI handle routine responses while you focus on what matters. Configure automatic replies for chats, stories, and half-swipes. Essential for creators managing volume. Requires an API key from Google AI Studio, they offer free tier access.
**Scheduled Snaps**
Time-shifted communication. Set a snap to send at midnight for a birthday. Queue content for optimal timing:your schedule, automated.
**Message Translator**
Language barriers, eliminated. Incoming messages automatically translate to your preferred language. Set it once, communicate globally.
<br>
### Privacy Enhancements
**Granular Controls**
Whitelist and blacklist modes for typing indicators and message logging. Choose exactly who sees what, friend by friend.
**Auto Delete Messages**
Set expiration times for your messages. They disappear on your schedule, not Snapchat's.
**Message Logger Viewer**
Review exported logs directly in-app. No external tools required.
<br>
### Data Persistence
**Friend Notes Backup**
Your notes are valuable. Now they're protected. Export and restore with ease. Never lose context again.
<br>
### Device Management
**ID Spoofing**
Device bans happen. Sometimes unjustly. We offer a solution for legitimate cases, approval required with proof of wrongful ban. This feature is restricted to prevent abuse.
**Model Spoofing**
Identify as a high-end device model. Potential performance improvements, enhanced camera processing, and better feature access.
**Network Spoofing**
Spoof your network status to Wi-Fi always to prevent several restrictions on mobile data.
<br>
### Restored Functionality
Features that disappeared? They're back.
Bulk messaging works again. Friend list management is fully operational. These aren't new features, they're restored capabilities that should never have been lost.
<br>
### Discovery
**Scripts Catalog**
Browse and install scripts without leaving the app. No more hunting for import links. One-tap installation. Always current.
**Friend Tracker Catalog**
Browse and install tracking rules without leaving the app. One-tap installation. Always current.
**Friend Tracker Import/Export**
Backup and restore your Friend Tracker configurations with ease.
<br>
### Advanced Tracking
**I Can See You Rule**
Track the exact time and duration when a friend enters your chat. Precision visibility tracking for Friend Tracker.
**Auto Open Snaps Enhancement**
More granular control with queue size configuration and customizable delays.
<br>
### Visual Refinements
**AMOLED Theme**
True black. Battery-saving. The most requested feature, delivered.
**Haptic Feedback**
Subtle tactile responses. Every interaction confirmed through touch.
**Customizable Bottom Bar**
Your navigation, your rules. Rearrange tabs, remove what you don't use, set your default view. Complete flexibility.
**Search History**
Recently searched features, instantly accessible. No more hunting through menus.
<br>
### Infrastructure
**In-App Updates**
New versions install directly from within the app. No GitHub hunting. No manual downloads. Just seamless updates.
**Update Notifications**
Stay current automatically. Get notified when new releases are available.
**Config Export/Import Summary**
See exactly which features a config will enable before importing or exporting. Know what you're getting.
<br>
### Quality
Hundreds of refinements you'll feel but might not see. Smoother animations. Faster response times. Eliminated edge cases. The kind of polish that separates good from exceptional.
> Some features are intended for educational, recovery, or accessibility purposes. Misuse may violate Snapchats terms of service.
> **We are not affiliated with Snap Inc. and are not responsible for any violations by the user. Our sole purpose is for education purposes only!**
@@ -425,15 +244,79 @@ Questions? Ideas? Found a bug? Our community is active and responsive.
<br>
## Contributing
## Build from Source
We welcome contributions from developers who share our vision. Whether it's code, documentation, etc., contributions are always appreciated. Feel free to open a PR :)
<br>
### 1) Fork
---
1. Fork this repository on GitHub.
```
### 2) Generate a Release Keystore (Certificate)
Create a signing keystore (`.jks`) and keep it secure:
```bash
keytool -genkeypair -v \
-keystore purrfectsnap-release.keystore \
-storetype JKS \
-alias purrfectsnap \
-keyalg RSA \
-keysize 2048 \
-validity 10000
```
Optional local placement for Gradle signing:
```bash
mkdir -p ~/.android
cp purrfectsnap-release.keystore ~/.android/purrfectsnap-release.keystore
```
### 3) Convert Keystore to Base64
For GitHub Actions secret `PS_BASE_64`:
Linux/macOS:
```bash
base64 -w 0 purrfectsnap-release.keystore > keystore.base64
```
PowerShell (Windows):
```powershell
[Convert]::ToBase64String([IO.File]::ReadAllBytes("purrfectsnap-release.keystore")) | Set-Content -NoNewline keystore.base64
```
### 4) Add Required GitHub Secrets (Your Fork)
Go to:
`Settings -> Secrets and variables -> Actions -> New repository secret`
Create these exact secrets (from `.github/workflows/release.yml`):
- `PS_BASE_64` = contents of `keystore.base64`
- `PS_RELEASE_KEY_ALIAS` = `purrfectsnap` (or your chosen alias)
- `PS_RELEASE_KEY_PASSWORD` = key password from `keytool`
- `PS_RELEASE_STORE_PASSWORD` = keystore password from `keytool`
### 5) Run Release Workflow
1. Open `Actions` in your fork.
2. Run `PurrfectSnap Release CI` (`workflow_dispatch`).
3. Workflow builds and signs:
- `armv8` release APK
- `armv7` release APK
4. Workflow creates a GitHub Release and uploads APK assets.
### Notes
- CI uses `JDK 21 (Temurin)`.
- Signing requires all four secrets above.
- Keep keystore and passwords private. Rotate immediately if exposed.
<br>
## Credits

View File

@@ -33,8 +33,8 @@ tasks.register<GetVersionTask>("getVersion") {
}
// You can still set these for legacy use by submodules or scripts:
rootProject.ext.set("appVersionName", providers.gradleProperty("APP_VERSION_NAME").orElse("1.3.0").get())
rootProject.ext.set("appVersionCode", providers.gradleProperty("APP_VERSION_CODE").orElse("270").get().toInt())
rootProject.ext.set("appVersionName", providers.gradleProperty("APP_VERSION_NAME").orElse("1.3.2").get())
rootProject.ext.set("appVersionCode", providers.gradleProperty("APP_VERSION_CODE").orElse("272").get().toInt())
rootProject.ext.set("applicationId", "me.eternal.purrfectsnap")
rootProject.ext.set(
"buildHash",

View File

@@ -1,3 +1,8 @@
## v1.3.2
- ⚠️ ALL USERS ARE REQUESTED TO UPDATE TO THIS VERSION AS SOON AS POSSIBLE!⚠️
- Fix: Crucial fixes
- Fix: Media File Picker and added mapper(tq to RSR)
## v1.3.0
- New: Bulk Manage Friend Requests(tq to RSR)
- Fix: Bulk Action Progress Dialogs(tq to RSR)

View File

@@ -30,6 +30,13 @@
"/snap.security.ArgosService/GetTokens",
"com.snap.playstate.net.ReadReceiptHttpInterface.batchUploadReadReceipts",
"com.snap.identity.network.suggestion.BqSuggestFriendHttpInterface.fetchLegacySuggestedFriend",
"com.snap.identity.FriendingHttpInterface.submitSuggestedFriendsAction"
"com.snap.identity.FriendingHttpInterface.submitSuggestedFriendsAction",
"/snapchat.janus.api.LoginService/",
"/snapchat.janus.api.RegistrationService/",
"com.snap.identity.network.suggestion.BqSuggestFriendHttpInterface.fetchHighAvailableSuggestedFriend",
"com.snap.identity.network.suggestion.BqSuggestFriendHttpInterface.fetchNotificationSuggestedFriends",
"com.snap.identity.FriendingHttpInterface.getFriends",
"snaptoken",
"snaptokentype"
]
}

View File

@@ -48,6 +48,7 @@ import me.eternal.purrfectsnap.core.ui.ViewAppearanceHelper
import me.eternal.purrfectsnap.core.util.dataBuilder
import me.eternal.purrfectsnap.core.util.hook.HookStage
import me.eternal.purrfectsnap.core.util.hook.hook
import me.eternal.purrfectsnap.mapper.impl.ChatMediaDrawerMapper
import java.io.InputStream
import java.lang.reflect.Method
import kotlin.random.Random
@@ -62,14 +63,27 @@ class MediaFilePicker : Feature("Media File Picker") {
onNextActivityCreate(defer = true) {
lateinit var chatMediaDrawerActionHandler: Any
lateinit var sendItemsMethod: Method
var sendItemsMethod: Method? = null
var drawerViewClass: Class<*>? = null
var sendItemsListItemClassFallback: Class<*>? = null
findClass("com.snap.composer.memories.ChatMediaDrawer").genericSuperclass?.getTypeArguments()?.getOrNull(1)?.apply {
methods.first {
it.parameterTypes.size == 1 && it.parameterTypes[0].name.endsWith("ChatMediaDrawerActionHandler")
}.also { method ->
sendItemsMethod = method.parameterTypes[0].methods.first { it.name == "sendItems" }
}.hook(HookStage.AFTER) {
context.mappings.useMapper(ChatMediaDrawerMapper::class) {
val drawerCls = chatMediaDrawerClass.getAsClass() ?: return@useMapper
val actionHandlerCls = actionHandlerClass.getAsClass() ?: return@useMapper
val sendItemsName = sendItemsMethodName.getAsString() ?: "sendItems"
drawerViewClass = drawerCls
sendItemsListItemClassFallback = sendItemsListItemClass.getAsClass()
val contextType = drawerCls.genericSuperclass?.getTypeArguments()?.getOrNull(1) ?: return@useMapper
val handlerParamMethod = contextType.methods.firstOrNull { method ->
method.parameterTypes.size == 1 && (
method.parameterTypes[0].name.endsWith("ChatMediaDrawerActionHandler") ||
actionHandlerCls.isAssignableFrom(method.parameterTypes[0])
)
} ?: return@useMapper
val sendItems = handlerParamMethod.parameterTypes[0].methods.firstOrNull { it.name == sendItemsName } ?: return@useMapper
sendItemsMethod = sendItems
handlerParamMethod.hook(HookStage.AFTER) {
chatMediaDrawerActionHandler = it.arg(0)
}
}
@@ -102,7 +116,7 @@ class MediaFilePicker : Feature("Media File Picker") {
}
context.event.subscribe(ActivityResultEvent::class) { event ->
if (event.requestCode != requestCode || event.resultCode != Activity.RESULT_OK) return@subscribe
if (sendItemsMethod == null || event.requestCode != requestCode || event.resultCode != Activity.RESULT_OK) return@subscribe
requestCode = null
firstVideoId = context.androidContext.contentResolver.query(
@@ -128,25 +142,35 @@ class MediaFilePicker : Feature("Media File Picker") {
}
fun sendMedia() {
sendItemsMethod.invoke(chatMediaDrawerActionHandler, listOf<Any>(), listOf(
sendItemsMethod.genericParameterTypes[1].getTypeArguments().first().dataBuilder {
from("_item") {
set("_cameraRollSource", "Snapchat")
set("_contentUri", "")
set("_durationMs", 0.0)
set("_disabled", false)
set("_imageRotation", 0.0)
set("_width", 1080.0)
set("_height", 1920.0)
set("_timestampMs", System.currentTimeMillis().toDouble())
from("_itemId") {
set("_itemId", firstVideoId.toString())
set("_type", "VIDEO")
}
val method = sendItemsMethod ?: return
val itemClass = method.genericParameterTypes.getOrNull(1)?.getTypeArguments()?.firstOrNull()
?: sendItemsListItemClassFallback
if (itemClass == null) {
context.log.warn("MediaFilePicker: sendItems second parameter type has no generic info (type erasure). genericParameterTypes[1]=${method.genericParameterTypes.getOrNull(1)}")
context.inAppOverlay.showStatusToast(Icons.Default.Error, "Failed to send media (incompatible version).")
return
}
val item = itemClass.dataBuilder {
from("_item") {
set("_cameraRollSource", "Snapchat")
set("_contentUri", "")
set("_durationMs", 0.0)
set("_disabled", false)
set("_imageRotation", 0.0)
set("_width", 1080.0)
set("_height", 1920.0)
set("_timestampMs", System.currentTimeMillis().toDouble())
from("_itemId") {
set("_itemId", firstVideoId.toString())
set("_type", "VIDEO")
}
set("_order", 0.0)
}
))
set("_order", 0.0)
} ?: run {
context.inAppOverlay.showStatusToast(Icons.Default.Error, "Failed to build media item.")
return
}
method.invoke(chatMediaDrawerActionHandler, listOf<Any>(), listOf(item))
}
fun startConversion(audioOnly: Boolean) {
@@ -204,7 +228,7 @@ class MediaFilePicker : Feature("Media File Picker") {
val buttonTag = Random.nextInt(0, 65535)
context.event.subscribe(AddViewEvent::class) { event ->
if (event.parent !is FrameLayout || !event.view::class.java.name.endsWith("ChatMediaDrawer")) return@subscribe
if (event.parent !is FrameLayout || drawerViewClass?.isInstance(event.view) != true) return@subscribe
event.view.addOnAttachStateChangeListener(object: View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {

View File

@@ -0,0 +1,18 @@
- New: Bulk Manage Friend Requests(tq to RSR)
- Fix: Bulk Action Progress Dialogs(tq to RSR)
- Fix: Audio note send override(tq to RSR)
- Fix: Improve PurrAura Active indicator(tq to RSR)
- Fix: Prevent Story Rewatch(tq to Brandon)
- Fix: Location search(tq to Brandon)
- Fix: story download & decryption(tq to Hazelnut)
- New: Add confirmation dialogs for Reset PurrfectSnap option
- New: Google Maps Integration(tq to Brandon)
- Fix: support more translation keys
- Fix: several inconsistencies
- Fix: add proper support for scripts
- New: Swap wiki with new website
- Fix: Custom emoji is now stable!
- Fix: Poor Call recorder quality
- New: order feature for chat exports
- Fix: Gemini Auto Reply(tq to RSR)
- New: Spotlight Creator Info(tq to RSR)

View File

@@ -0,0 +1,9 @@
PurrfectSnap is an open-source module for Snapchat that adds advanced customization and utility features for rooted and non-rooted setups.
It includes media download options, privacy controls, interface tweaks, automation tools, and quality-of-life improvements designed to give users more control over their Snapchat experience.
Highlights:
- Media and download utilities
- Privacy and visibility controls
- UI customization and theming
- Rooted and non-rooted support

Binary file not shown.

After

Width:  |  Height:  |  Size: 921 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

View File

@@ -0,0 +1 @@
Advanced Snapchat customization, privacy, and automation tools for rooted and non-rooted devices.

View File

@@ -0,0 +1 @@
PurrfectSnap

View File

@@ -1,6 +1,5 @@
org.gradle.jvmargs=-Xms2G -Xmx4G -Dfile.encoding=UTF-8 --enable-native-access=ALL-UNNAMED
org.gradle.parallel=true
org.gradle.workers.max=8
android.useAndroidX=true
kotlin.code.style=official
android.nonTransitiveRClass=true
@@ -8,10 +7,10 @@ org.gradle.configuration-cache=true
org.gradle.configuration-cache.problems=warn
nativeAbis=arm64-v8a
APP_VERSION_NAME=1.3.0
APP_VERSION_CODE=270
APP_VERSION_NAME=1.3.2
APP_VERSION_CODE=272
debug_build_hash=18fe2a814d0e2eb5
psIntegrityPinnedSha256=
EXPECTED_CERT_SHA256=97fc5c4dff7e33c159528eb9e53f86c356d430cd63d177f40c22b577ac829dee
EXPECTED_CERT_SHA256=371cb9fbebbe9880d147f59bab36d61eee122854ef8c9ee1ecf12b82368bcf10
android.disallowKotlinSourceSets=false
android.sourceset.disallowProvider=false

View File

@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
distributionSha256Sum=8fad3d78296ca518113f3d29016617c7f9367dc005f932bd9d93bf45ba46072b
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -40,6 +40,7 @@ class ClassMapper(
COFObservableMapper(),
FoldingLayoutMapper(),
PlatformClientAttestationMapper(),
ChatMediaDrawerMapper(),
)
}

View File

@@ -0,0 +1,48 @@
package me.eternal.purrfectsnap.mapper.impl
import me.eternal.purrfectsnap.mapper.AbstractClassMapper
import me.eternal.purrfectsnap.mapper.ext.getClassName
import me.eternal.purrfectsnap.mapper.ext.getSuperClassName
class ChatMediaDrawerMapper : AbstractClassMapper("ChatMediaDrawer") {
val chatMediaDrawerClass = classReference("chatMediaDrawerClass")
val actionHandlerClass = classReference("actionHandlerClass")
val sendItemsMethodName = string("sendItemsMethodName")
/** List element type for sendItems(List, List) second parameter — used when generic type is erased. */
val sendItemsListItemClass = classReference("sendItemsListItemClass")
init {
mapper {
val drawerClass = classes.firstOrNull { clazz ->
val name = clazz.getClassName()
val superName = clazz.getSuperClassName() ?: return@firstOrNull false
name.contains("ChatMediaDrawer") &&
!name.contains("ActionHandler") &&
superName.contains("ValdiGeneratedRootView")
} ?: return@mapper
val actionHandlerClazz = classes.firstOrNull { clazz ->
clazz.getClassName().contains("ChatMediaDrawerActionHandler")
} ?: return@mapper
val sendItemsMethod = actionHandlerClazz.methods.firstOrNull { method ->
method.name == "sendItems" && method.parameterTypes.size == 2
} ?: actionHandlerClazz.methods.firstOrNull { method ->
method.parameterTypes.size == 2 &&
method.parameterTypes[0].startsWith("Ljava/util/") &&
method.parameterTypes[1].startsWith("Ljava/util/")
} ?: return@mapper
chatMediaDrawerClass.set(drawerClass.getClassName().replace("/", "."))
actionHandlerClass.set(actionHandlerClazz.getClassName().replace("/", "."))
sendItemsMethodName.set(sendItemsMethod.name)
// Find the list item type (e.g. Azc) by structure: has _order and _item (Valdi schema)
val listItemClass = classes.firstOrNull { clazz ->
val fieldNames = clazz.fields.map { it.name }.toSet()
fieldNames.contains("_order") && fieldNames.contains("_item")
}
listItemClass?.let { sendItemsListItemClass.set(it.getClassName().replace("/", ".")) }
}
}
}

View File

@@ -88,16 +88,20 @@ ensure_omvll_bundle() {
local archive_url="${OMVLL_ARCHIVE_URL:-https://github.com/open-obfuscator/o-mvll/releases/download/${OMVLL_VERSION}/${OMVLL_LINUX_ASSET}}"
OMVLL_CACHE_DIR="$SCRIPT_DIR/.omvll/$OMVLL_VERSION"
OMVLL_PLUGIN_PATH="$OMVLL_CACHE_DIR/omvll-ndk.so"
local tmp_tar=""
if [ ! -f "$OMVLL_PLUGIN_PATH" ]; then
echo "Downloading O-MVLL bundle from $archive_url"
download_omvll_bundle() {
rm -rf "$OMVLL_CACHE_DIR"
mkdir -p "$OMVLL_CACHE_DIR"
local tmp_tar
tmp_tar=$(mktemp)
curl -fL "$archive_url" -o "$tmp_tar"
tar -xzf "$tmp_tar" -C "$OMVLL_CACHE_DIR"
rm -f "$tmp_tar"
}
if [ ! -f "$OMVLL_PLUGIN_PATH" ]; then
echo "Downloading O-MVLL bundle from $archive_url"
download_omvll_bundle
fi
if [ ! -f "$OMVLL_PLUGIN_PATH" ]; then
@@ -117,11 +121,16 @@ ensure_omvll_bundle() {
local py_root
py_root=$(find "$OMVLL_CACHE_DIR" -maxdepth 1 -type d -name 'Python-*' | head -n1 || true)
if [ -z "$py_root" ] || [ ! -d "$py_root/Lib" ]; then
echo "Unable to determine Python stdlib directory inside the O-MVLL bundle" >&2
echo "OMVLL_CACHE_DIR: $OMVLL_CACHE_DIR" >&2
echo "Contents:" >&2
ls -la "$OMVLL_CACHE_DIR" >&2 || true
exit 1
echo "OMVLL cache is incomplete. Re-downloading bundle..." >&2
download_omvll_bundle
py_root=$(find "$OMVLL_CACHE_DIR" -maxdepth 1 -type d -name 'Python-*' | head -n1 || true)
if [ -z "$py_root" ] || [ ! -d "$py_root/Lib" ]; then
echo "Unable to determine Python stdlib directory inside the O-MVLL bundle" >&2
echo "OMVLL_CACHE_DIR: $OMVLL_CACHE_DIR" >&2
echo "Contents:" >&2
ls -la "$OMVLL_CACHE_DIR" >&2 || true
exit 1
fi
fi
export OMVLL_PYTHONPATH="$py_root/Lib"
fi

View File

@@ -252,28 +252,39 @@ val syncTasks = cargoTargets.mapIndexed { index, target ->
omvllArchiveUrl?.let { environment("OMVLL_ARCHIVE_URL", it) }
}
tasks.register<Sync>("syncNative${target.taskSuffix}") {
tasks.register("syncNative${target.taskSuffix}") {
dependsOn(cargoTask)
val outputLibName = nativeLibFileName
inputs.property("outputLibName", outputLibName)
val wslCandidate = File(wslStagingDir, "native/rust/target/${target.triple}/release/libpurrfectsnap.so")
val localCandidate = layout.projectDirectory.file("rust/target/${target.triple}/release/libpurrfectsnap.so").asFile
val sourceLibs = files(wslCandidate, localCandidate)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(sourceLibs) {
rename { outputLibName }
}
from(sourceLibs) {
rename { "libpurrfectsnap.so" }
}
into(layout.buildDirectory.dir("rustJniLibs/android/${target.abi}"))
inputs.files(sourceLibs)
val outputDir = layout.buildDirectory.dir("rustJniLibs/android/${target.abi}")
inputs.property("outputLibName", outputLibName)
inputs.files(wslCandidate, localCandidate)
outputs.dir(outputDir)
outputs.upToDateWhen { false }
val checksumsDir = layout.buildDirectory.dir("checksums")
doLast {
val file = File(destinationDir, outputLibName)
if (!file.exists()) {
error("Native library not found for ${target.abi}. Expected ${file.absolutePath}")
val sourceFile = listOf(wslCandidate, localCandidate).firstOrNull { it.exists() }
?: error(
"Native library not found for ${target.abi}. Looked in: " +
"${wslCandidate.absolutePath}, ${localCandidate.absolutePath}"
)
val abiDir = outputDir.get().asFile
abiDir.mkdirs()
project.copy {
from(sourceFile)
into(abiDir)
rename { outputLibName }
}
project.copy {
from(sourceFile)
into(abiDir)
rename { "libpurrfectsnap.so" }
}
val file = File(abiDir, outputLibName)
val crc = CRC32()
crc.update(file.readBytes())
val checksumsDirFile = checksumsDir.get().asFile

View File

@@ -6,154 +6,191 @@
static const uint8_t KEY = 0x5Au;
static const uint8_t DET_LENS[] = {
5, 11, 6, 9, 15, 16, 7, 11, 25, 9, 14, 13, 16, 23, 21, 10, 21, 24, 21,
17, 10, 19, 18, 18, 19, 18, 17, 27, 20, 18, 16, 23, 13, 27, 22, 28,
19, 14
5, 11, 6, 9, 15, 16, 7, 11, 25, 9, 14, 13,
16, 23, 21, 10, 21, 24, 21, 17, 10, 19, 18, 18,
19, 18, 17, 27, 20, 18, 16, 23, 13, 27, 22, 28,
19, 14, 18, 20, 16, 15, 14, 19, 18, 13, 26, 9,
10, 17, 17, 15, 26, 28, 28, 29, 20, 21, 22,
};
static const uint8_t DET_ENC[] = {
0x3B, 0x28, 0x3D, 0x35, 0x29, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x33,
0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x39, 0x36, 0x33,
0x3F, 0x34, 0x2E, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E,
0x23, 0x39, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x05, 0x33, 0x34, 0x2E,
0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x29, 0x39, 0x3B, 0x28, 0x3D,
0x35, 0x29, 0x3B, 0x28, 0x3D, 0x35, 0x29, 0x39, 0x36, 0x33, 0x3F,
0x34, 0x2E, 0x2A, 0x36, 0x3B, 0x2E, 0x3C, 0x35, 0x28, 0x37, 0x39,
0x36, 0x33, 0x3F, 0x34, 0x2E, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E,
0x3B, 0x2E, 0x33, 0x35, 0x34, 0x29, 0x3B, 0x3C, 0x3F, 0x2E, 0x23,
0x34, 0x3F, 0x2E, 0x2A, 0x36, 0x3B, 0x23, 0x05, 0x33, 0x34, 0x2E,
0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x2A, 0x36, 0x3B, 0x23, 0x33,
0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x3F, 0x22, 0x2A,
0x28, 0x3F, 0x29, 0x29, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33,
0x2E, 0x23, 0x3B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x05, 0x31,
0x3F, 0x23, 0x05, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x3B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x31,
0x3F, 0x23, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33,
0x35, 0x34, 0x3B, 0x2A, 0x2A, 0x05, 0x3B, 0x2E, 0x2E, 0x3F, 0x29,
0x2E, 0x3D, 0x3F, 0x2E, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x32, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x29,
0x39, 0x35, 0x37, 0x2A, 0x2F, 0x2E, 0x3F, 0x3B, 0x2E, 0x2E, 0x3F,
0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x32, 0x3F, 0x3B, 0x3E,
0x3F, 0x28, 0x3D, 0x3F, 0x2E, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E,
0x3B, 0x2E, 0x33, 0x35, 0x34, 0x2A, 0x3B, 0x23, 0x36, 0x35, 0x3B,
0x3E, 0x3D, 0x3F, 0x34, 0x3F, 0x28, 0x3B, 0x2E, 0x3F, 0x29, 0x33,
0x3D, 0x34, 0x3B, 0x2E, 0x2F, 0x28, 0x3F, 0x32, 0x3F, 0x28, 0x37,
0x35, 0x3E, 0x05, 0x3E, 0x2F, 0x2A, 0x3E, 0x3F, 0x2C, 0x33, 0x39,
0x3F, 0x05, 0x2C, 0x3F, 0x28, 0x33, 0x3C, 0x33, 0x39, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x3E, 0x3F, 0x2C, 0x33, 0x39, 0x3F, 0x05, 0x3C,
0x33, 0x34, 0x3D, 0x3F, 0x28, 0x2A, 0x28, 0x33, 0x34, 0x2E, 0x3E,
0x3F, 0x2C, 0x33, 0x39, 0x3F, 0x05, 0x3B, 0x2E, 0x2E, 0x3F, 0x29,
0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x39, 0x36, 0x33, 0x3F, 0x34,
0x2E, 0x05, 0x2C, 0x3F, 0x28, 0x33, 0x3C, 0x33, 0x39, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x39, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x05, 0x3B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x2C,
0x3F, 0x34, 0x3E, 0x35, 0x28, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E,
0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3D, 0x35, 0x35, 0x3D, 0x36, 0x3F,
0x3B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x31, 0x3F, 0x23, 0x3B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3D,
0x3F, 0x2E, 0x29, 0x33, 0x3D, 0x34, 0x3F, 0x3E, 0x3B, 0x2E, 0x2E,
0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3B, 0x2E, 0x2E,
0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x37, 0x3B, 0x34,
0x3B, 0x3D, 0x3F, 0x28, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B,
0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x2E, 0x32,
0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39,
0x3F, 0x29, 0x29, 0x29, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x2E,
0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x2A,
0x2A, 0x3D, 0x28, 0x2A, 0x39, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A,
0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x29,
0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x28, 0x3F, 0x39, 0x35, 0x28,
0x3E, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23,
0x3B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x28, 0x3F, 0x39, 0x35, 0x28,
0x3E, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23,
0x3B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x3B, 0x39, 0x2E, 0x33, 0x35,
0x34, 0x3B, 0x2F, 0x2E, 0x32, 0x39, 0x35, 0x34, 0x2E, 0x3F, 0x22,
0x2E, 0x3E, 0x3F, 0x36, 0x3F, 0x3D, 0x3B, 0x2E, 0x3F, 0x3D, 0x3F,
0x2E, 0x3B, 0x2F, 0x2E, 0x32, 0x39, 0x35, 0x34, 0x2E, 0x3F, 0x22,
0x2E
0x2E, 0x33, 0x35, 0x34, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x33, 0x34,
0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x39, 0x36, 0x33, 0x3F, 0x34,
0x2E, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x39, 0x36,
0x33, 0x3F, 0x34, 0x2E, 0x05, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33,
0x2E, 0x23, 0x29, 0x39, 0x3B, 0x28, 0x3D, 0x35, 0x29, 0x3B, 0x28, 0x3D,
0x35, 0x29, 0x39, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x2A, 0x36, 0x3B, 0x2E,
0x3C, 0x35, 0x28, 0x37, 0x39, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x3B, 0x2E,
0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x29, 0x3B, 0x3C,
0x3F, 0x2E, 0x23, 0x34, 0x3F, 0x2E, 0x2A, 0x36, 0x3B, 0x23, 0x05, 0x33,
0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x2A, 0x36, 0x3B, 0x23,
0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x3F, 0x22, 0x2A,
0x28, 0x3F, 0x29, 0x29, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E,
0x23, 0x3B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x05, 0x31, 0x3F, 0x23,
0x05, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34,
0x3B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x31, 0x3F, 0x23, 0x3B, 0x2E,
0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3B, 0x2A, 0x2A,
0x05, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3D, 0x3F, 0x2E, 0x3B, 0x2E,
0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x32, 0x3F, 0x3B,
0x3E, 0x3F, 0x28, 0x29, 0x39, 0x35, 0x37, 0x2A, 0x2F, 0x2E, 0x3F, 0x3B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x32, 0x3F,
0x3B, 0x3E, 0x3F, 0x28, 0x3D, 0x3F, 0x2E, 0x3B, 0x2E, 0x2E, 0x3F, 0x29,
0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x2A, 0x3B, 0x23, 0x36, 0x35, 0x3B,
0x3E, 0x3D, 0x3F, 0x34, 0x3F, 0x28, 0x3B, 0x2E, 0x3F, 0x29, 0x33, 0x3D,
0x34, 0x3B, 0x2E, 0x2F, 0x28, 0x3F, 0x32, 0x3F, 0x28, 0x37, 0x35, 0x3E,
0x05, 0x3E, 0x2F, 0x2A, 0x3E, 0x3F, 0x2C, 0x33, 0x39, 0x3F, 0x05, 0x2C,
0x3F, 0x28, 0x33, 0x3C, 0x33, 0x39, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3E,
0x3F, 0x2C, 0x33, 0x39, 0x3F, 0x05, 0x3C, 0x33, 0x34, 0x3D, 0x3F, 0x28,
0x2A, 0x28, 0x33, 0x34, 0x2E, 0x3E, 0x3F, 0x2C, 0x33, 0x39, 0x3F, 0x05,
0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x39,
0x36, 0x33, 0x3F, 0x34, 0x2E, 0x05, 0x2C, 0x3F, 0x28, 0x33, 0x3C, 0x33,
0x39, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x39, 0x36, 0x33, 0x3F, 0x34, 0x2E,
0x05, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34,
0x2C, 0x3F, 0x34, 0x3E, 0x35, 0x28, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E,
0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3D, 0x35, 0x35, 0x3D, 0x36, 0x3F, 0x3B,
0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x31, 0x3F, 0x23, 0x3B, 0x2E, 0x2E,
0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3D, 0x3F, 0x2E, 0x29,
0x33, 0x3D, 0x34, 0x3F, 0x3E, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x37, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x2E, 0x32,
0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39, 0x3F,
0x29, 0x29, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23,
0x3B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x29, 0x3F, 0x28, 0x2C, 0x33, 0x39,
0x3F, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23, 0x3B,
0x2A, 0x2A, 0x3D, 0x28, 0x2A, 0x39, 0x2E, 0x32, 0x33, 0x28, 0x3E, 0x2A,
0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x29, 0x3F,
0x28, 0x2C, 0x33, 0x39, 0x3F, 0x28, 0x3F, 0x39, 0x35, 0x28, 0x3E, 0x2E,
0x32, 0x33, 0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39,
0x3F, 0x29, 0x29, 0x28, 0x3F, 0x39, 0x35, 0x28, 0x3E, 0x2E, 0x32, 0x33,
0x28, 0x3E, 0x2A, 0x3B, 0x28, 0x2E, 0x23, 0x3B, 0x39, 0x39, 0x3F, 0x29,
0x29, 0x3B, 0x39, 0x2E, 0x33, 0x35, 0x34, 0x3B, 0x2F, 0x2E, 0x32, 0x39,
0x35, 0x34, 0x2E, 0x3F, 0x22, 0x2E, 0x3E, 0x3F, 0x36, 0x3F, 0x3D, 0x3B,
0x2E, 0x3F, 0x3D, 0x3F, 0x2E, 0x3B, 0x2F, 0x2E, 0x32, 0x39, 0x35, 0x34,
0x2E, 0x3F, 0x22, 0x2E, 0x34, 0x3B, 0x2E, 0x33, 0x2C, 0x3F, 0x05, 0x34,
0x3F, 0x2E, 0x2D, 0x35, 0x28, 0x31, 0x05, 0x3B, 0x2A, 0x33, 0x34, 0x3B,
0x2E, 0x33, 0x2C, 0x3F, 0x34, 0x3F, 0x2E, 0x2D, 0x35, 0x28, 0x31, 0x28,
0x3F, 0x2B, 0x2F, 0x3F, 0x29, 0x2E, 0x34, 0x3B, 0x2E, 0x33, 0x2C, 0x3F,
0x34, 0x3F, 0x2E, 0x2D, 0x35, 0x28, 0x31, 0x3B, 0x2A, 0x33, 0x3B, 0x2E,
0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x2E, 0x23, 0x2A,
0x3F, 0x3B, 0x28, 0x3D, 0x35, 0x29, 0x3B, 0x34, 0x3E, 0x36, 0x3F, 0x3D,
0x3B, 0x39, 0x23, 0x28, 0x3F, 0x2B, 0x2F, 0x33, 0x28, 0x3F, 0x29, 0x3B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x3D, 0x3F,
0x2E, 0x3B, 0x28, 0x3D, 0x35, 0x29, 0x2E, 0x35, 0x31, 0x3F, 0x34, 0x3B,
0x29, 0x23, 0x34, 0x39, 0x3D, 0x3F, 0x2E, 0x3B, 0x28, 0x3D, 0x35, 0x29,
0x2E, 0x35, 0x31, 0x3F, 0x34, 0x3B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x32, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x29, 0x39,
0x3B, 0x36, 0x36, 0x38, 0x3B, 0x39, 0x31, 0x3B, 0x28, 0x3D, 0x35, 0x29,
0x37, 0x35, 0x3E, 0x3F, 0x3B, 0x28, 0x3D, 0x35, 0x29, 0x3F, 0x2C, 0x3F,
0x34, 0x2E, 0x3B, 0x28, 0x3D, 0x35, 0x29, 0x2E, 0x35, 0x31, 0x3F, 0x34,
0x28, 0x3F, 0x3C, 0x28, 0x3F, 0x29, 0x32, 0x3B, 0x28, 0x3D, 0x35, 0x29,
0x28, 0x3F, 0x3C, 0x28, 0x3F, 0x29, 0x28, 0x3F, 0x3B, 0x29, 0x35, 0x34,
0x3B, 0x28, 0x3D, 0x35, 0x29, 0x32, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x2E,
0x23, 0x2A, 0x3F, 0x3D, 0x35, 0x35, 0x3D, 0x36, 0x3F, 0x2A, 0x36, 0x3B,
0x23, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x37, 0x3B,
0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x28, 0x3F, 0x2B, 0x2F, 0x3F, 0x29, 0x2E,
0x3F, 0x22, 0x2A, 0x28, 0x3F, 0x29, 0x29, 0x33, 0x34, 0x2E, 0x3F, 0x3D,
0x28, 0x33, 0x2E, 0x23, 0x2E, 0x35, 0x31, 0x3F, 0x34, 0x28, 0x3F, 0x2B,
0x2F, 0x3F, 0x29, 0x2E, 0x39, 0x36, 0x3B, 0x29, 0x29, 0x33, 0x39, 0x33,
0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x2E, 0x35, 0x31, 0x3F,
0x34, 0x28, 0x3F, 0x2B, 0x2F, 0x3F, 0x29, 0x2E, 0x29, 0x2E, 0x3B, 0x34,
0x3E, 0x3B, 0x28, 0x3E, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E,
0x23, 0x2E, 0x35, 0x31, 0x3F, 0x34, 0x39, 0x36, 0x3B, 0x29, 0x29, 0x33,
0x39, 0x2A, 0x36, 0x3B, 0x23, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33,
0x2E, 0x23, 0x29, 0x2E, 0x3B, 0x34, 0x3E, 0x3B, 0x28, 0x3E, 0x2A, 0x36,
0x3B, 0x23, 0x33, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x39,
0x32, 0x3B, 0x36, 0x36, 0x3F, 0x34, 0x3D, 0x3F, 0x35, 0x28, 0x39, 0x32,
0x3F, 0x29, 0x2E, 0x28, 0x3B, 0x2E, 0x33, 0x35, 0x34,
};
static char DET_DEC[678];
static char DET_DEC[1132];
static size_t DET_LEN = 0;
static int DET_READY = 0;
static const uint8_t RISK_LENS[] = {
22, 24, 16, 18, 22, 19, 22, 19, 25, 21, 27, 21, 30, 18, 24, 26, 28,
34, 36, 14, 17, 18, 20, 14, 16, 12, 26, 10, 27, 29, 23, 53, 29, 30
22, 24, 16, 18, 22, 19, 22, 19, 25, 21, 27, 21,
30, 18, 24, 26, 28, 34, 36, 14, 17, 18, 20, 14,
16, 12, 26, 10, 27, 29, 23, 53, 29, 30, 26, 28,
50, 29,
};
static const uint8_t RISK_ENC[] = {
0x19, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28,
0x33, 0x2E, 0x23, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75,
0x19, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x13, 0x34, 0x2E, 0x3F, 0x3D,
0x28, 0x33, 0x2E, 0x23, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F,
0x75, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x09,
0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x13, 0x34, 0x2E, 0x3F,
0x3D, 0x28, 0x33, 0x2E, 0x23, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39,
0x3F, 0x75, 0x75, 0x1D, 0x3F, 0x2E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29,
0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x12, 0x3F, 0x3B, 0x3E, 0x3F,
0x28, 0x29, 0x75, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x12, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x29, 0x75,
0x1D, 0x3F, 0x2E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x0A, 0x3B, 0x23, 0x36, 0x35, 0x3B, 0x3E, 0x75,
0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34,
0x0A, 0x3B, 0x23, 0x36, 0x35, 0x3B, 0x3E, 0x75, 0x19, 0x35, 0x37,
0x2A, 0x2F, 0x2E, 0x3F, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x12, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x75,
0x1D, 0x3F, 0x2E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x12, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x75, 0x1D,
0x3F, 0x34, 0x3F, 0x28, 0x3B, 0x2E, 0x3F, 0x1B, 0x2E, 0x2E, 0x3F,
0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x0A, 0x3B, 0x23, 0x36,
0x35, 0x3B, 0x3E, 0x75, 0x1D, 0x3F, 0x2E, 0x09, 0x33, 0x3D, 0x34,
0x3F, 0x3E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33,
0x35, 0x34, 0x75, 0x1D, 0x3F, 0x2E, 0x09, 0x33, 0x3D, 0x34, 0x3F,
0x3E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x0D, 0x33, 0x2E, 0x32, 0x14, 0x35, 0x34, 0x39, 0x3F, 0x75,
0x0C, 0x3F, 0x34, 0x3E, 0x35, 0x28, 0x1B, 0x2E, 0x2E, 0x3F, 0x29,
0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x0C, 0x3F, 0x34, 0x3E, 0x35,
0x28, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x75, 0x0C, 0x3F,
0x34, 0x3E, 0x35, 0x28, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28,
0x75, 0x75, 0x1D, 0x35, 0x35, 0x3D, 0x36, 0x3F, 0x1B, 0x34, 0x3E,
0x28, 0x35, 0x33, 0x3E, 0x11, 0x3F, 0x23, 0x1B, 0x2E, 0x2E, 0x3F,
0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x1D, 0x35, 0x35, 0x3D,
0x36, 0x3F, 0x1B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x11, 0x3F,
0x23, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x75, 0x1D, 0x35,
0x35, 0x3D, 0x36, 0x3F, 0x1B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E,
0x11, 0x3F, 0x23, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E,
0x33, 0x35, 0x34, 0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x75,
0x75, 0x0A, 0x36, 0x3B, 0x23, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28,
0x33, 0x2E, 0x23, 0x75, 0x1F, 0x22, 0x2A, 0x28, 0x3F, 0x29, 0x29,
0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x09, 0x19,
0x1B, 0x28, 0x3D, 0x35, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39,
0x3F, 0x13, 0x37, 0x2A, 0x36, 0x75, 0x09, 0x19, 0x1B, 0x28, 0x3D,
0x35, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x13, 0x37,
0x2A, 0x36, 0x75, 0x09, 0x19, 0x1B, 0x28, 0x3D, 0x35, 0x29, 0x09,
0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x09, 0x19, 0x1B, 0x28,
0x3D, 0x35, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75,
0x75, 0x1B, 0x28, 0x3D, 0x35, 0x29, 0x19, 0x36, 0x33, 0x3F, 0x34,
0x2E, 0x75, 0x0A, 0x36, 0x3B, 0x2E, 0x3C, 0x35, 0x28, 0x37, 0x19,
0x36, 0x33, 0x3F, 0x34, 0x2E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E,
0x3B, 0x2E, 0x33, 0x35, 0x34, 0x32, 0x3F, 0x28, 0x37, 0x35, 0x3E,
0x05, 0x3E, 0x2F, 0x2A, 0x1D, 0x28, 0x2A, 0x39, 0x0E, 0x32, 0x33,
0x28, 0x3E, 0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F,
0x29, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x1D,
0x28, 0x2A, 0x39, 0x0E, 0x32, 0x33, 0x28, 0x3E, 0x0A, 0x3B, 0x28,
0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x09, 0x3F, 0x28,
0x2C, 0x33, 0x39, 0x3F, 0x75, 0x0E, 0x32, 0x33, 0x28, 0x3E, 0x0A,
0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x09,
0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x39, 0x35, 0x37, 0x74,
0x29, 0x34, 0x3B, 0x2A, 0x39, 0x32, 0x3B, 0x2E, 0x74, 0x3B, 0x2F,
0x2E, 0x32, 0x74, 0x2A, 0x28, 0x35, 0x2E, 0x35, 0x74, 0x2E, 0x2A,
0x3B, 0x74, 0x0E, 0x32, 0x33, 0x28, 0x3E, 0x0A, 0x3B, 0x28, 0x2E,
0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x09, 0x3F, 0x28, 0x2C,
0x33, 0x39, 0x3F, 0x75, 0x75, 0x08, 0x3F, 0x39, 0x35, 0x28, 0x3E,
0x0E, 0x32, 0x33, 0x28, 0x3E, 0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B,
0x39, 0x39, 0x3F, 0x29, 0x29, 0x1B, 0x39, 0x2E, 0x33, 0x35, 0x34,
0x75, 0x08, 0x3F, 0x39, 0x35, 0x28, 0x3E, 0x0E, 0x32, 0x33, 0x28,
0x3E, 0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29,
0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F
0x33, 0x2E, 0x23, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x19,
0x36, 0x33, 0x3F, 0x34, 0x2E, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33,
0x2E, 0x23, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x13, 0x34,
0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x09, 0x3F, 0x28, 0x2C, 0x33,
0x39, 0x3F, 0x75, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23,
0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x75, 0x1D, 0x3F, 0x2E,
0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x12,
0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x29, 0x75, 0x1B, 0x2E, 0x2E, 0x3F, 0x29,
0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x12, 0x3F, 0x3B, 0x3E, 0x3F, 0x28,
0x29, 0x75, 0x1D, 0x3F, 0x2E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x0A, 0x3B, 0x23, 0x36, 0x35, 0x3B, 0x3E, 0x75,
0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x0A,
0x3B, 0x23, 0x36, 0x35, 0x3B, 0x3E, 0x75, 0x19, 0x35, 0x37, 0x2A, 0x2F,
0x2E, 0x3F, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x12, 0x3F, 0x3B, 0x3E, 0x3F, 0x28, 0x75, 0x1D, 0x3F, 0x2E, 0x1B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x12, 0x3F,
0x3B, 0x3E, 0x3F, 0x28, 0x75, 0x1D, 0x3F, 0x34, 0x3F, 0x28, 0x3B, 0x2E,
0x3F, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34,
0x0A, 0x3B, 0x23, 0x36, 0x35, 0x3B, 0x3E, 0x75, 0x1D, 0x3F, 0x2E, 0x09,
0x33, 0x3D, 0x34, 0x3F, 0x3E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x75, 0x1D, 0x3F, 0x2E, 0x09, 0x33, 0x3D, 0x34,
0x3F, 0x3E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x0D, 0x33, 0x2E, 0x32, 0x14, 0x35, 0x34, 0x39, 0x3F, 0x75, 0x0C,
0x3F, 0x34, 0x3E, 0x35, 0x28, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x0C, 0x3F, 0x34, 0x3E, 0x35, 0x28, 0x1B, 0x2E,
0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x17, 0x3B, 0x34,
0x3B, 0x3D, 0x3F, 0x28, 0x75, 0x0C, 0x3F, 0x34, 0x3E, 0x35, 0x28, 0x1B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x17, 0x3B,
0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x75, 0x75, 0x1D, 0x35, 0x35, 0x3D, 0x36,
0x3F, 0x1B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x11, 0x3F, 0x23, 0x1B,
0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x1D, 0x35,
0x35, 0x3D, 0x36, 0x3F, 0x1B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x11,
0x3F, 0x23, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x75, 0x1D, 0x35, 0x35,
0x3D, 0x36, 0x3F, 0x1B, 0x34, 0x3E, 0x28, 0x35, 0x33, 0x3E, 0x11, 0x3F,
0x23, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35, 0x34,
0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28, 0x75, 0x75, 0x0A, 0x36, 0x3B,
0x23, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x75, 0x1F,
0x22, 0x2A, 0x28, 0x3F, 0x29, 0x29, 0x13, 0x34, 0x2E, 0x3F, 0x3D, 0x28,
0x33, 0x2E, 0x23, 0x09, 0x19, 0x1B, 0x28, 0x3D, 0x35, 0x29, 0x09, 0x3F,
0x28, 0x2C, 0x33, 0x39, 0x3F, 0x13, 0x37, 0x2A, 0x36, 0x75, 0x09, 0x19,
0x1B, 0x28, 0x3D, 0x35, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F,
0x13, 0x37, 0x2A, 0x36, 0x75, 0x09, 0x19, 0x1B, 0x28, 0x3D, 0x35, 0x29,
0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x09, 0x19, 0x1B, 0x28,
0x3D, 0x35, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x75,
0x1B, 0x28, 0x3D, 0x35, 0x29, 0x19, 0x36, 0x33, 0x3F, 0x34, 0x2E, 0x75,
0x0A, 0x36, 0x3B, 0x2E, 0x3C, 0x35, 0x28, 0x37, 0x19, 0x36, 0x33, 0x3F,
0x34, 0x2E, 0x1B, 0x2E, 0x2E, 0x3F, 0x29, 0x2E, 0x3B, 0x2E, 0x33, 0x35,
0x34, 0x32, 0x3F, 0x28, 0x37, 0x35, 0x3E, 0x05, 0x3E, 0x2F, 0x2A, 0x1D,
0x28, 0x2A, 0x39, 0x0E, 0x32, 0x33, 0x28, 0x3E, 0x0A, 0x3B, 0x28, 0x2E,
0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33,
0x39, 0x3F, 0x75, 0x1D, 0x28, 0x2A, 0x39, 0x0E, 0x32, 0x33, 0x28, 0x3E,
0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x09,
0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x0E, 0x32, 0x33, 0x28, 0x3E,
0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x09,
0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x39, 0x35, 0x37, 0x74, 0x29,
0x34, 0x3B, 0x2A, 0x39, 0x32, 0x3B, 0x2E, 0x74, 0x3B, 0x2F, 0x2E, 0x32,
0x74, 0x2A, 0x28, 0x35, 0x2E, 0x35, 0x74, 0x2E, 0x2A, 0x3B, 0x74, 0x0E,
0x32, 0x33, 0x28, 0x3E, 0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39,
0x3F, 0x29, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x75,
0x08, 0x3F, 0x39, 0x35, 0x28, 0x3E, 0x0E, 0x32, 0x33, 0x28, 0x3E, 0x0A,
0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39, 0x3F, 0x29, 0x29, 0x1B, 0x39,
0x2E, 0x33, 0x35, 0x34, 0x75, 0x08, 0x3F, 0x39, 0x35, 0x28, 0x3E, 0x0E,
0x32, 0x33, 0x28, 0x3E, 0x0A, 0x3B, 0x28, 0x2E, 0x23, 0x1B, 0x39, 0x39,
0x3F, 0x29, 0x29, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F, 0x1D, 0x35,
0x35, 0x3D, 0x36, 0x3F, 0x0A, 0x36, 0x3B, 0x23, 0x13, 0x34, 0x2E, 0x3F,
0x3D, 0x28, 0x33, 0x2E, 0x23, 0x17, 0x3B, 0x34, 0x3B, 0x3D, 0x3F, 0x28,
0x75, 0x1D, 0x35, 0x35, 0x3D, 0x36, 0x3F, 0x0A, 0x36, 0x3B, 0x23, 0x13,
0x34, 0x2E, 0x3F, 0x3D, 0x28, 0x33, 0x2E, 0x23, 0x17, 0x3B, 0x34, 0x3B,
0x3D, 0x3F, 0x28, 0x75, 0x75, 0x29, 0x34, 0x3B, 0x2A, 0x39, 0x32, 0x3B,
0x2E, 0x74, 0x30, 0x3B, 0x34, 0x2F, 0x29, 0x74, 0x3B, 0x2A, 0x33, 0x74,
0x19, 0x32, 0x3B, 0x36, 0x36, 0x3F, 0x34, 0x3D, 0x3F, 0x15, 0x28, 0x39,
0x32, 0x3F, 0x29, 0x2E, 0x28, 0x3B, 0x2E, 0x33, 0x35, 0x34, 0x09, 0x3F,
0x28, 0x2C, 0x33, 0x39, 0x3F, 0x75, 0x19, 0x32, 0x3B, 0x36, 0x36, 0x3F,
0x34, 0x3D, 0x3F, 0x15, 0x28, 0x39, 0x32, 0x3F, 0x29, 0x2E, 0x28, 0x3B,
0x2E, 0x33, 0x35, 0x34, 0x09, 0x3F, 0x28, 0x2C, 0x33, 0x39, 0x3F,
};
static char RISK_DEC[824];
static char RISK_DEC[993];
static size_t RISK_LEN = 0;
static int RISK_READY = 0;
@@ -210,7 +247,7 @@ const char* ss_get_hermod_dup(size_t* out_len) {
size_t enc_off = 0;
for (size_t i = 0; i < sizeof(DET_LENS); ++i) {
size_t L = DET_LENS[i];
if (i == 14) {
if (i == 20) {
size_t w = (L < sizeof(buf)-1) ? L : (sizeof(buf)-1);
for (size_t j = 0; j < w; ++j) buf[j] = (char)(DET_ENC[enc_off+j] ^ KEY);
buf[w] = '\0';