[Inform 6] Ecriture d'un fichier texte

Salut à tous,

Je fais appel aujourd’hui aux pros d’Inform 6, car là j’ai un souci…

J’aimerai écrire et lire un simple fichier texte. Les commandes qui vont bien sont @save pour écrire et @read.

Le DM4 précise :

Et pour ces fameux « byte arrays » :

Alors je souhaiterai enregistrer le contenu de plusieurs variables, mais j’ai du mal à saisir le fonctionnement des « byte array buffer ». Entre les erreurs de compilation, les crashs à l’exécution, voici le seul code que j’ai réussi à produire et qui arrive à écrire quelque chose :

[code]Array players_name → « Frank Booth »;

[Main length R test ;

length = 16;

@save players_name length "test.sav" -> R  ;

print "write";

if (R==true)
{
print " ok";
}
else print " error";


print "^^";

@restore test 4 "Þ.AUX" -> R  ;

if (R==true)
{
print " read ok";
}
else print " read error";

];
[/code]

Le résultat est un fichier au nom étrange (« Þ.AUX ») de 16 octets (logique) qui contient une lettre sur deux de ma chaîne et le reste des caractères d’erreur (caractères non visibles ici) :

 F r a n k B o

Et ce fichier n’est pas lisible par la méthode @restore. A l’exécution j’ai bien read ok mais j’ai un write error.

J’ai regardé le code des fonctions lecture et sauvegarde des librairies I6, mais bien qu’elles contiennent un @save et @restore le tableau d’octet qu’elles prennent en paramètre n’est pas construit dans la méthode, et je n’ai pas trouvé comment il l’était.

Des idées ?

Merci !

Ce que je vais dire ne va vraisemblablement pas te convenir, mais bon, étant donné que je ne connais pas Inform 6…

J’imagine que la Z-machine est bien capable de manipuler des fichiers puisqu’on peut faire des transcriptions (entre autres), mais je pense que Glulx est plus flexible pour ça.

En tout cas, la bibliothèque standard d’I7 ne permet de manipuler des fichiers que si on compile pour Glulx, ce qui ne doit pas être sans raison.

Mais s’il est vraiment nécessaire pour toi de rester en Z-machine, j’imagine que ça doit quand même être faisable et je laisse la parole à des gens plus compétents en la matière.

(Si tu peux nous dire ton utilisation exacte, ça pourrait peut-être aider ?)

Coucou, c’est tout à fait possible, mais ça n’est pas utilisé dans beaucoup de jeux ! (Ça s’appelle l’ « Extended Save » je crois.)

En tout cas apparemment ça a marché pour la bibliothèque « achievements.h » de Vince Laviano, que je te mets en pièce jointe, au cas où… Mais il a fait des trucs un peu différents donc je ne sais pas si ça peut aider.
(Au lieu d’utiliser les byte arrays il a utilisé les buffer arrays, qui sont la même chose sauf la taille de la première case, de taille 2 (1 pour les byte arrays). Du coup y’a un moment il faut qu’il décale des trucs vers la gauche.)

J’ai essayé de bidouiller ton code, j’ai la sauvegarde qui marche mais pas le restore x) Voilà ce que j’ai trouvé :

  • Si tu veux donner une suggestion de nom à ta sauvegarde, il faut que le nom soit dans un byte array (et pas juste entre guillemets). C’est cette erreur. Je l’ai mis dans mon code.
  • Sache que ce nom de fichier n’est qu’une suggestion, et que certains interpréteurs (Gargoyle pour Linux par exemple) ouvrent systématiquement la fenêtre qui demande où sauvegarder, etc. C’est contraire à la spécification mais il n’empêche qu’ils le font…
  • J’ai rajouté le dernier argument optionnel mais il est lui aussi ignoré par les interpréteurs… grmf
  • Tu avais oublié de déclarer ton tableau « test » qui contient le résultat du restore. Moi sur ton code j’ai un crash « attempted to write to illegal address 0x0 », je pense que c’est à cause de ça. Mais je ne sais pas trop comment le déclarer, j’avoue ! C’est sans doute la ligne qui est à améliorer…
  • Si tu remplaces « test » par « players_name », tu obtiens un ‹ read error › (mais plus de crash). À voir si « players_name » a été changé ou non…

Si tu trouves, ça m’intéresse ! En tout cas on dirait qu’il faudrait une extension I6 pour faire les sauvegardes plus simplement (genre qui convertit tout en les bons types, etc.), ou en tout cas une extension qui convertit les tableaux de différents formats entre eux…

Array players_name --> "Frank Booth";
Array test -> 100; ! cette ligne est sans doute fausse : array test string 100? array test buffer 100?

Array filename --> "test.sav";


[Main  length_save length_restore R test prompt ;

   length_save = players_name-->0;

   @save players_name length_save filename prompt -> R  ;

   print "write";
   
   if (R==true)
   {
   print " ok";
   }
   else print " error";
   
   
   print "^^";
   
   length_restore = players_name-->0;
   @restore test length_restore filename prompt -> R  ;
   
   if (R==true)
   {
   print " read ok";
   }
   else print " read error";
   
];

