Jeudi 15 Septembre 2011
Obfuscated C contest
Cela fait plus d'un an que je n'ai rien posté dans ce blog. Ce n'est pas faute d'idées de sujets sur lesquels écrire, mais un subtil mélange de manque de temps à y consacrer et de flemme plus ou moins aiguë selon les jours.

Folco ayant récemment exprimé sur IRC son désir de lire un nouveau billet de blog en prenant son café demain matin, et ne voulant pas le décevoir, je me vois donc dans l'obligation c'est avec grand plaisir que j'en rédige un.

Comme je reste fainéant, il s'agit en fait d'un truc que j'avais préparé il y a longtemps ; et je vais même avoir l'audace de vous mettre à contribution, un peu comme ces artistes qui font chanter leur public %)

Vous connaissez certainement l'Obfuscated C contest, un concours dont le but est d'écrire un programme valide en C qui soit le plus illisible possible. Mais il faut aussi que le programme lui-même soit intéressant à faire tourner et à analyser, sinon c'est beaucoup trop facile :p

Voici donc un programme que j'ai écrit sur ce thème. Pas d'obfuscation à grand coups de #define, de noms de variables abscons ou d'usage tordu des features du compilateur ici ; il ne s'agit "que" d'un algorithme tordu avec les constantes en dur :
#include <stdio.h> 
#include <math.h> 
#include <inttypes.h> 
 
int32_t mystere(uint32_t n) 
{ 
    n &= 0xFF; 
    if (!n) return -21; 
    if ((n & 0xC0) == 0x40) n &= 0xBF; 
    if ((n & 0x1F) > 16) return mystere((n ^ 0x1F) + 1); 
    if (n & 0x1C) return mystere(n - 4) + 12; 
    if ((n & 0xC3) == 0xC0) return mystere(n & 0xBF) - 1; 
    if ((n & 3) == 1) return mystere(n - 1) + 2 + ((n >> 7) * (((n & 0x40) >> 6) + 1)); 
    if ((n & 3) == 2) return mystere(n - 1) + 3 + ((((n & 0xC0) == 0x80) << 1) - ((!(n & 0xC0)) * (((n & 0x20) >> 5) + 1))); 
    if ((n & 3) == 3) return mystere(n - 1) + 4 - ((!(n & 0xE0)) + (((n & 0xC0) == 0x80) << 1)); 
    return mystere(0) - 3 + (((n & 0xE0) == 0xA0) << 1); 
} 
 
int main(void) 
{ 
    int16_t *buf = malloc(10584320); 
    int16_t *p = buf; 
    FILE *f = fopen("mystere.bin", "wb"); 
    for (uint32_t i = 0; i < 320; i++) 
    { 
        for (uint32_t j = 0; j < 8269; j++) 
        { 
            double s[2], n = sin((double)(j) * 0.062689 * pow(2, (double)mystere(i) / 12.0)) * 16384.0; 
            n *= exp((double)j * -5E-5); 
            if (i & 0x100) n *= exp(-(double)(i & 0xFF) * 0.1); 
            if (j < 441) n *= (double)(j) * 0.002268; 
            if (j > 7828) n *= (double)(8269 - j) * 0.002268; 
            s[0] = s[1] = n; 
            if ((p - buf) > 1999) { s[0] += (double)p[-2000] * 0.3; s[1] += (double)p[-2000] * 0.3; } 
            if (i > 1) { s[0] += (double)p[-33076] * 0.125; s[1] += (double)p[-33076] * 0.375; } 
            if (i > 2) { s[0] += (double)p[-49614] * 0.1875; s[1] += (double)p[-49614] *  0.0625; } 
            *p++ = (int16_t)s[0]; *p++ = (int16_t)s[1]; 
        } 
    } 
    fwrite(buf, 1, 10584320, f); return 0; 
}
Télécharger le fichier source (notez que ce code fait pile poil 42 lignes #hehe# )
Télécharger l'exécutable pour Windows (pour ceux qui utilisent Linux, je vous laisse compiler avec GCC ; utilisez l'option -std=c99)

Et maintenant, essayez de répondre aux questions suivantes (avant de lancer le programme, évidemment, sinon il n'y a pas de challenge :p )
1) quel est le type de fichier généré par ce programme ?
2) plus précisément, que constitue l'ensemble des données générées ?
3) d'où proviennent-elles à l'origine ? (si vous trouvez la réponse à celle-là juste en lisant le code, vous êtes très, très fort :D )
4) comment tout ce fatras fonctionne-t-il ?

Si vous jetez l'éponge ou que vous êtes simplement curieux, voici le fichier de sortie dans un format standard.

Vous aurez droit au making-of commenté au prochain épisode =)

PS : des spoilers peuvent s'être glissés dans les commentaires !
Posté à
01:53
 par Zerosquare -

21. Godzil à 19:50 10/09/2012 -
Ca va faire un an qu'on attends la suite :o

20. Orion_ à 13:54 12/10/2011 -
bon c'est moins beau, mais en assembleur c'est chaud quoi :p
http://www.youtube.com/watch?v=7HWrPG-g8Rg&t=8m8s

19. Orion_ à 13:51 12/10/2011 -
je sais déjà ce que ça fait, je l'ai même porté en assembleur dsp il me semble, non ? %)

18. GT Turbo à 21:46 11/10/2011 -
Du C rien que du bonheur ! C'est illisible par défaut ce langage pas besoin de faire un contest d'illisibilite ! o_O

17. Zeph à 16:30 17/09/2011 -
Ah oui, j'avais pas vu le blog de Godzil (sa CSS suffit à m'ôter l'envie d'aller voir ce qu'il s'y passe ^^). Mais je reste quand même intrigué par l'opération qui permet d'écrire un .wav "à la main" (ceci dit Google est mon ami au besoin) :)

