site sans réclame PythonCont@ctS'abonner

Lire les images d'écran ATARI ST: Degas, Neochrome, copie d'écran

V

OUS disposez peut-être encore des images .PI1, .PI2, .PI3, .PC1, .PC2 et .PC3 du logiciel Degas (Elite) ou les images .NEO (et .PIC?) du logiciel Neochrome de l'ATARI ST(E), qu'il est difficile de visionner sur les plateformes actuelles. Des solutions libres et partielles ou freeware existent, et une solution libre et complète ici. Vous trouverez ici de telles images.

Un utilitaire tout à fait libre, netpbm, contient trois commandes pour transformer les fichiers .PI1 ou .NEO (basse résolution) et .PI3 (haute) en .ppm (portable pixmap), mais pas la moyenne résolution ni les formats compressés. De plus pi1toppm ne tient pas compte de la palette plus fine (16 niveaux de RVB plutôt que 8) des fichiers produits par les (mega)STE.

Le freeware XnViewPM (Win, Mc et Linux) est censé lire ces trois formats d'image, mais je n'y suis pas parvenu.

La solution libre recoil semble difficilement compilable. Une page html permet de lire beaucoup d'images du monde Atari (et Amiga).

Pour d'autres formats d'images sur Atari ST(E), dont les images compressées .IMG, voir ici.

1. Organisation des fichiers

1.1 Résolution
1.2 Couleurs
1.3 Données et couleurs
1.4 Compression
1.5 Fichiers

2. Transformer un .PI3 ou .PC3 (640/400, noir et blanc)

3. Transformer un .PI1 ou .PC1 (320/200, 16 couleurs)

4. Transformer un .PI2 (640/200, 4 couleur)

Et finalement, le dernier script (PI2) permet également la transformation des PI1 et PI3.

1. Généralités des fichiers écrans

Les fichiers contenant une copie d'écran complète sur Atari sont le plus souvent organisés de la façon suivante:

Ces fichiers non compressés pour chacune des résolutions de l'Atari ST(E) donnent typiquement des copies d'écrans de 32000 (copie brute d'écran), 32034 (Degas), 32066 (Degas Elite) ou 32128 octets (Neochrome).

Les formats de l'Atari TT différaient de ceux du ST (basse résolution: 320x480 / 256 couleurs, moyenne: 640x480 / 16 couleurs et 1280x960 en haute résolution monochrome), les données non compressées étant codées avec 153 600 octets.

1.1 La résolution

L'Atari ST fonctionne en trois résolutions. Les deux résolutions en couleurs nécessitent un écran dédié (SC1224/5) ou une télévision couleur si l'on dispose d'un modulateur (interne à partir du STFM); la haute résolution nécessitent un écran dédié en noir et blanc (SM124/5). En mode émulation sur PC, l'écran habituel suffit. Le premier mot (2 octets) du fichier contient les valeurs 0, 1 ou 2:

On voit que c'est la résolution qui détermine les dimensions de l'image, largeur et hauteur de l'écran.

Les extensions .PC1, .PC2 et .PC3 sont destinées aux formats compressés (moins de 32000 octets de données). Les signatures sont (en hexadécimal) 8000 pour la basse résolution, 8001 pour la moyenne et 8002 pour la haute.

Degas aurait adapté son format pour les résolutions Atari-TT, avec un poids de 153600 + 34 ou 514):

Le format Neochrome sépare la signature de deux octets vides 00 00 en début de fichier de la résolution de deux octets: 00 00 (pour la basse résolution). Il existerait une version compressée dont la signature et la résolution sont au format Degas: 80 01 pour la compression d'un fichier en moyenne résolution.

1.2 Couleurs

Afin de garder une uniformité de structure entre les différents formats, Degas, Neochrome et Tiny prévoient 32 octets (16 mots) quelque soit le nombre de couleurs utiles:

