<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Karim DRIDI</title>
    <description>The latest articles on DEV Community by Karim DRIDI (@kdridi).</description>
    <link>https://dev.to/kdridi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F114143%2F4fa7a3fe-f590-4c65-a06e-c9434f95cc7d.jpeg</url>
      <title>DEV Community: Karim DRIDI</title>
      <link>https://dev.to/kdridi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kdridi"/>
    <language>en</language>
    <item>
      <title>Hexegesis : construction d’un programme PlayStation 3 minimal</title>
      <dc:creator>Karim DRIDI</dc:creator>
      <pubDate>Mon, 07 Sep 2026 22:03:48 +0000</pubDate>
      <link>https://dev.to/kdridi/hexegesis-construction-dun-programme-playstation-3-minimal-4nn7</link>
      <guid>https://dev.to/kdridi/hexegesis-construction-dun-programme-playstation-3-minimal-4nn7</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a"&gt;← Revenir au sommaire de la série&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Construire un cas minimal et reproductible
&lt;/h2&gt;

&lt;p&gt;Sur un ordinateur courant, un programme Hello World peut généralement être compilé et lancé en deux commandes. Sur PlayStation 3, le code C reste élémentaire, mais son exécution demande une chaîne beaucoup plus large : un compilateur croisé pour le PPU, les bibliothèques et les outils propres à la PS3, un exécutable au format SELF, un fichier de paramètres, un descripteur du support, une arborescence de jeu sur disque et, pour le chargement d’une image ISO dans RPCS3, une table de régions supplémentaire.&lt;/p&gt;

&lt;p&gt;Les informations nécessaires se trouvent dans plusieurs sources complémentaires : &lt;code&gt;ps3toolchain&lt;/code&gt; et PSL1GHT fournissent les outils et les règles de construction, la documentation communautaire décrit les formats, et le code source de RPCS3 révèle les contrôles effectués au chargement. Cet article réunit ces éléments dans une même procédure. Le problème traité n’est donc pas l’écriture du programme lui-même, mais l’identification, la compréhension et l’assemblage de tout ce qui permet de le lancer.&lt;/p&gt;

&lt;p&gt;Nous procéderons par soustraction. Nous conserverons uniquement les outils, les transformations et les fichiers nécessaires au cas vérifié dans RPCS3. Docker fournira un environnement de construction reproductible ; les commandes seront exécutées explicitement afin de rendre visible le rôle de chaque étape. Nous essaierons d’abord le répertoire de jeu, puis son image ISO, de manière à isoler les erreurs éventuelles.&lt;/p&gt;

&lt;p&gt;Le résultat sera un exemple synthétique et réutilisable : un programme PPU qui écrit &lt;code&gt;Bonjour depuis Hexegesis.&lt;/code&gt;, accompagné de &lt;code&gt;PARAM.SFO&lt;/code&gt;, de &lt;code&gt;PS3_DISC.SFB&lt;/code&gt; et d’&lt;code&gt;EBOOT.BIN&lt;/code&gt;, puis placé dans une image ISO 9660 que la version testée de RPCS3 peut lancer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sommaire
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Le résultat attendu&lt;/li&gt;
&lt;li&gt;Construire l’image Docker&lt;/li&gt;
&lt;li&gt;Créer les paramètres du jeu : &lt;code&gt;PARAM.SFO&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Créer le descripteur de disque minimal : &lt;code&gt;PS3_DISC.SFB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Compiler le programme et créer &lt;code&gt;EBOOT.BIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Vérifier le répertoire de jeu dans RPCS3&lt;/li&gt;
&lt;li&gt;Créer une image ISO 9660&lt;/li&gt;
&lt;li&gt;Ajouter la table de régions PS3&lt;/li&gt;
&lt;li&gt;Bilan&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Le résultat attendu
&lt;/h2&gt;

&lt;p&gt;Le répertoire de jeu construit dans cet article possède l’arborescence suivante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disc-root/
├── PS3_DISC.SFB
└── PS3_GAME/
    ├── PARAM.SFO
    └── USRDIR/
        └── EBOOT.BIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;RPCS3 sait démarrer des jeux organisés de cette manière. Son code source constitue ici une référence particulièrement utile, car il révèle les chemins recherchés et les contrôles effectués lors du chargement. Le &lt;a href="https://www.psdevwiki.com/ps3/Template:PS3_Disc_Game_Structure" rel="noopener noreferrer"&gt;PS3 Developer Wiki décrit également l’arborescence habituelle d’un jeu sur disque&lt;/a&gt;, avec davantage de fichiers que notre exemple minimal.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;PS3_GAME/USRDIR/EBOOT.BIN&lt;/code&gt; — le programme
&lt;/h3&gt;

&lt;p&gt;C’est le programme lancé lorsque le jeu démarre. Dans un jeu plus riche, &lt;code&gt;USRDIR&lt;/code&gt; contient aussi les ressources utilisées par ce programme : données, sons, textures, scripts ou fichiers de configuration.&lt;/p&gt;

&lt;p&gt;Dans notre exemple, &lt;code&gt;EBOOT.BIN&lt;/code&gt; contient du code destiné au PPU (&lt;em&gt;Power Processing Unit&lt;/em&gt;), l’unité de traitement principale du processeur Cell. &lt;code&gt;ppu-gcc&lt;/code&gt; produit d’abord un exécutable intermédiaire au format &lt;a href="https://fr.wikipedia.org/wiki/Executable_and_Linkable_Format" rel="noopener noreferrer"&gt;ELF (&lt;em&gt;Executable and Linkable Format&lt;/em&gt;)&lt;/a&gt; ; les outils PS3 construisent ensuite, à partir de cet ELF, un fichier exécutable au format SELF. Ce fichier est enregistré sous le nom conventionnel &lt;code&gt;EBOOT.BIN&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;PS3_GAME/PARAM.SFO&lt;/code&gt; — les paramètres du jeu
&lt;/h3&gt;

&lt;p&gt;Ce fichier rassemble les paramètres qui permettent d’identifier et de présenter le jeu : titre affiché, identifiant du titre, catégorie, version et capacités annoncées.&lt;/p&gt;

&lt;p&gt;Notre fichier synthétique contient notamment le titre &lt;code&gt;Bonjour depuis Hexegesis&lt;/code&gt;, l’identifiant &lt;code&gt;HXGS00001&lt;/code&gt;, la catégorie &lt;code&gt;DG&lt;/code&gt;, ainsi que les valeurs &lt;code&gt;01.00&lt;/code&gt; pour &lt;code&gt;APP_VER&lt;/code&gt; et &lt;code&gt;VERSION&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;PS3_DISC.SFB&lt;/code&gt; — le descripteur du support
&lt;/h3&gt;

&lt;p&gt;Ce fichier se place à la racine d’un jeu sur disque et participe à l’identification du support.&lt;/p&gt;

&lt;p&gt;Dans notre exemple minimal, ce fichier se réduit à la signature &lt;code&gt;.SFB&lt;/code&gt;. Nous reviendrons sur cette simplification lorsque nous le créerons.&lt;/p&gt;

&lt;p&gt;Cette arborescence suggère déjà le déroulement du travail :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;programme C
→ exécutable PPU au format ELF
→ programme PS3 au format SELF, enregistré comme EBOOT.BIN

métadonnées lisibles
→ PARAM.SFO binaire

signature minimale du disque
→ PS3_DISC.SFB

ces trois fichiers
→ répertoire de jeu que RPCS3 peut démarrer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le fichier central est &lt;code&gt;EBOOT.BIN&lt;/code&gt; : sans lui, il n’y a pas de programme à exécuter. &lt;code&gt;PARAM.SFO&lt;/code&gt; et &lt;code&gt;PS3_DISC.SFB&lt;/code&gt; donnent toutefois au répertoire le contexte nécessaire pour être traité comme un jeu sur disque plutôt que comme une simple collection de fichiers.&lt;/p&gt;

&lt;p&gt;Nous allons maintenant construire chaque fichier pas à pas. La &lt;a href="https://github.com/hexegesis/hexegesis-experiments/tree/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world" rel="noopener noreferrer"&gt;version du projet utilisée pour cet article est disponible sur GitHub&lt;/a&gt;. Elle contient également un &lt;a href="https://github.com/hexegesis/hexegesis-experiments/blob/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world/scripts/build.sh" rel="noopener noreferrer"&gt;script qui automatise la construction&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Construire l’image Docker
&lt;/h2&gt;

&lt;p&gt;La construction repose sur trois ensembles d’outils :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ps3dev/ps3toolchain/tree/e98d2ed5a5f58273ffce17d860f02af8f19eb9d9" rel="noopener noreferrer"&gt;&lt;code&gt;ps3toolchain&lt;/code&gt;&lt;/a&gt; installe &lt;code&gt;ppu-gcc&lt;/code&gt;, le compilateur croisé qui produit des fichiers ELF destinés au PPU, ainsi que &lt;code&gt;ppu-strip&lt;/code&gt;, qui en retire les informations inutiles à l’exécution ;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ps3dev/PSL1GHT/tree/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe" rel="noopener noreferrer"&gt;PSL1GHT&lt;/a&gt; ajoute les fichiers d’en-tête et les bibliothèques qui permettent au code C d’appeler les services de la PS3. Il fournit également &lt;code&gt;sfo&lt;/code&gt;, qui crée &lt;code&gt;PARAM.SFO&lt;/code&gt;, &lt;code&gt;sprxlinker&lt;/code&gt;, qui prépare l’ELF pour la conversion, et &lt;code&gt;make_self&lt;/code&gt;, qui produit le SELF ;&lt;/li&gt;
&lt;li&gt;les utilitaires usuels terminent la préparation du disque : &lt;code&gt;printf&lt;/code&gt; crée le fichier minimal &lt;code&gt;PS3_DISC.SFB&lt;/code&gt;, &lt;code&gt;xorriso&lt;/code&gt; place l’arborescence dans une image ISO 9660 générique, puis &lt;code&gt;printf&lt;/code&gt; et &lt;code&gt;dd&lt;/code&gt; écrivent les octets supplémentaires attendus par RPCS3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nous réunirons tous ces outils dans une même image Docker.&lt;/p&gt;

&lt;p&gt;Récupérons le projet, puis plaçons-nous dans son répertoire :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hexegesis/hexegesis-experiments.git
&lt;span class="nb"&gt;cd &lt;/span&gt;hexegesis-experiments
git checkout 8ca7eb9c02f36cc42c26b157f6013c68c56616e1
&lt;span class="nb"&gt;cd &lt;/span&gt;hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si le dépôt est déjà présent sur votre machine, ignorez &lt;code&gt;git clone&lt;/code&gt;, placez-vous à sa racine et exécutez &lt;code&gt;git fetch origin&lt;/code&gt; avant les deux dernières commandes.&lt;/p&gt;

&lt;p&gt;Le &lt;a href="https://github.com/hexegesis/hexegesis-experiments/blob/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world/Dockerfile.ps3dev" rel="noopener noreferrer"&gt;&lt;code&gt;Dockerfile.ps3dev&lt;/code&gt;&lt;/a&gt; construit les révisions indiquées de &lt;code&gt;ps3toolchain&lt;/code&gt;, PSL1GHT et &lt;code&gt;ps3libraries&lt;/code&gt;, puis installe &lt;code&gt;xorriso&lt;/code&gt; et &lt;code&gt;xxd&lt;/code&gt;. Construisons l’image :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; hexegesis-ps3dev &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--file&lt;/span&gt; Dockerfile.ps3dev &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La première construction télécharge et compile la chaîne PPU ; elle peut donc prendre plusieurs minutes. Les constructions suivantes réutilisent normalement le cache Docker.&lt;/p&gt;

&lt;p&gt;Ouvrons ensuite un shell dans cette image. Le répertoire courant de l’hôte est monté dans &lt;code&gt;/work&lt;/code&gt; : tout fichier créé dans &lt;code&gt;/work&lt;/code&gt; depuis le conteneur restera dans notre répertoire de travail après la sortie du shell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/work"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workdir&lt;/span&gt; /work &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; /bin/sh &lt;span class="se"&gt;\&lt;/span&gt;
  hexegesis-ps3dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L’invite est maintenant celle du conteneur. Toutes les commandes qui suivent sont exécutées dans ce shell ; nous ne répéterons plus &lt;code&gt;docker run&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd

&lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;tool &lt;span class="k"&gt;in &lt;/span&gt;ppu-gcc ppu-strip sprxlinker make_self sfo xorriso xxd&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tool&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La première commande affiche &lt;code&gt;/work&lt;/code&gt;. La seconde affiche un chemin pour chaque outil. Nous pouvons donc commencer à créer les fichiers. Nous laisserons ce shell ouvert jusqu’à la création et à la vérification de l’image ISO ; &lt;code&gt;--rm&lt;/code&gt; supprimera ensuite le conteneur temporaire, mais pas les fichiers du volume monté.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Créer les paramètres du jeu : &lt;code&gt;PARAM.SFO&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PARAM.SFO&lt;/code&gt; contient les métadonnées du jeu. Chaque entrée associe le nom d’un paramètre, comme &lt;code&gt;TITLE&lt;/code&gt; ou &lt;code&gt;TITLE_ID&lt;/code&gt;, à une valeur typée. RPCS3 lit ce fichier pour obtenir notamment le titre affiché, l’identifiant, la catégorie et la version du jeu.&lt;/p&gt;

&lt;p&gt;Le &lt;a href="https://www.psdevwiki.com/ps3/PARAM.SFO" rel="noopener noreferrer"&gt;PS3 Developer Wiki décrit sa structure&lt;/a&gt; comme un en-tête suivi d’une table d’entrées, d’une table de clés et d’une table de valeurs. L’outil &lt;code&gt;sfo&lt;/code&gt; installé par PSL1GHT produit cette structure à partir d’un fichier XML lisible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Déclarer les paramètres
&lt;/h3&gt;

&lt;p&gt;Créons le fichier &lt;a href="https://github.com/hexegesis/hexegesis-experiments/blob/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world/param-sfo.xml" rel="noopener noreferrer"&gt;&lt;code&gt;param-sfo.xml&lt;/code&gt;&lt;/a&gt; avec le contenu suivant :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;sfo&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Version de l'application. La forme attendue est NN.NN. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"APP_VER"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;01.00&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Drapeaux d'attributs optionnels. La valeur 0 n'en active aucun. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ATTRIBUTE"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Indique si le contenu est amorçable : 1 pour oui, 0 pour non. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"BOOTABLE"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Nature du contenu. DG désigne un jeu sur disque. --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- RPCS3 attend précisément DG lorsqu'il reconnaît ce répertoire comme jeu disque. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"CATEGORY"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;DG&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Niveau de contrôle parental, sur une échelle communautairement documentée de 0 à 11. --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Nous conservons la valeur 0 du modèle fourni par PSL1GHT. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"PARENTAL_LEVEL"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Version minimale du logiciel système annoncée par le jeu, sous la forme NN.NNNN. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"PS3_SYSTEM_VER"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;01.8000&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Drapeaux des modes vidéo annoncés. --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- 63 = 1 + 2 + 4 + 8 + 16 + 32 : tous les drapeaux définis par RPCS3 sont activés. --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Ils correspondent à 480, 576, 720, 1080, 480 au format 16:9 et 576 au format 16:9. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"RESOLUTION"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;63&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Masque des formats audio annoncés au système. --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- La valeur 1 correspond uniquement au mode LPCM 2.0. --&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- Le programme n'utilise aucune API audio et n'annonce donc aucun mode multicanal. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"SOUND_FORMAT"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Titre lisible du jeu. Des clés TITLE_00, TITLE_01, etc. peuvent fournir des traductions. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"TITLE"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bonjour depuis Hexegesis&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Identifiant du titre. HXGS00001 est un identifiant synthétique de neuf caractères. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"TITLE_ID"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;HXGS00001&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Version principale déclarée pour le contenu, distincte de APP_VER. --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"VERSION"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;01.00&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/sfo&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chaque balise &lt;code&gt;&amp;lt;value&amp;gt;&lt;/code&gt; contient trois informations. Prenons cet exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;value&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"BOOTABLE"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/value&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ici, &lt;code&gt;name="BOOTABLE"&lt;/code&gt; nomme le paramètre, &lt;code&gt;type="integer"&lt;/code&gt; indique qu’il s’agit d’un entier et &lt;code&gt;1&lt;/code&gt; en fournit la valeur. Pour &lt;code&gt;TITLE&lt;/code&gt;, le type &lt;code&gt;string&lt;/code&gt; désigne une chaîne de caractères.&lt;/p&gt;

