Traduction avec l’API MyMemory et CCAN/JSON

Dans cet article nous allons voir comment traduire du texte avec l’API web MyMemory. Je voulais utiliser celui de Google, mais il est payant. MyMemory n’a pas la même qualité de traduction que Google, mais le but est de faire une démonstration et non un produit commercial.

Pour analyser la réponse en JSON, au lieu d’utiliser la classe TJSONObject de la RTL, c’est la bibliothèque CCAN/JSON qui sera utilisée. Cette bibliothèque à code source ouvert est codée en C. Je l’ai choisie car dans une étude comparative sur 41 bibliothèques JSON elle semblait bien classée.

JSON Parsing Time
Temps d’analyse des bibliothèques JSON

J’ai dû exclure toutes celles en C++11, car j’utilise encore principalement le compilateur bcc32. Le facteur principal était simplement la facilité d’intégration. Si ça ne compilait pas dans les minutes suivant le téléchargement, je passais à la prochaine dans la liste.

La première étape est de créer un nouveau projet FireMonkey. Dans la Form il faut insérer un TRESTClient, un TRESTRequest, un TRESTResponse, trois contrôles TEdit, un TComboBox et un TButton.Vous pouvez donner comme texte à votre bouton le mot « Traduire ».

Voici ce à quoi l’interface devrait ressembler:

Il faut ajouter le fichier json.c qui se trouve trouve dans le dossier \ccan\json au projet pour qu’il soit compilé. Je n’ai pas eu de problème pour l’utiliser avec les plateformes Win32, Win64 et OS X.

Dans votre fichier cpp voici l’en-tête à utiliser.

extern "C"
{
    #include "json.h"
}

Il ne faut pas oublier qu’il s’agit de code en C et non C++, c’est pourquoi il faut ajouter un peu plus de code.

Voici le code à ajouter dans votre constructeur pour initialiser les contrôles:

    TListBoxItem* ListItems;

    ListItems = new TListBoxItem(this);
    ListItems->Parent = ComboBox1;
    ListItems->Text = L"Anglais";
    ListItems->TagString = L"en";

    ListItems = new TListBoxItem(this);
    ListItems->Parent = ComboBox1;
    ListItems->Text = L"Chinois (simplifié)";
    ListItems->TagString = L"zh-CN";

    ListItems = new TListBoxItem(this);
    ListItems->Parent = ComboBox1;
    ListItems->Text = L"Russe";
    ListItems->TagString = L"ru";

    ListItems = new TListBoxItem(this);
    ListItems->Parent = ComboBox1;
    ListItems->Text = L"Latin";
    ListItems->TagString = L"la";

    // On sélectionne le premier item par défaut
    ComboBox1->ItemIndex = 0;

    // Message à afficher quand la propriété Text est vide
    Edit1->TextPrompt = L"Texte à traduire";

    // Le nombre de match par défaut est mis à zéro
    Edit3->Text = L"0";

    // On n'écrit pas dans les champs de résultat
    Edit2->ReadOnly = true;
    Edit3->ReadOnly = true;

