Con RedSn0w es una operación sencilla y relativamente rápida, pero la versión actualmente disponible para iOS 5 es de tipo tethered. Esto quiere decir ni más ni menos que, cada vez que reiniciemos el dispositivo, habremos de tenerlo conectado al ordenador. De lo contrario, olvidémonos del correo y de Safari. iBooks es otra historia, como veremos. En cualquier caso, nos encontramos aquí el primer inconveniente.
Con iBooks sucede que, al menos en la versión b4 de RedSn0w, deja de funcionar. Quizá en versiones posteriores ya se incorpore algún fix. Sea como sea, yo me vi con iBooks fuera de combate. Segundo inconveniente.
Buscando una solución, leí acerca de un parche descargable desde uno de los repositorios accesibles mediante Cidia. Cuando intenté conectarme a él, recibí un mensaje acerca del repositorio en cuestión. Al parecer, publican material sujeto a derechos de autor sin la debida retribución al creador original. Son unos piratas, vaya. Fíjate por dónde, no me sorprendió.
¿Qué hice? Restauré mi iPad. Al fin y al cabo, no soy un fanático de la personalización; excepto por la historia de los nuevos gestos, las posibilidades que me ofrece el iPad "de serie" me convencen razonablemente; además, todo lo que he necesitado hasta ahora lo he encontrado en AppStore, sin que me importe pagar unos cuantos euros por las aplis que uso. No todo va a ser gratis en esta vida -casi nada bueno lo es, y el propio iPad tampoco lo regalan-. Por otro lado, los precios de las aplis son realmente asequibles: la más cara que tengo no llegó a los ocho euros.
En todo este mundillo del jailbreak hay muchos genios -Comex, MuscleNerd- y muchos buenos profesionales que simplemente exploran las posibilidades de un dispositivo sin vulnerar ningún derecho de copyright. Se trata, más que nada, de evadirse de las draconianas reglas del juego impuestas por Apple, con su monotemática obsesión por el control total del ecosistema generado en torno a sus atractivos productos.
Junto a éstos surgen indefectiblemente los sinvergüenzas que, haciendo alarde de una dureza facial pasmosa, tratan de sacar tajada, cuando no intentan persuadirnos de que sus raterías obedecen a elevados ideales de lucha contra los monopolios, difusión libre de la cultura y demás. Basura, en definitiva.
Yo vivo de mi trabajo; los señores que programan con Cocoa Touch, del suyo; los de Cupertino ídem de lienzo, y así sucesivamente. Por eso, tenemos que respetar un poquito el trabajo de los demás, ¿no?
Sea como fuere, lo que yo seguía necesitando era una forma de activar los nuevos gestos de cuatro y cinco dedos en mi iPad 1. Sólo eso, sin Cidia ni jailbreak. Pues es posible, como demuestran en este enlace. Todo el mérito es de alguien que firma D.B., si bien se apoya en el RedSn0w de MuscleNerd.
La idea es tan buena como sencilla. Para activar los multigestures basta cambiar una entrada en un xml de configuración. La dificultad es que dicho xml se encuentra en la carpeta system del iPad y, por tanto, no está accesible a menos que se disponga de privilegios root. Pero esto se consigue desde el modo DFU del dispositivo en los primeros pasos del jailbreak.
En lugar de inyectar el kernel modificado, instalar Cidia, mover las aplis, etc. ¿Por qué no limitarse simplemente a modificar el fichero de configuración necesario, sin tocar nada más? No me digáis, amigos, que no es bonito y elegante :-D
El código de DB, donde se ve claramente el cambio realizado en el fichero plist correspondiente, es interesante y muy didáctico. Os lo reproduzco a continuación.
/*
* Gestures tweak
* Replacement for jailbreak executable in redsn0w ramdisk
*
* - dB
*/
#include
#include
#include
#include
#include
#include
static int execute ( int *status, const char *command, char *arguments[] ) {
// Fork the process
int result = fork ();
if ( result == -1 ) {
perror ( "fork" ); return -1;
}
// Child process
else if ( result == 0 ) {
execv ( command, arguments );
perror ( "execv" );
exit(-1);
}
// Parent process
else {
// Wait for the child process to end
if ( waitpid ( result, status, 0 ) == -1 ) {
perror ( "waitpid" ); return -1;
} return 0;
}
}
static int executev ( int *status, const char *command, ... ) {
// Argument list
int argc = 0;
char* argv[512];
// Setup variable arguments
va_list vargs;
va_start ( vargs, command );
// Add passed arguments to the list
while ( argc++ < (sizeof(argv)/sizeof(argv[0])) ) {
int i = argc - 1;
argv[i] = va_arg ( vargs, char* );
if ( !argv[i] ) break;
} argv[argc] = NULL;
// Execute the command
int result = execute ( status, command, argv );
// Cleanup and return the result
va_end ( vargs );
return result;
}
static int modifycapability ( const char *capability, const char *device, int enable ) {
// Setup the path
char path[PATH_MAX];
snprintf ( path, sizeof ( path ), "/System/Library/CoreServices/SpringBoard.app/%s.plist", device );
// Perform the operation and return the result
int status = 0;
int result = executev ( &status, "/plutil", "/plutil",
"-key", "capabilities",
"-key", capability,
enable ? "-true" : "-false",
path, NULL
); return ( result != 0 || !(WIFEXITED(status) && WEXITSTATUS(status) == 0) ) ? -1 : 0;
}
static int copy ( const char *sourcepath, const char *targetpath ) {
// The reading buffer
char buffer[512];
// Open the source and target files
FILE* source = fopen ( sourcepath, "rb" );
if ( !source ) { perror ( "fopen" ); return -1; }
FILE* target = fopen ( targetpath, "wb" );
if ( !target ) { fclose ( source ); perror ( "fopen" ); return -1; }
// Perform the copy
for ( ;; ) {
int count = fread ( buffer, 1, sizeof ( buffer ), source );
if ( count <= 0 ) break;
fwrite ( buffer, 1, count, target );
}
// Cleanup
fclose ( source );
fclose ( target );
return 0;
}
static int replace ( const char *path, const char *pattern, const char *replacement ) {
// Open the source and target files
FILE* source = fopen ( path, "rb" );
if ( !source ) { perror ( "fopen" ); return -1; }
FILE* target = fopen ( "/tmp/replacement", "wb" );
if ( !target ) { fclose ( source ); perror ( "fopen" ); return -1; }
// Initial allocations and length calculations
int repllen = strlen ( replacement );
int pattlen = strlen ( pattern );
int bufflen = pattlen * 5;
char* buffer = malloc ( bufflen + 1 );
// Perform the search and replace
for ( ;; ) {
int count = fread ( buffer, 1, bufflen, source );
if ( count < pattlen ) {
fwrite ( buffer, 1, count, target ); break;
}
int i; for ( i = 0; i <= count - pattlen; i++ ) {
// If a match is found
if ( memcmp ( buffer + i, pattern, pattlen ) == 0 ) {
fwrite ( replacement, 1, repllen, target );
i += pattlen - 1;
}
// No match was found
else {
fwrite ( buffer + i, 1, 1, target );
}
} fwrite ( buffer + i, 1, count - i, target );
}
// Cleanup
fclose ( source );
fclose ( target );
free ( buffer );
// Copy
copy ( "/tmp/replacement", path );
unlink ( "/tmp/replacement" );
return 0;
}
int main () {
// Op status
int status = 0;
// Greeting message
printf ( "\n" ); // Initial blank line
printf ( "Gestures & display mirroring for iPad 1 (w/o Jailbreak)\n"
"by dB\n\n" ); fflush ( stdout );
// Attempt to update SpringBoard capabilities
printf ( "Updating SpringBoard capabilities...\n" );
printf ( "Enabling multitasking-gestures...\n" );
printf ( "%s\n",
modifycapability ( "multitasking-gestures", "K48AP", 1 ) == 0 ? "OK" : "FAIL" );
printf ( "Enabling display-mirroring...\n" );
printf ( "%s\n",
modifycapability ( "display-mirroring", "K48AP", 1 ) == 0 ? "OK" : "FAIL" );
// Convert /Applications/Preferences.app/General.plist into XML format
printf ( "Converting Preferences/General.plist into XML...\n" );
status = executev ( NULL, "/plutil", "/plutil",
"-xml", "/Applications/Preferences.app/General.plist", NULL
); printf ( "%s\n", (status == 0) ? "OK" : "FAIL" );
// Patch the file, correcting the misspelled "Mutltitasking_Gesture" to "Multitasking_Gesture"
printf ( "Patching Preferences/General.plist...\n" );
status = replace ( "/Applications/Preferences.app/General.plist", "Mutltitasking_Gesture", "Multitasking_Gesture" );
printf ( "%s\n", (status == 0) ? "OK" : "FAIL" );
// Convert /Applications/Preferences.app/General.plist back into binary format
printf ( "Converting Preferences/General.plist back into binary...\n" );
status = executev ( NULL, "/plutil", "/plutil",
"-binary", "/Applications/Preferences.app/General.plist", NULL
); printf ( "%s\n", (status == 0) ? "OK" : "FAIL" );
// Perform a disk sync
printf ( "\n" );
printf ( "Performing a sync... " ); fflush ( stdout );
sync (); printf ( "OK\n" );
// Countdown to reboot
printf ( "\n" );
printf ( "Rebooting in... " );
int i; for ( i = 10; i >= 1; i-- ) {
printf ( "%d ", i ); fflush ( stdout );
sleep ( 2 );
} printf ( "0!\n" );
// Reboot the device
reboot ( 0 );
return EXIT_SUCCESS;
}
Y funciona. Vaya que si funciona...
Saludos
No hay comentarios:
Publicar un comentario