&lt;h3&gt;
  
  
  Produire le fichier binaire
&lt;/h3&gt;

&lt;p&gt;Créons le répertoire de destination, puis demandons à &lt;code&gt;sfo&lt;/code&gt; de convertir le XML :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; disc-root/PS3_GAME
sfo &lt;span class="nt"&gt;--fromxml&lt;/span&gt; param-sfo.xml disc-root/PS3_GAME/PARAM.SFO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vérifions que l’outil peut relire le fichier produit :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sfo &lt;span class="nt"&gt;--list&lt;/span&gt; disc-root/PS3_GAME/PARAM.SFO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie affiche les onze paramètres du XML. Vérifions notamment que &lt;code&gt;CATEGORY&lt;/code&gt; vaut &lt;code&gt;DG&lt;/code&gt;, que &lt;code&gt;TITLE&lt;/code&gt; vaut &lt;code&gt;Bonjour depuis Hexegesis&lt;/code&gt; et que &lt;code&gt;TITLE_ID&lt;/code&gt; vaut &lt;code&gt;HXGS00001&lt;/code&gt;. Cette relecture confirme que l’outil &lt;code&gt;sfo&lt;/code&gt; retrouve les paramètres et les valeurs choisis ; l’essai dans RPCS3 vérifiera séparément que l’émulateur accepte le fichier. L’étude de l’en-tête, des tables et des offsets internes de &lt;code&gt;PARAM.SFO&lt;/code&gt; dépasse l’objectif de ce tutoriel.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Créer le descripteur de disque minimal : &lt;code&gt;PS3_DISC.SFB&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Le fichier &lt;code&gt;PS3_DISC.SFB&lt;/code&gt; se trouve à la racine du disque, tandis que &lt;code&gt;PARAM.SFO&lt;/code&gt; se trouve dans &lt;code&gt;PS3_GAME&lt;/code&gt;. Dans leur forme complète, ces fichiers ont des rôles distincts : le premier décrit le support et son contenu ; le second décrit le jeu lui-même. Le &lt;a href="https://www.psdevwiki.com/ps3/PS3_DISC.SFB" rel="noopener noreferrer"&gt;PS3 Developer Wiki&lt;/a&gt; documente un descripteur plus complet, avec des enregistrements et des drapeaux qui peuvent notamment désigner des paramètres du titre.&lt;/p&gt;

&lt;p&gt;Notre objectif est plus modeste : créer le marqueur minimal accepté par la vérification étudiée dans RPCS3. &lt;code&gt;printf&lt;/code&gt; écrit la chaîne &lt;code&gt;.SFB&lt;/code&gt; sans ajouter de retour à la ligne :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'.SFB'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; disc-root/PS3_DISC.SFB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le fichier obtenu contient uniquement ces quatre caractères. Il ne contient aucune des tables supplémentaires décrites pour un descripteur complet.&lt;/p&gt;

&lt;p&gt;Cette simplification est volontaire. Dans la révision de RPCS3 utilisée pour cette expérience, &lt;a href="https://github.com/RPCS3/rpcs3/blob/c6e96729cc80f9cf0bb5239679877bbfd832599e/rpcs3/Emu/System.cpp#L4971-L4987" rel="noopener noreferrer"&gt;&lt;code&gt;IsValidSfb&lt;/code&gt;&lt;/a&gt; exige que &lt;code&gt;PS3_DISC.SFB&lt;/code&gt; existe, comporte au moins quatre octets et commence par le préfixe &lt;code&gt;.SFB&lt;/code&gt;. Le &lt;a href="https://github.com/RPCS3/rpcs3/blob/c6e96729cc80f9cf0bb5239679877bbfd832599e/rpcs3/Emu/System.cpp#L2134-L2145" rel="noopener noreferrer"&gt;démarrage d’un répertoire de jeu sur disque appelle cette vérification&lt;/a&gt;. Notre fichier satisfait ce contrôle de l’émulateur. Cette construction ne vise ni à reproduire le descripteur d’un disque commercial ni à établir une compatibilité avec une console physique.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiler le programme et créer &lt;code&gt;EBOOT.BIN&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Le fichier de paramètres et le descripteur du support existent maintenant. Il reste à produire le programme qu’ils accompagnent.&lt;/p&gt;

&lt;p&gt;Créons les répertoires nécessaires au code source, à l’ELF préparé et au programme final :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nb"&gt;source &lt;/span&gt;build disc-root/PS3_GAME/USRDIR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Créons ensuite &lt;a href="https://github.com/hexegesis/hexegesis-experiments/blob/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world/source/main.c" rel="noopener noreferrer"&gt;&lt;code&gt;source/main.c&lt;/code&gt;&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/*
 * Écrit une ligne sur la sortie standard. La bibliothèque librt de PSL1GHT
 * transmet cette sortie au TTY de la PS3, que RPCS3 enregistre dans son journal.
 */&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* puts ajoute automatiquement un retour à la ligne après le message. */&lt;/span&gt;
    &lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bonjour depuis Hexegesis."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/* Indique au système que le programme s'est terminé normalement. */&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;EXIT_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le sigle &lt;code&gt;TTY&lt;/code&gt;, hérité des &lt;a href="https://fr.wikipedia.org/wiki/T%C3%A9l%C3%A9scripteur" rel="noopener noreferrer"&gt;téléscripteurs&lt;/a&gt;, désigne ici la sortie textuelle émulée de la PS3, que RPCS3 consigne dans son journal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compiler le code C et produire l’ELF PPU
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;ppu-gcc&lt;/code&gt; est un &lt;a href="https://fr.wikipedia.org/wiki/Compilateur#Compilation_crois%C3%A9e" rel="noopener noreferrer"&gt;compilateur croisé&lt;/a&gt; : il s’exécute dans le conteneur, mais produit du code destiné au PPU. Commençons par compiler le code source C en fichier objet :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ppu-gcc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mcpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cell &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mhard-float&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt;/main.c &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt;/main.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voici le rôle de chaque option :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-mcpu=cell&lt;/code&gt; demande au compilateur de produire du code pour le processeur PPU du Cell ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-mhard-float&lt;/code&gt; demande l’utilisation de l’unité matérielle de calcul en virgule flottante du PPU ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-O2&lt;/code&gt; active les optimisations usuelles ; cette option n’est pas indispensable à un programme aussi simple, mais correspond à un niveau d’optimisation courant ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-Wall&lt;/code&gt; active les avertissements courants du compilateur et n’affecte pas le format du fichier produit ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c&lt;/code&gt; arrête la construction après la compilation, avant l’édition de liens ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-o source/main.o&lt;/code&gt; nomme le fichier objet obtenu.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Procédons ensuite à l’édition de liens pour produire un exécutable PPU au format ELF :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ppu-gcc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mcpu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cell &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-mhard-float&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;source&lt;/span&gt;/main.o &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-L&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PSL1GHT&lt;/span&gt;&lt;span class="s2"&gt;/ppu/lib"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-lrt&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-llv2&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; hello-world.elf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ces options complètent l’édition de liens :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-L"$PSL1GHT/ppu/lib"&lt;/code&gt; ajoute le répertoire contenant les bibliothèques de PSL1GHT aux chemins parcourus par l’éditeur de liens ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-lrt&lt;/code&gt; lie &lt;code&gt;librt&lt;/code&gt;. Lorsque &lt;code&gt;puts&lt;/code&gt; écrit sur &lt;code&gt;stdout&lt;/code&gt;, l’&lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/ppu/librt/write.c" rel="noopener noreferrer"&gt;implémentation de &lt;code&gt;write&lt;/code&gt;&lt;/a&gt; fournie par cette bibliothèque appelle &lt;code&gt;sysTtyWrite&lt;/code&gt;. Le message est ainsi envoyé au TTY de la PS3, que RPCS3 consigne dans son journal ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-llv2&lt;/code&gt; lie &lt;code&gt;liblv2&lt;/code&gt;, la bibliothèque de base utilisée pour interagir avec LV2, le noyau système de la PS3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L’ordre &lt;code&gt;-lrt -llv2&lt;/code&gt; reprend celui du &lt;a href="https://github.com/hexegesis/hexegesis-experiments/blob/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world/Makefile" rel="noopener noreferrer"&gt;Makefile du projet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hello-world.elf&lt;/code&gt; est l’exécutable PPU intermédiaire. Il doit encore être transformé en SELF avant de prendre la place de &lt;code&gt;EBOOT.BIN&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transformer l’ELF en SELF
&lt;/h3&gt;

&lt;p&gt;L’ELF ne peut pas encore servir directement d’&lt;code&gt;EBOOT.BIN&lt;/code&gt;. Reproduisons les trois commandes employées par les &lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/ppu_rules" rel="noopener noreferrer"&gt;règles de construction de PSL1GHT&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ppu-strip hello-world.elf &lt;span class="nt"&gt;-o&lt;/span&gt; build/hello-world.elf
sprxlinker build/hello-world.elf
make_self build/hello-world.elf hello-world.self
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lors de la commande précédente, &lt;code&gt;ppu-gcc&lt;/code&gt; a déjà piloté l’éditeur de liens : les fichiers objets et les bibliothèques ont été réunis, leurs symboles ont été résolus et l’ELF exécutable a été produit. Les trois commandes suivantes accomplissent donc des transformations différentes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ppu-strip&lt;/code&gt; crée &lt;code&gt;build/hello-world.elf&lt;/code&gt; à partir de l’ELF original, puis supprime de cette copie la table des symboles et les autres informations inutiles à l’exécution ;&lt;/li&gt;
&lt;li&gt;malgré son nom, &lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/tools/sprxlinker/linker.c#L119-L170" rel="noopener noreferrer"&gt;&lt;code&gt;sprxlinker&lt;/code&gt;&lt;/a&gt; ne réalise pas une seconde édition de liens. Il effectue dans l’ELF déjà lié les ajustements propres à la PS3 : il l’identifie comme un exécutable Cell LV2 et complète les informations relatives aux bibliothèques importées et aux fonctions du PPU. L’ELF est alors prêt à être transformé en SELF ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make_self&lt;/code&gt; ne recompile pas le programme. Il construit un fichier SELF à partir des en-têtes et des segments de l’ELF préparé, &lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/tools/geohot/make_self.c#L111-L156" rel="noopener noreferrer"&gt;ajoute les métadonnées propres à ce format&lt;/a&gt;, puis &lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/tools/geohot/make_self.c#L177-L267" rel="noopener noreferrer"&gt;compresse et chiffre les segments&lt;/a&gt; avant d’&lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/tools/geohot/make_self.c#L464-L577" rel="noopener noreferrer"&gt;ajouter les informations d’intégrité des segments et la signature du SELF&lt;/a&gt;. Les instructions produites par GCC ne sont pas recompilées.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PSL1GHT ne fournit pas de manuel distinct pour &lt;code&gt;sprxlinker&lt;/code&gt; et &lt;code&gt;make_self&lt;/code&gt;. Leur rôle est établi ici à partir des &lt;a href="https://github.com/ps3dev/PSL1GHT/blob/f649a08fd536a9e27c08c7db2d93a2d7ee4c3bbe/ppu_rules#L56-L61" rel="noopener noreferrer"&gt;règles de construction du projet&lt;/a&gt; et de leur code source.&lt;/p&gt;

&lt;p&gt;Le recours à une copie allégée permet de produire le SELF tout en conservant l’ELF original. Pour effectuer un débogage au niveau du code source, il faudrait également compiler cet original avec &lt;code&gt;-g&lt;/code&gt; afin d’y ajouter les informations de débogage ; ce n’est pas l’objectif de ce tutoriel.&lt;/p&gt;

&lt;p&gt;Nous disposons maintenant du fichier SELF produit par PSL1GHT à partir de l’ELF. Copions-le à l’emplacement attendu pour &lt;code&gt;EBOOT.BIN&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;hello-world.self disc-root/PS3_GAME/USRDIR/EBOOT.BIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous pouvons enfin regarder l’arborescence complète :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find disc-root &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-print&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;disc-root/PS3_DISC.SFB
disc-root/PS3_GAME/PARAM.SFO
disc-root/PS3_GAME/USRDIR/EBOOT.BIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les trois fichiers présentés au début existent désormais au bon emplacement.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Vérifier le répertoire de jeu dans RPCS3
&lt;/h2&gt;

&lt;p&gt;Les essais de ce tutoriel ont été réalisés avec &lt;code&gt;RPCS3 Version: 0.0.42-19895-c6e96729 Alpha | master | local_build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Le volume monté rend les fichiers de &lt;code&gt;disc-root/&lt;/code&gt; immédiatement accessibles sur l’hôte. Laissons le shell Docker ouvert et passons à RPCS3.&lt;/p&gt;

&lt;p&gt;Dans RPCS3, choisissez &lt;strong&gt;File → Boot Game&lt;/strong&gt; puis sélectionnez le dossier &lt;code&gt;disc-root&lt;/code&gt;. Cette vérification permet d’isoler les problèmes liés au programme ou à l’arborescence avant d’introduire ceux que pourraient causer la création de l’image ISO ou sa table de régions.&lt;/p&gt;

&lt;p&gt;RPCS3 lance alors le programme. Dans son journal TTY, vous pouvez lire le message suivant :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bonjour depuis Hexegesis.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette observation établit le comportement de la version de RPCS3 testée avec cette arborescence synthétique. Elle ne démontre pas que le descripteur SFB minimal ou le SELF produit par la chaîne de développement seraient acceptés par une console physique.&lt;/p&gt;

&lt;p&gt;Revenons maintenant au shell Docker, resté ouvert, pour créer l’image ISO.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Créer une image ISO 9660
&lt;/h2&gt;

&lt;p&gt;Une image ISO représente le contenu d’un disque sous la forme d’une suite de secteurs. Nous demandons à &lt;code&gt;xorriso&lt;/code&gt; d’y créer un système de fichiers ISO 9660 à partir du répertoire déjà vérifié, en reprenant les mêmes trois fichiers sans modifier leur contenu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xorriso &lt;span class="nt"&gt;-as&lt;/span&gt; mkisofs &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-iso-level&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-volid&lt;/span&gt; HXGS00001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-no-pad&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-output&lt;/span&gt; hello-world.iso &lt;span class="se"&gt;\&lt;/span&gt;
  disc-root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les arguments donnés à &lt;code&gt;xorriso&lt;/code&gt; ont les rôles suivants :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-as mkisofs&lt;/code&gt; sélectionne l’interface de création compatible avec &lt;code&gt;mkisofs&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/xorriso/man_1_xorrisofs.html" rel="noopener noreferrer"&gt;&lt;code&gt;-iso-level 1&lt;/code&gt;&lt;/a&gt; applique aux noms ISO 9660 les contraintes du niveau d’échange 1 : les noms de répertoires sont limités à huit caractères et les noms de fichiers suivent la forme « 8.3 », soit au plus huit caractères avant le point et trois pour l’extension. Le &lt;a href="https://fr.wikipedia.org/wiki/ISO_9660#Niveaux_et_restrictions" rel="noopener noreferrer"&gt;résumé de Wikipédia&lt;/a&gt; présente également ces restrictions ; tous les noms de notre arborescence les respectent ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-volid HXGS00001&lt;/code&gt; inscrit l’identifiant du volume ; nous choisissons ici la même valeur que &lt;code&gt;TITLE_ID&lt;/code&gt;, même si les deux champs sont indépendants ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-no-pad&lt;/code&gt; désactive les 300 Kio de remplissage que &lt;code&gt;xorriso&lt;/code&gt; ajoute normalement à la fin de l’image ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-output hello-world.iso&lt;/code&gt; nomme l’image produite ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;disc-root&lt;/code&gt; est le répertoire à placer dans l’image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vérifions l’inventaire sans extraire les fichiers :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xorriso &lt;span class="nt"&gt;-indev&lt;/span&gt; hello-world.iso &lt;span class="nt"&gt;-find&lt;/span&gt; / &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; lsdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parmi les lignes affichées, vérifiez la présence des trois chemins suivants :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/PS3_DISC.SFB