Voici le code à ajouter dans l’évènement OnClick du bouton:

    if(Edit1->Text.IsEmpty() == true || ComboBox1->ItemIndex < 0)
    {
        Edit2->Text = "";
        Edit3->Text = "0";
        return;
    }

    String LQuery = Edit1->Text;
    String LTarget = ComboBox1->ListItems[ComboBox1->ItemIndex]->TagString;

    RESTClient1->BaseURL = "http://api.mymemory.translated.net/get" \
        "?q=" + Edit1->Text +
        "&langpair=fr|" + LTarget;

    RESTRequest1->Execute();

    // Conversion en UTF-8
    TBytes LUTF8Array = TEncoding::UTF8->GetBytes(RESTResponse1->JSONText + "0");
    LUTF8Array[LUTF8Array.High] = '\0'; // Le caractère nul de terminaison est requis

    JsonNode* LRootNode = json_decode((char *)&LUTF8Array[0]);
    if(LRootNode != NULL)
    {
        JsonNode* LResponseNode = json_find_member(LRootNode, "responseData");
        if(LResponseNode != NULL && LResponseNode->tag == JSON_OBJECT)
        {
            JsonNode* LTextNode = json_find_member(LResponseNode, "translatedText");
            if(LTextNode != NULL && LTextNode->tag == JSON_STRING)
            {
                TBytes LUTFAnswer;
                LUTFAnswer.Length = strlen(LTextNode->string_);
                memcpy(&LUTFAnswer[0], LTextNode->string_, LUTFAnswer.Length);
                Edit2->Text = TEncoding::UTF8->GetString(LUTFAnswer);
            }
            JsonNode* LMatchNode = json_find_member(LResponseNode, "match");
            if(LMatchNode != NULL && LMatchNode->tag == JSON_NUMBER)
            {
                Edit3->Text = LMatchNode->number_;
            }
        }
        json_delete(LRootNode);
    }

Le texte utilisé par CCAN/JSON utilise l’encodage UTF8, c’est pourquoi il y a des conversions. Sinon le code ressemble étrangement à ce que RAD Studio nous a habitués avec sa classe JSON.

Maintenant il ne reste qu’à exécuter l’application pour la tester.
Traduction macOS

Compression de fichiers avec FWZip

Dans un article précédent j’avais parlé de la classe TZipFile qui permet de faire de la compression de fichiers. Elle est très pratique. Par contre, il y a une fonctionnalité manquante qui est importante pour moi. Il s’agit de l’ajout d’un mot de passe à l’archive. C’est une limitation qui est connue d’Embarcadero depuis C++Builder XE2. Malheureusement il n’y a pas encore de solution qui a été apportée.

Je suis tombé par hasard sur une bibliothèque qui supporte cette fonctionnalité. Il s’agit de FWZip de Александр (Rouse_) Багель. Elle est codée en Pascal Objet et fonctionne sur Win32 et Win64 dans des projets VCL et FireMonkey. Elle peut être téléchargée sur GitHub.

C’est très simple à utiliser. Il faut d’abord ajouter les fichiers suivants à votre projet:

  • FWZipConsts.pas
  • FWZipCrc32.pas
  • FWZipCrypt.pas
  • FWZipReader.pas
  • FWZipStream.pas
  • FWZipWriter.pas
  • FWZipZLib.pas

On ajoute ensuite le fichier d’en-tête et la bibliothèque nécessaire.

#include <FWZipWriter.hpp>
#pragma comment(lib, "Shlwapi") // Pour PathCanonicalizeW / PathCanonicalizeA

Pour écrire une archive, on utilise ce code:

    TFWZipWriter* Zip = new TFWZipWriter(false,
        Fwzipzlib::TCompressionLevel::clLevel3, "MotDePasse");

    Zip->AddFile("C:\\img\\img0.png");
    Zip->AddFile("C:\\img\\img1.png");

    Zip->BuildZip("C:\\img\\images.zip");

    delete Zip;

Et voilà, maintenant lors de l’extraction des fichiers, un mot de passe sera demandé.

7-Zip Enter password window
Fenêtre de saisie de mot passe du logiciel 7-Zip

Initiation à la bibliothèque GRRLIB

GRRLIB est une bibliothèque graphique codée en langage C pour la conception d’homebrews sur la Wii. Grâce à cette bibliothèque, l’utilisation des fonctions GX n’est plus nécessaire. GX est la bibliothèque graphique de Nintendo qui interface le processeur graphique de la console.

GRRLIB est un projet à code source ouvert qui est hébergé sur le site Web de GitHub. La dernière version stable est disponible en téléchargement sous forme de fichier compressé et la version en développement peut être récupérée sous Subversion.

Pour compiler GRRLIB, il faut d’abord que devkitPro avec les composants devkitPPC, libogc et libfat soit installé sur votre PC. devkitPPC permet la compilation de code pour l’architecture powerpc-eabi. La bibliothèque libogc assure un support pour les fonctions GX et libfat permet l’utilisation du système de fichiers FAT.

