|
Iso CD Extractor - John COLIBRI.
|
- mots clé:extraction de fichiers d'une image ISO - CD - YellowBook
- logiciel utilisé: Windows XP, Delphi 6.0
- matériel utilisé: Pentium 1.400Mhz, 256 M de mémoire
- champ d'application: Delphi 1 à 6 sur Windows, Kylix
- niveau: programmeur Delphi
- plan:
1 - Introduction
De nombreux logiciels sont proposés sur le Web sous forme de fichier "ISO". Il
s'agit d'un paquet de données ayant un format particulier. Ce format est adapté
aux lecteurs de CD ROM, et nous ne pouvons donc pas afficher le contenu de ces
fichiers dans l'Explorateur Windows. Il faut alors
- soit placer ces fichiers sur un disque vierge
- soit extraire le contenu du fichier .ISO et les copier dans un répertoire
classique
N'étant pas arrivé à copier un fichier .ISO téléchargé, j'ai cherché un
programme pour analyser ce fichier et suis tombé sur
http://isolib.xenome.info/. Ce site propose un projet permettant l'analyse,
l'extraction de fichiers, et à terme, l'écriture de CD. Le programme est aussi
sur SourceForge. Notre projet est un descendant direct de ce projet (tIsoLib).
Nous allons ici:
- présenter le format des fichiers .ISO
- afficher la liste des répertoires et des fichiers
- extraire les fichiers dans un répertoire Windows.
2 - Format ISO
Format général
Les données placées sur un CD ROM peuvent être organisées selon plusieurs
formats: des formats pour la musique, pour les films, pour les données
informatiques etc.
Ce qui nous intéresse ici, c'est le format des données informatiques. Plusieurs
tentatives de normalisation ont été entreprises, et plusieurs formats, tels ISO
9660, High Sierra, Joliet etc ont été publiés.
Nous analyserons le format ISO 9660, qui serait similaire à High Sierra.
Un CD ROM ISO est organisé de la façon suivante:
- les données sont groupées par paquets de 2048 ou 2336 octets
- la structure est la suivante
- les 16 premiers secteurs sont inutilisés
- à partir du secteur 16 sont situés des descripteurs de volume
- vient une table des chemins
- puis nous trouvons le répertoire principal
- ce répertoire principal indique où se trouvent
- les fichiers contenus dans le répertoire principal
- les sous-répertoire qui contiennent d'autres fichiers
Globalement nous avons donc:
Et:
- les descripteurs vont être détaillés ci-dessous. Ils permettent
essentiellement de savoir où se trouve le "master dir"
- la table des chemins ne sera pas utilisée ici
- le "master dir" est le répertoire global. Il a la même structure que les
sous-répertoires: il contient des entrées pour des sous-répertoires ou des
fichiers:
- chaque sous-répertoire contient donc aussi des entrées de
sous-répertoires ou de fichiers
- les entrées de fichiers permettent de localiser les fichiers
Les Descripteurs
Chaque CD contient 2 descripteurs ou plus:
- il y a deux descripteurs principaux:
- le descripteur primaire est le plus important. C'est lui qui nous donne
accès à la table des matière principale
- le descripteur final sert à indiquer la fin des descripteurs
- entre les deux, 0 ou plusieurs descripteurs optionnels
- le descripteur de partition
- le descripteur de boot
Suivant l'operating system destinataire (Linux, Mac etc) il y aura plusieurs
de ces descripteurs.
Comme il ne nous servent pas à récupérer nos données, nous ne nous en
occuperons pas ici
La détection du type de CD se fait en vérifiant que les octets situés à 16*
2048 + 1 sont bien "CDOO1". Il s'agit alors bien du format "YellowBook, mode
1".
Les secteurs de descripteurs contiennent en position 0 un octet qui définit le
type de descripteur. Il suffit donc de lire le secteur jusqu'à ce que nous
tombions sur le secteur de "fin de descripteurs".
Le descripteur primaire qui a la signature $01 est défini par le type Delphi
suivant:
t_primary_descriptor= packed record
StandardIdentifier: array[0..4] of Char;
VolumeDescriptorVersion: Byte;
unused: Byte;
SystemIdentifier: array[0..31] of Char;
VolumeIdentifier: array[0..31] of Char;
Unused2: array[0..7] of Byte;
VolumeSpaceSize: TBothEndianDWord;
Unused3: array[0..31] of Byte;
VolumeSetSize: TBothEndianWord;
VolumeSequenceNumber: TBothEndianWord;
LogicalBlockSize: TBothEndianWord;
PathTableSize: TBothEndianDWord;
LocationOfTypeLPathTable: LongWord;
LocationOfOptionalTypeLPathTable: LongWord;
LocationOfTypeMPathTable: LongWord;
LocationOfOptionalTypeMPathTable: LongWord;
RootDirectory: TRootDirectoryRecord; // <===
VolumeSetIdentifier: array[0..127] of Char;
PublisherIdentifier: array[0..127] of Char;
DataPreparerIdentifier: array[0..127] of Char;
ApplicationIdentifier: array[0..127] of Char;
CopyrightFileIdentifier: array[0..36] of Char;
AbstractFileIdentifier: array[0..36] of Char;
BibliographicFileIdentifier: array[0..36] of Char;
VolumeCreationDateAndTime: TVolumeDateTime;
VolumeModificationDateAndTime: TVolumeDateTime;
VolumeExpirationDateAndTime: TVolumeDateTime;
VolumeEffectiveDateAndTime: TVolumeDateTime;
FileStructureVersion: Byte;
ReservedForFutureStandardization: Byte;
// -- the remaining bytes
ApplicationUse: array[0..511] of Byte;
ReservedForFutureStandardization2: array[0..652] of Byte;
end; // t_primary_descriptor
|
Et:
- StandardIdentifier est la signature "CD001" citée plus haut
- RootDirectory est un sous enregistrement qui indique où se trouve le
répertoire principal:
TRootDirectoryRecord= packed record
LengthOfDirectoryRecord: Byte;
ExtendedAttributeRecordLength: Byte;
LocationOfExtent: TBothEndianDWord;
DataLength: TBothEndianDWord;
RecordingDateAndTime: TDirectoryDateTime;
FileFlags: Byte;
FileUnitSize: Byte;
InterleaveGapSize: Byte;
VolumeSequenceNumber: TBothEndianWord;
LengthOfFileIdentifier: Byte; // = 1
FileIdentifier: Byte; // = 0
end; // TRootDirectoryRecord
|
et:
- LocationOfExtent est le numéro de secteur du répertoire principal
- DataLength nous donne le nombre d'octets de ce répertoire. En divisant
par 2048 nous obtenons le nombre de secteurs qu'il utilise.
Le Format d'une entrée
Les répertoires sont composés d'entrées.
Chaque entrée a le format suivant:
t_directory_entry= packed record
LengthOfDirectoryRecord: Byte;
ExtendedAttributeRecordLength: Byte;
LocationOfExtent: TBothEndianDWord;
DataLength: TBothEndianDWord;
RecordingDateAndTime: TDirectoryDateTime;
FileFlags: Byte;
FileUnitSize: Byte;
InterleaveGapSize: Byte;
VolumeSequenceNumber: TBothEndianWord;
LengthOfFileIdentifier: Byte;
// -- followed by FileIdentifier and padding bytes
end; // t_directory_entry
|
Plusieurs facteurs compliquent l'analyse des entrées:
- chaque entrée a une partie fixe (tailles, indicateurs divers) et un nom de
taille variable situé après la partie fixe
- une entrée ne peut pas chevaucher deux secteurs
- une entrée peut contenir un nom de taille 0, mais tout de même être suivie
par d'autres entrées valides
Les champs qui nous intéressent sont alors:
- LengthOfFileIdentifier: le nombre d'octets utilisés par le nom
- FileFlags: détermine le type d'entrée. Essentiellement le bit 1 qui est à 0
pour un fichier, à 1 pour un sous-répertoire
- LocationOfExtent: le numéro de secteur des données (le sous-répertoire ou
le fichier)
- DataLength: le nombre d'octets utiles
3 - Le Programme
Organisation
Nous avons utilisé les fichiers suivants:
- u_iso_definitions qui contient les définitions des descripteurs et des
entrées
- u_c_iso_file_read qui:
- détecte le type de fichier .ISO (en lisant les octets de signature)
- contient un tFileStream permettant de lire le fichier .ISO secteur par
secteur
- un structure arborescente récursive pour stocker:
- pour les répertoires:
- leur nom
- la liste de leurs sous-répertoires
- la liste de leurs fichiers
- pour les fichiers:
- leur nom
- leur numéro de secteur et leur taille
Nous avons ici utilisé notre classique encapsulation de tStringList (cf
u_c_tstringlist). La définition est la suivante:
c_iso_file= // one file
Class(c_basic_object)
// -- m_name: the file+ extension name
m_iso_sector_number: Integer;
m_iso_size: Integer;
Constructor create_iso_file(p_name: String;
p_iso_sector_number, p_iso_size: Integer);
function f_display_file: String;
function f_c_self: c_iso_file;
Destructor Destroy; Override;
end; // c_iso_file
c_iso_path= // -- recursive structure containing the iso directories
Class(c_basic_object)
// -- m_name: the full path
m_segment: String;
m_c_parent_path: c_iso_path;
m_c_file_list: tStringList;
m_c_sub_path_list: tStringList;
Constructor create_iso_path(p_name, p_segment: String;
p_c_parent_path: c_iso_path);
function f_c_self: c_iso_path;
function f_file_count: Integer;
function f_c_file(p_file_index: Integer): c_iso_file;
function f_index_of_file(p_file_name: String): Integer;
function f_c_find_by_file(p_file_name: String): c_iso_file;
procedure add_file(p_file_name: String; p_c_file: c_iso_file);
function f_c_add_file(p_file_name: String;
p_iso_sector_number, p_iso_size: Integer): c_iso_file;
procedure display_file_list;
function f_sub_path_count: Integer;
function f_c_sub_path(p_sub_path_index: Integer): c_iso_path;
function f_c_find_sub_path_by_name(p_sub_path_name: String): c_iso_path;
procedure add_sub_path(p_sub_path_name: String; p_c_sub_path: c_iso_path);
function f_c_add_sub_path(p_sub_path_name, p_segment: String): c_iso_path;
function f_c_get_or_add_sub_path(p_sub_path_name, p_segment: String): c_iso_path;
procedure display_sub_path_list;
procedure display_path_and_file;
Destructor Destroy; Override;
end; // c_iso_path
|
- l'arborescence est construite par une classe c_analyze_iso_file qui lit le
fichier et place les cellules en fonction du contenu du fichier
Le programme principal:
- contient une tDirectoryListBox associé à une tFileListBox qui permettent
la sélection du fichier .ISO et le lancement de la construction de l'arbre
- le bouton display_path_file visualise l'arborescence de façon indentée
(avec la position des fichiers et leur taille)
- le bouton build_tree_view_ permet de visualiser la structure dans un
tTreeView
- le bouton extract_iso_ copie tous les fichier du fichier .ISO dans un
répertoire de votre choix