/PS3_GAME/PARAM.SFO
/PS3_GAME/USRDIR/EBOOT.BIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ISO 9660 divise l’image en secteurs logiques de 2 048 octets, numérotés à partir de zéro. Le numéro d’un secteur est son adresse logique de bloc, ou LBA (&lt;em&gt;Logical Block Address&lt;/em&gt;). Un offset indique une position en octets depuis le début du fichier : la LBA 16 commence donc à l’offset &lt;code&gt;16 × 2048 = 32768&lt;/code&gt;, soit &lt;code&gt;0x8000&lt;/code&gt;. Les secteurs de LBA 0 à 15 constituent la zone système (&lt;em&gt;System Area&lt;/em&gt;) ; la séquence des descripteurs de volume commence à la LBA 16. Pour approfondir ces notions, consultez &lt;a href="https://dev.to/kdridi/dune-image-nommee-iso-a-un-premier-probe-en-haskell-41ia"&gt;l’article consacré à l’exploration d’une image ISO PlayStation 3&lt;/a&gt;, qui détaille la relation entre les LBA, les offsets et les descripteurs de volume.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xxd&lt;/code&gt; affiche une portion d’un fichier sous forme hexadécimale sans le modifier. Vérifions que &lt;code&gt;xorriso&lt;/code&gt; a placé l’en-tête attendu à la LBA 16 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; 7 hello-world.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008000: 01 43 44 30 30 31 01                             .CD001.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les octets &lt;code&gt;01 CD001 01&lt;/code&gt; identifient le descripteur principal de volume à l’emplacement attendu. Nous pourrons donc écrire la table de régions dans la zone système sans empiéter sur ce descripteur.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Ajouter la table de régions PS3
&lt;/h2&gt;

&lt;p&gt;Le mot « région » ne désigne pas ici une zone géographique, mais un intervalle inclusif de LBA. Une région non protégée contient des secteurs non chiffrés, tandis qu’une région protégée contient des secteurs chiffrés. La table placée au début de l’image donne les bornes de ces régions.&lt;/p&gt;

&lt;p&gt;D’où vient cette structure ? &lt;a href="https://ecma-international.org/publications-and-standards/standards/ecma-119/" rel="noopener noreferrer"&gt;ECMA-119&lt;/a&gt; réserve les seize premiers secteurs d’une image ISO 9660 à la zone système, mais ne définit pas leur usage par la PlayStation 3. Nous ne disposons pas d’une spécification publique de Sony à citer pour cette table. Sa description repose donc sur des documents et des implémentations indépendantes issues de la communauté PS3 :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l’annexe A du &lt;a href="https://www.infinitydream.com/downloads/PS3/3k3y_iso_tools_user_manual_v1.0.pdf" rel="noopener noreferrer"&gt;manuel de 3k3y ISO Tools&lt;/a&gt; explique l’alternance entre régions non chiffrées et chiffrées ;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/13xforever/ps3-disc-dumper/blob/0fc0c8658d83517d8b96efc47ec364b51c76b7b2/IrdLibraryClient/IrdFormat/IsoHeaderParser.cs#L83-L102" rel="noopener noreferrer"&gt;&lt;code&gt;IsoHeaderParser.GetUnprotectedRegions&lt;/code&gt;&lt;/a&gt;, dans PS3 Disc Dumper, lit en ordre big-endian — octet de poids fort en premier — un nombre de régions non protégées, un entier de 32 bits dont le rôle n’est pas nommé, puis les paires de LBA de début et de fin ;&lt;/li&gt;
&lt;li&gt;le &lt;a href="https://github.com/RPCS3/rpcs3/blob/c6e96729cc80f9cf0bb5239679877bbfd832599e/rpcs3/Loader/ISO.cpp#L419-L442" rel="noopener noreferrer"&gt;lecteur ISO de RPCS3&lt;/a&gt; lit le même compteur et reconstruit l’alternance des régions. C’est cette dernière implémentation qui définit directement ce que notre ISO doit contenir pour être chargée par la version testée de RPCS3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notre image synthétique n’est pas chiffrée. Elle ne contient donc qu’une région non protégée, de la LBA &lt;code&gt;0&lt;/code&gt; à la dernière LBA de l’image. Les seize premiers octets doivent décrire ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;offset 0x00 : nombre de régions non protégées = 1
offset 0x04 : champ non interprété = 0
offset 0x08 : première LBA = 0
offset 0x0c : dernière LBA = dernière LBA de l’image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vérifions la valeur actuelle de ces seize octets :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 4 &lt;span class="nt"&gt;-l&lt;/span&gt; 16 hello-world.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000: 00000000 00000000 00000000 00000000  ................
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;xorriso&lt;/code&gt; les a laissés à zéro. La première LBA et le champ non interprété ont donc déjà la valeur souhaitée pour cette expérience ; nous devons seulement écrire le compteur à l’offset &lt;code&gt;0&lt;/code&gt; et la dernière LBA à l’offset &lt;code&gt;12&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Commençons par calculer cette dernière LBA. La taille étant un multiple de 2 048, le nombre de secteurs est la taille divisée par 2 048, et la dernière LBA est ce nombre moins un :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;sector_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2048
&lt;span class="nv"&gt;image_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &amp;lt; hello-world.iso | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;sector_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;image_size &lt;span class="o"&gt;/&lt;/span&gt; sector_size&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nv"&gt;last_lba&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;sector_count &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'octets=%s\nsecteurs=%s\nderniere-lba=%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$image_size&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sector_count&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_lba&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans la construction observée pour cet article, l’image comporte 61 secteurs et sa dernière LBA vaut 60. Nous devons écrire &lt;code&gt;1&lt;/code&gt; et &lt;code&gt;60&lt;/code&gt; comme entiers de 32 bits en ordre big-endian. Cette petite fonction shell encode un entier en quatre octets dans cet ordre :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;write_be32&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
  &lt;span class="nv"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
  &lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\\%03o\\%03o\\%03o\\%03o'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;value &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;value &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;value &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="k"&gt;$((&lt;/span&gt;value &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%b'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$bytes&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello-world.iso &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$offset&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notrunc &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;none
&lt;span class="o"&gt;}&lt;/span&gt;

write_be32 1 0
write_be32 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$last_lba&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;conv=notrunc&lt;/code&gt; est essentiel : &lt;code&gt;dd&lt;/code&gt; écrit les quatre octets demandés sans tronquer le reste de l’image.&lt;/p&gt;

&lt;p&gt;Examinons les seize premiers octets :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 16 hello-world.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour 61 secteurs, le résultat est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000000: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 3c  ...............&amp;lt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les quatre premiers octets donnent le compteur &lt;code&gt;1&lt;/code&gt;. Les quatre octets à l’offset &lt;code&gt;8&lt;/code&gt; donnent la LBA de début &lt;code&gt;0&lt;/code&gt;. Les quatre derniers octets représentent &lt;code&gt;0x3c&lt;/code&gt;, soit la LBA de fin &lt;code&gt;60&lt;/code&gt;. Nous avons écrit huit octets dans la zone système, sans toucher au descripteur &lt;code&gt;CD001&lt;/code&gt; situé à la LBA 16.&lt;/p&gt;

&lt;p&gt;Le projet contient aussi &lt;a href="https://github.com/hexegesis/hexegesis-experiments/blob/8ca7eb9c02f36cc42c26b157f6013c68c56616e1/hello-world/scripts/patch-ps3-regions.sh" rel="noopener noreferrer"&gt;&lt;code&gt;scripts/patch-ps3-regions.sh&lt;/code&gt;&lt;/a&gt;, qui automatise ces calculs et contrôle la taille, les bornes et la présence du descripteur de volume ISO 9660. Ici, nous avons détaillé explicitement les opérations afin d’observer précisément les valeurs ajoutées.&lt;/p&gt;

&lt;p&gt;Contrôlons une dernière fois l’inventaire de l’image :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xorriso &lt;span class="nt"&gt;-indev&lt;/span&gt; hello-world.iso &lt;span class="nt"&gt;-find&lt;/span&gt; / &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; lsdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quittons enfin le conteneur :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans RPCS3, choisissez &lt;strong&gt;File → Boot ISO&lt;/strong&gt; et sélectionnez &lt;code&gt;hello-world.iso&lt;/code&gt;. Lors de notre essai, l’émulateur reconnaît l’unique région comme non protégée, n’essaie pas de la déchiffrer, monte le contenu de l’image, puis le programme écrit le même message :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bonjour depuis Hexegesis.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Bilan
&lt;/h2&gt;

&lt;p&gt;Nous sommes partis de trois fichiers dont nous connaissions la place et le rôle, puis nous les avons construits sans Makefile :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;param-sfo.xml
→ sfo
→ PS3_GAME/PARAM.SFO

printf '.SFB'
→ PS3_DISC.SFB

source/main.c
→ ppu-gcc
→ hello-world.elf
→ ppu-strip + sprxlinker + make_self
→ PS3_GAME/USRDIR/EBOOT.BIN

ces trois fichiers
→ xorriso
→ hello-world.iso
→ table de régions PS3 dans la zone système
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous avons relu &lt;code&gt;PARAM.SFO&lt;/code&gt; avec &lt;code&gt;sfo --list&lt;/code&gt;, contrôlé l’inventaire du répertoire et de l’image ISO, puis observé les valeurs ajoutées dans la zone système. Enfin, le message TTY a confirmé l’exécution du programme dans RPCS3.&lt;/p&gt;

&lt;p&gt;Le résultat est un cas d’essai synthétique et redistribuable, utile aux prochaines étapes d’Hexegesis. L’expérience démontre sa construction et son exécution dans la version testée de RPCS3 ; elle ne permet ni d’affirmer que notre &lt;code&gt;PS3_DISC.SFB&lt;/code&gt; minimal couvre le format complet, ni de conclure à une compatibilité avec une console PlayStation 3 ou avec des images de disques commerciaux.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

</description>
      <category>playstation3</category>
      <category>docker</category>
      <category>homebrew</category>
      <category>french</category>
    </item>
    <item>
      <title>Hexegesis : questions fréquentes</title>
      <dc:creator>Karim DRIDI</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:49:11 +0000</pubDate>
      <link>https://dev.to/kdridi/hexegesis-questions-frequentes-5m3</link>
      <guid>https://dev.to/kdridi/hexegesis-questions-frequentes-5m3</guid>
      <description>&lt;p&gt;Cette page rassemble les réponses aux questions générales sur Hexegesis. Elle pourra être complétée sans alourdir les articles techniques.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a"&gt;← Revenir au sommaire de la série&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Faut-il connaître Haskell ?
&lt;/h2&gt;

&lt;p&gt;Non. Les constructions nécessaires seront expliquées lorsqu’elles apparaîtront. Une première expérience de la programmation aidera à exécuter les exemples, mais elle ne conditionne pas la lecture des observations et de la méthode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faut-il posséder &lt;em&gt;Demon’s Souls&lt;/em&gt; ?
&lt;/h2&gt;

&lt;p&gt;Non pour lire la série ou exécuter les articles fondés sur des fixtures. Refaire les applications au corpus demandera en revanche de travailler sur son propre exemplaire, puisque les données commerciales ne seront pas fournies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Les fichiers des jeux ou des mods seront-ils publiés ?
&lt;/h2&gt;

&lt;p&gt;Non. Les dépôts contiendront du code, des fixtures synthétiques ou librement redistribuables, des références et des observations bornées. Ils ne contiendront pas d’images de disque ni de ressources extraites des jeux. Le contenu d’un mod existant ne sera pas republié sans autorisation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi utiliser des outils existants si Hexegesis doit les remplacer ?
&lt;/h2&gt;

&lt;p&gt;Parce qu’ils fournissent immédiatement une procédure et un résultat de référence. Ils permettent de comprendre le problème avant de le réimplémenter. Leur usage initial et l’objectif d’une boîte à outils Haskell autonome correspondent à deux étapes différentes du projet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment connaître les formats pris en charge par Hexegesis ?
&lt;/h2&gt;

&lt;p&gt;La documentation et les tests du dépôt public Hexegesis décriront les capacités effectivement disponibles. Une structure étudiée dans un article ou un prototype ne suffit pas, à elle seule, à établir sa prise en charge par le logiciel.&lt;/p&gt;

&lt;h2&gt;
  
  
  La feuille de route est-elle définitive ?
&lt;/h2&gt;

&lt;p&gt;Non. Les objectifs généraux sont connus, mais les formats rencontrés et les résultats obtenus détermineront le découpage des articles. Le sommaire restera le point d’entrée actualisé de la série.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a"&gt;↑ Revenir au sommaire de la série&lt;/a&gt;&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>modding</category>
      <category>reverseengineering</category>
      <category>french</category>
    </item>
    <item>
      <title>Hexegesis : sommaire de la série</title>
      <dc:creator>Karim DRIDI</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:46:20 +0000</pubDate>
      <link>https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a</link>
      <guid>https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a</guid>
      <description>&lt;p&gt;Cette page est le point d’entrée de la série Hexegesis. Elle regroupe les textes de présentation, les articles publiés et les principales directions de travail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Présentation et références
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kdridi/hexegesis-projet-de-modding-des-jeux-fromsoftware-en-haskell-4lm1"&gt;Présentation du projet&lt;/a&gt; — objectifs, choix de Haskell, méthode de travail et organisation des articles.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kdridi/hexegesis-questions-frequentes-5m3"&gt;Questions fréquentes&lt;/a&gt; — prérequis, données utilisées, outils existants et portée du logiciel.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Articles publiés
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Applications au corpus
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kdridi/dune-image-nommee-iso-a-un-premier-probe-en-haskell-41ia"&gt;Sonder une ISO PlayStation 3 en Haskell&lt;/a&gt; — lecture bornée de quelques métadonnées ISO 9660, Joliet et PlayStation 3, puis transcription de ces lectures dans GHCi.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fondations reproductibles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kdridi/hexegesis-construction-dun-programme-playstation-3-minimal-4nn7"&gt;Construire un programme PlayStation 3 minimal&lt;/a&gt; — chaîne reproductible pour compiler un programme PPU, produire les fichiers attendus par RPCS3 et construire une image ISO 9660 synthétique.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prochaine application envisagée
&lt;/h2&gt;

&lt;p&gt;Appliquer un mod existant à l’arborescence du jeu, vérifier son résultat dans RPCS3 et identifier précisément les fichiers et les opérations concernés. Cette référence donnera un objectif concret aux capacités suivantes d’Hexegesis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Directions de travail
&lt;/h2&gt;

