
¿Recuerdas muchos juegos rusos? Cualitativa? Memorable? Sí lo fueron. Si tienes más de 35 años o eres fanático de la industria de los juegos rusos, entonces probablemente estés familiarizado con las Tierras Malditas.
La historia comenzó muy prosaicamente: verano, calor. No hay nada especial que hacer, y cuando hojeaba perezosamente el contenido del disco duro de la computadora portátil, mi mirada captó una carpeta con el ícono del dragón familiar que había estado inactivo durante un par de años.
¿Qué fanático del juego no estará interesado en saber qué hay dentro?
Introduccion
Cursed Lands , o, como se les llamó fuera de la CEI, Evil Islands: Curse of the Lost Soul , un juego de rol sigiloso lanzado en 2000. El juego fue desarrollado por Nival Interactive, que en ese momento ya se había establecido como una serie de juegos de Alloda (Rage of Mages en el extranjero). La mayoría de los graduados de la Universidad Estatal de Moscú trabajaron en él: fueron muy capaces de realizar uno de los primeros juegos con un mundo completamente tridimensional.
En 2010, Mail.Ru ( información ) transfirió el título, pero el juego aún se vende en la tienda GOG en nombre de Nival.
Hace relativamente poco, el juego cumplió 18 años: se considera un cumpleaños el 26 de octubre, la fecha de lanzamiento en el CIS. A pesar de la antigüedad, el servidor maestro oficial todavía está en servicio: periódicamente, alguien decide arrastrarse por los bosques de Gipat y golpear una docena o dos esqueletos con un escuadrón de camaradas.
Brevemente sobre el artículo.
Inicialmente, mi objetivo era solo escribir un convertidor unidireccional "para mí" en Python 3, utilizando exclusivamente bibliotecas estándar. Sin embargo, en el proceso, la documentación sobre formatos comenzó sin problemas, los intentos de estandarizar de alguna manera la salida. Para algunos formatos, la estructura se describió utilizando Kaitai Struct . Como resultado, todo resultó en la redacción de este artículo y la wiki en formatos.
Noto de inmediato: en su mayor parte, los archivos del juego ya han sido investigados, los editores de fanáticos han sido escritos para ellos. Sin embargo, la información está extremadamente fragmentada y no hay una descripción más o menos completa de los formatos en el dominio público, ni hay un conjunto adecuado para crear modificaciones.
... y cómo leerlo
Se proporcionan esquemas (archivos .ksy) para todos los formatos, que se pueden convertir en dos clics en código en varios de los idiomas más populares.
Desafortunadamente, ya en las últimas etapas de la redacción de este artículo, descubrí que el estimado Habr no puede resaltar YAML (y JSON), y todos los esquemas lo usan. Esto no debería ser un gran problema, pero si resulta inconveniente leer el esquema, puedo aconsejarle que lo copie a un editor externo, por ejemplo, NPP.
Recursos y donde viven
El juego es una aplicación portátil que contiene un motor con bibliotecas, un lanzador y, de hecho, recursos empaquetados.
Esto es interesante: la configuración del juego se almacena casi por completo en el registro. El error de la cámara en la versión GOG se debe al hecho de que el instalador no registra los valores predeterminados correctos.
A primera vista del contenido de la carpeta del juego, notamos inmediatamente un par de nuevas extensiones de archivo: ASI y REG.
La primera es una biblioteca dinámica, que no consideraremos (esto lo hacen especialistas en ingeniería inversa), pero la segunda es el primer formato de archivo patentado del juego.
REG
Los archivos de este tipo son serialización binaria de archivos INI de texto conocidos.
El contenido se divide en secciones que almacenan claves y sus valores. Un archivo REG conserva esta jerarquía, pero acelera la lectura y el análisis de datos; en 2000, esto fue aparentemente crítico.
En general, puede describir la estructura de este diagrama:

Descripción de la estructurameta: 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
Esto es interesante: en 2002, Nival compartió algunas herramientas con la comunidad del juego ( instantánea del sitio ), una de ellas fue el serializador INI en REG. Como puede suponer, un deserializador apareció casi de inmediato, aunque no uno oficial.
Con la carpeta de inicio ordenada, pasemos a los subdirectorios.
El primer aspecto recae en la carpeta Cámaras que contiene los archivos CAM.
Cam
Un formato muy simple es simplemente empacar las posiciones de las cámaras a lo largo del tiempo. La cámara se describe por posición y rotación. Los otros dos campos son presumiblemente tiempo y paso en la secuencia de movimientos.