Lors de son installation, GRRLIB va installer d’autres bibliothèques telles que libjpeg pour l’utilisation de fichiers JPEG, libpngu qui nécessite libpng et zlib pour l’utilisation d’images PNG et enfin FreeType pour l’utilisation de polices de caractères TrueType.

Pour lancer l’installateur, ouvrez une invite de commandes dans le dossier GRRLIB et tapez la ligne suivante:
make clean all install
Ceci pourrait prendre un peu de temps dépendant de la puissance de votre ordinateur. Quand le défilement de commande cessera, cela voudra dire que tout est installé. Les bibliothèques devraient avoir été copiées dans le dossier C:\devkitPro\libogc\lib\wii et les fichiers d’entête dans le dossier C:\devkitPro\libogc\include.

Pour ceux qui n’ont rien compris au dernier paragraphe, vous pouvez créer un fichier nommé install.bat dans le dossier GRRLIB et y insérer le code suivant:

@echo off
echo Nettoyage de l'installation pr‚c‚dente
make clean -s
echo Compilation du code source
make -s
echo Installation des bibliothŠques dans le dossier de libogc
make install -s

Ensuite, il suffit de double-cliquer sur le fichier pour démarrer l’installation de GRRLIB.

Vous êtes maintenant prêts à commencer à programmer un homebrew pour la Wii à l’aide de GRRLIB. Dans le dossier examples, il y a un projet Programmer’s Notepad qui contient le code minimum pour utiliser GRRLIB. Le projet se nomme template.pnproj et il est situé à l’intérieur du dossier template. Le projet inclut les fichiers main.c et Makefile. Ces fichiers devraient toujours être utilisés pour démarrer la programmation d’un nouveau jeu.

Maintenant, si vous faites Tools / make ou Alt+1, le projet se compilera et les fichiers template.elf et template.dol seront gérés. S’il y a une erreur, c’est que vous n’avez pas suivi les étapes correctement. Je vous conseille donc de lire attentivement le document README.html incorporé dans le téléchargement de GRRLIB. Si le problème persiste, vous pourrez toujours aller poser une question sur le forum officiel de la bibliothèque.

Les images ne sont pas utilisées directement dans GRRLIB. Il faut d’abord les transformer en texture avec la fonction GRRLIB_LoadTexture. Le résultat sera enregistré dans une structure de type GRRLIB_texImg. Il existe aussi des fonctions spécifiques pour les trois types d’image supportés par GRRLIB, soit GRRLIB_LoadTexturePNG, GRRLIB_LoadTextureJPG et GRRLIB_LoadTextureBMP. Si vous avez besoin de plus d’information sur les fonctions incluses dans la bibliothèque, il existe une documentation générée par Doxygen.

Avant d’incorporer une image à un projet, il faut savoir que ses dimensions doivent être inférieures ou égales à 1024 pixels. De plus, elles doivent être des multiples de quatre. Si l’image est un PNG, il faut aussi qu’elle soit en 24 ou 32 bits. Les palettes ne sont pas gérées par PNGU.

Les images peuvent être chargées à partir d’une carte SD avec la fonction GRRLIB_LoadTextureFromFile. Par contre, ce n’est pas cette façon qui sera démontrée ici. Les images seront ajoutées à l’intérieur des fichiers .elf et .dol dans le but d’avoir un seul fichier exécutable. Ceci permettra de tester plus facilement le homebrew dans Dolphin ou de l’exécuter sur la Wii à l’aide de wiiload.

