feat: add tui (#758)

This commit also contains an entire redesign of the GUI interface, which now contains a sidebar for all panels. Hopefully checkra1n approves :)
This commit is contained in:
C authored and GitHub committed 2026-07-06 04:05:57 -07:00
1 parent 89b6147e26
commit 18023c4037
42 files changed
+2895 -487

No files matched your search

+4 -2
View File
@@ -23,20 +23,22 @@ payloads:
name=$$(basename "$$file"); \
name=$${name%.*}; \
echo " XXD $$file"; \
echo "#pragma once" > "src/gen/payloads/$$name.h"; \
xxd -i "$$file" \
| sed 's/unsigned char/static const unsigned char/g' \
| sed 's/unsigned int/static const size_t/g' \
> "src/gen/payloads/$$name.h"; \
>> "src/gen/payloads/$$name.h"; \
done
@for file in images/*; do \
name=$$(basename "$$file"); \
name=$${name%.*}; \
echo " XXD $$file"; \
echo "#pragma once" > "src/gen/images/$$name.h"; \
xxd -i "$$file" \
| sed 's/unsigned char/static const unsigned char/g' \
| sed 's/unsigned int/static const size_t/g' \
> "src/gen/images/$$name.h"; \
>> "src/gen/images/$$name.h"; \
done
xxd -i -n "DFUHelperDeviceInfo" resources/DFUHelperDeviceInfo.json > src/gen/DFUHelperDeviceInfo.h
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

+57 -66
View File
@@ -82,17 +82,16 @@
"image_name":"ipodtouch",
"image_width":116,
"image_height":240,
"image_offset_x":100,
"buttons":{
"top":{
"name":"- Top button",
"x":192,
"y":-4
"x":237,
"y":0
},
"vol_down":{
"name":"Volume down -",
"x":8,
"y":62
"x":46,
"y":56
}
},
"sequence":"ipodtouch7"
@@ -101,17 +100,16 @@
"image_name":"ipodtouch",
"image_width":116,
"image_height":240,
"image_offset_x":100,
"buttons":{
"top":{
"name":"- Top button",
"x":196,
"y":-4
"x":237,
"y":0
},
"home":{
"name":"Home button -",
"x":50,
"y":210
"x":89,
"y":190
}
},
"sequence":"iphone-old"
@@ -120,17 +118,16 @@
"image_name":"iphonese",
"image_width":119,
"image_height":240,
"image_offset_x":100,
"buttons":{
"top":{
"name":"- Top button",
"x":200,
"y":-4
"x":237,
"y":0
},
"home":{
"name":"Home button -",
"x":56,
"y":210
"x":90,
"y":195
}
},
"sequence":"iphone-old"
@@ -139,17 +136,16 @@
"image_name":"iphone6s",
"image_width":123,
"image_height":240,
"image_offset_x":100,
"buttons":{
"side":{
"name":"- Side button",
"x":224,
"y":58
"x":244,
"y":54
},
"home":{
"name":"Home button -",
"x":60,
"y":210
"x":90,
"y":195
}
},
"sequence":"iphone6"
@@ -158,17 +154,16 @@
"image_name":"iphone7",
"image_width":123,
"image_height":240,
"image_offset_x":100,
"buttons":{
"side":{
"name":"- Side button",
"x":224,
"y":58
"x":244,
"y":54
},
"vol_down":{
"name":"Volume down -",
"x":14,
"y":78
"x":44,
"y":72
}
},
"sequence":"iphone7"
@@ -177,17 +172,16 @@
"image_name":"iphone8",
"image_width":120,
"image_height":240,
"image_offset_x":100,
"buttons":{
"side":{
"name":"- Side button",
"x":220,
"y":58
"x":248,
"y":54
},
"vol_down":{
"name":"Volume down -",
"x":10,
"y":78
"x":44,
"y":72
}
},
"sequence":"iphone8"
@@ -196,17 +190,16 @@
"image_name":"iphonex",
"image_width":121,
"image_height":240,
"image_offset_x":100,
"buttons":{
"side":{
"name":"- Side button",
"x":220,
"y":68
"x":246,
"y":62
},
"vol_down":{
"name":"Volume down -",
"x":10,
"y":75
"x":44,
"y":70
}
},
"sequence":"iphone8"
@@ -217,14 +210,14 @@
"image_height":240,
"buttons":{
"top":{
"name":"Top button",
"x":149,
"y":5
"name":"- Top button",
"x":266,
"y":0
},
"home":{
"name":"Home button",
"x":89,
"y":225
"name":"Home button -",
"x":94,
"y":200
}
},
"sequence":"iphone-old"
@@ -235,14 +228,14 @@
"image_height":240,
"buttons":{
"top":{
"name":"Top button",
"x":140,
"y":4
"name":"- Top button",
"x":260,
"y":0
},
"home":{
"name":"Home button",
"x":86,
"y":224
"name":"Home button -",
"x":92,
"y":200
}
},
"sequence":"iphone-old"
@@ -251,17 +244,16 @@
"image_name":"siriremote",
"image_width":79,
"image_height":240,
"image_offset_x":120,
"buttons":{
"menu":{
"name":"Menu -",
"x":84,
"y":84
"x":110,
"y":74
},
"playpause":{
"name":"Play/Pause -",
"x":52,
"y":146
"x":80,
"y":130
}
},
"sequence":"appletv4"
@@ -270,11 +262,10 @@
"image_name":"atv_std_brd",
"image_width":300,
"image_height":240,
"image_offset_x":0,
"buttons":{
"dfu":{
"name":"-- DFU button",
"x":120,
"x":170,
"y":48
}
},
@@ -295,7 +286,7 @@
"top",
"vol_down"
],
"description":"Press and hold the Top and Volume down buttons together",
"description":"HOLD Top and Volume down",
"duration":4,
"action":"reboot"
},
@@ -303,7 +294,7 @@
"buttons":[
"vol_down"
],
"description":"Release the Top button BUT KEEP HOLDING the Volume down button",
"description":"Release Top and KEEP holding Volume down",
"duration":10
}
],
@@ -320,7 +311,7 @@
"top",
"home"
],
"description":"Press and hold the Top\n and Home buttons\n together",
"description":"HOLD Top and Home button",
"duration":4,
"action":"reboot"
},
@@ -328,7 +319,7 @@
"buttons":[
"home"
],
"description":"Release the Top button\n BUT KEEP HOLDING the\n Home button",
"description":"Release Top and KEEP holding Home button",
"duration":10
}
],
@@ -345,7 +336,7 @@
"side",
"home"
],
"description":"Press and hold the Side\n and Home buttons\n together",
"description":"HOLD Side and Home button",
"duration":4,
"action":"reboot"
},
@@ -353,7 +344,7 @@
"buttons":[
"home"
],
"description":"Release the Side button\n BUT KEEP HOLDING the\n Home button",
"description":"Release Side and KEEP holding Home button",
"duration":10
}
],
@@ -370,7 +361,7 @@
"side",
"vol_down"
],
"description":"Press and hold the Side\n and Volume down\n buttons together",
"description":"HOLD Side and Volume down",
"duration":4,
"action":"reboot"
},
@@ -378,7 +369,7 @@
"buttons":[
"vol_down"
],
"description":"Release the Side button\n BUT KEEP HOLDING the\n Volume down button",
"description":"Release Side and KEEP holding Volume down",
"duration":10
}
],
@@ -395,7 +386,7 @@
"side",
"vol_down"
],
"description":"Press and hold the Side\n and Volume down\n buttons together",
"description":"HOLD Side and Volume down",
"duration":4,
"action":"reboot"
},
@@ -403,7 +394,7 @@
"buttons":[
"vol_down"
],
"description":"Release the Side button\n BUT KEEP HOLDING the\n Volume down button",
"description":"Release Side and KEEP holding Volume down",
"duration":10
}
],
@@ -420,7 +411,7 @@
"menu",
"playpause"
],
"description":"Press and hold Menu\n and Play/Pause together\n ",
"description":"Press and hold Menu and Play/Pause together",
"duration":7
},
{
@@ -443,7 +434,7 @@
"buttons":[
"dfu"
],
"description":"Press and hold the DFU\n button",
"description":"Press and hold the DFU button",
"duration":10,
"action":"reboot"
}
+322 -143
View File
@@ -1,183 +1,362 @@
#ifdef WITH_GUI
#if defined(WITH_GUI) || defined(WITH_TUI)
#include "event.hpp"
#include "wx/AppFrame.hpp"
#include "state.hpp"
#include "sequence.hpp"
#include "utils.h"
#include <wx/wx.h>
#include <chrono>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libirecovery.h>
static inline DeviceState make_fresh_state()
{
return DeviceState{};
namespace {
struct ManagedDevice {
DeviceState state;
bool normalPresent = false;
bool recoveryPresent = false;
};
std::mutex g_mutex;
std::vector<DeviceStateCallback> g_callbacks;
std::unordered_map<uint64_t, ManagedDevice> g_devices; // keyed by ECID
std::unordered_map<std::string, uint64_t> g_udidToEcid;
uint64_t g_activeEcid = 0;
std::once_flag g_startOnce;
irecv_device_event_context_t g_recoveryCtx = nullptr;
void dispatch_callbacks(const std::vector<DeviceStateCallback>& callbacks, const DeviceState& state) {
for (const auto& cb : callbacks) {
if (cb)
cb(state);
}
}
void normal_device_event_cb(
const idevice_event_t* event,
void* user_data)
{
#ifdef WITH_GUI
auto* frame = static_cast<MainFrame*>(user_data);
if (!frame || !event)
return;
#endif
DeviceState build_public_state_locked() {
DeviceState out;
out.connectedDeviceCount = static_cast<uint32_t>(g_devices.size());
out.multipleDevices = out.connectedDeviceCount > 1;
DeviceState state = make_fresh_state();
if (event->event == IDEVICE_DEVICE_REMOVE)
{
#ifdef WITH_GUI
send_device_state(frame, state);
#endif
return;
if (g_devices.size() != 1) {
g_activeEcid = 0;
return out;
}
if (event->event != IDEVICE_DEVICE_ADD)
return;
auto it = g_devices.begin();
if (g_activeEcid != 0) {
auto activeIt = g_devices.find(g_activeEcid);
if (activeIt != g_devices.end())
it = activeIt;
}
g_activeEcid = it->first;
out = it->second.state;
out.connected = true;
out.connectedDeviceCount = 1;
out.multipleDevices = false;
return out;
}
void publish_state() {
std::vector<DeviceStateCallback> callbacks;
DeviceState state;
{
std::lock_guard<std::mutex> lock(g_mutex);
callbacks = g_callbacks;
state = build_public_state_locked();
}
dispatch_callbacks(callbacks, state);
}
bool lockdown_get_string(lockdownd_client_t client, const char* key, std::string& out) {
plist_t value = nullptr;
if (lockdownd_get_value(client, nullptr, key, &value) != LOCKDOWN_E_SUCCESS || !value)
return false;
char* raw = nullptr;
plist_get_string_val(value, &raw);
if (raw) {
out = raw;
free(raw);
}
plist_free(value);
return !out.empty();
}
bool lockdown_get_uint(lockdownd_client_t client, const char* key, uint64_t& out) {
plist_t value = nullptr;
if (lockdownd_get_value(client, nullptr, key, &value) != LOCKDOWN_E_SUCCESS || !value)
return false;
plist_get_uint_val(value, &out);
plist_free(value);
return out != 0;
}
bool build_normal_state(const idevice_event_t* event, DeviceState& out, std::string& connectionType) {
idevice_t device = nullptr;
if (idevice_new(&device, event->udid) != IDEVICE_E_SUCCESS)
return;
state.connected = true;
state.mode = DeviceMode::Normal;
state.udid = event->udid ? event->udid : "";
if (!event || idevice_new(&device, event->udid) != IDEVICE_E_SUCCESS || !device)
return false;
lockdownd_client_t client = nullptr;
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") == LOCKDOWN_E_SUCCESS)
{
plist_t product = nullptr;
plist_t version = nullptr;
plist_t ecid = nullptr;
char *p = nullptr, *v = nullptr;
uint64_t ecid_val = 0;
lockdownd_get_value(client, nullptr, "ProductType", &product);
lockdownd_get_value(client, nullptr, "ProductVersion", &version);
lockdownd_get_value(client, nullptr, "UniqueChipID", &ecid);
if (product) plist_get_string_val(product, &p);
if (version) plist_get_string_val(version, &v);
if (ecid) plist_get_uint_val(ecid, &ecid_val);
state.productType = p ? p : "";
state.productVersion = v ? v : "";
state.ecid = ecid_val;
free(p);
free(v);
if (product) plist_free(product);
if (version) plist_free(version);
if (ecid) plist_free(ecid);
lockdownd_client_free(client);
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") != LOCKDOWN_E_SUCCESS) {
idevice_free(device);
return false;
}
out.connected = true;
out.mode = DeviceMode::Normal;
out.udid = event->udid ? event->udid : "";
lockdown_get_string(client, "ProductType", out.productType);
lockdown_get_string(client, "ProductVersion", out.productVersion);
lockdown_get_string(client, "ConnectionType", connectionType);
lockdown_get_uint(client, "UniqueChipID", out.ecid);
out.isSupported = SequenceIsSupported(out.productType);
lockdownd_client_free(client);
idevice_free(device);
state.isSupported = SequenceIsSupported(state.productType);
#ifdef WITH_GUI
send_device_state(frame, state);
#endif
return true;
}
void recovery_device_event_cb(
const irecv_device_event_t* event,
void* user_data)
{
#ifdef WITH_GUI
auto* frame = static_cast<MainFrame*>(user_data);
if (!frame || !event)
return;
#endif
bool build_recovery_state(const irecv_device_event_t* event, DeviceState& out) {
if (!event || !event->device_info || !event->device_info->ecid)
return false;
DeviceState state = make_fresh_state();
if (event->type == IRECV_DEVICE_REMOVE)
{
#ifdef WITH_GUI
send_device_state(frame, state);
#endif
return;
}
if (event->type != IRECV_DEVICE_ADD)
return;
state.connected = true;
if (event->mode == IRECV_K_DFU_MODE)
{
state.mode = DeviceMode::DFU;
#ifdef WITH_GUI
send_device_state(frame, state);
#endif
return;
}
if (!(event->mode == IRECV_K_RECOVERY_MODE_1 ||
event->mode == IRECV_K_RECOVERY_MODE_2 ||
event->mode == IRECV_K_RECOVERY_MODE_3 ||
event->mode == IRECV_K_RECOVERY_MODE_4))
{
return;
}
state.mode = DeviceMode::Recovery;
uint64_t ecid_val = 0;
if (event->device_info && event->device_info->ecid)
ecid_val = event->device_info->ecid;
state.ecid = ecid_val;
out.connected = true;
out.ecid = event->device_info->ecid;
out.mode = (event->mode == IRECV_K_DFU_MODE) ? DeviceMode::DFU : DeviceMode::Recovery;
irecv_client_t client = nullptr;
int attempts = 0;
const int max_attempts = 10;
while (attempts < max_attempts)
{
if (irecv_open_with_ecid(&client, state.ecid) == IRECV_E_SUCCESS)
const int maxAttempts = 10;
while (attempts < maxAttempts) {
if (irecv_open_with_ecid(&client, out.ecid) == IRECV_E_SUCCESS)
break;
attempts++;
++attempts;
std::this_thread::sleep_for(std::chrono::milliseconds(300));
}
if (client)
{
LOG_VERBOSE("Opened client after %d attempts", attempts);
if (client) {
irecv_device_t device = nullptr;
if (irecv_devices_get_device_by_client(client, &device) == IRECV_E_SUCCESS && device)
{
state.productType = device->product_type ? device->product_type : "";
state.displayName = device->display_name ? device->display_name : "";
if (irecv_devices_get_device_by_client(client, &device) == IRECV_E_SUCCESS && device) {
out.productType = device->product_type ? device->product_type : "";
out.displayName = device->display_name ? device->display_name : "";
}
irecv_close(client);
}
else
{
LOG_ERROR("Failed to open irecv client after retries");
} else {
LOG_VERBOSE("Failed to open irecv client after retries for ECID=%llu", static_cast<unsigned long long>(out.ecid));
}
state.isSupported = SequenceIsSupported(state.productType);
#ifdef WITH_GUI
send_device_state(frame, state);
#endif
out.isSupported = SequenceIsSupported(out.productType);
return true;
}
#endif // WITH_GUI
void handle_normal_add(const idevice_event_t* event) {
if (!event)
return;
if (event->conn_type != CONNECTION_USBMUXD) {
LOG_VERBOSE("Ignoring non-USB normal mode device event");
return;
}
DeviceState discovered;
std::string connectionType;
if (!build_normal_state(event, discovered, connectionType))
return;
if (!connectionType.empty() && connectionType != "USB") {
LOG_VERBOSE("Ignoring over-the-air normal mode device (ConnectionType=%s)", connectionType.c_str());
return;
}
if (discovered.ecid == 0) {
LOG_VERBOSE("Ignoring normal mode device without ECID");
return;
}
{
std::lock_guard<std::mutex> lock(g_mutex);
ManagedDevice& m = g_devices[discovered.ecid];
m.state = discovered;
m.normalPresent = true;
m.recoveryPresent = false;
if (!discovered.udid.empty())
g_udidToEcid[discovered.udid] = discovered.ecid;
}
publish_state();
}
void handle_normal_remove(const idevice_event_t* event) {
if (!event || !event->udid)
return;
{
std::lock_guard<std::mutex> lock(g_mutex);
auto mapIt = g_udidToEcid.find(event->udid);
if (mapIt == g_udidToEcid.end())
return;
const uint64_t ecid = mapIt->second;
g_udidToEcid.erase(mapIt);
auto devIt = g_devices.find(ecid);
if (devIt == g_devices.end())
return;
devIt->second.normalPresent = false;
if (!devIt->second.normalPresent && !devIt->second.recoveryPresent)
g_devices.erase(devIt);
}
publish_state();
}
void handle_recovery_add(const irecv_device_event_t* event) {
DeviceState discovered;
if (!build_recovery_state(event, discovered))
return;
{
std::lock_guard<std::mutex> lock(g_mutex);
ManagedDevice& m = g_devices[discovered.ecid];
m.state = discovered;
m.recoveryPresent = true;
m.normalPresent = false;
}
publish_state();
}
void handle_recovery_remove(const irecv_device_event_t* event) {
bool changed = false;
{
std::lock_guard<std::mutex> lock(g_mutex);
uint64_t ecid = 0;
if (event && event->device_info)
ecid = event->device_info->ecid;
if (ecid == 0) {
for (const auto& [key, value] : g_devices) {
if (!value.recoveryPresent)
continue;
if (ecid != 0) {
ecid = 0;
break;
}
ecid = key;
}
}
if (ecid == 0)
return;
auto devIt = g_devices.find(ecid);
if (devIt == g_devices.end())
return;
devIt->second.recoveryPresent = false;
if (!devIt->second.normalPresent && !devIt->second.recoveryPresent)
g_devices.erase(devIt);
for (auto it = g_udidToEcid.begin(); it != g_udidToEcid.end();) {
if (it->second == ecid && g_devices.find(ecid) == g_devices.end())
it = g_udidToEcid.erase(it);
else
++it;
}
changed = true;
}
if (changed)
publish_state();
}
bool is_supported_recovery_mode(irecv_mode mode) {
return mode == IRECV_K_DFU_MODE ||
mode == IRECV_K_RECOVERY_MODE_1 ||
mode == IRECV_K_RECOVERY_MODE_2 ||
mode == IRECV_K_RECOVERY_MODE_3 ||
mode == IRECV_K_RECOVERY_MODE_4;
}
} // namespace
void register_device_state_callback(DeviceStateCallback cb) {
if (!cb)
return;
DeviceState snapshot;
{
std::lock_guard<std::mutex> lock(g_mutex);
g_callbacks.push_back(cb);
snapshot = build_public_state_locked();
}
cb(snapshot);
}
void ensure_device_event_system_started() {
std::call_once(g_startOnce, []() {
if (idevice_event_subscribe(normal_device_event_cb, nullptr) != IDEVICE_E_SUCCESS)
LOG_ERROR("Failed to subscribe to normal mode device events");
if (irecv_device_event_subscribe(&g_recoveryCtx, recovery_device_event_cb, nullptr) != IRECV_E_SUCCESS)
LOG_ERROR("Failed to subscribe to recovery/DFU device events");
char** udids = nullptr;
int count = 0;
if (idevice_get_device_list(&udids, &count) == IDEVICE_E_SUCCESS && udids) {
for (int i = 0; i < count; ++i) {
idevice_event_t synthetic_event;
synthetic_event.event = IDEVICE_DEVICE_ADD;
synthetic_event.udid = udids[i];
synthetic_event.conn_type = CONNECTION_USBMUXD;
handle_normal_add(&synthetic_event);
}
idevice_device_list_free(udids);
}
});
}
void normal_device_event_cb(const idevice_event_t* event, void* user_data) {
(void)user_data;
if (!event)
return;
if (event->event == IDEVICE_DEVICE_ADD) {
handle_normal_add(event);
return;
}
if (event->event == IDEVICE_DEVICE_REMOVE)
handle_normal_remove(event);
}
void recovery_device_event_cb(const irecv_device_event_t* event, void* user_data) {
(void)user_data;
if (!event)
return;
if (event->type == IRECV_DEVICE_ADD && is_supported_recovery_mode(event->mode)) {
handle_recovery_add(event);
return;
}
if (event->type == IRECV_DEVICE_REMOVE)
handle_recovery_remove(event);
}
#endif // WITH_GUI || WITH_TUI
+33 -5
View File
@@ -1,21 +1,49 @@
#ifdef WITH_GUI
#if defined(WITH_GUI) || defined(WITH_TUI)
#ifndef EVENT_H
#define EVENT_H
#include "state.hpp"
#include <cstdint>
#include <functional>
#include <string>
#include <wx/wx.h>
#ifdef WITH_GUI
# include <wx/wx.h>
#endif
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libirecovery.h>
class MainFrame;
enum class DeviceMode
{
None,
Normal,
Recovery,
DFU
};
struct DeviceState
{
bool connected = false;
bool isSupported = false;
bool multipleDevices = false;
DeviceMode mode = DeviceMode::None;
std::string productVersion;
std::string productType;
std::string displayName;
std::string udid;
uint64_t ecid = 0;
uint32_t connectedDeviceCount = 0;
};
using DeviceStateCallback = std::function<void(const DeviceState&)>;
void register_device_state_callback(DeviceStateCallback cb);
void ensure_device_event_system_started();
void normal_device_event_cb(const idevice_event_t* event, void* user_data);
void recovery_device_event_cb(const irecv_device_event_t* event, void* user_data);
#endif // EVENT_H
#endif // WITH_GUI
#endif // WITH_GUI || WITH_TUI
+30
View File
@@ -23,6 +23,36 @@
#define PID_PONGO (0x4141)
#define PID_DFU (0x1227)
#if defined(WITH_GUI) || defined(WITH_TUI)
const char* stage_to_string(int stage)
{
switch (stage) {
case STAGE_SETUP: return "Checking if device is ready";
case STAGE_SPRAY: return "Setting up the exploit (this is the heap spray)";
case STAGE_PATCH: return "Right before trigger (this is the real bug setup)";
case STAGE_YOLODFU: return "Booting PongoOS...";
case STAGE_PONGO: return "Booting...";
case STAGE_DONE: return "Done.";
default: return "Idle.";
}
}
int stage_to_progress(int stage)
{
switch (stage) {
case STAGE_SETUP: return 30;
case STAGE_SPRAY: return 40;
case STAGE_PATCH: return 50;
case STAGE_YOLODFU: return 60;
case STAGE_PONGO: return 70;
case STAGE_DONE: return 100;
default: return 0;
}
}
#endif // WITH_GUI || WITH_TUI
static void *exploit_thread(void *arg)
{
LOG_VERBOSE("Starting thread for DFU devices");
+7
View File
@@ -22,6 +22,13 @@ enum exploit_stage {
STAGE_DONE = 6,
};
#if defined(WITH_GUI) || defined(WITH_TUI)
const char* stage_to_string(int stage);
int stage_to_progress(int stage);
#endif // WITH_GUI || WITH_TUI
typedef struct {
atomic_int result;
atomic_bool stop;
+8 -6
View File
@@ -11,7 +11,9 @@
#endif
#include "m8/run.h"
#ifdef WITH_TUI
# include "tui/Tui.hpp"
#endif
#include "utils.h"
#include "globals.h"
#include "paleinfo.h"
@@ -31,9 +33,9 @@ void print_credits() {
":: Made by: asdfugil, kok3shidoll, claration, mineek\n"
":: nekohaxx, plooshi, staturnz\n"
":: ======== Thanks to =======>\n"
":: Thanks to: llsc12, itsnebulalol, lrdsnow, kirb, ehilwyma\n"
":: opa334, 0x7ff, sbingner, nikias (libimobiledevice)\n"
":: dedbeddedbed, tihmstar\n"
":: Thanks to: itsnebulalol, llsc12, lrdsnow, dedbeddedbed\n"
":: kirb, ehilwyma, opa334, 0x7ff, alfiecg25, sneko, sbingner\n"
":: nikias, tihmstar\n"
":: Checkra1n (Siguza, axi0mx, littlelailo et al.)\n"
":: Procursus (Hayden Seay, Cameron Katri, Keto et al.)\n"
":: ==========================>\n\n"
@@ -329,8 +331,8 @@ int main(int argc, char* argv[], char* envp[]) {
wxEntryCleanup();
return 0;
} else if (palerain_flags & palerain_option_tui) {
LOG_WARN("TUI is not implemented yet");
return 1;
ui_run();
return 0;
} else {
shared_t state{};
return exploit(&state);
+2 -3
View File
@@ -1,4 +1,4 @@
#ifdef WITH_GUI
#if defined(WITH_GUI) || defined(WITH_TUI)
#include <nlohmann/json.hpp>
#include "gen/DFUHelperDeviceInfo.h"
@@ -28,7 +28,6 @@ DfuSequence ParseSequence(const std::string& deviceKey)
seq.imageName = layout.value("image_name", "");
seq.imageWidth = layout.value("image_width", 0);
seq.imageHeight = layout.value("image_height", 0);
seq.imageOffsetX = layout.value("image_offset_x", 0);
if (layout.contains("buttons"))
{
@@ -85,4 +84,4 @@ bool SequenceIsSupported(const std::string& deviceKey)
return !seq.steps.empty();
}
#endif // WITH_GUI
#endif // WITH_GUI || WITH_TUI
+3 -3
View File
@@ -1,9 +1,10 @@
#ifdef WITH_GUI
#if defined(WITH_GUI) || defined(WITH_TUI)
#ifndef SEQUENCE_H
#define SEQUENCE_H
#include <string>
#include <vector>
struct DfuButton
{
@@ -26,7 +27,6 @@ struct DfuSequence
std::string imageName;
int imageWidth = 0;
int imageHeight = 0;
int imageOffsetX = 0;
std::vector<DfuButton> buttons;
std::vector<DfuStep> steps;
@@ -37,4 +37,4 @@ bool SequenceIsSupported(const std::string& deviceKey);
#endif // SEQUENCE_H
#endif // WITH_GUI
#endif // WITH_GUI || WITH_TUI
-27
View File
@@ -1,27 +0,0 @@
#ifndef STATE_H
#define STATE_H
#include <string>
#include <cstdint>
enum class DeviceMode
{
None,
Normal,
Recovery,
DFU
};
struct DeviceState
{
bool connected = false;
bool isSupported = false;
DeviceMode mode = DeviceMode::None;
std::string productVersion;
std::string productType;
std::string displayName;
std::string udid;
uint64_t ecid;
};
#endif // STATE_H
View File
Whitespace-only changes.
+408
View File
@@ -0,0 +1,408 @@
#ifdef WITH_TUI
#include "Tui.hpp"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <clocale>
#include <string>
#include <mutex>
#include <vector>
#include <memory>
#include <ncurses.h>
#include "TuiPanel.hpp"
#include "TuiMainPanel.hpp"
#include "TuiSettingsPanel.hpp"
#include "TuiRecoveryPanel.hpp"
#include "TuiDfuPanel.hpp"
#include "TuiExploitPanel.hpp"
#include "TuiBranding.hpp"
#include "../globals.h"
#include "../utils.h"
#include "../paleinfo.h"
#include "../event.hpp"
#define WIDTH 80
#define HEIGHT 24
static constexpr int LEFT_PANEL_WIDTH = 65;
static constexpr int RIGHT_PANEL_WIDTH = WIDTH - LEFT_PANEL_WIDTH;
static constexpr int RIGHT_INNER_WIDTH = RIGHT_PANEL_WIDTH - 2;
static constexpr int MAIN_BUTTON_VERTICAL_GAP = 1;
static constexpr int SIDEBAR_BUTTON_WIDTH = 13;
static constexpr int SIDEBAR_WALL_GAP = 1;
static constexpr short COLOR_PAIR_SUBTITLE = 1;
static std::string trim_button_label(std::string label) {
while (!label.empty() && label.front() == ' ') {
label.erase(label.begin());
}
while (!label.empty() && label.back() == ' ') {
label.pop_back();
}
return label;
}
static std::string format_sidebar_button_label(const char* raw_label, int button_width) {
if (button_width < 2) {
return "";
}
if (!raw_label) {
return std::string(static_cast<size_t>(button_width), ' ');
}
std::string label(raw_label);
if (label.size() >= 2 && label.front() == '[' && label.back() == ']') {
label = label.substr(1, label.size() - 2);
}
label = trim_button_label(label);
const int inner_width = button_width - 2;
if (inner_width <= 0) {
return "[]";
}
if (static_cast<int>(label.size()) > inner_width) {
label.resize(static_cast<size_t>(inner_width));
}
const int left_pad = std::max(0, (inner_width - static_cast<int>(label.size())) / 2);
const int right_pad = std::max(0, inner_width - static_cast<int>(label.size()) - left_pad);
return "[" + std::string(static_cast<size_t>(left_pad), ' ') + label + std::string(static_cast<size_t>(right_pad), ' ') + "]";
}
static void draw_panel_border(int y, int x, int width, int height, const char* title = nullptr) {
if (title) {
char title_buf[64];
std::snprintf(title_buf, sizeof(title_buf), "[ %s ]", title);
int title_len = static_cast<int>(std::strlen(title_buf));
int title_start_x = x + (width - title_len) / 2;
mvhline(y, x, ACS_HLINE, title_start_x - x);
mvprintw(y, title_start_x, "%s", title_buf);
mvhline(y, title_start_x + title_len, ACS_HLINE, (x + width) - (title_start_x + title_len));
} else {
mvhline(y, x, ACS_HLINE, width);
}
mvhline(y + height - 1, x, ACS_HLINE, width);
mvvline(y, x, ACS_VLINE, height);
mvvline(y, x + width - 1, ACS_VLINE, height);
mvaddch(y, x, ACS_ULCORNER);
mvaddch(y, x + width - 1, ACS_URCORNER);
mvaddch(y + height - 1, x, ACS_LLCORNER);
mvaddch(y + height - 1, x + width - 1, ACS_LRCORNER);
}
static TuiFrame* g_frame = nullptr;
TuiFrame::TuiFrame() {
InitPanels();
g_frame = this;
InitDeviceEventListeners();
}
TuiFrame::~TuiFrame() {
if (g_frame == this) {
g_frame = nullptr;
}
}
void TuiFrame::InitPanels() {
m_panels.resize(PANEL_COUNT);
m_panels[PANEL_MAIN] = std::make_unique<TuiMainPanel>(this);
m_panels[PANEL_OPTIONS] = std::make_unique<TuiSettingsPanel>(this);
m_panels[PANEL_START_1] = std::make_unique<TuiRecoveryPanel>(this);
m_panels[PANEL_START_2] = std::make_unique<TuiDfuPanel>(this);
m_panels[PANEL_FINAL] = std::make_unique<TuiExploitPanel>(this);
}
void TuiFrame::InitDeviceEventListeners() {
register_device_state_callback([](const DeviceState& state) {
update_tui_device_state(&state);
});
ensure_device_event_system_started();
}
void TuiFrame::ActivatePanelIfNeeded(PanelState previous_panel) {
if (m_currentPanel == previous_panel) {
return;
}
if (m_panels[m_currentPanel]) {
m_panels[m_currentPanel]->on_show();
}
}
void TuiFrame::DrawUi() {
int rows, cols;
getmaxyx(stdscr, rows, cols);
erase();
if (rows < HEIGHT || cols < WIDTH) {
mvprintw(rows / 2, (cols - 34) / 2, "Terminal too small, need 80x24");
refresh();
return;
}
int start_y = (rows - HEIGHT) / 2;
int start_x = (cols - WIDTH) / 2;
const int left_x = start_x;
const int right_x = start_x + LEFT_PANEL_WIDTH;
draw_panel_border(start_y, left_x, LEFT_PANEL_WIDTH, HEIGHT, "palera1n - Version beta " PALERAIN_VERSION);
draw_panel_border(start_y, right_x, RIGHT_PANEL_WIDTH, HEIGHT);
if (m_panels[m_currentPanel]) {
m_panels[m_currentPanel]->draw(start_y, left_x, m_selected);
}
if (m_panels[m_currentPanel]) {
int btn_cnt = m_panels[m_currentPanel]->get_button_count();
int total_items = m_panels[m_currentPanel]->get_total_items();
const char **panel_buttons = m_panels[m_currentPanel]->get_buttons();
const int sidebar_x = right_x;
const int sidebar_inner_x = sidebar_x + 1;
const int sidebar_inner_width = RIGHT_INNER_WIDTH;
const int sidebar_top = start_y + 1;
const int sidebar_bottom = start_y + HEIGHT - 2;
const int content_top = sidebar_top + SIDEBAR_WALL_GAP;
const int content_bottom = sidebar_bottom;
const int content_x = sidebar_inner_x + SIDEBAR_WALL_GAP;
const int content_width = std::max(1, sidebar_inner_width - (SIDEBAR_WALL_GAP * 2));
draw_tui_sidebar_branding(sidebar_top, sidebar_inner_x, sidebar_inner_width);
std::vector<std::string> labels;
labels.reserve(static_cast<size_t>(btn_cnt));
const int button_width = std::clamp(SIDEBAR_BUTTON_WIDTH, 8, content_width);
for (int i = 0; i < btn_cnt; ++i) {
labels.push_back(format_sidebar_button_label(panel_buttons[i], button_width));
}
const int buttons_block_height = std::max(1, btn_cnt + ((btn_cnt - 1) * MAIN_BUTTON_VERTICAL_GAP));
const int first_y = std::max(content_top, content_bottom - buttons_block_height + 1);
const int button_x = content_x + std::max(0, (content_width - button_width) / 2);
for (int i = 0; i < btn_cnt; ++i) {
if (i > 0) {
const int separator_y = first_y + (i * (MAIN_BUTTON_VERTICAL_GAP + 1)) - 1;
mvhline(separator_y, sidebar_inner_x, ACS_HLINE, sidebar_inner_width);
}
int btn_idx = total_items - btn_cnt + i;
bool enabled = m_panels[m_currentPanel]->is_button_enabled(btn_idx);
const std::string& label = labels[static_cast<size_t>(i)];
if (!enabled) {
attron(A_DIM);
mvprintw(first_y + (i * (MAIN_BUTTON_VERTICAL_GAP + 1)), button_x, "%s", label.c_str());
attroff(A_DIM);
} else {
if (btn_idx == m_selected) attron(A_REVERSE);
mvprintw(first_y + (i * (MAIN_BUTTON_VERTICAL_GAP + 1)), button_x, "%s", label.c_str());
if (btn_idx == m_selected) attroff(A_REVERSE);
}
}
}
refresh();
}
void TuiFrame::HandleInput(int ch, int start_y, int start_x) {
if (ch == ERR || !m_panels[m_currentPanel]) return;
int total_items = m_panels[m_currentPanel]->get_total_items();
if (total_items == 0) return;
PanelState previous_panel = m_currentPanel;
switch(ch) {
case KEY_UP:
case KEY_LEFT: {
int prev = m_selected;
do {
prev = (prev - 1 + total_items) % total_items;
} while (!m_panels[m_currentPanel]->is_button_enabled(prev) && prev != m_selected);
m_selected = prev;
break;
}
case KEY_DOWN:
case KEY_RIGHT: {
int next = m_selected;
do {
next = (next + 1) % total_items;
} while (!m_panels[m_currentPanel]->is_button_enabled(next) && next != m_selected);
m_selected = next;
break;
}
case KEY_ENTER:
case 10:
case 13:
if (!m_panels[m_currentPanel]->is_button_enabled(m_selected)) return;
m_panels[m_currentPanel]->handle_enter(m_selected, start_y, start_x);
break;
}
ActivatePanelIfNeeded(previous_panel);
}
void TuiFrame::Run() {
m_running = true;
std::setlocale(LC_ALL, "");
initscr();
noecho();
cbreak();
curs_set(0);
keypad(stdscr, TRUE);
timeout(100);
if (has_colors()) {
start_color();
use_default_colors();
init_pair(COLOR_PAIR_SUBTITLE, COLOR_YELLOW, -1);
}
int ch;
while (m_running) {
PanelState previous_panel = m_currentPanel;
if (m_panels[m_currentPanel]) {
m_panels[m_currentPanel]->update();
}
ActivatePanelIfNeeded(previous_panel);
DrawUi();
ch = getch();
if (ch == 'q' || ch == 'Q') {
Stop();
continue;
}
int rows, cols;
getmaxyx(stdscr, rows, cols);
if (rows >= HEIGHT && cols >= WIDTH) {
int start_y = (rows - HEIGHT) / 2;
int start_x = (cols - WIDTH) / 2;
HandleInput(ch, start_y, start_x);
}
}
endwin();
}
void TuiFrame::ShowMain(int selected) {
m_currentPanel = PANEL_MAIN;
m_selected = selected;
}
void TuiFrame::ShowSettings(int selected) {
m_currentPanel = PANEL_OPTIONS;
m_selected = selected;
}
void TuiFrame::ShowRecovery(int selected) {
m_currentPanel = PANEL_START_1;
m_selected = selected;
}
void TuiFrame::ShowDfu(int selected) {
m_currentPanel = PANEL_START_2;
m_selected = selected;
}
void TuiFrame::ShowExploit(int selected) {
m_currentPanel = PANEL_FINAL;
m_selected = selected;
}
PanelState TuiFrame::GetCurrentPanel() const {
return m_currentPanel;
}
int TuiFrame::GetSelected() const {
return m_selected;
}
void TuiFrame::SetSelected(int selected) {
m_selected = selected;
}
bool TuiFrame::IsRunning() const {
return m_running;
}
void TuiFrame::Stop() {
m_running = false;
}
DeviceState TuiFrame::GetDeviceState() const {
std::lock_guard<std::mutex> lock(m_deviceMutex);
return m_device;
}
void TuiFrame::UpdateDeviceState(const DeviceState& state) {
{
std::lock_guard<std::mutex> lock(m_deviceMutex);
m_device = state;
}
const PanelState previous_panel = m_currentPanel;
if (m_currentPanel != PANEL_MAIN && m_currentPanel != PANEL_OPTIONS && m_panels[m_currentPanel]) {
m_panels[m_currentPanel]->handle_device_update(state);
ActivatePanelIfNeeded(previous_panel);
}
}
TuiMainPanel* TuiFrame::GetMainPanel() const {
return static_cast<TuiMainPanel*>(m_panels[PANEL_MAIN].get());
}
TuiSettingsPanel* TuiFrame::GetSettingsPanel() const {
return static_cast<TuiSettingsPanel*>(m_panels[PANEL_OPTIONS].get());
}
TuiRecoveryPanel* TuiFrame::GetRecoveryPanel() const {
return static_cast<TuiRecoveryPanel*>(m_panels[PANEL_START_1].get());
}
TuiDfuPanel* TuiFrame::GetDfuPanel() const {
return static_cast<TuiDfuPanel*>(m_panels[PANEL_START_2].get());
}
TuiExploitPanel* TuiFrame::GetExploitPanel() const {
return static_cast<TuiExploitPanel*>(m_panels[PANEL_FINAL].get());
}
void update_tui_device_state(const DeviceState* new_state) {
if (!g_frame || !new_state) {
return;
}
g_frame->UpdateDeviceState(*new_state);
}
void ui_run(void) {
TuiFrame frame;
frame.Run();
}
#endif // WITH_TUI
+71
View File
@@ -0,0 +1,71 @@
#ifdef WITH_TUI
#ifndef TUI_HPP
#define TUI_HPP
#include <memory>
#include <mutex>
#include <vector>
#include "../event.hpp"
#include "TuiPanel.hpp"
class TuiPanel;
class TuiMainPanel;
class TuiSettingsPanel;
class TuiRecoveryPanel;
class TuiDfuPanel;
class TuiExploitPanel;
class TuiFrame {
public:
TuiFrame();
~TuiFrame();
void Run();
void ShowMain(int selected = 1);
void ShowSettings(int selected = 5);
void ShowRecovery(int selected = 1);
void ShowDfu(int selected = 1);
void ShowExploit(int selected = 0);
PanelState GetCurrentPanel() const;
int GetSelected() const;
void SetSelected(int selected);
bool IsRunning() const;
void Stop();
DeviceState GetDeviceState() const;
void UpdateDeviceState(const DeviceState& state);
TuiMainPanel* GetMainPanel() const;
TuiSettingsPanel* GetSettingsPanel() const;
TuiRecoveryPanel* GetRecoveryPanel() const;
TuiDfuPanel* GetDfuPanel() const;
TuiExploitPanel* GetExploitPanel() const;
private:
void InitPanels();
void InitDeviceEventListeners();
void ActivatePanelIfNeeded(PanelState previous_panel);
void DrawUi();
void HandleInput(int ch, int start_y, int start_x);
PanelState m_currentPanel = PANEL_MAIN;
int m_selected = 1;
bool m_running = true;
DeviceState m_device;
mutable std::mutex m_deviceMutex;
std::vector<std::unique_ptr<TuiPanel>> m_panels;
};
void ui_run(void);
void update_tui_device_state(const DeviceState* new_state);
#endif // TUI_HPP
#endif // WITH_TUI
+52
View File
@@ -0,0 +1,52 @@
#ifdef WITH_TUI
#include "TuiBranding.hpp"
#include <algorithm>
#include <cstring>
#include <ncurses.h>
static constexpr int kBrandingLeftTrim = 3;
void draw_tui_sidebar_branding(int y, int x, int width) {
static const char* art[] = {
" ++ ",
" #@@# ",
" +@@@@+ ",
" :@@@@@@: ",
" #@@@@@@# ",
" +@@@@@@@@+ ",
" :#*@@@@@@*#: ",
" %##palera1n% ",
" .####%@@%####. ",
" @############@ ",
"=@@*########*@@=",
"+@@@@%####%@@@@+",
":@@@@%%**%%@@@@:",
" +@@%%*@@*%%@@+ ",
" ++%@@@@%++ ",
" :++: "
};
static const int art_count = static_cast<int>(sizeof(art) / sizeof(art[0]));
int max_art_width = 0;
for (int i = 0; i < art_count; ++i) {
const int line_width = std::max(0, static_cast<int>(std::strlen(art[i])) - kBrandingLeftTrim);
max_art_width = std::max(max_art_width, line_width);
}
const int centered_x = x + std::max(0, (width - max_art_width) / 2);
const int art_x = std::max(x, centered_x - 7);
attron(A_DIM);
for (int i = 0; i < art_count; ++i) {
const char* line = art[i];
const int line_len = static_cast<int>(std::strlen(line));
const int trim = std::min(kBrandingLeftTrim, line_len);
mvprintw(y + i, art_x, "%.*s", width, line + trim);
}
attroff(A_DIM);
}
#endif // WITH_TUI
+10
View File
@@ -0,0 +1,10 @@
#ifdef WITH_TUI
#ifndef TUI_BRANDING_HPP
#define TUI_BRANDING_HPP
void draw_tui_sidebar_branding(int y, int x, int width);
#endif // TUI_BRANDING_HPP
#endif // WITH_TUI
+139
View File
@@ -0,0 +1,139 @@
#ifdef WITH_TUI
#include "TuiDfuArt.hpp"
#include <ncurses.h>
static const char* kIpadArt[] = {
" __ ",
" ┌───────────────────┐ ",
" │ o === │ ",
" │███████████████████│ ",
" |│███████████████████│ ",
" |│███████████████████│ ",
" │███████████████████│ ",
" │███████████████████│ ",
" │███████████████████│ ",
" │███████████████████│ ",
" │███████████████████│ ",
" │███████████████████│ ",
" │███████████████████│ ",
" │ ┌─┐ │ ",
" │ └─┘ │ ",
" └───────────────────┘ ",
};
static const char* kIphoneSeArt[] = {
" __ ",
" ┌─────────────┐ ",
" │ o === │ ",
" │█████████████│ ",
" |│█████████████│ ",
" |│█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │ ┌─┐ │ ",
" │ └─┘ │ ",
" └─────────────┘ ",
};
static const char* kIphone7Art[] = {
" ",
" ┌─────────────┐ ",
" │ o === │ ",
" │█████████████│ ",
" |│█████████████│| ",
" |│█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │ ┌─┐ │ ",
" │ └─┘ │ ",
" └─────────────┘ ",
};
static const char* kIphoneXArt[] = {
" ",
" ┌─────────────┐ ",
" │███ o === ███│ ",
" │█████████████│ ",
" |│█████████████││ ",
" |│█████████████││ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" │█████████████│ ",
" └─────────────┘ ",
};
static const char* kRemoteArt[] = {
" ",
" ┌─────────┐ ",
" │ │ ",
" │ │ ",
" │ │ ",
" │ ┌─┐ ┌─┐ │ ",
" │ └─┘ └─┘ │ ",
" │ ┌─┐ ┌─┐ │ ",
" │ └─┘ │+│ │ ",
" │ ┌─┐ │-│ │ ",
" │ └─┘ └─┘ │ ",
" │ │ ",
" │ │ ",
" │ │ ",
" └─────────┘ ",
};
static const char* kAtvStdBrdArt[] = {
" art not yet implemented ",
};
static const DfuAsciiPreview kPreviews[] = {
{ "ipad", kIpadArt, 16, 20, -1 },
{ "ipadmini", kIpadArt, 16, 19, -1 },
{ "ipodtouch", kIphoneSeArt, 16, 22, -1 },
{ "ipodtouch7", kIpadArt, 16, 20, -1 },
{ "siriremote", kRemoteArt, 15, 22, -1 },
{ "atv_std_brd", kAtvStdBrdArt, 1, 20, -1 },
{ "iphonese", kIphoneSeArt, 16, 22, -1 },
{ "iphone6s", kIphone7Art, 16, 22, -1 },
{ "iphone7", kIphone7Art, 16, 22, -1 },
{ "iphone8", kIphone7Art, 16, 22, -1 },
{ "iphonex", kIphoneXArt, 16, 22, -1 },
};
const DfuAsciiPreview* find_dfu_ascii_preview(const std::string& imageName) {
for (const auto& preview : kPreviews) {
if (imageName == preview.imageName) {
return &preview;
}
}
return nullptr;
}
void draw_dfu_ascii_preview(const DfuAsciiPreview& preview, int sx, int sy) {
const int start_x = sx + preview.xOffset;
const int start_y = sy + 4 + preview.yOffset;
attron(A_DIM);
for (int i = 0; i < preview.lineCount; ++i) {
mvprintw(start_y + i, start_x, "%s", preview.lines[i]);
}
attroff(A_DIM);
}
#endif // WITH_TUI
+21
View File
@@ -0,0 +1,21 @@
#ifdef WITH_TUI
#ifndef TUI_DFU_ART_HPP
#define TUI_DFU_ART_HPP
#include <string>
struct DfuAsciiPreview {
const char* imageName;
const char* const* lines;
int lineCount;
int xOffset;
int yOffset;
};
const DfuAsciiPreview* find_dfu_ascii_preview(const std::string& imageName);
void draw_dfu_ascii_preview(const DfuAsciiPreview& preview, int sx, int sy);
#endif // TUI_DFU_ART_HPP
#endif // WITH_TUI
+359
View File
@@ -0,0 +1,359 @@
#ifdef WITH_TUI
#include "TuiDfuPanel.hpp"
#include <algorithm>
#include <thread>
#include <chrono>
#include <unordered_set>
#include <ncurses.h>
#include <libirecovery.h>
#include "Tui.hpp"
#include "TuiDfuArt.hpp"
#include "TuiText.hpp"
#include "TuiRecoveryPanel.hpp"
#include "../sequence.hpp"
#include "../event.hpp"
#include "../globals.h"
#include "../paleinfo.h"
static const char *buttons[] = { "Back", "Start" };
static constexpr int kDfuPanelCoordinateWidth = 310;
static constexpr int kTuiDeviceCanvasWidth = 52;
static constexpr int kDfuButtonCoordinateMaxY = 200;
static constexpr int kDfuContentX = 2;
static constexpr int kDfuContentWidth = 60;
static constexpr int kDfuStepsMaxLines = 5;
static constexpr int kDfuStepsBottomY = 22;
static constexpr int kDfuButtonX = 2;
static constexpr int kDfuButtonY = 4;
TuiDfuPanel::TuiDfuPanel(TuiFrame* frame)
: TuiPanel(frame) {}
const char** TuiDfuPanel::get_buttons() const {
return buttons;
}
void TuiDfuPanel::reset_sequence_state() {
m_index = 0;
m_stepRemaining = -1;
m_actionExecutedIndex = -1;
m_isEnteringDfu = false;
m_dfuSuccess = false;
m_waitingForDfuTransition = false;
m_dfuSuccessAt = std::chrono::steady_clock::time_point{};
m_last_tick = std::chrono::steady_clock::time_point{};
}
void TuiDfuPanel::load_sequence_for_product(const std::string& product_type, bool reset_state) {
if (m_current_product_type != product_type) {
m_current_product_type = product_type;
m_sequence = ParseSequence(product_type);
}
if (reset_state) {
reset_sequence_state();
}
}
void TuiDfuPanel::on_show() {
const DeviceState state = GetFrame()->GetDeviceState();
load_sequence_for_product(state.productType, true);
GetFrame()->SetSelected(1);
if (palerain_flags & palerain_option_quick) {
start_sequence();
}
}
void TuiDfuPanel::update() {
if (!m_waitingForDfuTransition || !m_dfuSuccess) {
return;
}
const auto now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_dfuSuccessAt).count() < 3) {
return;
}
m_waitingForDfuTransition = false;
GetFrame()->ShowExploit(0);
}
bool TuiDfuPanel::is_button_enabled(int btn_idx) const {
(void)btn_idx;
return !m_isEnteringDfu && !m_dfuSuccess;
}
void TuiDfuPanel::start_sequence() {
if (m_sequence.steps.empty()) {
return;
}
m_isEnteringDfu = true;
m_dfuSuccess = false;
m_index = 0;
m_stepRemaining = -1;
m_actionExecutedIndex = -1;
m_last_tick = std::chrono::steady_clock::now();
}
void TuiDfuPanel::update_sequence_timer() {
if (!m_isEnteringDfu || m_sequence.steps.empty() || m_index >= m_sequence.steps.size()) {
return;
}
auto now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(now - m_last_tick).count() >= 1) {
m_last_tick = now;
const auto& step = m_sequence.steps[m_index];
if (m_stepRemaining < 0) {
m_stepRemaining = step.duration;
}
if (m_stepRemaining > 0) {
--m_stepRemaining;
}
if (m_stepRemaining == 0) {
++m_index;
m_stepRemaining = -1;
}
if (m_index < m_sequence.steps.size()) {
const auto& activeStep = m_sequence.steps[m_index];
if (!activeStep.action.empty() && m_actionExecutedIndex != (int)m_index) {
m_actionExecutedIndex = m_index;
if (activeStep.action == "reboot") {
reboot();
}
}
}
}
}
int TuiDfuPanel::scale_coord(int value, int input_span, int output_span) const {
if (input_span <= 0 || output_span <= 1) {
return 0;
}
return (value * (output_span - 1)) / input_span;
}
void TuiDfuPanel::draw_sequence_buttons(int button_x, int button_y, int button_width, int button_height) const {
if (m_sequence.buttons.empty()) {
return;
}
std::unordered_set<std::string> active_buttons;
if (m_isEnteringDfu && m_index < m_sequence.steps.size()) {
const auto& buttons_for_step = m_sequence.steps[m_index].activeButtons;
active_buttons.insert(buttons_for_step.begin(), buttons_for_step.end());
}
for (const auto& btn : m_sequence.buttons) {
const int mapped_x = button_x + scale_coord(btn.x, kDfuPanelCoordinateWidth, button_width);
const int mapped_y = button_y + scale_coord(btn.y, kDfuButtonCoordinateMaxY, button_height);
const int label_x = std::clamp(mapped_x, button_x, button_x + std::max(0, button_width - 1));
const bool is_active = active_buttons.find(btn.id) != active_buttons.end();
if (is_active) {
attron(A_BOLD);
} else {
attron(A_DIM);
}
mvprintw(mapped_y, label_x, "%s", btn.name.c_str());
if (is_active) {
attroff(A_BOLD);
} else {
attroff(A_DIM);
}
}
}
void TuiDfuPanel::draw_wrapped_steps(int steps_x, int steps_width, int max_lines, int bottom_y) const {
if (steps_width <= 0 || max_lines <= 0 || m_sequence.steps.empty()) {
return;
}
struct RenderedLine {
bool is_current;
bool is_completed;
std::string text;
};
std::vector<RenderedLine> rendered_lines;
rendered_lines.reserve(static_cast<size_t>(max_lines));
for (size_t i = 0; i < m_sequence.steps.size(); ++i) {
const auto& step = m_sequence.steps[i];
const int seconds_left = (m_isEnteringDfu && i == m_index && m_stepRemaining >= 0)
? m_stepRemaining
: ((i < m_index) || m_dfuSuccess) ? 0 : step.duration;
std::string text = std::to_string(i + 1) + ". " + step.description + " (" + std::to_string(seconds_left) + ")";
std::vector<std::string> wrapped;
tui_text::append_wrapped_lines(text, steps_width, wrapped);
const bool is_current = m_isEnteringDfu && i == m_index;
const bool is_completed = (i < m_index) || m_dfuSuccess;
for (const auto& line : wrapped) {
if (static_cast<int>(rendered_lines.size()) >= max_lines) {
break;
}
rendered_lines.push_back({ is_current, is_completed, line });
}
if (static_cast<int>(rendered_lines.size()) >= max_lines) {
break;
}
}
const int start_y = bottom_y - static_cast<int>(rendered_lines.size()) + 1;
for (size_t i = 0; i < rendered_lines.size(); ++i) {
const auto& line = rendered_lines[i];
if (line.is_current) {
attron(A_BOLD);
} else if (line.is_completed) {
attron(A_DIM);
}
mvprintw(start_y + static_cast<int>(i), steps_x, "%.*s", steps_width, line.text.c_str());
if (line.is_current) {
attroff(A_BOLD);
} else if (line.is_completed) {
attroff(A_DIM);
}
}
}
void TuiDfuPanel::draw(int sy, int sx, int selected) {
(void)selected;
update_sequence_timer();
const int content_x = sx + kDfuContentX;
if (m_sequence.steps.empty()) {
mvprintw(sy + 2, content_x, "No DFU helper sequence is available for this device.");
} else if (m_dfuSuccess) {
mvprintw(sy + 2, content_x, "Device entered DFU mode successfully.");
} else {
tui_text::draw_wrapped_text(
sy + 2,
content_x,
kDfuContentWidth,
"Time to put the device into DFU mode.",
2
);
}
const int steps_x = content_x;
const int steps_width = kDfuContentWidth;
const int steps_max_lines = kDfuStepsMaxLines;
const int steps_bottom_y = sy + kDfuStepsBottomY;
const int button_x = sx + kDfuButtonX;
const int button_y = sy + kDfuButtonY;
const int button_width = kTuiDeviceCanvasWidth - 1;
const int button_height = (steps_bottom_y - steps_max_lines + 1) - button_y;
if (const DfuAsciiPreview* preview = find_dfu_ascii_preview(m_sequence.imageName)) {
draw_dfu_ascii_preview(*preview, sx, sy);
}
draw_sequence_buttons(button_x, button_y, button_width, button_height);
draw_wrapped_steps(steps_x, steps_width, steps_max_lines, steps_bottom_y);
}
void TuiDfuPanel::handle_enter(int selected, int sy, int sx) {
switch(selected) {
case 0: { // Back
reboot();
m_isEnteringDfu = false;
m_dfuSuccess = false;
GetFrame()->ShowMain(0);
break;
}
case 1: { // Start
start_sequence();
break;
}
}
}
void TuiDfuPanel::handle_device_update(const DeviceState& state) {
if (state.connected && !state.productType.empty()) {
load_sequence_for_product(state.productType, false);
}
if (!state.connected) {
if (!m_isEnteringDfu) {
reset_sequence_state();
GetFrame()->ShowMain(1);
}
return;
}
if (m_isEnteringDfu && state.mode == DeviceMode::DFU) {
m_isEnteringDfu = false;
m_dfuSuccess = true;
m_waitingForDfuTransition = true;
m_dfuSuccessAt = std::chrono::steady_clock::now();
m_index = m_sequence.steps.size();
m_stepRemaining = -1;
GetFrame()->SetSelected(1);
return;
}
if (m_isEnteringDfu && state.mode == DeviceMode::Normal) {
reset_sequence_state();
if (GetFrame()->GetRecoveryPanel()) {
GetFrame()->GetRecoveryPanel()->SetStatusText("Hmm... It seems like the device didn't enter recovery mode.");
}
GetFrame()->ShowRecovery(0);
}
if (m_isEnteringDfu && state.mode == DeviceMode::Recovery) {
reset_sequence_state();
GetFrame()->ShowMain(0);
}
}
void TuiDfuPanel::reboot() {
std::thread([this]() {
const auto state = GetFrame()->GetDeviceState();
if (!state.connected || state.mode != DeviceMode::Recovery)
return;
irecv_client_t client = nullptr;
int attempts = 0;
const int max_attempts = 8;
while (attempts < max_attempts) {
if (irecv_open_with_ecid(&client, state.ecid) == IRECV_E_SUCCESS)
break;
attempts++;
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
if (!client) return;
irecv_setenv(client, "auto-boot", "true");
irecv_saveenv(client);
irecv_reboot(client);
irecv_close(client);
}).detach();
}
#endif // WITH_TUI
+51
View File
@@ -0,0 +1,51 @@
#ifdef WITH_TUI
#ifndef TUI_DFU_PANEL_HPP
#define TUI_DFU_PANEL_HPP
#include "TuiPanel.hpp"
#include "../sequence.hpp"
#include <string>
#include <chrono>
class TuiDfuPanel : public TuiPanel {
public:
explicit TuiDfuPanel(TuiFrame* frame);
~TuiDfuPanel() override = default;
void on_show() override;
void update() override;
void draw(int sy, int sx, int selected) override;
void handle_enter(int selected, int sy, int sx) override;
void handle_device_update(const DeviceState& state) override;
bool is_button_enabled(int btn_idx) const override;
int get_total_items() const override { return 2; }
int get_button_count() const override { return 2; }
const char** get_buttons() const override;
private:
void load_sequence_for_product(const std::string& product_type, bool reset_state);
void reset_sequence_state();
void start_sequence();
void reboot();
void update_sequence_timer();
int scale_coord(int value, int input_span, int output_span) const;
void draw_sequence_buttons(int button_x, int button_y, int button_width, int button_height) const;
void draw_wrapped_steps(int steps_x, int steps_width, int max_lines, int bottom_y) const;
DfuSequence m_sequence;
std::string m_current_product_type;
size_t m_index = 0;
int m_stepRemaining = -1;
int m_actionExecutedIndex = -1;
bool m_isEnteringDfu = false;
bool m_dfuSuccess = false;
bool m_waitingForDfuTransition = false;
std::chrono::steady_clock::time_point m_dfuSuccessAt;
std::chrono::steady_clock::time_point m_last_tick;
};
#endif // TUI_DFU_PANEL_HPP
#endif // WITH_TUI
+134
View File
@@ -0,0 +1,134 @@
#ifdef WITH_TUI
#include "TuiExploitPanel.hpp"
#include <algorithm>
#include <chrono>
#include <memory>
#include <string>
#include <thread>
#include <ncurses.h>
#include "Tui.hpp"
#include "TuiText.hpp"
#include "../m8/run.h"
static const char *buttons[] = { "Back" };
static constexpr int kExploitContentX = 2;
static constexpr int kExploitContentWidth = 60;
TuiExploitPanel::TuiExploitPanel(TuiFrame* frame)
: TuiPanel(frame) {}
const char** TuiExploitPanel::get_buttons() const {
return buttons;
}
void TuiExploitPanel::draw(int sy, int sx, int selected) {
(void)selected;
const int content_x = sx + kExploitContentX;
tui_text::draw_wrapped_text(
sy + 2,
content_x,
kExploitContentWidth,
"Installing jailbreak, this will take a moment. If the device asks for a passcode please enter it. Do not disconnect the device until finished.",
3
);
std::string status_text;
int progress = 0;
{
std::lock_guard<std::mutex> lock(m_status_mutex);
status_text = m_status_text;
progress = m_progress;
}
mvprintw(sy + 8, content_x, "%.*s", kExploitContentWidth, status_text.empty() ? stage_to_string(STAGE_RESET) : status_text.c_str());
const int bar_width = 50;
const int filled = (std::clamp(progress, 0, 100) * bar_width) / 100;
std::string bar(static_cast<size_t>(bar_width), '-');
for (int i = 0; i < filled; ++i) {
bar[static_cast<size_t>(i)] = '#';
}
mvprintw(sy + 10, sx + 2, "[%s] %3d%%", bar.c_str(), std::clamp(progress, 0, 100));
}
void TuiExploitPanel::handle_enter(int selected, int sy, int sx) {
if (selected == 0) {
GetFrame()->ShowMain(1);
}
}
void TuiExploitPanel::handle_device_update(const DeviceState& state) {
(void)state;
}
void TuiExploitPanel::on_show() {
exploit();
}
bool TuiExploitPanel::is_button_enabled(int btn_idx) const {
(void)btn_idx;
return !m_is_exploiting;
}
void TuiExploitPanel::set_status(const std::string& status, int progress) {
std::lock_guard<std::mutex> lock(m_status_mutex);
m_status_text = status;
m_progress = std::clamp(progress, 0, 100);
}
void TuiExploitPanel::exploit() {
if (m_is_exploiting) {
return;
}
m_is_exploiting = true;
set_status(stage_to_string(STAGE_RESET), 0);
std::thread([this]() {
const DeviceState deviceState = GetFrame()->GetDeviceState();
if (!deviceState.connected || deviceState.mode != DeviceMode::DFU) {
set_status("Device is not in DFU mode.", 0);
m_is_exploiting = false;
return;
}
auto sharedState = std::make_shared<shared_t>();
atomic_init(&sharedState->stage, STAGE_RESET);
atomic_init(&sharedState->stop, false);
atomic_init(&sharedState->result, 0);
auto isFinished = std::make_shared<std::atomic<bool>>(false);
std::thread worker([sharedState, isFinished]() {
::exploit(sharedState.get());
isFinished->store(true);
});
for (;;) {
int stage = atomic_load(&sharedState->stage);
int progress = stage_to_progress(stage);
bool finished = isFinished->load();
set_status(stage_to_string(stage), progress);
if (stage == STAGE_DONE || finished) {
atomic_store(&sharedState->stop, true);
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (worker.joinable()) {
worker.join();
}
m_is_exploiting = false;
}).detach();
}
#endif // WITH_TUI
+38
View File
@@ -0,0 +1,38 @@
#ifdef WITH_TUI
#ifndef TUI_EXPLOIT_PANEL_HPP
#define TUI_EXPLOIT_PANEL_HPP
#include "TuiPanel.hpp"
#include <atomic>
#include <mutex>
#include <string>
class TuiExploitPanel : public TuiPanel {
public:
explicit TuiExploitPanel(TuiFrame* frame);
~TuiExploitPanel() override = default;
void draw(int sy, int sx, int selected) override;
void handle_enter(int selected, int sy, int sx) override;
void handle_device_update(const DeviceState& state) override;
bool is_button_enabled(int btn_idx) const override;
void on_show() override;
int get_total_items() const override { return 1; }
int get_button_count() const override { return 1; }
const char** get_buttons() const override;
private:
void exploit();
void set_status(const std::string& status, int progress);
std::mutex m_status_mutex;
std::string m_status_text;
int m_progress = 0;
std::atomic<bool> m_is_exploiting = false;
};
#endif // TUI_EXPLOIT_PANEL_HPP
#endif // WITH_TUI
+227
View File
@@ -0,0 +1,227 @@
#ifdef WITH_TUI
#include "TuiMainPanel.hpp"
#include <string>
#include <ncurses.h>
#include "Tui.hpp"
#include "TuiText.hpp"
#include "../event.hpp"
#include "../globals.h"
#include "../paleinfo.h"
static const char *buttons[] = { "Options", "Start", "Quit" };
static constexpr int kMainContentX = 2;
static constexpr int kMainContentWidth = 60;
static constexpr int kMainSeparatorX = 1;
static constexpr int kMainSeparatorWidth = 63;
static std::string get_device_title(const DeviceState& state) {
if (!state.connected) {
if (state.connectedDeviceCount > 1) {
return "Multiple devices detected";
}
return "No device connected";
}
const std::string product = state.productType.empty() ? "Unknown" : state.productType;
if (state.mode == DeviceMode::DFU) {
return "DFU Mode device";
}
return product;
}
static std::string get_device_subtitle(const DeviceState& state) {
if (!state.connected) {
if (state.connectedDeviceCount > 1) {
return std::to_string(state.connectedDeviceCount) +
"USB devices connected. Disconnect extras and keep only one device attached.";
}
return "Please connect a device to get started. Ensure version range is 15.0+";
}
const std::string version = state.productVersion.empty() ? "Unknown" : state.productVersion;
const std::string ecid = state.ecid != 0 ? ("ECID: " + std::to_string(state.ecid)) : "ECID: Unknown";
switch (state.mode) {
case DeviceMode::Normal:
if (state.isSupported) {
return "Connected in normal mode * iOS " + version + "\n" + ecid;
}
return "Not supported * iOS " + version + "\n" + ecid;
case DeviceMode::Recovery:
if (state.isSupported) {
return "Connected in recovery mode\n" + ecid;
}
return "Not supported\n" + ecid;
case DeviceMode::DFU:
return "Sorry, jailbreaking in DFU mode is not supported.";
case DeviceMode::None:
return "Please connect a device to get started. Ensure version range is 15.0+";
}
return "";
}
TuiMainPanel::TuiMainPanel(TuiFrame* frame)
: TuiPanel(frame) {}
const char** TuiMainPanel::get_buttons() const {
return buttons;
}
void TuiMainPanel::draw(int sy, int sx, int selected) {
const DeviceState state = GetFrame()->GetDeviceState();
const int content_x = sx + kMainContentX;
int y = sy + 2;
const int bottom_y = sy + 22;
const int content_bottom = bottom_y - 1;
(void)selected;
const std::string title = get_device_title(state);
const std::string subtitle = get_device_subtitle(state);
attron(A_BOLD);
mvprintw(y, content_x, "%.*s", kMainContentWidth, title.c_str());
attroff(A_BOLD);
++y;
if (has_colors()) {
attron(COLOR_PAIR(1));
}
y += tui_text::draw_wrapped_text(y, content_x, kMainContentWidth, subtitle, 2);
if (has_colors()) {
attroff(COLOR_PAIR(1));
}
++y;
if (y <= content_bottom) {
mvhline(y, sx + kMainSeparatorX, ACS_HLINE, kMainSeparatorWidth);
y += 2;
}
const std::string made_by =
"Made by: asdfugil, claration, kok3shidoll, mineek, plooshi, staturnz";
const std::string thanks_to =
"Thanks to: itsnebulalol, llsc12, lrdsnow, dedbeddedbed, kirb, ehilwyma, "
"opa334, 0x7ff, alfiecg24, sneko, sbingner, nikias, "
"tihmstar, Checkra1n (Siguza, axi0mx, littlelailo et al.), Procursus "
"(Hayden Seay, Cameron Katri, Keto et al.)";
if (y <= content_bottom) {
y += tui_text::draw_wrapped_text(y, content_x, kMainContentWidth, made_by, 2);
}
if (y <= content_bottom) {
++y;
}
if (y <= content_bottom) {
y += tui_text::draw_wrapped_text(y, content_x, kMainContentWidth, thanks_to, 4);
}
if (y <= content_bottom) {
++y;
}
if (y <= content_bottom) {
mvhline(y, sx + kMainSeparatorX, ACS_HLINE, kMainSeparatorWidth);
y += 1;
}
if (y <= content_bottom) {
mvprintw(y, content_x, "Twitter: @palera1n");
++y;
}
if (y <= content_bottom) {
mvprintw(y, content_x, "Website: https://palera.in");
++y;
}
if (y <= content_bottom) {
attron(A_BOLD);
y += tui_text::draw_wrapped_text(
y,
content_x,
kMainContentWidth,
"NOTE: Please ensure you've made a backup of your device before proceeding.",
2
);
attroff(A_BOLD);
}
if (y <= content_bottom) {
mvhline(y, sx + kMainSeparatorX, ACS_HLINE, kMainSeparatorWidth);
y += 2;
}
const bool quick_mode_enabled = (palerain_flags & palerain_option_quick) != 0;
const std::string quick_mode_prefix = std::string("[") + (quick_mode_enabled ? "x" : " ") + "] ";
const std::string quick_mode_label = "Enable Quick Mode";
const int quick_mode_total_width = static_cast<int>(quick_mode_prefix.size() + quick_mode_label.size());
const int quick_mode_x = content_x + std::max(0, kMainContentWidth - quick_mode_total_width);
attron(A_DIM);
mvprintw(bottom_y, content_x, "%.*s", kMainContentWidth, "Made with <3 by C (claration)");
attroff(A_DIM);
mvprintw(bottom_y, quick_mode_x, "%s", quick_mode_prefix.c_str());
if (selected == 0) {
attron(A_REVERSE);
}
mvprintw(bottom_y, quick_mode_x + static_cast<int>(quick_mode_prefix.size()), "%s", quick_mode_label.c_str());
if (selected == 0) {
attroff(A_REVERSE);
}
}
void TuiMainPanel::handle_enter(int selected, int sy, int sx) {
switch(selected) {
case 0:
palerain_flags ^= palerain_option_quick;
break;
case 1:
GetFrame()->ShowSettings(5);
break;
case 2: {
DeviceState state = GetFrame()->GetDeviceState();
bool can_start = (state.connected && state.isSupported && state.mode != DeviceMode::DFU);
if (can_start) {
if (state.mode == DeviceMode::Recovery) {
GetFrame()->ShowDfu(1);
} else {
GetFrame()->ShowRecovery(1);
}
}
break;
}
case 3:
GetFrame()->Stop();
break;
}
}
void TuiMainPanel::handle_device_update(const DeviceState& state) {}
bool TuiMainPanel::is_button_enabled(int btn_idx) const {
DeviceState state = GetFrame()->GetDeviceState();
switch(btn_idx) {
case 0: return true;
case 1: return true;
case 2: return (state.connected && state.isSupported && state.mode != DeviceMode::DFU);
case 3: return true;
}
return false;
}
#endif // WITH_TUI
+25
View File
@@ -0,0 +1,25 @@
#ifdef WITH_TUI
#ifndef TUI_MAIN_PANEL_HPP
#define TUI_MAIN_PANEL_HPP
#include "TuiPanel.hpp"
class TuiMainPanel : public TuiPanel {
public:
explicit TuiMainPanel(TuiFrame* frame);
~TuiMainPanel() override = default;
void draw(int sy, int sx, int selected) override;
void handle_enter(int selected, int sy, int sx) override;
void handle_device_update(const DeviceState& state) override;
bool is_button_enabled(int btn_idx) const override;
int get_total_items() const override { return 4; }
int get_button_count() const override { return 3; }
const char** get_buttons() const override;
};
#endif // TUI_MAIN_PANEL_HPP
#endif // WITH_TUI
+49
View File
@@ -0,0 +1,49 @@
#ifdef WITH_TUI
#ifndef TUI_PANEL_HPP
#define TUI_PANEL_HPP
#include <ncurses.h>
#include "../event.hpp"
class TuiFrame;
enum PanelState {
PANEL_MAIN = 0,
PANEL_OPTIONS,
PANEL_START_1,
PANEL_START_2,
PANEL_FINAL,
PANEL_COUNT
};
class TuiPanel {
public:
explicit TuiPanel(TuiFrame* frame)
: m_frame(frame) {}
virtual ~TuiPanel() = default;
virtual void on_show() {}
virtual void update() {}
virtual void draw(int sy, int sx, int selected) = 0;
virtual void handle_enter(int selected, int sy, int sx) = 0;
virtual void handle_device_update(const DeviceState& state) = 0;
virtual bool is_button_enabled(int btn_idx) const { return true; }
virtual int get_total_items() const = 0;
virtual int get_button_count() const = 0;
virtual const char** get_buttons() const = 0;
protected:
TuiFrame* GetFrame() const {
return m_frame;
}
private:
TuiFrame* m_frame = nullptr;
};
#endif // TUI_PANEL_HPP
#endif // WITH_TUI
+139
View File
@@ -0,0 +1,139 @@
#ifdef WITH_TUI
#include "TuiRecoveryPanel.hpp"
#include <ncurses.h>
#include <string>
#include <thread>
#include <chrono>
#include "Tui.hpp"
#include "TuiText.hpp"
#include "../globals.h"
#include "../paleinfo.h"
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
static const char *buttons[] = { "Back", "Next" };
static constexpr int kRecoveryContentX = 2;
static constexpr int kRecoveryContentWidth = 60;
TuiRecoveryPanel::TuiRecoveryPanel(TuiFrame* frame)
: TuiPanel(frame), m_status_text(""), m_is_entering_recovery(false), m_buttons_disabled(false) {}
void TuiRecoveryPanel::SetStatusText(const std::string& text) {
m_status_text = text;
m_preserve_status_on_show = true;
}
const char** TuiRecoveryPanel::get_buttons() const {
return buttons;
}
bool TuiRecoveryPanel::is_button_enabled(int btn_idx) const {
return !m_buttons_disabled;
}
void TuiRecoveryPanel::on_show() {
if (!m_preserve_status_on_show) {
m_status_text = "";
}
m_preserve_status_on_show = false;
m_is_entering_recovery = false;
m_buttons_disabled = false;
GetFrame()->SetSelected(1);
if (palerain_flags & palerain_option_quick) {
m_status_text = "Entering recovery mode...";
m_buttons_disabled = true;
enter_recovery_mode();
}
}
void TuiRecoveryPanel::draw(int sy, int sx, int selected) {
const int content_x = sx + kRecoveryContentX;
const std::string intro = "The device needs to be put into DFU mode to apply the jailbreak. This is a manual process and we will guide you through it.";
tui_text::draw_wrapped_text(sy + 2, content_x, kRecoveryContentWidth, intro, 6);
if (!m_status_text.empty()) {
tui_text::draw_wrapped_text(sy + 9, content_x, kRecoveryContentWidth, m_status_text, 3);
}
}
void TuiRecoveryPanel::handle_enter(int selected, int sy, int sx) {
if (m_buttons_disabled) return;
switch(selected) {
case 0:
GetFrame()->ShowMain(1);
break;
case 1:
m_status_text = "Entering recovery mode...";
m_buttons_disabled = true;
enter_recovery_mode();
break;
}
}
void TuiRecoveryPanel::handle_device_update(const DeviceState& state) {
if (m_is_entering_recovery && state.mode == DeviceMode::Recovery) {
m_status_text = "Device is now in recovery mode.";
std::this_thread::sleep_for(std::chrono::seconds(3));
m_status_text = "";
m_is_entering_recovery = false;
m_buttons_disabled = false;
GetFrame()->ShowDfu(1);
return;
}
if (m_is_entering_recovery && state.connected && state.mode == DeviceMode::Normal) {
m_is_entering_recovery = false;
m_buttons_disabled = false;
m_status_text = "Hmm... It seems like the device didn't enter recovery mode. Please try again.";
return;
}
if (!m_is_entering_recovery && !state.connected) {
m_buttons_disabled = false;
GetFrame()->ShowMain(1);
return;
}
}
void TuiRecoveryPanel::enter_recovery_mode() {
m_is_entering_recovery = true;
m_buttons_disabled = true;
std::thread([this]() {
DeviceState state = GetFrame()->GetDeviceState();
if (!state.connected) {
m_is_entering_recovery = false;
m_buttons_disabled = false;
return;
}
idevice_t device = nullptr;
lockdownd_client_t client = nullptr;
if (idevice_new(&device, state.udid.c_str()) != IDEVICE_E_SUCCESS) {
m_is_entering_recovery = false;
m_buttons_disabled = false;
return;
}
if (lockdownd_client_new_with_handshake(device, &client, "palera1n") != LOCKDOWN_E_SUCCESS) {
idevice_free(device);
m_is_entering_recovery = false;
m_buttons_disabled = false;
return;
}
lockdownd_enter_recovery(client);
lockdownd_client_free(client);
idevice_free(device);
}).detach();
}
#endif // WITH_TUI
+39
View File
@@ -0,0 +1,39 @@
#ifdef WITH_TUI
#ifndef TUI_RECOVERY_PANEL_HPP
#define TUI_RECOVERY_PANEL_HPP
#include "TuiPanel.hpp"
#include <string>
#include <atomic>
class TuiRecoveryPanel : public TuiPanel {
public:
explicit TuiRecoveryPanel(TuiFrame* frame);
~TuiRecoveryPanel() override = default;
void SetStatusText(const std::string& text);
void on_show() override;
void draw(int sy, int sx, int selected) override;
void handle_enter(int selected, int sy, int sx) override;
void handle_device_update(const DeviceState& state) override;
bool is_button_enabled(int btn_idx) const override;
int get_total_items() const override { return 2; }
int get_button_count() const override { return 2; }
const char** get_buttons() const override;
private:
void enter_recovery_mode();
std::string m_status_text;
bool m_preserve_status_on_show = false;
std::atomic<bool> m_is_entering_recovery;
bool m_buttons_disabled;
};
#endif // TUI_RECOVERY_PANEL_HPP
#endif // WITH_TUI
+190
View File
@@ -0,0 +1,190 @@
#ifdef WITH_TUI
#include "TuiSettingsPanel.hpp"
#include <ncurses.h>
#include <cstdio>
#include <cstring>
#include "Tui.hpp"
#include "TuiText.hpp"
#include "../globals.h"
#include "../paleinfo.h"
static const char *buttons[] = { "Back" };
static constexpr int kSettingsContentX = 2;
static constexpr int kSettingsContentWidth = 60;
static constexpr int kSettingsBootArgsWidth = 56;
TuiSettingsPanel::TuiSettingsPanel(TuiFrame* frame)
: TuiPanel(frame) {}
const char** TuiSettingsPanel::get_buttons() const {
return buttons;
}
void TuiSettingsPanel::draw(int sy, int sx, int selected) {
const int content_x = sx + kSettingsContentX;
const bool is_rootful = (palerain_flags & palerain_option_rootful) != 0;
const int setup_selection = (palerain_flags & palerain_option_setup_rootful) ? 1
: (palerain_flags & palerain_option_setup_partial_root) ? 2 : 0;
tui_text::draw_wrapped_text(
sy + 2,
content_x,
kSettingsContentWidth,
"You may set the following options. If you don't know what they mean you'll probably have no reason to set them.",
2
);
int states[] = {
!!(palerain_flags & palerain_option_safemode),
!!(palerain_flags & palerain_option_verbose_boot),
!!(palerain_flags & palerain_option_force_revert),
!!(palerain_flags & palerain_option_flower_chain)
};
const char *labels[] = {
"Safe Mode",
"Verbose Boot",
"Restore System",
"Dark Blockchain"
};
for (int i = 0; i < 2; i++) {
mvprintw(sy + 6 + i, content_x, "[%c] ", states[i] ? 'x' : ' ');
if (selected == i) attron(A_REVERSE);
printw("%s", labels[i]);
if (selected == i) attroff(A_REVERSE);
}
mvprintw(sy + 8, sx + 6, "Boot Arguments:");
if (selected == 2) attron(A_REVERSE);
mvprintw(sy + 9, sx + 6, "%-56s", boot_args);
if (selected == 2) attroff(A_REVERSE);
for (int i = 2; i < 4; i++) {
mvprintw(sy + 8 + i, content_x, "[%c] ", states[i] ? 'x' : ' ');
if (selected == i + 1) attron(A_REVERSE);
printw("%s", labels[i]);
if (selected == i + 1) attroff(A_REVERSE);
}
mvprintw(sy + 13, content_x, "Jailbreak Type:");
if (selected == 5) attron(A_REVERSE);
mvprintw(sy + 14, content_x + 2, "(%c) Rootless", is_rootful ? ' ' : 'x');
if (selected == 5) attroff(A_REVERSE);
if (selected == 6) attron(A_REVERSE);
mvprintw(sy + 15, content_x + 2, "(%c) Rootful", is_rootful ? 'x' : ' ');
if (selected == 6) attroff(A_REVERSE);
mvprintw(sy + 17, content_x, "Rootful Options:");
for (int i = 0; i < 3; ++i) {
const int item_idx = 7 + i;
const int row = sy + 18 + i;
const bool active = is_rootful;
const bool is_selected = selected == item_idx;
const bool checked = setup_selection == i;
if (!active) attron(A_DIM);
if (is_selected) attron(A_REVERSE);
const char* option_label = i == 0 ? "Boot" : (i == 1 ? "Create FakeFS" : "Create BindFS");
mvprintw(row, content_x + 2, "(%c) %s", checked ? 'x' : ' ', option_label);
if (is_selected) attroff(A_REVERSE);
if (!active) attroff(A_DIM);
}
}
void TuiSettingsPanel::edit_boot_args(int start_y, int start_x) {
curs_set(1);
timeout(-1);
int field_y = start_y + 9;
int field_x = start_x + 6;
int max_len = kSettingsBootArgsWidth;
char temp_buf[128];
std::snprintf(temp_buf, sizeof(temp_buf), "%s", boot_args);
int len = static_cast<int>(std::strlen(temp_buf));
while (1) {
mvprintw(field_y, field_x, "%-56s", "");
mvprintw(field_y, field_x, "%s", temp_buf);
move(field_y, field_x + len);
refresh();
int input_ch = getch();
if (input_ch == KEY_ENTER || input_ch == 10 || input_ch == 13) {
break;
} else if (input_ch == KEY_BACKSPACE || input_ch == 127 || input_ch == '\b') {
if (len > 0) temp_buf[--len] = '\0';
} else if (input_ch >= 32 && input_ch <= 126) {
if (len < max_len) {
temp_buf[len++] = static_cast<char>(input_ch);
temp_buf[len] = '\0';
}
}
}
std::snprintf(boot_args, sizeof(boot_args), "%s", temp_buf);
curs_set(0);
timeout(100);
}
void TuiSettingsPanel::handle_enter(int selected, int sy, int sx) {
switch(selected) {
case 0: palerain_flags ^= palerain_option_safemode; break;
case 1: palerain_flags ^= palerain_option_verbose_boot; break;
case 2: edit_boot_args(sy, sx); break;
case 3: palerain_flags ^= palerain_option_force_revert; break;
case 4: palerain_flags ^= palerain_option_flower_chain; break;
case 5:
palerain_flags &= ~(palerain_option_rootless | palerain_option_rootful);
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
palerain_flags |= palerain_option_rootless;
break;
case 6:
palerain_flags &= ~(palerain_option_rootless | palerain_option_rootful);
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
palerain_flags |= palerain_option_rootful;
break;
case 7:
if (palerain_flags & palerain_option_rootful) {
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
}
break;
case 8:
if (palerain_flags & palerain_option_rootful) {
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
palerain_flags |= palerain_option_setup_rootful;
}
break;
case 9:
if (palerain_flags & palerain_option_rootful) {
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
palerain_flags |= palerain_option_setup_partial_root;
}
break;
case 10: GetFrame()->ShowMain(0); break;
}
}
void TuiSettingsPanel::handle_device_update(const DeviceState& state) {
return;
}
bool TuiSettingsPanel::is_button_enabled(int btn_idx) const {
if (btn_idx >= 7 && btn_idx <= 9) {
return (palerain_flags & palerain_option_rootful) != 0;
}
return true;
}
#endif // WITH_TUI
+28
View File
@@ -0,0 +1,28 @@
#ifdef WITH_TUI
#ifndef TUI_SETTINGS_PANEL_HPP
#define TUI_SETTINGS_PANEL_HPP
#include "TuiPanel.hpp"
class TuiSettingsPanel : public TuiPanel {
public:
explicit TuiSettingsPanel(TuiFrame* frame);
~TuiSettingsPanel() override = default;
void draw(int sy, int sx, int selected) override;
void handle_enter(int selected, int sy, int sx) override;
void handle_device_update(const DeviceState& state) override;
bool is_button_enabled(int btn_idx) const override;
int get_total_items() const override { return 11; }
int get_button_count() const override { return 1; }
const char** get_buttons() const override;
private:
void edit_boot_args(int start_y, int start_x);
};
#endif // TUI_SETTINGS_PANEL_HPP
#endif // WITH_TUI
+83
View File
@@ -0,0 +1,83 @@
#ifdef WITH_TUI
#ifndef TUI_TEXT_HPP
#define TUI_TEXT_HPP
#include <ncurses.h>
#include <string>
#include <vector>
namespace tui_text {
inline void append_wrapped_lines(const std::string& text, int width, std::vector<std::string>& out) {
if (width <= 0) {
return;
}
size_t start = 0;
while (start <= text.size()) {
size_t end = text.find('\n', start);
if (end == std::string::npos) {
end = text.size();
}
std::string paragraph = text.substr(start, end - start);
size_t cursor = 0;
if (paragraph.empty()) {
out.push_back("");
}
while (cursor < paragraph.size()) {
size_t remaining = paragraph.size() - cursor;
if (remaining <= static_cast<size_t>(width)) {
out.push_back(paragraph.substr(cursor));
break;
}
size_t cut = cursor + static_cast<size_t>(width);
size_t split = paragraph.rfind(' ', cut);
if (split == std::string::npos || split <= cursor) {
split = cut;
}
out.push_back(paragraph.substr(cursor, split - cursor));
cursor = split;
while (cursor < paragraph.size() && paragraph[cursor] == ' ') {
++cursor;
}
}
if (end == text.size()) {
break;
}
start = end + 1;
}
}
inline int draw_wrapped_text(int y, int x, int width, const std::string& text, int max_lines = 0) {
if (width <= 0 || text.empty()) {
return 0;
}
std::vector<std::string> lines;
append_wrapped_lines(text, width, lines);
int rendered = 0;
for (const auto& line : lines) {
if (max_lines > 0 && rendered >= max_lines) {
break;
}
mvprintw(y + rendered, x, "%.*s", width, line.c_str());
++rendered;
}
return rendered;
}
} // namespace tui_text
#endif // TUI_TEXT_HPP
#endif // WITH_TUI
+3
View File
@@ -48,6 +48,9 @@ static const char* get_timestamp(void) {
}
void log_write(log_level_t level, const char *fmt, ...) {
if (palerain_flags & palerain_option_tui)
return;
va_list args;
va_start(args, fmt);
+8 -28
View File
@@ -6,17 +6,14 @@
#include "../utils.h"
#include "../event.hpp"
#include <thread>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libirecovery.h>
wxDEFINE_EVENT(EVT_DEVICE_STATE_UPDATE, wxCommandEvent);
void send_device_state(MainFrame* frame, const DeviceState& state)
{
if (!wxTheApp) return;
wxTheApp->CallAfter([frame, state]() {
if (!frame) return;
auto* evt = new wxCommandEvent(EVT_DEVICE_STATE_UPDATE);
evt->SetClientData(new DeviceState(state));
wxPostEvent(frame, *evt);
@@ -28,7 +25,7 @@ MainFrame::MainFrame()
wxID_ANY,
"palera1n - Version beta " + wxString(PALERAIN_VERSION),
wxDefaultPosition,
wxSize(480, 360),
wxSize(480, 348),
wxDEFAULT_FRAME_STYLE & ~(wxMAXIMIZE_BOX | wxRESIZE_BORDER))
{
auto* root = new wxBoxSizer(wxVERTICAL);
@@ -61,27 +58,10 @@ MainFrame::MainFrame()
ShowMain();
std::thread([this]()
{
idevice_event_subscribe(normal_device_event_cb, this);
while (true)
sleep_ms(1000);
}).detach();
std::thread([this]()
{
static irecv_device_event_context_t ctx = nullptr;
irecv_device_event_subscribe(
&ctx,
recovery_device_event_cb,
this
);
while (true)
sleep_ms(1000);
}).detach();
register_device_state_callback([this](const DeviceState& state) {
send_device_state(this, state);
});
ensure_device_event_system_started();
}
void MainFrame::ShowMain()
+1 -1
View File
@@ -4,7 +4,7 @@
#ifdef WITH_GUI
#include <wx/wx.h>
#include "../state.hpp"
#include "../event.hpp"
#include "MainPanel.hpp"
#include "SettingsPanel.hpp"
+1 -1
View File
@@ -2,7 +2,7 @@
#define DEVICEPANEL_H
#include <wx/wx.h>
#include "../state.hpp"
#include "../event.hpp"
class MainFrame;
class DevicePanel : public wxPanel
+48 -22
View File
@@ -5,7 +5,7 @@
#include "AppFrame.hpp"
#include "DevicePanel.hpp"
#include "../state.hpp"
#include "../event.hpp"
#include "../sequence.hpp"
#include "../utils.h"
#include "../globals.h"
@@ -17,6 +17,7 @@
#include <wx/mstream.h>
#include <wx/image.h>
#include <wx/bitmap.h>
#include <wx/statline.h>
#include "../gen/images/atv_std_brd.h"
#include "../gen/images/ipad.h"
@@ -28,6 +29,7 @@
#include "../gen/images/iphonex.h"
#include "../gen/images/ipodtouch.h"
#include "../gen/images/siriremote.h"
#include "../gen/images/logo.h"
#include <libirecovery.h>
@@ -35,12 +37,23 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
: DevicePanel(frame, parent)
{
Bind(wxEVT_SHOW, &DfuPanel::OnShow, this);
auto* root = new wxBoxSizer(wxVERTICAL);
m_headerText = new wxStaticText(this, wxID_ANY, "Time to put the device into DFU mode. Locate the buttons as marked below\non your device and check the instructions on the right.");
root->Add(m_headerText, 0, wxALL, 10);
auto* root = new wxBoxSizer(wxHORIZONTAL);
auto* left = new wxBoxSizer(wxVERTICAL);
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
wxImage img(stream, wxBITMAP_TYPE_PNG);
wxBitmap bmp(img);
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
auto* logoText = new wxStaticText(this, wxID_ANY, "DFU");
auto* right = new wxBoxSizer(wxVERTICAL);
auto* contentRow = new wxBoxSizer(wxHORIZONTAL);
m_headerText = new wxStaticText(this, wxID_ANY,
"Time to put the device into DFU mode.",
wxDefaultPosition,
wxDefaultSize,
wxST_WRAP
);
left->Add(m_headerText, 0, wxTOP | wxLEFT | wxRIGHT, 10);
m_devicePanel = new wxPanel(this);
m_devicePanel->SetMinSize(wxSize(310, 260));
@@ -51,11 +64,18 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
wxBitmap()
);
contentRow->Add(m_devicePanel, 1, wxTOP, 10);
m_stepsSizer = new wxBoxSizer(wxVERTICAL);
contentRow->Add(m_stepsSizer, 2, wxEXPAND | wxALL, 0);
auto* deviceSizer = new wxBoxSizer(wxVERTICAL);
deviceSizer->AddStretchSpacer(1);
deviceSizer->Add(m_deviceImage, 0, wxALIGN_CENTER);
deviceSizer->AddStretchSpacer(1);
m_devicePanel->SetSizer(deviceSizer);
root->Add(contentRow, 1, wxEXPAND);
left->Add(m_devicePanel, 1, wxEXPAND | wxTOP, 0);
left->AddStretchSpacer(1);
m_stepsSizer = new wxBoxSizer(wxVERTICAL);
left->Add(m_stepsSizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
m_backButton = new wxButton(this, wxID_ANY, "Back");
m_startButton = new wxButton(this, wxID_ANY, "Start");
@@ -82,18 +102,21 @@ DfuPanel::DfuPanel(MainFrame* frame, wxWindow* parent)
m_stagnentTimer.Bind(wxEVT_TIMER, [this](wxTimerEvent&)
{
m_headerText->SetLabel("Time to put the device into DFU mode. Locate the buttons as marked below\non your device and check the instructions on the right.");
m_headerText->SetLabel("Time to put the device into DFU mode.");
m_backButton->Enable();
m_startButton->Enable();
GetMainFrame()->ShowExploit();
});
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
bottomRow->AddStretchSpacer();
bottomRow->Add(m_backButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
bottomRow->Add(m_startButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
right->AddStretchSpacer();
right->Add(m_backButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
right->Add(m_startButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
root->Add(bottomRow, 0, wxEXPAND);
root->Add(left, 1, wxEXPAND);
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
root->Add(right, 0, wxEXPAND);
SetSizer(root);
}
@@ -143,6 +166,14 @@ void DfuPanel::SetDeviceState(const DeviceState& state)
m_startButton->Enable();
GetMainFrame()->ShowMain();
}
if (!state.connected)
{
m_isEnteringDfu = false;
m_backButton->Enable();
m_startButton->Enable();
GetMainFrame()->ShowMain();
}
}
void DfuPanel::OnShow(wxShowEvent& event)
@@ -254,7 +285,6 @@ void DfuPanel::LoadDevice(const std::string& productType)
);
m_deviceImage->SetBitmap(wxBitmap(img));
m_deviceImage->SetPosition(wxPoint(m_sequence.imageOffsetX, 0));
}
}
@@ -290,7 +320,7 @@ void DfuPanel::LoadDevice(const std::string& productType)
lbl->SetForegroundColour(i == 0 ? kActive : kInactive);
m_stepsSizer->Add(lbl, 0, wxBOTTOM, 5);
m_stepsSizer->Add(lbl, 0, wxTOP, 5);
m_stepLabels.push_back(lbl);
}
@@ -452,14 +482,10 @@ void DfuPanel::Reboot()
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
if (!client)
{
return;
}
if (!client) return;
irecv_setenv(client, "auto-boot", "true");
irecv_saveenv(client);
irecv_reboot(client);
irecv_close(client);
}).detach();
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <wx/wx.h>
#include "DevicePanel.hpp"
#include "../state.hpp"
#include "../event.hpp"
#include "../sequence.hpp"
class DfuPanel;
+27 -40
View File
@@ -3,6 +3,10 @@
#include "ExploitPanel.hpp"
#include <wx/wx.h>
#include <wx/mstream.h>
#include <wx/image.h>
#include <wx/bitmap.h>
#include <wx/statline.h>
#include <thread>
#ifdef __cplusplus
@@ -17,42 +21,19 @@ using std::atomic_bool;
#include "AppFrame.hpp"
#include "../m8/run.h"
#include "../utils.h"
static const char* stage_to_string(int stage)
{
switch (stage) {
case STAGE_SETUP: return "Checking if device is ready";
case STAGE_SPRAY: return "Setting up the exploit (this is the heap spray)";
case STAGE_PATCH: return "Right before trigger (this is the real bug setup)";
case STAGE_YOLODFU: return "Booting PongoOS...";
case STAGE_PONGO: return "Booting...";
case STAGE_DONE: return "Done.";
default: return "Idle.";
}
}
static int stage_to_progress(int stage)
{
switch (stage) {
case STAGE_SETUP: return 30;
case STAGE_SPRAY: return 40;
case STAGE_PATCH: return 50;
case STAGE_YOLODFU: return 60;
case STAGE_PONGO: return 70;
case STAGE_DONE: return 100;
default: return 0;
}
}
#include "../gen/images/logo.h"
ExploitPanel::ExploitPanel(MainFrame* frame, wxWindow* parent)
: DevicePanel(frame, parent)
{
auto* root = new wxBoxSizer(wxVERTICAL);
auto* descriptionText = new wxStaticText(this, wxID_ANY,
"Installing jailbreak, this will take a moment. If the device asks for a passcode\n"
"please enter it. Do not disconnect the device until finished."
);
auto* root = new wxBoxSizer(wxHORIZONTAL);
auto* left = new wxBoxSizer(wxVERTICAL);
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
wxImage img(stream, wxBITMAP_TYPE_PNG);
wxBitmap bmp(img);
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
auto* logoText = new wxStaticText(this, wxID_ANY, "Jailbreak");
auto* right = new wxBoxSizer(wxVERTICAL);
m_statusText = new wxStaticText(this, wxID_ANY, stage_to_string(STAGE_RESET));
@@ -68,16 +49,22 @@ ExploitPanel::ExploitPanel(MainFrame* frame, wxWindow* parent)
frame->ShowMain();
});
root->Add(descriptionText, 0, wxALL, 10);
root->Add(m_statusText, 0, wxALL, 10);
root->Add(m_progressBar, 0, wxALL | wxEXPAND, 20);
left->Add(new wxStaticText(this, wxID_ANY, "Installing jailbreak, this will take a moment. If the device asks for a passcode please enter it. Do not disconnect the device until finished.",
wxDefaultPosition,
wxDefaultSize,
wxST_WRAP
), 0, wxALL, 10);
left->Add(m_statusText, 0, wxALL, 10);
left->Add(m_progressBar, 0, wxALL | wxEXPAND, 20);
root->AddStretchSpacer();
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
bottomRow->AddStretchSpacer();
bottomRow->Add(m_backButton, 0, wxRIGHT | wxBOTTOM, 12);
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
right->AddStretchSpacer();
right->Add(m_backButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
root->Add(bottomRow, 0, wxEXPAND);
root->Add(left, 1, wxEXPAND);
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
root->Add(right, 0, wxEXPAND);
SetSizer(root);
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <wx/wx.h>
#include "DevicePanel.hpp"
#include "../state.hpp"
#include "../event.hpp"
class ExploitPanel;
class ExploitPanel : public DevicePanel
+129 -94
View File
@@ -12,20 +12,22 @@
#include "AppFrame.hpp"
#include "DevicePanel.hpp"
#include "../state.hpp"
#include "../event.hpp"
#include "../globals.h"
#include "../utils.h"
#include "../paleinfo.h"
#include "../gen/images/logo.h"
const char* deviceTextString = "Connect your iPhone, iPod touch, iPad, or AppleTV to begin.";
const char* deviceTextString = "No device connected";
const char* deviceTextString2 = "Please connect a device to get started.\nEnsure version range is 15.0+";
MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
: DevicePanel(frame, parent)
{
auto* root = new wxBoxSizer(wxVERTICAL);
wxInitAllImageHandlers();
auto* root = new wxBoxSizer(wxHORIZONTAL);
auto* left = new wxBoxSizer(wxVERTICAL);
auto* right = new wxBoxSizer(wxVERTICAL);
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
wxImage img(stream, wxBITMAP_TYPE_PNG);
@@ -33,101 +35,70 @@ MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
auto* titletext = new wxStaticText(this, wxID_ANY, "Welcome to palera1n!");
wxFont titlefont = titletext->GetFont();
titlefont.SetWeight(wxFONTWEIGHT_BOLD);
titletext->SetFont(titlefont);
m_deviceTitle = new wxStaticText(this, wxID_ANY, deviceTextString);
m_deviceSubtitle = new wxStaticText(this, wxID_ANY, deviceTextString2);
m_deviceText = new wxStaticText(
wxFont titleFont = m_deviceTitle->GetFont();
titleFont.SetPointSize(titleFont.GetPointSize() + 6);
titleFont.SetWeight(wxFONTWEIGHT_SEMIBOLD);
m_deviceTitle->SetFont(titleFont);
m_deviceSubtitle->SetForegroundColour(wxColour(160, 160, 160));
auto* credtext = new wxStaticText(
this,
wxID_ANY,
deviceTextString
"Made by: asdfugil, claration, kok3shidoll, mineek, plooshi, staturnz\n\n"
"Thanks to: itsnebulalol, llsc12, lrdsnow, dedbeddedbed, kirb, ehilwyma, "
"opa334, 0x7ff, alfiecg24, sneko, sbingner, nikias, tihmstar, "
"Checkra1n (Siguza, axi0mx, littlelailo et al.), Procursus (Hayden Seay, "
"Cameron Katri, Keto et al.)",
wxDefaultPosition,
wxDefaultSize,
wxST_WRAP
);
auto* left = new wxBoxSizer(wxVERTICAL);
left->Add(titletext, 0, wxTOP | wxLEFT, 10);
left->Add(m_deviceText, 0, wxTOP | wxLEFT, 10);
auto* twitterLabel = new wxStaticText(this, wxID_ANY, "Twitter:");
auto* twitter = new wxStaticText(this, wxID_ANY, "@palera1n");
auto* row = new wxBoxSizer(wxHORIZONTAL);
row->Add(left, 0, wxALIGN_TOP);
row->AddStretchSpacer();
row->Add(logo, 0, wxTOP | wxRIGHT, 10);
auto* websiteLabel = new wxStaticText(this, wxID_ANY, "Website:");
auto* website = new wxStaticText(this, wxID_ANY, "https://palera.in");
root->Add(row, 0, wxEXPAND);
twitter->SetForegroundColour(wxColour(0, 120, 215));
website->SetForegroundColour(wxColour(0, 120, 215));
root->Add(new wxStaticLine(this), 0, wxEXPAND | wxALL, 10);
twitter->SetCursor(wxCursor(wxCURSOR_HAND));
website->SetCursor(wxCursor(wxCURSOR_HAND));
auto* credtext = new wxStaticText(this, wxID_ANY,
"Made by: asdfugil, kok3shidoll, C, mineek, nekohaxx, plooshi, staturnz\n\n"
"Thanks to: llsc12, itsnebulalol, lrdsnow, kirb, ehilwyma opa334, 0x7ff,\n"
"sbingner, nikias (libimobiledevice), Checkra1n (Siguza, axi0mx, littlelailo\n"
"et al.), Procursus (Hayden Seay, Cameron Katri, Keto et al.)"
);
auto* credrow = new wxBoxSizer(wxHORIZONTAL);
auto* lovetext = new wxStaticText(this, wxID_ANY,
#ifdef __APPLE__
"With 💖 from C (claration)"
#else
"With <3 from C (claration)"
#endif
);
auto* link1 = new wxStaticText(this, wxID_ANY, "@palera1n");
auto* link2 = new wxStaticText(this, wxID_ANY, "https://palera.in");
link1->SetForegroundColour(wxColour(0, 120, 215));
link2->SetForegroundColour(wxColour(0, 120, 215));
link1->SetCursor(wxCursor(wxCURSOR_HAND));
link2->SetCursor(wxCursor(wxCURSOR_HAND));
link1->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&) {
twitter->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&)
{
wxLaunchDefaultBrowser("https://twitter.com/intent/follow?screen_name=palera1n");
});
link2->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&) {
website->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent&)
{
wxLaunchDefaultBrowser("https://palera.in");
});
credrow->Add(lovetext, 0, wxALIGN_BOTTOM | wxLEFT, 0);
credrow->AddStretchSpacer();
auto* twitterRow = new wxBoxSizer(wxHORIZONTAL);
twitterRow->Add(twitterLabel, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
twitterRow->Add(twitter, 0, wxALIGN_CENTER_VERTICAL);
auto* linkSizer = new wxBoxSizer(wxVERTICAL);
linkSizer->Add(link1, 0, wxALIGN_RIGHT);
linkSizer->Add(link2, 0, wxALIGN_RIGHT);
auto* websiteRow = new wxBoxSizer(wxHORIZONTAL);
websiteRow->Add(websiteLabel, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
websiteRow->Add(website, 0, wxALIGN_CENTER_VERTICAL);
credrow->Add(linkSizer, 0, wxALIGN_CENTER_VERTICAL, 0);
root->Add(credtext, 0, wxLEFT | wxRIGHT, 10);
root->Add(credrow, 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
auto* logoText = new wxStaticText(this, wxID_ANY, "palera1n");
root->Add(new wxStaticLine(this), 0, wxEXPAND | wxALL, 10);
auto* notetext = new wxStaticText(this, wxID_ANY,
"NOTE: Please ensure you have a backup of your device before applying the\n"
"jailbreak. While data loss is unlikely, we won't be responsible if something goes\n"
"wrong. Use at your own risk.");
wxFont notefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
#ifdef __APPLE__
notefont.SetPointSize(12);
#else
notefont.SetPointSize(9);
#endif
notefont.SetStyle(wxFONTSTYLE_ITALIC);
notetext->SetFont(notefont);
root->Add(notetext, 0, wxLEFT | wxRIGHT, 10);
root->AddStretchSpacer();
auto* quickMode = new wxCheckBox(this, wxID_ANY, "Quick Mode");
auto* quickMode = new wxCheckBox(this, wxID_ANY, "Enable Quick Mode");
auto* donateButton = new wxButton(this, wxID_ANY, "Donate");
auto* optionsButton = new wxButton(this, wxID_ANY, "Options");
m_startButton = new wxButton(this, wxID_ANY, "Start");
m_startButton->Disable();
quickMode->SetValue(palerain_flags & palerain_option_quick);
quickMode->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
{
if (e.IsChecked())
@@ -144,22 +115,66 @@ MainPanel::MainPanel(MainFrame* frame, wxWindow* parent)
m_startButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
{
if (frame->GetDeviceState().mode == DeviceMode::Normal)
{
frame->ShowRecovery();
}
else if (frame->GetDeviceState().mode == DeviceMode::Recovery)
{
frame->ShowDfu();
}
});
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
bottomRow->AddStretchSpacer();
bottomRow->Add(quickMode, 0, wxRIGHT | wxTOP, 1);
bottomRow->Add(optionsButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
bottomRow->Add(m_startButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
left->Add(m_deviceTitle, 0, wxLEFT | wxRIGHT | wxTOP, 10);
left->Add(m_deviceSubtitle, 0, wxLEFT | wxRIGHT | wxBOTTOM, 10);
left->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
left->Add(credtext, 0, wxLEFT | wxRIGHT | wxBOTTOM, 10);
root->Add(bottomRow, 0, wxEXPAND);
auto* linksCol = new wxBoxSizer(wxVERTICAL);
linksCol->Add(twitterRow, 0);
linksCol->Add(websiteRow, 0);
auto* warningNote = new wxStaticText(this, wxID_ANY,
"NOTE: Please ensure you've made a backup of your device before proceeding.\n",
wxDefaultPosition,
wxDefaultSize,
wxST_WRAP
);
wxFont noteFont = warningNote->GetFont();
noteFont.SetStyle(wxFONTSTYLE_ITALIC);
warningNote->SetFont(noteFont);
warningNote->SetForegroundColour(wxColour(200, 80, 80));
auto* linksRow = new wxBoxSizer(wxHORIZONTAL);
linksRow->Add(linksCol, 0, wxLEFT | wxRIGHT | wxALIGN_TOP, 10);
linksRow->AddStretchSpacer();
linksRow->Add(warningNote, 0, wxRIGHT | wxALIGN_TOP, 10);
left->Add(linksRow, 0, wxEXPAND | wxBOTTOM, 10);
left->AddStretchSpacer();
left->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
#if __APPLE__
auto* madeBy = new wxStaticText(this, wxID_ANY, "Made with 💖 by C (claration)");
#else
auto* madeBy = new wxStaticText(this, wxID_ANY, "Made with <3 by C (claration)");
#endif
auto* quickModeRow = new wxBoxSizer(wxHORIZONTAL);
quickModeRow->Add(madeBy, 0, wxALIGN_CENTER_VERTICAL);
quickModeRow->AddStretchSpacer();
quickModeRow->Add(quickMode, 0, wxALIGN_CENTER_VERTICAL);
left->Add(quickModeRow, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
right->AddStretchSpacer();
right->Add(donateButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
right->Add(optionsButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
right->Add(m_startButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
root->Add(left, 1, wxEXPAND);
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
root->Add(right, 0, wxEXPAND);
SetSizer(root);
}
@@ -168,11 +183,25 @@ void MainPanel::SetDeviceState(const DeviceState& state)
{
DevicePanel::SetDeviceState(state);
if (m_deviceText)
if (m_deviceTitle && m_deviceSubtitle)
{
if (!state.connected)
{
m_deviceText->SetLabel(deviceTextString);
if (state.connectedDeviceCount > 1)
{
m_deviceTitle->SetLabel("Multiple devices detected");
m_deviceSubtitle->SetLabel(
wxString::Format(
"%u USB devices connected. Disconnect extras and keep only one device attached.",
state.connectedDeviceCount
)
);
}
else
{
m_deviceTitle->SetLabel(deviceTextString);
m_deviceSubtitle->SetLabel(deviceTextString2);
}
m_startButton->Enable(false);
}
else
@@ -187,28 +216,34 @@ void MainPanel::SetDeviceState(const DeviceState& state)
{
case DeviceMode::Normal:
if (state.isSupported) {
m_deviceText->SetLabel("Connected " + productString + " (iOS " + versionString + ") in normal mode.\n" + ecidString);
m_deviceTitle->SetLabel(productString);
m_deviceSubtitle->SetLabel("Connected in normal mode • iOS " + versionString + "\n" + ecidString);
m_startButton->Enable();
} else {
m_deviceText->SetLabel("Sorry, " + productString + " (iOS " + versionString + ") is not supported.\n" + ecidString);
m_deviceTitle->SetLabel(productString);
m_deviceSubtitle->SetLabel("Not supported • iOS " + versionString + "\n" + ecidString);
m_startButton->Enable(false);
}
break;
case DeviceMode::Recovery:
if (state.isSupported) {
m_deviceText->SetLabel("Connected " + productString + " in recovery mode.\n" + ecidString);
m_deviceTitle->SetLabel(productString);
m_deviceSubtitle->SetLabel("Connected in recovery mode\n" + ecidString);
m_startButton->Enable();
} else {
m_deviceText->SetLabel("Sorry, " + productString + " is not supported.\n" + ecidString);
m_deviceTitle->SetLabel(productString);
m_deviceSubtitle->SetLabel("Not supported\n" + ecidString);
m_startButton->Enable(false);
}
break;
case DeviceMode::DFU:
m_deviceText->SetLabel("Sorry, DFU mode is not supported.");
m_deviceTitle->SetLabel("DFU Mode device");
m_deviceSubtitle->SetLabel("Sorry, jailbreaking in DFU mode is not supported.\n");
m_startButton->Enable(false);
break;
case DeviceMode::None:
m_deviceText->SetLabel(deviceTextString);
m_deviceTitle->SetLabel(deviceTextString);
m_deviceSubtitle->SetLabel(deviceTextString2);
m_startButton->Enable(false);
break;
}
+3 -2
View File
@@ -5,7 +5,7 @@
#include <wx/wx.h>
#include "DevicePanel.hpp"
#include "../state.hpp"
#include "../event.hpp"
class MainPanel;
class MainPanel : public DevicePanel
@@ -14,7 +14,8 @@ public:
explicit MainPanel(MainFrame* frame, wxWindow* parent);
void SetDeviceState(const DeviceState& state) override;
private:
wxStaticText* m_deviceText = nullptr;
wxStaticText* m_deviceTitle = nullptr;
wxStaticText* m_deviceSubtitle = nullptr;
wxButton* m_startButton = nullptr;
};
+33 -19
View File
@@ -3,16 +3,21 @@
#include "RecoveryPanel.hpp"
#include <wx/wx.h>
#include <wx/mstream.h>
#include <wx/image.h>
#include <wx/bitmap.h>
#include <wx/statline.h>
#include <thread>
#include "AppFrame.hpp"
#include "DevicePanel.hpp"
#include "../state.hpp"
#include "../event.hpp"
#include "../utils.h"
#include "../globals.h"
#include "../paleinfo.h"
#include "../gen/images/logo.h"
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
@@ -22,20 +27,19 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
: DevicePanel(frame, parent)
{
Bind(wxEVT_SHOW, &RecoveryPanel::OnShow, this);
auto* root = new wxBoxSizer(wxVERTICAL);
root->Add(new wxStaticText(this, wxID_ANY,
"The device needs to be put into DFU mode to apply the jailbreak. This is a\n"
"manual process and we will guide you through it.\n"
"In order to prevent filesystem corruption through hard reset, the device will\n"
"be put into recovery mode first. Click next when you are ready."
), 0, wxALL, 10);
auto* root = new wxBoxSizer(wxHORIZONTAL);
auto* left = new wxBoxSizer(wxVERTICAL);
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
wxImage img(stream, wxBITMAP_TYPE_PNG);
wxBitmap bmp(img);
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
auto* logoText = new wxStaticText(this, wxID_ANY, "Recovery");
auto* right = new wxBoxSizer(wxVERTICAL);
m_statusText = new wxStaticText(this, wxID_ANY, "");
root->Add(m_statusText, 0, wxALL, 10);
m_backButton = new wxButton(this, wxID_ANY, "Back");
m_nextButton = new wxButton(this, wxID_ANY, "Next");
m_backButton->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
{
frame->ShowMain();
@@ -47,7 +51,6 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
m_nextButton->Disable();
EnterRecoveryMode();
});
m_timer.Bind(wxEVT_TIMER, [this](wxTimerEvent&)
{
m_backButton->Enable();
@@ -58,13 +61,24 @@ RecoveryPanel::RecoveryPanel(MainFrame* frame, wxWindow* parent)
m_statusText->SetLabel("");
});
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
bottomRow->AddStretchSpacer();
bottomRow->Add(m_backButton, 0, wxRIGHT | wxBOTTOM, 12);
bottomRow->Add(m_nextButton, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
left->Add(
new wxStaticText(this, wxID_ANY,
"The device needs to be put into DFU mode to apply the jailbreak. This is a manual process and we will guide you through it.",
wxDefaultPosition,
wxDefaultSize,
wxST_WRAP
), 0, wxALL, 10);
left->Add(m_statusText, 0, wxALL, 10);
root->AddStretchSpacer();
root->Add(bottomRow, 0, wxEXPAND);
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
right->AddStretchSpacer();
right->Add(m_backButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
right->Add(m_nextButton, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
root->Add(left, 1, wxEXPAND);
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
root->Add(right, 0, wxEXPAND);
SetSizer(root);
}
@@ -85,7 +99,7 @@ void RecoveryPanel::SetDeviceState(const DeviceState& state)
if (m_isEnteringRecovery && state.mode == DeviceMode::Normal)
{
m_isEnteringRecovery = false;
m_statusText->SetLabel("Hmm... It seems like the device didn't enter recovery mode. Please try again.");
m_statusText->SetLabel("Hmm... It seems like the device didn't enter recovery mode.");
m_backButton->Enable();
m_nextButton->Enable();
}
+111 -23
View File
@@ -3,32 +3,90 @@
#include "SettingsPanel.hpp"
#include <wx/wx.h>
#include <wx/mstream.h>
#include <wx/image.h>
#include <wx/bitmap.h>
#include <wx/statline.h>
#include "AppFrame.hpp"
#include "DevicePanel.hpp"
#include "../utils.h"
#include "../globals.h"
#include "../paleinfo.h"
#include "../gen/images/logo.h"
SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
: DevicePanel(frame, parent)
{
auto* root = new wxBoxSizer(wxVERTICAL);
auto* root = new wxBoxSizer(wxHORIZONTAL);
auto* left = new wxBoxSizer(wxVERTICAL);
wxMemoryInputStream stream(images_logo_png, images_logo_png_len);
wxImage img(stream, wxBITMAP_TYPE_PNG);
wxBitmap bmp(img);
auto* logo = new wxStaticBitmap(this, wxID_ANY, bmp);
auto* logoText = new wxStaticText(this, wxID_ANY, "Options");
auto* right = new wxBoxSizer(wxVERTICAL);
root->Add(new wxStaticText(this, wxID_ANY,
"You may set the following options. If you don't know what they mean you'll\n"
"probably have no reason to set them."
left->Add(new wxStaticText(this, wxID_ANY,
"You may set the following options. If you don't know what they mean you'll probably have no reason to set them.",
wxDefaultPosition,
wxDefaultSize,
wxST_WRAP
), 0, wxALL, 10);
auto* optionSafemode = new wxCheckBox(this, wxID_ANY, "Safe Mode");
auto* optionVerbose = new wxCheckBox(this, wxID_ANY, "Verbose Boot");
auto* optionRevert = new wxCheckBox(this, wxID_ANY, "Restore System");
auto* optionDarkBlockchain = new wxCheckBox(this, wxID_ANY, "Dark Blockchain");
auto* bootArgs = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
auto* rootType = new wxRadioBox(
this,
wxID_ANY,
"Jailbreak Type",
wxDefaultPosition,
wxDefaultSize,
2,
(const wxString[]){
"Rootless",
"Rootful"
},
1,
wxRA_SPECIFY_COLS
);
auto* setupMode = new wxRadioBox(
this,
wxID_ANY,
"Rootful Options",
wxDefaultPosition,
wxDefaultSize,
3,
(const wxString[]){
"Boot",
"Create FakeFS",
"Create BindFS"
},
1,
wxRA_SPECIFY_COLS
);
optionSafemode->SetValue(palerain_flags & palerain_option_safemode);
optionVerbose->SetValue(palerain_flags & palerain_option_verbose_boot);
optionRevert->SetValue(palerain_flags & palerain_option_force_revert);
optionDarkBlockchain->SetValue(palerain_flags & palerain_option_flower_chain);
bootArgs->SetValue(wxString(boot_args, wxConvUTF8));
if (palerain_flags & palerain_option_rootful)
rootType->SetSelection(1);
else
rootType->SetSelection(0);
if (palerain_flags & palerain_option_setup_rootful)
setupMode->SetSelection(1);
else if (palerain_flags & palerain_option_setup_partial_root)
setupMode->SetSelection(2);
else
setupMode->SetSelection(0);
setupMode->Enable(rootType->GetSelection() == 1);
optionSafemode->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
{
@@ -37,7 +95,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
else
palerain_flags &= ~palerain_option_safemode;
});
optionVerbose->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
{
if (e.IsChecked())
@@ -45,7 +102,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
else
palerain_flags &= ~palerain_option_verbose_boot;
});
optionRevert->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
{
if (e.IsChecked())
@@ -53,7 +109,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
else
palerain_flags &= ~palerain_option_force_revert;
});
optionDarkBlockchain->Bind(wxEVT_CHECKBOX, [&](wxCommandEvent& e)
{
if (e.IsChecked())
@@ -61,13 +116,6 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
else
palerain_flags &= ~palerain_option_flower_chain;
});
root->Add(optionSafemode, 0, wxLEFT | wxRIGHT, 10);
root->Add(optionVerbose, 0, wxLEFT | wxRIGHT | wxTOP, 10);
root->Add(new wxStaticText(this, wxID_ANY, "Boot Arguments:"), 0, wxALL, 10);
auto* bootArgs = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize);
bootArgs->SetValue(wxString(boot_args, wxConvUTF8));
bootArgs->Bind(wxEVT_TEXT, [&](wxCommandEvent& e)
{
wxString value = e.GetString();
@@ -77,25 +125,65 @@ SettingsPanel::SettingsPanel(MainFrame* frame, wxWindow* parent)
}
snprintf(boot_args, sizeof(boot_args), "%s", value.ToStdString().c_str());
});
rootType->Bind(wxEVT_RADIOBOX, [setupMode](wxCommandEvent& e)
{
palerain_flags &= ~(palerain_option_rootless | palerain_option_rootful);
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
root->Add(bootArgs, 0, wxLEFT | wxRIGHT | wxEXPAND, 30);
root->Add(optionRevert, 0, wxLEFT | wxRIGHT | wxTOP, 10);
root->Add(optionDarkBlockchain, 0, wxLEFT | wxRIGHT | wxTOP, 10);
setupMode->Enable(e.GetSelection() == 1);
setupMode->SetSelection(0);
root->AddStretchSpacer();
if (e.GetSelection() == 0)
palerain_flags |= palerain_option_rootless;
else
palerain_flags |= palerain_option_rootful;
});
setupMode->Bind(wxEVT_RADIOBOX, [&](wxCommandEvent& e)
{
palerain_flags &= ~(palerain_option_setup_rootful | palerain_option_setup_partial_root);
if (e.GetSelection() == 1)
palerain_flags |= palerain_option_setup_rootful;
else if (e.GetSelection() == 2)
palerain_flags |= palerain_option_setup_partial_root;
});
auto* contentRow = new wxBoxSizer(wxHORIZONTAL);
auto* leftColumn = new wxBoxSizer(wxVERTICAL);
auto* rightColumn = new wxBoxSizer(wxVERTICAL);
leftColumn->Add(rootType, 0, wxALL | wxEXPAND, 5);
leftColumn->Add(setupMode, 0, wxALL | wxEXPAND, 5);
rightColumn->Add(optionSafemode, 0, wxALL, 5);
rightColumn->Add(optionVerbose, 0, wxALL, 5);
rightColumn->Add(optionRevert, 0, wxALL, 5);
rightColumn->Add(optionDarkBlockchain, 0, wxALL, 5);
rightColumn->AddStretchSpacer();
contentRow->Add(leftColumn, 1, wxEXPAND | wxRIGHT, 10);
contentRow->Add(rightColumn, 1, wxEXPAND | wxLEFT, 10);
left->Add(contentRow, 1, wxEXPAND | wxLEFT | wxRIGHT, 10);
left->Add(new wxStaticText(this, wxID_ANY, "Boot Arguments:"), 0, wxALL, 15);
left->Add(bootArgs, 0, wxLEFT | wxRIGHT | wxEXPAND, 30);
left->AddStretchSpacer();
auto* back = new wxButton(this, wxID_ANY, "Back");
right->Add(logo, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxLEFT | wxRIGHT, 10);
right->Add(logoText, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, 5);
right->AddStretchSpacer();
right->Add(back, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
back->Bind(wxEVT_BUTTON, [frame](wxCommandEvent&)
{
frame->ShowMain();
});
auto* bottomRow = new wxBoxSizer(wxHORIZONTAL);
bottomRow->AddStretchSpacer();
bottomRow->Add(back, 0, wxRIGHT | wxLEFT | wxBOTTOM, 12);
root->Add(bottomRow, 0, wxEXPAND);
root->Add(left, 1, wxEXPAND);
root->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
root->Add(right, 0, wxEXPAND);
SetSizer(root);
}