&lt;p&gt;Les sujets actuellement envisagés comprennent :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;la construction de fixtures binaires synthétiques ;&lt;/li&gt;
&lt;li&gt;la description d’une structure commune à plusieurs interprétations ;&lt;/li&gt;
&lt;li&gt;le parcours d’une arborescence Joliet ;&lt;/li&gt;
&lt;li&gt;la localisation et l’extraction d’une ressource précise ;&lt;/li&gt;
&lt;li&gt;l’étude des archives, des textures et des polices employées par le jeu ;&lt;/li&gt;
&lt;li&gt;l’export d’une ressource vers un format modifiable ;&lt;/li&gt;
&lt;li&gt;l’import de la modification et la reconstruction des conteneurs nécessaires ;&lt;/li&gt;
&lt;li&gt;l’examen ultérieur des éléments associés au contenu abandonné.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cette liste n’est ni un calendrier ni le plan définitif des articles. Les titres annonceront soit un concept de programmation effectivement étudié, soit un résultat concret obtenu dans le jeu. Ce sommaire sera mis à jour à mesure que le travail progressera.&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>modding</category>
      <category>reverseengineering</category>
      <category>french</category>
    </item>
    <item>
      <title>Hexegesis : projet de modding des jeux FromSoftware en Haskell</title>
      <dc:creator>Karim DRIDI</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:40:00 +0000</pubDate>
      <link>https://dev.to/kdridi/hexegesis-projet-de-modding-des-jeux-fromsoftware-en-haskell-4lm1</link>
      <guid>https://dev.to/kdridi/hexegesis-projet-de-modding-des-jeux-fromsoftware-en-haskell-4lm1</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a"&gt;← Revenir au sommaire de la série&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hexegesis/hexegesis" rel="noopener noreferrer"&gt;Hexegesis&lt;/a&gt; est un projet personnel consacré au modding des jeux FromSoftware en &lt;a href="https://www.haskell.org/" rel="noopener noreferrer"&gt;Haskell&lt;/a&gt;. J’ai envie d’y construire progressivement une boîte à outils composée de petits programmes en ligne de commande : inspecter les fichiers d’un jeu, identifier leurs formats, extraire des ressources, les transformer et les réintégrer.&lt;/p&gt;

&lt;p&gt;Le point de départ reste très concret. Je voudrais remplacer une texture, modifier une police de caractères ou changer un écran d’accueil, puis vérifier le résultat dans &lt;a href="https://rpcs3.net/quickstart" rel="noopener noreferrer"&gt;RPCS3&lt;/a&gt;. Chaque objectif fournit un problème précis à résoudre et détermine les capacités dont j’ai réellement besoin. Plutôt que de chercher à prendre en charge intégralement un format, je commence par la plus petite partie utile.&lt;/p&gt;

&lt;p&gt;Cette série me sert de journal de bord. J’y rassemble les problèmes rencontrés, les documents qui m’ont aidé, les choix d’implémentation et les limites de ce que j’ai effectivement vérifié. Je ne connais pas encore toutes les étapes nécessaires, et je ne souhaite pas reconstruire après coup une progression artificielle : les résultats obtenus détermineront la suite.&lt;/p&gt;

&lt;p&gt;Ce préambule présente le projet, les raisons qui me poussent à le développer et la manière dont les articles seront organisés.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sommaire
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Une première modification visible dans RPCS3&lt;/li&gt;
&lt;li&gt;L’ambition d’une BusyBox du modding&lt;/li&gt;
&lt;li&gt;Pourquoi &lt;em&gt;Demon’s Souls&lt;/em&gt; sur PlayStation 3 ?&lt;/li&gt;
&lt;li&gt;Pourquoi Haskell ?&lt;/li&gt;
&lt;li&gt;Du besoin à l’implémentation minimale&lt;/li&gt;
&lt;li&gt;Écrire pour apprendre&lt;/li&gt;
&lt;li&gt;Pourquoi écrire en français ?&lt;/li&gt;
&lt;li&gt;Reproductibilité et corpus commercial&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Une première modification visible dans RPCS3
&lt;/h2&gt;

&lt;p&gt;Je souhaite commencer par une modification dont le résultat se constate immédiatement. Une nouvelle texture sur une arme ou un vêtement de départ, une autre police de caractères ou un écran d’accueil modifié conviendraient tous à cette première étape. Le choix définitif dépendra surtout de la chaîne de formats qu’il faudra réellement traiter.&lt;/p&gt;

&lt;p&gt;RPCS3 peut charger une image du jeu ou une version extraite sous forme de répertoires. Pour appliquer les premiers mods, je travaillerai sur cette arborescence extraite : les fichiers modifiés pourront y être ajoutés ou remplacés sans reconstruire immédiatement une image ISO.&lt;/p&gt;

&lt;p&gt;Une première expérience consistera à appliquer à mon exemplaire un mod déjà publié. Le but ne sera pas de republier son contenu, mais d’établir un résultat de référence : relever les fichiers concernés, identifier les outils employés, appliquer la modification, puis vérifier son effet dans RPCS3. Une fois cette procédure comprise, je pourrai reprendre chaque opération et déterminer comment produire moi-même une modification de même nature.&lt;/p&gt;

&lt;p&gt;La chaîne de travail devrait ressembler à ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;obtenir l’arborescence du jeu
→ localiser le fichier concerné
→ identifier ses éventuels conteneurs
→ extraire la ressource recherchée
→ la convertir vers une représentation modifiable
→ effectuer la modification
→ reconstruire les conteneurs nécessaires
→ replacer les fichiers dans l’arborescence
→ vérifier le résultat dans RPCS3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette liste ne décrit pas encore une procédure validée. Je connais le résultat recherché, mais pas tous les formats intermédiaires ni toutes les contraintes de reconstruction. Les premières manipulations serviront précisément à les déterminer.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  L’ambition d’une BusyBox du modding
&lt;/h2&gt;

&lt;p&gt;Dans un premier temps, j’utiliserai les outils existants pour avancer, comprendre la chaîne de traitement et établir les résultats attendus. Un script Python, un programme spécialisé ou une manipulation manuelle peut parfaitement servir de référence provisoire.&lt;/p&gt;

&lt;p&gt;À terme, j’aimerais cependant qu’Hexegesis permette de réaliser l’ensemble de ce travail sans dépendre d’une collection disparate de programmes. J’ai davantage une boussole qu’un plan arrêté : mon ambition serait qu’Hexegesis devienne, à son échelle, une sorte de &lt;a href="https://busybox.net/" rel="noopener noreferrer"&gt;BusyBox&lt;/a&gt; du modding. La comparaison place la barre assez haut, mais elle décrit bien la direction recherchée.&lt;/p&gt;

&lt;p&gt;BusyBox réunit de nombreux utilitaires spécialisés derrière un même programme. Hexegesis suivrait un principe semblable dans un domaine plus restreint : sonder une image, parcourir une arborescence, trouver un fichier, l’extraire, reconnaître son format, l’exporter vers une représentation usuelle, importer une modification et reconstruire les données nécessaires.&lt;/p&gt;

&lt;p&gt;Chaque commande devra accomplir une opération précise. Elle devra posséder des entrées, une sortie, des erreurs et des limites explicites. Les commandes pourront être utilisées seules ou composées depuis le Shell ; leur logique restera également accessible depuis une bibliothèque Haskell.&lt;/p&gt;

&lt;p&gt;L’existence préalable d’un outil ne retire donc pas automatiquement une opération du périmètre d’Hexegesis. Cet outil peut d’abord me permettre d’avancer et de vérifier mon résultat. Son remplacement ultérieur par une implémentation Haskell répondra à un autre objectif : obtenir une boîte à outils cohérente, documentée et suffisante pour les formats effectivement pris en charge.&lt;/p&gt;

&lt;p&gt;La lecture seule constitue le premier périmètre. Lorsqu’une opération d’écriture deviendra nécessaire, elle devra produire un nouveau fichier ou une nouvelle arborescence plutôt que modifier silencieusement la source. Les règles précises seront définies et testées à partir du premier besoin réel d’import ou de reconstruction.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi &lt;em&gt;Demon’s Souls&lt;/em&gt; sur PlayStation 3 ?
&lt;/h2&gt;

&lt;p&gt;Il fallait choisir un premier jeu parmi des milliers. J’ai choisi la version PlayStation 3 de &lt;a href="https://fr.wikipedia.org/wiki/Demon%27s_Souls" rel="noopener noreferrer"&gt;&lt;em&gt;Demon’s Souls&lt;/em&gt;&lt;/a&gt; pour une raison simple : j’aime ce jeu. Je préfère consacrer du temps à des fichiers que j’ai réellement envie de comprendre et pouvoir lancer régulièrement le jeu sans que la vérification devienne une corvée.&lt;/p&gt;

&lt;p&gt;Je collectionne les jeux vidéo depuis l’enfance et j’en possède aujourd’hui plus d’un millier. Les jeux FromSoftware occupent une place particulière dans cette collection. J’apprécie aussi la manière dont les mods permettent de redécouvrir un jeu connu, parfois plusieurs années après sa sortie. Le modding réunit ainsi deux activités qui m’intéressent déjà séparément : programmer et jouer.&lt;/p&gt;

&lt;p&gt;Je possède au moins deux versions différentes de &lt;em&gt;Demon’s Souls&lt;/em&gt;, &lt;em&gt;Dark Souls&lt;/em&gt; et &lt;em&gt;Dark Souls II&lt;/em&gt;. Elles pourront plus tard servir de corpus comparatif. Une structure observée dans une version ne sera cependant pas attribuée aux autres sans vérification : une édition régionale, une révision ou une plateforme différente peut employer une organisation distincte.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Demon’s Souls&lt;/em&gt; offre enfin un autre sujet qui m’intéresse depuis longtemps. De nombreux articles consacrés au &lt;em&gt;data mining&lt;/em&gt; évoquent les ressources associées au royaume des géants et à la sixième Archstone, abandonnée dans le jeu publié. Ces articles rapportent les conclusions de personnes qui ont examiné les fichiers, mais ils donnent rarement les moyens de mesurer directement ce qui subsiste.&lt;/p&gt;

&lt;p&gt;J’aimerais pouvoir le vérifier moi-même. S’agit-il de quelques fichiers isolés autour desquels les interprétations se sont accumulées ? D’une zone déjà largement construite mais privée de certaines ressources ? Quels éléments sont directement observables et quelles conclusions demandent une extrapolation ? Je ne veux pas supposer la réponse. Je veux disposer des outils nécessaires pour examiner les données et délimiter ce qu’elles permettent réellement d’affirmer.&lt;/p&gt;

&lt;p&gt;Cette étude viendra plus tard. Avant d’analyser un contenu abandonné, il faut savoir atteindre les ressources, reconnaître leurs formats et les lire correctement.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi Haskell ?
&lt;/h2&gt;

&lt;p&gt;La première raison est personnelle : j’aime Haskell et j’ai envie de mieux le maîtriser. Le projet ne cherche pas à démontrer qu’il serait le meilleur langage pour la rétro-ingénierie ou le modding. De nombreux langages et outils savent déjà traiter des formats binaires. Je choisis Haskell parce que c’est dans ce langage que je souhaite réfléchir à ces problèmes.&lt;/p&gt;

&lt;p&gt;J’ai commencé à programmer avec de l’assembleur. J’ai ensuite pratiqué le VHDL et l’assembleur sur Game Boy, puis je me suis progressivement tourné vers les langages fortement typés et fonctionnels, notamment &lt;a href="https://ocaml.org/" rel="noopener noreferrer"&gt;OCaml&lt;/a&gt; et Haskell. Ces deux parcours ne me paraissent pas contradictoires. Le bas niveau oblige à comprendre la représentation effective des données ; les types et les abstractions permettent ensuite d’exprimer cette compréhension sans la laisser dispersée dans le programme.&lt;/p&gt;

&lt;p&gt;Après une trentaine d’années de programmation, je prends de plus en plus de plaisir à reprendre des opérations simples et à chercher la formulation qui les rend à la fois explicites et composables. Lire quatre octets, calculer un offset ou décoder un entier ne présente aucune difficulté algorithmique particulière. La question intéressante devient : où placer la connaissance de la largeur du champ ? Comment représenter un résultat inconnu ou invalide ? Comment séparer la description d’une structure de l’effet qui consiste à lire un fichier ? Comment réutiliser une même description pour décoder, annoter ou documenter ?&lt;/p&gt;

&lt;p&gt;Le programme produit m’intéresse, mais la manière de l’écrire m’intéresse tout autant. Je souhaite trouver une syntaxe dans laquelle l’intention reste visible et où les contraintes du format ne sont pas disséminées entre plusieurs couches de code.&lt;/p&gt;

&lt;p&gt;Les types algébriques permettent de nommer les différents résultats au lieu de les réduire à des valeurs conventionnelles. Les fonctions pures permettent d’étudier séparément la transformation des octets et leur acquisition. L’écriture monadique permet d’enchaîner les calculs tout en rendant explicites les effets qu’ils requièrent. Cette séparation entre la valeur recherchée et les effets nécessaires à son obtention correspond particulièrement bien à la manière dont j’ai envie d’aborder les parseurs.&lt;/p&gt;

&lt;p&gt;J’ai déjà expérimenté des architectures fondées sur les &lt;a href="https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/gadt.html" rel="noopener noreferrer"&gt;GADT&lt;/a&gt;, les approches &lt;a href="https://www.okmij.org/ftp/tagless-final/" rel="noopener noreferrer"&gt;&lt;em&gt;tagless final&lt;/em&gt;&lt;/a&gt; ou les &lt;a href="https://hackage.haskell.org/package/free/docs/Control-Monad-Free.html" rel="noopener noreferrer"&gt;monades libres&lt;/a&gt;. Je connais encore peu certaines bibliothèques d’effets comme &lt;a href="https://hackage.haskell.org/package/effectful" rel="noopener noreferrer"&gt;&lt;code&gt;effectful&lt;/code&gt;&lt;/a&gt;, mais j’y pressens des outils susceptibles de m’intéresser. Hexegesis me fournit une raison concrète de les étudier, de les comparer et surtout de constater ce qu’elles apportent sur un programme réel.&lt;/p&gt;

&lt;p&gt;Ces techniques ne seront pas introduites pour donner au projet une apparence sophistiquée. Elles apparaîtront si un besoin les justifie. Une abstraction pourra être essayée, mesurée sur plusieurs formats, puis conservée, simplifiée ou abandonnée. Le journal de bord devra rendre compte de cette décision au lieu de présenter après coup l’architecture retenue comme une évidence.&lt;/p&gt;

&lt;p&gt;Le modding est donc à la fois une finalité et un domaine d’application pour mon apprentissage de Haskell. Inversement, Haskell me fournit les outils conceptuels avec lesquels je souhaite formaliser le travail de modding. Aucun des deux sujets n’est un simple décor pour l’autre.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Du besoin à l’implémentation minimale
&lt;/h2&gt;

&lt;p&gt;Les informations utiles au modding sont souvent dispersées entre des forums, des dépôts de code, des scripts, des outils spécialisés et des documents techniques volumineux. Beaucoup de ces ressources ont été écrites pour résoudre efficacement un problème précis. Leur contexte, leurs hypothèses et les raisons d’un offset particulier ne sont donc pas toujours explicités.&lt;/p&gt;

&lt;p&gt;Je souhaite produire le type de document qui me manque lorsque j’aborde un nouveau format : un objectif concret, les passages utiles des sources, les observations correspondantes, le raisonnement qui les relie et une implémentation dont le périmètre est annoncé.&lt;/p&gt;

&lt;p&gt;Il ne s’agit pas de lire intégralement une spécification avant d’agir. Dans le premier article, la &lt;a href="https://ecma-international.org/publications-and-standards/standards/ecma-119/" rel="noopener noreferrer"&gt;norme ISO 9660 publiée sous la référence ECMA-119&lt;/a&gt; contient bien davantage d’informations que nécessaire pour retrouver le nom du volume, sa taille logique ou le marqueur Joliet. Je consulte les sections qui répondent à la question en cours, tout en conservant la référence complète afin de savoir où poursuivre si un besoin ultérieur l’exige.&lt;/p&gt;

