DEV Community

Cover image for Hexegesis : construction d’un programme PlayStation 3 minimal
Karim DRIDI
Karim DRIDI

Posted on

Hexegesis : construction d’un programme PlayStation 3 minimal

← Revenir au sommaire de la série

Construire un cas minimal et reproductible

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.

Les informations nécessaires se trouvent dans plusieurs sources complémentaires : ps3toolchain 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.

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.

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

Sommaire

Le résultat attendu

Le répertoire de jeu construit dans cet article possède l’arborescence suivante :

disc-root/
├── PS3_DISC.SFB
└── PS3_GAME/
    ├── PARAM.SFO
    └── USRDIR/
        └── EBOOT.BIN
Enter fullscreen mode Exit fullscreen mode

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 PS3 Developer Wiki décrit également l’arborescence habituelle d’un jeu sur disque, avec davantage de fichiers que notre exemple minimal.

PS3_GAME/USRDIR/EBOOT.BIN — le programme

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

Dans notre exemple, EBOOT.BIN contient du code destiné au PPU (Power Processing Unit), l’unité de traitement principale du processeur Cell. ppu-gcc produit d’abord un exécutable intermédiaire au format ELF (Executable and Linkable Format) ; les outils PS3 construisent ensuite, à partir de cet ELF, un fichier exécutable au format SELF. Ce fichier est enregistré sous le nom conventionnel EBOOT.BIN.

PS3_GAME/PARAM.SFO — les paramètres du jeu

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.

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

PS3_DISC.SFB — le descripteur du support

Ce fichier se place à la racine d’un jeu sur disque et participe à l’identification du support.

Dans notre exemple minimal, ce fichier se réduit à la signature .SFB. Nous reviendrons sur cette simplification lorsque nous le créerons.

Cette arborescence suggère déjà le déroulement du travail :

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
Enter fullscreen mode Exit fullscreen mode

Le fichier central est EBOOT.BIN : sans lui, il n’y a pas de programme à exécuter. PARAM.SFO et PS3_DISC.SFB 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.

Nous allons maintenant construire chaque fichier pas à pas. La version du projet utilisée pour cet article est disponible sur GitHub. Elle contient également un script qui automatise la construction.

↑ Retour au sommaire

Construire l’image Docker

La construction repose sur trois ensembles d’outils :

  • ps3toolchain installe ppu-gcc, le compilateur croisé qui produit des fichiers ELF destinés au PPU, ainsi que ppu-strip, qui en retire les informations inutiles à l’exécution ;
  • PSL1GHT 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 sfo, qui crée PARAM.SFO, sprxlinker, qui prépare l’ELF pour la conversion, et make_self, qui produit le SELF ;
  • les utilitaires usuels terminent la préparation du disque : printf crée le fichier minimal PS3_DISC.SFB, xorriso place l’arborescence dans une image ISO 9660 générique, puis printf et dd écrivent les octets supplémentaires attendus par RPCS3.

Nous réunirons tous ces outils dans une même image Docker.

Récupérons le projet, puis plaçons-nous dans son répertoire :

git clone https://github.com/hexegesis/hexegesis-experiments.git
cd hexegesis-experiments
git checkout 8ca7eb9c02f36cc42c26b157f6013c68c56616e1
cd hello-world
Enter fullscreen mode Exit fullscreen mode

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

Le Dockerfile.ps3dev construit les révisions indiquées de ps3toolchain, PSL1GHT et ps3libraries, puis installe xorriso et xxd. Construisons l’image :

docker build \
  --tag hexegesis-ps3dev \
  --file Dockerfile.ps3dev \
  .
Enter fullscreen mode Exit fullscreen mode

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.

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

docker run --rm -it \
  --volume "$PWD:/work" \
  --workdir /work \
  --entrypoint /bin/sh \
  hexegesis-ps3dev
Enter fullscreen mode Exit fullscreen mode

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 docker run.

pwd

for tool in ppu-gcc ppu-strip sprxlinker make_self sfo xorriso xxd; do
  command -v "$tool"
done
Enter fullscreen mode Exit fullscreen mode

La première commande affiche /work. 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 ; --rm supprimera ensuite le conteneur temporaire, mais pas les fichiers du volume monté.

↑ Retour au sommaire

Créer les paramètres du jeu : PARAM.SFO

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

Le PS3 Developer Wiki décrit sa structure 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 sfo installé par PSL1GHT produit cette structure à partir d’un fichier XML lisible.

Déclarer les paramètres

Créons le fichier param-sfo.xml avec le contenu suivant :