Les Atari-ST offrent huit valeurs (de 0 à 7) pour chaque composantes vidéo (rouge, vert et bleu), soit 512 possibilités (83) pour chacune des 4 ou 16 couleurs des basse et moyenne résolutions. Le mot (2 octets) définissant une couleur est structuré de la sorte: -----RRR-VVV-BBB (binaire, le - est indéterminé, R, V et B valent 0 ou 1), par exemple 0777 (hexadécimal): le rouge, le vert et le bleu sont au maximum, pour donner le blanc.

Les couleurs se succèdent en début de fichier, selon l'ordre hardware (c'est à dire du XBIOS) et non l'ordre VDI (voir ici).

Les Atari-STE ont augmenté la sensibilité à 16 valeurs, de 0 à 15. Les valeurs de 0 à 7 sont interprétées par un STE comme allant de 0 à 14, les valeurs intermédiaires étant indiquée par le bit de gauche (en minuscules): ----rRRRvVVVbBBB. Il résulte deux conséquences de ce bricolage des temps héroïques:

De ce fait, comment un Atari-STE doit-il interpréter une image PI1 ou PI2 produite sur un Atari-ST? S'il ajoute systématiquement la demi-valeur 8 pour que le blanc 0777 devienne 0FFF, le noir 0000 deviendra 0888 (le gris le plus foncé). Il est possible de n'ajouter la valeur intermédiaire qu'à partir de la valeur 4, augmentant alors le contraste entre composantes sombres (0, 1, 2 et 3) et composantes claires (4, 5, 6 et 7) de chaque couleur, la valeur 11 n'existant plus (3+8 est l'intermédiaire entre 3 et 4).

Néanmoins, puisqu'il s'agit ici de convertir des fichiers Atari en fichiers capables d'afficher 16 millions de couleurs, ce problème ne nous concerne pas. Il suffit de préciser dans le header du fichier ppm produit que la teinte maximale est de 7 ou de 15, les teintes intermédiaires étant automatiquement calculées lors de l'affichage. Voyez ici.

1.3 Données non compressées

Pour la haute résolution, les 32000 octets (16000 mots) définissent les pixels un à un. Le mot (hexadécimal) C2A5 valant (en binaire) 1101 0010 1010 0101, les deux premiers pixels auront la valeur de la couleur 1 (noir), le troisième de la couleur 0 (blanc), le quatrième la couleur 1, etc. 40 mots (80 octets) sont nécessaires pour la première ligne, qui compte comme les autres 640 pixels, le 41e codera les 16 premiers pixels de la seconde ligne. C'est pour cela que la première couleur de l'entête est codée 0777 (ST) ou 0FFF (STE ou TT), la seconde 0000, les 14 mots suivants n'ayant aucune importance.

En moyenne résolution, la couleur est déterminée par un codage sur deux bits provenant de bits de même rang de deux mots qui se suivent, le bit du premier mot codant l'unité, le second codant la paire:

01011001 00111010 - premier mot
00111000 11110011 - second mot: un bit vaut 0 ou 2
01233001 22331032 - résultats de la combinaison de bits

Le résultat, de 0 à 3, renvoie à l'une des quatre couleurs du début de fichier.

En basse résolution, la couleur est déterminée par un codage sur quatre bits provenant de bits de même rang de quatre mots qui se suivent, le bit du premier mot codant l'unité, le second codant la paire, le troisième la double paire et le quatrième la quadruple paire:

01010101 01010101 - premier mot: un bit vaut 0 ou 1
00110011 00110011 - second mot: un bit vaut 0 ou 2
00001111 00001111 - troisième mot: un bit vaut 0 ou 4
00000000 11111111 - quatrième mot: un bit vaut 0 ou 8
01234567 89ABCDEF - résultats (hexadécimaux) de la combinaison de bits

Le résultat, de 0 à 15, renvoie à l'une des seize couleurs du début de fichier.

1.4 La compression

La compression des écrans Atari se fait selon la méthode RLE (Run Length Encoding), assez simple: un octet décide de la façon de considérer un certains nombre d'octets suivants.

Degas et Neochrome

Par exemple, la suite d'octets (en hexadécimal) F1 FF 01 EB 62 3D 00 se décompose comme ceci:

Conversion chiffre hexadécimal ⇔ binaire / pixels:

0=0000⇔....  
1=0001⇔...x
2=0010⇔..x.
3=0011⇔..xx
4=0100⇔.x..  
5=0101⇔.x.x
6=0110⇔.xx
7=0111⇔.xxx
8=1000⇔x...  
9=1001⇔x..x
A=1010⇔x.x.
B=1011⇔x.xx
C=1100⇔xx..
D=1101⇔xx.x
E=1110⇔xxx.
F=1111⇔xxxx

Notes:

Tiny

Le format .TNY est plus complexe. Le fichier comporte deux plages distinctes: une plage d'octets de contrôle et une plage de mots (deux octets) de donnée. La plage de contrôle est parcourue octet par octet, et la plage de données mot par mot (deux octets). Pour un octet valant n:

En outre, caractéristique de ce format, les mots décompressés s'alignent de façon verticale en quatre colonnes indépendantes.

À suivre: le codage des couleurs

1.5 Les différents fichiers

Copie d'écran

Il existe un format de copie d'écran brute, sans notification de résolution ni de couleur. Ils sont produits par certains logiciels, comme Doodle, et peuvent avoir pour extension .DOO, .SCR, .RAW, .MUR... Le fait que ces fichiers ne comportent ni résolution ni définition de couleurs fait qu'ils sont utilisés par des logiciels simplistes (Doodle est réputé monochrome, bien qu'il existe des fichiers .DOO en moyenne et basse résolutions) ou qui utilisent les couleurs par défaut du ST. Des jeux définissant leurs propres couleurs peuvent utiliser ce genre d'image en sauvegardant les couleurs dans un fichier joint. Tous ces fichiers comportent exactement 32000 octets, quelle que soit leur résolution:

Formats Degas non compressé

Le format Degas, qui connaît les trois extensions .PI1 (basse résolution), .PI2 (moyenne) et .PI3 (haute), utilise deux octets pour la résolution, 32 octets pour la définition des couleurs, et 32000 octets pour les données non compressées. C'est la résolution qui permet de savoir comment sont entrelacées les information sur les couleurs. Ils pèsent toujours 32034 octets, quelle que soit la résolution.

Le format Degas Elite non compressé ajoute 32 octets en fin de fichier pour donner des informations sur la façon dont les images apparaissent lors d'un diaporama. Elles pèsent toujours 32066

Le format NEOchrome .NEO utilise 4 octets pour la signature et la résolution [basse uniquement?], puis 32 octets pour la définition des couleurs, puis encore 92 octets pour une «animation» et des octets réservés, ce qui donne 128 octets d'entête, avant les 32000 octets de données. Certains fichiers .PIC sont très proches de ce format NEO.

Formats Degas compressés

Il semble que seul Degas Elite compresse les données .PC1 (basse résolution), .PC2 (moyenne) et .PC3 (haute). L'entête ne changeant pas, les données commencent à l'octet 34. Elles terminent 32 octets avant la fin, cequi est simple à spécifier en python: data=image[34:-32]

Il se trouve parfois des fichiers .NEO compressés (que la version 2.28 reconnaît). L'entête est alors différent, commençant par la signature/résolution en deux octets 80 02, suivie par 32 octets de couleurs, comme pour les fichiers Degas PIx et PCx.

2. Transformer un .PI3 (High Rez) en .pnm

Écrit en python, ce script recense tous les fichers .PI3 du répertoire, permettant de désigner ceux qui doivent être transformés selon leur numéro d'ordre (* pour tous).

#! /usr/bin/python
import sys, os

repertoires=os.listdir(".")

# filtering ".PI3" files
for i in range(len(repertoires)-1,-1,-1): # list reverse parsing
  if repertoires[i][-4:].upper()!=".PI3": # not pi3, PI3, Pi3 or pI3?
    repertoires.pop(i) # get rid of the file (doesn't delete it)

repertoires=sorted(repertoires)
leng=len(repertoires)
for i in range(leng): # showing all .PI3 files of the directory
  print "%3d %s" %(i, repertoires[i])

rep=raw_input("(space separated) file number(s) to transform, * for all: ")

files=[]
if rep=="*":
  files=list(repertoires) # OK
else:
  nblist=rep.split()
  for j in nblist:
    i=int(j)
    if i>leng-1 or i<0:
      print " Sorry! wrong file number..."
      sys.exit()
    else:
      files+=[repertoires[i]]

for nom in files: # processing each file
  han=open(nom)
  img=han.read()
  han.close()

  if len(img) not in [32034, 32066]:
    print "not a .PI3 format by Degas (Elite) on AtariST(E)"
    sys.exit()

  format0=img[0:2] # first word must be 2 for high rez pi3 Degas image 
  colours=img[2:6] # two words, first is colour0, second is colour1
  data=img[34:] # next 32000 bytes are the data

  newdata="" # initializing target data string 

  if format0!=chr(0)+chr(2): # magic number for high-rez atari-ST(E) Degas file
    print "not a .PI3 - Degas 640x400 image 2 colours on AtariST(E)"
    if format0!=chr(0)+chr(1): # magic number for med-rez atari-ST(E) Degas file
      print "seems to be a .PI2 - Degas 640x200 image 4 colours on AtariST(E)"
    if format0!=chr(0)+chr(0): # magic number for low-rez atari-ST(E) Degas file
      print "seems to be a .PI1 - Degas 320x200 image 16 colours on AtariST(E)"
    sys.exit()

  test=0; flag=0 # trying to see if STE colour format bits exist 
  for i in range(2):
    clr=ord(colours[i*2])*256+ord(colours[i*2+1])
    test |= clr # loading bits on 'test' 
  header="P6 640 400 7\n" # by default ST colour-format (8 levels: 0-7)
  if test & int("888",16): # checking any bits 3, 7 or 11 is set
    flag=1 # then STE colour-format (16 levels: 0-15 for RGB)
    header="P6 640 400 15\n"

  clrs=[] # initializing a two colours list
  for i in range(2): # collecting the first two colours
    clr=ord(colours[i*2])*256+ord(colours[i*2+1])
    # ----rRRRvGGGbBBB - RRR for red, GGG for green, BBB for blue
    ch=format(clr,"016b")[4:] # get rid of the first 4 bits
    red=int(ch[1:4],2) # 3bits for a value from 0 to 7
    green=int(ch[5:8],2)
    blue=int(ch[9:],2)
    if flag: # STE color: add semi-value
      red=red*2+int(ch[0]) # intercalar value for STE (levels 0-15)
      green=green*2+int(ch[4])
      blue=blue*2+int(ch[8])
    clrs+=[[red, green, blue]]

  for i in range(32000): # treating data bytes (each bit is a pixel)
    byte=ord(data[i])
    for j in range(8):
      jj=7-j; px=(byte & 2**jj)>>jj
      newdata+="%c%c%c" %(clrs[px][0], clrs[px][1], clrs[px][2])

  han=open(nom+".pnm","w")
  han.write(header+newdata)
  han.close()

  print "%s >>> %s.pnm (saved)" %(nom, nom),

  try:
    os.system("convert %s.pnm %s.png" %(nom, nom)) # Unix only! do
    os.system("rm %s.pnm" %(nom))                   # edit for other OS!
    print ">>> converted into %s.png" %(nom)
  except:
    print "- no '.png' conversion, since 'netpbm' is not installed"

Fichier compressé .PC3, haute résolution

Voici un script qui transforme un .PC3 en image PNM (il faudra introduire le nom du fichier .PC3 à transformer).

#! /usr/bin/python

nom=raw_input("nom: ")
han=open(nom)
img=han.read()
han.close()

rez=img[:2]
header=img[2:34]
data=img[34:-32]

i=0; nv=""
while i<len(data):
  octet=ord(data[i])
  i+=1
  if octet<128:
    nbr=octet+1
    for j in range(nbr):
      nv+=data[i]; i+=1
  else:
    nbr=257-octet
    nv+=data[i]*nbr; i+=1

han=open(nom+".pnm","w")
han.write("P4 640 400\n"+nv)
han.close()

3. Transformer un fichier .PI1 en .pnm

Suit le script python pour transformer les fichiers Degas PI1 (Atari) en .pnm (Portable aNy Map).

#! /usr/bin/python
import sys, os

repertoires=os.listdir(".")

# filtering ".PI1" files
for i in range(len(repertoires)-1,-1,-1): # reverse list parsing
  if repertoires[i][-4:].upper()!=".PI1": # pi1, PI1, Pi1 or pI1
    repertoires.pop(i)

repertoires=sorted(repertoires)
leng=len(repertoires)
for i in range(leng): # showing all .PI3 files of the directory
  print "%3d %s" %(i, repertoires[i])

rep=raw_input("(space separated) file number(s) to transform, * for all: ")

files=[]
if rep=="*":
  files=list(repertoires) # OK
else:
  nblist=rep.split()
  for j in nblist:
    i=int(j)
    if i>leng-1 or i<0:
      print " Sorry! wrong file number..."
      sys.exit()
    else:
      files+=[repertoires[i]]

for nom in files: # processing each file
  han=open(nom)
  img=han.read()
  han.close()

  if len(img) not in [32034, 32066, 32128]:
    print "not a .PI1 format by Degas (Elite) on AtariST(E): %d octets" %len(img)
    sys.exit()

  format0=img[0:2] # first word must be 2 for high rez pi3 Degas image 
  colours=img[2:34] # two words, first is colour0, second is colour1
  data=img[34:] # next 32000 bytes are the data

  newdata="" # initializing target data string 

  if format0!=chr(0)+chr(0): # magic number for high-rez atari-ST(E) Degas file
    print "not a .PI1 - Degas 320x200 image / 16 colours on AtariST(E)"
    if format0==chr(0)+chr(1): # magic number for med-rez atari-ST(E) Degas file
      print "seems to be a .PI2 - Degas 640x200 / image 4 colours on AtariST(E)"
    if format0==chr(0)+chr(2): # magic number for low-rez atari-ST(E) Degas file
      print "seems to be a .PI3 - Degas 640x400 / image 2 colours on AtariST(E)"
    sys.exit()

  test=0; flag=0 # trying to see if STE colour format bits exist 
  for i in range(2):
    clr=ord(colours[i*2])*256+ord(colours[i*2+1])
    test |= clr # loading bits on 'test' 
  header="P6 320 200 7\n" # by default ST colour-format (8 levels: 0-7)
  if test & int("888",16): # checking any bits 3, 7 or 11 is set
    flag=1 # then STE colour-format (16 levels: 0-15 for RGB)
    header="P6 320 200 15\n"

  clrs=[] # initializing a two colours list
  for i in range(16):
    clr=ord(colours[i*2])*256+ord(colours[i*2+1])
    # ----rRRRvGGGbBBB - RRR for red, GGG for green, BBB for blue
    ch=format(clr,"016b")[4:] # get rid of the first 4 bits
    red=int(ch[1:4],2) # 3bits for a value from 0 to 7
    green=int(ch[5:8],2)
    blue=int(ch[9:],2)
    if flag: # STE color: add semi-value
      red=red*2+int(ch[0]) # intercalar value for STE (levels 0-15)
      green=green*2+int(ch[4])
      blue=blue*2+int(ch[8])
    clrs+=[[red, green, blue]]

  for i in range(4000):
    ofs=i*8
    sh0=ord(data[ofs])*256+ord(data[ofs+1])
    sh1=ord(data[ofs+2])*256+ord(data[ofs+3])
    sh2=ord(data[ofs+4])*256+ord(data[ofs+5])
    sh3=ord(data[ofs+6])*256+ord(data[ofs+7])

    for j in range(16):
      jj=15-j
      px=((sh0 & 2**jj)/2**jj)+((sh1 & 2**jj)/2**jj)*2+((sh2 & 2**jj)/2**jj)*4+((sh3 & 2**jj)/2**jj)*8
      newdata+="%c%c%c"%(clrs[px][0], clrs[px][1], clrs[px][2])

  han=open(nom+".pnm","w")
  han.write(header+newdata)
  han.close()

  print "%s >>> %s.pnm (saved)" %(nom, nom),

  try:
    os.system("convert %s.pnm %s.png" %(nom, nom)) # Unix only! do
    os.system("rm %s.pnm" %(nom))                   # edit for other OS!
    print ">>> converted into %s.png" %(nom)
  except:
    print "- no '.png' conversion, since 'ImageMagick' is not installed or other OS than Unix"

Fichier compressé .PC1, basse résolution

Voici un script qui transforme un .PC1 en image PNM (il faudra introduire le nom du fichier .PC1 à transformer). Attention, quatre plans successifs de 40 octets doivent être produits avant de combiner les bits de chaque plan pour en faire des pixels.

#! /usr/bin/python

nom=raw_input("nom: ")
han=open(nom)
img=han.read()
han.close()

colours=img[2:34]; clrs=[]; test=0; flag=0
for i in range(16):
  clr=ord(colours[i*2])*256+ord(colours[i*2+1])
  clrs+=[clr]
  test |= clr # loading bits on 'test' 
if test & int("888",16): # checking any bits 3, 7 or 11 is set
  flag=1 # then STE colour-format (16 levels: 0-15 for RGB)

for i in range(len(clrs)):
  # ----rRRRgGGGbBBB - RRR for red, GGG for green, BBB for blue; r, g, b for STE semi-values
  ch=format(clrs[i],"016b")[4:] # get rid of the first 4 bits
  red=int(ch[1:4],2) # 3bits for a value from 0 to 7
  green=int(ch[5:8],2)
  blue=int(ch[9:],2)
  if flag: # STE color: add semi-value
    red=red*2+int(ch[0]) # intercalar value for STE (levels 0-15)
    green=green*2+int(ch[4])
    blue=blue*2+int(ch[8])
  clrs[i]=[red, green, blue]
print clrs
data=img[34:-32]

i=0; nv=""; plan=0; ligne=["","","",""]
while i<len(data):
  octet=ord(data[i]); i+=1
  if octet<128:
    for j in range(octet+1):
      ligne[plan]+=data[i]; i+=1
  else:
    nbr=257-octet
    ligne[plan]+=data[i]*nbr; i+=1
  if len(ligne[plan])>=39:
    if plan==3:
      for j in range(40):
        oct0=ord(ligne[0][j])
        oct1=ord(ligne[1][j])
        oct2=ord(ligne[2][j])
        oct3=ord(ligne[3][j])
        for k in range(7,-1,-1):
          p=2**k
          qu=((oct0 & p) +(oct1 & p)*2+(oct2 & p)*4+(oct3 & p)*8)>>k
          if qu:
            print qu
          nv=nv+chr(clrs[qu][0])+chr(clrs[qu][1])+chr(clrs[qu][2])
          if qu>0:
            clrs[qu]
      ligne=["","","",""]
    plan=(plan+1)%4

maxc=(1+flag)*7+flag # 15 if flag==1, 7 otherwise

han=open(nom+".pnm","w")
han.write("P6 320 200 %d\n" %(maxc) +nv)
han.close()

4. .PI2 (med rez) vers .pnm

Le script qui suit prend finalement en compte les trois formats pi1, pi2 et pi3, en sacrifiant un peu la lisibilité du script. Les scripts pour les fichiers compressés .PC1 et .PC3 n'ont pas (encore?) été intégrés; le script pour les fichiers .PC2 devrait pouvoir être facilement produit à partir de celui concernant les .PC1 (deux lignes de 80 octets par ligne de pixels).

#! /usr/bin/python
import sys, os

repertoires=os.listdir(".")

# filtering ".PI1", ".PI2" or ".PI3" files
for i in range(len(repertoires)-1,-1,-1): # reverse list parsing
  if repertoires[i][-4:].upper() not in (".PI1", ".PI2", ".PI3"):
    repertoires.pop(i) # get rid if not p|P, i|I, 1|2|3

repertoires=sorted(repertoires)
leng=len(repertoires)
for i in range(leng): # showing all .PI* files of the directory
  print "%3d %s" %(i, repertoires[i])

rep=raw_input("(space separated) file number(s) to transform, * for all: ")
print

files=[]
if rep=="*":
  files=list(repertoires) # OK
else:
  nblist=rep.split()
  for j in nblist:
    i=int(j)
    if i>leng-1 or i<0:
      print " Sorry! wrong file number..."
      sys.exit()
    else:
      files+=[repertoires[i]]

cpt=0
for nom in files: # processing each file
  han=open(nom)
  img=han.read()
  han.close()

  resolution=img[0:2] # Degas image resolution (not compressed)
  rez=ord(resolution[0])*256+ord(resolution[1])
  ext=nom.rsplit(".",1)[1].upper()

  if len(img) not in [32034, 32066, 32128]:
    print "<%s> is not a Degas file (non compressed) on AtariST(E): %d octets" %(nom, len(img))
    sys.exit()

  extensions=("PI1", "PI2", "PI3")
  if ext!=extensions[rez]:
    print "\n  <%s> file has a %s extension but a %s signature! (first file 'word' is %d)" %(nom, ext, extensions[rez], rez)
    if rez==0 or ext=="PI1":
      print "  0 or <.PI1> means a Degas 320x200 image / 16 colours on AtariST(E)"
    if rez==1 or ext=="PI2": # magic number for med-rez atari-ST(E) Degas file
      print "  1 or <.PI2> means a Degas 640x200 image / 4 colours on AtariST(E)"
    if rez==2 or ext=="PI3": # magic number for low-rez atari-ST(E) Degas file
      print "  2 or <.PI3> means a Degas 640x400 image / 2 colours on AtariST(E)"
    print "  <%s> not processed\n" %(nom)
    continue

  colours=img[2:34] # two words, first is colour0, second is colour1
  clrs=[] # initializing colours list
  test=0; flag=0 # do STE colour extra bits exist?
  nbrclr=2**(2**(2-rez)) # rez 0 -> 16 clrs ; 1 -> 4 ; 2 -> 2

  for i in range(nbrclr):
    clr=ord(colours[i*2])*256+ord(colours[i*2+1])
    clrs+=[clr]
    test |= clr # loading bits on 'test' 
  if test & int("888",16): # checking any bits 3, 7 or 11 is set
    flag=1 # then STE colour-format (16 levels: 0-15 for RGB)
  # width and height according to rez ; 7 or 15 according to flag (0 or 1):
  header="P6 %d %d %d\n" %((320,640,640)[rez], (200,400,400)[rez], 7*(1+flag)+flag)

  for i in range(len(clrs)):
    # ----rRRRgGGGbBBB - RRR for red, GGG for green, BBB for blue; r, g, b for STE semi-values
    ch=format(clrs[i],"016b")[4:] # get rid of the first 4 bits
    red=int(ch[1:4],2) # 3bits for a value from 0 to 7
    green=int(ch[5:8],2)
    blue=int(ch[9:],2)
    if flag: # STE color: add semi-value
      red=red*2+int(ch[0]) # intercalar value for STE (levels 0-15)
      green=green*2+int(ch[4])
      blue=blue*2+int(ch[8])
    clrs[i]=[red, green, blue]

  data=img[34:] # next 32000 bytes are the data
  plans=2**(2-rez) # low rez 0 -> 4 ; med 1 -> 2 ; high 2 -> 1
  inter=""; newdata="" # initializing target data string 
  for i in range(0,32000,plans*2): # low: pace is 8 bytes ; med: 4 ; high: 2
    sh=[]
    for j in range(plans):
      sh+=[ord(data[i+j*2])*256+ord(data[i+j*2+1])]
    for k in range(15,-1,-1): # from 15 to 0 (right bit is 0th)
      px=0
      for j in range(len(sh)):
        px+=(sh[j] & 2**k)/(2**k)*2**j
      inter+="%c%c%c"%(clrs[px][0], clrs[px][1], clrs[px][2])
    if (i+plans*2)%160==0:
       if rez==1: inter*=2 # 640x200 must be converted into 640x400 (Atari ST med rez double height px)
       newdata+=inter; inter=""

  han=open(nom+".pnm","w")
  han.write(header+newdata)
  han.close()
  print "%s >>> %s.pnm (saved)" %((nom,)*2)