Descripción de la estructura 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
En la siguiente carpeta - Res, (¡inesperadamente!) Se almacenan los archivos RES que son archivos.
RES
Este formato a veces está oculto bajo otras extensiones, pero el original sigue siendo exactamente RES.
La estructura de datos es muy típica para un archivo con acceso aleatorio a los archivos: hay tablas para almacenar información sobre archivos dentro, una tabla de nombres y el contenido de los archivos.
La estructura del directorio está contenida directamente en los nombres.
Vale la pena señalar dos hechos extremadamente interesantes:
- El archivo está optimizado para cargar información de archivo en una lista vinculada con hashing cerrado.
- Puede almacenar el contenido del archivo una vez, pero consúltelo con diferentes nombres. Hasta donde sé, este hecho se usó en un reempaque de ventilador, donde el tamaño del juego se redujo en gran medida debido a esto. En la distribución original, no se utilizó la optimización de archivo.

Descripción de la estructura 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
Esto es interesante: en la versión rusa del juego, el archivo Speech.res contiene dos subdirectorios syt con contenidos completamente idénticos, por lo que el tamaño del archivo es dos veces mayor, por lo que el juego no cabe en un CD.
Ahora puede descomprimir todos los archivos (se pueden anidar):
- RES es solo un archivo,
- MPR - paisaje de niveles de juego,
- MQ: información sobre las tareas del multijugador,
- ANM: un conjunto de animaciones,
- MOD - modelo 3d,
- BON: la ubicación de los huesos del modelo.
Si los archivos dentro del archivo no tienen una extensión, colocaremos la extensión principal, para los archivos BON y ANM.
También puede dividir todos los archivos recibidos en cuatro grupos:
- Texturas
- Bases de datos
- Modelos
- Nivelar archivos.
Comencemos con lo simple, con las texturas.
MMP
En realidad, la textura. Tiene un encabezado pequeño que indica los parámetros de la imagen, el número de niveles de MIP y la compresión utilizada. Después del encabezado están los niveles de imagen MIP en orden descendente de tamaño.

Descripción de la estructura 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
Posibles formatos de empaque de píxeles:
fourcc | Descripción |
---|
44 44 00 00 | ARGB4 |
44 58 54 31 | Dxt1 |
44 58 54 33 | Dxt3 |
50 4E 54 33 | PNT3 - RGB comprimido ARGB8 |
50 56 00 00 | R5G5B5 |
51 55 00 00 | A1R5G5B5 |
88 88 00 00 | ARGB8 |
Sobre PNT3Si el formato de imagen es PNT3 , entonces la estructura de píxeles después de desempacar es ARGB8; bits_per_pixel
- tamaño de la imagen comprimida en bytes.
Desembalaje 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
Esto es interesante: algunas de las texturas se reflejan verticalmente (¿o algunas no se reflejan?).
Y el juego es muy celoso de la transparencia: si la imagen tiene un canal alfa, el color de los píxeles transparentes debe ser exactamente negro. O blanco, así de afortunado.
Los formatos simples han terminado, pasemos a los más rígidos: en algún momento, las filas de los fabricantes de mod mantuvieron furiosamente sus propias herramientas de edición para los siguientes formatos, y no en vano. Te lo advertí
Bases de datos (* DB y otros)
Este formato es extremadamente incómodo de describir: en esencia, este es un árbol de nodos serializado (o tablas de registro). Un archivo consta de varias tablas con los tipos de campo especificados. Estructura general: las tablas están anidadas en un nodo "raíz" común, los registros son nodos dentro de una tabla.
En cada nodo, se especifica su tipo y tamaño:
unsigned char type_index; unsigned char raw_size;
El tipo de campo de la tabla lo toma el índice de la cadena de formato de la tabla, el tipo real se determina por el valor obtenido.
Tipos de campodesignación | la descripcion |
---|
S | cuerda |
Yo | 4b int |
U | 4b sin firmar |
F | Flotador 4b |
X | bits byte |
f | matriz flotante |
yo | int array |
B | bool |
b | matriz bool |
H | bytes hexadecimales desconocidos |
T | tiempo |
0 0 | no declarado |
1 | 0FII |
2 | SUFF |
3 | FFFF |
4 4 | 0SISS |
5 5 | 0SISS00000U |
Descripción de las bases.Artículos (.idb)
mesa | la estructura |
---|
Materiales | SSSIFFFIFIFfIX |
Arma | SSISIIIFFFFIFIXB00000IHFFFfHHFF |
Armadura | SSISIIIFFFFIFIXB00000ffBiHH |
Artículos rápidos | SSISIIIFFFFIFIXB00000IIFFSbH |
Artículos de misión | SSISIIIFFFFIFIXB00000Is |
Venta de artículos | SSISIIIFFFFIFIXB00000IHI |
Interruptores (.ldb)
mesa | la estructura |
---|
Cambiar prototipo | SfIFTSSS |
Habilidades y Habilidades (.pdb)
mesa | la estructura |
---|
Habilidades | SSI0000000s |
Las habilidades | SSI0000000SSIIIFFFIIIIBI |
Huellas (prints.db)
mesa | la estructura |
---|
Rastros de sangre | 0S11 |
Rastros de llamas | 0S110000001 |
Huellas | 0S11 |
Hechizos (.sdb)
mesa | la estructura |
---|
Prototipos | SSSFIFIFFFFIIIIUSSIIbIXFFFFF |
Modificadores | SSFIFFISX |
Patrones | 0SssSX |
Plantillas de armadura | 0SssSX |
Patrones de armas | 0SssSX |
Criaturas (.udb)
mesa | la estructura |
---|
Partes dañadas | SffUU |
Carrera | SUFFUUFfFUUf222222000000000000SssFSsfUUfUUIUSBFUUUU |
Prototipos de monstruos | SSIUIFFFSFFFFFFFFFUFFFFFFff33sfssSFFFFFUFUSF |
NPC | SUFFFFbbssssFUB |
Gritos (acks.db)
mesa | la estructura |
---|
Las respuestas | 0S0000000044444444444444444444445444444444444 |
Gritos | 0S0000000044444 |
Otros | 0S0000000044 |
Misiones (.qdb)
mesa | la estructura |
---|
Misiones | SFIISIIs |
Reuniones informativas | SFFsSsssssI |
Esto es interesante: el 16 de enero de 2002, Nival publicó las bases de origen para el modo multijugador en formato csv, así como un convertidor de utilidad en el formato del juego ( instantánea del sitio ). Naturalmente, el conversor inverso no tardó en aparecer. También hay al menos dos documentos que describen los campos y sus tipos de los modmakers, pero leerlos es muy difícil.
Adb
Base de datos de animación para un tipo específico de unidad. A diferencia del * DB mencionado anteriormente, es bastante "humano": es una tabla de un solo nivel con tamaños de campo estático.