4 - Utilisation
Pour utiliser le programme:
|
récupérez sur le Web le fichier .ISO qui vous intéresse
|
|
téléchargez le .ZIP
|
|
décompactez-le dans n'importe quel répertoire
|
|
exécutez-le
|
|
sélectionnez à l'aide de la tDirectoryListBox le chemin
|
|
cliquez dans la tFileListBox le fichier que vous souhaitez analyser /
décompacter
|
|
le programme construit l'arborescence en mémoire
|
|
pour visualiser le contenu du .ISO
- cliquez sur display_path_file pour lister les répertoires et fichiers
- cliquez sur build_tree_view_ pour construire le tTreeView qui sera
visible dans l'onglet directory_
|
|
pour extraire les fichiers et les placer dans une arborescence Windows
- créez le répertoire avec l'Explorateur Windows
- changez le nom k_save_path dans le fichiers (sinon le répertoire
"c:\prog\essais_util\util\_cd_iso\_data\" sera créé et utilisé)
- cliquez extract_iso_. Attendez quelques secondes que les fichiers
soient copiés
|
5 - Améliorations
Améliorations
Ce programme a été crée pour nous permettre de décompacter un .ISO que nous
avions téléchargé. Il n'a pas été testé sur d'autres fichiers.
Le programme pourrait être amélioré:
- en lisant directement depuis le lecteur, ou en gravant des fichiers sur le
CD
- en testant et en ajoutant le traitement des autres formats (JOLIET etc)
Je recherche
D'après certains messages sur le Web, XP contiendrait des API, "mal
documentées" (c'est peu dire), permettant d'écrire des CD en utilisant les DLL
(NERO ou autre). Si quelqu'un a des informations, et quelques lignes de
programmes, elles sont les bienvenues. Graver mes CD de sauvegarde est toujours
une galère. Il faut en effet:
- extraire les fichiers manuellement
- vérifier approximativement que la taille ne dépasse pas la capacité du CD
- lancer une mystérieux .EXE qui grave, ou qui envoie des messages sibyllins
s'il n'y arrive pas
Or tout le travail de préparation peut être avantageusement effectué en Delphi,
en utilisant des procédure inverses de celles présentées ici pour la lecture.
Si on connaît les primitives d'écriture, la gravure des secteurs préparés
devrait alors être un jeu d'enfant.
Si on connaît les primitives d'écriture...
6 - Télécharger le source
Nous avons placé le projet dans un .ZIP qui comprend:
- le .DPR, la forme principale
- les unités citées ici
- nos utilitaires habituels d'affichage, de log etc
Ce .ZIP est auto suffisant.
Il contient des chemins RELATIFS. Par conséquent:
- créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Au niveau sécurité:
- les .ZIP:
- ne modifient pas votre PC (pas de changement de la Base de Registre,
d'écrasement de DLL ou autre .BAT). Aucune modification de répertoire ou
de contenu de répertoire ailleurs que dans celui où vous dézippez
- ne contiennent aucun programme qui s'exécuterait à la décompression
(.EXE, .BAT, .SCR ou autre .VXD) ou qui seraient lancés plus tard
(reboot)
- passez-les à l'antivirus avant décompression si vous êtes inquiets.
- les programmes ne changent pas la base de registre et ne modifient aucun
autre répertoire de votre machine
- pour supprimer le projet, effacez simplement le répertoire.
Voici le .ZIP:
Comme d'habitude:
- nous vous remercions de nous signaler toute erreur, inexactitude ou
problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion
d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre
e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos réponses
sur les messageries. C'est très simple: plus nous aurons de visiteurs et de
références Google, plus nous écrirons d'articles.
7 - Conclusion
Ce projet permet d'analyser le contenu d'un fichier .ISO et d'en extraire les
fichiers dans un répertoire Windows
8 - Références
- http://isolib.xenome.info/ (ou SourceForge)
Le projet Delphi dont nous sommes paris
9 - L'auteur
John COLIBRI est passionné par le développement
Delphi et les applications de Bases de Données. Il a écrit de nombreux livres
et articles, et partage son temps entre la
développement de projets pour
ses clients, le conseil et la formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des
stages
de formation Delphi, base de données, Ado.Net, Asp.Net et UML qu'il
anime personellement tous les mois, à Paris, en province ou sur site client.
|