Pour accomplir cette transformation, il existe quelques outils. raw2c est un utilitaire qui fonctionne par ligne de commandes et il est inclus dans le dossier bin de devkitPPC. WiiBuilder est un logiciel fonctionnant sous Windows qui permet de faire la même chose que raw2c en plus de plusieurs autres choses. Il est disponible en téléchargement sur le site Web de WiiBrew. Les avantages sont qu’il ne nécessite pas de lignes de commandes, qu’il donne des avertissements lorsque les images n’ont pas un format approprié et qu’il permet de générer seulement les fichiers d’entête. Le dernier point peut paraître inutile, mais cela fait en sorte que le fichier généré pourra être déplacé dans n’importe quel dossier sans avoir à modifier le Makefile pour ajouter un endroit supplémentaire à la compilation. La modification du fichier Makefile cause souvent des maux de tête au programmeur débutant, c’est pourquoi je ne parlerai pas de l’utilisation de bin2o pour générer un fichier objet qui inclut l’image.

Pour la démonstration, je vais me servir de l’image pirate.png qui se trouve dans le dossier \examples\bitmap_fx\source\gfx. Vous pouvez créer un dossier images à l’intérieur du dossier template dans lequel vous pourrez copier l’image. Si vous utilisez WiiBuilder, il suffit de faire un glisser-déposer de l’image à l’intérieur de la liste de la section File. Un fichier portant le nom de pirate.h devrait avoir été créé. C’est ce fichier qu’il faut ajouter dans main.c.

Passons maintenant à la programmation. Pour afficher l’image, il suffit d’ajouter quatre lignes au code du template. On ajoute à la liste d’entête celui-ci:

#include "../images/pirate.h"

Après la commande GRRLIB_Init, il faut ajouter ceci pour charger la texture:

GRRLIB_texImg *MaTexture = GRRLIB_LoadTexture(pirate);

À l’endroit où il est écrit « Place your drawing code here », on met le code suivant pour dessiner la texture à l’écran:

GRRLIB_DrawImg(100, 100, MaTexture, 0, 1, 1, 0xFFFFFFFF);

Finalement, on ajoute le code suivant à la suite de GRRLIB_Exit pour libérer la mémoire attribuée par GRRLIB_LoadTexture:

GRRLIB_FreeTexture(MaTexture);

Si vous utilisez l’émulateur Dolphin vous devriez avoir le résultat suivant:
L'émulateur Dolphin qui roule un homebrew assez simple

Ce n’est pas ce qu’on pourrait qualifier de meilleur homebrew au monde, mais au moins c’est un point de départ. Si vous êtes intéressés a continuer, allez regarder les autres exemples inclus avec GRRLIB.

Bonne chance dans la programmation de votre jeu révolutionnaire!

Utiliser la bibliothèque MikMod sous C++Builder

Cet article vous expliquera comment compiler la bibliothèque MikMod sous forme de bibliothèque statique (.lib) que vous pourrez intégrer directement dans un programme fait avec C++Builder. Tout d’abord, MikMod est une bibliothèque de programmes codée en langage C qui sert à faire jouer des fichiers module. Cela inclut les fichiers MOD (15 et 31 instruments), S3M (ScreamTracker 3), IT (Impulse Tracker) et XM (FastTracker 2).

La première étape consiste à aller télécharger le code source de la bibliothèque logicielle sur le site Web officiel. Il se trouve dans la section Files de la page. C’est le fichier libmikmod-3.2.0-beta2.zip qui sera utilisé. Il est a noter qu’une version plus récente du code est disponible par CVS sur le site Web de SourceForge.

Une fois le téléchargement terminé, il faut extraire les données dans un dossier que l’on nommera MikMod. Dans C++Builder nous allons créer le nouveau projet en allant dans File / New / Other…. La touche de raccourcis Ctrl+N aurait pu être utilisée. Puis, on sélectionne Static Library et on clique sur OK. Maintenant, nous pouvons sauvegarder notre projet. Pour cela, on va dans File et on sélectionne Save Project As… On choisit comme nom de projet MikMod pour que le fichier lib porte ce nom. L’emplacement du fichier cbproj doit être dans le dossier où les données on été extraites.