Hmmm… Je me rends compte en voyant le message de Mule que ma réponse à ton message, Natrium à disparu…? Pour te répondre j’avais indiqué qu’effectivement en I6 c’est possible selon moi, vu que les interpréteurs permettent d’enregistrer des transcripts et des fichiers de sauvegarde. La fonction utilisée est bien @save, mais je n’arrivais pas à comprendre comment le fameux tableau de bytes était généré. Après si pour des questions de facilité je dois switcher en glulxe, c’est pas un souci non plus. :slight_smile:

@Mule : Super merci je regarde ça et je vous tiens au courant.

C’est fait ! (je savais pas que c’était possible.)[code]!% -Cu
!% -v8
!% -~S

Array nom string « nom_fichier »;

Array data_save buffer « Test de Save and Restore »;

Array data_restore buffer 256;

[ main key flag n i;
n = data_save–>0 + WORDSIZE;

@save data_save n nom  -> flag;
if (flag == 0) print "Save failed.^";

@restore data_restore n nom  -> flag;
if (flag == 0) print "Restore failed.^";

i = 0;
while (i < n) {
	print (char) data_restore->(WORDSIZE+i);
	i++;
}

new_line;
.readkey;
@read_char 1 ->key;

];[/code]
La réponse est là :
http://inform-fiction.org/zmachine/standards/z1point1/sect15.html#save

Ah cool ! Un wordsize qui manque et un array de type string apparemment. Super !

Intéressant… Je suis en train de tester la méthode proposée dans la librairie « Achievements » indiquée par Mule. L’idée est de construire le buffer de sortie en redirigeant @output_buffer non pas vers l’écran mais vers un fameux buffer qu’on vient ensuite écrire avec @save. Mais j’ai encore des crashs… J’ai échangé quelques emails avec l’auteur de la librairie, mais en attendant je vais tester le code d’Auraes… :slight_smile:

Extrait :

[code] @output_stream 3 ach_zfile_buf;

print « Bonjour a tous »;

@output_stream -3;
len = ach_zfile_buf–>0 + WORDSIZE; ! length is prepended to the ach data