<?xml version="1.0"?>
<sfo>
  <!-- Version de l'application. La forme attendue est NN.NN. -->
  <value name="APP_VER" type="string">01.00</value>

  <!-- Drapeaux d'attributs optionnels. La valeur 0 n'en active aucun. -->
  <value name="ATTRIBUTE" type="integer">0</value>

  <!-- Indique si le contenu est amorçable : 1 pour oui, 0 pour non. -->
  <value name="BOOTABLE" type="integer">1</value>

  <!-- Nature du contenu. DG désigne un jeu sur disque. -->
  <!-- RPCS3 attend précisément DG lorsqu'il reconnaît ce répertoire comme jeu disque. -->
  <value name="CATEGORY" type="string">DG</value>

  <!-- Niveau de contrôle parental, sur une échelle communautairement documentée de 0 à 11. -->
  <!-- Nous conservons la valeur 0 du modèle fourni par PSL1GHT. -->
  <value name="PARENTAL_LEVEL" type="integer">0</value>

  <!-- Version minimale du logiciel système annoncée par le jeu, sous la forme NN.NNNN. -->
  <value name="PS3_SYSTEM_VER" type="string">01.8000</value>

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

  <!-- Masque des formats audio annoncés au système. -->
  <!-- La valeur 1 correspond uniquement au mode LPCM 2.0. -->
  <!-- Le programme n'utilise aucune API audio et n'annonce donc aucun mode multicanal. -->
  <value name="SOUND_FORMAT" type="integer">1</value>

  <!-- Titre lisible du jeu. Des clés TITLE_00, TITLE_01, etc. peuvent fournir des traductions. -->
  <value name="TITLE" type="string">Bonjour depuis Hexegesis</value>

  <!-- Identifiant du titre. HXGS00001 est un identifiant synthétique de neuf caractères. -->
  <value name="TITLE_ID" type="string">HXGS00001</value>

  <!-- Version principale déclarée pour le contenu, distincte de APP_VER. -->
  <value name="VERSION" type="string">01.00</value>
</sfo>
Enter fullscreen mode Exit fullscreen mode

Chaque balise <value> contient trois informations. Prenons cet exemple :

<value name="BOOTABLE" type="integer">1</value>
Enter fullscreen mode Exit fullscreen mode

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

Produire le fichier binaire

Créons le répertoire de destination, puis demandons à sfo de convertir le XML :

mkdir -p disc-root/PS3_GAME
sfo --fromxml param-sfo.xml disc-root/PS3_GAME/PARAM.SFO
Enter fullscreen mode Exit fullscreen mode

Vérifions que l’outil peut relire le fichier produit :

sfo --list disc-root/PS3_GAME/PARAM.SFO
Enter fullscreen mode Exit fullscreen mode

La sortie affiche les onze paramètres du XML. Vérifions notamment que CATEGORY vaut DG, que TITLE vaut Bonjour depuis Hexegesis et que TITLE_ID vaut HXGS00001. Cette relecture confirme que l’outil sfo 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 PARAM.SFO dépasse l’objectif de ce tutoriel.

↑ Retour au sommaire

Créer le descripteur de disque minimal : PS3_DISC.SFB

Le fichier PS3_DISC.SFB se trouve à la racine du disque, tandis que PARAM.SFO se trouve dans PS3_GAME. 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 PS3 Developer Wiki documente un descripteur plus complet, avec des enregistrements et des drapeaux qui peuvent notamment désigner des paramètres du titre.

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

printf '.SFB' > disc-root/PS3_DISC.SFB
Enter fullscreen mode Exit fullscreen mode

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

Cette simplification est volontaire. Dans la révision de RPCS3 utilisée pour cette expérience, IsValidSfb exige que PS3_DISC.SFB existe, comporte au moins quatre octets et commence par le préfixe .SFB. Le démarrage d’un répertoire de jeu sur disque appelle cette vérification. 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.

↑ Retour au sommaire

Compiler le programme et créer EBOOT.BIN

Le fichier de paramètres et le descripteur du support existent maintenant. Il reste à produire le programme qu’ils accompagnent.

Créons les répertoires nécessaires au code source, à l’ELF préparé et au programme final :

mkdir -p source build disc-root/PS3_GAME/USRDIR
Enter fullscreen mode Exit fullscreen mode

Créons ensuite source/main.c :

#include <stdio.h>
#include <stdlib.h>

/*
 * É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.
 */
int main(void)
{
    /* puts ajoute automatiquement un retour à la ligne après le message. */
    puts("Bonjour depuis Hexegesis.");

    /* Indique au système que le programme s'est terminé normalement. */
    return EXIT_SUCCESS;
}
Enter fullscreen mode Exit fullscreen mode

Le sigle TTY, hérité des téléscripteurs, désigne ici la sortie textuelle émulée de la PS3, que RPCS3 consigne dans son journal.

