Cela faisait longtemps qu'on ne s'était pas donné rendez-vous pour un petit défi. Surprise, c'est sur Apple II GS que ça se passe, et vous allez le voir, cela change tout ! Oubliez en effet tout ce qui a été vu jusqu'à présent pour aborder un tel challenge, pourtant classique en apparence : signer une fois encore une image. Ici, nous ne sommes plus sur 8 bits, ce n'est plus une page Hires mais Super-Hires propre au GS et en plus, il nous faudra même utiliser les Tool Sets (quelle hérésie...) pour arriver à nos fins. Bref, en attaquant ce défi, je savais que j'allais en baver, croyant ne pas connaître grand-chose au GS. En fait, j'avais tord : je n'y connaissais absolument rien ! Et pour aller au bout de l'aventure, il m'a fallu tout réapprendre (ou presque). Plutôt que de vous abreuver de longues pages d'explications arides ou de listings bien longuets (rassurez-vous, il y en aura quand même), je vous propose cette fois un compte-rendu un peu plus vivant, car illustré de screenshots. Le GS peut afficher de vraies couleurs, autant en profiter !

Partie I : réussir à lancer le programme.

Car oui, premier problème : passer cet écran ! La première fois que j'ai lancé le programme, j'ai tout simplement cru que la disquette était morte. On a en effet droit à un festival de sons (couinements de drives) et lumières (diodes des lecteurs qui s'allument dans tous les sens) nous rappelant les heures les plus sombres du support magnétique ! En fait, au boot du disque, nous sommes tout simplement face au Piège à Con tendu par l'équipe de GS On the Rocks et qui était une des marques de fabrique de la série. Pour ceux qui ne sont pas au courant, tout s'arrête ici et ils balanceront de rage la disquette à la benne. Les autres savent, eux, qu'il suffit de taper lsd (pour Lyon Software Diffusion, les créateurs de la série) pour passer à la suite. Bien entendu tout ceci n'a rien à voir avec le défi et sert juste de mise en bouche.

Car le défi en lui-même, le voici. Il apparaît lors du lancement du programme principal (le lecteur de fichiers texte) et nous invite, comme d'habitude je dirais, à signer une image. Gronk (et cie) nous promet même le champagne en cas de réussite. Bon, puisqu'il me semble avoir lu que la caisse avait été gagnée à l'époque, nous allons donc nous attaquer au défi, non pas pour la picole, mais pour la beauté du geste. En attendant, on notera (nous sommes aussi là pour bosser...) que le programme du défi utilise une interface graphique standard et repose donc sur les Tools GS.

 

Un petit coup l’œil au contenu de la disquette sous le Finder GS/OS et on remarque immédiatement les trois fichiers PICx (type ANI) ainsi que le programme principal LSD.SYS16. Le type des fichiers PICx n'est pas standard pour des images et leur taille (variable) laisse supposer que les fichiers sont compressés. Info, intox ou tout simplement fausse-route ?

 

 

Que se passe-t'il si on veut lancer directement LSD.SYS16 depuis le Finder ? Et bien l'écran ci-contre apparaît et le programme boucle sur lui-même. Second problème donc : je comptais utiliser GSBug (le debugger GS) pour m'attaquer au défi mais il nécessite GS/OS. Or GS On The Rocks semble ne pas trop l'apprécier.

 

Puisque le programme rechigne à aller plus loin, on va tout simplement utiliser GSBug afin de voir ce qui se passe un peu. On l'active et on passe en mode SINGLE STEP pour voir qu'effectivement le code boucle sur la vérification d'une adresse qui, non égale à zéro, nous renvoie encore et encore sur la comparaison. Plutôt que de tracer pour découvrir d'où provient le contenu de cette adresse, essayons plutôt la méthode bourrine (qui nous caractérise) et tentons de forcer le passage.

 

 

Cela tombe bien, GSBug possède une fonction permettant de skiper une instruction. On zappe donc le branchement obligatoire (BRL) pour passer à la suite... Et on rend la main au programme.

 

 

On se retrouve alors avec cet écran. On remarque que les deux messages (le classique en cas de boot direct, et le message nous invitant à relancer l'application) s'affichent simultanément. Mais le programme, et c'est le plus important, se poursuit normalement et s'exécute correctement.  Et depuis le Finder cette fois... Rappelez-vous la manip à faire, car nous aurons à l'effectuer à chaque fois que nous voudrons exécuter le programme depuis GS/OS.

 

Partie II :  localiser la routine mystère.

La première chose la plus évidente, une fois qu'on a réussi à lancer le programme dans les conditions voulues, c'est bien évidemment de regarder un peu à l'intérieur... En fait pour résoudre ce défi, nous allons devoir à la fois travailler en direct live avec GSBug mais aussi avoir une vision plus globale du code assembleur de LSD.SYS16 afin de pouvoir étudier son fonctionnement à tête reposée. Pour ce faire nous allons utiliser Orca/Disassembler qui permet d'afficher le code sous l'interface graphique typique GS/OS. Il permet également d'avoir une vision rapide des segments utilisés par le programme. On remarque d'ailleurs immédiatement qu'un des trois segments s'appelle tout simplement pic. De quoi évidemment attirer notre attention. La méthode n'est pas spécialement technique mais plutôt pleine de bon sens. J'ai souvent vanté dans les défis précédents les bienfaits de l'observation. Ici encore, après quelques minutes à scroller à l'intérieur du code du segment pic, on remarque rapidement un LDA #$ABCD trop étrange pour être honnête.

 

Ou encore, comme ci-contre, un EOR #$4321 tout aussi louche. Et quand on voit apparaître juste au-dessus un LDA suivi d'un EOR puis d'un STA, on repense immédiatement aux défis sur 8 bits et aux routines de décodage rencontrées... Bien évidemment, il va maintenant falloir vérifier tout ça en live avec GSBug.

 

Nouvelle difficulté propre au GS : l'allocation mémoire étant dynamique, un loader s'occupe de placer le programme en RAM lors de son chargement à un emplacement libre. Les adresses mémoire vues plus haut avec le code désassemblé ne sont en fait que des offsets. Pour localiser réellement en mémoire telle ou telle portion de code, il faudra ajouter son offset à l'adresse réelle de son segment. Heureusement, GSBug est fourni avec un petit Classic Desk Accessory (programme résident) permettant de fournir ces précieuses informations pour chaque programme chargé actuellement en mémoire : j'ai nommé Loader Dumper. Attention de bien utiliser une version compatible avec votre GS/OS. Pour la 6.0.1, il faudra Loader Dumper 4, celui fourni avec GSBug 1.5 ne fonctionnant pas...

 

 

On repère notre programme parmi tous les processus actuellement en mémoire et on récupère son Identificateur unique. Ici l'UserID est $1002.

 

 

Grâce à cet UserID, on obtient ensuite les adresses de début et fin des segments. Il ne reste plus alors qu'à ajouter les offsets des instructions qui nous intéressent pour obtenir leurs adresses mémoire et poser des breakpoints dessus ! Exemple : ici le segment 3 (pic) commence en $0F6C3D. On veut obtenir l'adresse du EOR #$4321 qui nous titille. L'offset correspondant est : $0552.  L'adresse en mémoire sera donc : $0F6C3D+0552 soit $0F718F !

 

On vérifie et effectivement, on retrouve bien notre EOR #4321 ! Attention, le code ayant été totalement relogé en mémoire par le loader, il est parfois peu évident de le reconnaître. En effet, toutes les adresses mémoire (hors Page Zéro) et tous les sauts dans le code même seront différents de ce qui est affiché avec un désassembleur.
Pour en revenir à notre EOR #4321, une méthode simple pour vérifier que nous sommes bien dans la routine recherchée consiste tout simplement à le remplacer par des NOP. Si nous observons un résultat différent de celui normalement obtenu, on pourra considérer que nous sommes bien au bon endroit.

 

 

Je pense que le résultat parle de lui-même. L'image n'est clairement plus décryptée correctement. Notre EOR se situe bien au cœur de la routine qui nous intéresse.

 

 

 

Partie III : le décryptage de l'image.

Une fois la routine localisée, il suffit de poser judicieusement un breakpoint (en gros juste avant qu'elle ne commence) pour ensuite la suivre à la trace, instruction par instruction grâce aux fonctionnalités de GSBug. Ne pas s'abstenir évidemment d'afficher les adresses en Page Zéro utilisées par le programme pour bien comprendre ce qui s'y passe. Il faut en fait subtilement mélanger analyse en dead listing (avec le code obtenu par désassemblage) et le traçage en live qui permet lui de voir la variation des valeurs en direct. Je vous colle ci-après le code complet de la routine agrémenté de quelques petits commentaires. Ceux qui regardent juste les images peuvent directement passer à la suite !

00/03BD: A9 01 00      LDA #0001 ; image 1
00/03C0: 85 DB         STA DB
00/03C2: A9 03 00      LDA #0003 ; 3 images à traiter
00/03C5: 85 F9         STA F9
00/03C7: 38            SEC
00/03C8: E5 DB         SBC DB
00/03CA: 70 03         BVS 03CF {+03}
00/03CC: 49 00 80      EOR #8000
00/03CF: 30 03         BMI 03D4 {+03}
00/03D1: 82 94 02      BRL 0668 {+294}
 
00/03D4: A5 DB         LDA DB
00/03D6: 3A            DEC
00/03D7: 0A            ASL
00/03D8: 0A            ASL
00/03D9: AA            TAX
00/03DA: BD D4 08      LDA 08D4,X
00/03DD: A8            TAY
00/03DE: BD D6 08      LDA 08D6,X
00/03E1: AA            TAX
00/03E2: 98            TYA
00/03E3: 85 AD         STA AD
00/03E5: 86 AF         STX AF
00/03E7: 64 B1         STZ B1
00/03E9: 64 B3         STZ B3
00/03EB: F4 00 00      PEA 0000
00/03EE: 7B            TDC
00/03EF: 18            CLC
00/03F0: 69 AB 00      ADC #00AB
00/03F3: 48            PHA
00/03F4: 22 25 36 01   JSL 013625 ; FONCTION OPEN PRODOS 16 (fichier PICx)
00/03F8: B0 03         BCS 03FD {+03}
00/03FA: 82 03 00      BRL 0400 {+03}
00/03FD: 82 35 03      BRL 0735 {+335}
00/0400: F4 00 00      PEA 0000
00/0403: 7B            TDC
00/0404: 18            CLC
00/0405: 69 AB 00      ADC #00AB
00/0408: 48            PHA
00/0409: 22 52 36 01   JSL 013652 ; FCT GET_EOF (End of file)
00/040D: A5 AD         LDA AD
00/040F: 85 D1         STA D1     ; retour = taille du fichier (lo)
00/0411: A5 AF         LDA AF
00/0413: 85 D3         STA D3     ; (hi)
00/0415: 48            PHA
00/0416: 48            PHA
00/0417: F4 00 00      PEA 0000
00/041A: F4 20 7D      PEA 7D20
00/041D: AD 00 00      LDA 0000
00/0420: 48            PHA
00/0421: F4 00 C0      PEA C000
00/0424: F4 00 00      PEA 0000
00/0427: F4 00 00      PEA 0000
00/042A: A2 02 09      LDX #0902
00/042D: 22 00 00 E1   JSL E10000  NewHandle(Size/4,MemID,Attr,@loc):H
00/0431: 8D 9E 09      STA 099E
00/0434: 68            PLA
00/0435: FA            PLX
00/0436: 85 CD         STA CD
00/0438: 86 CF         STX CF
00/043A: A5 DB         LDA DB
00/043C: 3A            DEC
00/043D: 0A            ASL
00/043E: 0A            ASL
00/043F: AA            TAX
00/0440: A5 CD         LDA CD
00/0442: 9D C8 08      STA 08C8,X
00/0445: A5 CF         LDA CF
00/0447: 9D CA 08      STA 08CA,X
00/044A: A0 02 00      LDY #0002
00/044D: B7 CD         LDA [CD],Y
00/044F: AA            TAX
00/0450: A7 CD         LDA [CD]
00/0452: 85 E3         STA E3
00/0454: 86 E5         STX E5
00/0456: 48            PHA	; réserve place pour le Handle retourné
00/0457: 48            PHA      ; (4 octets)
00/0458: D4 D3         PEI D3   ; Size (hi)
00/045A: D4 D1         PEI D1   ; Size (lo)
00/045C: AD 00 00      LDA 0000 ;
00/045F: 48            PHA      ; ID
00/0460: F4 00 C0      PEA C000 ; Attribut
00/0463: F4 00 00      PEA 0000 ; Pointer to Loc
00/0466: F4 00 00      PEA 0000 ; Pointer to Loc
00/0469: A2 02 09      LDX #0902
00/046C: 22 00 00 E1   JSL E10000  NewHandle(Size/4,MemID,Attr,@loc):H
00/0470: 8D 9E 09      STA 099E
00/0473: 68            PLA ; handle
00/0474: FA            PLX ; handle
00/0475: 85 C9         STA C9 ; sauvegarde
00/0477: 86 CB         STX CB
00/0479: A0 02 00      LDY #0002
00/047C: B7 C9         LDA [C9],Y
00/047E: AA            TAX
00/047F: A7 C9         LDA [C9]
00/0481: 85 DF         STA DF ; memory block (lo)
00/0483: 86 E1         STX E1 ; memory block (hi)
00/0485: A5 DF         LDA DF
00/0487: 85 AD         STA AD
00/0489: A5 E1         LDA E1
00/048B: 85 AF         STA AF
00/048D: A5 D1         LDA D1
00/048F: 85 B1         STA B1
00/0491: A5 D3         LDA D3
00/0493: 85 B3         STA B3
00/0495: F4 00 00      PEA 0000
00/0498: 7B            TDC
00/0499: 18            CLC
00/049A: 69 AB 00      ADC #00AB
00/049D: 48            PHA
00/049E: 22 2F 36 01   JSL 01362F ; FONCTION READ
00/04A2: B0 03         BCS 04A7 {+03}
00/04A4: 82 03 00      BRL 04AA {+03}
00/04A7: 82 8B 02      BRL 0735 {+28B}
00/04AA: F4 00 00      PEA 0000
00/04AD: 7B            TDC
00/04AE: 18            CLC
00/04AF: 69 AB 00      ADC #00AB
00/04B2: 48            PHA
00/04B3: 22 39 36 01   JSL 013639 ; CLOSE
 
00/04B7: F4 00 00      PEA 0000
00/04BA: F4 20 00      PEA 0020   ; 32 octets
00/04BD: A5 D1         LDA D1
00/04BF: A6 D3         LDX D3
00/04C1: 22 A7 2A 01   JSL 012AA7 ; "soustraction" D3/D1-0000/0020 -> D3/D1
00/04C5: 68            PLA
00/04C6: FA            PLX
00/04C7: 85 D1         STA D1  ; D1 contient la taille du fichier - 32
00/04C9: 86 D3         STX D3
 
; décryptage PHASE 1 (ascendante) A = A EOR (A-2)'
 
00/04CB: A9 01 EF      LDA #EF01 ; valeur de départ pour A'
00/04CE: 85 D9         STA D9
00/04D0: 64 D7         STZ D7    ; on part de 00
 
00/04D2: A5 D7         LDA D7
00/04D4: A2 00 00      LDX #0000
00/04D7: C9 00 00      CMP #0000
00/04DA: 10 01         BPL 04DD {+01}
00/04DC: CA            DEX
00/04DD: DA            PHX
00/04DE: 48            PHA
00/04DF: A5 DF         LDA DF ; adresse mémoire début
00/04E1: A6 E1         LDX E1 ; fichier chargé
00/04E3: 22 9C 2A 01   JSL 012A9C ; "addition" E1/DF+0000/D7 -> C7/C5
00/04E7: 68            PLA
00/04E8: FA            PLX
00/04E9: 85 C5         STA C5
00/04EB: 86 C7         STX C7
00/04ED: A7 C5         LDA [C5] ; *** A
00/04EF: 45 D9         EOR D9
00/04F1: 85 D9         STA D9
00/04F3: A5 D9         LDA D9
00/04F5: 87 C5         STA [C5] ; ***
00/04F7: A5 D9         LDA D9
00/04F9: 0A            ASL
00/04FA: 85 D9         STA D9
00/04FC: A5 D9         LDA D9
00/04FE: 49 21 43      EOR #4321
00/0501: 85 D9         STA D9   ; A' = ASL + EOR #$4321
00/0503: E6 D7         INC D7   ; on incrémente de 2
00/0505: E6 D7         INC D7   ; (on traite un WORD à chaque fois)
00/0507: A5 D7         LDA D7
00/0509: A2 00 00      LDX #0000
00/050C: C9 00 00      CMP #0000
00/050F: 10 01         BPL 0512 {+01}
00/0511: CA            DEX
00/0512: C5 D1         CMP D1	; taille buffer à traiter (D1-2 max donc)
00/0514: D0 02         BNE 0518 {+02}
00/0516: E4 D3         CPX D3
00/0518: F0 03         BEQ 051D {+03}
00/051A: 82 B5 FF      BRL 04D2 {-4B} ; on boucle
 
; décryptage PHASE 2 (descendante) A = A EOR (A+2)'
 
00/051D: A9 CD AB      LDA #ABCD , valeur de départ pour A'
00/0520: 85 D9         STA D9
00/0522: A5 D1         LDA D1 ; on part de D1
00/0524: 85 D7         STA D7 ; 
 
00/0526: A5 D7         LDA D7
00/0528: A2 00 00      LDX #0000
00/052B: C9 00 00      CMP #0000
00/052E: 10 01         BPL 0531 {+01}
00/0530: CA            DEX
00/0531: DA            PHX
00/0532: 48            PHA
00/0533: A5 DF         LDA DF
00/0535: A6 E1         LDX E1
00/0537: 22 9C 2A 01   JSL 012A9C ; "addition" E1/DF+0000/D7 -> C7/C5
00/053B: 68            PLA
00/053C: FA            PLX
00/053D: 85 C5         STA C5
00/053F: 86 C7         STX C7
00/0541: A7 C5         LDA [C5] ; ***
00/0543: 45 D9         EOR D9
00/0545: 85 D9         STA D9
00/0547: A5 D9         LDA D9
00/0549: 87 C5         STA [C5] ; ***
00/054B: A5 D9         LDA D9   ;
00/054D: 0A            ASL
00/054E: 85 D9         STA D9
00/0550: A5 D9         LDA D9
00/0552: 49 21 43      EOR #4321
00/0555: 85 D9         STA D9   ; A' = ASL + EOR #$4321
00/0557: C6 D7         DEC D7
00/0559: C6 D7         DEC D7
00/055B: A5 D7         LDA D7
00/055D: C9 00 00      CMP #0000 ; (buffer traité jusqu'à 2)
00/0560: F0 03         BEQ 0565 {+03}
00/0562: 82 C1 FF      BRL 0526 {-3F}
 
00/0565: A9 00 7D      LDA #7D00
00/0568: 85 D5         STA D5
00/056A: 48            PHA          ; return data (number of bytes unpacked)
00/056B: D4 E1         PEI E1       ; Pointer to packed data (hi)
00/056D: D4 DF         PEI DF       ; Pointer to packed data (lo)
00/056F: D4 D1         PEI D1       ; size of packed data
00/0571: 7B            TDC
00/0572: 18            CLC
00/0573: 69 E3 00      ADC #00E3
00/0576: A2 00 00      LDX #0000
00/0579: DA            PHX          ; Pointer to pointer to unpacked data (hi)
00/057A: 48            PHA          ; Pointer to pointer to unpacked data (lo)
00/057B: 7B            TDC
00/057C: 18            CLC
00/057D: 69 D5 00      ADC #00D5
00/0580: A2 00 00      LDX #0000
00/0583: DA            PHX          ; Pointer to DWORD contening Size (hi)
00/0584: 48            PHA          ; Pointer to DWORD contening Size (lo)
00/0585: A2 03 27      LDX #2703    ; Tool Number (UnPackBytes)
00/0588: 22 00 00 E1   JSL E10000  UnPackBytes(@Buff,BfSz,@StartPtr,@Sz):Size
00/058C: 8D 9E 09      STA 099E
00/058F: 68            PLA ; size
00/0590: 85 DD         STA DD
00/0592: A5 DD         LDA DD
00/0594: A2 00 00      LDX #0000
00/0597: C9 00 00      CMP #0000
00/059A: 10 01         BPL 059D {+01}
00/059C: CA            DEX
00/059D: DA            PHX
00/059E: 48            PHA
00/059F: A5 DF         LDA DF
00/05A1: A6 E1         LDX E1
00/05A3: 22 9C 2A 01   JSL 012A9C  ; addition
00/05A7: 68            PLA
00/05A8: FA            PLX
00/05A9: 85 DF         STA DF
00/05AB: 86 E1         STX E1
00/05AD: A5 DB         LDA DB
00/05AF: 3A            DEC
00/05B0: 0A            ASL
00/05B1: 0A            ASL
00/05B2: 0A            ASL
00/05B3: 0A            ASL
00/05B4: 0A            ASL
00/05B5: AA            TAX
00/05B6: F4 68 08      PEA 0868
00/05B9: A9 68 08      LDA #0868
00/05BC: 18            CLC
00/05BD: 86 F7         STX F7
00/05BF: 65 F7         ADC F7
00/05C1: 48            PHA
00/05C2: A5 E1         LDA E1
00/05C4: 48            PHA
00/05C5: A5 DF         LDA DF
00/05C7: 48            PHA
00/05C8: F4 20 00      PEA 0020   ; les fameux 32 derniers octets
00/05CB: 22 A9 23 01   JSL 0123A9 ; move Palettes block
00/05CF: D4 CB         PEI CB
00/05D1: D4 C9         PEI C9
00/05D3: A2 02 10      LDX #1002
00/05D6: 22 00 00 E1   JSL E10000  DisposeHandle(H)
00/05DA: 8D 9E 09      STA 099E
00/05DD: A5 DB         LDA DB
00/05DF: 3A            DEC
00/05E0: 0A            ASL
00/05E1: 0A            ASL
00/05E2: 0A            ASL
00/05E3: 0A            ASL
00/05E4: AA            TAX
00/05E5: 86 F7         STX F7
00/05E7: 48            PHA
00/05E8: A2 04 17      LDX #1704
00/05EB: 22 00 00 E1   JSL E10000  GetMasterSCB():SCB
00/05EF: 8D 9E 09      STA 099E
00/05F2: 68            PLA
00/05F3: A6 F7         LDX F7
00/05F5: 9D 38 08      STA 0838,X
00/05F8: A5 DB         LDA DB
00/05FA: 3A            DEC
00/05FB: 0A            ASL
00/05FC: 0A            ASL
00/05FD: 0A            ASL
00/05FE: 0A            ASL
00/05FF: AA            TAX
00/0600: 86 F7         STX F7
00/0602: A0 02 00      LDY #0002
00/0605: B7 CD         LDA [CD],Y
00/0607: AA            TAX
00/0608: A7 CD         LDA [CD]
00/060A: 85 F3         STA F3
00/060C: 86 F5         STX F5
00/060E: A6 F7         LDX F7
00/0610: A5 F3         LDA F3
00/0612: 9D 3A 08      STA 083A,X
00/0615: A5 F5         LDA F5
00/0617: 9D 3C 08      STA 083C,X
00/061A: A5 DB         LDA DB
00/061C: 3A            DEC
00/061D: 0A            ASL
00/061E: 0A            ASL
00/061F: 0A            ASL
00/0620: 0A            ASL
00/0621: AA            TAX
00/0622: A9 A0 00      LDA #00A0
00/0625: 9D 3E 08      STA 083E,X
00/0628: A5 DB         LDA DB
00/062A: 3A            DEC
00/062B: 0A            ASL
00/062C: 0A            ASL
00/062D: 0A            ASL
00/062E: 0A            ASL
00/062F: AA            TAX
00/0630: A9 40 08      LDA #0840
00/0633: 18            CLC
00/0634: 86 F7         STX F7
00/0636: 65 F7         ADC F7
00/0638: A2 40 08      LDX #0840
00/063B: 85 F3         STA F3
00/063D: 86 F5         STX F5
00/063F: A9 00 00      LDA #0000
00/0642: 87 F3         STA [F3]
00/0644: A0 02 00      LDY #0002
00/0647: A9 00 00      LDA #0000
00/064A: 97 F3         STA [F3],Y
00/064C: A9 C8 00      LDA #00C8
00/064F: A0 04 00      LDY #0004
00/0652: 97 F3         STA [F3],Y
00/0654: A9 40 01      LDA #0140
00/0657: A0 06 00      LDY #0006
00/065A: 97 F3         STA [F3],Y
00/065C: A5 DB         LDA DB
00/065E: C5 F9         CMP F9
00/0660: F0 06         BEQ 0668 {+06}
00/0662: 1A            INC
00/0663: 85 DB         STA DB
00/0665: 82 6C FD      BRL 03D4 {-294}

On a donc une routine qui va effectuer, pour chacune des trois images affichées par le programme, la série d'actions suivantes (et dans cet ordre) :

  • lecture du fichier PICx depuis le disque et détermination de sa taille (utilisation des fonctions OPEN/READ/GET_EOF de Prodos 16).
  • première phase de décryptage : EOR ascendant effectué sur l'ensemble du fichier sauf les 32 derniers octets suivant la formule : A<= A EOR (A-2)'. Grosso modo, on fait un EOR entre un WORD et celui qui le précède légèrement modifié (d'où le "prime"). Le tout premier WORD est eoré avec #EF01 (valeur fixe).
  • seconde phase de décryptage : EOR descendant effectué sur l'ensemble du fichier sauf les 31 derniers octets (oui 31 et non 32 cette fois !) suivant la formule : A<= A EOR (A+2)'. Le dernier WORD est eoré avec #ABCD (valeur fixe encore).
  • pour obtenir le "prime", deux petites manipulations de bits sont effectuées : A' = (ASL A) EOR #$4321.
  • troisième phase : le résultat obtenu, après les deux phases de décodage par EOR, est envoyé vers la fonction UnPackBytes du Tool Set 3 (Miscellanous Tool Set)  !
  • Le résultat final est la partie "pixel" d'une image Super-Hires. Il ne contient ni les 200 Scanline Control Bytes ni les Color Tables ! Ceux-ci sont ajoutés par la suite par le programme . Toutefois, les 32 octets de fin de fichier,  non traités par le EOR (ou presque),  correspondent à la palette principale de l'image.

À la lecture de ce petit descriptif, on comprend tout de suite que pour notre propre programme d'encodage, il faudra utiliser la fonction inverse à UnPackBytes à savoir (je vous le donne en mille) : PackBytes. Ce qui implique bien sûr l'utilisation des Tools GS ! Terminé les petits trucs faits à l'arrache sous Merlin comme au bon vieux temps du 8 bits !

 

Partie IV : récupérer l'image !

En effet, pour pouvoir la signer, il va déjà falloir avoir une version vierge de l'image. Bien entendu, elle n'est pas fournie sur le disque ! Nous devrons la récupérer nous-même. La solution la plus simple est d'intercepter l'image (ou plutôt) la zone mémoire où elle est stockée juste après l'appel de la fonction UnPackBytes. Si vous avez lu ce qui précède, vous savez que c'est la dernière étape du décryptage et donc que l'image est sous sa forme quasi-définitive après son exécution. On pose donc un breakpoint sur l'adresse de la fonction. Une fois le breakpoint déclenché, on en profite pour checker l'adresse mémoire contenant l'adresse (oui c'est un pointeur d'un pointeur...) où sera unpacked notre image. Cette adresse est importante car c'est cette zone que nous allons ensuite sauvegarder sous forme de fichier. Ne reste plus alors qu'à exécuter la fonction UnPackBytes en mode SINGLE STEP. Et à effectuer la sauvegarde.

Entrée alors en scène d'un nouvel utilitaire, le CDA Nifty List et son complément NiftyFile. Juste après avoir exécuter la fonction UnPackBytes, on lance le CDA et on sauve la zone unpacked. En fait, vous remarquerez sur l'écran ci-contre que je sauve directement $8000 bytes.  À ce stade, nous avons en fait uniquement la partie "pixel" de l'image qui a été décryptée. Celle-ci fait $7D00 octets. Il nous manque $300 octets de données valides (les Scan Lines et les Color Tables). La fin du fichier sera donc pour le moment occupée par des datas ne correspondant à rien. Nous les remplacerons bientôt. Pour info, le "C1"  de la ligne de commande permet de fixer directement le type de fichier sur PICTURE. Cela nous servira lorsqu'il faudra ouvrir l'image avec un logiciel de dessin.

 

Si vous essayez de visualiser directement l'image récupérée précédemment, voici à peu près ce que vous devriez voir... Contrairement aux apparences, nous sommes sur la bonne voie ! Il manque certes les infos de Scan Lines et les Palettes de couleurs mais le reste y est. Il nous faut maintenant récupérer les données manquantes depuis l'image originale.

 

 

L'idée la plus évidente est d'attendre tout simplement que le soft la charge, la décrypte et l'affiche à l'écran. Il suffit alors de passer sous GSBug pour stopper le programme. Puis de relancer Nifty Lift pour enfin sauver la partie qui nous intéresse.

 

 

On récupère directement les infos dans la partie Super-Hires de la mémoire (en bank $E1). On ne prend que les $300 derniers octets (entre $9D00 et $9DFF) et on sauve le tout sur disk. Là, vous allez sûrement vous demander pourquoi ne pas sauver toute l'image à ce moment-là uniquement et éviter le cirque précédent ? La réponse est simple et vous l'avez même sous les yeux :  sauver toute la page Super-Hires inclurait la barre de menu de l'interface graphique du soft. Alors oui c'est sûr que ce qui est en-dessous (si tant est qu'il existe quelque chose) ne se verra de toute façon pas et on pourrait très bien effacer ensuite la barre par l'intermédiaire d'un soft de dessin (que nous allons de toute façon devoir utiliser pour la signature). C'est une approche, qui d'ailleurs, marcherait parfaitement dans le cas présent. Toutefois, je préfère systématiquement récupérer l'image juste après son décryptage. C'est la méthode la plus sûre (pour l'intégrité de l'image) et la plus universelle.

Bien, une fois nos $300 octets récupérés (et seulement ceux-là !), la suite, je suppose que vous l'imaginez vous-même. Nous allons coller cette partie à la fin de notre pseudo image sauvée précédemment, à la place des $300 octets de pur garbage. Le couple "pixel"/"info palettes" de l'image finale sera ainsi recomposé !

Mais avant de recoller les morceaux, nous allons devoir utiliser un utilitaire bien connu des utilisateurs Prodos, le bien nommé Block Warden. Il va nous permettre de fixer un petit défaut de notre dump de $300 octets. En effet, nous avons capturé les infos de l'écran principal contenant notre image mais aussi celles concernant la barre de menu. Attention, je ne parle pas ici de la partie "pixel" mais bien des "infos palettes". Regardez d'ailleurs avec Block Warden les premiers octets qui correspondent aux premières lignes de l'image.

Voici à quoi ressemblerait notre image finale si nous ne modifions pas les premiers octets. Remarquez, cela ne se verra pas. En fait, cela n'a même aucune importance car une fois signée, nous n'allons encrypter et coder que la partie "pixel" de l'image. Mais autant faire les choses proprement. La nuance est subtile avec la partie précédente où je voulais avoir l'intégralité de l'image, y compris les éventuels pixels "cachés" sous la barre de menu. Ici, nous avons bien l'image en entier, ce sont simplement les couleurs qui ne sont pas les bonnes pour les premières lignes de l'image.

 

On revient à Block Warden, avec lequel on uniformise les premiers octets à $08 comme les autres (utilisation de la palette de couleurs 8). Il ne reste plus maintenant qu'à regrouper notre dump fixé avec la partie "pixel" de l'image.

 

 

 

Pour ce faire, on va de nouveau utiliser l'extension NiftyFile sous Nifty List. L'idée est simple : on charge notre pseudo image récupérée tout à l'heure, on remplace les $300 derniers octets par notre dump modifié précédemment et on sauve le tout. Nous avions déjà un fichier de $8000 octets donc aucun problème de place mémoire.

 

 

Et voici le résultat : nous avons enfin une image propre et complète. Elle va maintenant nous servir pour deux choses : la première, évidente, sera son utilisation comme base pour l'ajout de notre signature. Mais avant cela, non modifiée, elle nous sera indispensable pour la mise au point du programme d'encodage. En repartant de cette image, nous devrons être capables, avec notre routine, de générer le même fichier PIC1 que celui utilisé par le programme...

 

Partie V : le programme de codage.

Pour ce faire, notre programme devra être exactement l'inverse du programme de décodage (logique...). Y compris au niveau de la chronologie des évènements. Nous avions trois phases pour le décodage, nous allons donc retrouver trois phases pour le codage :

  • Packer/compresser la partie "pixel" de l'image (les $7D00 premiers octets) avec la fonction du Tool Set 3, PackBytes.
  • Appliquer la phase 1 de codage : A<= A EOR (A+2)'. C'est exactement la même formule que pour le décodage phase 2.  Par contre, nous avions une phase 2 descendante pour le décodage, l'inverse sera donc une phase 1 ascendante.
  • Ensuite viendra la phase 2 de codage : A<=A EOR (A-2)'. Là encore, la phase 2 de codage sera l'inverse de la phase 1 de décodage. Nous avions une phase 1 ascendante. Notre phase 2 sera donc descendante.
  • Comme vu précédemment, on passe de A à A' - donc de (A-2) à (A-2)' - en faisant un simple (ASL A) EOR #$4321 . Que ça soit pour le décodage ou le codage, la même formule est appliquée.
  • La seule difficulté (qui m'aura fait m'arracher les cheveux que je n'ai plus) sera de bien "borner" les algos. En effet, il faut que les phases inverses s'effectuent exactement sur le même nombre d'octets.

Je vous livre ci-après le code source de ma routine d'encodage. J'ai utilisé les routines Text Tool pour la sortie caractères, afin de simplifier le tout et ne pas devoir utiliser le Tool Set QuickDrawII (ce qui aurait alourdi considérablement le code). Pas de fenêtre, pas de souris, mais un truc sobre et efficace, à l'ancienne quoi...

;...............................................................
; DEFI GS ON THE ROCKS 4 (SIGNATURE IMAGE 1)
; WWW.CTRL-POMME-RESET.FR
; v0.5 - ORCA/M
;...............................................................
 
               KEEP test
	       MCOPY test.mac   ; fichier MACRO (généré avec macgen)
 
DeRefLoc	GEQU $0         
 
HandleBuffer	GEQU $10	
 
VC5		GEQU $C5
VC7		GEQU $C7
VD1		GEQU $D1
VD3		GEQU $D3
VD7		GEQU $D7
VD9		GEQU $D9
VDF		GEQU $DF
VE1		GEQU $E1
 
TEMP		GEQU $A0
VDC		GEQU $B0
 
Main		START
 
		PHK	        ; data bank
		PLB             ; = code bank
 
		_TLStartup	; Tools Locator on
		_MTStartup	; Miscellanous Tools on
 
		PHA
		_MMStartup      ; Memory Manager on
		Pullword MyID
 
		PHA
		PHA
		PushLong $100	; direct Page memory request
		PushWord MyID
		PushWord #$C005	; locked, fixed, aligned, fixed bank
		PushLong #0	; bank 0
		_NewHandle	; Direct Page
		PullLong DeRefLoc	
 
		LDA [DeRefLoc]
		PHA
		PLD	        ; direct Page Register
 
                PEA MSG1|-16
		PEA MSG1
		_WriteCString   ; message intro + open image
 
                JSL $E100A8	; ProDOS16 entry point
                DC I2'$10'	; commande OPEN
		DC I4'OpenParms'; parmtable pour OPEN
		BCC s1          ; open OK ?
		BRL Error	; sinon erreur
 
s1	        PEA MSGOK|-16
		PEA MSGOK
		_WriteCString   ; OPEN OK
 
		PEA MSG2|-16
		PEA MSG2
		_WriteCString	; message allocate memory
 
		LDA refnum
		STA refnum2	; pour close
		STA refnum3	; pour read
 
		PEA $0000
		PEA $0000	; push longword for result
		PEA $0001
		PEA $0000	; push size = $10000 (128ko)
		LDA MyID
		PHA	        ; push ID
		PEA $C000	; attributes for block
		PEA $0000
		PEA $0000	; location (0=default)
		_NewHandle	; allocation mémoire
		BCC s2
                BRL Error
s2		PLA
		PLX
		STA HandleBuffer
		STX HandleBuffer+2
 
                PEA MSGOK|-16
		PEA MSGOK
		_WriteCString
 
                PEA MSG3|-16
		PEA MSG3
		_WriteCString	 ; message read file
 
		LDA [HandleBuffer]
		STA Buffer
		LDY #2
		LDA [HandleBuffer],Y
		STA Buffer+2
 
		LDA #32768	; lecture image complète ($8000)
		STA NbBytes
		LDA #0000
		STA NbBytes+2
 
		JSL $E100A8
		DC I2'$0012'	; commande READ
		DC I4'ReadParms'
		BCC s3
		BRL Error
 
s3		PEA MSGOK|-16
		PEA MSGOK
		_WriteCString	; lecture OK
 
;...............................................................
;encryptage + package
;...............................................................
 
		PEA MSG5|-16
		PEA MSG5
		_WriteCString	; message Packing
 
		PHA	        ; result space
		PEA Buffer|-16
		PEA Buffer
		PEA Size|-16
		PEA Size	; adresse WORD contenant size
 
		LDA Buffer
                CLC
		ADC #$8000	; calcul adresse output buffer
		STA OutBuff
		STA VDF	        ; on sauve le buffer (lo) pour la suite
		LDA Buffer+2
		ADC #$0000
		STA OutBuff+2
		STA VE1
		PHA	        ; output buffer (hi)
		LDA OutBuff
		PHA	        ; output buffer (lo)
		PEA $8000	; output buffer size
                _PackBytes
		BCC s4
		BRL Error
 
s4		PLA
		TAX
		AND #01
		BEQ s5
 
		PEA MSG90|-16
		PEA MSG90
		_WriteCString	; message erreur size impaire
		BRL Fin
 
s5	        TXA
		STA VD1	        ; nb packed bytes
		STZ VD3
		CLC
		ADC #$20	; on ajoute 32 octets avant sauvegarde
		STA WriteSize	; sauve nb bytes packed pour WRITE
		STZ WriteSize+2	; (hi=0)
 
		PEA MSGOK|-16
		PEA MSGOK
		_WriteCString	; Packing OK
 
               JSL $E100A8
		DC I2'$0014'	; commande CLOSE
		DC I4'CloseParms'
 
		PEA MSG4|-16	; Message Encryptage
		PEA MSG4
		_WriteCString	;
 
; on colle la palette à la fin
 
	        LDA VD1
		LDX #0000
		CMP #0000
		BPL suite1t
		DEX
suite1t		PHX
		PHA
		LDA VDF
		LDX VE1
		JSL ADDITION
		PLA
		PLX
		STA VC5
		STX VC7
 
		PEA PALETTE|-16
		PLA
		STA TEMP+2
		PEA PALETTE
		PLA
		STA TEMP
 
		LDY #00
 
BB1		LDA [TEMP],Y
		STA [VC5],Y
		INY
		INY
		CPY #32
		BNE BB1
 
; début double encryptage par EOR
; D1 = nb Packed Bytes après Fonction PackBytes
; phase 1 : ascendante (inverse descendante)
; de 2 à D1 (D1 EOR ABCD)
 
		LDA #02	       ; on part de 2 (0 non traité)
		STA VD7
 
		LDA VD1
		STA VDC
 
		DEC VD1	       ; D1-2
		DEC VD1
 
Boucle1		LDA VD7
		LDX #0000
		CMP #0000
		BPL suite1
		DEX
suite1		PHX
		PHA
		LDA VDF
		LDX VE1
		JSL ADDITION
		PLA
		PLX
		STA VC5
		STX VC7
		LDY #2
		LDA [VC5],Y     ; au max D1-2 + 2 = D1
		ASL A
		EOR #$4321
		STA VD9
		LDA [VC5]
		EOR VD9
		STA [VC5]
		INC VD7
		INC VD7
		LDA VD7
		LDX #0000
		CMP #0000                       	;
		BPL suite2
		DEX
suite2		CMP VDC	       ; D1 (DONC D1-2 MAX traité)
		BNE suite3
		CPX VD3
suite3		BEQ suite4
		BRL Boucle1		
 
suite4	        LDA VDC
		LDX #0000
		CMP #0000
		BPL suite4b
		DEX
suite4b		PHX
		PHA
		LDA VDF
		LDX VE1
		JSL ADDITION
		PLA
		PLX
		STA VC5
		STX VC7
		LDA [VC5]
		EOR #$ABCD
		STA [VC5]	; on fixe le dernier WORD
 
		DEC VD1
		DEC VD1	        ; VD1 passe à -4
 
; phase 2 : descendante (inverse ascendante)
; de 0 à D1-2 (0 EOR EF01)
 
		LDA VD1         ; D1-4
		STA VD7
 
Boucle2		LDA VD7
		LDX #0000
		CMP #0000
		BPL suite5
		DEX
suite5		PHX
		PHA
		LDA VDF
		LDX VE1
		JSL ADDITION
		PLA
		PLX
		STA VC5
		STX VC7
		LDA [VC5]	; DI-4 au max
		ASL A
		EOR #$4321
		STA VD9
		LDY #2
		LDA [VC5],Y     ; donc D1-4 + 2 = D1-2 !
		EOR VD9
		STA [VC5],Y
		DEC VD7
		DEC VD7
		LDA VD7
		CMP #$FFFE	; VD7 = 0 inclus
		BEQ suite6
                BRL Boucle2
 
suite6		LDA [VDF]	; on fixe le premier
		EOR #$EF01      ; WORD
		STA [VDF]
 
; fin encryptage
 
		PEA MSGOK|-16
		PEA MSGOK
		_WriteCString	; OK encryptage
 
		PEA MSG6|-16
		PEA MSG6
		_WriteCString	; Message Sauvegarde
 
                JSL $E100A8
		DC I2'$0001'
		DC I4'CreateParms'; commande CREATE
 
		JSL $E100A8
		DC I2'$10'
		DC I4'OpenParms2' ; commande OPEN
		BCS Error
 
		LDA refnum4       ; ref du file ouvert
		STA refnum5       ; pour write
		STA refnum2	  ; pour close
 
		LDA OutBuff
		STA WriteData
		LDA OutBuff+2
		STA WriteData+2	  ; Buffer Write
 
		JSL $E100A8
		DC I2'$13'
		DC I4'WriteParms' ; commande WRITE
		BCS Error
 
		PEA MSGOK|-16
		PEA MSGOK
		_WriteCString	  ; Save OK
 
		JSL $E100A8
		DC I2'$14'
		DC I4'CloseParms' ; commande CLOSE
 
                BRA Fin
 
Error		PEA MSGERROR|-16
		PEA MSGERROR
		_WriteCString
 
Fin		ANOP
 
		PEA MSG99|-16
		PEA MSG99
		_WriteCString	  ; message fin
 
		PHA	          ; space for result
		PEA $0000	  ; pas d'echo
		_ReadChar	
 
                PushWord MyID
		_DisposeAll	  ; ménage !
 
	        PushWord MyID
		_MMShutDown       ; Memory Manager off
 
                _MTShutDown	  ; Miscellanous Tools off
		_TLShutDown	  ; Tools Locator off
 
		_Quit QuitParams
 
		BRK
 
ADDITION	CLC
		ADC 04,S
		STA 04,S
		TXA
		ADC 06,S
		STA 06,S
		RTL
 
MyID		DS 2
 
QuitParams	DC I4'0'
		DC I'0000'
 
MSG1		DC C'Resolution DEFI GS ON THE ROCKS 4 v0.5',I1'$8D,$8A'
                DC C'Ouverture Image : ',I1'0'
 
MSG2		DC C'Allocation memoire : ',I1'0'
 
MSG3		DC C'Lecture Image : ',I1'0'
 
MSG4		DC C'Encryptage : ',I1'0'
 
MSG5		DC C'Packing : ',I1'0'
 
MSG6		DC C'Sauvegarde Image Packed et Encrypted : ',I1'0'
 
MSG90		DC C'ERREUR : taille image packed non paire !',I1'$8D,$8A,0'
 
MSG99		DC C'Appuyez sur une touche pour quitter...',I1'0'
 
MSGOK		DC C'OK',I1'$8D,$8A,0'
 
MSGERROR	DC C'ERREUR !',I1'0'
 
OpenParms	ANOP
refnum		DS 2
		DC I4'FileName'
                DS 4
 
FileName	DC I1'11'
		DC C'1/PICSIGNED'
 
CloseParms	ANOP
refnum2		DS 2
 
ReadParms	ANOP
refnum3		DS 2
Buffer		DS 4
NbBytes		DS 4
		DS 4
 
CreateParms	ANOP
		DC I4'SaveFileName'
		DC I2'$E3'	; access code
		DC I2'$C2'	; type file ($C2 comme l'original)
		DC I4'0'	; aux type
		DC I2'2'	; storage type (sapling file)
		DC I2'0'	; création date
		DC I2'0'	; création time
 
OpenParms2	ANOP
refnum4		DS 2
		DC I4'SaveFileName'
		DS 4
 
SaveFileName	DC I1'11'
		DC C'1/PICRYPTED'
 
WriteParms	ANOP
refnum5		DS 2
WriteData	DS 4
WriteSize	DS 4
		DS 4
 
Size		DC I2'$7D00' ; nb de bytes à packer (pixels only)
OutBuff		DS 4
 
PALETTE		DC I1'$00,$01,$51,$09,$62,$0A,$62,$0B'
                DC I1'$73,$0B,$73,$0C,$84,$0D,$95,$0E'
                DC I1'$A6,$0F,$31,$08,$52,$0C,$00,$01'
                DC I1'$00,$04,$21,$07,$31,$09,$FF,$0F'
 
		END

La routine de cryptage est loin d'être optimisée car j'ai surtout cherché à la calquer sur celle de décodage. Ce qui explique aussi le nom peu orthodoxe des variables utilisées (j'ai repris les adresses Page Zéro du code original). Pour le reste, l'essentiel est composé de l'appel aux différentes fonctions Tool Sets et ProDOS 16 (pour la gestion des fichiers). À ce sujet, j'ai limité l'utilisation des macros propres à ORCA/M pour la gestion de ces fonctions afin de garder un code source proche de celui obtenu après assemblage. Pour une utilisation sous Merlin 16, il faudra sans doute faire quelques adaptations.
À noter que le programme ne laisse pas le choix du nom des fichiers ni en entrée ni en sortie. Il faudra penser à les renommer en fonction des besoins.

Partie VI : signer l'image.

 

Pour la signature, j'ai commencé par utiliser Deluxe Paint II pour ajouter un petit gribouillis sur l'image. Puis, je me suis aperçu (tardivement) que la sauvegarde ne pouvait s'effectuer qu'au format propriétaire compressé. Il nous faut, je vous le rappelle, un format non compressé, le package et l'encodage se faisant par notre routine.

 

J'ai donc repris l'image sauvée avec Deluxe Paint sous PaintWorks Gold qui lui propose l'option "Sreen Format" lors de la sauvegarde. Bien évidemment, signer directement l'image sous PaintWorks marche aussi. Vous pouvez également éditer l'image avec votre logiciel favori puis la convertir ensuite avec les multiples utilitaires disponibles sous GS/OS faisant le job. Bref, je laisse l'artiste en vous s'exprimer librement. Ce qui compte par contre, c'est d'avoir une image finale non compressée et sauvée au format écran (donc avec l'organisation mémoire classique).

Et... je vous laisse découvrir le superbe résultat obtenu avec la première version a priori fonctionnelle de mon programme d'encodage. Pourtant, je vous garantie qu'avec l'image non retouchée, tout était OK, elle était ré-encryptée et affichée correctement. Pourquoi donc avec une image modifiée, nous subissons un problème évident de palette (alors que l'image utilise forcément celle par défaut) ? Y'aurait-il une vérification cachée quelque part ? Là encore, il m'a fallu un peu de temps pour comprendre le problème. Et il faut remonter un peu en arrière pour en trouver l'origine !

Explication : la routine de décodage des images de GS On The Rocks 4 décrypte les octets par groupe de deux. Elle travaille en 16 bits, donc sur des WORD. Les deux boucles principales de décryptage utilisent le taille de l'image cryptée (moins 32) comme valeur de base. Les compteurs de boucles étant incrémentés (ou décrémentés) de deux à chaque passage, si cette taille est paire, pas de problème, on aura bien exécution d'un nombre de boucles donné. C'est le cas avec l'image d'origine (qui fait 15866 octets). Avec une taille impaire par contre, les problèmes commencent. Les boucles de décodage deviennent infinies car les conditions de fin ne sont jamais vérifiées. Pour éviter ce type de plantage, j'avais donc intégré à ma routine de codage une détection sur la parité de la taille après PackedBytes de façon à ajouter artificiellement un octet supplémentaire si nécessaire, de façon à toujours obtenir une taille d'image paire.

Or, en ajoutant cet octet, j'ai engendré un autre problème touchant la palette intégrée au fichier (les fameux 32 octets de fin). L'octet supplémentaire ajouté après la partie "pixel" de l'image cryptée, décale d'autant la palette, modifiant totalement la correspondance des couleurs. D'où ce superbe bleu observé ci-dessus. On ne peut pas non plus ajouter l'octet supplémentaire à la fin du fichier car la taille de la partie cryptée (obtenue, je vous le rappelle, par taille fichier - 32) est alors faussée pour le décodage. Seule solution viable : avoir une vraie taille d'image Packed paire ! Cette taille dépendant directement de l'image à compresser, il faudra systématiquement vérifier si, après PackedBytes, la taille obtenue est correcte. La dernière version de mon code inclut cette vérification. Hélas, pour savoir si une image est OK , il n'y a pas d'autre solution que de lancer la routine et d'attendre le verdict.

 

Voici ce qu'affiche le programme de codage/package lorsque la taille ne lui convient pas ! Dans ce cas, l'image ne pourra pas être encodée. Il faudra en essayer une autre.

 

 

 

 

Exemple : cette image-ci donnera un résultat incorrect (c'est d'ailleurs celle à l'origine de l'effet bleuté vu précédemment).

 

 

 

 

Celle-ci non plus ne passera pas... Essayons de modifier légèrement la couleur, ou le positionnement de la signature. Méthode empirique obligatoire !

 

 

 

Bingo, nous avons un gagnant !
Il suffit maintenant de renommer le fichier PICRYPTED obtenu en PIC1 et de remplacer celui sur le disque GS On The Rocks 4 !

 

 

 

 

L'instant de vérité : le programme du défi décode cette fois parfaitement l'image... Vous remarquerez la subtile différence avec l'image d'au-dessus qui ne passait pas l'encodage. Au fait, finalement pas de test caché, pas de checksum fourbe, rien... Game Over !

 

 

Petit récapitulatif des outils utilisés pour mener à bien ce défi :

  • GSBug version 1.6 (Apple Computer) et System Loader Dumper 4 (Greg Branche).
  • ORCA/Disassembler 1.2 (Paul Elseth/Byte Works). Fait partie du package Opus ][ vendu par Syndicomm. Oui, continuer à vendre des softs d'il y a 30 ans plus de 80$ pour une version totalement dématérialisée, c'est limite abusé... Comme alternative, vous pouvez utiliser l'excellent The Flaming Bird Disassembler (Ferox/Phoenix Corporation).
  • Block Warden 2.5 (Glen Bredon).
  • Nifty Lift 3.4 (David A. Lyons) avec l'extension NiftyFile 0.99 (Jonah Stich).
  • Deluxe Paint II 2.01 (Dan Silva/Electronic Arts) et  PaintWorks Gold 1.0 (Luc Barthelet & Henri Lamiraux/Version Soft).
  • ORCA/M Macro Assembler 2.1.0 (Byte Works). Alternative possible : Merlin 16 (Glen Bredon) disponible et utilisable gratuitement lui...
  • Mon programme de codage/package en version 0.5 non optimisé, probablement bourré de fautes de conception mais qui a le mérite de sortir une image cryptée ! Disponible avec ses sources sur le disk de travail en fin d'article.

Pour trouver tous ces programmes (tout au moins ceux pour lesquels personne ne vous réclamera des sous), les sources habituelles (Asimov, What is the Apple IIGS, Mac GUI City, Apple IIGS FranceBrutal Deluxe, etc.) sont vos amies. Pour les autres...

Et pour finir quelques remarques en guise de conclusion :

  • Cet article n'a pas vocation à vous apprendre à maîtriser chaque utilitaire utilisé pour la résolution du défi. La plupart sont complexes et demandent une certaine habitude pour être exploités au maximum. Je n'ai d'ailleurs fait qu'effleurer leurs possibilités. Je vous invite à vous référer à leur doc respective pour en avoir un aperçu plus complet (et obtenir des éclaircissements sur certaines commandes utilisées si besoin est).
  • De même, certaines étapes n'ont pas été détaillées ou même mentionnées (manipulations de disquettes, changements de prefix, modifications de file type et bien sûr toutes les phases de tracing sous GSBug avec suivi des adresses mémoire, etc.). Le but était surtout de proposer un aperçu général du processus pour mener à bien le défi plutôt qu'un véritable mode d'emploi à suivre point par point.
  • Attention aux infos trouvées dans les divers ouvrages parus sur le GS. J'ai perdu beaucoup de temps sur la fonction PackBytes car le type d'un des arguments référencés était incorrect ! En cas de doute, recoupez les infos et utilisez notamment les Toolbox Reference signés Apple comme juge de paix !
  • Aussi curieux que cela puisse paraître, ma petite expérience de la programmation et de la bidouille sous Windows aura été un vrai plus pour aborder ce défi (et me servira sans doute par la suite). Avec les Tool Sets, la programmation GS ressemble beaucoup à la programmation sous un OS moderne. Et par extension, on retrouve à peu près les même méthodes pour aborder le côté sombre de la force...
  • Bien qu'ayant travaillé essentiellement sous ActiveGS, j'ai pris soin de n'utiliser que des techniques ou des softs typiquement GS pour chacune des étapes. Avoir recours par exemple à Ciderpress ou à un éditeur Hexa aurait simplifié et accéléré les choses certes mais était totalement hors défi. Par contre, il est bien évident qu'un émulateur (en vitesse illimité) permet de gagner beaucoup de temps pour les boot du système. Se retrouver sous environnement ORCA en moins de 10 secondes a été un gros plus lors de la mise au point du programme de codage par exemple.
  • Quant au défi en lui-même, merci (avec quelques décennies de retard) à Gronk et à son équipe pour ce sympathique challenge. Même si dans l'absolu, il était relativement simple à résoudre, à chaque étape, il m'aura posé un problème spécifique. Une bonne façon de défricher le terrain pour la suite...

 

Téléchargements :

 

"Quelle délivrance d'appuyer sur le bouton Publier et d'en avoir enfin fini avec cet article" (blogueur anonyme)