DĂ©mo d'analyse de code Second Reality


Le 23 juillet 2013, le code source de la démo Second Reality (1993) a été publié . Comme beaucoup, j'avais hâte de voir l'intérieur de la démo qui nous a tant inspiré au fil des ans.

Je m'attendais à voir le chaos monolithique de l'assembleur, mais au lieu de cela, à ma grande surprise, j'ai découvert une architecture complexe qui combine gracieusement plusieurs langages. Je n'ai jamais vu un tel code auparavant, représentant parfaitement deux aspects intégraux du développement d'une démo:

  • Travail en Ă©quipe.
  • Obfuscation.

Comme d'habitude, j'ai rédigé un article pour mes notes: j'espère que cela fera gagner quelques heures à quelqu'un et incitera peut-être les autres à lire plus de code source et à devenir des ingénieurs plus expérimentés.

Partie 1: Introduction


DĂ©mo


Avant de me lancer dans le code, je vais vous donner un lien pour capturer la démo légendaire en vidéo HD (Michael Hut). Aujourd'hui, c'est le seul moyen d'évaluer pleinement la démo sans problèmes graphiques (même DOSBox ne peut pas le lancer correctement).


Premier contact avec le code


Le code source est publié sur GitHub. Entrez simplement une commande git:

git clone git@github.com:mtuomi/SecondReality.git

Au début, le contenu est déroutant: 32 dossiers et un mystérieux U2.EXEqui ne démarre pas dans DosBox.


La démo avait le titre de travail «Unreal 2» (le premier «Unreal» était la précédente démo Future Crew, sortie pour la première Assemblée en 1992). Et ce n'est qu'au cours du processus de développement que le nom a été changé pour «Second Reality». Cela explique le nom du fichier "U2.EXE", mais pas pourquoi le fichier ne fonctionne pas ...

Si vous exécutez CLOC , nous obtiendrons des métriques intéressantes:

    -------------------------------------------------------------------------------
                                                   
    -------------------------------------------------------------------------------
    Assembly                        99           3029           1947          33350
    C++                            121           1977            915          24551
    C/C++ Header                     8             86            240            654
    make                            17            159             25            294
    DOS Batch                       71              3              1            253
    -------------------------------------------------------------------------------
    SUM:                           316           5254           3128          59102
    -------------------------------------------------------------------------------

  • «» 50% .
  • Doom.
  • Il a dix-sept makefiles. Pourquoi pas un seul?

Lancer la démo


C'est difficile à comprendre, mais la démo publiée peut être lancée dans DosBox: vous devez la renommer U2.EXEet l'exécuter au bon endroit.

Quand j'ai appris le fonctionnement interne du code, il a commencé à sembler très logique:

        CD MAIN
        DÉPLACER LES DONNÉES U2.EXE / SECOND.EXE
        DONNÉES CD
        SECOND.EXE

Et le tour est joué!


Architecture


Dans les années 90, les démos étaient principalement distribuées sur disquettes. Après le déballage, il a fallu installer deux gros fichiers: SECOND.EXEet REALITY.FC:

    . <DIR> 08/08/2013 16:40
    .. <DIR> 08/01/2013 16:40
    FCINFO10 TXT 48462 10/04/1993 11:48
    FILE_ID DIZ 378 04-10-1993 11:30
    README 1ST 4.222 04-10-1993 12:59
    REALITY FC 992.188 07-10-1993 12:59 
    SECOND EXE 1,451,093 07-10-1993 13:35
        5 fichier (s) 2,496,343 octets.
        2 Dir (s) 262.111.744 octets libres.

Sur la base de mon expérience dans le développement de jeux, je m'attends toujours à ce que l'image entière ressemble à ceci:

  • SECOND.EXE: moteur avec tous les effets dans un fichier exĂ©cutable.
  • REALITY.FC: Actifs (musique, effets sonores, images) dans un format propriĂ©taire / cryptĂ© Ă  la WADDoom.