&lt;p&gt;Pour chaque capacité, je suivrai autant que possible le même ordre de travail :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;formuler un résultat observable ;&lt;/li&gt;
&lt;li&gt;examiner les outils et les implémentations existants ;&lt;/li&gt;
&lt;li&gt;retrouver les documents qui décrivent les structures nécessaires ;&lt;/li&gt;
&lt;li&gt;effectuer une lecture petite, précise, bornée et strictement en lecture seule ;&lt;/li&gt;
&lt;li&gt;interpréter les valeurs et consigner leur provenance ;&lt;/li&gt;
&lt;li&gt;distinguer les faits établis, les observations locales, les inférences et les questions restantes ;&lt;/li&gt;
&lt;li&gt;écrire la plus petite implémentation utile au résultat ;&lt;/li&gt;
&lt;li&gt;construire des données synthétiques pour tester les cas valides et les erreurs ;&lt;/li&gt;
&lt;li&gt;confronter séparément l’implémentation au corpus réel ;&lt;/li&gt;
&lt;li&gt;annoncer exactement les capacités obtenues et leurs limites.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cette discipline est connue sous le nom de &lt;a href="https://www.sirlin.net/articles/subtractive-design" rel="noopener noreferrer"&gt;&lt;strong&gt;design par soustraction&lt;/strong&gt;&lt;/a&gt;. Fumito Ueda l’a mise en œuvre dans la conception d’&lt;a href="https://shmuplations.com/ico/" rel="noopener noreferrer"&gt;&lt;em&gt;Ico&lt;/em&gt;&lt;/a&gt; en retirant les éléments qui ne servaient pas son idée centrale. David Sirlin, présent lors de la conférence donnée par Ueda à la Game Developers Conference de 2004, en propose une explication détaillée à partir de cet exemple.&lt;/p&gt;

&lt;p&gt;La référence trouve naturellement sa place dans ce projet : &lt;a href="https://www.theguardian.com/technology/2015/mar/31/bloodborne-dark-souls-creator-hidetaka-miyazaki-interview" rel="noopener noreferrer"&gt;Hidetaka Miyazaki a expliqué que la découverte d’&lt;em&gt;Ico&lt;/em&gt; lui avait révélé les possibilités du jeu vidéo et donné envie d’en créer lui-même&lt;/a&gt;. Cette décision l’a conduit à rejoindre FromSoftware, où il a ensuite dirigé &lt;em&gt;Demon’s Souls&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Appliqué à Hexegesis, le principe reste concret. Lorsqu’une capacité ne contribue pas au résultat actuel, je préfère l’écarter plutôt que la développer sur la base d’un usage hypothétique. Une abstraction n’est introduite qu’après l’apparition d’une répétition ou d’une difficulté réelle. Sa confrontation à un second format permet ensuite de décider si elle doit être conservée, réduite ou remplacée.&lt;/p&gt;

&lt;p&gt;Cette méthode n’oppose pas la pratique à la théorie. Une observation sans modèle reste difficile à généraliser ; une description théorique qui n’est jamais confrontée aux données ne suffit pas davantage. Les commandes Shell, les spécifications, les types Haskell et les tests apportent des formes de preuve différentes qui doivent rester reliées sans être confondues.&lt;/p&gt;

&lt;p&gt;Une norme définit un format, mais ne prouve pas qu’une image particulière lui est entièrement conforme. Deux implémentations indépendantes peuvent corroborer une structure sans devenir une spécification officielle. Une valeur lue dans mon exemplaire reste une observation locale. Une fixture synthétique prouve qu’un test couvre le cas qu’elle représente, pas que le programme accepte toutes les variantes rencontrées dans le monde réel.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Écrire pour apprendre
&lt;/h2&gt;

&lt;p&gt;Cette série est d’abord écrite pour moi. Les problèmes de modding qui m’intéressent sont difficiles à reprendre plusieurs mois plus tard lorsque les informations restent réparties entre mon historique Shell, quelques notes et des dépôts externes. Un article m’oblige à reconstruire le raisonnement de manière assez précise pour pouvoir m’en servir de nouveau.&lt;/p&gt;

&lt;p&gt;J’ai travaillé une dizaine d’années comme formateur à Epitech et je suis aujourd’hui consultant en intelligence artificielle. Cette expérience a renforcé une conviction simple : transmettre quelque chose reste pour moi l’un des meilleurs moyens de l’apprendre. Expliquer une structure oblige à vérifier son vocabulaire ; publier un exemple oblige à retrouver sa source ; écrire un test oblige à préciser le comportement attendu.&lt;/p&gt;

&lt;p&gt;Je ne présente donc pas un programme d’enseignement dont je maîtriserais déjà toutes les étapes. Je partage un apprentissage en cours. Certains articles documenteront une réussite directe ; d’autres conserveront une erreur ou une hypothèse abandonnée lorsqu’elle aide réellement à comprendre la solution.&lt;/p&gt;

&lt;p&gt;Je ne souhaite pas non plus réduire artificiellement les sujets difficiles. Une notion complexe peut être présentée simplement sans employer un synonyme approximatif ni dissimuler sa véritable définition. Le rôle du texte est d’introduire le terme exact, de fournir le modèle mental nécessaire et de montrer son usage sur un cas concret.&lt;/p&gt;

&lt;p&gt;La série s’adresse ainsi aux personnes qui souhaitent commencer le modding, découvrir les formats binaires, lire du Haskell dans un contexte concret ou mieux formaliser une pratique déjà empirique. Il n’est pas nécessaire de connaître Haskell ni d’avoir déjà étudié des formats binaires avant de commencer. Une première expérience du terminal et de la programmation facilitera les manipulations, mais les notions nécessaires seront présentées au moment où elles interviennent.&lt;/p&gt;

&lt;p&gt;Ce ne sera pas pour autant un cursus exhaustif. Je développerai ce que j’ai moi-même besoin de comprendre ou de formaliser. Lorsqu’une notion périphérique est déjà bien traitée ailleurs, une explication courte et une référence pourront suffire.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Pourquoi écrire en français ?
&lt;/h2&gt;

&lt;p&gt;J’ai longtemps hésité entre l’anglais, qui permettrait de toucher une communauté plus large, et le français, que je préfère lire et écrire. Je consulte quotidiennement de la documentation anglaise et j’ai vécu plusieurs années à Londres ; ce choix ne vient donc pas d’une difficulté à travailler en anglais.&lt;/p&gt;

&lt;p&gt;J’ai finalement choisi le français parce que j’ai envie de contribuer à une documentation technique francophone précise et de travailler mon propre style dans cette langue. Je souhaite écrire les articles que j’aurais aimé trouver moi-même : naturels, rigoureux et suffisamment détaillés pour retrouver le raisonnement, pas seulement son résultat.&lt;/p&gt;

&lt;p&gt;Le français de l’article n’impose pas de traduire tout le vocabulaire. Les identifiants du code resteront naturellement en anglais. Les noms officiels des champs et des structures seront conservés lorsqu’ils facilitent la consultation des sources. L’objectif n’est pas de franciser mécaniquement la terminologie, mais de choisir le mot qui décrit le plus exactement le concept.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproductibilité et corpus commercial
&lt;/h2&gt;

&lt;p&gt;Les images et les ressources des jeux étudiés sont commerciales et ne seront pas distribuées avec les articles. Cette contrainte impose de distinguer le protocole documenté de l’expérience entièrement reproductible.&lt;/p&gt;

&lt;p&gt;Une commande exécutée sur mon exemplaire produit une observation locale. Je peux publier la commande, l’offset, la règle d’interprétation et le résultat utile au raisonnement. Un lecteur qui travaille sur son propre exemplaire pourra reprendre ce protocole, mais il devra vérifier ses valeurs au lieu de supposer qu’elles sont identiques aux miennes.&lt;/p&gt;

&lt;p&gt;La série comportera donc deux catégories principales d’articles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fondations reproductibles
&lt;/h3&gt;

&lt;p&gt;Ces articles étudieront un mécanisme général sur des données synthétiques ou librement redistribuables : lecture binaire bornée, endianness, description de formats, validation, interprètes, diagnostics, tests ou gestion des effets.&lt;/p&gt;

&lt;p&gt;Le code et les fixtures permettront d’exécuter les exemples, de modifier les valeurs et de provoquer les erreurs décrites. La CI exécutera les tests associés aux propriétés explicitement vérifiables sur ces données. Elle ne prétendra pas valider les observations réalisées sur les jeux commerciaux.&lt;/p&gt;

&lt;h3&gt;
  
  
  Applications au corpus
&lt;/h3&gt;

&lt;p&gt;Ces articles appliqueront les mécanismes précédents aux exemplaires de jeux FromSoftware que j’étudie. Ils pourront publier des offsets, des métadonnées, des méthodes et des résultats bornés, mais pas les images de disque, les ressources commerciales, les clés ou les exécutables déchiffrés.&lt;/p&gt;

&lt;p&gt;Un article pourra naturellement mêler les deux dimensions. Sa catégorie indiquera seulement de quoi dépend son résultat principal. Une difficulté rencontrée sur le corpus pourra fournir le problème d’un prochain article général ; une capacité validée sur des fixtures devra ensuite être vérifiée séparément sur les données réelles.&lt;/p&gt;

&lt;p&gt;Cette alternance permet aussi de lire la série pour des raisons différentes. Une personne intéressée par Haskell mais ne possédant pas &lt;em&gt;Demon’s Souls&lt;/em&gt; pourra exécuter les fondations reproductibles. Une personne surtout intéressée par le modding pourra suivre leur mise en application sans être obligée d’adopter toutes les abstractions étudiées.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a"&gt;← Revenir au sommaire de la série&lt;/a&gt;&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>modding</category>
      <category>reverseengineering</category>
      <category>french</category>
    </item>
    <item>
      <title>Hexegesis : sonder une ISO PlayStation 3 en Haskell</title>
      <dc:creator>Karim DRIDI</dc:creator>
      <pubDate>Thu, 03 Sep 2026 00:31:00 +0000</pubDate>
      <link>https://dev.to/kdridi/dune-image-nommee-iso-a-un-premier-probe-en-haskell-41ia</link>
      <guid>https://dev.to/kdridi/dune-image-nommee-iso-a-un-premier-probe-en-haskell-41ia</guid>
      <description>&lt;p&gt;&lt;strong&gt;À propos de cette série.&lt;/strong&gt; Cette série raconte la construction progressive, en Haskell, d’outils consacrés à l’étude et à la transformation des formats binaires employés par les jeux FromSoftware sur PlayStation 3. Elle prend pour premier corpus la version PlayStation 3 originale de &lt;em&gt;Demon’s Souls&lt;/em&gt; et avance par enquêtes documentées : observer une structure, établir ce qui est documenté, distinguer les faits des hypothèses, puis implémenter la plus petite capacité nécessaire à l’étape suivante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/kdridi/hexegesis-sommaire-de-la-serie-551a"&gt;← Revenir au sommaire de la série&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Dans ce premier article, nous allons déterminer ce qu’un fichier nommé &lt;code&gt;image.iso&lt;/code&gt; permet réellement d’affirmer. Nous commencerons par observer ses métadonnées au moyen de lectures précisément bornées, avant de transcrire cette reconnaissance dans un prototype direct écrit en Haskell.&lt;/p&gt;

&lt;p&gt;Le nom &lt;code&gt;image.iso&lt;/code&gt; constitue une hypothèse, non une preuve de format. Une extension est une convention du système de fichiers hôte ; elle ne garantit pas le contenu du fichier. Nous allons donc rechercher une structure définie par ISO 9660, lire plusieurs champs précis, puis reconnaître deux informations supplémentaires : un marqueur Joliet et un enregistrement propre aux disques PlayStation 3.&lt;/p&gt;

&lt;p&gt;Notre périmètre restera strictement en lecture seule. Nous ne monterons pas l’image, ne parcourrons aucun répertoire, n’extrairons rien, ne déchiffrerons rien et n’écrirons aucun octet. À la fin, notre prototype affichera seulement :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps3.product-id=BLES-00932
iso9660.volume-id=PS3VOLUME
iso9660.logical-block-size=2048
joliet.level=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ces quatre lignes sont un résultat de reconnaissance borné, non un certificat de conformité de l’image entière.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sommaire
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Localiser les métadonnées : de la LBA à l’offset&lt;/li&gt;
&lt;li&gt;Reconnaître ISO 9660 dans les octets&lt;/li&gt;
&lt;li&gt;Suivre les descripteurs jusqu’au terminateur&lt;/li&gt;
&lt;li&gt;Reconnaître Joliet dans un descripteur supplémentaire&lt;/li&gt;
&lt;li&gt;Retrouver les indices propres à la PlayStation 3&lt;/li&gt;
&lt;li&gt;Construire le probe dans GHCi&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Localiser les métadonnées : de la LBA à l’offset
&lt;/h2&gt;

&lt;p&gt;Un fichier binaire est une suite d’octets. Nous appelons &lt;strong&gt;offset&lt;/strong&gt; la position, comptée depuis zéro, d’un octet dans cette suite.&lt;/p&gt;

&lt;p&gt;ISO 9660 organise les informations qui nous intéressent en secteurs logiques de 2 048 octets. Une &lt;strong&gt;LBA&lt;/strong&gt; (&lt;em&gt;Logical Block Address&lt;/em&gt;, adresse logique de bloc) est le numéro d’un tel secteur : le secteur de LBA 0 commence à l’offset 0, celui de LBA 1 à l’offset 2 048, et ainsi de suite. La formule que nous réutiliserons est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;offset absolu = LBA × taille du secteur + offset relatif du champ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selon &lt;a href="https://ecma-international.org/publications-and-standards/standards/ecma-119/" rel="noopener noreferrer"&gt;ECMA-119&lt;/a&gt;, la norme qui décrit ISO 9660, les secteurs de LBA 0 à 15 forment la &lt;em&gt;System Area&lt;/em&gt;, ou zone système. La séquence des descripteurs de volume commence à la LBA 16. &lt;a href="https://wiki.osdev.org/ISO_9660" rel="noopener noreferrer"&gt;La page ISO 9660 d’OSDev&lt;/a&gt; fournit une table pratique pour s’orienter ; ECMA-119 demeure la source normative.&lt;/p&gt;

&lt;p&gt;Nous commençons par deux constantes Shell :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;image.iso &lt;span class="c"&gt;# Remplacer au besoin par un chemin absolu vers l’image locale.&lt;/span&gt;
&lt;span class="nv"&gt;SECTOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2048
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Toutes les commandes &lt;code&gt;xxd&lt;/code&gt; de cet article emploient &lt;code&gt;-s&lt;/code&gt; pour fixer l’offset et &lt;code&gt;-l&lt;/code&gt; pour borner la quantité lue. L’option &lt;code&gt;-g 1&lt;/code&gt; sépare les octets à l’affichage. Aucune de ces commandes ne modifie l’image ; nous n’utiliserons notamment jamais &lt;code&gt;xxd -r&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconnaître ISO 9660 dans les octets
&lt;/h2&gt;

&lt;p&gt;Un &lt;strong&gt;descripteur de volume&lt;/strong&gt; (&lt;em&gt;Volume Descriptor&lt;/em&gt;) est un secteur de métadonnées. La séquence qui les contient commence à la LBA 16 et doit comprendre au moins un &lt;strong&gt;descripteur principal de volume&lt;/strong&gt; (&lt;em&gt;Primary Volume Descriptor&lt;/em&gt;). Dans notre image, ce descripteur principal est précisément le premier élément de la séquence.&lt;/p&gt;