Descripción de la estructura 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'
Esto es interesante: para varias unidades, se utiliza un formato de base de datos parcialmente truncado, que apenas ha sido explorado.
Habiendo tratado con las bases de datos, declaramos un corte publicitario. Pero no anunciaremos nada, no es nuestro método. Mejor denote lo que viene a continuación: cómo se nombran los archivos de criaturas.
El nombre se recopila de grupos de dos caracteres: abreviaturas del "nivel" lógico.
Por ejemplo, el personaje femenino será unhufe
- Unit > Human > Female
, e initwesp
- Inventory > Item > Weapon > Spear
, es decir, una lanza en el inventario (no la parte de atrás, y eso es bueno).
Árbol completo de elementos de nombre: 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
Esto es interesante: según esta clasificación, los hongos son árboles, los golems con duendes son hermanos, y Tka-Rick es un monstruo. También aquí puede ver los nombres "operativos" de los monstruos, sospechosamente similares a los de D&D: espectador (mal de ojo), succub (arpía), ogro (caníbal), driad (silvicultores).
Habiendo descansado moralmente, nos lanzamos de cabeza al modelo. Se presentan en varios formatos que están vinculados entre sí.
Lnk
Lógicamente: la base del modelo. Describe la jerarquía de partes del modelo, en términos de modelado 3D moderno: la jerarquía de los huesos.

Descripción de la estructura 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
El nombre principal del hueso primario es una cadena vacía (longitud 0).
Hay huesos, sin embargo, no es suficiente nombrarlos y juntarlos; debe ensamblarlos en un esqueleto.
Bon
Anteriormente mencionado, este formato (si no es un archivo) establece la posición de las partes (huesos) del modelo en relación con la parte principal. Solo se almacena el desplazamiento, sin rotación, una de las diferencias con los formatos modernos.

