Table des matières
Planet X3 - Format of "savegame.dat"
Ce fichier est un “dump mémoire” du jeu, il n'est pas compressé ni crypté; on y retrouve la meme structure que dans le code source.
Le fichier est composé de 3 “blocs” de données:
- 1 block de 32 Kb (0x8000 bytes) avec la carte en cours
- 1 block de 2560 bytes (0x1010 bytes) contenant les unités/batiments des 2 factions
- 1 block de 5 bytes (0x0005 bytes) contenant principalement les timers du jeu.
une sauvegarde fait donc TOUJOURS 36885 bytes
Function de sauvegarde/chargement
Dans le code source, la sauvegarde se fait uniquement dans le fichier SAVEGAME.DAT
.
Game_WriteSaveGame: MOV AH,0x3c ; bug no mod selected : random ? MOV CX,0x0 MOV DX,fname_SaveGame_DAT ; = "SAVEGAME.DAT" MOV AH,0x3c INT 0x21 ; Create/truncate file JC exitError ; exit function without handling MOV [fhandler_SaveGame],AX ; preserve file handler JMP continue exitError: RET
On découvre ici que cette fonction ne gère aucune erreur, la sauvegarde est simplement abandonnée en cas d'erreur d'ouverture de fichier.
L'usage du JMP pour sauter au dessus du RET d'erreur ressemble beaucoup à une structure de gestion d'erreurs (qui a été éliminée de la version finale) propre aux processeurs sans caches (8088, 8086, 6502, etc).
Personnellement, je n'aurais pas rompu le flux avec un JMP j'aurais placé la gestion (ou non) des erreurs à la fin de la fonction, et réservé le saut (rupture de flux) aux erreurs, afin de ne pas forcer un “flush cache” sur des processeurs plus puissant (x386 et suivant).
continue: MOV BX,word ptr [fhandler_SaveGame] ; fhandler ; save current game map MOV DX,0x0 MOV CX,0x8000 ; 0x8000 = 256*128 MOV AH,0x40 PUSH DS ; switch to map's Segment MOV DS,word ptr [g_Seg_GameMap] INT 0x21 ; write map (32kb) POP DS ; restore DS
la carte du jeu est sauvegardée “tel quel” sans aucune encryption ni compression.
; save unit/building records MOV BX,word ptr [fhandler_SaveGame] MOV DX,Game_UnitsArr_Type MOV CX,0x1010 ; Write 256 units/buildings (10 attributes) MOV AH,0x40 INT 0x21
Pareil pour la structure des unités/batiments, sauvegardée “tel quel” sans aucune encryption ni compression.
; save Ingame timers MOV BX,word ptr [fhandler_SaveGame] MOV DX,Timer_InGame_Counter MOV CX,0x5 ; 1 word (total seconds) + 3 bytes (Hours:minutes:seconds) MOV AH,0x40 INT 0x21
Avant de fermer le fichier, Planet X3 sauvegarde le compteur de temps générale (1 mot) et le temps “en jeu” 3 bytes
MOV AH,0x3e ; close file MOV BX,word ptr [fhandler_SaveGame] INT 0x21 RET
Block Map
Block d'unités/Batiments
Offset | Size | Description |
---|---|---|
+0x0000 | 0x0100 | Type d'Unités/batiments |
+0x0100 | 0x0100 | Position X |
+0x0200 | 0x0100 | Position Y |
+0x0300 | 0x0100 | Target X |
+0x0400 | 0x0100 | Target Y |
+0x0500 | 0x0100 | OOT Type |
+0x0600 | 0x0100 | Unknow |
+0x0700 | 0x0100 | Points de Vie |
+0x0800 | 0x0100 | Unknow |
+0x0900 | 0x0100 | Generic Parameter A |
+0x0A00 | 0x0100 | Generic Parameter B |
+0x0B00 | 0x0100 | Generic Parameter C |
+0x0C00 | 0x0100 | Generic Parameter D |
+0x0D00 | 0x0100 | AL |
+0x0E00 | 0x0100 | Unknow |
+0x0F00 | 0x0100 | Etat du Silo Missile |
+0x1000 | 0x0010 | Bloc de configuration |
Pour n'importe quel sous-block de 0x100 bytes est divisé en 2 sous sections, 1 pour les humains (offset 0x0000), 1 pour l'ordinateur (offset 0x0080).
Offset | Taille | Description |
---|---|---|
+0x0000 | 0x14 | Unités mobiles Humaines |
+0x0014 | 0x6C | Batiments Humains |
+0x0080 | 0x14 | Unités mobiles Ordinateur |
+0x0094 | 0x6C | Batiments Ordinateur |
Suivant le code, le nombre maximum d'unités mobiles humaine diffère.
- Dans 2 fonctions, PX3 vérifie le status (offset 0x0000) pour les indices de 0 à 20 (0x00-0x14)
- Dans 1 fonction, PX3 vérifie le status (offset 0x0000) pour les indices de 20 à 50 (0x14-0x32)
- Dans 5 fonctions, PX3 vérifie le status (offset 0x0000) pour les indices de 0 à 64 (0x00-0x40)
- Dans 2 fonctions, PX3 vérifie le status (offset 0x0000) pour les indices de 20 à 64 (0x14-0x40)
meme si le code présente bien des limites pour le joueur, je n'ai pas trouvé trace de limite similaire pour l'ordinateur (logiquement placée aux offset 0x80, 0x94, …)
Block de configuration
offset | byte | Description |
---|---|---|
+0 | 1 | Décors de la carte ('1','2','3') |
+1 | 1 | Unité sélectionnée |
+2 | 1 | Réserve de minerais |
+3 | 1 | Réserve de Gaz |
+4 | 1 | Réserve d'électricité |
+5 | 0xB | inconnu |
Block "Timer"
offset | byte | description |
---|---|---|
+0 | 2 | temps global en jeu (en ms) |
+2 | 1 | Temps : Secondes |
+3 | 1 | Temps : minutes |
+4 | 1 | Temps : heures |
Information détaillée
Offset | Taille | Taille en Bytes | Joueur/ Ordinateur | Type | Description |
---|---|---|---|---|---|
0000 | 8000 | 32768 | 256×128 tableau de case (carte de jeu) 0x0000 = coin haut-gauche | ||
8000 | 14 | 20 | Joueur | Unité | type |
8014 | 6C | 108 | Joueur | Batiment | type |
8080 | 80 | 128 | Ordinateur | mixte | type |
8100 | 14 | 20 | Joueur | Unité | position X |
8114 | 6C | 108 | Joueur | Batiment | position X |
8180 | 80 | 128 | Ordinateur | mixte | position X |
8200 | 14 | 20 | Joueur | Unité | position Y |
8214 | 6C | 108 | Joueur | Batiment | position Y |
8280 | 480 | 1152 | Ordinateur | mixte | position Y |
8700 | 14 | 20 | Joueur | Unité | Vie |
8714 | 6C | 108 | Joueur | Batiment | Vie |
8780 | 190 | 400 | Ordinateur | Batiment | Vie |
8910 | 6F0 | 1776 | Joueur | Missile | Silot Vide/Plein |
9000 |
Type de Carte/Décors
Type d'Unité mobiles
Type de Batiments
Valeur | Proriétaire | Description |
---|---|---|
0x14 | Joueur | Headquarters |
0x15 | Joueur | Radar |
0x16 | Joueur | Power Station |
0x17 | Joueur | Solar Panel |
0x19 | Joueur | factory |
0x1A | Joueur | Missile Silo |
0x1B | Joueur | Smelter |
Joueur | Refenery | |
0x21 | Ennemi | Headquarters (Pyramids) |
Ennemi | Academy | |
Ennemi | Clone Facility | |
Ennemi | Research Building | |
Ennemi | Factory | |
Ennemi | Sentry Pod |