
Vous souvenez-vous de nombreux jeux russes? Qualitatif? Mémorable? Oui, ils l'étaient. Si vous avez plus de 35 ans ou que vous êtes un fan de l'industrie du jeu russe, vous connaissez probablement les terres maudites.
L'histoire a commencé de façon très prosaïque: l'été, la chaleur. Il n'y a rien de spécial à faire, et lorsque je parcourais paresseusement le contenu du disque dur de l'ordinateur portable, mon regard attrapa un dossier avec l'icône de dragon familière qui était inactive depuis quelques années.
Quel fan du jeu ne sera pas intéressé de savoir ce qu'il y a à l'intérieur?
Présentation
Terres maudites - ou, comme on les appelait en dehors de la CEI, Evil Islands: Curse of the Lost Soul , un jeu de RPG furtif sorti en 2000. Le jeu a été développé par Nival Interactive, qui à l'époque s'était déjà imposé comme une série de jeux Alloda (Rage of Mages à l'étranger). La plupart du temps, des diplômés de l'Université d'État de Moscou y travaillaient - ils étaient tout à fait capables de réaliser l'un des premiers jeux avec un monde complètement en trois dimensions.
En 2010, Mail.Ru ( information ) a transféré le titre, mais le jeu est toujours en vente sur la boutique GOG pour le compte de Nival.
Relativement récemment, le jeu a eu 18 ans - l'anniversaire est considéré comme le 26 octobre, date de sortie dans la CEI. Malgré l'âge, le serveur maître officiel est toujours en service: périodiquement quelqu'un décide de ramper à travers les forêts du Gipat et de frapper une dizaine ou deux squelettes avec une escouade de camarades.
En bref sur l'article
Initialement, mon objectif était uniquement d'écrire un convertisseur unidirectionnel "pour moi" en Python 3, en utilisant exclusivement des bibliothèques standard. Cependant, au cours du processus, la documentation sur les formats a commencé en douceur, tentant en quelque sorte de normaliser la sortie. Pour certains formats, la structure a été décrite à l'aide de Kaitai Struct . En conséquence, tout a abouti à la rédaction de cet article et du wiki dans des formats.
Je note tout de suite: pour la plupart, les fichiers de jeu ont déjà été recherchés, des éditeurs de fans ont été écrits pour eux. Cependant, les informations sont extrêmement fragmentées, et il n'y a pas de description plus ou moins complète des formats dans le domaine public, ni un ensemble adéquat pour créer des modifications.
... et comment le lire
Des schémas (fichiers .ksy) sont fournis pour tous les formats, qui peuvent être convertis en deux clics en code dans plusieurs des langues les plus populaires.
Malheureusement, déjà aux dernières étapes de la rédaction de cet article, j'ai trouvé que le estimé Habr n'est pas en mesure de mettre en évidence YAML (et JSON), et tous les schémas l'utilisent. Cela ne devrait pas être un gros problème, mais s'il n'est pas pratique de lire le schéma, je peux vous conseiller de le copier dans un éditeur tiers, par exemple NPP.
Ressources et où ils vivent
Le jeu est une application portable contenant un moteur avec des bibliothèques, un lanceur et, en fait, des ressources emballées.
C'est intéressant: les paramètres du jeu sont presque entièrement stockés dans le registre. Le bug de la caméra dans la version GOG est dû au fait que le programme d'installation n'enregistre pas les valeurs par défaut correctes.
À première vue sur le contenu du dossier du jeu, nous remarquons immédiatement quelques nouvelles extensions de fichiers: ASI et REG.
La première est une bibliothèque dynamique, que nous ne considérerons pas (cela est fait par des spécialistes de la rétro-ingénierie), mais la seconde est le premier format de fichier propriétaire du jeu.
REG
Les fichiers de ce type sont des sérialisations binaires de fichiers texte INI bien connus.
Le contenu est divisé en sections stockant les clés et leurs valeurs. Un fichier REG conserve cette hiérarchie, mais accélère la lecture et l'analyse des données - en 2000, cela était apparemment critique.
En général, vous pouvez décrire la structure de ce diagramme:

Description de la structuremeta: id: reg title: Evil Islands, REG file (packed INI) application: Evil Islands file-extension: reg license: MIT endian: le doc: Packed INI file seq: - id: magic contents: [0xFB, 0x3E, 0xAB, 0x45] doc: Magic bytes - id: sections_count type: u2 doc: Number of sections - id: sections_offsets type: section_offset doc: Sections offset table repeat: expr repeat-expr: sections_count types: section_offset: doc: Section position in file seq: - id: order type: s2 doc: Section order number - id: offset type: u4 doc: Global offset of section in file instances: section: pos: offset type: section types: section: doc: Section representation seq: - id: keys_count type: u2 doc: Number of keys in section - id: name_len type: u2 doc: Section name lenght - id: name type: str encoding: cp1251 size: name_len doc: Section name - id: keys type: key doc: Section's keys repeat: expr repeat-expr: keys_count types: key: doc: Named key seq: - id: order type: s2 doc: Key order in section - id: offset type: u4 doc: Key offset in section instances: key_record: pos: _parent._parent.offset + offset type: key_data key_data: seq: - id: packed_type type: u1 doc: Key value info - id: name_len type: u2 doc: Key name lenght - id: name type: str encoding: cp1251 size: name_len doc: Key name - id: value type: value doc: Key value instances: is_array: value: packed_type > 127 doc: Is this key contain array value_type: value: packed_type & 0x7F doc: Key value type types: value: doc: Key value seq: - id: array_size type: u2 if: _parent.is_array doc: Value array size - id: data type: switch-on: _parent.value_type cases: 0: s4 1: f4 2: string repeat: expr repeat-expr: '_parent.is_array ? array_size : 1' doc: Key value data string: doc: Sized string seq: - id: len type: u2 doc: String lenght - id: value type: str encoding: cp1251 size: len doc: String
C'est intéressant: en 2002, Nival a partagé quelques outils avec la communauté du jeu ( instantané du site ) - l'un d'eux était le sérialiseur INI dans REG. Comme vous pouvez le deviner, un désérialiseur est apparu presque immédiatement, mais pas officiellement.
Une fois le dossier de départ trié, passons aux sous-répertoires.
Le premier regard tombe sur le dossier Cameras contenant les fichiers CAM.
Cam
Un format très simple consiste simplement à regrouper les positions des caméras au fil du temps. La caméra est décrite par sa position et sa rotation. Les deux autres champs sont vraisemblablement le temps et le pas dans la séquence des mouvements.

Description de la structure meta: id: cam title: Evil Islands, CAM file (cameras) application: Evil Islands file-extension: cam license: MIT endian: le doc: Camera representation seq: - id: cams type: camera repeat: eos types: vec3: doc: 3d vector seq: - id: x type: f4 doc: x axis - id: y type: f4 doc: y axis - id: z type: f4 doc: z axis quat: doc: quaternion seq: - id: w type: f4 doc: w component - id: x type: f4 doc: x component - id: y type: f4 doc: y component - id: z type: f4 doc: z component camera: doc: Camera parameters seq: - id: unkn0 type: u4 doc: unknown - id: unkn1 type: u4 doc: unknown - id: position type: vec3 doc: camera's position - id: rotation type: quat doc: camera's rotation
Dans le dossier suivant - Res, (de manière inattendue!) Les fichiers RES qui sont des archives sont stockés.
RES
Ce format est parfois masqué sous d'autres extensions, mais l'original est toujours exactement RES.
La structure des données est très typique d'une archive avec un accès aléatoire aux fichiers: il existe des tables pour stocker des informations sur les fichiers à l'intérieur, une table des noms et le contenu des fichiers.
La structure du répertoire est contenue directement dans les noms.
Il convient de noter deux faits extrêmement intéressants:
- L'archive est optimisée pour charger les informations de fichier dans une liste chaînée avec hachage fermé.
- Vous pouvez stocker le contenu du fichier une fois, mais vous y référer sous des noms différents. Pour autant que je sache, ce fait a été utilisé dans un repack de fans, où la taille du jeu a été considérablement réduite à cause de cela. Dans la distribution d'origine, l'optimisation des archives n'était pas utilisée.

Description de la structure meta: id: res title: Evil Islands, RES file (resources archive) application: Evil Islands file-extension: res license: MIT endian: le doc: Resources archive seq: - id: magic contents: [0x3C, 0xE2, 0x9C, 0x01] doc: Magic bytes - id: files_count type: u4 doc: Number of files in archive - id: filetable_offset type: u4 doc: Filetable offset - id: nametable_size type: u4 doc: Size of filenames instances: nametable_offset: value: filetable_offset + 22 * files_count doc: Offset of filenames table filetable: pos: filetable_offset type: file_record repeat: expr repeat-expr: files_count doc: Files metadata table types: file_record: doc: File metadata seq: - id: next_index type: s4 doc: Next file index - id: file_size type: u4 doc: Size of file in bytes - id: file_offset type: u4 doc: File data offset - id: last_change type: u4 doc: Unix timestamp of last change time - id: name_len type: u2 doc: Lenght of filename - id: name_offset type: u4 doc: Filename offset in name array instances: name: io: _root._io pos: name_offset + _parent.nametable_offset type: str encoding: cp1251 size: name_len doc: File name data: io: _root._io pos: file_offset size: file_size doc: Content of file
C'est intéressant: dans la version russe du jeu, l'archive Speech.res contient deux sous-répertoires s et t avec un contenu complètement identique, c'est pourquoi la taille de l'archive est deux fois plus grande - c'est pourquoi le jeu ne tient pas sur un seul CD.
Vous pouvez maintenant décompresser toutes les archives (peuvent être imbriquées):
- RES n'est qu'une archive,
- MPR - paysage des niveaux de jeu,
- MQ - informations sur les tâches du multijoueur,
- ANM - un ensemble d'animations,
- MOD - modèle 3D,
- BON - l'emplacement des os du modèle.
Si les fichiers à l'intérieur de l'archive n'ont pas d'extension, nous mettrons l'extension parent - pour les archives BON et ANM.
Vous pouvez également diviser tous les fichiers reçus en quatre groupes:
- Textures
- Bases de données
- Les modèles
- Fichiers de niveau.
Commençons par le simple - avec les textures.
MMP
En fait, la texture. Il a un petit en-tête indiquant les paramètres d'image, le nombre de niveaux MIP et la compression utilisés. Après l'en-tête se trouvent les niveaux d'image MIP par ordre décroissant de taille.

Description de la structure meta: id: mmp title: Evil Islands, MMP file (texture) application: Evil Islands file-extension: mmp license: MIT endian: le doc: MIP-mapping texture seq: - id: magic contents: [0x4D, 0x4D, 0x50, 0x00] doc: Magic bytes - id: width type: u4 doc: Texture width - id: height type: u4 doc: Texture height - id: mip_levels_count type: u4 doc: Number of MIP-mapping stored levels - id: fourcc type: u4 enum: pixel_formats doc: FourCC label of pixel format - id: bits_per_pixel type: u4 doc: Number of bits per pixel - id: alpha_format type: channel_format doc: Description of alpha bits - id: red_format type: channel_format doc: Description of red bits - id: green_format type: channel_format doc: Description of green bits - id: blue_format type: channel_format doc: Description of blue bits - id: unused size: 4 doc: Empty space - id: base_texture type: switch-on: fourcc cases: 'pixel_formats::argb4': block_custom 'pixel_formats::dxt1': block_dxt1 'pixel_formats::dxt3': block_dxt3 'pixel_formats::pnt3': block_pnt3 'pixel_formats::r5g6b5': block_custom 'pixel_formats::a1r5g5b5': block_custom 'pixel_formats::argb8': block_custom _: block_custom types: block_pnt3: seq: - id: raw size: _root.bits_per_pixel block_dxt1: seq: - id: raw size: _root.width * _root.height >> 1 block_dxt3: seq: - id: raw size: _root.width * _root.height block_custom: seq: - id: lines type: line_custom repeat: expr repeat-expr: _root.height types: line_custom: seq: - id: pixels type: pixel_custom repeat: expr repeat-expr: _root.width types: pixel_custom: seq: - id: raw type: switch-on: _root.bits_per_pixel cases: 8: u1 16: u2 32: u4 instances: alpha: value: '_root.alpha_format.count == 0 ? 255 : 255 * ((raw & _root.alpha_format.mask) >> _root.alpha_format.shift) / (_root.alpha_format.mask >> _root.alpha_format.shift)' red: value: '255 * ((raw & _root.red_format.mask) >> _root.red_format.shift) / (_root.red_format.mask >> _root.red_format.shift)' green: value: '255 * ((raw & _root.green_format.mask) >> _root.green_format.shift) / (_root.green_format.mask >> _root.green_format.shift)' blue: value: '255 * ((raw & _root.blue_format.mask) >> _root.blue_format.shift) / (_root.blue_format.mask >> _root.blue_format.shift)' channel_format: doc: Description of bits for color channel seq: - id: mask type: u4 doc: Binary mask for channel bits - id: shift type: u4 doc: Binary shift for channel bits - id: count type: u4 doc: Count of channel bits enums: pixel_formats: 0x00004444: argb4 0x31545844: dxt1 0x33545844: dxt3 0x33544E50: pnt3 0x00005650: r5g6b5 0x00005551: a1r5g5b5 0x00008888: argb8
Formats d'emballage de pixels possibles:
fourcc | La description |
---|
44 44 00 00 | ARGB4 |
44 58 54 31 | Dxt1 |
44 58 54 33 | Dxt3 |
50 4E 54 33 | PNT3 - RLE compressé ARGB8 |
50 56 00 00 | R5G5B5 |
51 55 00 00 | A1R5G5B5 |
88 88 00 00 | ARGB8 |
À propos de PNT3Si le format d'image est PNT3 , alors la structure de pixels après déballage est ARGB8; bits_per_pixel
- taille de l'image compressée en octets.
Déballage du PNT3
n = 0 destination = b"" while src < size: v = int.from_bytes(source[src:src + 4], byteorder='little') src += 4 if v > 1000000 or v == 0: n += 1 else: destination += source[src - (1 + n) * 4:src - 4] destination += b"\x00" * v n = 0
C'est intéressant: certaines textures sont réfléchies verticalement (ou certaines ne le sont pas?).
Et le jeu est très jaloux de la transparence - si l'image est avec un canal alpha, la couleur des pixels transparents doit être exactement noire. Ou blanc - c'est comme ça que la chance.
Les formats simples sont terminés, passons à des formats plus rigides - à un moment donné, les rangs des fabricants de mods ont furieusement conservé leurs propres outils d'édition pour les formats suivants, et pas en vain. Je t'ai prévenu.
Bases de données (* DB et autres)
Ce format est extrêmement gênant à décrire - en substance, il s'agit d'un arbre sérialisé de nœuds (ou tables d'enregistrement). Un fichier se compose de plusieurs tables avec les types de champs spécifiés. Structure générale: les tables sont imbriquées dans un nœud "racine" commun, les enregistrements sont des nœuds à l'intérieur d'une table.
Dans chaque nœud, son type et sa taille sont spécifiés:
unsigned char type_index; unsigned char raw_size;
Le type du champ de table est pris par l'index de la chaîne de formatage de la table, le type réel est déterminé par la valeur obtenue.
Types de champsdésignation | la description |
---|
S | chaîne |
Je | 4b int |
U | 4b non signé |
F | Flotteur 4b |
X | octets bits |
f | tableau flottant |
je | tableau int |
B | bool |
b | tableau booléen |
H | octets hexadécimaux inconnus |
T | le temps |
0 | non déclaré |
1 | 0FII |
2 | SUFF |
3 | FFFF |
4 | 0SISS |
5 | 0SISS00000U |
Description des basesÉléments (.idb)
table | la structure |
---|
Matériaux | SSSIFFFIFIFfIX |
Arme | SSISIIIFFFFIFIXB00000IHFFFfHHFF |
Armure | SSISIIIFFFFIFIXB00000ffBiHH |
Articles rapides | SSISIIIFFFFIFIXB00000IIFFSbH |
Objets de quête | SSISIIIFFFFIFIXB00000Is |
Vendre des articles | SSISIIIFFFFIFIXB00000IHI |
Commutateurs (.ldb)
table | la structure |
---|
Commutateur prototype | SfIFTSSS |
Compétences et compétences (.pdb)
table | la structure |
---|
Capacités | SSI0000000s |
Compétences | SSI0000000SSIIIFFFIIIIBI |
Empreintes (prints.db)
table | la structure |
---|
Des traces de sang | 0S11 |
Traces de flammes | 0S110000001 |
Empreintes | 0S11 |
Sorts (.sdb)
table | la structure |
---|
Prototypes | SSSFIFIFFFFIIIIUSSIIbIXFFFFF |
Modificateurs | SSFIFFISX |
Patterns | 0SssSX |
Modèles d'armure | 0SssSX |
Modèles d'arme | 0SssSX |
Créatures (.udb)
table | la structure |
---|
Pièces endommagées | SffUU |
Race | SUFFUUFfFUUf222222000000000000SssFSsfUUfUUIUSBFUUUU |
Prototypes de monstres | SSIUIFFFSFFFFFFFFFUFFFFFFff33sfssSFFFFFUFUSF |
Npc | SUFFFFbbssssFUB |
Cris (acks.db)
table | la structure |
---|
Les réponses | 0S0000000044444444444444444444445444444444444 |
Cris | 0S0000000044444 |
Autre | 0S0000000044 |
Quêtes (.qdb)
table | la structure |
---|
Missions | SFIISIIs |
Briefings | SFFsSsssssI |
C'est intéressant: le 16 janvier 2002, Nival a publié les bases sources du multijoueur au format csv, ainsi qu'un utilitaire-convertisseur au format jeu ( instantané du site ). Naturellement, le convertisseur inverse n'a pas tardé à apparaître. Il existe également au moins deux documents décrivant les champs et leurs types par les modmakers, mais leur lecture est très difficile.
Adb
Base de données d'animation pour un type spécifique d'unité. Contrairement à la * DB mentionnée ci-dessus, elle est assez «humaine» - il s'agit d'une table à un niveau avec des tailles de champ statiques.

Description de la structure meta: id: adb title: Evil Islands, ADB file (animations database) application: Evil Islands file-extension: adb license: MIT endian: le doc: Animations database seq: - id: magic contents: [0x41, 0x44, 0x42, 0x00] doc: Magic bytes - id: animations_count type: u4 doc: Number of animations in base - id: unit_name type: str encoding: cp1251 size: 24 doc: Name of unit - id: min_height type: f4 doc: Minimal height of unit - id: mid_height type: f4 doc: Middle height of unit - id: max_height type: f4 doc: Maximal height of unit - id: animations type: animation doc: Array of animations repeat: expr repeat-expr: animations_count types: animation: doc: Animation's parameters seq: - id: name type: str encoding: cp1251 size: 16 doc: Animation's name - id: number type: u4 doc: Index in animations array - id: additionals type: additional doc: Packed structure with animation parameters - id: action_probability type: u4 doc: Percents of action probability - id: animation_length type: u4 doc: Lenght of animation in game ticks - id: movement_speed type: f4 doc: Movement speed - id: start_show_hide1 type: u4 - id: start_show_hide2 type: u4 - id: start_step_sound1 type: u4 - id: start_step_sound2 type: u4 - id: start_step_sound3 type: u4 - id: start_step_sound4 type: u4 - id: start_hit_frame type: u4 - id: start_special_sound type: u4 - id: spec_sound_id1 type: u4 - id: spec_sound_id2 type: u4 - id: spec_sound_id3 type: u4 - id: spec_sound_id4 type: u4 types: additional: seq: - id: packed type: u8 instances: weapons: value: 'packed & 127' allowed_states: value: '(packed >> 15) & 7' action_type: value: '(packed >> 18) & 15' action_modifyer: value: '(packed >> 22) & 255' animation_stage: value: '(packed >> 30) & 3' action_forms: value: '(packed >> 36) & 63'
C'est intéressant: pour plusieurs unités, un format de base de données partiellement tronqué est utilisé, peu exploré.
Après avoir traité les bases de données, nous déclarons une pause publicitaire. Mais nous ne publierons rien - pas notre méthode. Mieux vaut indiquer ce qui est utile ensuite - comment les fichiers de créatures sont nommés.
Le nom est collecté à partir de groupes de deux caractères - abréviations du "niveau" logique.
Par exemple, le personnage féminin sera unhufe
- Unit > Human > Female
, et initwesp
- Inventory > Item > Weapon > Spear
, c'est-à-dire une lance dans l'inventaire (pas le dos, et c'est bien).
Arborescence complète des éléments de nom: un: # unit an: # animal wi: # wild ti # tiger ba # bat bo # boar hy # hyen de # deer gi # rat ra # rat cr # crawler wo # wolf ho: # home co # cow pi # pig do # dog ho # horse ha # hare or: # orc fe # female ma # male mo: # monster co # column (menu) un # unicorn cu # Curse be # beholder tr # troll el # elemental su # succub (harpie) ba # banshee dr # driad sh # shadow li # lizard sk # skeleton sp # spider go # golem, goblin ri # Rick og # ogre zo # zombie bi # Rik's dragon cy # cyclope dg # dragon wi # willwisp mi # octopus to # toad hu: # human fe # female ma # male in: # inventory it: # item qu # quest qi # interactive ar: # armor pl # plate gl # gloves lg # leggins bt # boots sh # shirt hl # helm pt # pants li: # loot mt # material tr # trade we: # weapon hm # hammer dg # dagger sp # spear cb # crossbow sw # sword ax # axe bw # bow gm # game menu fa: # faces un: # unit an: # animal wi: # wild ti: # tiger face # face ba: # bat face # face bo: # boar face # face de: # deer face # face ra: # rat face # face cr: # crawler face # face wo: # wolf face # face ho: # home co: # cow face # face pi: # pig face # face do: # dog face # face ho: # horse face # face ha: # hare face # face hu: # human fe: # female fa # me # th # ma: # male fa # me # th # mo: # monster to: # toad face # face tr: # troll face # face or: # orc face # face sp: # spider face # face li: # lizard face # face na: # nature fl: # flora bu # bush te # termitary tr # tree li # waterplant wa # waterfall sk # sky st # stone ef: # effects cu # ar # co # components st: # static si # switch bu: # building to # tower ho # house tr # trap br # bridge ga # gate we # well (waterhole) wa: # wall me # medium li # light to # torch st # static
C'est intéressant: selon cette classification, les champignons sont des arbres, les golems avec des gobelins sont des frères et Tka-Rick est un monstre. Ici aussi, vous pouvez voir les noms "de travail" des monstres, étrangement similaires à ceux de D&D - spectateur (mauvais œil), succub (harpie), ogre (cannibale), driad (forestiers).
Après s'être reposé moralement, nous plongons tête baissée dans le modèle. Ils sont présentés dans plusieurs formats liés entre eux.
Lnk
Logiquement - la base du modèle. Décrit la hiérarchie des parties du modèle, en termes de modélisation 3D moderne - la hiérarchie des os.

Description de la structure meta: id: lnk title: Evil Islands, LNK file (bones hierarchy) application: Evil Islands file-extension: lnk license: MIT endian: le doc: Bones hierarchy seq: - id: bones_count type: u4 doc: Number of bones - id: bones_array type: bone repeat: expr repeat-expr: bones_count doc: Array of bones types: bone: doc: Bone node seq: - id: bone_name_len type: u4 doc: Length of bone's name - id: bone_name type: str encoding: cp1251 size: bone_name_len doc: Bone's name - id: parent_name_len type: u4 doc: Length of bone's parent name - id: parent_name type: str encoding: cp1251 size: parent_name_len doc: Bone's parent name
Le nom du parent de l'os parent est une chaîne vide (longueur 0).
Il y a des os, cependant, il ne suffit pas de les nommer et de les assembler - vous devez les assembler en un squelette.
Bon
Précédemment mentionné, ce format (s'il ne s'agit pas d'une archive) définit la position des pièces (os) du modèle par rapport à la pièce parente. Seul le décalage est stocké, sans rotation - une des différences par rapport aux formats modernes.

Description de la structure meta: id: bon title: Evil Islands, BON file (bone position) application: Evil Islands file-extension: bon license: MIT endian: le doc: Bone position seq: - id: position type: vec3 doc: Bone translation repeat: eos types: vec3: doc: 3d vector seq: - id: x type: f4 doc: x axis - id: y type: f4 doc: y axis - id: z type: f4 doc: z axis
Comme vous pouvez le voir, il y a trop de nombres pour un décalage - le fait est que nous avons d'abord rencontré ici l'une des principales caractéristiques du moteur de jeu - l'interpolation du modèle trilinéaire.
Comment ça marche: le modèle a trois paramètres d'interpolation - conditionnellement, force, dextérité, croissance. Il existe également 8 états extrêmes du modèle. En utilisant les paramètres, nous pouvons obtenir le modèle final par interpolation trilinéaire.
L'algorithme lui-même def trilinear(val, coefs=[0, 0, 0]):
C'est intéressant: l' interpolation du modèle trilinéaire est utilisée pour animer certains objets, par exemple, ouvrir une porte en pierre et des coffres.
Il est maintenant temps de regarder les parties du modèle elles-mêmes.
FIG
Peut-être que ce rassemblement est impossible à comprendre. Vous pouvez trouver sa description et le plug-in pour le mélangeur sur le net, mais même avec eux, la prise de conscience ne vient pas tout de suite. Jetez un oeil:

Description de la structure meta: id: fig title: Evil Islands, FIG file (figure) application: Evil Islands file-extension: fig license: MIT endian: le doc: 3d mesh seq: - id: magic contents: [0x46, 0x49, 0x47, 0x38] doc: Magic bytes - id: vertex_count type: u4 doc: Number of vertices blocks - id: normal_count type: u4 doc: Number of normals blocks - id: texcoord_count type: u4 doc: Number of UV pairs - id: index_count type: u4 doc: Number of indeces - id: vertex_components_count type: u4 doc: Number of vertex components - id: morph_components_count type: u4 doc: Number of morphing components - id: unknown contents: [0, 0, 0, 0] doc: Unknown (aligment) - id: group type: u4 doc: Render group - id: texture_index type: u4 doc: Texture offset - id: center type: vec3 doc: Center of mesh repeat: expr repeat-expr: 8 - id: aabb_min type: vec3 doc: AABB point of mesh repeat: expr repeat-expr: 8 - id: aabb_max type: vec3 doc: AABB point of mesh repeat: expr repeat-expr: 8 - id: radius type: f4 doc: Radius of boundings repeat: expr repeat-expr: 8 - id: vertex_array type: vertex_block doc: Blocks of raw vertex data repeat: expr repeat-expr: 8 - id: normal_array type: vec4x4 doc: Packed normal data repeat: expr repeat-expr: normal_count - id: texcoord_array type: vec2 doc: Texture coordinates data repeat: expr repeat-expr: texcoord_count - id: index_array type: u2 doc: Triangles indeces repeat: expr repeat-expr: index_count - id: vertex_components_array type: vertex_component doc: Vertex components array repeat: expr repeat-expr: vertex_components_count - id: morph_components_array type: morph_component doc: Morphing components array repeat: expr repeat-expr: morph_components_count types: morph_component: doc: Morphing components indeces seq: - id: morph_index type: u2 doc: Index of morphing data - id: vertex_index type: u2 doc: Index of vertex vertex_component: doc: Vertex components indeces seq: - id: position_index type: u2 doc: Index of position data - id: normal_index type: u2 doc: Index of normal data - id: texture_index type: u2 doc: Index of texcoord data vec2: doc: 2d vector seq: - id: u type: f4 doc: u axis - id: v type: f4 doc: v axis vec3: doc: 3d vector seq: - id: x type: f4 doc: x axis - id: y type: f4 doc: y axis - id: z type: f4 doc: z axis vec3x4: doc: 3d vector with 4 values per axis seq: - id: x type: f4 doc: x axis repeat: expr repeat-expr: 4 - id: y type: f4 doc: y axis repeat: expr repeat-expr: 4 - id: z type: f4 doc: z axis repeat: expr repeat-expr: 4 vertex_block: doc: Vertex raw block seq: - id: block type: vec3x4 doc: Vertex data repeat: expr repeat-expr: _root.vertex_count vec4x4: doc: 4d vector with 4 values per axis seq: - id: x type: f4 doc: x axis repeat: expr repeat-expr: 4 - id: y type: f4 doc: y axis repeat: expr repeat-expr: 4 - id: z type: f4 doc: z axis repeat: expr repeat-expr: 4 - id: w type: f4 doc: w axis repeat: expr repeat-expr: 4
Quelle est la difficulté? Donc, après tout, les données des normales et des sommets sont stockées en blocs de 4, et les sommets sont également disposés en 8 blocs pour l'interpolation.
C'est intéressant: vraisemblablement, un tel regroupement a été fait pour accélérer le traitement à l'aide des instructions SSE qui sont apparues dans les processeurs Intel depuis 1999.
Eh bien, nous avons lu et composé le modèle, mais il manque quelque chose. Exactement - des animations!
Anm
L'animation est stockée sous forme de composant sous forme d'états clés. Un fait intéressant est qu'il prend en charge non seulement l'animation squelettique, mais également le morphing des sommets.

Description de la structure meta: id: anm title: Evil Islands, ANM file (bone animation) application: Evil Islands file-extension: anm license: MIT endian: le doc: Bone animation seq: - id: rotation_frames_count type: u4 doc: Number of rotation frames - id: rotation_frames type: quat repeat: expr repeat-expr: rotation_frames_count doc: Bone rotations - id: translation_frames_count type: u4 doc: Number of translation frames - id: translation_frames type: vec3 repeat: expr repeat-expr: translation_frames_count doc: Bone translation - id: morphing_frames_count type: u4 doc: Number of morphing frames - id: morphing_vertex_count type: u4 doc: Number of vertices with morphing - id: morphing_frames type: morphing_frame repeat: expr repeat-expr: morphing_frames_count doc: Array of morphing frames types: vec3: doc: 3d vector seq: - id: x type: f4 doc: x axis - id: y type: f4 doc: y axis - id: z type: f4 doc: z axis quat: doc: quaternion seq: - id: w type: f4 doc: w component - id: x type: f4 doc: x component - id: y type: f4 doc: y component - id: z type: f4 doc: z component morphing_frame: doc: Array of verteces morphing seq: - id: vertex_shift type: vec3 repeat: expr repeat-expr: _parent.morphing_vertex_count doc: Morphing shift per vertex
C'est tout - maintenant nous avons un modèle à part entière, vous pouvez admirer le lézard ermite fraîchement rendu:

Un moment de nostalgieDécouvrez ce dont le lézard a besoin
Conversation avec le lézard chez lui
Hermit Lizard: Vous êtes venu, mec. C'est bien.
Zach: C'est tout ce que tu voulais me dire?
Hermit Lizard: Vous êtes à nouveau pressé. Je me souviens de vos questions et y répondrai. Je suis venu voir des gens en fer pour conclure un marché. Mais j'ai vu comment ils t'ont fait. Ils ne tiennent pas un mot, j'ai cessé de les croire. Vous avez tenu parole. Un deal vous sera proposé.
Hermit Lizard: Les gens aiment l'or. Les lézards d'or sont sans intérêt. Vous terminerez ma tâche et je vous donnerai l'or que j'ai. Il y a beaucoup d'or.
Zach (pensif et sans grand intérêt) : Hmm ... Or ... Ça ne fera certainement pas de mal ...
Zach: Ce serait mieux si tu pouvais m'aider à découvrir où le vieux magicien que je cherchais depuis si longtemps. Après tout, les lézards sont un peuple ancien, et vous pouvez le savoir!
Hermit Lizard: Vous avez raison. Les lézards sont un peuple ancien. Je peux rassembler tout ce que nous savons sur le vieil homme. Acceptez-vous de terminer ma mission?
Zach: Quel discours! Considérez que tout a déjà été fait.
Hermit Lizard (sérieusement) : Déjà fait? Tu veux me tromper?
Zach: En fait, je voulais faire une blague, sinon tu étais vraiment sérieux.
Le lézard ermite: Je vois. C'est une blague. Je suppose que je peux aussi faire une blague. Alors. Et maintenant, j'ai besoin que vous retourniez l'eau au canal. Des orques nous ont volé de l'eau.
Hermit Lizard: Allez vers le sud le long de l'eau. Vous verrez le barrage et le canal. Le barrage doit être levé. Effet de levier. Je vais le donner. Le canal doit être bloqué. La pierre. Je ne donnerai pas de pierre. Il se trouve déjà au bord du canal. En amont du barrage. La pierre est lourde. Lorsque les orcs ont creusé, ils l'ont soulevé pendant longtemps. Si vous le poussez, il retombera rapidement.
Hermit Lizard: Après cela, revenez. Je vais vous dire tout ce que j'apprends sur le vieux magicien.
Zach: Main dans la main! Mais au fait, si vous ajoutez un peu de pièces à l'histoire, je ne serai pas du tout offensé.
Le lézard ermite: Pour les pièces, allez chez mes proches qui vivent dans les bas-fonds plus au sud. Allez sur l'île de sable la plus éloignée, la troisième d'affilée. Les trésors seront à vous!
Ermite Lézard (à lui-même) : Étrange. Cet homme aime l'humour. Je plaisantais. L'homme n'a pas ri. Très étrange.
Maintenant - le plus intéressant: comment la carte est stockée.
MP
Il s'agit du fichier d'en-tête de carte. En raison d'une coïncidence malheureuse, l'extension coïncide avec celle des fichiers de sauvegarde multijoueurs, que nous ne considérerons pas.
Vous devez d'abord donner une description générale du paysage:
- le nombre de "morceaux" - morceaux de la carte 32x32 mètres;
- hauteur maximale (puisque la hauteur des sommets est stockée dans une échelle entière);
- nombre d'atlas de tuiles.
, — , .

meta: id: mp title: Evil Islands, MP file (map header) application: Evil Islands file-extension: mp license: MIT endian: le doc: Map header seq: - id: magic contents: [0x72, 0xF6, 0x4A, 0xCE] doc: Magic bytes - id: max_altitude type: f4 doc: Maximal height of terrain - id: x_chunks_count type: u4 doc: Number of sectors by x - id: y_chunks_count type: u4 doc: Number of sectors by y - id: textures_count type: u4 doc: Number of texture files - id: texture_size type: u4 doc: Size of texture in pixels by side - id: tiles_count type: u4 doc: Number of tiles - id: tile_size type: u4 doc: Size of tile in pixels by side - id: materials_count type: u2 doc: Number of materials - id: animated_tiles_count type: u4 doc: Number of animated tiles - id: materials type: material doc: Map materials repeat: expr repeat-expr: materials_count - id: id_array type: u4 doc: Tile type repeat: expr repeat-expr: tiles_count enum: tile_type - id: animated_tiles type: animated_tile doc: Animated tiles repeat: expr repeat-expr: animated_tiles_count types: material: doc: Material parameters seq: - id: type type: u4 doc: Material type by enum: terrain_type - id: color type: rgba doc: RGBA diffuse color - id: self_illumination type: f4 doc: Self illumination - id: wave_multiplier type: f4 doc: Wave speed multiplier - id: warp_speed type: f4 doc: Warp speed multiplier - id: unknown size: 12 types: rgba: doc: RGBA color seq: - id: r type: f4 doc: Red channel - id: g type: f4 doc: Green channel - id: b type: f4 doc: Blue channel - id: a type: f4 doc: Alpha channel enums: terrain_type: 0: base 1: water_notexture 2: grass 3: water animated_tile: doc: Animated tile parameters seq: - id: start_index type: u2 doc: First tile of animation - id: length type: u2 doc: Animation frames count enums: tile_type: 0: grass 1: ground 2: stone 3: sand 4: rock 5: field 6: water 7: road 8: empty 9: snow 10: ice 11: drygrass 12: snowballs 13: lava 14: swamp 15: highrock
material type | Tapez |
---|
0 | grass |
1 | ground |
2 | stone |
3 | sand |
4 | rock |
5 | field |
6 | water |
7 | road |
8 | (empty) |
9 | snow |
10 | ice |
11 | drygrass |
12 | snowballs |
13 | lava |
14 | swamp |
15 | highrock |
, Res/aiinfo.res/tileDesc.reg
.
: , — .
: .
. !
SEC
— 3232 . , ZonenameXXXYYY
.

meta: id: sec title: Evil Islands, SEC file (map sector) application: Evil Islands file-extension: sec license: MIT endian: le doc: Map sector seq: - id: magic contents: [0x74, 0xF7, 0x4B, 0xCF] doc: Magic bytes - id: liquids type: u1 doc: Liquids layer indicator - id: vertexes type: vertex doc: Vertex array 33x33 repeat: expr repeat-expr: 1089 - id: liquid_vertexes type: vertex doc: Vertex array 33x33 if: liquids != 0 repeat: expr repeat-expr: 'liquids != 0 ? 1089 : 0' - id: tiles type: tile doc: Tile array 16x16 repeat: expr repeat-expr: 256 - id: liquid_tiles type: tile doc: Tile array 16x16 if: liquids != 0 repeat: expr repeat-expr: 'liquids != 0 ? 256 : 0' - id: liquid_material type: u2 doc: Index of material if: liquids != 0 repeat: expr repeat-expr: 'liquids != 0 ? 256 : 0' types: vertex: doc: Vertex data seq: - id: x_shift type: s1 doc: Shift by x axis - id: y_shift type: s1 doc: Shift by y axis - id: altitude type: u2 doc: Height (z position) - id: packed_normal type: normal doc: Packed normal normal: doc: Normal (3d vector) seq: - id: packed type: u4 doc: Normal packed in 4b instances: x: doc: Unpacked x component value: packed >> 11 & 0x7FF y: doc: Unpacked y component value: packed & 0x7FF z: doc: Unpacked z component value: packed >> 22 tile: doc: Tile parameters seq: - id: packed type: u2 doc: Tile information packed in 2b instances: index: doc: Tile index in texture value: packed & 63 texture: doc: Texture index value: packed >> 6 & 255 rotation: doc: Tile rotation (*90 degrees) value: packed >> 14 & 3
— .
10 z, 11 x y
unsigned packed_normal; float x = ((float)((packed_normal >> 11) & 0x7FF) - 1000.0f) / 1000.0f; float y = ((float)(packed_normal & 0x7FF) - 1000.0f) / 1000.0f; float z = (float)(packed_normal >> 22) / 1000.0f;
6 , 8 , 2
unsigned short texture; unsigned char tile_index = f & 63; unsigned char texture_index = (f >> 6) & 255; unsigned char rotation = (f >> 14) & 3;
3d33 33 , , 3232 . — 1 .
:
x = x + x_offset / 254
y = y + y_offset / 254
z = altitude / 65535 * max_altitude ( .mp )
"", :
0 1 2 *-*-* |\|\| ~ 33 *-*-* |\|\| ~ 66 *-*-* ~ ~ ~
, , 1616 . — 2 . , 90 .
. , , ID , MP .
: MP, , : ID , - .
ID — .
— :

- — , .
MOB
( ) , , : . — " ", .
, ( ).
:
typedef structure { unsigned type_id; unsigned size; byte data[size - 8]; } node;
(, !)
( , )
meta: id: mob title: Evil Islands, MOB file (map entities) application: Evil Islands file-extension: mob license: MIT endian: le doc: Map entities tree seq: - id: root_node type: node doc: Root node types: node: doc: Entity node seq: - id: type_id type: u4 doc: Node children type ID - id: size type: u4 doc: Node full size - id: data type: node_data size: size - 8 doc: Node stored data node_data: doc: Node data seq: - id: value type: switch-on: _parent.type_id cases: 0xA000: node 0x00001E00: node 0x00001E01: node 0x00001E02: node 0x00001E03: node 0x00001E0B: node 0x00001E0E: node 0x0000A000: node 0x0000AA01: node 0x0000ABD0: node 0x0000B000: node 0x0000B001: node 0x0000CC01: node 0x0000DD01: node 0x0000E000: node 0x0000E001: node 0x0000F000: node 0x0000FF00: node 0x0000FF01: node 0x0000FF02: node 0xBBAB0000: node 0xBBAC0000: node 0xBBBB0000: node 0xBBBC0000: node 0xBBBD0000: node 0xBBBE0000: node 0xBBBF0000: node 0xDDDDDDD1: node _: u1 doc: Node elements repeat: eos
| () | |
---|
AiGraph | | |
AreaArray | | |
Byte | 1 | 1 |
Diplomacy | 4096 | 32x32 2 |
Dword | 4 | 4 |
Flotter | 4 | 4 |
LeverStats | 12 | |
Null | 0 | |
Plot | 12 | 3 floats (vec3) |
Plot2DArray | | |
Quaternion | 16 | 4 floats (vec4) |
Record | >8 | |
Rectangle | | |
String | | |
StringArray | >4 | |
StringEncrypted | >4 | |
UnitStats | 180 | |
Unknown | | |
type_idtype_id | | |
---|
0x00000000 | Record | ROOT |
0x00001E00 | Record | VSS_SECTION |
0x00001E01 | Record | VSS_TRIGER |
0x00001E02 | Record | VSS_CHECK |
0x00001E03 | Record | VSS_PATH |
0x00001E04 | Dword | VSS_ID |
0x00001E05 | Rectangle | VSS_RECT |
0x00001E06 | Dword | VSS_SRC_ID |
0x00001E07 | Dword | VSS_DST_ID |
0x00001E08 | String | VSS_TITLE |
0x00001E09 | String | VSS_COMMANDS |
0x00001E0A | Byte | VSS_ISSTART |
0x00001E0B | Record | VSS_LINK |
0x00001E0C | String | VSS_GROUP |
0x00001E0D | Byte | VSS_IS_USE_GROUP |
0x00001E0E | Record | VSS_VARIABLE |
0x00001E0F | StringArray | VSS_BS_CHECK |
0x00001E10 | StringArray | VSS_BS_COMMANDS |
0x00001E11 | String | VSS_CUSTOM_SRIPT |
0x0000A000 | Record | OBJECTDBFILE |
0x0000AA00 | Null | LIGHT_SECTION |
0x0000AA01 | Record | LIGHT |
0x0000AA02 | Flotter | LIGHT_RANGE |
0x0000AA03 | String | LIGHT_NAME |
0x0000AA04 | Plot | LIGHT_POSITION |
0x0000AA05 | Dword | LIGHT_ID |
0x0000AA06 | Byte | LIGHT_SHADOW |
0x0000AA07 | Plot | LIGHT_COLOR |
0x0000AA08 | String | LIGHT_COMMENTS |
0x0000ABD0 | Record | WORLD_SET |
0x0000ABD1 | Plot | WS_WIND_DIR |
0x0000ABD2 | Flotter | WS_WIND_STR |
0x0000ABD3 | Flotter | WS_TIME |
0x0000ABD4 | Flotter | WS_AMBIENT |
0x0000ABD5 | Flotter | WS_SUN_LIGHT |
0x0000B000 | Record | OBJECTSECTION |
0x0000B001 | Record | OBJECT |
0x0000B002 | Dword | NID |
0x0000B003 | Dword | OBJTYPE |
0x0000B004 | String | OBJNAME |
0x0000B005 | Null | OBJINDEX |
0x0000B006 | String | OBJTEMPLATE |
0x0000B007 | String | OBJPRIMTXTR |
0x0000B008 | String | OBJSECTXTR |
0x0000B009 | Plot | OBJPOSITION |
0x0000B00A | Quaternion | OBJROTATION |
0x0000B00B | Null | OBJTEXTURE |
0x0000B00C | Plot | OBJCOMPLECTION |
0x0000B00D | StringArray | OBJBODYPARTS |
0x0000B00E | String | PARENTTEMPLATE |
0x0000B00F | String | OBJCOMMENTS |
0x0000B010 | Null | OBJ_DEF_LOGIC |
0x0000B011 | Byte | OBJ_PLAYER |
0x0000B012 | Dword | OBJ_PARENT_ID |
0x0000B013 | Byte | OBJ_USE_IN_SCRIPT |
0x0000B014 | Byte | OBJ_IS_SHADOW |
0x0000B015 | Null | OBJ_R |
0x0000B016 | String | OBJ_QUEST_INFO |
0x0000C000 | Null | SC_OBJECTDBFILE |
0x0000CC00 | Null | SOUND_SECTION |
0x0000CC01 | Record | SOUND |
0x0000CC02 | Dword | SOUND_ID |
0x0000CC03 | Plot | SOUND_POSITION |
0x0000CC04 | Dword | SOUND_RANGE |
0x0000CC05 | String | SOUND_NAME |
0x0000CC06 | Dword | SOUND_MIN |
0x0000CC07 | Dword | SOUND_MAX |
0x0000CC08 | String | SOUND_COMMENTS |
0x0000CC09 | Null | SOUND_VOLUME |
0x0000CC0A | StringArray | SOUND_RESNAME |
0x0000CC0B | Dword | SOUND_RANGE2 |
0x0000CC0D | Byte | SOUND_AMBIENT |
0x0000CC0E | Byte | SOUND_IS_MUSIC |
0x0000D000 | Null | PR_OBJECTDBFILE |
0x0000DD00 | Null | PARTICL_SECTION |
0x0000DD01 | Record | PARTICL |
0x0000DD02 | Dword | PARTICL_ID |
0x0000DD03 | Plot | PARTICL_POSITION |
0x0000DD04 | String | PARTICL_COMMENTS |
0x0000DD05 | String | PARTICL_NAME |
0x0000DD06 | Dword | PARTICL_TYPE |
0x0000DD07 | Flotter | PARTICL_SCALE |
0x0000E000 | Record | DIRICTORY |
0x0000E001 | Record | FOLDER |
0x0000E002 | String | DIR_NAME |
0x0000E003 | Dword | DIR_NINST |
0x0000E004 | Dword | DIR_PARENT_FOLDER |
0x0000E005 | Byte | DIR_TYPE |
0x0000F000 | Record | DIRICTORY_ELEMENTS |
0x0000FF00 | Record | SEC_RANGE |
0x0000FF01 | Record | MAIN_RANGE |
0x0000FF02 | Record | RANGE |
0x0000FF05 | Dword | MIN_ID |
0x0000FF06 | Dword | MAX_ID |
0x31415926 | AiGraph | AIGRAPH |
0xACCEECCA | String | SS_TEXT_OLD |
0xACCEECCB | StringEncrypted | SS_TEXT |
0xBBAB0000 | Record | MAGIC_TRAP |
0xBBAB0001 | Dword | MT_DIPLOMACY |
0xBBAB0002 | String | MT_SPELL |
0xBBAB0003 | AreaArray | MT_AREAS |
0xBBAB0004 | Plot2DArray | MT_TARGETS |
0xBBAB0005 | Dword | MT_CAST_INTERVAL |
0xBBAC0000 | Record | LEVER |
0xBBAC0001 | Null | LEVER_SCIENCE_STATS |
0xBBAC0002 | Byte | LEVER_CUR_STATE |
0xBBAC0003 | Byte | LEVER_TOTAL_STATE |
0xBBAC0004 | Byte | LEVER_IS_CYCLED |
0xBBAC0005 | Byte | LEVER_CAST_ONCE |
0xBBAC0006 | LeverStats | LEVER_SCIENCE_STATS_NEW |
0xBBAC0007 | Byte | LEVER_IS_DOOR |
0xBBAC0008 | Byte | LEVER_RECALC_GRAPH |
0xBBBB0000 | Record | UNIT |
0xBBBB0001 | Null | UNIT_R |
0xBBBB0002 | String | UNIT_PROTOTYPE |
0xBBBB0003 | Null | UNIT_ITEMS |
0xBBBB0004 | UnitStats | UNIT_STATS |
0xBBBB0005 | StringArray | UNIT_QUEST_ITEMS |
0xBBBB0006 | StringArray | UNIT_QUICK_ITEMS |
0xBBBB0007 | StringArray | UNIT_SPELLS |
0xBBBB0008 | StringArray | UNIT_WEAPONS |
0xBBBB0009 | StringArray | UNIT_ARMORS |
0xBBBB000A | Byte | UNIT_NEED_IMPORT |
0xBBBC0000 | Record | UNIT_LOGIC |
0xBBBC0001 | Null | UNIT_LOGIC_AGRESSIV |
0xBBBC0002 | Byte | UNIT_LOGIC_CYCLIC |
0xBBBC0003 | Dword | UNIT_LOGIC_MODEL |
0xBBBC0004 | Flotter | UNIT_LOGIC_GUARD_R |
0xBBBC0005 | Plot | UNIT_LOGIC_GUARD_PT |
0xBBBC0006 | Byte | UNIT_LOGIC_NALARM |
0xBBBC0007 | Byte | UNIT_LOGIC_USE |
0xBBBC0008 | Null | UNIT_LOGIC_REVENGE |
0xBBBC0009 | Null | UNIT_LOGIC_FEAR |
0xBBBC000A | Flotter | UNIT_LOGIC_WAIT |
0xBBBC000B | Byte | UNIT_LOGIC_ALARM_CONDITION |
0xBBBC000C | Flotter | UNIT_LOGIC_HELP |
0xBBBC000D | Byte | UNIT_LOGIC_ALWAYS_ACTIVE |
0xBBBC000E | Byte | UNIT_LOGIC_AGRESSION_MODE |
0xBBBD0000 | Record | GUARD_PT |
0xBBBD0001 | Plot | GUARD_PT_POSITION |
0xBBBD0002 | Null | GUARD_PT_ACTION |
0xBBBE0000 | Record | ACTION_PT |
0xBBBE0001 | Plot | ACTION_PT_LOOK_PT |
0xBBBE0002 | Dword | ACTION_PT_WAIT_SEG |
0xBBBE0003 | Dword | ACTION_PT_TURN_SPEED |
0xBBBE0004 | Byte | ACTION_PT_FLAGS |
0xBBBF0000 | Record | TORCH |
0xBBBF0001 | Flotter | TORCH_STRENGHT |
0xBBBF0002 | Plot | TORCH_PTLINK |
0xBBBF0003 | String | TORCH_SOUND |
0xDDDDDDD1 | Record | DIPLOMATION |
0xDDDDDDD2 | Diplomacy | DIPLOMATION_FOF |
0xDDDDDDD3 | StringArray | DIPLOMATION_PL_NAMES |
0xFFFFFFFF | Unknown | UNKNOWN |
— , , Nival, — , ( , ).
unsigned key; for (size_t i = 0; i < size; i++) { key += (((((key * 13) << 4) + key) << 8) - key) * 4 + 2531011; data[i] ^= key >> 16; }
: , ( ) . , , , .
( , , — Windows 98):

: , . , ( , , " : ", ).
, , - - , , Collada :

. , .
, . - , — - , . , -...
— !
UPD (23.01.2019):
, : github .
, (, "" ).
- http://gipatgroup.org/utilities — EiEdit (.res, .*db), MobSurgeon (.mob)
- http://svn.gipat.org/trac/GGWiki — EiEdit (.res, .*db), MobSurgeon (.mob), .mp, .sec, .*db
- https://github.com/demothorg/eifixer —
- https://github.com/konstvest/ei_figer — Blender .lnk, .fig, .bon, .anm
- https://github.com/demothorg/ei-tools — (.mob, .lnk, .mpr, .res) +
- https://github.com/konstvest/ei_maper — (.mpr, .mp, .sec, .mob)
- https://github.com/chemmalion/EIDBEditor — (.*db)
- https://gitlab.com/ykurganov/open-evil-islands — ,