Mais après l'avoir lu, MAIN/PACK.Cj'ai trouvé que je me trompais: le moteur Second Reality n'est qu'un chargeur et un serveur d'interruption (appelé DIS). Chaque démo de scène (également appelée «PART») est un exécutable DOS entièrement fonctionnel. Chaque pièce est chargée par le chargeur Loader et lancée l'une après l'autre. Les pièces sont stockées sous forme cryptée à la fin SECOND.EXE:


  • REALITY.FC contient deux compositions musicales jouĂ©es pendant la dĂ©mo (pour le remplissage d'obfuscation ajoutĂ© remplissage et marqueur au dĂ©but).
  • SECOND.EXE contient le chargeur de dĂ©marrage et le serveur d'interruption de dĂ©monstration (DIS).
  • Après la fin SECOND.EXE, 32 parties (PART) de la dĂ©mo sont ajoutĂ©es en tant que fichiers DOS exĂ©cutables (cryptĂ©s).

Une telle architecture présente de nombreux avantages:

  • : PART , _start (450 ).
  • EXE SECOND.EXE -.
  • : Loader DIS 20 . DOS .
  • : PART PART .
  • / : , PART ( ), : EXE , .
  • Tout langage peut ĂŞtre utilisĂ© pour la programmation PART: dans le code on trouve C, Assembly ... et Pascal.

lecture recommandée


Les trois piliers pour comprendre le code source de Second Reality sont VGA, l'assembleur et l'architecture PC (programmation PIC et PIT). Voici quelques liens incroyablement utiles:


Partie 2: Deuxième moteur de réalité


Comme discuté dans la partie 1, le fondement de Second Reality consiste en:

  • Le chargeur de dĂ©marrage en tant qu'exĂ©cutable DOS.
  • Gestionnaire de mĂ©moire (pool de pile simple)
  • Serveur d'interruption de dĂ©monstration (DIS).

Dans cette partie, je donnerai des recommandations aux programmeurs qui souhaitent lire le moteur et le chargeur de démarrage (DIS sera discuté dans la partie suivante).

Code moteur


Le code moteur est 100% ASM, mais il est très bien écrit et assez bien documenté:


En pseudo-code, il peut ĂŞtre Ă©crit comme ceci:

    exemus  db 'STARTMUS.EXE',0
    exe0    db 'START.EXE',0
    ...
    exe23   db 'ENDSCRL.EXE',0

    start:
       cli                         ; Disable all interrupts
       mov     ah,4ah              ; Deallocate all memory
       call checkall               ; Check for 570,000 bytes of mem, 386 CPU and VGA
       call file_getexepath        
       call dis_setint             ; Install Demo Interrupt Server on Interrupt 0fch
       call file_initpacking       ; Check exe signature (no tempering) !
       call file_setint            ; Replace DOS routines (only OPENFILE, SEEK and READ) on Interrupt 021h
       call flushkbd               ; Flush the keyboard buffer
       
       call  checkcmdline          ; check/process commandline

       ;======== Here we go! ========
       call vmode_init             ; Init VGA (not necessarly Mode13h or ModeX), each PARTs had its own resolution

       mov si,OFFSET exe0
       call executehigh            ; loaded to high in memory. Used for loading music loaders and stuff.
   
       call  _zinit ; Start music
       call  restartmus

       mov   si,OFFSET exe1     ;Parameter for partexecute: Offset to exec name
       call  partexecute
       ; Execute all parts until exe23

       call fademusic
       ;======== And Done! (or fatal exit) ========

    fatalexit:
       mov cs:notextmode,0
       call vmode_deinit

Toutes les Ă©tapes sont assez faciles Ă  lire:

  1. DĂ©finissez le serveur d'interruption DIS comme interruption 0fch.
  2. Remplacement des appels système DOS par interruption 021h(pour plus de détails, voir la section "Modes Dev et Prod" ).
  3. Téléchargez de la musique sur une carte son via la mémoire EMS.
  4. Exécution de musique.
  5. Exécution de chaque partie de la démo.
  6. Terminé!

Détails des procédures execute:

 execute:
      cld
      call  openfile ; Open the DOS executable for this PART
      call  loadexe  ; loads the specified exe file to memory, does relocations and creates psp
      call  closefile
      call  runexe   ;runs the exe file loaded previously with loadexe.
                     ; returns after exe executed, and frees the memory
                     ; it uses.

Gestionnaire de mémoire


Il y avait de nombreuses légendes selon lesquelles Second Reality utilise un gestionnaire de mémoire complexe via MMU; il n'y avait aucune trace dans le moteur. La gestion de la mémoire est en fait transférée vers DOS: le moteur commence par libérer toute la RAM puis la distribue sur demande . La seule astuce délicate est la capacité d'allouer de la RAM à partir de la fin du tas: cela se fait en utilisant la valeur de retour malloc DOS quand trop de RAM est demandée .

Partie 3: DIS


Demo Interrupt Server (DIS) fournit une large gamme de services pour chaque PART: de l'échange de données entre différents PART à la synchronisation avec VGA.

Services DIS


Au moment de l'exécution, PART, le serveur DIS lui fournit des services. Une liste des fonctionnalités peut être consultée sur DIS/DIS.H.

Les services les plus importants:

  • Échange entre diffĂ©rents PART ( dis_msgarea): DIS fournit trois tampons de 64 octets chacun afin que PART puisse recevoir les paramètres du chargeur du PART prĂ©cĂ©dent.
  • Emulation Copper ( dis_setcopper): Simulateur Amiga Copper qui vous permet d'effectuer des opĂ©rations qui sont commutĂ©es par l'Ă©tat de VGA.
  • Mode Dev / Prod ( dis_indemo): permet au PART de savoir qu'il s'exĂ©cute en mode DEV (ce qui signifie qu'il doit initialiser la vidĂ©o) ou lancĂ© Ă  partir du chargeur de dĂ©marrage en mode PROD.
  • Nombre de trames VGA ( _dis_getmframe)
  • En attente du backstop VGA ( dis_waitb).

Code du serveur d'interruption de démonstration


Le code source DIS est également 100% ASM ... et assez bien commenté:

  • DIS/DIS.ASM(gestionnaire d'interruption dĂ©fini sur int 0fch).
  • DIS/DISINT.ASM (ProcĂ©dures DIS elles-mĂŞmes).
  • Puisque Second Reality est Ă©galement partiellement Ă©crit en C, le code a une interface pour C: DIS/DIS.Het DIS/DISC.ASM.

Comment ça fonctionne


DIS est défini comme un gestionnaire d'interruption pour int programmatique 0fch. La grande chose à ce sujet est qu'il peut fonctionner en interne SECOND.EXElorsque la démo est en cours d'exécution, ou en tant que programme résident ( TSR ) en mode Dev. Cette flexibilité vous permet de tester individuellement différentes démos de PART pendant le développement:

                          // Imaginons que nous sommes un développeur FC et que nous voulons démarrer directement la partie STAR.
  C: \> CD DDSTARS            
  C: \ DDSTARS> K

  ERREUR: DIS non chargé. 

                          // Oups, le PART n'a pas pu trouver le DIS Ă  int 0fch.
  C: \ DDSTARS> CD .. \ DIS
  C: \ DIS> DIS

  Demo Int Server (DIS) V1.0 Copyright (C) 1993 The Future Crew
  VERSION BETA - Compilé: 26/07/93 03:15:53 
  Installé (int fc).
  REMARQUE: ce serveur DIS ne prend pas en charge la synchronisation cuivre ou musique!
                          // DIS est installé, réessayons.

  C: \ DIS> CD ../DDSTARS
  C: \ DDSTARS> K

Et le tour est joué!


Cuivre



"Copper" est le coprocesseur que les développeurs de la démo pour Amiga ont adoré. Il faisait partie du jeu de puces d'origine et vous permettait d'exécuter un flux programmable de commandes synchronisées avec l'équipement vidéo. Il n'y avait pas un tel coprocesseur sur le PC et Future Crew a dû écrire un simulateur de cuivre fonctionnant à l'intérieur de DIS.

L'équipe FC a utilisé les chipsets matériels PC 8254-PIT et 8259-PIC pour simuler le cuivre. Elle a créé un système synchronisé avec la fréquence VGA , capable de démarrer des procédures à trois endroits du faisceau vertical arrière :

  • Place 0: après avoir allumĂ© l'Ă©cran (environ sur la ligne de balayage 25)
  • Lieu 1: immĂ©diatement après le balayage du faisceau inverse (IL EST POSSIBLE D'ÉVITER CE POSSIBLE)
  • Place 2: dans le faisceau inverse du faisceau de balayage

Comment cela se fait peut ĂŞtre lu MAIN/COPPER.ASM(et voir dans le diagramme ci-dessous):

  1. La minuterie de la puce 8254 est configurée pour déclencher IRQ0 avec la fréquence souhaitée.
  2. Le gestionnaire d'interruption de 8h (qui est appelé par le PIC 8259 après avoir reçu IRQ0) est remplacé par une procédure ici intti8.

Remarque: le service de comptage de trames DIS est en fait fourni par le simulateur de cuivre.

Partie 4: Modes de développement et de production


En lisant le code source de Second Reality, vous êtes le plus frappé par l'attention portée par l'équipe au passage transparent de DEV à PROD.

Mode de développement



En mode Développement, chaque composant de la démo était un fichier exécutable distinct.

  • Le DIS a Ă©tĂ© chargĂ© dans le TSR rĂ©sident et accessible via une interruption 0cfh.
  • Le chargeur de dĂ©marrage a provoquĂ© une interruption DOS 21hpour ouvrir, lire, rechercher et fermer des fichiers.

Cette configuration DEV présente les avantages suivants:

  • Chaque codeur et artiste peut travailler sur le fichier exĂ©cutable et le tester sĂ©parĂ©ment, sans affecter le reste de l'Ă©quipe.
  • La dĂ©mo complète Ă  tout moment peut ĂŞtre testĂ©e Ă  l'aide d'une petite SECOND.EXE(sans ajouter tous les EXE Ă  la fin). L'exĂ©cutable de chaque PART a Ă©tĂ© chargĂ© Ă  l'aide d'une interruption DOS Ă  021hpartir d'un fichier sĂ©parĂ©.

Production (mode démo)



En mode Production, le petit SECOND.EXE(contenant le chargeur de démarrage), le DIS et certaines parties de la démo en tant qu'EXE séparés ont été combinés en un seul épais SECOND.EXE.

  • L'accès au DIS se faisait toujours par interruption 0fch.
  • L'API d'interruption DOS 21h a Ă©tĂ© corrigĂ©e par ses propres routines Future Crew, qui ouvrent les fichiers Ă  la fin d'un fichier volumineux SECOND.EXE.

Cette configuration PROD présente un avantage en termes de temps de chargement et de protection contre l'ingénierie inverse ... mais surtout, du point de vue de la programmation ou du chargement de PART, RIEN ne change lors du passage de DEV à PROD.

Partie 5: PARTIE distincte


Chacun des effets visuels de Second Reality est un exécutable DOS entièrement fonctionnel. Ils sont appelés PART et tous 23. Une telle solution architecturale a permis un prototypage rapide, un développement parallèle (car FC n'avait probablement pas d'outils de contrôle de version) et le libre choix des langues (ASM, C et même Pascal se trouvent dans la source).

PARTIE séparée


Une liste de tous les PART / EXE se trouve dans le code source du moteur: U2.ASM . Voici une brève description plus pratique des 23 parties (avec l'emplacement du code source, bien que les noms puissent être très déroutants):

TitreFichier exécutableCodeurCapture d'écranLa source
STARTMUS.EXEMAIN / STARTMUS.C
START.EXEINCENDIESSTART / MAIN.c
Partie cachéeDDSTARS.EXEINCENDIESDDSTARS / STARS.ASM
Alkutekstit iALKU.EXEINCENDIESALKU / MAIN.C
Alkutekstit IIU2A.EXEPSIVISU / C / CPLAY.C
Alkutekstit IIIPAM.EXETRUG / WILDFIREPAM /
BEGLOGO.EXEBEG / BEG.C
GlenzGLENZ.EXEPSIGLENZ /
DottitunneliTUNNELI.EXETRUGTUNNELI / TUN10.PAS
TechnoTECHNO.EXEPSITECHNO / KOEA.ASM
PanicfakePANICEND.EXEPSIPanique
Vuori-scrollMNTSCRL.EXEFOREST / READ2.PAS
Étoiles de rêve du désertDDSTARS.EXETRUG
LentillePSI
RotazoomerLNS & ZOOM.EXEPSIOBJECTIF /
PlasmaINCENDIES
PlasmacubePLZPART.EXEINCENDIESPLZPART /
MiniVectorBallsMINVBALL.EXEPSIDOTS /
PeilipalloscrollRAYSCRL.EXETRUGEAU / DEMO.PAS
3D sinusfield3DSINFLD.EXEPSICOMAN / DOLOOP.C
JellypicJPLOGO.EXEPSIJPLOGO / JP.C
Vector partie II 'U2E.EXEPSIVISU / C / CPLAY.C
LĂ©gendes / Remerciements
ENDLOGO.EXEEND / END.C
CRED.EXEINCENDIESCRÉDITS / MAIN.C
ENDSCRL.EXEENDSCRL / MAIN.C

Il semble que chaque développeur ait sa propre spécialisation, qui pourrait être partagée en une seule partie. Cela est particulièrement visible dans la première scène avec défilement, navires et explosions (Alkutekstit). Bien que cela ressemble à un effet continu, il s'agit en fait de trois fichiers exécutables écrits par trois personnes différentes:

Séquence Alkutekstit (Crédits)
ALKU par WILDFIREU2A par PSIPAM par TRUG / WILDFIRE

Les atouts


Les ressources d'image ( .LBM) sont générées à l'aide de Deluxe Paint , un éditeur de bitmap extrêmement populaire dans les années 90. Fait intéressant, ils sont convertis en un tableau d'octets et compilés dans PART. À la suite de cela, le fichier exe télécharge également tous les actifs. De plus, cela complique l'ingénierie inverse.

Parmi les ensembles d'actifs sympas figurent les célèbres CITY et SHIP de la dernière scène 3D:



Unité interne PART


Comme ils ont tous été compilés en exécutables DOS, dans PART, n'importe quel langage peut être utilisé:


En ce qui concerne l'utilisation de la mémoire, j'ai lu beaucoup de choses sur MMU sur Wikipedia et d'autres sites Web ... mais en fait, chaque partie pouvait utiliser n'importe quoi, car après l'exécution, elle était complètement déchargée de la mémoire.

Lorsque vous travaillez avec VGA, chaque partie utilise son propre ensemble de trucs et travaille dans sa résolution. Dans chacun d'eux, ni le Mode 13h ni le ModeX n'ont été utilisés, mais plutôt un mode 13h modifié avec sa propre résolution. Le fichier SCRIPT mentionne souvent 320x200 et 320x400.

Malheureusement, lors de l'analyse de PART, la lecture du code source devient une tâche intimidante: la qualité du code et des commentaires diminue considérablement. Peut-être que cela s'est produit à cause de la ruée ou parce que chaque PARTIE a travaillé sur son propre développeur (c'est-à-dire qu'il n'y avait pas de "vrai" besoin de commentaires ou de compréhensibilité du code), mais le résultat était quelque chose de complètement déroutant:


Des algorithmes sophistiqués ne sont pas seulement difficiles à comprendre même les noms des variables ( a, b, co[]...). Le code serait beaucoup plus lisible si les développeurs nous laissaient des indices dans les notes de version. En conséquence, je n'ai pas consacré beaucoup de temps à l'étude de chaque partie; L'exception était le moteur 3D responsable de U2A.EXE et U2E.EXE.

Moteur 3D Second Reality




Quoi qu'il en soit, j'ai décidé d'étudier en détail le moteur 3D, qui était utilisé en deux parties: U2A.EXEet U2E.EXE.

Le code source est C avec des procédures optimisées par l'assembleur (en particulier le remplissage et l'ombrage Gouro):

  • CITY.C (code principal).
  • VISU.C (bibliothèque visu.lib).
  • AVID.ASM (vidĂ©o d'assemblage optimisĂ©e (nettoyage, copie d'Ă©cran, etc.)).
  • ADRAW.ASM (dessiner des objets et tronquer).
  • ACALC.ASM (matrices et calculs rapides sin / cos).


L'architecture de ces composants est tout à fait remarquable: la bibliothèque VISUeffectue toutes les tâches complexes, par exemple le chargement des actifs: objets 3DS, matériaux et flux (mouvements de caméras et de navires).

Le moteur trie les objets à dessiner et les rend à l'aide de l'algorithme de l'artiste. Cela conduit à une grande quantité de redessin, mais comme les verrous VGA vous permettent d'enregistrer 4 pixels en même temps, ce n'est pas si mal.

Un fait intéressant: le moteur effectue des transformations à l'ancienne: au lieu d'utiliser des matrices 4x4 homogènes communes, il utilise des matrices de rotation 3 * 3 et un vecteur de déplacement.

Voici un résumé en pseudo code:

      main(){

            scenem=readfile(tmpname);  // Load materials
            scene0=readfile(tmpname);  // Load animation

            for(f=-1,c=1;c<d;c++){  //Load objects
              sprintf(tmpname,"%s.%03i",scene,e);
              co[c].o=vis_loadobject(tmpname);
            }

            vid_init(1);
            vid_setpal(cp);

            for(;;){

                vid_switch();
                _asm mov bx,1   _asm int 0fch // waitb for retrace via copper simulator interrupt call 
                vid_clear();
                
                // parse animation stream, update objects
                for(;;){}

                vid_cameraangle(fov); // Field of vision

                // Calc matrices and add to order list (only enabled objects)
                for(a=1;ac<conum;a++) if(co[a].on) /* start at 1 to skip camera */
                    calc_applyrmatrix(o->r,&cam);

                // Zsort via Bubble Sort
                for(a=0;ac<ordernum;a++)
                    for(b=a-1;b>=0 && dis>co[order[b]].dist;b--)

                // Draw
                for(a=0;ac<ordernum;a++)
                    vis_drawobject(o);
              }
            }
            return(0);
     }

Ports pour systèmes modernes


Après la publication de cet article, de nombreux développeurs ont commencé à porter Second Reality sur des systèmes modernes. Claudio Matsuoka a décidé de créer sr-port , un port C pour Linux et OpenGL ES 2.0, qui jusqu'à présent semble assez impressionnant. Nick Kovacs a fait un excellent travail sur PART PLZ, le portant sur C (maintenant il fait partie du code source sr-port), ainsi que sur javascript :


All Articles