PROJET AUTOBLOG


Tiger-222

Archivé

Site original : Tiger-222

⇐ retour index

Fix : atikmdag.sys BSOD Windows 7

vendredi 29 novembre 2013 à 01:32
Édit : ça ne fonctionne pas !

bsod.jpg
Voici le fameux Écran Bleu de la Mort... Une tuerie !

Ça fait 5-6 fois que le PC portable de mon frangin redémarre tout seul. Juste avant, l'écran devient noir, plus vert, plus bleu, ... Sa carte graphique est une ATI Mobility Radeon HD 4500/5100 Series.

Voici ce que l'outil WhoCrashed nous dit :
On Thu 28/11/2013 21:49:35 your computer crashed
This was likely caused by the following module: atikmpag.sys
Bugcheck code: 0x116 (0xFFFFFA800823E290, 0xFFFFF88004297EFC, 0x0, 0x2)
Error: VIDEO_TDR_ERROR
Dump file: C:\Windows\Minidump\112813-16770-01.dmp
file path: C:\Windows\system32\drivers\atikmpag.sys
product: AMD driver
company: Advanced Micro Devices, Inc.
description: AMD multi-vendor Miniport Driver

J'avais déjà mis à jour le pilote, mais il s'avère que ça ne règlera pas le problème. Surtout que la machine ne sert pas à se divertir avec les derniers jeux haute définition, il s'agit d'un outil de travail (bureautique, web, etc).

Je suis tombé sur cet article qui explique que le fichier atikmpag.sys n'est jamais mis à jour lors d'une mise à jour du pilote... Ouais, rien que ça. Je n'ai pas vérifié la véracité de la chose, mais rien n'est impossible.


Alors voilà la procédure de réparation :

  1. Télécharger la dernière version du pilote (l'outil AMD Driver Autodetect peut s'en charger, très pratique)
  2. Décompresser le pilote (généralement dans C:\AMD\Support)
  3. Localiser le fichier atikmpag.sy_ et le copier à la racine de C:
  4. Ouvrir une console (Démarrer > Exécuter > cmd), puis :
    > C:
    > expand -r atikmpag.sy_
    > del atikmpag.sy_
    > cd C:\Windows\System32\Drivers\
    > rename atikmpag.sys atikmpag.sys.or
    > rename C:\atikmpag.sys C:\Windows\System32\Drivers\
    Pour informations, expand sert à décompresser un fichier.

C'est tout bon, il ne vous reste plus qu'à redémarrer !

Comment générer un bon ID pour les flux Atom/RSS

jeudi 28 novembre 2013 à 13:30
Ce qui suit est une traduction libre de l'article How to make a good ID in Atom de Mark Pilgrim, le vendredi 28 mai 2004.
Je me suis permis de le compléter avec le flux RSS et mettre à jour une RFC.

Table des matières :

Avant-propos


Lorsque l'article fera référence à l'élément <id>, il s'agira soit de l'élément <id> du flux Atom, soit de l'élément <guid> du flux RSS. Et ce, par soucis de lisibilité. Cependant, tout ce qui suit est valable pour les flux Atom comme RSS.


Introduction


Chaque entrée d'un flux Atom/RSS doit avoir un ID globalement unique, dans l'élément <id>. Cela aide les agrégateurs à garder une trace de l'entrée, même si elle est mise à jour. Certains agrégateurs réaffichent les entrées modifiées, d'autres non, d'autres changent au fil du temps. Mais avant de pouvoir faire quoi que ce soit, vous devez identifier chaque entrée de manière unique, et c'est le rôle de <id>.

Il y a 3 conditions requises pour un ID :

  1. L'ID doit être un URI valide, tel que défini par la RFC 2396 RFC 3986.
  2. L'ID doit être globalement unique, quelque soit le flux Atom/RSS, partout, et pour toujours. Il s'avère que cette partie est plus facile qu'elle n'y paraît.
  3. L'ID ne doit jamais, jamais changer.

Il existe plusieurs manières de construire un ID immuable globalement unique, mais certaines sont mieux que d'autres.