Compiler le code C et produire l’ELF PPU

ppu-gcc est un compilateur croisé : 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 :

ppu-gcc \
  -O2 \
  -Wall \
  -mcpu=cell \
  -mhard-float \
  -c source/main.c \
  -o source/main.o
Enter fullscreen mode Exit fullscreen mode

Voici le rôle de chaque option :

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

Procédons ensuite à l’édition de liens pour produire un exécutable PPU au format ELF :

ppu-gcc \
  -mcpu=cell \
  -mhard-float \
  source/main.o \
  -L"$PSL1GHT/ppu/lib" \
  -lrt \
  -llv2 \
  -o hello-world.elf
Enter fullscreen mode Exit fullscreen mode

Ces options complètent l’édition de liens :

  • -L"$PSL1GHT/ppu/lib" ajoute le répertoire contenant les bibliothèques de PSL1GHT aux chemins parcourus par l’éditeur de liens ;
  • -lrt lie librt. Lorsque puts écrit sur stdout, l’implémentation de write fournie par cette bibliothèque appelle sysTtyWrite. Le message est ainsi envoyé au TTY de la PS3, que RPCS3 consigne dans son journal ;
  • -llv2 lie liblv2, la bibliothèque de base utilisée pour interagir avec LV2, le noyau système de la PS3.

L’ordre -lrt -llv2 reprend celui du Makefile du projet.

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

Transformer l’ELF en SELF

L’ELF ne peut pas encore servir directement d’EBOOT.BIN. Reproduisons les trois commandes employées par les règles de construction de PSL1GHT :

ppu-strip hello-world.elf -o build/hello-world.elf
sprxlinker build/hello-world.elf
make_self build/hello-world.elf hello-world.self
Enter fullscreen mode Exit fullscreen mode

Lors de la commande précédente, ppu-gcc 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 :

  • ppu-strip crée build/hello-world.elf à partir de l’ELF original, puis supprime de cette copie la table des symboles et les autres informations inutiles à l’exécution ;
  • malgré son nom, sprxlinker 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 ;
  • make_self ne recompile pas le programme. Il construit un fichier SELF à partir des en-têtes et des segments de l’ELF préparé, ajoute les métadonnées propres à ce format, puis compresse et chiffre les segments avant d’ajouter les informations d’intégrité des segments et la signature du SELF. Les instructions produites par GCC ne sont pas recompilées.

PSL1GHT ne fournit pas de manuel distinct pour sprxlinker et make_self. Leur rôle est établi ici à partir des règles de construction du projet et de leur code source.

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 -g afin d’y ajouter les informations de débogage ; ce n’est pas l’objectif de ce tutoriel.

Nous disposons maintenant du fichier SELF produit par PSL1GHT à partir de l’ELF. Copions-le à l’emplacement attendu pour EBOOT.BIN :

cp hello-world.self disc-root/PS3_GAME/USRDIR/EBOOT.BIN
Enter fullscreen mode Exit fullscreen mode

Nous pouvons enfin regarder l’arborescence complète :

find disc-root -type f -print | sort
Enter fullscreen mode Exit fullscreen mode
disc-root/PS3_DISC.SFB
disc-root/PS3_GAME/PARAM.SFO
disc-root/PS3_GAME/USRDIR/EBOOT.BIN
Enter fullscreen mode Exit fullscreen mode

Les trois fichiers présentés au début existent désormais au bon emplacement.

↑ Retour au sommaire

Vérifier le répertoire de jeu dans RPCS3

Les essais de ce tutoriel ont été réalisés avec RPCS3 Version: 0.0.42-19895-c6e96729 Alpha | master | local_build.

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

Dans RPCS3, choisissez File → Boot Game puis sélectionnez le dossier disc-root. 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.

RPCS3 lance alors le programme. Dans son journal TTY, vous pouvez lire le message suivant :

Bonjour depuis Hexegesis.
Enter fullscreen mode Exit fullscreen mode

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.

Revenons maintenant au shell Docker, resté ouvert, pour créer l’image ISO.

↑ Retour au sommaire

Créer une image ISO 9660

Une image ISO représente le contenu d’un disque sous la forme d’une suite de secteurs. Nous demandons à xorriso 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.

xorriso -as mkisofs \
  -iso-level 1 \
  -volid HXGS00001 \
  -no-pad \
  -output hello-world.iso \
  disc-root
Enter fullscreen mode Exit fullscreen mode