Descripción de la estructura 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
Como puede ver, hay demasiados números para un turno; el hecho es que aquí encontramos por primera vez una de las características clave del motor del juego: la interpolación trilineal de modelos.
Cómo funciona: el modelo tiene tres parámetros de interpolación: condicionalmente, fuerza, destreza, crecimiento. También hay 8 estados extremos del modelo. Usando los parámetros, podemos obtener el modelo final por interpolación trilineal.
El algoritmo mismo def trilinear(val, coefs=[0, 0, 0]):
Esto es interesante: la interpolación de modelos trilineales se usa para animar algunos objetos, por ejemplo, abrir una puerta de piedra y cofres.
Ahora es el momento de mirar las partes del modelo en sí.
FIG
Quizás este rally sea imposible de entender. Puede encontrar su descripción y el complemento para la licuadora en la red, pero incluso con ellos, la conciencia no llega de inmediato. Echa un vistazo:

Descripción de la estructura 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
Cual es la dificultad? Entonces, después de todo, los datos de normales y vértices se almacenan en bloques de 4, y los vértices también se organizan en 8 bloques para la interpolación.
Esto es interesante: presumiblemente, dicha agrupación se hizo para acelerar el procesamiento con la ayuda de las instrucciones SSE que aparecieron en los procesadores Intel desde 1999.
Bueno, leímos y compusimos el modelo, pero falta algo. Exactamente - animaciones!
Anm
La animación se almacena en forma de componente como estados clave. Un hecho interesante es que no solo admite la animación esquelética, sino también la transformación de vértices.

Descripción de la estructura 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
Eso es todo: ahora tenemos un modelo completo, puedes admirar el lagarto ermitaño recién renderizado:

Un momento de nostalgiaDescubre lo que necesita el lagarto
Conversación con el lagarto en su casa.
Lagarto Ermitaño: Has venido, hombre. Esto es bueno
Zach: ¿ Eso es todo lo que querías decirme?
Ermitaño Lagarto: Tienes prisa de nuevo. Recuerdo tus preguntas y las responderé. Llegué a la gente de hierro para hacer un trato. Pero vi cómo te hicieron. No tienen una palabra, dejé de creerles. Cumpliste tu palabra. Se le ofrecerá un trato.
Lagarto ermitaño: la gente ama el oro. Los lagartos dorados no son interesantes. Completarás mi tarea y te daré el oro que tengo. Hay mucho oro
Zach (pensativo y sin mucho interés) : Hmm ... Oro ... Ciertamente no dolerá ...
Zach: Sería mejor si pudieras ayudarme a descubrir dónde vive el viejo mago que he estado buscando durante tanto tiempo. Después de todo, los lagartos son un pueblo antiguo, ¡y puedes saberlo!
Lagarto Ermitaño: Tienes razón. Los lagartos son un pueblo antiguo. Puedo recoger todo lo que sabemos sobre el viejo. ¿Estás de acuerdo en completar mi misión?
Zach: ¡ Qué charla! Tenga en cuenta que todo ya se ha hecho.
Ermitaño Lagarto (en serio) : ¿ Ya está hecho? Quieres engañarme
Zach: En realidad, quería hacer una broma, de lo contrario hablabas muy en serio.
El lagarto ermitaño: Ya veo. Esto es una broma Supongo que también puedo hacer una broma. Entonces Y ahora necesito que devuelvas el agua al Canal. Los orcos nos robaron agua.
Lagarto Ermitaño: Ve hacia el sur a lo largo del agua. Verá la presa y el Canal. La presa debe ser levantada. Apalancamiento Lo daré El canal necesita ser bloqueado. La piedra No voy a dar una piedra. Ya se encuentra al borde del canal. Aguas arriba de la presa. La piedra es pesada. Cuando los orcos cavaron, lo levantaron durante mucho tiempo. Si lo empujas, retrocederá rápidamente.
Ermitaño Lagarto: Después de eso, vuelve. Te contaré todo lo que aprendo sobre el viejo mago.
Zach: ¡ Mano a mano! Pero, por cierto, si agrega un poco de monedas a la historia, no me ofende en absoluto.
El lagarto ermitaño: para obtener monedas, ve a mis parientes que viven en las aguas poco profundas del sur. Ve a la isla arenosa más lejana, la tercera en una fila. ¡Los tesoros serán tuyos!
Ermitaño Lagarto (para sí mismo) : Extraño. Este hombre ama el humor. Estaba bromeando El hombre no se rio. Muy extraño
Ahora, lo más interesante: cómo se almacena el mapa.
MP
Este es el archivo de encabezado del mapa. Debido a una desafortunada coincidencia, la extensión coincide con la de los archivos guardados para varios jugadores, que no consideraremos.
Primero debes dar una descripción general del paisaje:
- el número de "trozos" - piezas de la tarjeta de 32x32 metros;
- altura máxima (ya que la altura de los vértices se almacena en una escala entera);
- Número de atlas de azulejos.
, — , .

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 | |
---|
0 0 | grass |
1 | ground |
2 | stone |
3 | sand |
4 | rock |
5 | field |
6 6 | water |
7 7 | road |
8 | (empty) |
9 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 |
Flotador | 4 | 4 |
LeverStats | 12 | |
Null | 0 0 | |
Plot | 12 | 3 floats (vec3) |
Plot2DArray | | |
Quaternion | 16 | 4 floats (vec4) |
Record | >8 | |
Rectangle | | |
Cadena | | |
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 | Cadena | VSS_TITLE |
0x00001E09 | Cadena | VSS_COMMANDS |
0x00001E0A | Byte | VSS_ISSTART |
0x00001E0B | Record | VSS_LINK |
0x00001E0C | Cadena | VSS_GROUP |
0x00001E0D | Byte | VSS_IS_USE_GROUP |
0x00001E0E | Record | VSS_VARIABLE |
0x00001E0F | StringArray | VSS_BS_CHECK |
0x00001E10 | StringArray | VSS_BS_COMMANDS |
0x00001E11 | Cadena | VSS_CUSTOM_SRIPT |
0x0000A000 | Record | OBJECTDBFILE |
0x0000AA00 | Null | LIGHT_SECTION |
0x0000AA01 | Record | LIGHT |
0x0000AA02 | Flotador | LIGHT_RANGE |
0x0000AA03 | Cadena | LIGHT_NAME |
0x0000AA04 | Plot | LIGHT_POSITION |
0x0000AA05 | Dword | LIGHT_ID |
0x0000AA06 | Byte | LIGHT_SHADOW |
0x0000AA07 | Plot | LIGHT_COLOR |
0x0000AA08 | Cadena | LIGHT_COMMENTS |
0x0000ABD0 | Record | WORLD_SET |
0x0000ABD1 | Plot | WS_WIND_DIR |
0x0000ABD2 | Flotador | WS_WIND_STR |
0x0000ABD3 | Flotador | WS_TIME |
0x0000ABD4 | Flotador | WS_AMBIENT |
0x0000ABD5 | Flotador | WS_SUN_LIGHT |
0x0000B000 | Record | OBJECTSECTION |
0x0000B001 | Record | OBJECT |
0x0000B002 | Dword | NID |
0x0000B003 | Dword | OBJTYPE |
0x0000B004 | Cadena | OBJNAME |
0x0000B005 | Null | OBJINDEX |
0x0000B006 | Cadena | OBJTEMPLATE |
0x0000B007 | Cadena | OBJPRIMTXTR |
0x0000B008 | Cadena | OBJSECTXTR |
0x0000B009 | Plot | OBJPOSITION |
0x0000B00A | Quaternion | OBJROTATION |
0x0000B00B | Null | OBJTEXTURE |
0x0000B00C | Plot | OBJCOMPLECTION |
0x0000B00D | StringArray | OBJBODYPARTS |
0x0000B00E | Cadena | PARENTTEMPLATE |
0x0000B00F | Cadena | 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 | Cadena | 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 | Cadena | SOUND_NAME |
0x0000CC06 | Dword | SOUND_MIN |
0x0000CC07 | Dword | SOUND_MAX |
0x0000CC08 | Cadena | 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 | Cadena | PARTICL_COMMENTS |
0x0000DD05 | Cadena | PARTICL_NAME |
0x0000DD06 | Dword | PARTICL_TYPE |
0x0000DD07 | Flotador | PARTICL_SCALE |
0x0000E000 | Record | DIRICTORY |
0x0000E001 | Record | FOLDER |
0x0000E002 | Cadena | 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 | Cadena | SS_TEXT_OLD |
0xACCEECCB | StringEncrypted | SS_TEXT |
0xBBAB0000 | Record | MAGIC_TRAP |
0xBBAB0001 | Dword | MT_DIPLOMACY |
0xBBAB0002 | Cadena | 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 | Cadena | 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 | Flotador | 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 | Flotador | UNIT_LOGIC_WAIT |
0xBBBC000B | Byte | UNIT_LOGIC_ALARM_CONDITION |
0xBBBC000C | Flotador | 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 | Flotador | TORCH_STRENGHT |
0xBBBF0002 | Plot | TORCH_PTLINK |
0xBBBF0003 | Cadena | 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 — ,