Pourquoi vous ne devriez pas utiliser le permalien comme ID


Le fait d'utiliser l'URL du permalien comme <id> est valide, mais je le déconseille parce qu'il peut porter à confusion sur l'élément qui devrait être traité comme permalien. Les développeurs qui ne lisent pas les spécifications regarderont votre flux Atom/RSS et verront deux éléments d'information identiques, ils en choisiront un comme permalien, et certains se tromperont. Puis il iront vers un autre flux où les deux éléments ne sont pas les mêmes, et seront confus.

Dans un flux Atom/RSS, <link rel="alternate"> est toujours le permalien de l'entrée. <id> est toujours l'unique identifiant de l'entrée. Les deux sont requis, mais ils ont des objectifs différents. L'ID d'une entrée ne devrait jamais changer, même si le permalien est modifié.

« Permalien modifié » ? Oui, les permaliens ne sont pas aussi permanents qu vous pourriez le penser. Voici un exemple qui m'est arrivé. Mes permaliens étaient générés automatiquement d'après le titre de mon entrée, mais plus tard j'ai mis à jour une entrée et modifé le titre. Devinez quoi, l'URL du "permalien" avait changé ! Si vous êtes malins, vous pouvez utiliser une redirection HTTP pour rediriger le visiteur depuis un ancien permalien vers le nouveau (ce que j'ai fait). Mais vous ne pouvez pas rediriger un ID.

L'ID d'une entrée Atom/RSS ne doit jamais changer ! Dans l'idéal, vous devriez générer l'ID d'une entrée une seule fois, et le stocker quelque part. Si vous le générez automatiquement, à chaque fois que vous apporterez un changement, l'ID de l'entrée sera modifié, ce qui va à l'encontre de l'objectif.


Pourquoi vous ne devriez pas utiliser un URN comme ID


La RFC 2141 définie la syntaxe pour les URN. Les URN ont été spécialement élaboré pour être utilisés comme identifiant globalement unique. Il s'agit d'URI valides. Ils ressemblent, en quelque sorte, aux URL que vous tapez dans la barre d'adresse d'un naviagteur, à ceci près que les URN ne sont pas déstinés à être cliquable. Ce sont de simples identificateurs structurés.

Alors pourquoi ne pas les utiliser ? Eh bien, la raison principale est qu'ils doivent être enregistrés (comme décrit dans la RFC 3406). Vous ne pouvez pas juste utiliser le nom de domaine que vous avez déjà ; l'enregistrement d'un espace de nom URN est un processus à part.

Si vous avez enregistré un espace de nom URN, alors vous pouvez l'utiliser pour générer des ID. Dans le cas contraire, vous ne pouvez pas simplement utiliser un URN et le publier. Les URN ne fonctionnent pas de cette manière.


Comment générer un ID à l'aide du Tag URI


Il y a un standard émergeant qui permet à tout le monde de générer des identifiants globaux uniques sans enregistrement : le Tag URI. Pour en générer un, seuls un nom de domaine et une adresse courriel sont nécessaires. (Un sous-domaine fonctionne aussi.) Pour ce tutoriel, je supposerai que vous possédez votre propre nom de domaine ou sous-domaine, et que vous ne souhaitiez pas divulguer votre adresse courriel aux yeux des spammeurs.

Commençons par l'URL du permalien. J'utiliserait http://diveintomark.org/archives/2004/05/27/howto-atom-linkblog, un exemple d'article réel. Votre permalien peut sembler différent, il ne contient peut-être pas de date, ou est composé d'un simple ID numérique ; il peut contenir un identifiant fragmenté (avec la marque #). Pas de soucis, vous pouvez construire un Tag URI à partir de n'importe quelle URL.

  1. Jeter tout ce qui précède le nom de domaine :
    diveintomark.org/archives/2004/05/27/howto-atom-linkblog

  2. Remplacer tous les # par / :
    (dans notre cas, rien ne change)

  3. Juste après le nom de domaine, ajoutez une virgule, suivie de l'année-mois-jour de publication de l'article, puis deux-points (« : »). Veillez à noter l'année sur 4 chiffres, le mois et le jour sur deux chiffres chacun. N'oubliez pas le deux-points :
    diveintomark.org,2004-05-27:/archives/2004/05/27/howto-atom-linkblog

  4. Ajoutez tag: au début. (N'ajoutez pas de barres obliques, ou slashes, c'est juste “tag:“. C'est une erreur commune.) :
    tag:diveintomark.org,2004-05-27:/archives/2004/05/27/howto-atom-linkblog

Voilà ! Il y a d'autres manières de créer un Tag URI valide, mais cette procédure fonctionne pour toutes les URL.

Le seul problème potentiel que je vois, c'est si votre permalien est amené à changer dans le temps (par exemple, s'il est basé sur le titre et que vous le modifier après publication, ou si vous changer radicalement le schéma URL des permaliens), vous ne devez pas reconstruire le Tag URI à chaque fois que le permalien change. Dans l'idéal, vous devriez générer une seule fois l'ID et le stocker avec le reste des données de l'entrée. Si ça n'est pas faisable, et si vous ne pouvez pas garantir que vos permaliens ne changeront jamais, vous devriez envisager une autre méthode pour générer le Tag URI.


Autres méthodes pour construire un ID valide



Que se passe-t-il si la même entrée apparaît dans plusieurs flux ?


Si la même entrée apparaît dans deux flux différents, elle doit avoir le même ID dans les deux. Ça n'est pas une exception à la règle "globalement unique", ça en fait partie intégrante. L'ID d'une entrée est la clef de cette entrée à travers le temps et l'espace. Si la même entrée apparaît à deux endroits, elle doit avoir le même ID partout — sinon ça n'est pas réellement la même entrée.

Quand cela pourrait-il arriver ?

Dans le cas où un site web propose plusieurs flux, vous devez simplement faire attention à ce que chaque ID généré soit identique quelque soit le flux. Soyez sûr que l'ID ne soit pas basé sur l'URL du flux dans lequel il apparaît, ni du nom de la catégorie, ou tout autre donnée susceptible de différer suivant qu'elle soit dans un flux ou l'autre.

Dans le cas de sites qui agrègent le contenu d'autres sites, le script d'agrégation devrait préserver l'élément <id> original de l'entrée de chaque flux.

Comment les agrégateurs gèrent le duplica de données est à leur entière charge. Si une entrée apparaît dans deux flux, et que vous êtes abonnés aux deux, certains agrégateurs pourrait afficher l'entrée dans les deux, mais la marquer comme "lue" partout lorsque vous l'aurez lue une seule fois. Le comportement du logiciel côté client est entièrement à la merci du développeur qui l'a écrit. La seule chose que fait l'élément <id>, c'est de donner au développeur la capacité de prendre ces décisions sans complications ni erreurs heuristiques.


Résumé


Un ID Atom/RSS est un URI immuable, global et unique. Tous ces attributs sont importants. Si l'ID d'une entrée change constamment, ça va à l'encontre de l'objectif. Si vous réutilisez les ID pour différentes entrées, ça va vraiment à l'encontre du principe. Il y a plusieurs techniques pour construire un URI immuable globalement unique, et vous devriez choisir le plus facile pour vous.

§

PHP : l'erreur subtile de l'opérateur ternaire

lundi 25 novembre 2013 à 12:15
Récemment, j'ai découvert un bogue dans BlogoText. La ligne incriminée est :
$token = (isset($_POST['token'])) ? $_POST['token'] : (isset($_GET['token'])) ? $_GET['token'] : 'false';
Soit :
$token =
isset($_POST['token']) ?
$_POST['token'] :
isset($_GET['token']) ?
$_GET['token'] :
'false';

Il s'agit d'une imbrication de conditions ternaires. Comme le dit la documentation, l'expression (expr1) ? (expr2) : (expr3) est évaluée à expr2 si expr1 est évaluée à TRUE, et expr3 si expr1 est évaluée à FALSE.

operateur-ternaire.png

Ici l'auteur désire récupérer le token envoyé par la méthode POST, ou sinon GET.
En situation réelle, $_POST contient bien une entrée token, donc nous devrions avoir $_POST['token']. Hors, l'expression ci-dessus retournera NULL... Voyons pourquoi.


L'expression ternaire est évaluée de gauche à droite, pour que ça soit plus clair :
$token =
isset($_POST['token']) ?
'a' :
isset($_GET['token']) ?
'b' :
isset($_SESSION['token']) ?
'c' :
isset($je_suis_un_crack) ?
'd':
FALSE;
Que vaut $token dans cet l'exemple ?
En théorie 'a', puisque $_POST['token'] existe. Au lieu de ça, $token vaut 'd'. Si je remplace isset($_POST['token']) par !isset($_POST['token']), $token faudra FALSE.

Pour simplifier, les expressions du milieu sont ignorées. On aura le résultat de la dernière expression :
$token =
isset($_POST['token']) ?
//'a' :
//isset($_GET['token']) ?
//'b' :
//isset($_SESSION['token']) ?
//'c' :
//isset($je_suis_un_crack) ?
'd':
FALSE;


Revenons-en à nos moutons, pourquoi NULL est renvoyé ? Car $_POST['token'] existe bel et bien, donc d'après la suite de l'expression, on retourne $_GET['token'], qui n'existe pas, donc NULL. D'ailleurs, en activant l'affichage des erreurs, on a bien un averto :
Notice: Undefined index: token in inc/veri.php on line 160

Pour corriger le problème, il suffit d'ajouter des parenthèses, comme en maths :
$token = isset($_POST['token']) ? $_POST['token'] : (isset($_GET['token']) ? $_GET['token'] : 'false');
Soit :
$token =
isset($_POST['token']) ?
$_POST['token'] :
(isset($_GET['token']) ?
$_GET['token'] :
'false');

Le port d'armes, z'êtes fou ?!

mardi 19 novembre 2013 à 22:26
v-for-vendetta-mini.jpg
Version haute résolution (copie locale)


En ce moment même le documentaire Des armes dans les caddies passe sur Chérie 25 : une enquête menée aux Etats-Unis sur le nombre grandissant des Américaines qui ont choisi de porter une arme pour se défendre.

Ça me sidère ! Des femmes avec un flingue ?! Non, je plaisante bien entendu. Des humains avec des armes ?!

Bien sûr, ce qui suit n'engage que moi. Et j'en rajoute peut-être un peu (ou pas).


Argumentaire


Les arguments des politiques et des vendeurs ne sont que fumisterie, ça m'enrage de voir la populasse se laisser engrainer si facilement. Le lobby de l'armement est horriblement puissant, certes, mais c'est bien nous (la populasse) qui lui fournissons l'argent et la demande.

Quand je vois des pistolets roses pour plaire aux dames, c'est terriblement malsain, on en vient à moderniser l'arme, tel un gadget à la mode.

Quand j'entends un vendeur dire que cette 22 bleue plaît énormément aux petits garçons, ça me tue ! Non mais QUOI ?! Personne n'a une once d'intelligence, de raisonnement logique, ou simplement de tête ? Qui peut bien acheter un fusil à son gosse, que peut-il bien en faire ? Un enfant reste un enfant, il n'est pas capable de comprendre ce qu'il fait, il ne se rend pas compte qu'il s'agit d'un engin de mort. De mort !

Et la peur ? Cette fameuse peur de voir une bande de gros balèzes débarquer devant chez toi, bazouka à la main, pour te violer, tuer tes enfants, voler tes économies et faire exploser ta baraque... Dur de finir comme ça. Et quand est-ce que vous réfléchissez ? Pas trop, mais juste un minimum. Il y a déjà tellement d'exemples de malades, ou de personnes qui sont juste dans une mauvaise passe, et qui prenne le chemin le plus pitoyable, le plus facile, pour dégommer ses anciens amis, sa famille ou tout autre personne qui, par malchance, se trouvait là.
Dernier exemple en date : la mort d'un des chefs exécutif de Grooveshark, Eddy Vasquez, tué par une de ses connaissances de 2 balles dans la poitrine suite à un malentendu à propos d'un sujet. Chez des personnes à peu près cohérentes, on appelle ça une discussion, un dialogue, qui oppose des arguments et des contre-arguments. Pas des balles de pistolet. Il avait 27 ans. 27 pauvres années d'existence qui finissent comme ça, sur un coup de tête (expression).

Au pire, ayez des couilles (filles comprises) et battez-vous à mains nues. Ça défoule bien plus et, en théorie, chacun devrait se réveiller le lendemain matin. Ou sinon, rien ne vaut une bonne batte et un poing américain (ironie).


Il y a peut-être pire dans tout ça, c'est que ce genre de peur mis en place par le pouvoir, peut se retrouver dans tous les domaines. Et ce ne sont jamais les gentils qui gagnent. Voyez les pédo-nazis et autres pirates tueurs de bébés phoques pour exterminer la libertée sur Internet. Ça va finir comme dans V pour Vendetta. Pressé.

Voilà, fallait que ça sorte. Surtout ne vous sentez pas concernés quand je dis vous ou nous, rien de personnel ☮

Projet Avatar

mardi 19 novembre 2013 à 16:57
J'avais envie de mettre une image à côté de chaque commentaire, mais ne souhaitais pas utiliser un service tiers comme Gravatar. J'ai regardé vite fait quelles pouvaient être les autres solutions existantes mais n'ai pas trouvé mon bonheur parmis, entre autres, VizHash GD de Seb, libravatar, MonsterID et ces dépôts GitHub. Alors voici ma modeste contribution : Avatar.
projet-avatar.png
Note : commentaire choisi au pseudo-hasard ^^

En situation réelle, ça donne ceci et cela.


Comment ça marche


Le code PHP est relativement simple :
class Avatar
{

private $length = 80;

public function __construct($text = 'Tiger-222')
{
$this->char = strtoupper($text[0]);
if ( $this->char < 'A' || $this->char > 'Z' ) {
$this->char = '*';
}

$this->color = hash('crc32', $text); // RGBA
$this->filename = sprintf('%s_%s_%d.png',
$this->color, $this->char, $this->length);

if ( !is_file($this->filename) ) {
$this->create_thumb();
}
$this->render();
}

private function color($im, $r, $g, $b, $i) {
$i *= 3; // 5 pour 120px
$r = $r + $this->diffs[0] * $i;
$g = $g + $this->diffs[1] * $i;
$b = $b + $this->diffs[2] * $i;
return imagecolorallocate($im, $r, $g, $b);
}

private function create_thumb()
{
$s = $this->length / 4;
list($r_, $g_, $b_, $a_) = str_split($this->color, 2);
$r = hexdec($r_);
$g = hexdec($g_);
$b = hexdec($b_);
$this->diffs = array(
(255 - $r) / $this->length,
(255 - $g) / $this->length,
(255 - $b) / $this->length
);
$i = 16;

$im = imagecreatetruecolor($this->length, $this->length);
$color = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 0, 0, $this->length, $this->length, $color);

// First row
$shift = $s-1;
imagefilledrectangle($im, 0*$s+0, 0, 1*$s-1, $shift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 1*$s+1, 0, 2*$s-1, $shift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 2*$s+1, 0, 3*$s-1, $shift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 3*$s+1, 0, 4*$s-1, $shift, $this->color($im, $r, $g, $b, --$i));
// Second row
$sshift = $s+1;
$eshift = 2*$s-1;
imagefilledrectangle($im, 3*$s+1, $sshift, 4*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 2*$s+1, $sshift, 3*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 1*$s+1, $sshift, 2*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 0*$s+0, $sshift, 1*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
// Third row
$sshift = 2*$s+1;
$eshift = 3*$s-1;
imagefilledrectangle($im, 0*$s+0, $sshift, 1*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 1*$s+1, $sshift, 2*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 2*$s+1, $sshift, 3*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 3*$s+1, $sshift, 4*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
// Last row
$sshift = 3*$s+1;
$eshift = 4*$s-1;
imagefilledrectangle($im, 3*$s+1, $sshift, 4*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 2*$s+1, $sshift, 3*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 1*$s+1, $sshift, 2*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));
imagefilledrectangle($im, 0*$s+0, $sshift, 1*$s-1, $eshift, $this->color($im, $r, $g, $b, --$i));

// Paint the letter
$size = 50;
$angle = 12;
$font = './_avatars.ttf';
$dim = imagettfbbox($size, $angle, $font, $this->char);
$letter_width = max($dim[0], $dim[2], $dim[4], $dim[6]);
$letter_height = min($dim[1], $dim[3], $dim[5], $dim[7]);
$x = ($this->length - $letter_width) / 2; $x += 2;
$y = ($this->length - $letter_height) / 2;
// Shadow
$rgb = 222;
$shadow = 33;
if ( ($r + $g + $b) / 3 < 127 ) {
$rgb = 33;
$shadow = 222;
}
$color = imagecolorallocate($im, $rgb, $rgb, $rgb);
imagettftext($im, $size, $angle, $x-1, $y-1, $color, $font, $this->char);
// Letter
$color = imagecolorallocate($im, $shadow, $shadow, $shadow);
imagettftext($im, $size, $angle, $x, $y, $color, $font, $this->char);

imagetruecolortopalette($im, false, 255);
imagepng($im, $this->filename);
imagedestroy($im);
}

private function render()
{
header('Content-Type: image/png');
readfile($this->filename);
exit();
}

}

$text = isset($_GET['t']) ? $_GET['t'] : '*';
new Avatar($text);

Chaque image obtient un dégradé suivant la somme CRC32 du texte passé à la classe Avatar. Comme les choses sont bien faites, une somme CRC32 est un code héxadécimal de longueur 8. Je m'en sers pour déterminer la couleur de base du dégradé :
Somme CRC32 : 81c44142
Rouge = 0x81 = 129
Vert = 0xc4 = 194
Bleu = 0x41 = 65
Alpha = 0x42 = 66

Soit la couleur finale : RGB(129, 194, 65) ou #81c441.

J'ignore la valeur alpha puisque le dégradé sera évalué plus tard par un calcul scientifique fort complexe...


Composition de l'image


Une image sera plus compréhensible que des explications fumeuses :

projet-avatar-composition.png

À chaque étape, la couleur est redéfinie suivant 16 palliers.
Par dessus le carré final, s'ajoute la première lettre du texte passé à la classe Avatar, en majuscule. Ainsi qu'une ombre portée. Les couleurs de la lettre et de l'ombre sont inversées suivant la couleur du carré :

projet-avatar-composition-finale.png

La lettre doit être comprise dans l'alphabet [A-Z*], mais vous pouvez modifier le code pour accepter toutes les lettres. J'ai mis cette restriction car je me suis servi de ce service en ligne pour générer le fichier _avatar.ttf ; à vous de faire votre choix ou d'incoporer une police complète.


Intégration


<html>
<head>
<meta content="utf-8">
<title>Avatar</title>
<style>
img {
background-color: #fff;
padding: 1px;
border: 1px outset #111;
border-radius: 60px;
}
</style>
</head>
<body>
<img src="https://tiger-222.fr/avatars/?t=Kevin"/>
<img src="https://tiger-222.fr/index2.php"/>
</body>
</html>

Bien entendu, les images générées sont gardées en cache. Aperçu final :

projet-avatar-2.png


Améliorations


Dans l'idéal, j'aurai voulu générer la couleur du carré en utilisant l'adresse IP, mais cette information n'est pas gardée dans BlogoText. Et je n'avais pas vraiment l'envie de bidouiller la base de données pour ajouter ne serait ce que le condensat SHA256 de l'adresse IP du commentateur.
Dans le dépôt GitHub, j'ai ajouté une version qui prend en paramètre l'adresse IP ainsi que la lettre (fichier index2.php). Ça permet d'avoir un pseudonyme différent pour une même adresse IP.

Aussi, si vous trouvez une solution plus courte, une idée d'amélioration ou tout ce que vous voulez, n'hésitez pas à forker et proposer un patch ☺