Les arguments donnés à xorriso ont les rôles suivants :

  • -as mkisofs sélectionne l’interface de création compatible avec mkisofs ;
  • -iso-level 1 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 résumé de Wikipédia présente également ces restrictions ; tous les noms de notre arborescence les respectent ;
  • -volid HXGS00001 inscrit l’identifiant du volume ; nous choisissons ici la même valeur que TITLE_ID, même si les deux champs sont indépendants ;
  • -no-pad désactive les 300 Kio de remplissage que xorriso ajoute normalement à la fin de l’image ;
  • -output hello-world.iso nomme l’image produite ;
  • disc-root est le répertoire à placer dans l’image.

Vérifions l’inventaire sans extraire les fichiers :

xorriso -indev hello-world.iso -find / -type f -exec lsdl
Enter fullscreen mode Exit fullscreen mode

Parmi les lignes affichées, vérifiez la présence des trois chemins suivants :

/PS3_DISC.SFB
/PS3_GAME/PARAM.SFO
/PS3_GAME/USRDIR/EBOOT.BIN
Enter fullscreen mode Exit fullscreen mode

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 (Logical Block Address). Un offset indique une position en octets depuis le début du fichier : la LBA 16 commence donc à l’offset 16 × 2048 = 32768, soit 0x8000. Les secteurs de LBA 0 à 15 constituent la zone système (System Area) ; la séquence des descripteurs de volume commence à la LBA 16. Pour approfondir ces notions, consultez l’article consacré à l’exploration d’une image ISO PlayStation 3, qui détaille la relation entre les LBA, les offsets et les descripteurs de volume.

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

xxd -g 1 -s $((16 * 2048)) -l 7 hello-world.iso
Enter fullscreen mode Exit fullscreen mode
00008000: 01 43 44 30 30 31 01                             .CD001.
Enter fullscreen mode Exit fullscreen mode

Les octets 01 CD001 01 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.

↑ Retour au sommaire

Ajouter la table de régions PS3

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.

D’où vient cette structure ? ECMA-119 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 :

  • l’annexe A du manuel de 3k3y ISO Tools explique l’alternance entre régions non chiffrées et chiffrées ;
  • IsoHeaderParser.GetUnprotectedRegions, 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 ;
  • le lecteur ISO de RPCS3 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.

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

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
Enter fullscreen mode Exit fullscreen mode

Vérifions la valeur actuelle de ces seize octets :

xxd -g 4 -l 16 hello-world.iso
Enter fullscreen mode Exit fullscreen mode
00000000: 00000000 00000000 00000000 00000000  ................
Enter fullscreen mode Exit fullscreen mode

xorriso 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 0 et la dernière LBA à l’offset 12.

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 :

sector_size=2048
image_size=$(wc -c < hello-world.iso | tr -d ' ')
sector_count=$((image_size / sector_size))
last_lba=$((sector_count - 1))
printf 'octets=%s\nsecteurs=%s\nderniere-lba=%s\n' "$image_size" "$sector_count" "$last_lba"
Enter fullscreen mode Exit fullscreen mode

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

write_be32() {
  value=$1
  offset=$2
  bytes=$(printf '\\%03o\\%03o\\%03o\\%03o' \
    $(((value >> 24) & 255)) \
    $(((value >> 16) & 255)) \
    $(((value >> 8) & 255)) \
    $((value & 255)))
  printf '%b' "$bytes" | dd of=hello-world.iso bs=1 seek="$offset" conv=notrunc status=none
}

write_be32 1 0
write_be32 "$last_lba" 12
Enter fullscreen mode Exit fullscreen mode

conv=notrunc est essentiel : dd écrit les quatre octets demandés sans tronquer le reste de l’image.

Examinons les seize premiers octets :

xxd -g 1 -l 16 hello-world.iso
Enter fullscreen mode Exit fullscreen mode

Pour 61 secteurs, le résultat est :

00000000: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 3c  ...............<
Enter fullscreen mode Exit fullscreen mode

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

Le projet contient aussi scripts/patch-ps3-regions.sh, 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.

Contrôlons une dernière fois l’inventaire de l’image :

xorriso -indev hello-world.iso -find / -type f -exec lsdl
Enter fullscreen mode Exit fullscreen mode

Quittons enfin le conteneur :

exit
Enter fullscreen mode Exit fullscreen mode

Dans RPCS3, choisissez File → Boot ISO et sélectionnez hello-world.iso. 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 :

Bonjour depuis Hexegesis.
Enter fullscreen mode Exit fullscreen mode

↑ Retour au sommaire

Bilan

Nous sommes partis de trois fichiers dont nous connaissions la place et le rôle, puis nous les avons construits sans Makefile :

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
Enter fullscreen mode Exit fullscreen mode

Nous avons relu PARAM.SFO avec sfo --list, 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.

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 PS3_DISC.SFB minimal couvre le format complet, ni de conclure à une compatibilité avec une console PlayStation 3 ou avec des images de disques commerciaux.

↑ Retour au sommaire

Top comments (0)