@save ach_zfile_buf len ach_filename_buf prompt → result;
if (~~result) {
print « *** SavePersistentAchsZ: @@64save to ' », (string) ach_filename, « ’ failed ***^ »;
return;[/code]

J’ai simplifié, dans la mesure ou cela semble limité à 255 octets + 1 octet pour la taille du buffer, inutile de compliqué.

[code]!% -Cu
!% -v8
!% -~S

Array nom_fichier string « nom_fichier »;

Array data_save string « Test de Save and Restore »;
Array data_restore string 255;

[ main key flag n i;
n = data_save->0 + 1;

@save data_save n nom_fichier → flag;
if (flag == 0) print « Save failed.^ »;

@restore data_restore n nom_fichier → flag;
if (flag == 0) print « Restore failed.^ »;

i = 0;
while (i < n) {
print (char) data_restore->i;
i++;
}
new_line;
.readkey;
@read_char 1 ->key;
];[/code]J’ai pas testé si le flag fonctionne.

Si tu veux utiliser @output_stream 3, ça change pas grand chose et visiblement les sauvegardes ne sont pas limitées à 256 octets !

[code]!% -Cu
!% -v8
!% -~S

Array nom_fichier string « nom_fichier »;

Array data_save buffer 1024;
Array data_restore buffer 1024;

[ main key flag len i;

@output_stream 3 data_save;
print "Thalassius vero ea tempestate praefectus praetorio praesens ipse quoque adrogantis ingenii, considerans incitationem eius ad multorum augeri discrimina, non maturitate vel consiliis mitigabat, ut aliquotiens celsae potestates iras principum molliverunt, sed adversando iurgandoque cum parum congrueret, eum ad rabiem potius evibrabat, Augustum actus eius exaggerando creberrime docens, idque, incertum qua mente, ne lateret adfectans. quibus mox Caesar acrius efferatus, velut contumaciae quoddam vexillum altius erigens, sine respectu salutis alienae vel suae ad vertenda opposita instar rapidi fluminis irrevocabili impetu ferebatur.";
@output_stream -3;

len = data_save-->0 + WORDSIZE;

@save data_save len nom_fichier -> flag;
if (flag == 0) print "Save failed.^";

@restore data_restore len nom_fichier -> flag;
if (flag == 0) print "Restore failed.^";

i = 0;
while (i < len) {
	print (char) data_restore->(WORDSIZE+i);
	i++;
}
new_line;
.readkey;
@read_char 1 ->key;

];[/code]Par contre, si ça se passe bien avec Frotz, Gorgoyle(Frotz) ne prend pas en compte le nom du fichier, il m’affiche un ‹ Save failed › et me raffiche mon texte avec deux ?? à la fin.
*** Le problème vient de Gorgoyle(Frotz), avec Gorgoyle(Bocfel), ça fonctionne. ***

Merci !

J’ai effectivement un save failed avec Gargoyle Frotz, rien avec Gargoyle Bocfel.

Par contre dans tout les cas le fichier de sauvegarde semble cohérent, j’ai juste un caractère étrange en début de fichier, qui ne semble d’ailleurs pas gêner la relecture. Dans les deux cas (Frotz et Bocfel) le texte s’affiche normalement.

Par contre, (dernier ?) problème : comment rebalancer X caractères de la array lue dans une variable ?

i = 0; Variable = ""; while (i < 5) { print (char) data_restore->(WORDSIZE+i); Variable = Variable + data_restore->(WORDSIZE+i); !Je voudrais faire un truc comme ça... i++; }

C’est normal, puisque on sauvegarde aussi les 2 ou 4 (WORDSIZE) premiers octets du tableau qui sont la taille de celui-ci. Mais tu peux trés bien ne pas les sauvegarder. [code]!% -Cu
!% -v5
!% -~S

Array nom_fichier string « nom_fichier »;

Array data_save buffer 1024;
Array data_restore buffer 1024;

[ main key flag len i pt;

@output_stream 3 data_save;
print "Thalassius vero ea tempestate praefectus praetorio praesens ipse quoque adrogantis ingenii, considerans incitationem eius ad multorum augeri discrimina, non maturitate vel consiliis mitigabat, ut aliquotiens celsae potestates iras principum molliverunt, sed adversando iurgandoque cum parum congrueret, eum ad rabiem potius evibrabat, Augustum actus eius exaggerando creberrime docens, idque, incertum qua mente, ne lateret adfectans. quibus mox Caesar acrius efferatus, velut contumaciae quoddam vexillum altius erigens, sine respectu salutis alienae vel suae ad vertenda opposita instar rapidi fluminis irrevocabili impetu ferebatur.";
@output_stream -3;

len = data_save-->0;

pt = data_save + WORDSIZE;
@save pt len nom_fichier -> flag;
if (flag == 0) print "Save failed.^";

pt = data_restore + WORDSIZE;
@restore pt len nom_fichier -> flag;
if (flag == 0) print "Restore failed.^";

data_restore-->0 = len;

i = 0;
while (i < len) {
	print (char) data_restore->(WORDSIZE+i);
	i++;
}
new_line;
.readkey;
@read_char 1 ->key;

];[/code]Tu n’es pas non plus obligé de faire un tableau data_save et un tableau data_restore ; tu peux n’en faire qu’un. Et si tu utilises les bibliothèques, tu peux utiliser PrintToBuffer() au lieu de @output_stream 3/-3

[code]!% -Cu
!% -v8
!% -~S

Array nom_fichier string « nom_fichier »;

Array data_buf buffer 1024;

Array MaVariable1 buffer 32;
Array MaVariable2 buffer 128;

[ memcpy src dest pos i;
i = 0;
pos–;
while (i < dest–>0 && pos < src–>0) {
dest->(WORDSIZE+i) = src->(WORDSIZE+pos);
i++;
pos++;
}
dest–>0 = i;
];

[ print_txt buf i;
i = 0;
while (i < buf–>0) {
print (char) buf->(WORDSIZE+i);
i++;
}
];

[ main key flag len pt pos;

@output_stream 3 data_buf;
print « Thalassius vero ea tempestate praefectus praetorio praesens ipse quoque adrogantis ingenii, considerans incitationem eius ad multorum augeri discrimina, non maturitate vel consiliis mitigabat, ut aliquotiens celsae potestates iras principum molliverunt, sed adversando iurgandoque cum parum congrueret, eum ad rabiem potius evibrabat, Augustum actus eius exaggerando creberrime docens, idque, incertum qua mente, ne lateret adfectans. quibus mox Caesar acrius efferatus, velut contumaciae quoddam vexillum altius erigens, sine respectu salutis alienae vel suae ad vertenda opposita instar rapidi fluminis irrevocabili impetu ferebatur. »; !635 caractères
@output_stream -3;

len = data_buf–>0;

pt = data_buf + WORDSIZE; !pour ne pas sauvegarder la taille du tableau

@save pt len nom_fichier → flag;
if (flag == 0) print « Save failed.^ »;

@restore pt len nom_fichier → flag;
if (flag == 0) print « Restore failed.^ »;

! Je me positionne ou je veux commencer la copie dans mon data_buf

pos = 20; ! Je commence la copie à la 20e lettre
memcpy(data_buf, MaVariable1, pos);

pos = 200; ! Je commence la copie à la 200e lettre
memcpy(data_buf, MaVariable2, pos);

print "Var 1 = ~", (print_txt) MaVariable1, "~^";
print "Var 2 = ~", (print_txt) MaVariable2, "~^";

new_line;
.readkey;
@read_char 1 ->key;
];
[/code]*** cette fois ça doit être bon ! ***