Il faut ajouter les fichiers .c au projet.

  • Pour le dossier drivers: "drv_stdout.c" "drv_wav.c" "drv_win.c" "drv_ds.c" "drv_nos.c" "drv_raw.c"
  • Pour le dossier loaders, on sélectionne tous les fichiers.
  • Pour le dossier mmio, on sélectionne tous les fichiers.
  • Pour le dossier playercode, on sélectionne tous les fichiers.

Étant donné que seulement les fichiers .c ont été ajoutés au projet, il manque les chemins d’accès pour certains fichiers .h. Il faut donc aller dans Project / Options… ou Alt+F7. Dans Include path on ajoute: win32\;include\;
Ensuite, dans Conditional defines, on ajoute ceci: WIN32;DRV_WIN;HAVE_FCNTL_H;HAVE_MALLOC_H;HAVE_LIMITS_H;__STDC__=1
Par la suite, on quitte la fenêtre d’option, on choisit la configuration Release et on compile le code. Il se peut qu’il y ait plusieurs avertissements lors de la compilation, mais cela n’empêchera pas le code de fonctionner correctement. Le fichier MikMod.lib devrait être créé dans le dossier Release.

Afin de valider les étapes précédentes, il va falloir créer un nouveau projet indépendant pour être certain que la bibliothèque fonctionnera peu importe l’emplacement du projet. Dans cette nouvelle application VCL, on ajoute un contrôle TTimer et on met la propriété Interval à 100ms. Par la suite, on ajoute au projet les fichiers mikmod_build.h et MikMod.lib.

Dans le constructeur de la Form, on ajoute le code suivant:

	// Enregistrement de la bibliothèque
	MikMod_RegisterDriver(&drv_win);
	MikMod_RegisterAllLoaders();

	// Initialise la bibliothèque
    if(MikMod_Init(""))
    {
        throw(Exception("L'initialisation de MikMod a échouée."));
    }
    else
    {
        // Charge le module
        AnsiString FileName = "..\\music.s3m";
        module = Player_Load(FileName.c_str(), 32, 0);
        if(module)
        {
            module->wrap = true;    // Recommence lorsqu'il finit
            Player_Start(module);   // Démarre la musique
        }
        else
        {
            throw(Exception("Le fichier n'a pu être chargé."));
        }
    }

C’est le pilote pour l’API multimédia de Windows qui est chargé à la ligne 2. On aurait aussi pu utiliser drv_ds pour que le son soit pris en charge par DirectSound. Par contre, si l’on décide de faire ce choix, il va falloir ajouter DRV_DS à la liste des Conditional defines. En plus, il est nécessaire d’ajouter le fichier dsound.lib au projet pour accéder aux fonctions l’API de DirectSound. Si vous êtes intéressé à générer un fichier wav plutôt que de lire un fichier audio, alors c’est le pilote drv_wav qu’il faut utiliser.

À la ligne 13, on trouve la variable FileName, on doit lui affecter le chemin du fichier module que l’on veut faire jouer.

Dans le destructeur, on arrête la musique et on libère la mémoire avec le code suivant:

    Timer1->Enabled = false;
    if(module)
    {
        Player_Stop();
        Player_Free(module);
        MikMod_Exit();
    }

Dans l’évènement OnTimer du TTimer, on ajoute ceci:

    if(module && Player_Active())
    {
        MikMod_Update();
    }

La fonction MikMod_Update doit être appelée sur une base régulière, car elle sert à remplir la mémoire tampon de la sortie audio.

Si vous êtes intéressé à aller un peu plus loin, vous pouvez ajouter un contrôle TTrackBar au projet auquel les propriétés Max et Position devront être initialisées à 128. Dans l’évènement OnChange, ajoutez le code suivant:

    Player_SetVolume(TrackBar1->Position);

Un contrôle de volume est maintenant disponible dans votre logiciel.

Si vous êtes à la recherche de pièces musicales, je vous conseille d’aller faire un tour sur le site Web The Mod Archive.

À présent, vous avez tout ce qu’il faut pour inclure de la musique à vos applications.