16. Zerosquare à 15:58 17/09/2011 -
Pour l'instant tu as tout bon #hehe#

Voir le blog de Godzil pour une explication du fonctionnement de main() (mais pas de mystere() :) ).

15. Zeph à 15:16 17/09/2011 -
je n'y connais strictement rien non plus en son ; mystere n'a pas l'air bien complexe, ça génère la hauteur de la n-ième note de la mélodie ? si oui je suppose que le main permet de transformer ça dans les données-qui-vont-bien pour en faire un fichier audio lisible dans un format que je ne connais pas.

/me en attente des explications ^^

14. Zerosquare à 17:40 16/09/2011 -
Ah ben ça j'en sais rien, je n'ai que ta parole :D

13. Godzil à 20:56 15/09/2011 -
C'est pourtant facile :o tu peux meme demander a zerosquare a quel point je suis une bille en audio ;)

12. Folco à 18:45 15/09/2011 -
Super, Zerosquare, prochaine fois poste-moi un problème de 68k au petit-déj, parce que là je suis à la rue complètement, j'ai jamais mis le nez dans quoi que ce soit d'audio :D

11. Godzil à 17:17 15/09/2011 -
Merci, tu m'a permis de faire marcher mon cerveau cet aprem ;)

10. Zerosquare à 17:00 15/09/2011 -
Bravo, cf. réponse sur ton blog ;)

9. Godzil à 15:30 15/09/2011 -
Pour un son plus NES: remplacer mystere2 par ça:
if (c > 6.28319) return mystere2(c - 6.28319);
if (c <= 1.57808) return 1; return -1;

8. Godzil à 15:23 15/09/2011 -
Je propose cette version, en 42 lignes egalement ;) : (sur mirari: http://www.mirari.fr/jbnb )

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
double mystere2(double c) {
if (c > 6.28319) return mystere2(c - 6.28319);
if (c <= 1.57808) return c/1.57808; else c -= 1.57808;
if (c <= 3.14159) return -(c*0.628319)+1; else c -= 3.14159;
return mystere2(c) - 1; }
int32_t mystere(uint32_t n) {
n &= 0xFF;
if (!n) return -21;
if ((n & 0xC0) == 0x40) n &= 0xBF;
if ((n & 0x1F) > 16) return mystere((n ^ 0x1F) + 1);
if (n & 0x1C) return mystere(n - 4) + 12;
if ((n & 0xC3) == 0xC0) return mystere(n & 0xBF) - 1;
if ((n & 3) == 1) return mystere(n - 1) + 2 + ((n >> 7) * (((n & 0x40) >> 6) + 1));
if ((n & 3) == 2) return mystere(n - 1) + 3 + ((((n & 0xC0) == 0x80) << 1) - ((!(n & 0xC0)) * (((n & 0x20) >> 5) + 1)));
if ((n & 3) == 3) return mystere(n - 1) + 4 - ((!(n & 0xE0)) + (((n & 0xC0) == 0x80) << 1));
return mystere(0) - 3 + (((n & 0xE0) == 0xA0) << 1); }
int main(void) {
uint32_t i, j;
int16_t *buf = malloc(10584320);
int16_t *p = buf;
FILE *f = fopen("mystere2.bin", "wb");
for (i = 0; i < 320; i++)
for (j = 0; j < 8269; j++)
{
double n = mystere2((double)(j) * 0.062689 * pow(2, (double)mystere(i) / 12.0)) * 16384.0;
double s;
n *= exp((double)j * -5E-5);
if (i & 0x100) n *= exp(-(double)(i & 0xFF) * 0.1);
if (j < 441) n *= (double)(j) * 0.002268;
if (j > 7828) n *= (double)(8269 - j) * 0.002268;
s = s = n;
if ((p - buf) > 1999) { s += (double)p[-2000] * 0.3; s += (double)p[-2000] * 0.3; }
if (i > 1) { s += (double)p[-33076] * 0.125; s += (double)p[-33076] * 0.375; }
if (i > 2) { s += (double)p[-49614] * 0.1875; s += (double)p[-49614] * 0.0625; }
*p++ = (int16_t)s; *p++ = (int16_t)s;
}
fwrite(buf, 1, 10584320, f);
return 0; }

7. Zerosquare à 14:24 15/09/2011 -
Pas mal, pas mal !

Pour le sin(), j'avais prévu de mettre une simulation discrète de système du 2nd ordre à la place (pour que ça se voie moins), mais j'ai pas réussi à la faire marcher et ça m'a gavé %)

6. Godzil à 14:07 15/09/2011 -
PS: le sin t'a trahi ;)

5. Godzil à 14:01 15/09/2011 -
Alors a vu de nez avec un passage tres rapide sur le code :
1 -> un fichier son, donc j'imagine du wav ou equivalent (quoique ça peux etre du brut)
2 -> une mélodie je serait pret a le parier ;)
3 -> dans la fonction mystere une sequence de valeur "simple" y est codé en dur
4 -> C'est explicite dans le code rho :o

4. Folco à 10:58 15/09/2011 -
Ahahah merci t'es un chou \o/ :D
Bon, là j'ai la pile de repassage et un frigo à nettoyer qui m'attend, mais j'y reviendrai un peu plus tard %)

3. vince à 10:33 15/09/2011 -
non

2. Zerosquare à 09:48 15/09/2011 -
/me offre un free hug à Vince
Content ? ;)

1. vince à 09:35 15/09/2011 -
Et personne n'a rien fait pour mon petit dej :|

Pseudo :

Adresse mail : (optionnel)

Site web : (optionnel)

Veuillez entrer la somme de un et trois :
Message :


 RSS  - ©yNBlogs 2004

Login : - Mot de passe :