&lt;p&gt;Tous les descripteurs ISO 9660 commencent par le même en-tête de sept octets. Les offsets de l’article sont comptés depuis zéro ; la colonne « Position ECMA-119 » reprend la numérotation à partir de 1 employée par la norme.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset relatif&lt;/th&gt;
&lt;th&gt;Position ECMA-119&lt;/th&gt;
&lt;th&gt;Taille&lt;/th&gt;
&lt;th&gt;Nom canonique du champ&lt;/th&gt;
&lt;th&gt;Rôle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;nature du descripteur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2 à 6&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Standard Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant &lt;code&gt;CD001&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;version de la structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Le descripteur principal occupe exactement 2 048 octets. Sa disposition complète est la suivante. Les champs que notre probe décodera ne sont donc pas les seuls renseignements disponibles dans ce secteur.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset relatif&lt;/th&gt;
&lt;th&gt;Position ECMA-119&lt;/th&gt;
&lt;th&gt;Taille&lt;/th&gt;
&lt;th&gt;Nom canonique du champ&lt;/th&gt;
&lt;th&gt;Encodage ou fonction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 8 bits, valeur 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2 à 6&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Standard Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;CD001&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 8 bits, valeur 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Unused Field&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;octet nul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;9 à 40&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;&lt;code&gt;System Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;41 à 72&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;d&lt;/code&gt;, complétés à largeur fixe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;73 à 80&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Unused Field&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;81 à 88&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Space Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 32 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;89 à 120&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Unused Field&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;121 à 124&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Set Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 16 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;124&lt;/td&gt;
&lt;td&gt;125 à 128&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Sequence Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 16 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;129 à 132&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Logical Block Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 16 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;td&gt;133 à 140&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Path Table Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 32 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;140&lt;/td&gt;
&lt;td&gt;141 à 144&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Occurrence of Type L Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, little-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;td&gt;145 à 148&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Optional Occurrence of Type L Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, little-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;148&lt;/td&gt;
&lt;td&gt;149 à 152&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Occurrence of Type M Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, big-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;td&gt;153 à 156&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Optional Occurrence of Type M Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, big-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;156&lt;/td&gt;
&lt;td&gt;157 à 190&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Directory Record for Root Directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;enregistrement du répertoire racine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;190&lt;/td&gt;
&lt;td&gt;191 à 318&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Set Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;d&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;318&lt;/td&gt;
&lt;td&gt;319 à 446&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Publisher Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;446&lt;/td&gt;
&lt;td&gt;447 à 574&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Data Preparer Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;574&lt;/td&gt;
&lt;td&gt;575 à 702&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Application Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;702&lt;/td&gt;
&lt;td&gt;703 à 739&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Copyright File Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant de fichier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;739&lt;/td&gt;
&lt;td&gt;740 à 776&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Abstract File Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant de fichier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;776&lt;/td&gt;
&lt;td&gt;777 à 813&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bibliographic File Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant de fichier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;813&lt;/td&gt;
&lt;td&gt;814 à 830&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Creation Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;830&lt;/td&gt;
&lt;td&gt;831 à 847&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Modification Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;847&lt;/td&gt;
&lt;td&gt;848 à 864&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Expiration Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;864&lt;/td&gt;
&lt;td&gt;865 à 881&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Effective Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;881&lt;/td&gt;
&lt;td&gt;882&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;File Structure Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 8 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;882&lt;/td&gt;
&lt;td&gt;883&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;champ réservé&lt;/td&gt;
&lt;td&gt;octet nul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;883&lt;/td&gt;
&lt;td&gt;884 à 1395&lt;/td&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Application Use&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;contenu non spécifié par ECMA-119&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1395&lt;/td&gt;
&lt;td&gt;1396 à 2048&lt;/td&gt;
&lt;td&gt;653&lt;/td&gt;
&lt;td&gt;champ réservé&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Les catégories de caractères &lt;code&gt;a&lt;/code&gt; et &lt;code&gt;d&lt;/code&gt; sont définies par ECMA-119. Elles imposent un répertoire de caractères plus précis que la simple propriété « octet ASCII ». Nous ne décoderons ici que les champs nécessaires au résultat final, mais les offsets du tableau permettent d’examiner les autres sans rechercher de nouveau la disposition générale.&lt;/p&gt;

&lt;p&gt;La LBA 16 commence à :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;16 × 2 048 = 32 768 = 0x8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous lisons donc exactement ses sept premiers octets :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 7 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie observée dans notre image est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008000: 01 43 44 30 30 31 01                             .CD001.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous pouvons maintenant situer chaque valeur au lieu de reconnaître seulement une chaîne plausible :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;00008000&lt;/code&gt; est l’offset absolu affiché en hexadécimal ; &lt;code&gt;0x8000&lt;/code&gt; vaut bien 32 768 ;&lt;/li&gt;
&lt;li&gt;le premier octet, &lt;code&gt;01&lt;/code&gt;, est le type du descripteur ; le type 1 désigne le descripteur principal de volume ;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;43 44 30 30 31&lt;/code&gt; est l’encodage ASCII (&lt;em&gt;American Standard Code for Information Interchange&lt;/em&gt;) de l’identifiant standard &lt;code&gt;CD001&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;le dernier &lt;code&gt;01&lt;/code&gt; est la version commune du descripteur, ici 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;C’est le &lt;strong&gt;type&lt;/strong&gt;, et non l’octet de version, qui établit la nature principale de ce descripteur. À ce stade, nous avons reconnu un en-tête attendu ; nous n’avons pas validé tout le secteur ni le système de fichiers.&lt;/p&gt;

&lt;p&gt;Si une autre image produit des octets différents, la règle réutilisable n’est pas de rechercher visuellement le mot &lt;code&gt;CD001&lt;/code&gt; n’importe où. Il faut retrouver, à l’offset exact de la LBA 16, le triplet structuré « type 1, identifiant &lt;code&gt;CD001&lt;/code&gt;, version prise en charge ».&lt;/p&gt;

&lt;h3&gt;
  
  
  Le champ &lt;code&gt;Volume Identifier&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Le champ anglais canonique &lt;code&gt;Volume Identifier&lt;/code&gt; désigne l’identifiant textuel du volume. Dans le descripteur principal de volume, il commence à l’offset relatif 40 et occupe 32 octets. Sa position absolue vaut donc :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;16 × 2 048 + 40 = 32 808 = 0x8028
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous lisons les 32 octets, sans davantage :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 32 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous observons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008028: 50 53 33 56 4f 4c 55 4d 45 20 20 20 20 20 20 20  PS3VOLUME
00008038: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les octets &lt;code&gt;50 53 33 56 4f 4c 55 4d 45&lt;/code&gt; donnent &lt;code&gt;PS3VOLUME&lt;/code&gt; en ASCII. Les octets &lt;code&gt;20&lt;/code&gt; qui suivent sont des espaces de remplissage jusqu’à la largeur fixe de 32 octets. Pour l’affichage, nous retirerons uniquement ces espaces terminaux. Nous ne supprimerons ni un éventuel espace initial, ni des octets arbitraires qui appartiendraient réellement au champ d’une autre image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le champ &lt;code&gt;Volume Space Size&lt;/code&gt; et ses deux ordres d’octets
&lt;/h3&gt;

&lt;p&gt;Le champ &lt;code&gt;Volume Space Size&lt;/code&gt;, à l’offset relatif 80, indique un &lt;strong&gt;nombre de blocs logiques&lt;/strong&gt;, pas encore une taille en octets. ECMA-119 l’enregistre deux fois sur quatre octets : d’abord en little-endian, puis en big-endian.&lt;/p&gt;

&lt;p&gt;Ces deux termes décrivent l’ordre des octets d’un entier multioctet. En little-endian, l’octet de poids faible vient en premier ; en big-endian, l’octet de poids fort vient en premier. Nous séparons ici les deux copies afin que leur ordre reste visible :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 4 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 4 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;84&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les sorties observées sont :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008050: a0 27 43 00                                      .'C.
00008054: 00 43 27 a0                                      .C'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La colonne de droite est seulement une tentative de représentation ASCII par &lt;code&gt;xxd&lt;/code&gt; ; elle n’a aucune signification pour ce champ numérique.&lt;/p&gt;

&lt;p&gt;Pour quatre octets &lt;code&gt;b0 b1 b2 b3&lt;/code&gt;, les formules sont :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;little-endian = b0 + b1×256 + b2×65 536 + b3×16 777 216
big-endian    = b0×16 777 216 + b1×65 536 + b2×256 + b3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Appliquons-les aux octets réellement lus. La copie little-endian donne :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0xa0 + 0x27×0x100 + 0x43×0x10000 + 0x00×0x1000000
= 0x4327a0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La copie big-endian donne directement la même écriture numérique :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00 43 27 a0 = 0x4327a0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;code&gt;0x4327a0&lt;/code&gt; vaut 4 401 056. Le volume annonce donc 4 401 056 blocs logiques. Sur une autre image, il faut appliquer les mêmes formules à ses propres octets, puis comparer les deux résultats. Un lecteur ne choisit pas la copie qui paraît la plus vraisemblable : leur désaccord constitue une incohérence du champ both-endian.&lt;/p&gt;

&lt;h3&gt;
  
  
  Le champ &lt;code&gt;Logical Block Size&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Il nous manque la taille d’un bloc logique. Le champ &lt;code&gt;Logical Block Size&lt;/code&gt; se trouve à l’offset relatif 128. Il occupe quatre octets : une copie little-endian de deux octets, puis une copie big-endian de deux octets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 2 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 2 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous observons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008080: 00 08                                            ..
00008082: 08 00                                            ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour deux octets &lt;code&gt;b0 b1&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;little-endian = b0 + b1×256
big-endian    = b0×256 + b1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La première copie, &lt;code&gt;00 08&lt;/code&gt;, vaut donc &lt;code&gt;0x00 + 0x08×0x100 = 0x0800&lt;/code&gt;. La seconde, &lt;code&gt;08 00&lt;/code&gt;, vaut également &lt;code&gt;0x0800&lt;/code&gt;. En décimal, &lt;code&gt;0x0800&lt;/code&gt; vaut 2 048 octets par bloc logique.&lt;/p&gt;

&lt;p&gt;Nous pouvons désormais reconstruire la taille annoncée de l’espace du volume :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4 401 056 × 2 048 = 9 013 362 688 octets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comparons cette valeur à la longueur du fichier hôte, toujours sans le modifier :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie observée est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-rw-r--r--@ 1 kdridi  staff  9013362688 Sep  2 02:02 image.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour cet échantillon, la longueur du fichier est exactement égale à l’espace annoncé. C’est un contrôle de cohérence utile, pas une égalité universelle : un conteneur peut ajouter du remplissage ou des données après le volume. Dans le modèle direct que nous utilisons ici, un fichier plus court que l’espace annoncé serait en revanche insuffisant pour contenir ce volume.&lt;/p&gt;

&lt;p&gt;Le résultat dépasse 4 Gio (gibioctets). Notre code Haskell devra donc convertir les deux facteurs vers un entier suffisamment large avant de les multiplier ; un entier limité à 32 bits ne convient pas à ce calcul.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Suivre les descripteurs jusqu’au terminateur
&lt;/h2&gt;

&lt;p&gt;Le secteur de LBA 16 n’est que le début d’une séquence. ISO 9660 autorise d’autres descripteurs dans les secteurs suivants et marque la fin de l’ensemble par un &lt;strong&gt;terminateur de séquence de descripteurs de volume&lt;/strong&gt; (&lt;em&gt;Volume Descriptor Set Terminator&lt;/em&gt;) de type 255.&lt;/p&gt;

&lt;p&gt;Le premier octet de l’en-tête commun détermine la nature de chaque secteur de métadonnées :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Valeur du type&lt;/th&gt;
&lt;th&gt;Nom canonique&lt;/th&gt;
&lt;th&gt;Fonction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Boot Record&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;informations destinées à un système d’amorçage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Primary Volume Descriptor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;description principale du volume et d’une arborescence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Supplementary Volume Descriptor&lt;/code&gt; ou &lt;code&gt;Enhanced Volume Descriptor&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;description d’une arborescence supplémentaire ; la version distingue les deux formes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Partition Descriptor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;description d’une partition du volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 à 254&lt;/td&gt;
&lt;td&gt;réservé&lt;/td&gt;
&lt;td&gt;aucune interprétation normalisée ici&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Set Terminator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;fin de la séquence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Le terminateur possède lui-même une disposition complète particulièrement simple :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset relatif&lt;/th&gt;
&lt;th&gt;Taille&lt;/th&gt;
&lt;th&gt;Nom canonique du champ&lt;/th&gt;
&lt;th&gt;Valeur&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;255&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Standard Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CD001&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;2 041&lt;/td&gt;
&lt;td&gt;champ réservé&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Puisque les descripteurs occupent chacun un secteur et sont contigus à partir de la LBA 16, les sept octets que nous lirons à chaque itération se répartissent toujours ainsi :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset absolu pour la LBA &lt;code&gt;n&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Taille&lt;/th&gt;
&lt;th&gt;Champ lu&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;n × 2 048 + 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Type&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;n × 2 048 + 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Standard Identifier&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;n × 2 048 + 6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pour observer les deux secteurs suivants :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;n &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;17 18&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;do
    &lt;/span&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 7 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie observée est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008800: 02 43 44 30 30 31 01                             .CD001.
00009000: ff 43 44 30 30 31 01                             .CD001.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En réunissant l’en-tête déjà lu à la LBA 16 et les deux premiers résultats de cette boucle, nous obtenons la séquence ISO 9660 suivante :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;LBA&lt;/th&gt;
&lt;th&gt;Offset absolu&lt;/th&gt;
&lt;th&gt;Sept premiers octets&lt;/th&gt;
&lt;th&gt;Interprétation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x8000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;01 43 44 30 30 31 01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;descripteur principal, &lt;code&gt;CD001&lt;/code&gt;, version 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x8800&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;02 43 44 30 30 31 01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;descripteur supplémentaire, &lt;code&gt;CD001&lt;/code&gt;, version 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x9000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ff 43 44 30 30 31 01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;terminateur, &lt;code&gt;CD001&lt;/code&gt;, version 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;À la LBA 17, &lt;code&gt;02 CD001 01&lt;/code&gt; est l’en-tête d’un &lt;strong&gt;descripteur supplémentaire de volume&lt;/strong&gt; (&lt;em&gt;Supplementary Volume Descriptor&lt;/em&gt;). À la LBA 18, &lt;code&gt;ff CD001 01&lt;/code&gt; est le terminateur. La séquence ISO 9660 de notre image comprend donc, dans l’ordre observé, le descripteur principal à la LBA 16, un descripteur supplémentaire à la LBA 17 et le terminateur à la LBA 18.&lt;/p&gt;

&lt;p&gt;Cette commande d’observation connaissait déjà les deux LBA à lire. Un scanner ISO 9660 général, lui, doit commencer à la LBA 16, avancer secteur par secteur, contrôler l’identifiant et la version, puis s’arrêter immédiatement au type 255. Il lui faut aussi une borne défensive : une image mal formée ne doit pas provoquer une lecture indéfinie si le terminateur manque.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconnaître Joliet dans un descripteur supplémentaire
&lt;/h2&gt;

&lt;p&gt;Le type 2 ne signifie pas à lui seul « Joliet ». ECMA-119 définit un descripteur supplémentaire générique. La version 1 désigne le descripteur supplémentaire étudié ici ; la version 2 désigne un descripteur amélioré (&lt;em&gt;Enhanced Volume Descriptor&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Le descripteur supplémentaire conserve l’essentiel de la disposition du descripteur principal, mais ajoute les informations nécessaires à l’interprétation de ses champs textuels. Voici sa structure complète :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset relatif&lt;/th&gt;
&lt;th&gt;Taille&lt;/th&gt;
&lt;th&gt;Nom canonique du champ&lt;/th&gt;
&lt;th&gt;Différence ou fonction principale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;valeur 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Standard Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;CD001&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Descriptor Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1 pour la forme supplémentaire, 2 pour la forme améliorée&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Flags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;propriétés des séquences d’échappement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;&lt;code&gt;System Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères interprétés selon la forme du descripteur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;d1&lt;/code&gt; dans la forme supplémentaire&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;72&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Unused Field&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Space Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 32 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Escape Sequences&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;désignation des jeux de caractères&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Set Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 16 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;124&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Sequence Number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 16 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Logical Block Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 16 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Path Table Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 32 bits both-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;140&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Occurrence of Type L Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, little-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;144&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Optional Occurrence of Type L Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, little-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;148&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Occurrence of Type M Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, big-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;152&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Location of Optional Occurrence of Type M Path Table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;numéro de bloc, big-endian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;156&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Directory Record for Root Directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;enregistrement du répertoire racine de cette arborescence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;190&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Set Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;d1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;318&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Publisher Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;446&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Data Preparer Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;574&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Application Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;caractères &lt;code&gt;a1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;702&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Copyright File Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant de fichier en caractères &lt;code&gt;d1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;739&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Abstract File Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant de fichier en caractères &lt;code&gt;d1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;776&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bibliographic File Identifier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;identifiant de fichier en caractères &lt;code&gt;d1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;813&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Creation Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;830&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Modification Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;847&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Expiration Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;864&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Volume Effective Date and Time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;date et heure décimales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;881&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;File Structure Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;entier 8 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;882&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;champ réservé&lt;/td&gt;
&lt;td&gt;octet nul&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;883&lt;/td&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Application Use&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;contenu non spécifié par ECMA-119&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1395&lt;/td&gt;
&lt;td&gt;653&lt;/td&gt;
&lt;td&gt;champ réservé&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La section 8.5.6 d’ECMA-119 place donc le champ &lt;code&gt;Escape Sequences&lt;/code&gt; à l’offset relatif 88, sur 32 &lt;strong&gt;octets&lt;/strong&gt;. Ce champ sert à désigner des jeux de caractères ; il ne contient pas nécessairement le nom d’une extension.&lt;/p&gt;

&lt;p&gt;La spécification historique &lt;a href="https://nick-black.com/dankwiki/images/7/73/Microsoft_Joliet_Spec.pdf" rel="noopener noreferrer"&gt;Microsoft Joliet&lt;/a&gt; utilise précisément ce mécanisme pour décrire un ensemble de fichiers dont les noms emploient UCS-2. &lt;strong&gt;UCS-2&lt;/strong&gt; (&lt;em&gt;Universal Character Set, 2-byte form&lt;/em&gt;) est un codage historique fondé sur des unités de code de 16 bits. Nous devons donc examiner le champ avant de classifier le descripteur.&lt;/p&gt;

&lt;p&gt;Son offset absolu est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;17 × 2 048 + 88 = 34 904 = 0x8858
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cette arithmétique permet notamment d’éviter une erreur de transcription entre les offsets relatifs 87 et 88. Lisons le champ complet :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 32 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;88&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous observons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00008858: 25 2f 40 00 00 00 00 00 00 00 00 00 00 00 00 00  %/@.............
00008868: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les trois premiers octets, &lt;code&gt;25 2f 40&lt;/code&gt;, représentent &lt;code&gt;%/@&lt;/code&gt; en ASCII ; les 29 octets suivants sont nuls.&lt;/p&gt;

&lt;p&gt;La chaîne documentaire qui donne son sens à cette observation mérite d’être conservée. ISO 2022 définit un mécanisme de désignation des jeux de caractères. ISO 2375 en organise l’enregistrement, et le registre ISO-IR associe des séquences complètes à trois niveaux d’UCS-2 :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Registre&lt;/th&gt;
&lt;th&gt;Jeu de caractères&lt;/th&gt;
&lt;th&gt;Séquence complète enregistrée&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://web.archive.org/web/20201001033018id_/https://www.itscj.ipsj.or.jp/iso-ir/162.pdf" rel="noopener noreferrer"&gt;ISO-IR 162&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;UCS-2 niveau 1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ESC 25 2F 40&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://web.archive.org/web/20201001053549id_/https://www.itscj.ipsj.or.jp/iso-ir/174.pdf" rel="noopener noreferrer"&gt;ISO-IR 174&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;UCS-2 niveau 2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ESC 25 2F 43&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://web.archive.org/web/20201001050724id_/https://www.itscj.ipsj.or.jp/iso-ir/176.pdf" rel="noopener noreferrer"&gt;ISO-IR 176&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;UCS-2 niveau 3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ESC 25 2F 45&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;ESC&lt;/code&gt; désigne ici l’octet de contrôle d’échappement. Ces enregistrements datent de 1992 et 1993, avant la publication de Joliet en 1995. Le champ du descripteur supplémentaire contient les trois octets qui suivent &lt;code&gt;ESC&lt;/code&gt; ; Microsoft n’a donc pas inventé une étiquette privée appelée « Joliet », mais a sélectionné des identifiants de jeux de caractères déjà enregistrés.&lt;/p&gt;

&lt;p&gt;La spécification Joliet associe ces valeurs aux niveaux suivants :&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Valeur dans le champ&lt;/th&gt;
&lt;th&gt;Niveau Joliet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%/@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%/C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%/E&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dans notre image, &lt;code&gt;%/@&lt;/code&gt; constitue donc un marqueur Joliet de niveau 1. Cette conclusion reste étroite : le marqueur indique comment interpréter le descripteur et l’arborescence qui lui est associée, mais il ne prouve pas que cette arborescence entière respecte Joliet. Il faudrait la parcourir et la valider pour l’affirmer, ce que nous ne ferons pas dans cet article.&lt;/p&gt;

&lt;p&gt;Un lecteur général devrait également définir une politique pour d’autres séquences enregistrées. Notre probe reconnaîtra seulement &lt;code&gt;%/@&lt;/code&gt;, &lt;code&gt;%/C&lt;/code&gt; et &lt;code&gt;%/E&lt;/code&gt;. Une autre valeur restera le champ d’un descripteur supplémentaire reconnu, avec un jeu de caractères non pris en charge ; elle ne sera ni devinée ni silencieusement reclassée.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrouver les indices propres à la PlayStation 3
&lt;/h2&gt;

&lt;p&gt;Les seize premiers secteurs sont réservés par ISO 9660 à la zone système, mais ECMA-119 ne définit pas la disposition PlayStation 3 que nous y observons. Aucune spécification publique Sony normative de ces champs n’a été identifiée. Leur interprétation repose sur la convergence d’implémentations indépendantes — notamment &lt;a href="https://github.com/RPCS3/rpcs3/blob/677e13da42a36dc16eef090cc2a7d4d22e48aa5a/rpcs3/Loader/ISO.cpp" rel="noopener noreferrer"&gt;RPCS3&lt;/a&gt; et &lt;a href="https://github.com/aldostools/ps3netsrv/blob/4e2fba86e92f6d59884f0dba29ebc3b4c4ce2e5c/include/VIsoFile.h" rel="noopener noreferrer"&gt;ps3netsrv&lt;/a&gt; — ainsi que sur l’observation de notre image. Ce sont des preuves d’implémentation non normatives, pas une norme Sony.&lt;/p&gt;

&lt;p&gt;Les 512 premiers octets du secteur 1 suivent la disposition observée et corroborée ci-dessous. Le reste de ce secteur est nul dans notre image ; cette dernière propriété reste une observation locale.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Offset relatif&lt;/th&gt;
&lt;th&gt;Taille&lt;/th&gt;
&lt;th&gt;Contenu ou fonction&lt;/th&gt;
&lt;th&gt;Statut dans cet article&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x010&lt;/code&gt; (16)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PlayStation3&lt;/code&gt; suivi de quatre octets nuls&lt;/td&gt;
&lt;td&gt;reconnu par le probe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x010&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x020&lt;/code&gt; (32)&lt;/td&gt;
&lt;td&gt;identifiant produit complété par des espaces&lt;/td&gt;
&lt;td&gt;reconnu par le probe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x030&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x010&lt;/code&gt; (16)&lt;/td&gt;
&lt;td&gt;octets nuls&lt;/td&gt;
&lt;td&gt;observé, non utilisé&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x040&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x1b0&lt;/code&gt; (432)&lt;/td&gt;
&lt;td&gt;informations de disque dont la sémantique n’est pas établie ici&lt;/td&gt;
&lt;td&gt;opaque&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x1f0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x010&lt;/code&gt; (16)&lt;/td&gt;
&lt;td&gt;valeur opaque décrite comme un hachage par &lt;code&gt;ps3netsrv&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;opaque&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x200&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x600&lt;/code&gt; (1 536)&lt;/td&gt;
&lt;td&gt;fin du secteur, nulle dans l’image étudiée&lt;/td&gt;
&lt;td&gt;observation locale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Le marqueur de plateforme
&lt;/h3&gt;

&lt;p&gt;À la LBA 1, nous lisons d’abord un champ de 16 octets :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 16 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie réelle est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000800: 50 6c 61 79 53 74 61 74 69 6f 6e 33 00 00 00 00  PlayStation3....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les douze premiers octets codent &lt;code&gt;PlayStation3&lt;/code&gt; en ASCII ; quatre octets nuls complètent le champ. Nous parlons d’un champ de 16 octets, non d’un texte arbitraire de 16 caractères.&lt;/p&gt;

&lt;h3&gt;
  
  
  L’identifiant produit
&lt;/h3&gt;

&lt;p&gt;Le champ adjacent commence 16 octets plus loin et en occupe 32 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xxd &lt;span class="nt"&gt;-g&lt;/span&gt; 1 &lt;span class="nt"&gt;-l&lt;/span&gt; 32 &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; SECTOR &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous observons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000810: 42 4c 45 53 2d 30 30 39 33 32 20 20 20 20 20 20  BLES-00932
00000820: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les dix premiers octets donnent &lt;code&gt;BLES-00932&lt;/code&gt; ; vingt-deux espaces complètent le champ. Notre reconnaissance minimale exigera le marqueur &lt;code&gt;PlayStation3&lt;/code&gt; complet, puis retirera seulement les espaces terminaux de l’identifiant produit. Cette combinaison suffit à la ligne que nous voulons rapporter, mais ne valide pas toutes les structures propres à un disque PlayStation 3.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Construire le probe dans GHCi
&lt;/h2&gt;

&lt;p&gt;Nous avons maintenant une suite précise de règles à transcrire. &lt;strong&gt;GHCi&lt;/strong&gt; est l’interpréteur interactif de Glasgow Haskell Compiler : il permet de saisir des définitions Haskell et d’en voir immédiatement le résultat. Nous allons y construire un prototype direct, sans infrastructure générale de décodage binaire.&lt;/p&gt;

&lt;p&gt;Depuis le même terminal, nous rendons le chemin accessible au processus, puis lançons GHCi avec le paquet &lt;code&gt;bytestring&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;IMAGE&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.iso&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
ghci &lt;span class="nt"&gt;-package&lt;/span&gt; bytestring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L’invite ressemble à :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ghci&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un &lt;code&gt;ByteString&lt;/code&gt; est une suite compacte d’octets. Nous utiliserons sa variante stricte parce que chacune de nos lectures est petite et bornée. Les interfaces employées sont documentées dans &lt;a href="https://hackage.haskell.org/package/base/docs/System-IO.html" rel="noopener noreferrer"&gt;&lt;code&gt;System.IO&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://hackage.haskell.org/package/bytestring/docs/Data-ByteString.html" rel="noopener noreferrer"&gt;&lt;code&gt;Data.ByteString&lt;/code&gt;&lt;/a&gt; et &lt;a href="https://hackage.haskell.org/package/base/docs/Data-Word.html" rel="noopener noreferrer"&gt;&lt;code&gt;Data.Word&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saisie multiligne dans GHCi.&lt;/strong&gt; Les blocs qui suivent sont présentés comme du code Haskell ordinaire, sans commandes propres à l’interpréteur. Pour saisir une définition multiligne directement dans GHCi, commencez par entrer &lt;code&gt;:{&lt;/code&gt; sur une ligne isolée, saisissez ou collez la définition, puis terminez par &lt;code&gt;:}&lt;/code&gt;, également sur une ligne isolée. Ces deux commandes sont volontairement omises des blocs afin de ne pas les confondre avec la syntaxe du langage Haskell.&lt;/p&gt;

&lt;h3&gt;
  
  
  Octets bruts, puis décodage endian
&lt;/h3&gt;

&lt;p&gt;Commençons par les imports communs :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Control.Monad&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Bits&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;shiftL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.|.&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;Data.ByteString&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;BS&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;Data.ByteString.Char8&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;BSC&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Word&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Word8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Word16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Word32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;System.Environment&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;IOMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ReadMode&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kt"&gt;SeekMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;AbsoluteSeek&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;hFileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;hSeek&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;withBinaryFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avant toute entrée-sortie, reprenons quatre octets synthétiques. Leur type ne leur attribue encore ni signification ni ordre :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rawBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x03&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt; &lt;span class="n"&gt;rawBytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GHCi affiche :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0,0,0,3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;L’endianness n’est pas une propriété du &lt;code&gt;ByteString&lt;/code&gt; ni un mode de lecture du fichier. C’est une règle du champ que nous sommes en train de décoder. Nous définissons deux transformations privées :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Décode un champ big-endian dont l’appelant a déjà contrôlé la largeur.&lt;/span&gt;
&lt;span class="n"&gt;decodeBE&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Bits&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Num&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;decodeBE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foldl'&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shiftL&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;fromIntegral&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="c1"&gt;-- Même opération pour un champ little-endian.&lt;/span&gt;
&lt;span class="n"&gt;decodeLE&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Bits&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Num&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="n"&gt;decodeLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foldr'&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shiftL&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;.|.&lt;/span&gt; &lt;span class="n"&gt;fromIntegral&lt;/span&gt; &lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La même suite brute donne des valeurs différentes selon l’interprétation :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;decodeBE&lt;/span&gt; &lt;span class="n"&gt;rawBytes&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word32&lt;/span&gt;
&lt;span class="n"&gt;decodeLE&lt;/span&gt; &lt;span class="n"&gt;rawBytes&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word32&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Résultat :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3
50331648
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;50331648&lt;/code&gt; vaut &lt;code&gt;0x03000000&lt;/code&gt;. Le format, et non une préférence du programmeur, détermine quel décodeur convient. Ces fonctions n’imposent pas elles-mêmes une largeur ; dans ce prototype, cette garantie appartient à la frontière de lecture qui les précède.&lt;/p&gt;

&lt;h3&gt;
  
  
  Une lecture exacte et bornée
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;BS.hGet&lt;/code&gt; peut renvoyer moins d’octets que demandé si la fin du fichier survient. Nous devons contrôler la longueur avant tout &lt;code&gt;BS.index&lt;/code&gt; ou décodage. Les offsets restent des &lt;code&gt;Integer&lt;/code&gt;, afin de ne pas limiter artificiellement l’adressage aux fichiers de moins de 4 Gio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;sectorSize&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt;
&lt;span class="n"&gt;sectorSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;

&lt;span class="c1"&gt;-- Centralise les lectures bornées et refuse tout résultat partiel.&lt;/span&gt;
&lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
&lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"negative read offset"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"negative read width"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;otherwise&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;hSeek&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="kt"&gt;AbsoluteSeek&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;
        &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hGet&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
        &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
            &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;
            &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;ioError&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"short read at offset "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;": expected "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;" bytes, got "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ReadMode&lt;/code&gt; exprime l’intention de lecture seule. &lt;code&gt;withBinaryFile&lt;/code&gt; referme le handle même si une exception survient. Vérifions cette frontière avec les sept octets déjà observés :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;imageFile&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;getEnv&lt;/span&gt; &lt;span class="s"&gt;"IMAGE"&lt;/span&gt;

&lt;span class="n"&gt;headerBytes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;withBinaryFile&lt;/span&gt; &lt;span class="n"&gt;imageFile&lt;/span&gt; &lt;span class="kt"&gt;ReadMode&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sectorSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;

&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="n"&gt;headerBytes&lt;/span&gt;
&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt; &lt;span class="n"&gt;headerBytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous retrouvons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7
[1,67,68,48,48,49,1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les décimaux &lt;code&gt;67 68 48 48 49&lt;/code&gt; sont les codes ASCII de &lt;code&gt;CD001&lt;/code&gt;. Si GHCi affiche parfois un &lt;code&gt;ByteString&lt;/code&gt; textuel sous la forme &lt;code&gt;"\SOHCD001\SOH"&lt;/code&gt;, &lt;code&gt;SOH&lt;/code&gt; n’est pas un octet ajouté : c’est le nom d’affichage du caractère de contrôle &lt;code&gt;0x01&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Décoder les champs utiles du descripteur principal
&lt;/h3&gt;

&lt;p&gt;Nous donnons maintenant un type aux trois composants de l’en-tête, puis un type aux trois informations ISO que notre lecture conservera :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Représente l’en-tête commun de sept octets des descripteurs ISO 9660.&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;headerType&lt;/span&gt;       &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word8&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headerIdentifier&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headerVersion&lt;/span&gt;    &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word8&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- Conserve uniquement les champs du descripteur principal utiles au probe.&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;IsoInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;IsoInfo&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;isoVolumeIdentifier&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isoVolumeSpaceSize&lt;/span&gt;  &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word32&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isoLogicalBlockSize&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word16&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- Précondition : la largeur de l’en-tête a été contrôlée en amont.&lt;/span&gt;
&lt;span class="n"&gt;decodeDescriptorHeader&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt;
&lt;span class="n"&gt;decodeDescriptorHeader&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;headerType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headerIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headerVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;decodeDescriptorHeader&lt;/code&gt; emploie &lt;code&gt;BS.index&lt;/code&gt;, qui serait partiel sur une entrée trop courte. Il reste privé et ne reçoit ici que des données dont la largeur a déjà été vérifiée par &lt;code&gt;readExactlyAt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;La fonction suivante lit exactement le secteur de 2 048 octets à la LBA 16, contrôle son en-tête, découpe les trois champs, décode séparément les deux copies numériques et vérifie leur égalité. Pour le contrôle de cohérence avec le fichier hôte, elle refuse seulement une longueur insuffisante ; elle accepte d’éventuelles données terminales.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Lit et valide le sous-ensemble du descripteur principal nécessaire au probe.&lt;/span&gt;
&lt;span class="n"&gt;readPrimaryVolumeDescriptor&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="kt"&gt;IsoInfo&lt;/span&gt;
&lt;span class="n"&gt;readPrimaryVolumeDescriptor&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;descriptor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sectorSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;
    &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeDescriptorHeader&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;

    &lt;span class="n"&gt;unless&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headerType&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;headerIdentifier&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="s"&gt;"CD001"&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;headerVersion&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"no supported Primary Volume Descriptor at LBA 16"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;volumeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;spaceField&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;blockField&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt; &lt;span class="n"&gt;descriptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spaceLEBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spaceBEBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitAt&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;spaceField&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blockLEBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blockBEBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;splitAt&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="n"&gt;blockField&lt;/span&gt;

        &lt;span class="n"&gt;spaceLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeLE&lt;/span&gt; &lt;span class="n"&gt;spaceLEBytes&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word32&lt;/span&gt;
        &lt;span class="n"&gt;spaceBE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeBE&lt;/span&gt; &lt;span class="n"&gt;spaceBEBytes&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word32&lt;/span&gt;
        &lt;span class="n"&gt;blockLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeLE&lt;/span&gt; &lt;span class="n"&gt;blockLEBytes&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word16&lt;/span&gt;
        &lt;span class="n"&gt;blockBE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodeBE&lt;/span&gt; &lt;span class="n"&gt;blockBEBytes&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Word16&lt;/span&gt;

    &lt;span class="n"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spaceLE&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;spaceBE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"inconsistent Volume Space Size copies"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blockLE&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;blockBE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"inconsistent Logical Block Size copies"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blockLE&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"zero Logical Block Size"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;hostSize&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;hFileSize&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;
    &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;declaredSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toInteger&lt;/span&gt; &lt;span class="n"&gt;spaceLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;toInteger&lt;/span&gt; &lt;span class="n"&gt;blockLE&lt;/span&gt;
    &lt;span class="n"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostSize&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;declaredSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ioError&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"truncated image: declared "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;declaredSize&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;" bytes, host file has "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;hostSize&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="n"&gt;pure&lt;/span&gt;
        &lt;span class="kt"&gt;IsoInfo&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;isoVolumeIdentifier&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropWhileEnd&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;volumeField&lt;/span&gt;
            &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isoVolumeSpaceSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spaceLE&lt;/span&gt;
            &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;isoLogicalBlockSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blockLE&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exécutons ce bloc sur l’image :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;isoInfo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;withBinaryFile&lt;/span&gt; &lt;span class="n"&gt;imageFile&lt;/span&gt; &lt;span class="kt"&gt;ReadMode&lt;/span&gt; &lt;span class="n"&gt;readPrimaryVolumeDescriptor&lt;/span&gt;
&lt;span class="n"&gt;isoInfo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie importante rassemble les valeurs reconstruites dans le Shell :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IsoInfo {isoVolumeIdentifier = "PS3VOLUME", isoVolumeSpaceSize = 4401056, isoLogicalBlockSize = 2048}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce résultat signifie que les contrôles explicitement codés ont réussi. Il ne prétend pas valider les nombreux champs du descripteur que nous n’avons pas lus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parcourir directement et sans débordement la séquence ISO 9660
&lt;/h3&gt;

&lt;p&gt;Nous généralisons maintenant la lecture des sept octets d’en-tête à une LBA quelconque. Le scanner part de 16, exige &lt;code&gt;CD001&lt;/code&gt; et la version 1 à chaque étape, inclut le terminateur dans son résultat et s’y arrête. La limite de 64 descripteurs est un choix défensif du probe : elle transforme l’absence de terminateur proche en erreur au lieu de poursuivre sans borne.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Retrouve l’en-tête situé à une LBA donnée.&lt;/span&gt;
&lt;span class="n"&gt;readDescriptorHeader&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt;
&lt;span class="n"&gt;readDescriptorHeader&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;decodeDescriptorHeader&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sectorSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;

&lt;span class="c1"&gt;-- Parcourt au plus 64 descripteurs et s’arrête au terminateur de type 255.&lt;/span&gt;
&lt;span class="n"&gt;scanIsoSequence&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;scanIsoSequence&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="kt"&gt;[]&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;go&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="n"&gt;accumulated&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="n"&gt;ioError&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt; &lt;span class="s"&gt;"ISO descriptor scan limit reached"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;otherwise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
            &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readDescriptorHeader&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt;
            &lt;span class="n"&gt;unless&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headerIdentifier&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="s"&gt;"CD001"&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;headerVersion&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ioError&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userError&lt;/span&gt;
                        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid ISO descriptor header at LBA "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

            &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;located&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;headerType&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;
                &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;located&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;accumulated&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;located&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;accumulated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous n’avons plus à connaître d’avance le nombre de descripteurs :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;descriptors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;withBinaryFile&lt;/span&gt; &lt;span class="n"&gt;imageFile&lt;/span&gt; &lt;span class="kt"&gt;ReadMode&lt;/span&gt; &lt;span class="n"&gt;scanIsoSequence&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headerType&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;descriptors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous obtenons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[(16,1),(17,2),(18,255)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le secteur 19 n’est jamais consommé par ce scanner ISO 9660. C’est exactement la différence observée entre la boucle Shell exploratoire et la règle du format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Examiner tous les descripteurs supplémentaires et classifier Joliet
&lt;/h3&gt;

&lt;p&gt;ECMA-119 autorise zéro ou plusieurs descripteurs supplémentaires. Sélectionner seulement le premier serait donc une hypothèse injustifiée. Nous lisons le champ &lt;code&gt;Escape Sequences&lt;/code&gt; complet de chacun d’eux et conservons aussi les valeurs que notre petit probe ne sait pas classifier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel1&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel2&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel3&lt;/span&gt;
    &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;SupplementaryFinding&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;JolietFinding&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;UnsupportedCharacterSet&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
    &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- Classe le marqueur Joliet sans perdre les champs non pris en charge.&lt;/span&gt;
&lt;span class="n"&gt;classifySupplementary&lt;/span&gt;
    &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;SupplementaryFinding&lt;/span&gt;
&lt;span class="n"&gt;classifySupplementary&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;
        &lt;span class="n"&gt;zeroPadding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;level&lt;/span&gt;
            &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="s"&gt;"%/@"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel1&lt;/span&gt;
            &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="s"&gt;"%/C"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel2&lt;/span&gt;
            &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="s"&gt;"%/E"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel3&lt;/span&gt;
            &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;otherwise&lt;/span&gt;                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
    &lt;span class="kr"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zeroPadding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="n"&gt;jolietLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;JolietFinding&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="n"&gt;jolietLevel&lt;/span&gt;
        &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;UnsupportedCharacterSet&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="c1"&gt;-- Examine tous les descripteurs de type 2, pas seulement le premier.&lt;/span&gt;
&lt;span class="n"&gt;readSupplementaryFindings&lt;/span&gt;
    &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Handle&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="kt"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;DescriptorHeader&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;SupplementaryFinding&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;readSupplementaryFindings&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;allDescriptors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;mapM&lt;/span&gt; &lt;span class="n"&gt;readOne&lt;/span&gt; &lt;span class="n"&gt;supplementaryLbas&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;supplementaryLbas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt;
        &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;allDescriptors&lt;/span&gt;
        &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headerType&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;readOne&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;sectorSize&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
        &lt;span class="n"&gt;pure&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;classifySupplementary&lt;/span&gt; &lt;span class="n"&gt;lba&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Appliquons cette lecture à tous les candidats trouvés :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;supplementaryFindings&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;withBinaryFile&lt;/span&gt; &lt;span class="n"&gt;imageFile&lt;/span&gt; &lt;span class="kt"&gt;ReadMode&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;readSupplementaryFindings&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;descriptors&lt;/span&gt;

&lt;span class="n"&gt;supplementaryFindings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie situe le résultat autant qu’elle le classe :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[JolietFinding 17 JolietLevel1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le constructeur &lt;code&gt;UnsupportedCharacterSet&lt;/code&gt; évite de transformer une séquence inconnue en absence de descripteur. Quant à &lt;code&gt;JolietFinding&lt;/code&gt;, il atteste seulement le marqueur, jamais la conformité de l’arborescence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reconnaître le minimum propre à la PlayStation 3
&lt;/h3&gt;

&lt;p&gt;Pour produire l’identifiant produit, 48 octets suffisent : le champ marqueur de 16 octets, puis le champ produit de 32 octets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Rapporte l’identifiant produit uniquement si le marqueur PS3 est exact.&lt;/span&gt;
&lt;span class="n"&gt;readPs3ProductId&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;readPs3ProductId&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readExactlyAt&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;sectorSize&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;
    &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;
        &lt;span class="n"&gt;productField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;
        &lt;span class="n"&gt;expectedMarker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
            &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pack&lt;/span&gt; &lt;span class="s"&gt;"PlayStation3"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replicate&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;pure&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;marker&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expectedMarker&lt;/span&gt;
            &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dropWhileEnd&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;productField&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exécutons-la :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;ps3ProductId&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;withBinaryFile&lt;/span&gt; &lt;span class="n"&gt;imageFile&lt;/span&gt; &lt;span class="kt"&gt;ReadMode&lt;/span&gt; &lt;span class="n"&gt;readPs3ProductId&lt;/span&gt;
&lt;span class="n"&gt;ps3ProductId&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nous retrouvons :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Just "BLES-00932"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Nothing&lt;/code&gt; signifierait seulement que ce marqueur d’implémentation n’a pas été reconnu. Cela ne suffirait pas à classifier universellement l’entrée comme « non-PlayStation 3 ».&lt;/p&gt;

&lt;h3&gt;
  
  
  Agréger les observations et les rendre dans un ordre stable
&lt;/h3&gt;

&lt;p&gt;Il reste à ouvrir l’image une fois, à effectuer les lectures précédentes par offsets absolus, puis à construire les lignes reconnues. La structure finale conserve les descripteurs supplémentaires non pris en charge, même si le rendu demandé n’émet que les niveaux Joliet reconnus.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Réunit les observations indépendantes avant leur rendu textuel.&lt;/span&gt;
&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;ProbeResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ProbeResult&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;resultPs3ProductId&lt;/span&gt;       &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;BS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;ByteString&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultIso9660&lt;/span&gt;            &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IsoInfo&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultSupplementary&lt;/span&gt;      &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;SupplementaryFinding&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- Ouvre l’image une seule fois, en lecture seule, pour toutes les lectures.&lt;/span&gt;
&lt;span class="n"&gt;probe&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;FilePath&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="kt"&gt;ProbeResult&lt;/span&gt;
&lt;span class="n"&gt;probe&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;withBinaryFile&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="kt"&gt;ReadMode&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;isoDescriptors&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;scanIsoSequence&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;
        &lt;span class="n"&gt;recognizedIso&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readPrimaryVolumeDescriptor&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;
        &lt;span class="n"&gt;supplementary&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;
            &lt;span class="n"&gt;readSupplementaryFindings&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="n"&gt;isoDescriptors&lt;/span&gt;
        &lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;readPs3ProductId&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;
        &lt;span class="n"&gt;pure&lt;/span&gt;
            &lt;span class="kt"&gt;ProbeResult&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;resultPs3ProductId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;
                &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultIso9660&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recognizedIso&lt;/span&gt;
                &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultSupplementary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;supplementary&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;renderJolietLevel&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="n"&gt;renderJolietLevel&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;
&lt;span class="n"&gt;renderJolietLevel&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;
&lt;span class="n"&gt;renderJolietLevel&lt;/span&gt; &lt;span class="kt"&gt;JolietLevel3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;

&lt;span class="c1"&gt;-- Produit uniquement les lignes reconnues, dans un ordre stable.&lt;/span&gt;
&lt;span class="n"&gt;renderProbeResult&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;ProbeResult&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;renderProbeResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;maybe&lt;/span&gt;
        &lt;span class="kt"&gt;[]&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ps3.product-id="&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultPs3ProductId&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"iso9660.volume-id="&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;BSC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unpack&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isoVolumeIdentifier&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultIso9660&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
       &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"iso9660.logical-block-size="&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isoLogicalBlockSize&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultIso9660&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"joliet.level="&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;renderJolietLevel&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt;
       &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;JolietFinding&lt;/span&gt; &lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;resultSupplementary&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lançons enfin le probe et son rendu :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;probe&lt;/span&gt; &lt;span class="n"&gt;imageFile&lt;/span&gt;
&lt;span class="n"&gt;mapM_&lt;/span&gt; &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderProbeResult&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La sortie est exactement :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps3.product-id=BLES-00932
iso9660.volume-id=PS3VOLUME
iso9660.logical-block-size=2048
joliet.level=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;La première ligne repose sur un marqueur et une disposition de champs corroborés par plusieurs implémentations PlayStation 3 non normatives. Les deuxième et troisième lignes proviennent de champs précis du descripteur principal de volume ISO 9660, après contrôle de l’en-tête, des deux copies endian et de la taille minimale du fichier. La quatrième rapporte un marqueur Joliet de niveau 1 trouvé dans l’unique descripteur supplémentaire observé, après parcours borné de toute la séquence ISO 9660.&lt;/p&gt;

&lt;p&gt;Elles ne prouvent ni la conformité complète à ISO 9660 ou Joliet, ni la validité des répertoires et des noms UCS-2, ni l’état de chiffrement de l’image. Elles n’autorisent encore aucun parcours de répertoire, aucune extraction, aucun déchiffrement et aucune écriture. Elles disent seulement que quatre informations ont été reconnues selon les règles et les limites que nous avons rendues explicites.&lt;/p&gt;

&lt;p&gt;Notre code direct révèle enfin une répétition concrète : plusieurs lectures fixes reviennent à combiner un offset, une largeur et une opération de décodage, puis à interpréter le résultat. Un prochain article technique pourra partir de cette duplication observée — non d’une abstraction décidée à l’avance — pour étudier comment décrire ces opérations et leur donner plusieurs interprétations plus générales.&lt;/p&gt;

&lt;p&gt;↑ Retour au sommaire&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>modding</category>
      <category>reverseengineering</category>
      <category>french</category>
    </item>
  </channel>
</rss>
